blob: 545951b736af78841501c984124544dcffe6844b [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 Chen44cf27c2017-09-14 22:32:50 -070017#define DEBUG true // STOPSHIP if true
Joe Onorato9fc9edf2017-10-15 20:08:52 -070018#include "Log.h"
Yao Chen44cf27c2017-09-14 22:32:50 -070019
Yang Lu3eba6212017-10-25 19:54:45 -070020#include "../anomaly/DiscreteAnomalyTracker.h"
Yao Chen729093d2017-10-16 10:33:26 -070021#include "CountMetricProducer.h"
22#include "stats_util.h"
Yao Chen44cf27c2017-09-14 22:32:50 -070023
Yao Chen44cf27c2017-09-14 22:32:50 -070024#include <limits.h>
25#include <stdlib.h>
26
yro2b0f8862017-11-06 14:27:31 -080027using android::util::FIELD_TYPE_BOOL;
28using android::util::FIELD_TYPE_FLOAT;
29using android::util::FIELD_TYPE_INT32;
30using android::util::FIELD_TYPE_INT64;
31using android::util::FIELD_TYPE_MESSAGE;
yro24809bd2017-10-31 23:06:53 -070032using android::util::ProtoOutputStream;
Yao Chen729093d2017-10-16 10:33:26 -070033using std::map;
34using std::string;
Yao Chen44cf27c2017-09-14 22:32:50 -070035using std::unordered_map;
Yao Chen729093d2017-10-16 10:33:26 -070036using std::vector;
Yao Chen44cf27c2017-09-14 22:32:50 -070037
38namespace android {
39namespace os {
40namespace statsd {
41
yro24809bd2017-10-31 23:06:53 -070042// for StatsLogReport
43const int FIELD_ID_METRIC_ID = 1;
44const int FIELD_ID_START_REPORT_NANOS = 2;
45const int FIELD_ID_END_REPORT_NANOS = 3;
46const int FIELD_ID_COUNT_METRICS = 5;
47// for CountMetricDataWrapper
48const int FIELD_ID_DATA = 1;
49// for CountMetricData
50const int FIELD_ID_DIMENSION = 1;
51const int FIELD_ID_BUCKET_INFO = 2;
52// for KeyValuePair
53const int FIELD_ID_KEY = 1;
54const int FIELD_ID_VALUE_STR = 2;
55const int FIELD_ID_VALUE_INT = 3;
56const int FIELD_ID_VALUE_BOOL = 4;
57const int FIELD_ID_VALUE_FLOAT = 5;
58// for CountBucketInfo
59const int FIELD_ID_START_BUCKET_NANOS = 1;
60const int FIELD_ID_END_BUCKET_NANOS = 2;
61const int FIELD_ID_COUNT = 3;
62
Yao Chen729093d2017-10-16 10:33:26 -070063// TODO: add back AnomalyTracker.
Yang Lu3eba6212017-10-25 19:54:45 -070064
Yao Chen729093d2017-10-16 10:33:26 -070065CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
Yao Chen93fe3a32017-11-02 13:52:59 -070066 const sp<ConditionWizard>& wizard,
67 const uint64_t startTimeNs)
68 : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
Yao Chen44cf27c2017-09-14 22:32:50 -070069 // TODO: evaluate initial conditions. and set mConditionMet.
70 if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
Yao Chen729093d2017-10-16 10:33:26 -070071 mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
Yao Chen44cf27c2017-09-14 22:32:50 -070072 } else {
Yao Chen729093d2017-10-16 10:33:26 -070073 mBucketSizeNs = LLONG_MAX;
Yao Chen44cf27c2017-09-14 22:32:50 -070074 }
75
Bookatzd3606c72017-10-19 10:13:49 -070076 mAnomalyTrackers.reserve(metric.alerts_size());
77 for (int i = 0; i < metric.alerts_size(); i++) {
78 const Alert& alert = metric.alerts(i);
79 if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) {
Yang Lu3eba6212017-10-25 19:54:45 -070080 mAnomalyTrackers.push_back(std::make_unique<DiscreteAnomalyTracker>(alert));
Bookatzd3606c72017-10-19 10:13:49 -070081 } else {
82 ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d",
83 alert.trigger_if_sum_gt(), alert.number_of_buckets());
84 }
85 }
86
Yao Chen729093d2017-10-16 10:33:26 -070087 // TODO: use UidMap if uid->pkg_name is required
88 mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
89
90 if (metric.links().size() > 0) {
91 mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
92 metric.links().end());
93 mConditionSliced = true;
94 }
95
yro24809bd2017-10-31 23:06:53 -070096 startNewProtoOutputStream(mStartTimeNs);
97
Yao Chen729093d2017-10-16 10:33:26 -070098 VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
99 (long long)mBucketSizeNs, (long long)mStartTimeNs);
Yao Chen44cf27c2017-09-14 22:32:50 -0700100}
101
Yao Chen44cf27c2017-09-14 22:32:50 -0700102CountMetricProducer::~CountMetricProducer() {
103 VLOG("~CountMetricProducer() called");
104}
105
yro24809bd2017-10-31 23:06:53 -0700106void CountMetricProducer::startNewProtoOutputStream(long long startTime) {
107 mProto = std::make_unique<ProtoOutputStream>();
108 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
109 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
110 mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
Yao Chen44cf27c2017-09-14 22:32:50 -0700111}
112
yro24809bd2017-10-31 23:06:53 -0700113void CountMetricProducer::finish() {
Yao Chen729093d2017-10-16 10:33:26 -0700114}
115
Yao Chen5154a372017-10-30 22:57:06 -0700116void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700117 VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
118}
119
120StatsLogReport CountMetricProducer::onDumpReport() {
Yao Chen93fe3a32017-11-02 13:52:59 -0700121 long long endTime = time(nullptr) * NS_PER_SEC;
Yao Chen729093d2017-10-16 10:33:26 -0700122
123 // Dump current bucket if it's stale.
124 // If current bucket is still on-going, don't force dump current bucket.
125 // In finish(), We can force dump current bucket.
yro24809bd2017-10-31 23:06:53 -0700126 flushCounterIfNeeded(endTime);
Yao Chen93fe3a32017-11-02 13:52:59 -0700127 VLOG("metric %lld dump report now...", mMetric.metric_id());
Yao Chen729093d2017-10-16 10:33:26 -0700128
Yao Chen93fe3a32017-11-02 13:52:59 -0700129 for (const auto& counter : mPastBuckets) {
yro24809bd2017-10-31 23:06:53 -0700130 const HashableDimensionKey& hashableKey = counter.first;
Yao Chen93fe3a32017-11-02 13:52:59 -0700131 VLOG(" dimension key %s", hashableKey.c_str());
Yao Chen729093d2017-10-16 10:33:26 -0700132 auto it = mDimensionKeyMap.find(hashableKey);
133 if (it == mDimensionKeyMap.end()) {
134 ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
135 continue;
136 }
yro24809bd2017-10-31 23:06:53 -0700137 long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
Yao Chen729093d2017-10-16 10:33:26 -0700138
yro24809bd2017-10-31 23:06:53 -0700139 // First fill dimension (KeyValuePairs).
140 for (const auto& kv : it->second) {
141 long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
142 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
143 if (kv.has_value_str()) {
144 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
145 } else if (kv.has_value_int()) {
146 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
147 } else if (kv.has_value_bool()) {
148 mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
149 } else if (kv.has_value_float()) {
150 mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
151 }
152 mProto->end(dimensionToken);
153 }
154
155 // Then fill bucket_info (CountBucketInfo).
Yao Chen93fe3a32017-11-02 13:52:59 -0700156 for (const auto& bucket : counter.second) {
157 long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
158 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
159 (long long)bucket.mBucketStartNs);
160 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
161 (long long)bucket.mBucketEndNs);
162 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
163 mProto->end(bucketInfoToken);
164 VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
165 (long long)bucket.mBucketEndNs, (long long)bucket.mCount);
yro24809bd2017-10-31 23:06:53 -0700166 }
yro24809bd2017-10-31 23:06:53 -0700167 mProto->end(wrapperToken);
Yao Chen729093d2017-10-16 10:33:26 -0700168 }
yro24809bd2017-10-31 23:06:53 -0700169
170 mProto->end(mProtoToken);
171 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
172 (long long)mCurrentBucketStartTimeNs);
173
yro2b0f8862017-11-06 14:27:31 -0800174 VLOG("metric %lld dump report now...", mMetric.metric_id());
175 std::unique_ptr<uint8_t[]> buffer = serializeProto();
yro24809bd2017-10-31 23:06:53 -0700176
177 startNewProtoOutputStream(endTime);
Yao Chen93fe3a32017-11-02 13:52:59 -0700178 mPastBuckets.clear();
yro24809bd2017-10-31 23:06:53 -0700179 mByteSize = 0;
180
181 // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
182 // return std::move(buffer);
183 return StatsLogReport();
184
185 // TODO: Clear mDimensionKeyMap once the report is dumped.
Yao Chen44cf27c2017-09-14 22:32:50 -0700186}
187
Yao Chen5154a372017-10-30 22:57:06 -0700188void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700189 VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
Yao Chencaf339d2017-10-06 16:01:10 -0700190 mCondition = conditionMet;
191}
192
Yao Chenb7041772017-10-20 16:59:25 -0700193void CountMetricProducer::onMatchedLogEventInternal(
194 const size_t matcherIndex, const HashableDimensionKey& eventKey,
195 const map<string, HashableDimensionKey>& conditionKey, bool condition,
Chenjie Yub3dda412017-10-24 13:41:59 -0700196 const LogEvent& event, bool scheduledPull) {
Yao Chen729093d2017-10-16 10:33:26 -0700197 uint64_t eventTimeNs = event.GetTimestampNs();
Yao Chen44cf27c2017-09-14 22:32:50 -0700198
Yao Chen729093d2017-10-16 10:33:26 -0700199 flushCounterIfNeeded(eventTimeNs);
200
Yao Chenb7041772017-10-20 16:59:25 -0700201 if (condition == false) {
202 return;
Yao Chen44cf27c2017-09-14 22:32:50 -0700203 }
Yao Chen729093d2017-10-16 10:33:26 -0700204
Yang Lu3eba6212017-10-25 19:54:45 -0700205 auto it = mCurrentSlicedCounter->find(eventKey);
Yao Chen729093d2017-10-16 10:33:26 -0700206
Yang Lu3eba6212017-10-25 19:54:45 -0700207 if (it == mCurrentSlicedCounter->end()) {
Yao Chen729093d2017-10-16 10:33:26 -0700208 // create a counter for the new key
Yang Lu3eba6212017-10-25 19:54:45 -0700209 (*mCurrentSlicedCounter)[eventKey] = 1;
Yao Chen729093d2017-10-16 10:33:26 -0700210 } else {
211 // increment the existing value
212 auto& count = it->second;
213 count++;
214 }
215
Yao Chenb7041772017-10-20 16:59:25 -0700216 VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
Yang Lu3eba6212017-10-25 19:54:45 -0700217 (*mCurrentSlicedCounter)[eventKey]);
Yao Chen44cf27c2017-09-14 22:32:50 -0700218}
219
Yao Chen729093d2017-10-16 10:33:26 -0700220// When a new matched event comes in, we check if event falls into the current
221// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
222void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
223 if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
Yao Chen44cf27c2017-09-14 22:32:50 -0700224 return;
225 }
226
Yao Chen44cf27c2017-09-14 22:32:50 -0700227 // adjust the bucket start time
Yang Lu3eba6212017-10-25 19:54:45 -0700228 // TODO: This (and addPastBucket to which it goes) doesn't really need to be an int64.
229 uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
Bookatza4bc9c42017-10-04 11:45:57 -0700230
Yao Chen93fe3a32017-11-02 13:52:59 -0700231 CountBucket info;
232 info.mBucketStartNs = mCurrentBucketStartTimeNs;
233 info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
Yang Lu3eba6212017-10-25 19:54:45 -0700234 for (const auto& counter : *mCurrentSlicedCounter) {
Yao Chen93fe3a32017-11-02 13:52:59 -0700235 info.mCount = counter.second;
236 auto& bucketList = mPastBuckets[counter.first];
237 bucketList.push_back(info);
Yao Chen729093d2017-10-16 10:33:26 -0700238 VLOG("metric %lld, dump key value: %s -> %d", mMetric.metric_id(), counter.first.c_str(),
239 counter.second);
Yao Chen93fe3a32017-11-02 13:52:59 -0700240 mByteSize += sizeof(info);
Yao Chen729093d2017-10-16 10:33:26 -0700241 }
242
Yang Lu3eba6212017-10-25 19:54:45 -0700243 for (auto& tracker : mAnomalyTrackers) {
244 tracker->addOrUpdateBucket(mCurrentSlicedCounter, mCurrentBucketNum);
245 tracker->declareAndDeclareAnomaly();
246 }
Bookatzd3606c72017-10-19 10:13:49 -0700247
Yang Lu3eba6212017-10-25 19:54:45 -0700248 // Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers).
249 mCurrentSlicedCounter = std::make_shared<DimToValMap>();
Yao Chen729093d2017-10-16 10:33:26 -0700250
251 mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
Yang Lu3eba6212017-10-25 19:54:45 -0700252 mCurrentBucketNum += numBucketsForward;
Yao Chen729093d2017-10-16 10:33:26 -0700253 VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
254 (long long)mCurrentBucketStartTimeNs);
Yao Chen44cf27c2017-09-14 22:32:50 -0700255}
256
yro24809bd2017-10-31 23:06:53 -0700257// Rough estimate of CountMetricProducer buffer stored. This number will be
258// greater than actual data size as it contains each dimension of
259// CountMetricData is duplicated.
yro69007c82017-10-26 20:42:57 -0700260size_t CountMetricProducer::byteSize() {
yro24809bd2017-10-31 23:06:53 -0700261 return mByteSize;
yro69007c82017-10-26 20:42:57 -0700262}
263
Yao Chen44cf27c2017-09-14 22:32:50 -0700264} // namespace statsd
265} // namespace os
yro69007c82017-10-26 20:42:57 -0700266} // namespace android