blob: 1791654ba7ccc4150fbda2a091cd151cb8261dc9 [file] [log] [blame]
Yangster1d4d6862017-10-31 12:58:51 -07001/*
2* Copyright (C) 2017 The Android Open Source Project
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16
17#define DEBUG true // STOPSHIP if true
18#include "Log.h"
19
20#include "GaugeMetricProducer.h"
Yao Chenb3561512017-11-21 18:07:17 -080021#include "guardrail/StatsdStats.h"
Yangster1d4d6862017-10-31 12:58:51 -070022#include "stats_util.h"
23
24#include <cutils/log.h>
25#include <limits.h>
26#include <stdlib.h>
27
yrob0378b02017-11-09 20:36:25 -080028using android::util::FIELD_COUNT_REPEATED;
yro2b0f8862017-11-06 14:27:31 -080029using android::util::FIELD_TYPE_BOOL;
30using android::util::FIELD_TYPE_FLOAT;
31using android::util::FIELD_TYPE_INT32;
32using android::util::FIELD_TYPE_INT64;
33using android::util::FIELD_TYPE_MESSAGE;
Yangster-macd1815dc2017-11-13 21:43:15 -080034using android::util::FIELD_TYPE_STRING;
yro2b0f8862017-11-06 14:27:31 -080035using android::util::ProtoOutputStream;
Yangster1d4d6862017-10-31 12:58:51 -070036using std::map;
37using std::string;
38using std::unordered_map;
39using std::vector;
40
41namespace android {
42namespace os {
43namespace statsd {
44
yro2b0f8862017-11-06 14:27:31 -080045// for StatsLogReport
Yangster-macd1815dc2017-11-13 21:43:15 -080046const int FIELD_ID_NAME = 1;
yro2b0f8862017-11-06 14:27:31 -080047const int FIELD_ID_START_REPORT_NANOS = 2;
48const int FIELD_ID_END_REPORT_NANOS = 3;
49const int FIELD_ID_GAUGE_METRICS = 8;
50// for GaugeMetricDataWrapper
51const int FIELD_ID_DATA = 1;
52// for GaugeMetricData
53const int FIELD_ID_DIMENSION = 1;
54const int FIELD_ID_BUCKET_INFO = 2;
55// for KeyValuePair
56const int FIELD_ID_KEY = 1;
57const int FIELD_ID_VALUE_STR = 2;
58const int FIELD_ID_VALUE_INT = 3;
59const int FIELD_ID_VALUE_BOOL = 4;
60const int FIELD_ID_VALUE_FLOAT = 5;
61// for GaugeBucketInfo
62const int FIELD_ID_START_BUCKET_NANOS = 1;
63const int FIELD_ID_END_BUCKET_NANOS = 2;
64const int FIELD_ID_GAUGE = 3;
65
Yao Chenb3561512017-11-21 18:07:17 -080066GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
67 const int conditionIndex,
Yangster-mace2cd6d52017-11-09 20:38:30 -080068 const sp<ConditionWizard>& wizard, const int pullTagId,
69 const int64_t startTimeNs)
Yao Chenb3561512017-11-21 18:07:17 -080070 : MetricProducer(key, startTimeNs, conditionIndex, wizard),
Yangster1d4d6862017-10-31 12:58:51 -070071 mMetric(metric),
72 mPullTagId(pullTagId) {
73 if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
74 mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
75 } else {
76 mBucketSizeNs = kDefaultGaugemBucketSizeNs;
77 }
78
79 // TODO: use UidMap if uid->pkg_name is required
80 mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
81
82 if (metric.links().size() > 0) {
83 mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
84 metric.links().end());
85 mConditionSliced = true;
86 }
87
88 // Kicks off the puller immediately.
89 if (mPullTagId != -1) {
90 mStatsPullerManager.RegisterReceiver(mPullTagId, this,
91 metric.bucket().bucket_size_millis());
92 }
93
yro2b0f8862017-11-06 14:27:31 -080094 startNewProtoOutputStream(mStartTimeNs);
95
Yangster-macd1815dc2017-11-13 21:43:15 -080096 VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
Yangster1d4d6862017-10-31 12:58:51 -070097 (long long)mBucketSizeNs, (long long)mStartTimeNs);
98}
99
100GaugeMetricProducer::~GaugeMetricProducer() {
101 VLOG("~GaugeMetricProducer() called");
102}
103
yro2b0f8862017-11-06 14:27:31 -0800104void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) {
105 mProto = std::make_unique<ProtoOutputStream>();
Yangster-macd1815dc2017-11-13 21:43:15 -0800106 mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
yro2b0f8862017-11-06 14:27:31 -0800107 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
108 mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
Yangster1d4d6862017-10-31 12:58:51 -0700109}
110
yro2b0f8862017-11-06 14:27:31 -0800111void GaugeMetricProducer::finish() {
Yangster1d4d6862017-10-31 12:58:51 -0700112}
113
yro17adac92017-11-08 23:16:29 -0800114std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
Yangster-macd1815dc2017-11-13 21:43:15 -0800115 VLOG("gauge metric %s dump report now...", mMetric.name().c_str());
Yangster1d4d6862017-10-31 12:58:51 -0700116
Yangster1d4d6862017-10-31 12:58:51 -0700117 // Dump current bucket if it's stale.
118 // If current bucket is still on-going, don't force dump current bucket.
119 // In finish(), We can force dump current bucket.
Yangster-mace2cd6d52017-11-09 20:38:30 -0800120 flushIfNeeded(time(nullptr) * NS_PER_SEC);
Yangster1d4d6862017-10-31 12:58:51 -0700121
122 for (const auto& pair : mPastBuckets) {
123 const HashableDimensionKey& hashableKey = pair.first;
124 auto it = mDimensionKeyMap.find(hashableKey);
125 if (it == mDimensionKeyMap.end()) {
126 ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
127 continue;
128 }
129
130 VLOG(" dimension key %s", hashableKey.c_str());
yrob0378b02017-11-09 20:36:25 -0800131 long long wrapperToken =
132 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
yro2b0f8862017-11-06 14:27:31 -0800133
134 // First fill dimension (KeyValuePairs).
135 for (const auto& kv : it->second) {
yrob0378b02017-11-09 20:36:25 -0800136 long long dimensionToken =
137 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
yro2b0f8862017-11-06 14:27:31 -0800138 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
139 if (kv.has_value_str()) {
Yao Chen1ff4f432017-11-16 17:01:40 -0800140 mProto->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
yro2b0f8862017-11-06 14:27:31 -0800141 } else if (kv.has_value_int()) {
142 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
143 } else if (kv.has_value_bool()) {
144 mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
145 } else if (kv.has_value_float()) {
146 mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
147 }
148 mProto->end(dimensionToken);
149 }
150
151 // Then fill bucket_info (GaugeBucketInfo).
152 for (const auto& bucket : pair.second) {
yrob0378b02017-11-09 20:36:25 -0800153 long long bucketInfoToken =
154 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
yro2b0f8862017-11-06 14:27:31 -0800155 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
156 (long long)bucket.mBucketStartNs);
157 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
158 (long long)bucket.mBucketEndNs);
159 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_GAUGE, (long long)bucket.mGauge);
160 mProto->end(bucketInfoToken);
161 VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
162 (long long)bucket.mBucketEndNs, (long long)bucket.mGauge);
163 }
164 mProto->end(wrapperToken);
Yangster1d4d6862017-10-31 12:58:51 -0700165 }
yro2b0f8862017-11-06 14:27:31 -0800166 mProto->end(mProtoToken);
167 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
168 (long long)mCurrentBucketStartTimeNs);
169
yro17adac92017-11-08 23:16:29 -0800170 std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
yro2b0f8862017-11-06 14:27:31 -0800171
172 startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
173 mPastBuckets.clear();
yro2b0f8862017-11-06 14:27:31 -0800174
yro17adac92017-11-08 23:16:29 -0800175 return buffer;
yro2b0f8862017-11-06 14:27:31 -0800176
177 // TODO: Clear mDimensionKeyMap once the report is dumped.
Yangster1d4d6862017-10-31 12:58:51 -0700178}
179
180void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
181 AutoMutex _l(mLock);
Yangster-macd1815dc2017-11-13 21:43:15 -0800182 VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
Yangster-mace2cd6d52017-11-09 20:38:30 -0800183 flushIfNeeded(eventTime);
Yangster1d4d6862017-10-31 12:58:51 -0700184 mCondition = conditionMet;
185
Yangster-mace2cd6d52017-11-09 20:38:30 -0800186 // Push mode. No need to proactively pull the gauge data.
Yangster1d4d6862017-10-31 12:58:51 -0700187 if (mPullTagId == -1) {
188 return;
189 }
Yangster-mace2cd6d52017-11-09 20:38:30 -0800190 if (!mCondition) {
191 return;
192 }
193 // Already have gauge metric for the current bucket, do not do it again.
194 if (mCurrentSlicedBucket->size() > 0) {
Yangster1d4d6862017-10-31 12:58:51 -0700195 return;
196 }
197 vector<std::shared_ptr<LogEvent>> allData;
198 if (!mStatsPullerManager.Pull(mPullTagId, &allData)) {
199 ALOGE("Stats puller failed for tag: %d", mPullTagId);
200 return;
201 }
202 for (const auto& data : allData) {
203 onMatchedLogEvent(0, *data, false /*scheduledPull*/);
204 }
Yangster-mace2cd6d52017-11-09 20:38:30 -0800205 flushIfNeeded(eventTime);
Yangster1d4d6862017-10-31 12:58:51 -0700206}
207
208void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
Yangster-macd1815dc2017-11-13 21:43:15 -0800209 VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
Yangster1d4d6862017-10-31 12:58:51 -0700210}
211
Yangster-mace2cd6d52017-11-09 20:38:30 -0800212int64_t GaugeMetricProducer::getGauge(const LogEvent& event) {
Yangster1d4d6862017-10-31 12:58:51 -0700213 status_t err = NO_ERROR;
Yangster-mace2cd6d52017-11-09 20:38:30 -0800214 int64_t val = event.GetLong(mMetric.gauge_field(), &err);
Yangster1d4d6862017-10-31 12:58:51 -0700215 if (err == NO_ERROR) {
216 return val;
217 } else {
218 VLOG("Can't find value in message.");
219 return -1;
220 }
221}
222
223void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
224 AutoMutex mutex(mLock);
Yangster1d4d6862017-10-31 12:58:51 -0700225 for (const auto& data : allData) {
226 onMatchedLogEvent(0, *data, true /*scheduledPull*/);
227 }
Yangster1d4d6862017-10-31 12:58:51 -0700228}
229
Yao Chenb3561512017-11-21 18:07:17 -0800230bool GaugeMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
231 if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
232 return false;
233 }
234 // 1. Report the tuple count if the tuple count > soft limit
235 if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
236 size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
237 StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
238 newTupleCount);
239 // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
240 if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
241 ALOGE("GaugeMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
242 newKey.c_str());
243 return true;
244 }
245 }
246
247 return false;
248}
249
Yangster1d4d6862017-10-31 12:58:51 -0700250void GaugeMetricProducer::onMatchedLogEventInternal(
251 const size_t matcherIndex, const HashableDimensionKey& eventKey,
252 const map<string, HashableDimensionKey>& conditionKey, bool condition,
253 const LogEvent& event, bool scheduledPull) {
254 if (condition == false) {
255 return;
256 }
257 uint64_t eventTimeNs = event.GetTimestampNs();
258 if (eventTimeNs < mCurrentBucketStartTimeNs) {
259 VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
260 (long long)mCurrentBucketStartTimeNs);
261 return;
262 }
263
Yangster-mace2cd6d52017-11-09 20:38:30 -0800264 // When the event happens in a new bucket, flush the old buckets.
265 if (eventTimeNs >= mCurrentBucketStartTimeNs + mBucketSizeNs) {
266 flushIfNeeded(eventTimeNs);
267 }
268
Yao Chenb3561512017-11-21 18:07:17 -0800269 // For gauge metric, we just simply use the first gauge in the given bucket.
Yangster-mace2cd6d52017-11-09 20:38:30 -0800270 if (!mCurrentSlicedBucket->empty()) {
Yangster1d4d6862017-10-31 12:58:51 -0700271 return;
272 }
Yangster-mace2cd6d52017-11-09 20:38:30 -0800273 const long gauge = getGauge(event);
274 if (gauge >= 0) {
Yao Chenb3561512017-11-21 18:07:17 -0800275 if (hitGuardRail(eventKey)) {
276 return;
277 }
Yangster-mace2cd6d52017-11-09 20:38:30 -0800278 (*mCurrentSlicedBucket)[eventKey] = gauge;
279 }
280 for (auto& tracker : mAnomalyTrackers) {
281 tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, gauge);
Yangster1d4d6862017-10-31 12:58:51 -0700282 }
283}
284
285// When a new matched event comes in, we check if event falls into the current
286// bucket. If not, flush the old counter to past buckets and initialize the new
287// bucket.
288// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
289// the GaugeMetricProducer while holding the lock.
Yangster-mace2cd6d52017-11-09 20:38:30 -0800290void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
291 if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
Yangster1d4d6862017-10-31 12:58:51 -0700292 return;
293 }
294
yro2b0f8862017-11-06 14:27:31 -0800295 GaugeBucket info;
296 info.mBucketStartNs = mCurrentBucketStartTimeNs;
297 info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
Yangster-mace2cd6d52017-11-09 20:38:30 -0800298 info.mBucketNum = mCurrentBucketNum;
Yangster1d4d6862017-10-31 12:58:51 -0700299
Yangster-mace2cd6d52017-11-09 20:38:30 -0800300 for (const auto& slice : *mCurrentSlicedBucket) {
yro2b0f8862017-11-06 14:27:31 -0800301 info.mGauge = slice.second;
Yangster1d4d6862017-10-31 12:58:51 -0700302 auto& bucketList = mPastBuckets[slice.first];
303 bucketList.push_back(info);
Yangster-mace2cd6d52017-11-09 20:38:30 -0800304 VLOG("gauge metric %s, dump key value: %s -> %lld", mMetric.name().c_str(),
305 slice.first.c_str(), (long long)slice.second);
Yangster1d4d6862017-10-31 12:58:51 -0700306 }
Yangster1d4d6862017-10-31 12:58:51 -0700307
Yangster-mace2cd6d52017-11-09 20:38:30 -0800308 // Reset counters
309 for (auto& tracker : mAnomalyTrackers) {
310 tracker->addPastBucket(mCurrentSlicedBucket, mCurrentBucketNum);
311 }
312
313 mCurrentSlicedBucket = std::make_shared<DimToValMap>();
314
315 // Adjusts the bucket start time
316 int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
Yangster1d4d6862017-10-31 12:58:51 -0700317 mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
Yangster-mace2cd6d52017-11-09 20:38:30 -0800318 mCurrentBucketNum += numBucketsForward;
Yangster-macd1815dc2017-11-13 21:43:15 -0800319 VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
Yangster1d4d6862017-10-31 12:58:51 -0700320 (long long)mCurrentBucketStartTimeNs);
321}
322
Yangster7c334a12017-11-22 14:24:24 -0800323size_t GaugeMetricProducer::byteSize() const {
Yangster-mace2cd6d52017-11-09 20:38:30 -0800324 size_t totalSize = 0;
325 for (const auto& pair : mPastBuckets) {
326 totalSize += pair.second.size() * kBucketSize;
327 }
328 return totalSize;
yro2b0f8862017-11-06 14:27:31 -0800329}
330
Yangster1d4d6862017-10-31 12:58:51 -0700331} // namespace statsd
332} // namespace os
333} // namespace android