blob: a35070bab54bad4cef6c1df83d72dcfd7154061b [file] [log] [blame]
Chenjie Yub3dda412017-10-24 13:41:59 -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 // STOPSHIP if true
18#include "Log.h"
19
20#include "ValueMetricProducer.h"
21
22#include <cutils/log.h>
23#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;
Yangster-macd1815dc2017-11-13 21:43:15 -080032using android::util::FIELD_TYPE_STRING;
yro2b0f8862017-11-06 14:27:31 -080033using android::util::ProtoOutputStream;
Chenjie Yub3dda412017-10-24 13:41:59 -070034using std::list;
Chenjie Yu6736c892017-11-09 10:50:09 -080035using std::make_pair;
Chenjie Yub3dda412017-10-24 13:41:59 -070036using std::make_shared;
Yao Chen93fe3a32017-11-02 13:52:59 -070037using std::map;
Chenjie Yub3dda412017-10-24 13:41:59 -070038using std::shared_ptr;
39using std::unique_ptr;
Yao Chen93fe3a32017-11-02 13:52:59 -070040using std::unordered_map;
Chenjie Yub3dda412017-10-24 13:41:59 -070041
42namespace android {
43namespace os {
44namespace statsd {
45
yro2b0f8862017-11-06 14:27:31 -080046// for StatsLogReport
Yangster-macd1815dc2017-11-13 21:43:15 -080047const int FIELD_ID_NAME = 1;
yro2b0f8862017-11-06 14:27:31 -080048const int FIELD_ID_START_REPORT_NANOS = 2;
49const int FIELD_ID_END_REPORT_NANOS = 3;
50const int FIELD_ID_VALUE_METRICS = 7;
51// for ValueMetricDataWrapper
52const int FIELD_ID_DATA = 1;
53// for ValueMetricData
54const int FIELD_ID_DIMENSION = 1;
55const int FIELD_ID_BUCKET_INFO = 2;
56// for KeyValuePair
57const int FIELD_ID_KEY = 1;
58const int FIELD_ID_VALUE_STR = 2;
59const int FIELD_ID_VALUE_INT = 3;
60const int FIELD_ID_VALUE_BOOL = 4;
61const int FIELD_ID_VALUE_FLOAT = 5;
62// for ValueBucketInfo
63const int FIELD_ID_START_BUCKET_NANOS = 1;
64const int FIELD_ID_END_BUCKET_NANOS = 2;
65const int FIELD_ID_VALUE = 3;
66
Chenjie Yu6736c892017-11-09 10:50:09 -080067static const uint64_t kDefaultBucketSizeMillis = 60 * 60 * 1000L;
68
Chenjie Yub3dda412017-10-24 13:41:59 -070069// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
70ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
Yao Chen93fe3a32017-11-02 13:52:59 -070071 const sp<ConditionWizard>& wizard, const int pullTagId,
Chenjie Yu6736c892017-11-09 10:50:09 -080072 const uint64_t startTimeNs,
73 shared_ptr<StatsPullerManager> statsPullerManager)
74 : MetricProducer(startTimeNs, conditionIndex, wizard),
75 mMetric(metric),
76 mStatsPullerManager(statsPullerManager),
77 mPullTagId(pullTagId) {
Yao Chen93fe3a32017-11-02 13:52:59 -070078 // TODO: valuemetric for pushed events may need unlimited bucket length
Chenjie Yu6736c892017-11-09 10:50:09 -080079 if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
80 mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
81 } else {
82 mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000;
83 }
Chenjie Yub3dda412017-10-24 13:41:59 -070084
Yao Chen93fe3a32017-11-02 13:52:59 -070085 mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
Chenjie Yub3dda412017-10-24 13:41:59 -070086
Yao Chen93fe3a32017-11-02 13:52:59 -070087 if (metric.links().size() > 0) {
88 mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
89 metric.links().end());
90 mConditionSliced = true;
91 }
Chenjie Yub3dda412017-10-24 13:41:59 -070092
Yao Chen93fe3a32017-11-02 13:52:59 -070093 if (!metric.has_condition() && mPullTagId != -1) {
Chenjie Yu6736c892017-11-09 10:50:09 -080094 VLOG("Setting up periodic pulling for %d", mPullTagId);
95 mStatsPullerManager->RegisterReceiver(mPullTagId, this,
96 metric.bucket().bucket_size_millis());
Yao Chen93fe3a32017-11-02 13:52:59 -070097 }
Chenjie Yub3dda412017-10-24 13:41:59 -070098
yro2b0f8862017-11-06 14:27:31 -080099 startNewProtoOutputStream(mStartTimeNs);
100
Yangster-macd1815dc2017-11-13 21:43:15 -0800101 VLOG("value metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
Yao Chen93fe3a32017-11-02 13:52:59 -0700102 (long long)mBucketSizeNs, (long long)mStartTimeNs);
Chenjie Yub3dda412017-10-24 13:41:59 -0700103}
104
Chenjie Yu6736c892017-11-09 10:50:09 -0800105// for testing
106ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
107 const sp<ConditionWizard>& wizard, const int pullTagId,
108 const uint64_t startTimeNs)
109 : ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs,
110 make_shared<StatsPullerManager>()) {
111}
112
Chenjie Yub3dda412017-10-24 13:41:59 -0700113ValueMetricProducer::~ValueMetricProducer() {
Yao Chen93fe3a32017-11-02 13:52:59 -0700114 VLOG("~ValueMetricProducer() called");
Chenjie Yu6736c892017-11-09 10:50:09 -0800115 if (mPullTagId != -1) {
116 mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
117 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700118}
119
yro2b0f8862017-11-06 14:27:31 -0800120void ValueMetricProducer::startNewProtoOutputStream(long long startTime) {
121 mProto = std::make_unique<ProtoOutputStream>();
Yangster-macd1815dc2017-11-13 21:43:15 -0800122 mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
yro2b0f8862017-11-06 14:27:31 -0800123 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
124 mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
125}
126
Chenjie Yub3dda412017-10-24 13:41:59 -0700127void ValueMetricProducer::finish() {
Yao Chen93fe3a32017-11-02 13:52:59 -0700128 // TODO: write the StatsLogReport to dropbox using
129 // DropboxWriter.
Chenjie Yub3dda412017-10-24 13:41:59 -0700130}
131
Chenjie Yub3dda412017-10-24 13:41:59 -0700132void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
Yangster-macd1815dc2017-11-13 21:43:15 -0800133 VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
Chenjie Yub3dda412017-10-24 13:41:59 -0700134}
135
yro17adac92017-11-08 23:16:29 -0800136std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
Yangster-macd1815dc2017-11-13 21:43:15 -0800137 VLOG("metric %s dump report now...", mMetric.name().c_str());
Chenjie Yub3dda412017-10-24 13:41:59 -0700138
Yao Chen93fe3a32017-11-02 13:52:59 -0700139 for (const auto& pair : mPastBuckets) {
140 const HashableDimensionKey& hashableKey = pair.first;
yro2b0f8862017-11-06 14:27:31 -0800141 VLOG(" dimension key %s", hashableKey.c_str());
Yao Chen93fe3a32017-11-02 13:52:59 -0700142 auto it = mDimensionKeyMap.find(hashableKey);
143 if (it == mDimensionKeyMap.end()) {
144 ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
145 continue;
146 }
yrob0378b02017-11-09 20:36:25 -0800147 long long wrapperToken =
148 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
Chenjie Yub3dda412017-10-24 13:41:59 -0700149
yro2b0f8862017-11-06 14:27:31 -0800150 // First fill dimension (KeyValuePairs).
151 for (const auto& kv : it->second) {
yrob0378b02017-11-09 20:36:25 -0800152 long long dimensionToken =
153 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
yro2b0f8862017-11-06 14:27:31 -0800154 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
155 if (kv.has_value_str()) {
156 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
157 } else if (kv.has_value_int()) {
158 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
159 } else if (kv.has_value_bool()) {
160 mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
161 } else if (kv.has_value_float()) {
162 mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
163 }
164 mProto->end(dimensionToken);
165 }
166
167 // Then fill bucket_info (ValueBucketInfo).
168 for (const auto& bucket : pair.second) {
yrob0378b02017-11-09 20:36:25 -0800169 long long bucketInfoToken =
170 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
yro2b0f8862017-11-06 14:27:31 -0800171 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
172 (long long)bucket.mBucketStartNs);
173 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
174 (long long)bucket.mBucketEndNs);
175 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE, (long long)bucket.mValue);
176 mProto->end(bucketInfoToken);
177 VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
178 (long long)bucket.mBucketEndNs, (long long)bucket.mValue);
179 }
180 mProto->end(wrapperToken);
Chenjie Yub3dda412017-10-24 13:41:59 -0700181 }
yro2b0f8862017-11-06 14:27:31 -0800182 mProto->end(mProtoToken);
183 mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
184 (long long)mCurrentBucketStartTimeNs);
185
Yangster-macd1815dc2017-11-13 21:43:15 -0800186 VLOG("metric %s dump report now...", mMetric.name().c_str());
yro17adac92017-11-08 23:16:29 -0800187 std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
yro2b0f8862017-11-06 14:27:31 -0800188
189 startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
190 mPastBuckets.clear();
191 mByteSize = 0;
192
yro17adac92017-11-08 23:16:29 -0800193 return buffer;
yro2b0f8862017-11-06 14:27:31 -0800194
195 // TODO: Clear mDimensionKeyMap once the report is dumped.
Chenjie Yub3dda412017-10-24 13:41:59 -0700196}
197
198void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) {
Chenjie Yu7f8def92017-11-03 09:33:15 -0700199 AutoMutex _l(mLock);
Chenjie Yub3dda412017-10-24 13:41:59 -0700200 mCondition = condition;
201
Chenjie Yu5305e1d2017-10-31 13:49:36 -0700202 if (mPullTagId != -1) {
Chenjie Yub3dda412017-10-24 13:41:59 -0700203 if (mCondition == true) {
Chenjie Yu6736c892017-11-09 10:50:09 -0800204 mStatsPullerManager->RegisterReceiver(mPullTagId, this,
205 mMetric.bucket().bucket_size_millis());
206 } else if (mCondition == false) {
207 mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
Chenjie Yub3dda412017-10-24 13:41:59 -0700208 }
Chenjie Yu5305e1d2017-10-31 13:49:36 -0700209
210 vector<shared_ptr<LogEvent>> allData;
Chenjie Yu6736c892017-11-09 10:50:09 -0800211 if (mStatsPullerManager->Pull(mPullTagId, &allData)) {
Chenjie Yu5305e1d2017-10-31 13:49:36 -0700212 if (allData.size() == 0) {
213 return;
214 }
Chenjie Yu5305e1d2017-10-31 13:49:36 -0700215 for (const auto& data : allData) {
216 onMatchedLogEvent(0, *data, false);
217 }
218 flush_if_needed(eventTime);
Chenjie Yub3dda412017-10-24 13:41:59 -0700219 }
Chenjie Yu5305e1d2017-10-31 13:49:36 -0700220 return;
Chenjie Yub3dda412017-10-24 13:41:59 -0700221 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700222}
223
224void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
Chenjie Yu7f8def92017-11-03 09:33:15 -0700225 AutoMutex _l(mLock);
Chenjie Yu6736c892017-11-09 10:50:09 -0800226 if (mCondition == true || !mMetric.has_condition()) {
Chenjie Yub3dda412017-10-24 13:41:59 -0700227 if (allData.size() == 0) {
228 return;
229 }
230 uint64_t eventTime = allData.at(0)->GetTimestampNs();
Chenjie Yu6736c892017-11-09 10:50:09 -0800231 // alarm is not accurate and might drift.
232 if (eventTime > mCurrentBucketStartTimeNs + mBucketSizeNs * 3 / 2) {
233 flush_if_needed(eventTime);
234 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700235 for (const auto& data : allData) {
236 onMatchedLogEvent(0, *data, true);
237 }
238 flush_if_needed(eventTime);
239 }
240}
241
242void ValueMetricProducer::onMatchedLogEventInternal(
Yao Chen93fe3a32017-11-02 13:52:59 -0700243 const size_t matcherIndex, const HashableDimensionKey& eventKey,
244 const map<string, HashableDimensionKey>& conditionKey, bool condition,
245 const LogEvent& event, bool scheduledPull) {
246 uint64_t eventTimeNs = event.GetTimestampNs();
247 if (eventTimeNs < mCurrentBucketStartTimeNs) {
248 VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
249 (long long)mCurrentBucketStartTimeNs);
250 return;
Chenjie Yub3dda412017-10-24 13:41:59 -0700251 }
Yao Chen93fe3a32017-11-02 13:52:59 -0700252
253 Interval& interval = mCurrentSlicedBucket[eventKey];
254
255 long value = get_value(event);
256
Chenjie Yu6736c892017-11-09 10:50:09 -0800257 if (mPullTagId != -1) {
258 if (scheduledPull) {
259 // scheduled pull always sets beginning of current bucket and end
260 // of next bucket
261 if (interval.raw.size() > 0) {
Yao Chen93fe3a32017-11-02 13:52:59 -0700262 interval.raw.back().second = value;
Chenjie Yu6736c892017-11-09 10:50:09 -0800263 } else {
264 interval.raw.push_back(make_pair(value, value));
265 }
266 Interval& nextInterval = mNextSlicedBucket[eventKey];
267 if (nextInterval.raw.size() == 0) {
268 nextInterval.raw.push_back(make_pair(value, 0));
269 } else {
270 nextInterval.raw.front().first = value;
271 }
272 } else {
273 if (mCondition == true) {
274 interval.raw.push_back(make_pair(value, 0));
275 } else {
276 if (interval.raw.size() != 0) {
277 interval.raw.back().second = value;
278 } else {
279 interval.tainted = true;
280 VLOG("Data on condition true missing!");
281 }
Yao Chen93fe3a32017-11-02 13:52:59 -0700282 }
283 }
Chenjie Yu6736c892017-11-09 10:50:09 -0800284 } else {
Yao Chen93fe3a32017-11-02 13:52:59 -0700285 flush_if_needed(eventTimeNs);
Chenjie Yu6736c892017-11-09 10:50:09 -0800286 interval.raw.push_back(make_pair(value, 0));
Yao Chen93fe3a32017-11-02 13:52:59 -0700287 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700288}
289
290long ValueMetricProducer::get_value(const LogEvent& event) {
Yao Chen93fe3a32017-11-02 13:52:59 -0700291 status_t err = NO_ERROR;
292 long val = event.GetLong(mMetric.value_field(), &err);
293 if (err == NO_ERROR) {
294 return val;
295 } else {
Chenjie Yu6736c892017-11-09 10:50:09 -0800296 VLOG("Can't find value in message. %s", event.ToString().c_str());
Yao Chen93fe3a32017-11-02 13:52:59 -0700297 return 0;
298 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700299}
300
301void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
302 if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
303 VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
304 (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
305 return;
306 }
307
308 VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
309 (int)mCurrentSlicedBucket.size());
yro2b0f8862017-11-06 14:27:31 -0800310 ValueBucket info;
311 info.mBucketStartNs = mCurrentBucketStartTimeNs;
312 info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
Chenjie Yub3dda412017-10-24 13:41:59 -0700313
Chenjie Yu6736c892017-11-09 10:50:09 -0800314 int tainted = 0;
Chenjie Yub3dda412017-10-24 13:41:59 -0700315 for (const auto& slice : mCurrentSlicedBucket) {
Yao Chen93fe3a32017-11-02 13:52:59 -0700316 long value = 0;
Chenjie Yu6736c892017-11-09 10:50:09 -0800317 if (mPullTagId != -1) {
318 for (const auto& pair : slice.second.raw) {
319 value += (pair.second - pair.first);
320 }
321 } else {
322 for (const auto& pair : slice.second.raw) {
323 value += pair.first;
324 }
Yao Chen93fe3a32017-11-02 13:52:59 -0700325 }
Chenjie Yu6736c892017-11-09 10:50:09 -0800326 tainted += slice.second.tainted;
yro2b0f8862017-11-06 14:27:31 -0800327 info.mValue = value;
Chenjie Yu6736c892017-11-09 10:50:09 -0800328 VLOG(" %s, %ld, %d", slice.first.c_str(), value, tainted);
Yao Chen93fe3a32017-11-02 13:52:59 -0700329 // it will auto create new vector of ValuebucketInfo if the key is not found.
330 auto& bucketList = mPastBuckets[slice.first];
331 bucketList.push_back(info);
yro2b0f8862017-11-06 14:27:31 -0800332 mByteSize += sizeof(info);
Chenjie Yub3dda412017-10-24 13:41:59 -0700333 }
334
335 // Reset counters
336 mCurrentSlicedBucket.swap(mNextSlicedBucket);
337 mNextSlicedBucket.clear();
338 int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
Yao Chen93fe3a32017-11-02 13:52:59 -0700339 if (numBucketsForward > 1) {
Chenjie Yub3dda412017-10-24 13:41:59 -0700340 VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
341 }
342 mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
Yangster-macd1815dc2017-11-13 21:43:15 -0800343 VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
Chenjie Yub3dda412017-10-24 13:41:59 -0700344 (long long)mCurrentBucketStartTimeNs);
345}
346
yro2b0f8862017-11-06 14:27:31 -0800347size_t ValueMetricProducer::byteSize() {
348 return mByteSize;
349}
350
Chenjie Yub3dda412017-10-24 13:41:59 -0700351} // namespace statsd
352} // namespace os
Yao Chen93fe3a32017-11-02 13:52:59 -0700353} // namespace android