blob: 100a7a4489dd1f0bf15d88508f2b51f99e30b1f9 [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,
Yao Chen93fe3a32017-11-02 13:52:59 -070061 const sp<ConditionWizard>& wizard,
62 const uint64_t startTimeNs)
63 : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
Yao Chen44cf27c2017-09-14 22:32:50 -070064 // TODO: evaluate initial conditions. and set mConditionMet.
65 if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
Yao Chen729093d2017-10-16 10:33:26 -070066 mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
Yao Chen44cf27c2017-09-14 22:32:50 -070067 } else {
Yao Chen729093d2017-10-16 10:33:26 -070068 mBucketSizeNs = LLONG_MAX;
Yao Chen44cf27c2017-09-14 22:32:50 -070069 }
70
Bookatzd3606c72017-10-19 10:13:49 -070071 mAnomalyTrackers.reserve(metric.alerts_size());
72 for (int i = 0; i < metric.alerts_size(); i++) {
73 const Alert& alert = metric.alerts(i);
74 if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) {
75 mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert));
76 } else {
77 ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d",
78 alert.trigger_if_sum_gt(), alert.number_of_buckets());
79 }
80 }
81
Yao Chen729093d2017-10-16 10:33:26 -070082 // TODO: use UidMap if uid->pkg_name is required
83 mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
84
85 if (metric.links().size() > 0) {
86 mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
87 metric.links().end());
88 mConditionSliced = true;
89 }
90
yro24809bd2017-10-31 23:06:53 -070091 startNewProtoOutputStream(mStartTimeNs);
92
Yao Chen729093d2017-10-16 10:33:26 -070093 VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
94 (long long)mBucketSizeNs, (long long)mStartTimeNs);
Yao Chen44cf27c2017-09-14 22:32:50 -070095}
96
Yao Chen44cf27c2017-09-14 22:32:50 -070097CountMetricProducer::~CountMetricProducer() {
98 VLOG("~CountMetricProducer() called");
99}
100
yro24809bd2017-10-31 23:06:53 -0700101void CountMetricProducer::startNewProtoOutputStream(long long startTime) {
102 mProto = std::make_unique<ProtoOutputStream>();
103 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
104 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
105 mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
Yao Chen44cf27c2017-09-14 22:32:50 -0700106}
107
yro24809bd2017-10-31 23:06:53 -0700108void CountMetricProducer::finish() {
Yao Chen729093d2017-10-16 10:33:26 -0700109}
110
Yao Chen5154a372017-10-30 22:57:06 -0700111void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700112 VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
113}
114
115StatsLogReport CountMetricProducer::onDumpReport() {
Yao Chen93fe3a32017-11-02 13:52:59 -0700116 long long endTime = time(nullptr) * NS_PER_SEC;
Yao Chen729093d2017-10-16 10:33:26 -0700117
118 // Dump current bucket if it's stale.
119 // If current bucket is still on-going, don't force dump current bucket.
120 // In finish(), We can force dump current bucket.
yro24809bd2017-10-31 23:06:53 -0700121 flushCounterIfNeeded(endTime);
Yao Chen93fe3a32017-11-02 13:52:59 -0700122 VLOG("metric %lld dump report now...", mMetric.metric_id());
Yao Chen729093d2017-10-16 10:33:26 -0700123
Yao Chen93fe3a32017-11-02 13:52:59 -0700124 for (const auto& counter : mPastBuckets) {
yro24809bd2017-10-31 23:06:53 -0700125 const HashableDimensionKey& hashableKey = counter.first;
Yao Chen93fe3a32017-11-02 13:52:59 -0700126 VLOG(" dimension key %s", hashableKey.c_str());
Yao Chen729093d2017-10-16 10:33:26 -0700127 auto it = mDimensionKeyMap.find(hashableKey);
128 if (it == mDimensionKeyMap.end()) {
129 ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
130 continue;
131 }
yro24809bd2017-10-31 23:06:53 -0700132 long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
Yao Chen729093d2017-10-16 10:33:26 -0700133
yro24809bd2017-10-31 23:06:53 -0700134 // First fill dimension (KeyValuePairs).
135 for (const auto& kv : it->second) {
136 long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
137 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
138 if (kv.has_value_str()) {
139 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
140 } else if (kv.has_value_int()) {
141 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
142 } else if (kv.has_value_bool()) {
143 mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
144 } else if (kv.has_value_float()) {
145 mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
146 }
147 mProto->end(dimensionToken);
148 }
149
150 // Then fill bucket_info (CountBucketInfo).
Yao Chen93fe3a32017-11-02 13:52:59 -0700151 for (const auto& bucket : counter.second) {
152 long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
153 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
154 (long long)bucket.mBucketStartNs);
155 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
156 (long long)bucket.mBucketEndNs);
157 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
158 mProto->end(bucketInfoToken);
159 VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
160 (long long)bucket.mBucketEndNs, (long long)bucket.mCount);
yro24809bd2017-10-31 23:06:53 -0700161 }
yro24809bd2017-10-31 23:06:53 -0700162 mProto->end(wrapperToken);
Yao Chen729093d2017-10-16 10:33:26 -0700163 }
yro24809bd2017-10-31 23:06:53 -0700164
165 mProto->end(mProtoToken);
166 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
167 (long long)mCurrentBucketStartTimeNs);
168
169 size_t bufferSize = mProto->size();
yro24809bd2017-10-31 23:06:53 -0700170 std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]);
Yao Chen93fe3a32017-11-02 13:52:59 -0700171
yro24809bd2017-10-31 23:06:53 -0700172 size_t pos = 0;
173 auto it = mProto->data();
174 while (it.readBuffer() != NULL) {
175 size_t toRead = it.currentToRead();
176 std::memcpy(&buffer[pos], it.readBuffer(), toRead);
177 pos += toRead;
178 it.rp()->move(toRead);
179 }
180
181 startNewProtoOutputStream(endTime);
Yao Chen93fe3a32017-11-02 13:52:59 -0700182 mPastBuckets.clear();
yro24809bd2017-10-31 23:06:53 -0700183 mByteSize = 0;
184
185 // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
186 // return std::move(buffer);
187 return StatsLogReport();
188
189 // TODO: Clear mDimensionKeyMap once the report is dumped.
Yao Chen44cf27c2017-09-14 22:32:50 -0700190}
191
Yao Chen5154a372017-10-30 22:57:06 -0700192void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700193 VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
Yao Chencaf339d2017-10-06 16:01:10 -0700194 mCondition = conditionMet;
195}
196
Yao Chenb7041772017-10-20 16:59:25 -0700197void CountMetricProducer::onMatchedLogEventInternal(
198 const size_t matcherIndex, const HashableDimensionKey& eventKey,
199 const map<string, HashableDimensionKey>& conditionKey, bool condition,
Chenjie Yub3dda412017-10-24 13:41:59 -0700200 const LogEvent& event, bool scheduledPull) {
Yao Chen729093d2017-10-16 10:33:26 -0700201 uint64_t eventTimeNs = event.GetTimestampNs();
Yao Chen44cf27c2017-09-14 22:32:50 -0700202
Yao Chen729093d2017-10-16 10:33:26 -0700203 flushCounterIfNeeded(eventTimeNs);
204
Yao Chenb7041772017-10-20 16:59:25 -0700205 if (condition == false) {
206 return;
Yao Chen44cf27c2017-09-14 22:32:50 -0700207 }
Yao Chen729093d2017-10-16 10:33:26 -0700208
Yao Chenb7041772017-10-20 16:59:25 -0700209 auto it = mCurrentSlicedCounter.find(eventKey);
Yao Chen729093d2017-10-16 10:33:26 -0700210
211 if (it == mCurrentSlicedCounter.end()) {
212 // create a counter for the new key
Yao Chenb7041772017-10-20 16:59:25 -0700213 mCurrentSlicedCounter[eventKey] = 1;
Yao Chen729093d2017-10-16 10:33:26 -0700214
215 } else {
216 // increment the existing value
217 auto& count = it->second;
218 count++;
219 }
220
Bookatzd3606c72017-10-19 10:13:49 -0700221 // TODO: Re-add anomaly detection (similar to):
222 // for (auto& tracker : mAnomalyTrackers) {
223 // tracker->checkAnomaly(mCounter);
224 // }
225
Yao Chenb7041772017-10-20 16:59:25 -0700226 VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
227 mCurrentSlicedCounter[eventKey]);
Yao Chen44cf27c2017-09-14 22:32:50 -0700228}
229
Yao Chen729093d2017-10-16 10:33:26 -0700230// When a new matched event comes in, we check if event falls into the current
231// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
232void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
233 if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
Yao Chen44cf27c2017-09-14 22:32:50 -0700234 return;
235 }
236
Yao Chen44cf27c2017-09-14 22:32:50 -0700237 // adjust the bucket start time
Yao Chen729093d2017-10-16 10:33:26 -0700238 int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
Bookatza4bc9c42017-10-04 11:45:57 -0700239
Yao Chen93fe3a32017-11-02 13:52:59 -0700240 CountBucket info;
241 info.mBucketStartNs = mCurrentBucketStartTimeNs;
242 info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
Yao Chen729093d2017-10-16 10:33:26 -0700243 for (const auto& counter : mCurrentSlicedCounter) {
Yao Chen93fe3a32017-11-02 13:52:59 -0700244 info.mCount = counter.second;
245 auto& bucketList = mPastBuckets[counter.first];
246 bucketList.push_back(info);
Yao Chen729093d2017-10-16 10:33:26 -0700247 VLOG("metric %lld, dump key value: %s -> %d", mMetric.metric_id(), counter.first.c_str(),
248 counter.second);
Yao Chen93fe3a32017-11-02 13:52:59 -0700249 mByteSize += sizeof(info);
Yao Chen729093d2017-10-16 10:33:26 -0700250 }
251
Bookatzd3606c72017-10-19 10:13:49 -0700252 // TODO: Re-add anomaly detection (similar to):
253 // for (auto& tracker : mAnomalyTrackers) {
254 // tracker->addPastBucket(mCounter, numBucketsForward);
255 //}
256
Yao Chen729093d2017-10-16 10:33:26 -0700257 // Reset counters
258 mCurrentSlicedCounter.clear();
259
260 mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
261 VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
262 (long long)mCurrentBucketStartTimeNs);
Yao Chen44cf27c2017-09-14 22:32:50 -0700263}
264
yro24809bd2017-10-31 23:06:53 -0700265// Rough estimate of CountMetricProducer buffer stored. This number will be
266// greater than actual data size as it contains each dimension of
267// CountMetricData is duplicated.
yro69007c82017-10-26 20:42:57 -0700268size_t CountMetricProducer::byteSize() {
yro24809bd2017-10-31 23:06:53 -0700269 return mByteSize;
yro69007c82017-10-26 20:42:57 -0700270}
271
Yao Chen44cf27c2017-09-14 22:32:50 -0700272} // namespace statsd
273} // namespace os
yro69007c82017-10-26 20:42:57 -0700274} // namespace android