blob: 01362b6b4fedfe96d84edafdc7f05ed8e78d62cb [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
Chenjie Yu88588972018-08-03 09:49:22 -070017#define DEBUG false // STOPSHIP if true
Chenjie Yub3dda412017-10-24 13:41:59 -070018#include "Log.h"
19
20#include "ValueMetricProducer.h"
Chenjie Yuc5875052018-03-09 10:13:11 -080021#include "../guardrail/StatsdStats.h"
22#include "../stats_log_util.h"
Chenjie Yub3dda412017-10-24 13:41:59 -070023
24#include <cutils/log.h>
25#include <limits.h>
26#include <stdlib.h>
27
yrob0378b02017-11-09 20:36:25 -080028using android::util::FIELD_COUNT_REPEATED;
yro2b0f8862017-11-06 14:27:31 -080029using android::util::FIELD_TYPE_BOOL;
Chenjie Yua0f02242018-07-06 16:14:34 -070030using android::util::FIELD_TYPE_DOUBLE;
yro2b0f8862017-11-06 14:27:31 -080031using android::util::FIELD_TYPE_INT32;
32using android::util::FIELD_TYPE_INT64;
33using android::util::FIELD_TYPE_MESSAGE;
Yangster-macd1815dc2017-11-13 21:43:15 -080034using android::util::FIELD_TYPE_STRING;
yro2b0f8862017-11-06 14:27:31 -080035using android::util::ProtoOutputStream;
Chenjie Yub3dda412017-10-24 13:41:59 -070036using std::list;
Chenjie Yu6736c892017-11-09 10:50:09 -080037using std::make_pair;
Chenjie Yub3dda412017-10-24 13:41:59 -070038using std::make_shared;
Yao Chen93fe3a32017-11-02 13:52:59 -070039using std::map;
Chenjie Yub3dda412017-10-24 13:41:59 -070040using std::shared_ptr;
41using std::unique_ptr;
Yao Chen93fe3a32017-11-02 13:52:59 -070042using std::unordered_map;
Chenjie Yub3dda412017-10-24 13:41:59 -070043
44namespace android {
45namespace os {
46namespace statsd {
47
yro2b0f8862017-11-06 14:27:31 -080048// for StatsLogReport
Yangster-mac94e197c2018-01-02 16:03:03 -080049const int FIELD_ID_ID = 1;
yro2b0f8862017-11-06 14:27:31 -080050const int FIELD_ID_VALUE_METRICS = 7;
Yangster-mac9def8e32018-04-17 13:55:51 -070051const int FIELD_ID_TIME_BASE = 9;
52const int FIELD_ID_BUCKET_SIZE = 10;
53const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
54const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
Howard Ro9440e092018-12-16 19:15:21 -080055const int FIELD_ID_IS_ACTIVE = 14;
yro2b0f8862017-11-06 14:27:31 -080056// for ValueMetricDataWrapper
57const int FIELD_ID_DATA = 1;
David Chen81245fd2018-04-12 14:33:37 -070058const int FIELD_ID_SKIPPED = 2;
Yangster-mac9def8e32018-04-17 13:55:51 -070059const int FIELD_ID_SKIPPED_START_MILLIS = 3;
60const int FIELD_ID_SKIPPED_END_MILLIS = 4;
yro2b0f8862017-11-06 14:27:31 -080061// for ValueMetricData
Yangster-mac468ff042018-01-17 12:26:34 -080062const int FIELD_ID_DIMENSION_IN_WHAT = 1;
63const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
64const int FIELD_ID_BUCKET_INFO = 3;
Yangster-mac9def8e32018-04-17 13:55:51 -070065const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
66const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
yro2b0f8862017-11-06 14:27:31 -080067// for ValueBucketInfo
Chenjie Yu32717c32018-10-20 23:54:48 -070068const int FIELD_ID_VALUE_INDEX = 1;
69const int FIELD_ID_VALUE_LONG = 2;
70const int FIELD_ID_VALUE_DOUBLE = 3;
71const int FIELD_ID_VALUES = 9;
Yangster-mac9def8e32018-04-17 13:55:51 -070072const int FIELD_ID_BUCKET_NUM = 4;
73const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
74const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
Yao Chene6cfb142019-04-08 12:00:01 -070075const int FIELD_ID_CONDITION_TRUE_NS = 10;
yro2b0f8862017-11-06 14:27:31 -080076
Chenjie Yuf275f612018-11-30 23:29:06 -080077const Value ZERO_LONG((int64_t)0);
78const Value ZERO_DOUBLE((int64_t)0);
79
Chenjie Yub3dda412017-10-24 13:41:59 -070080// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
Chenjie Yuf275f612018-11-30 23:29:06 -080081ValueMetricProducer::ValueMetricProducer(
82 const ConfigKey& key, const ValueMetric& metric, const int conditionIndex,
83 const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
84 const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
85 const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager)
Chenjie Yu054ce9c2018-11-12 15:27:29 -080086 : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard),
87 mWhatMatcherIndex(whatMatcherIndex),
88 mEventMatcherWizard(matcherWizard),
Chenjie Yue2219202018-06-08 10:07:51 -070089 mPullerManager(pullerManager),
Chenjie Yuc5875052018-03-09 10:13:11 -080090 mPullTagId(pullTagId),
Chenjie Yua0f02242018-07-06 16:14:34 -070091 mIsPulled(pullTagId != -1),
David Chen81245fd2018-04-12 14:33:37 -070092 mMinBucketSizeNs(metric.min_bucket_size_nanos()),
Chenjie Yuc5875052018-03-09 10:13:11 -080093 mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
94 StatsdStats::kAtomDimensionKeySizeLimitMap.end()
95 ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first
96 : StatsdStats::kDimensionKeySizeSoftLimit),
97 mDimensionHardLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
98 StatsdStats::kAtomDimensionKeySizeLimitMap.end()
99 ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second
Chenjie Yu47234642018-05-14 10:14:16 -0700100 : StatsdStats::kDimensionKeySizeHardLimit),
Chenjie Yua0f02242018-07-06 16:14:34 -0700101 mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()),
102 mAggregationType(metric.aggregation_type()),
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700103 mUseDiff(metric.has_use_diff() ? metric.use_diff() : (mIsPulled ? true : false)),
104 mValueDirection(metric.value_direction()),
Chenjie Yuf275f612018-11-30 23:29:06 -0800105 mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
106 mUseZeroDefaultBase(metric.use_zero_default_base()),
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800107 mHasGlobalBase(false),
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000108 mCurrentBucketIsInvalid(false),
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800109 mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
Chenjie Yucd1b7972019-01-16 20:38:15 -0800110 : StatsdStats::kPullMaxDelayNs),
Yao Chene6cfb142019-04-08 12:00:01 -0700111 mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()),
Muhammad Qureshi18e46922019-05-24 16:38:49 -0700112 mConditionTimer(mIsActive && mCondition == ConditionState::kTrue, timeBaseNs) {
Yangster-macb8144812018-01-04 10:56:23 -0800113 int64_t bucketSizeMills = 0;
114 if (metric.has_bucket()) {
yro59cc24d2018-02-13 20:17:32 -0800115 bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
Chenjie Yu6736c892017-11-09 10:50:09 -0800116 } else {
Yangster-macb8144812018-01-04 10:56:23 -0800117 bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR);
Chenjie Yu6736c892017-11-09 10:50:09 -0800118 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700119
Yangster-macb8144812018-01-04 10:56:23 -0800120 mBucketSizeNs = bucketSizeMills * 1000000;
Chenjie Yu32717c32018-10-20 23:54:48 -0700121
122 translateFieldMatcher(metric.value_field(), &mFieldMatchers);
123
Yao Chen8a8d16c2018-02-08 14:50:40 -0800124 if (metric.has_dimensions_in_what()) {
125 translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
Yangster13fb7e42018-03-07 17:30:49 -0800126 mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
Yao Chen8a8d16c2018-02-08 14:50:40 -0800127 }
128
129 if (metric.has_dimensions_in_condition()) {
130 translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
131 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700132
Yao Chen93fe3a32017-11-02 13:52:59 -0700133 if (metric.links().size() > 0) {
Yao Chen8a8d16c2018-02-08 14:50:40 -0800134 for (const auto& link : metric.links()) {
135 Metric2Condition mc;
136 mc.conditionId = link.condition();
137 translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
138 translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
139 mMetric2ConditionLinks.push_back(mc);
140 }
Yao Chen93fe3a32017-11-02 13:52:59 -0700141 }
Yao Chen8a8d16c2018-02-08 14:50:40 -0800142
Yao Chen8a8d16c2018-02-08 14:50:40 -0800143 mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
Yangster-mac9def8e32018-04-17 13:55:51 -0700144 mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700145 HasPositionALL(metric.dimensions_in_condition());
Chenjie Yub3dda412017-10-24 13:41:59 -0700146
Tej Singh597c7162019-04-17 16:41:45 -0700147 int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs);
148 mCurrentBucketNum += numBucketsForward;
149
Chenjie Yue1361ed2018-07-23 17:33:09 -0700150 flushIfNeededLocked(startTimeNs);
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700151
Chenjie Yua0f02242018-07-06 16:14:34 -0700152 if (mIsPulled) {
Chenjie Yue1361ed2018-07-23 17:33:09 -0700153 mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
154 mBucketSizeNs);
Yao Chen93fe3a32017-11-02 13:52:59 -0700155 }
Chenjie Yu1a0a9412018-03-28 10:07:22 -0700156
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700157 // Only do this for partial buckets like first bucket. All other buckets should use
Chenjie Yue1361ed2018-07-23 17:33:09 -0700158 // flushIfNeeded to adjust start and end to bucket boundaries.
159 // Adjust start for partial bucket
160 mCurrentBucketStartTimeNs = startTimeNs;
Yao Chene6cfb142019-04-08 12:00:01 -0700161 mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs);
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700162 VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
163 (long long)mBucketSizeNs, (long long)mTimeBaseNs);
Chenjie Yub3dda412017-10-24 13:41:59 -0700164}
165
166ValueMetricProducer::~ValueMetricProducer() {
Yao Chen93fe3a32017-11-02 13:52:59 -0700167 VLOG("~ValueMetricProducer() called");
Chenjie Yua0f02242018-07-06 16:14:34 -0700168 if (mIsPulled) {
Chenjie Yue2219202018-06-08 10:07:51 -0700169 mPullerManager->UnRegisterReceiver(mPullTagId, this);
Chenjie Yu6736c892017-11-09 10:50:09 -0800170 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700171}
172
Tej Singh35c7a572019-05-01 16:47:54 -0700173void ValueMetricProducer::prepareFirstBucketLocked() {
Tej Singh597c7162019-04-17 16:41:45 -0700174 // Kicks off the puller immediately if condition is true and diff based.
175 if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
176 pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition);
177 }
178}
179
Yao Chen427d3722018-03-22 15:21:52 -0700180void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
Yangster-macb142cc82018-03-30 15:22:08 -0700181 const int64_t eventTime) {
Yangster-mac94e197c2018-01-02 16:03:03 -0800182 VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
Chenjie Yub3dda412017-10-24 13:41:59 -0700183}
184
Yangster-macb142cc82018-03-30 15:22:08 -0700185void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
Olivier Gaillard320952b2019-02-06 13:57:24 +0000186 StatsdStats::getInstance().noteBucketDropped(mMetricId);
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000187 // We are going to flush the data without doing a pull first so we need to invalidte the data.
188 bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue;
189 if (pullNeeded) {
190 invalidateCurrentBucket();
191 }
192 flushIfNeededLocked(dropTimeNs);
193 clearPastBucketsLocked(dropTimeNs);
Yao Chen06dba5d2018-01-26 13:38:16 -0800194}
195
Yangster-maca802d732018-04-24 07:50:38 -0700196void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
Yangster-maca802d732018-04-24 07:50:38 -0700197 mPastBuckets.clear();
198 mSkippedBuckets.clear();
199}
200
Yangster-macb142cc82018-03-30 15:22:08 -0700201void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
Yangster-mace68f3a52018-04-04 00:01:43 -0700202 const bool include_current_partial_bucket,
Bookatzff71cad2018-09-20 17:17:49 -0700203 const bool erase_data,
Olivier Gaillard6c75ecd2019-02-20 09:57:33 +0000204 const DumpLatency dumpLatency,
Yangster-mac9def8e32018-04-17 13:55:51 -0700205 std::set<string> *str_set,
Yao Chen288c6002017-12-12 13:43:18 -0800206 ProtoOutputStream* protoOutput) {
Yangster-mac94e197c2018-01-02 16:03:03 -0800207 VLOG("metric %lld dump report now...", (long long)mMetricId);
Yangster-mace68f3a52018-04-04 00:01:43 -0700208 if (include_current_partial_bucket) {
Olivier Gaillard6c75ecd2019-02-20 09:57:33 +0000209 // For pull metrics, we need to do a pull at bucket boundaries. If we do not do that the
210 // current bucket will have incomplete data and the next will have the wrong snapshot to do
211 // a diff against. If the condition is false, we are fine since the base data is reset and
212 // we are not tracking anything.
213 bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue;
214 if (pullNeeded) {
215 switch (dumpLatency) {
216 case FAST:
217 invalidateCurrentBucket();
218 break;
219 case NO_TIME_CONSTRAINTS:
Olivier Gaillarda8b70112019-02-25 11:24:23 +0000220 pullAndMatchEventsLocked(dumpTimeNs, mCondition);
Olivier Gaillard6c75ecd2019-02-20 09:57:33 +0000221 break;
222 }
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000223 }
Olivier Gaillard6c75ecd2019-02-20 09:57:33 +0000224 flushCurrentBucketLocked(dumpTimeNs, dumpTimeNs);
Yangster-mace68f3a52018-04-04 00:01:43 -0700225 }
Yang Lub4722912018-11-15 11:02:03 -0800226 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
Howard Ro07e23ff2018-12-17 17:28:07 -0800227 protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
Yang Lub4722912018-11-15 11:02:03 -0800228
David Chen81245fd2018-04-12 14:33:37 -0700229 if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
Yangster-mac635b4b32018-01-23 20:17:35 -0800230 return;
231 }
Yangster-mac9def8e32018-04-17 13:55:51 -0700232 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
233 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
234 // Fills the dimension path if not slicing by ALL.
235 if (!mSliceByPositionALL) {
236 if (!mDimensionsInWhat.empty()) {
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700237 uint64_t dimenPathToken =
238 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
Yangster-mac9def8e32018-04-17 13:55:51 -0700239 writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
240 protoOutput->end(dimenPathToken);
241 }
242 if (!mDimensionsInCondition.empty()) {
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700243 uint64_t dimenPathToken =
244 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
Yangster-mac9def8e32018-04-17 13:55:51 -0700245 writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
246 protoOutput->end(dimenPathToken);
247 }
248 }
249
Yi Jin5ee07872018-03-05 18:18:27 -0800250 uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
Yao Chen6a8c7992017-11-29 20:02:07 +0000251
David Chen81245fd2018-04-12 14:33:37 -0700252 for (const auto& pair : mSkippedBuckets) {
253 uint64_t wrapperToken =
254 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED);
Yangster-mac9def8e32018-04-17 13:55:51 -0700255 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS,
256 (long long)(NanoToMillis(pair.first)));
257 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS,
258 (long long)(NanoToMillis(pair.second)));
David Chen81245fd2018-04-12 14:33:37 -0700259 protoOutput->end(wrapperToken);
260 }
David Chen81245fd2018-04-12 14:33:37 -0700261
Yao Chen93fe3a32017-11-02 13:52:59 -0700262 for (const auto& pair : mPastBuckets) {
Yangster-mac93694462018-01-22 20:49:31 -0800263 const MetricDimensionKey& dimensionKey = pair.first;
Yangster13fb7e42018-03-07 17:30:49 -0800264 VLOG(" dimension key %s", dimensionKey.toString().c_str());
Yi Jin5ee07872018-03-05 18:18:27 -0800265 uint64_t wrapperToken =
Yao Chen288c6002017-12-12 13:43:18 -0800266 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
Chenjie Yub3dda412017-10-24 13:41:59 -0700267
Yangster-mac20877162017-12-22 17:19:39 -0800268 // First fill dimension.
Yangster-mac9def8e32018-04-17 13:55:51 -0700269 if (mSliceByPositionALL) {
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700270 uint64_t dimensionToken =
271 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
Yangster-mac9def8e32018-04-17 13:55:51 -0700272 writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
273 protoOutput->end(dimensionToken);
274 if (dimensionKey.hasDimensionKeyInCondition()) {
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700275 uint64_t dimensionInConditionToken =
276 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
277 writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), str_set,
278 protoOutput);
Yangster-mac9def8e32018-04-17 13:55:51 -0700279 protoOutput->end(dimensionInConditionToken);
280 }
281 } else {
282 writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
283 FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
284 if (dimensionKey.hasDimensionKeyInCondition()) {
285 writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700286 FIELD_ID_DIMENSION_LEAF_IN_CONDITION, str_set,
287 protoOutput);
Yangster-mac9def8e32018-04-17 13:55:51 -0700288 }
Yangster-mac93694462018-01-22 20:49:31 -0800289 }
yro2b0f8862017-11-06 14:27:31 -0800290
291 // Then fill bucket_info (ValueBucketInfo).
292 for (const auto& bucket : pair.second) {
Yi Jin5ee07872018-03-05 18:18:27 -0800293 uint64_t bucketInfoToken = protoOutput->start(
Yao Chen288c6002017-12-12 13:43:18 -0800294 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
Yangster-mac9def8e32018-04-17 13:55:51 -0700295
296 if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
297 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
298 (long long)NanoToMillis(bucket.mBucketStartNs));
299 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
300 (long long)NanoToMillis(bucket.mBucketEndNs));
301 } else {
302 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
303 (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
304 }
Yao Chene6cfb142019-04-08 12:00:01 -0700305 // only write the condition timer value if the metric has a condition.
306 if (mConditionTrackerIndex >= 0) {
307 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
308 (long long)bucket.mConditionTrueNs);
309 }
Chenjie Yu32717c32018-10-20 23:54:48 -0700310 for (int i = 0; i < (int)bucket.valueIndex.size(); i ++) {
311 int index = bucket.valueIndex[i];
312 const Value& value = bucket.values[i];
313 uint64_t valueToken = protoOutput->start(
314 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_VALUES);
315 protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_INDEX,
316 index);
317 if (value.getType() == LONG) {
318 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_LONG,
319 (long long)value.long_value);
320 VLOG("\t bucket [%lld - %lld] value %d: %lld", (long long)bucket.mBucketStartNs,
321 (long long)bucket.mBucketEndNs, index, (long long)value.long_value);
322 } else if (value.getType() == DOUBLE) {
323 protoOutput->write(FIELD_TYPE_DOUBLE | FIELD_ID_VALUE_DOUBLE,
324 value.double_value);
325 VLOG("\t bucket [%lld - %lld] value %d: %.2f", (long long)bucket.mBucketStartNs,
326 (long long)bucket.mBucketEndNs, index, value.double_value);
327 } else {
328 VLOG("Wrong value type for ValueMetric output: %d", value.getType());
329 }
330 protoOutput->end(valueToken);
Chenjie Yua0f02242018-07-06 16:14:34 -0700331 }
Yao Chen288c6002017-12-12 13:43:18 -0800332 protoOutput->end(bucketInfoToken);
yro2b0f8862017-11-06 14:27:31 -0800333 }
Yao Chen288c6002017-12-12 13:43:18 -0800334 protoOutput->end(wrapperToken);
Chenjie Yub3dda412017-10-24 13:41:59 -0700335 }
Yao Chen288c6002017-12-12 13:43:18 -0800336 protoOutput->end(protoToken);
yro2b0f8862017-11-06 14:27:31 -0800337
Yangster-mac94e197c2018-01-02 16:03:03 -0800338 VLOG("metric %lld dump report now...", (long long)mMetricId);
Bookatzff71cad2018-09-20 17:17:49 -0700339 if (erase_data) {
340 mPastBuckets.clear();
341 mSkippedBuckets.clear();
342 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700343}
344
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000345void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase() {
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000346 if (!mCurrentBucketIsInvalid) {
347 // Only report once per invalid bucket.
348 StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
349 }
350 mCurrentBucketIsInvalid = true;
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000351}
352
353void ValueMetricProducer::invalidateCurrentBucket() {
354 invalidateCurrentBucketWithoutResetBase();
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000355 resetBase();
356}
357
Chenjie Yuf275f612018-11-30 23:29:06 -0800358void ValueMetricProducer::resetBase() {
359 for (auto& slice : mCurrentSlicedBucket) {
360 for (auto& interval : slice.second) {
361 interval.hasBase = false;
362 }
363 }
364 mHasGlobalBase = false;
365}
366
Muhammad Qureshi18e46922019-05-24 16:38:49 -0700367// Handle active state change. Active state change is treated like a condition change:
368// - drop bucket if active state change event arrives too late
369// - if condition is true, pull data on active state changes
370// - ConditionTimer tracks changes based on AND of condition and active state.
371void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000372 bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
Muhammad Qureshi18e46922019-05-24 16:38:49 -0700373 if (ConditionState::kTrue == mCondition && isEventTooLate) {
374 // Drop bucket because event arrived too late, ie. we are missing data for this bucket.
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000375 invalidateCurrentBucket();
Yao Chen2794da22017-12-13 16:01:55 -0800376 }
377
Muhammad Qureshi18e46922019-05-24 16:38:49 -0700378 // Call parent method once we've verified the validity of current bucket.
379 MetricProducer::onActiveStateChangedLocked(eventTimeNs);
380
381 if (ConditionState::kTrue != mCondition) {
382 return;
383 }
384
385 // Pull on active state changes.
386 if (!isEventTooLate) {
387 if (mIsPulled) {
388 pullAndMatchEventsLocked(eventTimeNs, mCondition);
389 }
390 // When active state changes from true to false, clear diff base but don't
391 // reset other counters as we may accumulate more value in the bucket.
392 if (mUseDiff && !mIsActive) {
393 resetBase();
394 }
395 }
396
Chenjie Yu1a0a9412018-03-28 10:07:22 -0700397 flushIfNeededLocked(eventTimeNs);
Muhammad Qureshi18e46922019-05-24 16:38:49 -0700398
399 // Let condition timer know of new active state.
400 mConditionTimer.onConditionChanged(mIsActive, eventTimeNs);
Chenjie Yue1361ed2018-07-23 17:33:09 -0700401}
402
Muhammad Qureshi18e46922019-05-24 16:38:49 -0700403void ValueMetricProducer::onConditionChangedLocked(const bool condition,
404 const int64_t eventTimeNs) {
405 ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
406 bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
407
408 if (mIsActive) {
409 if (isEventTooLate) {
410 VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
411 (long long)mCurrentBucketStartTimeNs);
412 StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
413 invalidateCurrentBucket();
414 } else {
415 if (mCondition == ConditionState::kUnknown) {
416 // If the condition was unknown, we mark the bucket as invalid since the bucket will
417 // contain partial data. For instance, the condition change might happen close to
418 // the end of the bucket and we might miss lots of data.
419 //
420 // We still want to pull to set the base.
421 invalidateCurrentBucket();
422 }
423
424 // Pull on condition changes.
425 bool conditionChanged =
426 (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)
427 || (mCondition == ConditionState::kFalse &&
428 newCondition == ConditionState::kTrue);
429 // We do not need to pull when we go from unknown to false.
430 //
431 // We also pull if the condition was already true in order to be able to flush the
432 // bucket at the end if needed.
433 //
434 // onConditionChangedLocked might happen on bucket boundaries if this is called before
435 // #onDataPulled.
436 if (mIsPulled && (conditionChanged || condition)) {
437 pullAndMatchEventsLocked(eventTimeNs, newCondition);
438 }
439
440 // When condition change from true to false, clear diff base but don't
441 // reset other counters as we may accumulate more value in the bucket.
442 if (mUseDiff && mCondition == ConditionState::kTrue
443 && newCondition == ConditionState::kFalse) {
444 resetBase();
445 }
446 }
447 }
448
449 mCondition = isEventTooLate ? initialCondition(mConditionTrackerIndex) : newCondition;
450
451 if (mIsActive) {
452 flushIfNeededLocked(eventTimeNs);
453 mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
454 }
455}
456
457void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs,
458 ConditionState condition) {
Chenjie Yue1361ed2018-07-23 17:33:09 -0700459 vector<std::shared_ptr<LogEvent>> allData;
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800460 if (!mPullerManager->Pull(mPullTagId, &allData)) {
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000461 ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000462 invalidateCurrentBucket();
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800463 return;
Chenjie Yub3dda412017-10-24 13:41:59 -0700464 }
Olivier Gaillard11203df2019-02-06 13:18:09 +0000465
Olivier Gaillarda8b70112019-02-25 11:24:23 +0000466 accumulateEvents(allData, timestampNs, timestampNs, condition);
Olivier Gaillard11203df2019-02-06 13:18:09 +0000467}
468
469int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
470 return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
471}
472
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000473// By design, statsd pulls data at bucket boundaries using AlarmManager. These pulls are likely
474// to be delayed. Other events like condition changes or app upgrade which are not based on
475// AlarmManager might have arrived earlier and close the bucket.
Olivier Gaillard11203df2019-02-06 13:18:09 +0000476void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
477 bool pullSuccess, int64_t originalPullTimeNs) {
478 std::lock_guard<std::mutex> lock(mMutex);
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000479 if (mCondition == ConditionState::kTrue) {
Olivier Gaillard11203df2019-02-06 13:18:09 +0000480 // If the pull failed, we won't be able to compute a diff.
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000481 if (!pullSuccess) {
482 invalidateCurrentBucket();
483 } else {
484 bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs();
485 if (isEventLate) {
486 // If the event is late, we are in the middle of a bucket. Just
487 // process the data without trying to snap the data to the nearest bucket.
Olivier Gaillarda8b70112019-02-25 11:24:23 +0000488 accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs, mCondition);
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000489 } else {
490 // For scheduled pulled data, the effective event time is snap to the nearest
491 // bucket end. In the case of waking up from a deep sleep state, we will
492 // attribute to the previous bucket end. If the sleep was long but not very
493 // long, we will be in the immediate next bucket. Previous bucket may get a
494 // larger number as we pull at a later time than real bucket end.
495 //
496 // If the sleep was very long, we skip more than one bucket before sleep. In
497 // this case, if the diff base will be cleared and this new data will serve as
498 // new diff base.
499 int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
500 StatsdStats::getInstance().noteBucketBoundaryDelayNs(
501 mMetricId, originalPullTimeNs - bucketEndTime);
Olivier Gaillarda8b70112019-02-25 11:24:23 +0000502 accumulateEvents(allData, originalPullTimeNs, bucketEndTime, mCondition);
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000503 }
504 }
Olivier Gaillard11203df2019-02-06 13:18:09 +0000505 }
506
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000507 // We can probably flush the bucket. Since we used bucketEndTime when calling
508 // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed.
509 flushIfNeededLocked(originalPullTimeNs);
Olivier Gaillard11203df2019-02-06 13:18:09 +0000510}
511
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000512void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
Olivier Gaillarda8b70112019-02-25 11:24:23 +0000513 int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
514 ConditionState condition) {
Olivier Gaillard11203df2019-02-06 13:18:09 +0000515 bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs;
516 if (isEventLate) {
517 VLOG("Skip bucket end pull due to late arrival: %lld vs %lld",
518 (long long)eventElapsedTimeNs, (long long)mCurrentBucketStartTimeNs);
519 StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
520 invalidateCurrentBucket();
521 return;
522 }
523
524 const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs;
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000525 StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800526 if (pullDelayNs > mMaxPullDelayNs) {
527 ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId,
528 (long long)mMaxPullDelayNs);
529 StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000530 // We are missing one pull from the bucket which means we will not have a complete view of
531 // what's going on.
532 invalidateCurrentBucket();
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800533 return;
534 }
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800535
Olivier Gaillard11203df2019-02-06 13:18:09 +0000536 if (allData.size() == 0) {
537 VLOG("Data pulled is empty");
538 StatsdStats::getInstance().noteEmptyData(mPullTagId);
Misha Wagner1eee2212019-01-22 11:47:11 +0000539 }
540
Olivier Gaillard11203df2019-02-06 13:18:09 +0000541 mMatchedMetricDimensionKeys.clear();
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800542 for (const auto& data : allData) {
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800543 LogEvent localCopy = data->makeCopy();
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800544 if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
545 MatchingState::kMatched) {
Olivier Gaillard11203df2019-02-06 13:18:09 +0000546 localCopy.setElapsedTimestampNs(eventElapsedTimeNs);
Chenjie Yu0bd73db2018-12-16 07:37:04 -0800547 onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
548 }
549 }
Olivier Gaillard11203df2019-02-06 13:18:09 +0000550 // If the new pulled data does not contains some keys we track in our intervals, we need to
551 // reset the base.
552 for (auto& slice : mCurrentSlicedBucket) {
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000553 bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first)
Olivier Gaillard11203df2019-02-06 13:18:09 +0000554 != mMatchedMetricDimensionKeys.end();
555 if (!presentInPulledData) {
556 for (auto& interval : slice.second) {
557 interval.hasBase = false;
Chenjie Yu054ce9c2018-11-12 15:27:29 -0800558 }
Chenjie Yua7259ab2017-12-10 08:31:05 -0800559 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700560 }
Olivier Gaillard11203df2019-02-06 13:18:09 +0000561 mMatchedMetricDimensionKeys.clear();
562 mHasGlobalBase = true;
Olivier Gaillard1e0d8fc2019-02-11 18:08:43 +0000563
564 // If we reach the guardrail, we might have dropped some data which means the bucket is
565 // incomplete.
566 //
567 // The base also needs to be reset. If we do not have the full data, we might
568 // incorrectly compute the diff when mUseZeroDefaultBase is true since an existing key
569 // might be missing from mCurrentSlicedBucket.
570 if (hasReachedGuardRailLimit()) {
571 invalidateCurrentBucket();
572 mCurrentSlicedBucket.clear();
573 }
Chenjie Yub3dda412017-10-24 13:41:59 -0700574}
575
Yangster-maca78d0082018-03-12 12:02:56 -0700576void ValueMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
577 if (mCurrentSlicedBucket.size() == 0) {
578 return;
579 }
580
581 fprintf(out, "ValueMetric %lld dimension size %lu\n", (long long)mMetricId,
582 (unsigned long)mCurrentSlicedBucket.size());
583 if (verbose) {
584 for (const auto& it : mCurrentSlicedBucket) {
Chenjie Yu32717c32018-10-20 23:54:48 -0700585 for (const auto& interval : it.second) {
Chenjie Yua0f02242018-07-06 16:14:34 -0700586 fprintf(out, "\t(what)%s\t(condition)%s (value)%s\n",
587 it.first.getDimensionKeyInWhat().toString().c_str(),
588 it.first.getDimensionKeyInCondition().toString().c_str(),
Chenjie Yu32717c32018-10-20 23:54:48 -0700589 interval.value.toString().c_str());
590 }
Yangster-maca78d0082018-03-12 12:02:56 -0700591 }
592 }
593}
594
Olivier Gaillard1e0d8fc2019-02-11 18:08:43 +0000595bool ValueMetricProducer::hasReachedGuardRailLimit() const {
596 return mCurrentSlicedBucket.size() >= mDimensionHardLimit;
597}
598
Yangster-mac93694462018-01-22 20:49:31 -0800599bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
Yao Chenb3561512017-11-21 18:07:17 -0800600 // ===========GuardRail==============
601 // 1. Report the tuple count if the tuple count > soft limit
602 if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
603 return false;
604 }
Chenjie Yuc5875052018-03-09 10:13:11 -0800605 if (mCurrentSlicedBucket.size() > mDimensionSoftLimit - 1) {
Yao Chenb3561512017-11-21 18:07:17 -0800606 size_t newTupleCount = mCurrentSlicedBucket.size() + 1;
Yangster-mac94e197c2018-01-02 16:03:03 -0800607 StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
Yao Chenb3561512017-11-21 18:07:17 -0800608 // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
Olivier Gaillard1e0d8fc2019-02-11 18:08:43 +0000609 if (hasReachedGuardRailLimit()) {
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700610 ALOGE("ValueMetric %lld dropping data for dimension key %s", (long long)mMetricId,
611 newKey.toString().c_str());
Misha Wagner1eee2212019-01-22 11:47:11 +0000612 StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
Yao Chenb3561512017-11-21 18:07:17 -0800613 return true;
614 }
615 }
616
617 return false;
618}
619
Chenjie Yudbe5c502018-11-30 23:15:57 -0800620bool ValueMetricProducer::hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey) {
621 // ===========GuardRail==============
622 // 1. Report the tuple count if the tuple count > soft limit
623 if (mCurrentFullBucket.find(newKey) != mCurrentFullBucket.end()) {
624 return false;
625 }
626 if (mCurrentFullBucket.size() > mDimensionSoftLimit - 1) {
627 size_t newTupleCount = mCurrentFullBucket.size() + 1;
628 // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
629 if (newTupleCount > mDimensionHardLimit) {
630 ALOGE("ValueMetric %lld dropping data for full bucket dimension key %s",
631 (long long)mMetricId,
632 newKey.toString().c_str());
633 return true;
634 }
635 }
636
637 return false;
638}
639
Chenjie Yu32717c32018-10-20 23:54:48 -0700640bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret) {
641 for (const FieldValue& value : event.getValues()) {
642 if (value.mField.matches(matcher)) {
643 switch (value.mValue.type) {
644 case INT:
645 ret.setLong(value.mValue.int_value);
646 break;
647 case LONG:
648 ret.setLong(value.mValue.long_value);
649 break;
650 case FLOAT:
651 ret.setDouble(value.mValue.float_value);
652 break;
653 case DOUBLE:
654 ret.setDouble(value.mValue.double_value);
655 break;
656 default:
657 break;
658 }
659 return true;
660 }
Chenjie Yua0f02242018-07-06 16:14:34 -0700661 }
Chenjie Yu32717c32018-10-20 23:54:48 -0700662 return false;
Chenjie Yua0f02242018-07-06 16:14:34 -0700663}
664
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700665void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIndex,
666 const MetricDimensionKey& eventKey,
667 const ConditionKey& conditionKey,
668 bool condition, const LogEvent& event) {
Yangster-macb142cc82018-03-30 15:22:08 -0700669 int64_t eventTimeNs = event.GetElapsedTimestampNs();
Yao Chen6a8c7992017-11-29 20:02:07 +0000670 if (eventTimeNs < mCurrentBucketStartTimeNs) {
671 VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
672 (long long)mCurrentBucketStartTimeNs);
673 return;
674 }
Olivier Gaillard11203df2019-02-06 13:18:09 +0000675 mMatchedMetricDimensionKeys.insert(eventKey);
Yao Chen6a8c7992017-11-29 20:02:07 +0000676
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000677 if (!mIsPulled) {
678 // We cannot flush without doing a pull first.
679 flushIfNeededLocked(eventTimeNs);
680 }
Chenjie Yua7259ab2017-12-10 08:31:05 -0800681
Olivier Gaillardfbee9162019-04-11 11:48:01 +0100682 // We should not accumulate the data for pushed metrics when the condition is false.
683 bool shouldSkipForPushMetric = !mIsPulled && !condition;
684 // For pulled metrics, there are two cases:
685 // - to compute diffs, we need to process all the state changes
686 // - for non-diffs metrics, we should ignore the data if the condition wasn't true. If we have a
687 // state change from
688 // + True -> True: we should process the data, it might be a bucket boundary
689 // + True -> False: we als need to process the data.
690 bool shouldSkipForPulledMetric = mIsPulled && !mUseDiff
691 && mCondition != ConditionState::kTrue;
692 if (shouldSkipForPushMetric || shouldSkipForPulledMetric) {
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700693 VLOG("ValueMetric skip event because condition is false");
694 return;
695 }
696
Yangsterf2bee6f2017-11-29 12:01:05 -0800697 if (hitGuardRailLocked(eventKey)) {
Yangster8de69392017-11-27 13:48:29 -0800698 return;
699 }
Chenjie Yu32717c32018-10-20 23:54:48 -0700700 vector<Interval>& multiIntervals = mCurrentSlicedBucket[eventKey];
701 if (multiIntervals.size() < mFieldMatchers.size()) {
702 VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
703 multiIntervals.resize(mFieldMatchers.size());
Yangster-maca7fb12d2018-01-03 17:17:20 -0800704 }
Yao Chen6a8c7992017-11-29 20:02:07 +0000705
Misha Wagner26531762019-01-21 14:18:51 +0000706 // We only use anomaly detection under certain cases.
707 // N.B.: The anomaly detection cases were modified in order to fix an issue with value metrics
708 // containing multiple values. We tried to retain all previous behaviour, but we are unsure the
709 // previous behaviour was correct. At the time of the fix, anomaly detection had no owner.
710 // Whoever next works on it should look into the cases where it is triggered in this function.
711 // Discussion here: http://ag/6124370.
712 bool useAnomalyDetection = true;
713
Chenjie Yu32717c32018-10-20 23:54:48 -0700714 for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
715 const Matcher& matcher = mFieldMatchers[i];
716 Interval& interval = multiIntervals[i];
717 interval.valueIndex = i;
718 Value value;
719 if (!getDoubleOrLong(event, matcher, value)) {
720 VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
Misha Wagner1eee2212019-01-22 11:47:11 +0000721 StatsdStats::getInstance().noteBadValueType(mMetricId);
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700722 return;
723 }
Chenjie Yudbe5c502018-11-30 23:15:57 -0800724 interval.seenNewData = true;
Chenjie Yuc715b9e2018-10-19 07:52:12 -0700725
Chenjie Yu32717c32018-10-20 23:54:48 -0700726 if (mUseDiff) {
Chenjie Yu32717c32018-10-20 23:54:48 -0700727 if (!interval.hasBase) {
Chenjie Yuf275f612018-11-30 23:29:06 -0800728 if (mHasGlobalBase && mUseZeroDefaultBase) {
729 // The bucket has global base. This key does not.
730 // Optionally use zero as base.
731 interval.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
732 interval.hasBase = true;
733 } else {
734 // no base. just update base and return.
735 interval.base = value;
736 interval.hasBase = true;
Misha Wagner26531762019-01-21 14:18:51 +0000737 // If we're missing a base, do not use anomaly detection on incomplete data
738 useAnomalyDetection = false;
739 // Continue (instead of return) here in order to set interval.base and
740 // interval.hasBase for other intervals
741 continue;
Chenjie Yuf275f612018-11-30 23:29:06 -0800742 }
Chenjie Yu32717c32018-10-20 23:54:48 -0700743 }
744 Value diff;
745 switch (mValueDirection) {
746 case ValueMetric::INCREASING:
747 if (value >= interval.base) {
748 diff = value - interval.base;
749 } else if (mUseAbsoluteValueOnReset) {
750 diff = value;
751 } else {
752 VLOG("Unexpected decreasing value");
753 StatsdStats::getInstance().notePullDataError(mPullTagId);
754 interval.base = value;
Misha Wagner26531762019-01-21 14:18:51 +0000755 // If we've got bad data, do not use anomaly detection
756 useAnomalyDetection = false;
757 continue;
Chenjie Yu32717c32018-10-20 23:54:48 -0700758 }
759 break;
760 case ValueMetric::DECREASING:
761 if (interval.base >= value) {
762 diff = interval.base - value;
763 } else if (mUseAbsoluteValueOnReset) {
764 diff = value;
765 } else {
766 VLOG("Unexpected increasing value");
767 StatsdStats::getInstance().notePullDataError(mPullTagId);
768 interval.base = value;
Misha Wagner26531762019-01-21 14:18:51 +0000769 // If we've got bad data, do not use anomaly detection
770 useAnomalyDetection = false;
771 continue;
Chenjie Yu32717c32018-10-20 23:54:48 -0700772 }
773 break;
774 case ValueMetric::ANY:
775 diff = value - interval.base;
776 break;
777 default:
778 break;
779 }
780 interval.base = value;
781 value = diff;
Yao Chen6a8c7992017-11-29 20:02:07 +0000782 }
Chenjie Yu32717c32018-10-20 23:54:48 -0700783
784 if (interval.hasValue) {
785 switch (mAggregationType) {
786 case ValueMetric::SUM:
787 // for AVG, we add up and take average when flushing the bucket
788 case ValueMetric::AVG:
789 interval.value += value;
790 break;
791 case ValueMetric::MIN:
792 interval.value = std::min(value, interval.value);
793 break;
794 case ValueMetric::MAX:
795 interval.value = std::max(value, interval.value);
796 break;
797 default:
798 break;
799 }
800 } else {
801 interval.value = value;
802 interval.hasValue = true;
803 }
804 interval.sampleSize += 1;
Yangster8de69392017-11-27 13:48:29 -0800805 }
Bookatzde1b55622017-12-14 18:38:27 -0800806
Misha Wagner26531762019-01-21 14:18:51 +0000807 // Only trigger the tracker if all intervals are correct
808 if (useAnomalyDetection) {
809 // TODO: propgate proper values down stream when anomaly support doubles
810 long wholeBucketVal = multiIntervals[0].value.long_value;
811 auto prev = mCurrentFullBucket.find(eventKey);
812 if (prev != mCurrentFullBucket.end()) {
813 wholeBucketVal += prev->second;
814 }
815 for (auto& tracker : mAnomalyTrackers) {
Yao Chen4ce07292019-02-13 13:06:36 -0800816 tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
817 wholeBucketVal);
Misha Wagner26531762019-01-21 14:18:51 +0000818 }
Bookatzde1b55622017-12-14 18:38:27 -0800819 }
Yangster8de69392017-11-27 13:48:29 -0800820}
821
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000822// For pulled metrics, we always need to make sure we do a pull before flushing the bucket
823// if mCondition is true!
Yangster-macb142cc82018-03-30 15:22:08 -0700824void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
825 int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
Yangster-mac15f6bbc2018-04-08 11:52:26 -0700826 if (eventTimeNs < currentBucketEndTimeNs) {
Chenjie Yub3dda412017-10-24 13:41:59 -0700827 VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
David Chen27785a82018-01-19 17:06:45 -0800828 (long long)(currentBucketEndTimeNs));
Chenjie Yub3dda412017-10-24 13:41:59 -0700829 return;
830 }
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000831 int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
Olivier Gaillard6c75ecd2019-02-20 09:57:33 +0000832 int64_t nextBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
833 flushCurrentBucketLocked(eventTimeNs, nextBucketStartTimeNs);
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000834}
David Chen27785a82018-01-19 17:06:45 -0800835
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000836int64_t ValueMetricProducer::calcBucketsForwardCount(const int64_t& eventTimeNs) const {
837 int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
838 if (eventTimeNs < currentBucketEndTimeNs) {
839 return 0;
David Chen27785a82018-01-19 17:06:45 -0800840 }
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000841 return 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
David Chen27785a82018-01-19 17:06:45 -0800842}
843
Olivier Gaillard6c75ecd2019-02-20 09:57:33 +0000844void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
845 const int64_t& nextBucketStartTimeNs) {
Olivier Gaillarde63d9e02019-02-12 14:43:59 +0000846 if (mCondition == ConditionState::kUnknown) {
847 StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
848 }
849
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000850 int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
Olivier Gaillard47a9efc2019-02-22 15:43:31 +0000851 if (numBucketsForward > 1) {
852 VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
853 StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
854 // Something went wrong. Maybe the device was sleeping for a long time. It is better
855 // to mark the current bucket as invalid. The last pull might have been successful through.
856 invalidateCurrentBucketWithoutResetBase();
857 }
858
Chenjie Yub3dda412017-10-24 13:41:59 -0700859 VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
860 (int)mCurrentSlicedBucket.size());
Yangster-macb142cc82018-03-30 15:22:08 -0700861 int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
Chenjie Yu32717c32018-10-20 23:54:48 -0700862 int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
Yao Chene6cfb142019-04-08 12:00:01 -0700863 // Close the current bucket.
864 int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime);
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000865 bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
866 if (isBucketLargeEnough && !mCurrentBucketIsInvalid) {
David Chen81245fd2018-04-12 14:33:37 -0700867 // The current bucket is large enough to keep.
David Chen81245fd2018-04-12 14:33:37 -0700868 for (const auto& slice : mCurrentSlicedBucket) {
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000869 ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
Yao Chene6cfb142019-04-08 12:00:01 -0700870 bucket.mConditionTrueNs = conditionTrueDuration;
Chenjie Yu32717c32018-10-20 23:54:48 -0700871 // it will auto create new vector of ValuebucketInfo if the key is not found.
872 if (bucket.valueIndex.size() > 0) {
David Chen81245fd2018-04-12 14:33:37 -0700873 auto& bucketList = mPastBuckets[slice.first];
Chenjie Yu32717c32018-10-20 23:54:48 -0700874 bucketList.push_back(bucket);
David Chen81245fd2018-04-12 14:33:37 -0700875 }
Chenjie Yuae63b0a2018-04-10 14:59:31 -0700876 }
David Chen81245fd2018-04-12 14:33:37 -0700877 } else {
Chenjie Yu32717c32018-10-20 23:54:48 -0700878 mSkippedBuckets.emplace_back(mCurrentBucketStartTimeNs, bucketEndTime);
Chenjie Yub3dda412017-10-24 13:41:59 -0700879 }
880
Olivier Gaillardc3719912019-03-15 17:33:40 +0000881 appendToFullBucket(eventTimeNs, fullBucketEndTimeNs);
Olivier Gaillarda8b70112019-02-25 11:24:23 +0000882 initCurrentSlicedBucket(nextBucketStartTimeNs);
Yao Chene6cfb142019-04-08 12:00:01 -0700883 // Update the condition timer again, in case we skipped buckets.
884 mConditionTimer.newBucketStart(nextBucketStartTimeNs);
Olivier Gaillardc3719912019-03-15 17:33:40 +0000885 mCurrentBucketNum += numBucketsForward;
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000886}
887
888ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
889 const std::vector<Interval>& intervals) {
890 ValueBucket bucket;
891 bucket.mBucketStartNs = mCurrentBucketStartTimeNs;
892 bucket.mBucketEndNs = bucketEndTime;
893 for (const auto& interval : intervals) {
894 if (interval.hasValue) {
895 // skip the output if the diff is zero
896 if (mSkipZeroDiffOutput && mUseDiff && interval.value.isZero()) {
897 continue;
898 }
899 bucket.valueIndex.push_back(interval.valueIndex);
900 if (mAggregationType != ValueMetric::AVG) {
901 bucket.values.push_back(interval.value);
902 } else {
903 double sum = interval.value.type == LONG ? (double)interval.value.long_value
904 : interval.value.double_value;
905 bucket.values.push_back(Value((double)sum / interval.sampleSize));
906 }
907 }
908 }
909 return bucket;
910}
911
Olivier Gaillarda8b70112019-02-25 11:24:23 +0000912void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) {
913 StatsdStats::getInstance().noteBucketCount(mMetricId);
914 // Cleanup data structure to aggregate values.
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000915 for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
916 bool obsolete = true;
917 for (auto& interval : it->second) {
918 interval.hasValue = false;
919 interval.sampleSize = 0;
920 if (interval.seenNewData) {
921 obsolete = false;
922 }
923 interval.seenNewData = false;
924 }
925
926 if (obsolete) {
927 it = mCurrentSlicedBucket.erase(it);
928 } else {
929 it++;
930 }
931 }
Olivier Gaillarda8b70112019-02-25 11:24:23 +0000932
933 mCurrentBucketIsInvalid = false;
934 // If we do not have a global base when the condition is true,
935 // we will have incomplete bucket for the next bucket.
936 if (mUseDiff && !mHasGlobalBase && mCondition) {
937 mCurrentBucketIsInvalid = false;
938 }
939 mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
940 VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
941 (long long)mCurrentBucketStartTimeNs);
Olivier Gaillard9a5d3592019-02-05 15:12:39 +0000942}
943
944void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) {
Olivier Gaillardc3719912019-03-15 17:33:40 +0000945 bool isFullBucketReached = eventTimeNs > fullBucketEndTimeNs;
946 if (mCurrentBucketIsInvalid) {
947 if (isFullBucketReached) {
948 // If the bucket is invalid, we ignore the full bucket since it contains invalid data.
949 mCurrentFullBucket.clear();
950 }
951 // Current bucket is invalid, we do not add it to the full bucket.
952 return;
953 }
954
955 if (isFullBucketReached) { // If full bucket, send to anomaly tracker.
David Chen27785a82018-01-19 17:06:45 -0800956 // Accumulate partial buckets with current value and then send to anomaly tracker.
957 if (mCurrentFullBucket.size() > 0) {
958 for (const auto& slice : mCurrentSlicedBucket) {
Chenjie Yudbe5c502018-11-30 23:15:57 -0800959 if (hitFullBucketGuardRailLocked(slice.first)) {
960 continue;
961 }
Chenjie Yua0f02242018-07-06 16:14:34 -0700962 // TODO: fix this when anomaly can accept double values
Olivier Gaillardc3719912019-03-15 17:33:40 +0000963 auto& interval = slice.second[0];
964 if (interval.hasValue) {
965 mCurrentFullBucket[slice.first] += interval.value.long_value;
966 }
David Chen27785a82018-01-19 17:06:45 -0800967 }
968 for (const auto& slice : mCurrentFullBucket) {
969 for (auto& tracker : mAnomalyTrackers) {
970 if (tracker != nullptr) {
971 tracker->addPastBucket(slice.first, slice.second, mCurrentBucketNum);
972 }
973 }
974 }
975 mCurrentFullBucket.clear();
976 } else {
977 // Skip aggregating the partial buckets since there's no previous partial bucket.
978 for (const auto& slice : mCurrentSlicedBucket) {
979 for (auto& tracker : mAnomalyTrackers) {
980 if (tracker != nullptr) {
Chenjie Yua0f02242018-07-06 16:14:34 -0700981 // TODO: fix this when anomaly can accept double values
Olivier Gaillardc3719912019-03-15 17:33:40 +0000982 auto& interval = slice.second[0];
983 if (interval.hasValue) {
984 tracker->addPastBucket(slice.first, interval.value.long_value,
985 mCurrentBucketNum);
986 }
David Chen27785a82018-01-19 17:06:45 -0800987 }
988 }
989 }
990 }
991 } else {
992 // Accumulate partial bucket.
993 for (const auto& slice : mCurrentSlicedBucket) {
Chenjie Yua0f02242018-07-06 16:14:34 -0700994 // TODO: fix this when anomaly can accept double values
Olivier Gaillardc3719912019-03-15 17:33:40 +0000995 auto& interval = slice.second[0];
996 if (interval.hasValue) {
997 mCurrentFullBucket[slice.first] += interval.value.long_value;
998 }
David Chen27785a82018-01-19 17:06:45 -0800999 }
1000 }
Chenjie Yub3dda412017-10-24 13:41:59 -07001001}
1002
Yangsterf2bee6f2017-11-29 12:01:05 -08001003size_t ValueMetricProducer::byteSizeLocked() const {
Yangster-mace2cd6d52017-11-09 20:38:30 -08001004 size_t totalSize = 0;
1005 for (const auto& pair : mPastBuckets) {
1006 totalSize += pair.second.size() * kBucketSize;
1007 }
1008 return totalSize;
yro2b0f8862017-11-06 14:27:31 -08001009}
1010
Chenjie Yub3dda412017-10-24 13:41:59 -07001011} // namespace statsd
1012} // namespace os
Yao Chen93fe3a32017-11-02 13:52:59 -07001013} // namespace android