blob: dfed27576165d8cfd47b7b580306582051a7da7b [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
26using std::string;
27using std::unordered_map;
28using std::vector;
29
30namespace android {
31namespace os {
32namespace statsd {
33
34DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
35 const int conditionIndex, const size_t startIndex,
36 const size_t stopIndex, const size_t stopAllIndex,
Yao Chen5154a372017-10-30 22:57:06 -070037 const sp<ConditionWizard>& wizard,
38 const vector<KeyMatcher>& internalDimension)
Yao Chen729093d2017-10-16 10:33:26 -070039 // TODO: Pass in the start time from MetricsManager, instead of calling time() here.
40 : MetricProducer(time(nullptr) * NANO_SECONDS_IN_A_SECOND, conditionIndex, wizard),
41 mMetric(metric),
42 mStartIndex(startIndex),
43 mStopIndex(stopIndex),
Yao Chen5154a372017-10-30 22:57:06 -070044 mStopAllIndex(stopAllIndex),
45 mInternalDimension(internalDimension) {
Yao Chen729093d2017-10-16 10:33:26 -070046 // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
47 // them in the base class, because the proto generated CountMetric, and DurationMetric are
48 // not related. Maybe we should add a template in the future??
49 if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
50 mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000000;
51 } else {
52 mBucketSizeNs = LLONG_MAX;
53 }
54
55 // TODO: use UidMap if uid->pkg_name is required
56 mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
57
58 if (metric.links().size() > 0) {
59 mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
60 metric.links().end());
61 mConditionSliced = true;
62 }
63
64 VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
65 (long long)mBucketSizeNs, (long long)mStartTimeNs);
66}
67
68DurationMetricProducer::~DurationMetricProducer() {
69 VLOG("~DurationMetric() called");
70}
71
Yao Chen5154a372017-10-30 22:57:06 -070072unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
73 vector<DurationBucketInfo>& bucket) {
74 switch (mMetric.type()) {
75 case DurationMetric_AggregationType_DURATION_SUM:
76 return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex,
77 mCurrentBucketStartTimeNs, mBucketSizeNs,
78 bucket);
79 case DurationMetric_AggregationType_DURATION_MAX_SPARSE:
80 return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex,
81 mCurrentBucketStartTimeNs, mBucketSizeNs,
82 bucket);
83 }
84}
85
Yao Chen729093d2017-10-16 10:33:26 -070086void DurationMetricProducer::finish() {
87 // TODO: write the StatsLogReport to dropbox using
88 // DropboxWriter.
89}
90
Yao Chen5154a372017-10-30 22:57:06 -070091void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -070092 VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
93 // Now for each of the on-going event, check if the condition has changed for them.
Yao Chen5154a372017-10-30 22:57:06 -070094 flushIfNeeded(eventTime);
Yao Chen729093d2017-10-16 10:33:26 -070095 for (auto& pair : mCurrentSlicedDuration) {
Yao Chen5154a372017-10-30 22:57:06 -070096 pair.second->onSlicedConditionMayChange(eventTime);
Yao Chen729093d2017-10-16 10:33:26 -070097 }
98}
99
Yao Chen5154a372017-10-30 22:57:06 -0700100void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700101 VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
102 mCondition = conditionMet;
103 // TODO: need to populate the condition change time from the event which triggers the condition
104 // change, instead of using current time.
Yao Chen5154a372017-10-30 22:57:06 -0700105
106 flushIfNeeded(eventTime);
Yao Chen729093d2017-10-16 10:33:26 -0700107 for (auto& pair : mCurrentSlicedDuration) {
Yao Chen5154a372017-10-30 22:57:06 -0700108 pair.second->onConditionChanged(conditionMet, eventTime);
Yao Chen729093d2017-10-16 10:33:26 -0700109 }
110}
111
112static void addDurationBucketsToReport(StatsLogReport_DurationMetricDataWrapper& wrapper,
113 const vector<KeyValuePair>& key,
114 const vector<DurationBucketInfo>& buckets) {
115 DurationMetricData* data = wrapper.add_data();
116 for (const auto& kv : key) {
117 data->add_dimension()->CopyFrom(kv);
118 }
119 for (const auto& bucket : buckets) {
120 data->add_bucket_info()->CopyFrom(bucket);
Yao Chen5154a372017-10-30 22:57:06 -0700121 VLOG("\t bucket [%lld - %lld] duration(ns): %lld", bucket.start_bucket_nanos(),
Yao Chen729093d2017-10-16 10:33:26 -0700122 bucket.end_bucket_nanos(), bucket.duration_nanos());
123 }
124}
125
126StatsLogReport DurationMetricProducer::onDumpReport() {
127 VLOG("metric %lld dump report now...", mMetric.metric_id());
128 StatsLogReport report;
129 report.set_metric_id(mMetric.metric_id());
130 report.set_start_report_nanos(mStartTimeNs);
131 // Dump current bucket if it's stale.
132 // If current bucket is still on-going, don't force dump current bucket.
133 // In finish(), We can force dump current bucket.
Yao Chen5154a372017-10-30 22:57:06 -0700134 flushIfNeeded(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
Yao Chen729093d2017-10-16 10:33:26 -0700135 report.set_end_report_nanos(mCurrentBucketStartTimeNs);
136
137 StatsLogReport_DurationMetricDataWrapper* wrapper = report.mutable_duration_metrics();
138 for (const auto& pair : mPastBuckets) {
139 const HashableDimensionKey& hashableKey = pair.first;
140 auto it = mDimensionKeyMap.find(hashableKey);
141 if (it == mDimensionKeyMap.end()) {
142 ALOGW("Dimension key %s not found?!?! skip...", hashableKey.c_str());
143 continue;
144 }
145 VLOG(" dimension key %s", hashableKey.c_str());
146 addDurationBucketsToReport(*wrapper, it->second, pair.second);
147 }
148 return report;
149};
150
Yao Chen5154a372017-10-30 22:57:06 -0700151void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
Yao Chen729093d2017-10-16 10:33:26 -0700152 if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
153 return;
154 }
155
Yao Chen5154a372017-10-30 22:57:06 -0700156 VLOG("flushing...........");
Yao Chen729093d2017-10-16 10:33:26 -0700157 for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end(); ++it) {
Yao Chen5154a372017-10-30 22:57:06 -0700158 if (it->second->flushIfNeeded(eventTime)) {
159 VLOG("erase bucket for key %s", it->first.c_str());
Yao Chen729093d2017-10-16 10:33:26 -0700160 mCurrentSlicedDuration.erase(it);
Yao Chen729093d2017-10-16 10:33:26 -0700161 }
162 }
Yao Chen5154a372017-10-30 22:57:06 -0700163
164 int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
165 mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
166}
167
168void DurationMetricProducer::onMatchedLogEventInternal(
169 const size_t matcherIndex, const HashableDimensionKey& eventKey,
170 const map<string, HashableDimensionKey>& conditionKeys, bool condition,
171 const LogEvent& event) {
172 flushIfNeeded(event.GetTimestampNs());
173
174 if (matcherIndex == mStopAllIndex) {
175 for (auto& pair : mCurrentSlicedDuration) {
176 pair.second->noteStopAll(event.GetTimestampNs());
177 }
178 return;
179 }
180
181 HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
182
183 if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
184 mCurrentSlicedDuration[eventKey] = createDurationTracker(mPastBuckets[eventKey]);
185 }
186
187 auto it = mCurrentSlicedDuration.find(eventKey);
188
189 if (matcherIndex == mStartIndex) {
190 it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
191
192 } else if (matcherIndex == mStopIndex) {
193 it->second->noteStop(atomKey, event.GetTimestampNs());
194 }
Yao Chen729093d2017-10-16 10:33:26 -0700195}
196
yro69007c82017-10-26 20:42:57 -0700197size_t DurationMetricProducer::byteSize() {
198// TODO: return actual proto size when ProtoOutputStream is ready for use for
199// DurationMetricsProducer.
200// return mProto->size();
201 return 0;
202}
203
Yao Chen729093d2017-10-16 10:33:26 -0700204} // namespace statsd
205} // namespace os
206} // namespace android