blob: 94f4adf11c7917aa226ad40e66bf3f0354b4cb79 [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
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.
64CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
Yao Chen93fe3a32017-11-02 13:52:59 -070065 const sp<ConditionWizard>& wizard,
66 const uint64_t startTimeNs)
67 : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
Yao Chen44cf27c2017-09-14 22:32:50 -070068 // TODO: evaluate initial conditions. and set mConditionMet.
69 if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
Yao Chen729093d2017-10-16 10:33:26 -070070 mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
Yao Chen44cf27c2017-09-14 22:32:50 -070071 } else {
Yao Chen729093d2017-10-16 10:33:26 -070072 mBucketSizeNs = LLONG_MAX;
Yao Chen44cf27c2017-09-14 22:32:50 -070073 }
74
Bookatzd3606c72017-10-19 10:13:49 -070075 mAnomalyTrackers.reserve(metric.alerts_size());
76 for (int i = 0; i < metric.alerts_size(); i++) {
77 const Alert& alert = metric.alerts(i);
78 if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) {
79 mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert));
80 } else {
81 ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d",
82 alert.trigger_if_sum_gt(), alert.number_of_buckets());
83 }
84 }
85
Yao Chen729093d2017-10-16 10:33:26 -070086 // TODO: use UidMap if uid->pkg_name is required
87 mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
88
89 if (metric.links().size() > 0) {
90 mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
91 metric.links().end());
92 mConditionSliced = true;
93 }
94
yro24809bd2017-10-31 23:06:53 -070095 startNewProtoOutputStream(mStartTimeNs);
96
Yao Chen729093d2017-10-16 10:33:26 -070097 VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
98 (long long)mBucketSizeNs, (long long)mStartTimeNs);
Yao Chen44cf27c2017-09-14 22:32:50 -070099}
100
Yao Chen44cf27c2017-09-14 22:32:50 -0700101CountMetricProducer::~CountMetricProducer() {
102 VLOG("~CountMetricProducer() called");
103}
104
yro24809bd2017-10-31 23:06:53 -0700105void CountMetricProducer::startNewProtoOutputStream(long long startTime) {
106 mProto = std::make_unique<ProtoOutputStream>();
107 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
108 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
109 mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
Yao Chen44cf27c2017-09-14 22:32:50 -0700110}
111
yro24809bd2017-10-31 23:06:53 -0700112void CountMetricProducer::finish() {
Yao Chen729093d2017-10-16 10:33:26 -0700113}
114
Yao Chen5154a372017-10-30 22:57:06 -0700115void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700116 VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
117}
118
119StatsLogReport CountMetricProducer::onDumpReport() {
Yao Chen93fe3a32017-11-02 13:52:59 -0700120 long long endTime = time(nullptr) * NS_PER_SEC;
Yao Chen729093d2017-10-16 10:33:26 -0700121
122 // Dump current bucket if it's stale.
123 // If current bucket is still on-going, don't force dump current bucket.
124 // In finish(), We can force dump current bucket.
yro24809bd2017-10-31 23:06:53 -0700125 flushCounterIfNeeded(endTime);
Yao Chen93fe3a32017-11-02 13:52:59 -0700126 VLOG("metric %lld dump report now...", mMetric.metric_id());
Yao Chen729093d2017-10-16 10:33:26 -0700127
Yao Chen93fe3a32017-11-02 13:52:59 -0700128 for (const auto& counter : mPastBuckets) {
yro24809bd2017-10-31 23:06:53 -0700129 const HashableDimensionKey& hashableKey = counter.first;
Yao Chen93fe3a32017-11-02 13:52:59 -0700130 VLOG(" dimension key %s", hashableKey.c_str());
Yao Chen729093d2017-10-16 10:33:26 -0700131 auto it = mDimensionKeyMap.find(hashableKey);
132 if (it == mDimensionKeyMap.end()) {
133 ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
134 continue;
135 }
yro24809bd2017-10-31 23:06:53 -0700136 long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
Yao Chen729093d2017-10-16 10:33:26 -0700137
yro24809bd2017-10-31 23:06:53 -0700138 // First fill dimension (KeyValuePairs).
139 for (const auto& kv : it->second) {
140 long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
141 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
142 if (kv.has_value_str()) {
143 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
144 } else if (kv.has_value_int()) {
145 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
146 } else if (kv.has_value_bool()) {
147 mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
148 } else if (kv.has_value_float()) {
149 mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
150 }
151 mProto->end(dimensionToken);
152 }
153
154 // Then fill bucket_info (CountBucketInfo).
Yao Chen93fe3a32017-11-02 13:52:59 -0700155 for (const auto& bucket : counter.second) {
156 long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
157 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
158 (long long)bucket.mBucketStartNs);
159 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
160 (long long)bucket.mBucketEndNs);
161 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
162 mProto->end(bucketInfoToken);
163 VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
164 (long long)bucket.mBucketEndNs, (long long)bucket.mCount);
yro24809bd2017-10-31 23:06:53 -0700165 }
yro24809bd2017-10-31 23:06:53 -0700166 mProto->end(wrapperToken);
Yao Chen729093d2017-10-16 10:33:26 -0700167 }
yro24809bd2017-10-31 23:06:53 -0700168
169 mProto->end(mProtoToken);
170 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
171 (long long)mCurrentBucketStartTimeNs);
172
yro2b0f8862017-11-06 14:27:31 -0800173 VLOG("metric %lld dump report now...", mMetric.metric_id());
174 std::unique_ptr<uint8_t[]> buffer = serializeProto();
yro24809bd2017-10-31 23:06:53 -0700175
176 startNewProtoOutputStream(endTime);
Yao Chen93fe3a32017-11-02 13:52:59 -0700177 mPastBuckets.clear();
yro24809bd2017-10-31 23:06:53 -0700178 mByteSize = 0;
179
180 // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
181 // return std::move(buffer);
182 return StatsLogReport();
183
184 // TODO: Clear mDimensionKeyMap once the report is dumped.
Yao Chen44cf27c2017-09-14 22:32:50 -0700185}
186
Yao Chen5154a372017-10-30 22:57:06 -0700187void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700188 VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
Yao Chencaf339d2017-10-06 16:01:10 -0700189 mCondition = conditionMet;
190}
191
Yao Chenb7041772017-10-20 16:59:25 -0700192void CountMetricProducer::onMatchedLogEventInternal(
193 const size_t matcherIndex, const HashableDimensionKey& eventKey,
194 const map<string, HashableDimensionKey>& conditionKey, bool condition,
Chenjie Yub3dda412017-10-24 13:41:59 -0700195 const LogEvent& event, bool scheduledPull) {
Yao Chen729093d2017-10-16 10:33:26 -0700196 uint64_t eventTimeNs = event.GetTimestampNs();
Yao Chen44cf27c2017-09-14 22:32:50 -0700197
Yao Chen729093d2017-10-16 10:33:26 -0700198 flushCounterIfNeeded(eventTimeNs);
199
Yao Chenb7041772017-10-20 16:59:25 -0700200 if (condition == false) {
201 return;
Yao Chen44cf27c2017-09-14 22:32:50 -0700202 }
Yao Chen729093d2017-10-16 10:33:26 -0700203
Yao Chenb7041772017-10-20 16:59:25 -0700204 auto it = mCurrentSlicedCounter.find(eventKey);
Yao Chen729093d2017-10-16 10:33:26 -0700205
206 if (it == mCurrentSlicedCounter.end()) {
207 // create a counter for the new key
Yao Chenb7041772017-10-20 16:59:25 -0700208 mCurrentSlicedCounter[eventKey] = 1;
Yao Chen729093d2017-10-16 10:33:26 -0700209
210 } else {
211 // increment the existing value
212 auto& count = it->second;
213 count++;
214 }
215
Bookatzd3606c72017-10-19 10:13:49 -0700216 // TODO: Re-add anomaly detection (similar to):
217 // for (auto& tracker : mAnomalyTrackers) {
218 // tracker->checkAnomaly(mCounter);
219 // }
220
Yao Chenb7041772017-10-20 16:59:25 -0700221 VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
222 mCurrentSlicedCounter[eventKey]);
Yao Chen44cf27c2017-09-14 22:32:50 -0700223}
224
Yao Chen729093d2017-10-16 10:33:26 -0700225// When a new matched event comes in, we check if event falls into the current
226// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
227void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
228 if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
Yao Chen44cf27c2017-09-14 22:32:50 -0700229 return;
230 }
231
Yao Chen44cf27c2017-09-14 22:32:50 -0700232 // adjust the bucket start time
Yao Chen729093d2017-10-16 10:33:26 -0700233 int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
Bookatza4bc9c42017-10-04 11:45:57 -0700234
Yao Chen93fe3a32017-11-02 13:52:59 -0700235 CountBucket info;
236 info.mBucketStartNs = mCurrentBucketStartTimeNs;
237 info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
Yao Chen729093d2017-10-16 10:33:26 -0700238 for (const auto& counter : mCurrentSlicedCounter) {
Yao Chen93fe3a32017-11-02 13:52:59 -0700239 info.mCount = counter.second;
240 auto& bucketList = mPastBuckets[counter.first];
241 bucketList.push_back(info);
Yao Chen729093d2017-10-16 10:33:26 -0700242 VLOG("metric %lld, dump key value: %s -> %d", mMetric.metric_id(), counter.first.c_str(),
243 counter.second);
Yao Chen93fe3a32017-11-02 13:52:59 -0700244 mByteSize += sizeof(info);
Yao Chen729093d2017-10-16 10:33:26 -0700245 }
246
Bookatzd3606c72017-10-19 10:13:49 -0700247 // TODO: Re-add anomaly detection (similar to):
248 // for (auto& tracker : mAnomalyTrackers) {
249 // tracker->addPastBucket(mCounter, numBucketsForward);
250 //}
251
Yao Chen729093d2017-10-16 10:33:26 -0700252 // Reset counters
253 mCurrentSlicedCounter.clear();
254
255 mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
256 VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
257 (long long)mCurrentBucketStartTimeNs);
Yao Chen44cf27c2017-09-14 22:32:50 -0700258}
259
yro24809bd2017-10-31 23:06:53 -0700260// Rough estimate of CountMetricProducer buffer stored. This number will be
261// greater than actual data size as it contains each dimension of
262// CountMetricData is duplicated.
yro69007c82017-10-26 20:42:57 -0700263size_t CountMetricProducer::byteSize() {
yro24809bd2017-10-31 23:06:53 -0700264 return mByteSize;
yro69007c82017-10-26 20:42:57 -0700265}
266
Yao Chen44cf27c2017-09-14 22:32:50 -0700267} // namespace statsd
268} // namespace os
yro69007c82017-10-26 20:42:57 -0700269} // namespace android