blob: 74ce9cd6e4544ae77c2e3d2363b3444000f3c566 [file] [log] [blame]
Yao Chen729093d2017-10-16 10:33:26 -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
17#define DEBUG true
Yao Chen5154a372017-10-30 22:57:06 -070018
Yao Chen729093d2017-10-16 10:33:26 -070019#include "Log.h"
Yao Chen5154a372017-10-30 22:57:06 -070020#include "DurationMetricProducer.h"
Yao Chen729093d2017-10-16 10:33:26 -070021#include "stats_util.h"
22
Yao Chen729093d2017-10-16 10:33:26 -070023#include <limits.h>
24#include <stdlib.h>
25
yrob0378b02017-11-09 20:36:25 -080026using android::util::FIELD_COUNT_REPEATED;
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;
32using android::util::ProtoOutputStream;
Yao Chen729093d2017-10-16 10:33:26 -070033using std::string;
34using std::unordered_map;
35using std::vector;
36
37namespace android {
38namespace os {
39namespace statsd {
40
yro2b0f8862017-11-06 14:27:31 -080041// for StatsLogReport
42const int FIELD_ID_METRIC_ID = 1;
43const int FIELD_ID_START_REPORT_NANOS = 2;
44const int FIELD_ID_END_REPORT_NANOS = 3;
45const int FIELD_ID_DURATION_METRICS = 6;
46// for DurationMetricDataWrapper
47const int FIELD_ID_DATA = 1;
48// for DurationMetricData
49const int FIELD_ID_DIMENSION = 1;
50const int FIELD_ID_BUCKET_INFO = 2;
51// for KeyValuePair
52const int FIELD_ID_KEY = 1;
53const int FIELD_ID_VALUE_STR = 2;
54const int FIELD_ID_VALUE_INT = 3;
55const int FIELD_ID_VALUE_BOOL = 4;
56const int FIELD_ID_VALUE_FLOAT = 5;
57// for DurationBucketInfo
58const int FIELD_ID_START_BUCKET_NANOS = 1;
59const int FIELD_ID_END_BUCKET_NANOS = 2;
60const int FIELD_ID_DURATION = 3;
61
Yao Chen729093d2017-10-16 10:33:26 -070062DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
63 const int conditionIndex, const size_t startIndex,
64 const size_t stopIndex, const size_t stopAllIndex,
Yao Chen5154a372017-10-30 22:57:06 -070065 const sp<ConditionWizard>& wizard,
Yao Chen93fe3a32017-11-02 13:52:59 -070066 const vector<KeyMatcher>& internalDimension,
67 const uint64_t startTimeNs)
68 : MetricProducer(startTimeNs, conditionIndex, wizard),
Yao Chen729093d2017-10-16 10:33:26 -070069 mMetric(metric),
70 mStartIndex(startIndex),
71 mStopIndex(stopIndex),
Yao Chen5154a372017-10-30 22:57:06 -070072 mStopAllIndex(stopAllIndex),
73 mInternalDimension(internalDimension) {
Yao Chen729093d2017-10-16 10:33:26 -070074 // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
75 // them in the base class, because the proto generated CountMetric, and DurationMetric are
76 // not related. Maybe we should add a template in the future??
77 if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
78 mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000000;
79 } else {
80 mBucketSizeNs = LLONG_MAX;
81 }
82
83 // 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
yro2b0f8862017-11-06 14:27:31 -080092 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);
96}
97
98DurationMetricProducer::~DurationMetricProducer() {
99 VLOG("~DurationMetric() called");
100}
101
yro2b0f8862017-11-06 14:27:31 -0800102void DurationMetricProducer::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_DURATION_METRICS);
107}
108
Yao Chen5154a372017-10-30 22:57:06 -0700109unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
yro2b0f8862017-11-06 14:27:31 -0800110 vector<DurationBucket>& bucket) {
Yao Chen5154a372017-10-30 22:57:06 -0700111 switch (mMetric.type()) {
112 case DurationMetric_AggregationType_DURATION_SUM:
113 return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex,
114 mCurrentBucketStartTimeNs, mBucketSizeNs,
115 bucket);
116 case DurationMetric_AggregationType_DURATION_MAX_SPARSE:
117 return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex,
118 mCurrentBucketStartTimeNs, mBucketSizeNs,
119 bucket);
120 }
121}
122
Yao Chen729093d2017-10-16 10:33:26 -0700123void DurationMetricProducer::finish() {
124 // TODO: write the StatsLogReport to dropbox using
125 // DropboxWriter.
126}
127
Yao Chen5154a372017-10-30 22:57:06 -0700128void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700129 VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
130 // Now for each of the on-going event, check if the condition has changed for them.
Yao Chen5154a372017-10-30 22:57:06 -0700131 flushIfNeeded(eventTime);
Yao Chen729093d2017-10-16 10:33:26 -0700132 for (auto& pair : mCurrentSlicedDuration) {
Yao Chen5154a372017-10-30 22:57:06 -0700133 pair.second->onSlicedConditionMayChange(eventTime);
Yao Chen729093d2017-10-16 10:33:26 -0700134 }
135}
136
Yao Chen5154a372017-10-30 22:57:06 -0700137void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700138 VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
139 mCondition = conditionMet;
140 // TODO: need to populate the condition change time from the event which triggers the condition
141 // change, instead of using current time.
Yao Chen5154a372017-10-30 22:57:06 -0700142
143 flushIfNeeded(eventTime);
Yao Chen729093d2017-10-16 10:33:26 -0700144 for (auto& pair : mCurrentSlicedDuration) {
Yao Chen5154a372017-10-30 22:57:06 -0700145 pair.second->onConditionChanged(conditionMet, eventTime);
Yao Chen729093d2017-10-16 10:33:26 -0700146 }
147}
148
149static void addDurationBucketsToReport(StatsLogReport_DurationMetricDataWrapper& wrapper,
150 const vector<KeyValuePair>& key,
151 const vector<DurationBucketInfo>& buckets) {
152 DurationMetricData* data = wrapper.add_data();
153 for (const auto& kv : key) {
154 data->add_dimension()->CopyFrom(kv);
155 }
156 for (const auto& bucket : buckets) {
157 data->add_bucket_info()->CopyFrom(bucket);
Yao Chen5154a372017-10-30 22:57:06 -0700158 VLOG("\t bucket [%lld - %lld] duration(ns): %lld", bucket.start_bucket_nanos(),
Yao Chen729093d2017-10-16 10:33:26 -0700159 bucket.end_bucket_nanos(), bucket.duration_nanos());
160 }
161}
162
yro17adac92017-11-08 23:16:29 -0800163std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
yro2b0f8862017-11-06 14:27:31 -0800164 long long endTime = time(nullptr) * NS_PER_SEC;
165
Yao Chen729093d2017-10-16 10:33:26 -0700166 // Dump current bucket if it's stale.
167 // If current bucket is still on-going, don't force dump current bucket.
168 // In finish(), We can force dump current bucket.
yro2b0f8862017-11-06 14:27:31 -0800169 flushIfNeeded(endTime);
170 VLOG("metric %lld dump report now...", mMetric.metric_id());
Yao Chen729093d2017-10-16 10:33:26 -0700171
Yao Chen729093d2017-10-16 10:33:26 -0700172 for (const auto& pair : mPastBuckets) {
173 const HashableDimensionKey& hashableKey = pair.first;
yro2b0f8862017-11-06 14:27:31 -0800174 VLOG(" dimension key %s", hashableKey.c_str());
Yao Chen729093d2017-10-16 10:33:26 -0700175 auto it = mDimensionKeyMap.find(hashableKey);
176 if (it == mDimensionKeyMap.end()) {
177 ALOGW("Dimension key %s not found?!?! skip...", hashableKey.c_str());
178 continue;
179 }
yrob0378b02017-11-09 20:36:25 -0800180 long long wrapperToken =
181 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
yro2b0f8862017-11-06 14:27:31 -0800182
183 // First fill dimension (KeyValuePairs).
184 for (const auto& kv : it->second) {
yrob0378b02017-11-09 20:36:25 -0800185 long long dimensionToken =
186 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
yro2b0f8862017-11-06 14:27:31 -0800187 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
188 if (kv.has_value_str()) {
189 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
190 } else if (kv.has_value_int()) {
191 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
192 } else if (kv.has_value_bool()) {
193 mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
194 } else if (kv.has_value_float()) {
195 mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
196 }
197 mProto->end(dimensionToken);
198 }
199
200 // Then fill bucket_info (DurationBucketInfo).
201 for (const auto& bucket : pair.second) {
yrob0378b02017-11-09 20:36:25 -0800202 long long bucketInfoToken =
203 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
yro2b0f8862017-11-06 14:27:31 -0800204 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
205 (long long)bucket.mBucketStartNs);
206 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
207 (long long)bucket.mBucketEndNs);
208 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration);
209 mProto->end(bucketInfoToken);
210 VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs,
211 (long long)bucket.mBucketEndNs, (long long)bucket.mDuration);
212 }
213
214 mProto->end(wrapperToken);
Yao Chen729093d2017-10-16 10:33:26 -0700215 }
yro2b0f8862017-11-06 14:27:31 -0800216
217 mProto->end(mProtoToken);
218 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
219 (long long)mCurrentBucketStartTimeNs);
220
yro17adac92017-11-08 23:16:29 -0800221 std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
yro2b0f8862017-11-06 14:27:31 -0800222
223 startNewProtoOutputStream(endTime);
224 mPastBuckets.clear();
225
yro17adac92017-11-08 23:16:29 -0800226 return buffer;
yro2b0f8862017-11-06 14:27:31 -0800227}
Yao Chen729093d2017-10-16 10:33:26 -0700228
Yao Chen5154a372017-10-30 22:57:06 -0700229void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700230 if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
231 return;
232 }
233
Yao Chen5154a372017-10-30 22:57:06 -0700234 VLOG("flushing...........");
Yao Chen729093d2017-10-16 10:33:26 -0700235 for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end(); ++it) {
Yao Chen5154a372017-10-30 22:57:06 -0700236 if (it->second->flushIfNeeded(eventTime)) {
237 VLOG("erase bucket for key %s", it->first.c_str());
Yao Chen729093d2017-10-16 10:33:26 -0700238 mCurrentSlicedDuration.erase(it);
Yao Chen729093d2017-10-16 10:33:26 -0700239 }
240 }
Yao Chen5154a372017-10-30 22:57:06 -0700241
242 int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
243 mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
244}
245
246void DurationMetricProducer::onMatchedLogEventInternal(
247 const size_t matcherIndex, const HashableDimensionKey& eventKey,
248 const map<string, HashableDimensionKey>& conditionKeys, bool condition,
Chenjie Yub3dda412017-10-24 13:41:59 -0700249 const LogEvent& event, bool scheduledPull) {
Yao Chen5154a372017-10-30 22:57:06 -0700250 flushIfNeeded(event.GetTimestampNs());
251
252 if (matcherIndex == mStopAllIndex) {
253 for (auto& pair : mCurrentSlicedDuration) {
254 pair.second->noteStopAll(event.GetTimestampNs());
255 }
256 return;
257 }
258
259 HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
260
261 if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
262 mCurrentSlicedDuration[eventKey] = createDurationTracker(mPastBuckets[eventKey]);
263 }
264
265 auto it = mCurrentSlicedDuration.find(eventKey);
266
267 if (matcherIndex == mStartIndex) {
268 it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
Yao Chen5154a372017-10-30 22:57:06 -0700269 } else if (matcherIndex == mStopIndex) {
270 it->second->noteStop(atomKey, event.GetTimestampNs());
271 }
Yao Chen729093d2017-10-16 10:33:26 -0700272}
273
yro69007c82017-10-26 20:42:57 -0700274size_t DurationMetricProducer::byteSize() {
yro2b0f8862017-11-06 14:27:31 -0800275 size_t totalSize = 0;
276 for (const auto& pair : mPastBuckets) {
277 totalSize += pair.second.size() * kBucketSize;
278 }
279 return totalSize;
yro69007c82017-10-26 20:42:57 -0700280}
281
Yao Chen729093d2017-10-16 10:33:26 -0700282} // namespace statsd
283} // namespace os
284} // namespace android