blob: 71cb7717d2d743a60c9de75691bc250a712d6887 [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
Bookatza4bc9c42017-10-04 11:45:57 -070020#include "CountAnomalyTracker.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
yro24809bd2017-10-31 23:06:53 -070027using namespace android::util;
28using android::util::ProtoOutputStream;
Yao Chen729093d2017-10-16 10:33:26 -070029using std::map;
30using std::string;
Yao Chen44cf27c2017-09-14 22:32:50 -070031using std::unordered_map;
Yao Chen729093d2017-10-16 10:33:26 -070032using std::vector;
Yao Chen44cf27c2017-09-14 22:32:50 -070033
34namespace android {
35namespace os {
36namespace statsd {
37
yro24809bd2017-10-31 23:06:53 -070038// for StatsLogReport
39const int FIELD_ID_METRIC_ID = 1;
40const int FIELD_ID_START_REPORT_NANOS = 2;
41const int FIELD_ID_END_REPORT_NANOS = 3;
42const int FIELD_ID_COUNT_METRICS = 5;
43// for CountMetricDataWrapper
44const int FIELD_ID_DATA = 1;
45// for CountMetricData
46const int FIELD_ID_DIMENSION = 1;
47const int FIELD_ID_BUCKET_INFO = 2;
48// for KeyValuePair
49const int FIELD_ID_KEY = 1;
50const int FIELD_ID_VALUE_STR = 2;
51const int FIELD_ID_VALUE_INT = 3;
52const int FIELD_ID_VALUE_BOOL = 4;
53const int FIELD_ID_VALUE_FLOAT = 5;
54// for CountBucketInfo
55const int FIELD_ID_START_BUCKET_NANOS = 1;
56const int FIELD_ID_END_BUCKET_NANOS = 2;
57const int FIELD_ID_COUNT = 3;
58
Yao Chen729093d2017-10-16 10:33:26 -070059// TODO: add back AnomalyTracker.
60CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
61 const sp<ConditionWizard>& wizard)
62 // TODO: Pass in the start time from MetricsManager, instead of calling time() here.
63 : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard),
Bookatzd3606c72017-10-19 10:13:49 -070064 mMetric(metric) {
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
Bookatzd3606c72017-10-19 10:13:49 -070072 mAnomalyTrackers.reserve(metric.alerts_size());
73 for (int i = 0; i < metric.alerts_size(); i++) {
74 const Alert& alert = metric.alerts(i);
75 if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) {
76 mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert));
77 } else {
78 ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d",
79 alert.trigger_if_sum_gt(), alert.number_of_buckets());
80 }
81 }
82
Yao Chen729093d2017-10-16 10:33:26 -070083 // TODO: use UidMap if uid->pkg_name is required
84 mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
85
86 if (metric.links().size() > 0) {
87 mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
88 metric.links().end());
89 mConditionSliced = true;
90 }
91
yro24809bd2017-10-31 23:06:53 -070092 startNewProtoOutputStream(mStartTimeNs);
93
Yao Chen729093d2017-10-16 10:33:26 -070094 VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
95 (long long)mBucketSizeNs, (long long)mStartTimeNs);
Yao Chen44cf27c2017-09-14 22:32:50 -070096}
97
Yao Chen44cf27c2017-09-14 22:32:50 -070098CountMetricProducer::~CountMetricProducer() {
99 VLOG("~CountMetricProducer() called");
100}
101
yro24809bd2017-10-31 23:06:53 -0700102void CountMetricProducer::startNewProtoOutputStream(long long startTime) {
103 mProto = std::make_unique<ProtoOutputStream>();
104 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
105 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
106 mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
Yao Chen44cf27c2017-09-14 22:32:50 -0700107}
108
yro24809bd2017-10-31 23:06:53 -0700109void CountMetricProducer::finish() {
Yao Chen729093d2017-10-16 10:33:26 -0700110}
111
Yao Chen5154a372017-10-30 22:57:06 -0700112void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700113 VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
114}
115
116StatsLogReport CountMetricProducer::onDumpReport() {
yro24809bd2017-10-31 23:06:53 -0700117 long long endTime = time(nullptr) * NANO_SECONDS_IN_A_SECOND;
Yao Chen729093d2017-10-16 10:33:26 -0700118
119 // Dump current bucket if it's stale.
120 // If current bucket is still on-going, don't force dump current bucket.
121 // In finish(), We can force dump current bucket.
yro24809bd2017-10-31 23:06:53 -0700122 flushCounterIfNeeded(endTime);
Yao Chen729093d2017-10-16 10:33:26 -0700123
yro24809bd2017-10-31 23:06:53 -0700124 for (const auto& counter : mPastBucketProtos) {
125 const HashableDimensionKey& hashableKey = counter.first;
Yao Chen729093d2017-10-16 10:33:26 -0700126 auto it = mDimensionKeyMap.find(hashableKey);
127 if (it == mDimensionKeyMap.end()) {
128 ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
129 continue;
130 }
yro24809bd2017-10-31 23:06:53 -0700131 long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
Yao Chen729093d2017-10-16 10:33:26 -0700132
yro24809bd2017-10-31 23:06:53 -0700133 // First fill dimension (KeyValuePairs).
134 for (const auto& kv : it->second) {
135 long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
136 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
137 if (kv.has_value_str()) {
138 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
139 } else if (kv.has_value_int()) {
140 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
141 } else if (kv.has_value_bool()) {
142 mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
143 } else if (kv.has_value_float()) {
144 mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
145 }
146 mProto->end(dimensionToken);
147 }
148
149 // Then fill bucket_info (CountBucketInfo).
150 for (const auto& proto : counter.second) {
151 size_t bufferSize = proto->size();
152 char* buffer(new char[bufferSize]);
153 size_t pos = 0;
154 auto it = proto->data();
155 while (it.readBuffer() != NULL) {
156 size_t toRead = it.currentToRead();
157 std::memcpy(&buffer[pos], it.readBuffer(), toRead);
158 pos += toRead;
159 it.rp()->move(toRead);
160 }
161 mProto->write(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION, buffer, bufferSize);
162 }
163
164 mProto->end(wrapperToken);
Yao Chen729093d2017-10-16 10:33:26 -0700165 }
yro24809bd2017-10-31 23:06:53 -0700166
167 mProto->end(mProtoToken);
168 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
169 (long long)mCurrentBucketStartTimeNs);
170
171 size_t bufferSize = mProto->size();
172 VLOG("metric %lld dump report now...", mMetric.metric_id());
173 std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]);
174 size_t pos = 0;
175 auto it = mProto->data();
176 while (it.readBuffer() != NULL) {
177 size_t toRead = it.currentToRead();
178 std::memcpy(&buffer[pos], it.readBuffer(), toRead);
179 pos += toRead;
180 it.rp()->move(toRead);
181 }
182
183 startNewProtoOutputStream(endTime);
184 mPastBucketProtos.clear();
185 mByteSize = 0;
186
187 // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
188 // return std::move(buffer);
189 return StatsLogReport();
190
191 // TODO: Clear mDimensionKeyMap once the report is dumped.
Yao Chen44cf27c2017-09-14 22:32:50 -0700192}
193
Yao Chen5154a372017-10-30 22:57:06 -0700194void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700195 VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
Yao Chencaf339d2017-10-06 16:01:10 -0700196 mCondition = conditionMet;
197}
198
Yao Chenb7041772017-10-20 16:59:25 -0700199void CountMetricProducer::onMatchedLogEventInternal(
200 const size_t matcherIndex, const HashableDimensionKey& eventKey,
201 const map<string, HashableDimensionKey>& conditionKey, bool condition,
Chenjie Yub3dda412017-10-24 13:41:59 -0700202 const LogEvent& event, bool scheduledPull) {
Yao Chen729093d2017-10-16 10:33:26 -0700203 uint64_t eventTimeNs = event.GetTimestampNs();
Yao Chen44cf27c2017-09-14 22:32:50 -0700204
Yao Chen729093d2017-10-16 10:33:26 -0700205 flushCounterIfNeeded(eventTimeNs);
206
Yao Chenb7041772017-10-20 16:59:25 -0700207 if (condition == false) {
208 return;
Yao Chen44cf27c2017-09-14 22:32:50 -0700209 }
Yao Chen729093d2017-10-16 10:33:26 -0700210
Yao Chenb7041772017-10-20 16:59:25 -0700211 auto it = mCurrentSlicedCounter.find(eventKey);
Yao Chen729093d2017-10-16 10:33:26 -0700212
213 if (it == mCurrentSlicedCounter.end()) {
214 // create a counter for the new key
Yao Chenb7041772017-10-20 16:59:25 -0700215 mCurrentSlicedCounter[eventKey] = 1;
Yao Chen729093d2017-10-16 10:33:26 -0700216
217 } else {
218 // increment the existing value
219 auto& count = it->second;
220 count++;
221 }
222
Bookatzd3606c72017-10-19 10:13:49 -0700223 // TODO: Re-add anomaly detection (similar to):
224 // for (auto& tracker : mAnomalyTrackers) {
225 // tracker->checkAnomaly(mCounter);
226 // }
227
Yao Chenb7041772017-10-20 16:59:25 -0700228 VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
229 mCurrentSlicedCounter[eventKey]);
Yao Chen44cf27c2017-09-14 22:32:50 -0700230}
231
Yao Chen729093d2017-10-16 10:33:26 -0700232// When a new matched event comes in, we check if event falls into the current
233// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
234void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
235 if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
Yao Chen44cf27c2017-09-14 22:32:50 -0700236 return;
237 }
238
Yao Chen44cf27c2017-09-14 22:32:50 -0700239 // adjust the bucket start time
Yao Chen729093d2017-10-16 10:33:26 -0700240 int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
Bookatza4bc9c42017-10-04 11:45:57 -0700241
Yao Chen729093d2017-10-16 10:33:26 -0700242 for (const auto& counter : mCurrentSlicedCounter) {
yro24809bd2017-10-31 23:06:53 -0700243 unique_ptr<ProtoOutputStream> proto = make_unique<ProtoOutputStream>();
244 proto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
245 (long long)mCurrentBucketStartTimeNs);
246 proto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
247 (long long)mCurrentBucketStartTimeNs + mBucketSizeNs);
248 proto->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)counter.second);
249
250 auto& bucketList = mPastBucketProtos[counter.first];
251 bucketList.push_back(std::move(proto));
252 mByteSize += proto->size();
Yao Chen44cf27c2017-09-14 22:32:50 -0700253
Yao Chen729093d2017-10-16 10:33:26 -0700254 VLOG("metric %lld, dump key value: %s -> %d", mMetric.metric_id(), counter.first.c_str(),
255 counter.second);
256 }
257
Bookatzd3606c72017-10-19 10:13:49 -0700258 // TODO: Re-add anomaly detection (similar to):
259 // for (auto& tracker : mAnomalyTrackers) {
260 // tracker->addPastBucket(mCounter, numBucketsForward);
261 //}
262
Yao Chen729093d2017-10-16 10:33:26 -0700263 // Reset counters
264 mCurrentSlicedCounter.clear();
265
266 mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
267 VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
268 (long long)mCurrentBucketStartTimeNs);
Yao Chen44cf27c2017-09-14 22:32:50 -0700269}
270
yro24809bd2017-10-31 23:06:53 -0700271// Rough estimate of CountMetricProducer buffer stored. This number will be
272// greater than actual data size as it contains each dimension of
273// CountMetricData is duplicated.
yro69007c82017-10-26 20:42:57 -0700274size_t CountMetricProducer::byteSize() {
yro24809bd2017-10-31 23:06:53 -0700275 return mByteSize;
yro69007c82017-10-26 20:42:57 -0700276}
277
Yao Chen44cf27c2017-09-14 22:32:50 -0700278} // namespace statsd
279} // namespace os
yro69007c82017-10-26 20:42:57 -0700280} // namespace android