blob: a24364df0fb22539fea58e3dd12eec651a663324 [file] [log] [blame]
Yao Chen44cf27c2017-09-14 22:32:50 -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
Yao Chen3c0b95c2017-12-16 14:34:20 -080017#define DEBUG false // STOPSHIP if true
Joe Onorato9fc9edf2017-10-15 20:08:52 -070018#include "Log.h"
Yao Chen44cf27c2017-09-14 22:32:50 -070019
Yao Chen729093d2017-10-16 10:33:26 -070020#include "CountMetricProducer.h"
Yao Chenb3561512017-11-21 18:07:17 -080021#include "guardrail/StatsdStats.h"
Yao Chen729093d2017-10-16 10:33:26 -070022#include "stats_util.h"
Yangster-mac20877162017-12-22 17:19:39 -080023#include "stats_log_util.h"
Yao Chen44cf27c2017-09-14 22:32:50 -070024
Yao Chen44cf27c2017-09-14 22:32:50 -070025#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;
yro24809bd2017-10-31 23:06:53 -070035using android::util::ProtoOutputStream;
Yao Chen729093d2017-10-16 10:33:26 -070036using std::map;
37using std::string;
Yao Chen44cf27c2017-09-14 22:32:50 -070038using std::unordered_map;
Yao Chen729093d2017-10-16 10:33:26 -070039using std::vector;
Yao Chen44cf27c2017-09-14 22:32:50 -070040
41namespace android {
42namespace os {
43namespace statsd {
44
yro24809bd2017-10-31 23:06:53 -070045// for StatsLogReport
Yangster-mac94e197c2018-01-02 16:03:03 -080046const int FIELD_ID_ID = 1;
yro24809bd2017-10-31 23:06:53 -070047const int FIELD_ID_START_REPORT_NANOS = 2;
48const int FIELD_ID_END_REPORT_NANOS = 3;
49const int FIELD_ID_COUNT_METRICS = 5;
50// for CountMetricDataWrapper
51const int FIELD_ID_DATA = 1;
52// for CountMetricData
53const int FIELD_ID_DIMENSION = 1;
54const int FIELD_ID_BUCKET_INFO = 2;
yro24809bd2017-10-31 23:06:53 -070055// for CountBucketInfo
56const int FIELD_ID_START_BUCKET_NANOS = 1;
57const int FIELD_ID_END_BUCKET_NANOS = 2;
58const int FIELD_ID_COUNT = 3;
59
Yao Chenb3561512017-11-21 18:07:17 -080060CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
61 const int conditionIndex,
Yao Chen93fe3a32017-11-02 13:52:59 -070062 const sp<ConditionWizard>& wizard,
63 const uint64_t startTimeNs)
Yangster-mac94e197c2018-01-02 16:03:03 -080064 : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
Yao Chen44cf27c2017-09-14 22:32:50 -070065 // TODO: evaluate initial conditions. and set mConditionMet.
66 if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
Yao Chen729093d2017-10-16 10:33:26 -070067 mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
Yao Chen44cf27c2017-09-14 22:32:50 -070068 } else {
Yao Chen729093d2017-10-16 10:33:26 -070069 mBucketSizeNs = LLONG_MAX;
Yao Chen44cf27c2017-09-14 22:32:50 -070070 }
71
Yao Chen729093d2017-10-16 10:33:26 -070072 // TODO: use UidMap if uid->pkg_name is required
Yangster-mac20877162017-12-22 17:19:39 -080073 mDimensions = metric.dimensions();
Yao Chen729093d2017-10-16 10:33:26 -070074
75 if (metric.links().size() > 0) {
76 mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
77 metric.links().end());
78 mConditionSliced = true;
79 }
80
Yangster-mac94e197c2018-01-02 16:03:03 -080081 VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
Yao Chen729093d2017-10-16 10:33:26 -070082 (long long)mBucketSizeNs, (long long)mStartTimeNs);
Yao Chen44cf27c2017-09-14 22:32:50 -070083}
84
Yao Chen44cf27c2017-09-14 22:32:50 -070085CountMetricProducer::~CountMetricProducer() {
86 VLOG("~CountMetricProducer() called");
87}
88
Yangsterf2bee6f2017-11-29 12:01:05 -080089void CountMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
Yangster-mac94e197c2018-01-02 16:03:03 -080090 VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
Yao Chen729093d2017-10-16 10:33:26 -070091}
92
Yangster-mac20877162017-12-22 17:19:39 -080093void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
94 flushIfNeededLocked(dumpTimeNs);
Yangster-mac94e197c2018-01-02 16:03:03 -080095 report->set_metric_id(mMetricId);
Yangster-mac20877162017-12-22 17:19:39 -080096 report->set_start_report_nanos(mStartTimeNs);
97
98 auto count_metrics = report->mutable_count_metrics();
99 for (const auto& counter : mPastBuckets) {
100 CountMetricData* metricData = count_metrics->add_data();
101 *metricData->mutable_dimension() = counter.first.getDimensionsValue();
102 for (const auto& bucket : counter.second) {
103 CountBucketInfo* bucketInfo = metricData->add_bucket_info();
104 bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
105 bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs);
106 bucketInfo->set_count(bucket.mCount);
107 }
108 }
109}
110
Yao Chen288c6002017-12-12 13:43:18 -0800111void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
112 ProtoOutputStream* protoOutput) {
113 flushIfNeededLocked(dumpTimeNs);
Yao Chen6a8c7992017-11-29 20:02:07 +0000114
Yangster-mac94e197c2018-01-02 16:03:03 -0800115 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
Yao Chen288c6002017-12-12 13:43:18 -0800116 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
117 long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
118
Yangster-mac94e197c2018-01-02 16:03:03 -0800119 VLOG("metric %lld dump report now...",(long long)mMetricId);
Yao Chen729093d2017-10-16 10:33:26 -0700120
Yao Chen93fe3a32017-11-02 13:52:59 -0700121 for (const auto& counter : mPastBuckets) {
yro24809bd2017-10-31 23:06:53 -0700122 const HashableDimensionKey& hashableKey = counter.first;
Yao Chen93fe3a32017-11-02 13:52:59 -0700123 VLOG(" dimension key %s", hashableKey.c_str());
Yao Chend5aa01b32017-12-19 16:46:36 -0800124
yrob0378b02017-11-09 20:36:25 -0800125 long long wrapperToken =
Yao Chen288c6002017-12-12 13:43:18 -0800126 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
Yao Chen729093d2017-10-16 10:33:26 -0700127
Yangster-mac20877162017-12-22 17:19:39 -0800128 // First fill dimension.
129 long long dimensionToken = protoOutput->start(
130 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
131 writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
132 protoOutput->end(dimensionToken);
yro24809bd2017-10-31 23:06:53 -0700133
134 // Then fill bucket_info (CountBucketInfo).
Yao Chen93fe3a32017-11-02 13:52:59 -0700135 for (const auto& bucket : counter.second) {
Yao Chen288c6002017-12-12 13:43:18 -0800136 long long bucketInfoToken = protoOutput->start(
137 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
138 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
139 (long long)bucket.mBucketStartNs);
140 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
141 (long long)bucket.mBucketEndNs);
142 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
143 protoOutput->end(bucketInfoToken);
Yao Chen93fe3a32017-11-02 13:52:59 -0700144 VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
145 (long long)bucket.mBucketEndNs, (long long)bucket.mCount);
yro24809bd2017-10-31 23:06:53 -0700146 }
Yao Chen288c6002017-12-12 13:43:18 -0800147 protoOutput->end(wrapperToken);
Yao Chen729093d2017-10-16 10:33:26 -0700148 }
Yao Chen6a8c7992017-11-29 20:02:07 +0000149
Yao Chen288c6002017-12-12 13:43:18 -0800150 protoOutput->end(protoToken);
151 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
yro24809bd2017-10-31 23:06:53 -0700152
Yao Chen6a8c7992017-11-29 20:02:07 +0000153 mPastBuckets.clear();
Yao Chen288c6002017-12-12 13:43:18 -0800154 mStartTimeNs = mCurrentBucketStartTimeNs;
Yao Chen6a8c7992017-11-29 20:02:07 +0000155
156 // TODO: Clear mDimensionKeyMap once the report is dumped.
Yao Chen44cf27c2017-09-14 22:32:50 -0700157}
158
Yangsterf2bee6f2017-11-29 12:01:05 -0800159void CountMetricProducer::onConditionChangedLocked(const bool conditionMet,
160 const uint64_t eventTime) {
Yangster-mac94e197c2018-01-02 16:03:03 -0800161 VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
Yao Chencaf339d2017-10-06 16:01:10 -0700162 mCondition = conditionMet;
163}
164
Yangsterf2bee6f2017-11-29 12:01:05 -0800165bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
Yao Chenb3561512017-11-21 18:07:17 -0800166 if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
167 return false;
168 }
169 // ===========GuardRail==============
170 // 1. Report the tuple count if the tuple count > soft limit
171 if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
172 size_t newTupleCount = mCurrentSlicedCounter->size() + 1;
Yangster-mac94e197c2018-01-02 16:03:03 -0800173 StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
Yao Chenb3561512017-11-21 18:07:17 -0800174 // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
175 if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
Yangster-mac94e197c2018-01-02 16:03:03 -0800176 ALOGE("CountMetric %lld dropping data for dimension key %s",
177 (long long)mMetricId, newKey.c_str());
Yao Chenb3561512017-11-21 18:07:17 -0800178 return true;
179 }
180 }
181
182 return false;
183}
Yangsterf2bee6f2017-11-29 12:01:05 -0800184
185void CountMetricProducer::onMatchedLogEventInternalLocked(
Yao Chenb7041772017-10-20 16:59:25 -0700186 const size_t matcherIndex, const HashableDimensionKey& eventKey,
Yangster-mac20877162017-12-22 17:19:39 -0800187 const ConditionKey& conditionKey, bool condition,
Chenjie Yua7259ab2017-12-10 08:31:05 -0800188 const LogEvent& event) {
Yao Chen729093d2017-10-16 10:33:26 -0700189 uint64_t eventTimeNs = event.GetTimestampNs();
Yao Chen44cf27c2017-09-14 22:32:50 -0700190
Yangsterf2bee6f2017-11-29 12:01:05 -0800191 flushIfNeededLocked(eventTimeNs);
Yao Chen729093d2017-10-16 10:33:26 -0700192
Yao Chen6a8c7992017-11-29 20:02:07 +0000193 if (condition == false) {
Yao Chenb7041772017-10-20 16:59:25 -0700194 return;
Yao Chen44cf27c2017-09-14 22:32:50 -0700195 }
Yao Chen729093d2017-10-16 10:33:26 -0700196
Yao Chen6a8c7992017-11-29 20:02:07 +0000197 auto it = mCurrentSlicedCounter->find(eventKey);
198
199 if (it == mCurrentSlicedCounter->end()) {
200 // ===========GuardRail==============
Yangsterf2bee6f2017-11-29 12:01:05 -0800201 if (hitGuardRailLocked(eventKey)) {
Yao Chenb3561512017-11-21 18:07:17 -0800202 return;
203 }
204
Yao Chen6a8c7992017-11-29 20:02:07 +0000205 // create a counter for the new key
206 (*mCurrentSlicedCounter)[eventKey] = 1;
207 } else {
208 // increment the existing value
209 auto& count = it->second;
210 count++;
Yao Chen729093d2017-10-16 10:33:26 -0700211 }
Yao Chen6a8c7992017-11-29 20:02:07 +0000212
213 for (auto& tracker : mAnomalyTrackers) {
214 tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
215 mCurrentSlicedCounter->find(eventKey)->second);
216 }
217
Yangster-mac94e197c2018-01-02 16:03:03 -0800218 VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.c_str(),
Yao Chen6a8c7992017-11-29 20:02:07 +0000219 (long long)(*mCurrentSlicedCounter)[eventKey]);
Yao Chen44cf27c2017-09-14 22:32:50 -0700220}
221
Yao Chen729093d2017-10-16 10:33:26 -0700222// When a new matched event comes in, we check if event falls into the current
223// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
Yangsterf2bee6f2017-11-29 12:01:05 -0800224void CountMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) {
Yangster-mace2cd6d52017-11-09 20:38:30 -0800225 if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
Yao Chen44cf27c2017-09-14 22:32:50 -0700226 return;
227 }
228
Yao Chen93fe3a32017-11-02 13:52:59 -0700229 CountBucket info;
230 info.mBucketStartNs = mCurrentBucketStartTimeNs;
231 info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
Yangster-mace2cd6d52017-11-09 20:38:30 -0800232 info.mBucketNum = mCurrentBucketNum;
Yang Lu3eba6212017-10-25 19:54:45 -0700233 for (const auto& counter : *mCurrentSlicedCounter) {
Yao Chen93fe3a32017-11-02 13:52:59 -0700234 info.mCount = counter.second;
235 auto& bucketList = mPastBuckets[counter.first];
236 bucketList.push_back(info);
Yangster-mac94e197c2018-01-02 16:03:03 -0800237 VLOG("metric %lld, dump key value: %s -> %lld",
238 (long long)mMetricId, counter.first.c_str(), (long long)counter.second);
Yao Chen729093d2017-10-16 10:33:26 -0700239 }
240
Yang Lu3eba6212017-10-25 19:54:45 -0700241 for (auto& tracker : mAnomalyTrackers) {
Yangster-mace2cd6d52017-11-09 20:38:30 -0800242 tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum);
Yang Lu3eba6212017-10-25 19:54:45 -0700243 }
Bookatzd3606c72017-10-19 10:13:49 -0700244
Yang Lu3eba6212017-10-25 19:54:45 -0700245 // Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers).
246 mCurrentSlicedCounter = std::make_shared<DimToValMap>();
Yangster-mace2cd6d52017-11-09 20:38:30 -0800247 uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
Yao Chen729093d2017-10-16 10:33:26 -0700248 mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
Yang Lu3eba6212017-10-25 19:54:45 -0700249 mCurrentBucketNum += numBucketsForward;
Yangster-mac94e197c2018-01-02 16:03:03 -0800250 VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
Yao Chen729093d2017-10-16 10:33:26 -0700251 (long long)mCurrentBucketStartTimeNs);
Yao Chen44cf27c2017-09-14 22:32:50 -0700252}
253
yro24809bd2017-10-31 23:06:53 -0700254// Rough estimate of CountMetricProducer buffer stored. This number will be
255// greater than actual data size as it contains each dimension of
256// CountMetricData is duplicated.
Yangsterf2bee6f2017-11-29 12:01:05 -0800257size_t CountMetricProducer::byteSizeLocked() const {
Yangster-mace2cd6d52017-11-09 20:38:30 -0800258 size_t totalSize = 0;
259 for (const auto& pair : mPastBuckets) {
260 totalSize += pair.second.size() * kBucketSize;
261 }
262 return totalSize;
yro69007c82017-10-26 20:42:57 -0700263}
264
Yao Chen44cf27c2017-09-14 22:32:50 -0700265} // namespace statsd
266} // namespace os
yro69007c82017-10-26 20:42:57 -0700267} // namespace android