1 /*
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 STATSD_DEBUG false
18
19 #include "Log.h"
20
21 #include "DurationMetricProducer.h"
22
23 #include <limits.h>
24 #include <stdlib.h>
25
26 #include "guardrail/StatsdStats.h"
27 #include "metrics/parsing_utils/metrics_manager_util.h"
28 #include "stats_log_util.h"
29 #include "stats_util.h"
30
31 using android::util::FIELD_COUNT_REPEATED;
32 using android::util::FIELD_TYPE_BOOL;
33 using android::util::FIELD_TYPE_FLOAT;
34 using android::util::FIELD_TYPE_INT32;
35 using android::util::FIELD_TYPE_INT64;
36 using android::util::FIELD_TYPE_MESSAGE;
37 using android::util::FIELD_TYPE_STRING;
38 using android::util::ProtoOutputStream;
39 using std::string;
40 using std::unordered_map;
41 using std::vector;
42 using std::shared_ptr;
43
44 namespace android {
45 namespace os {
46 namespace statsd {
47
48 // for StatsLogReport
49 const int FIELD_ID_ID = 1;
50 const int FIELD_ID_DURATION_METRICS = 6;
51 const int FIELD_ID_TIME_BASE = 9;
52 const int FIELD_ID_BUCKET_SIZE = 10;
53 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
54 const int FIELD_ID_IS_ACTIVE = 14;
55 // for DurationMetricDataWrapper
56 const int FIELD_ID_DATA = 1;
57 // for DurationMetricData
58 const int FIELD_ID_DIMENSION_IN_WHAT = 1;
59 const int FIELD_ID_BUCKET_INFO = 3;
60 const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
61 const int FIELD_ID_SLICE_BY_STATE = 6;
62 // for DurationBucketInfo
63 const int FIELD_ID_DURATION = 3;
64 const int FIELD_ID_BUCKET_NUM = 4;
65 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
66 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
67 const int FIELD_ID_CONDITION_TRUE_NS = 7;
68
DurationMetricProducer(const ConfigKey & key,const DurationMetric & metric,const int conditionIndex,const vector<ConditionState> & initialConditionCache,const int whatIndex,const int startIndex,const int stopIndex,const int stopAllIndex,const bool nesting,const sp<ConditionWizard> & wizard,const uint64_t protoHash,const FieldMatcher & internalDimensions,const int64_t timeBaseNs,const int64_t startTimeNs,const unordered_map<int,shared_ptr<Activation>> & eventActivationMap,const unordered_map<int,vector<shared_ptr<Activation>>> & eventDeactivationMap,const vector<int> & slicedStateAtoms,const unordered_map<int,unordered_map<int,int64_t>> & stateGroupMap)69 DurationMetricProducer::DurationMetricProducer(
70 const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
71 const vector<ConditionState>& initialConditionCache, const int whatIndex,
72 const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
73 const sp<ConditionWizard>& wizard, const uint64_t protoHash,
74 const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
75 const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
76 const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
77 const vector<int>& slicedStateAtoms,
78 const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
79 : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
80 protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
81 stateGroupMap, getAppUpgradeBucketSplit(metric)),
82 mAggregationType(metric.aggregation_type()),
83 mStartIndex(startIndex),
84 mStopIndex(stopIndex),
85 mStopAllIndex(stopAllIndex),
86 mNested(nesting),
87 mContainANYPositionInInternalDimensions(false) {
88 if (metric.has_bucket()) {
89 mBucketSizeNs =
90 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
91 } else {
92 mBucketSizeNs = LLONG_MAX;
93 }
94
95 if (metric.has_threshold()) {
96 mUploadThreshold = metric.threshold();
97 }
98
99 if (metric.has_dimensions_in_what()) {
100 translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
101 mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
102 }
103
104 if (internalDimensions.has_field()) {
105 translateFieldMatcher(internalDimensions, &mInternalDimensions);
106 mContainANYPositionInInternalDimensions = HasPositionANY(internalDimensions);
107 }
108 if (mContainANYPositionInInternalDimensions) {
109 ALOGE("Position ANY in internal dimension not supported.");
110 }
111 if (mContainANYPositionInDimensionsInWhat) {
112 ALOGE("Position ANY in dimension_in_what not supported.");
113 }
114
115 // Dimensions in what must be subset of internal dimensions
116 if (!subsetDimensions(mDimensionsInWhat, mInternalDimensions)) {
117 ALOGE("Dimensions in what must be a subset of the internal dimensions");
118 // TODO: Add invalidConfigReason
119 mValid = false;
120 }
121
122 mShouldUseNestedDimensions = ShouldUseNestedDimensions(metric.dimensions_in_what());
123
124 if (metric.links().size() > 0) {
125 for (const auto& link : metric.links()) {
126 Metric2Condition mc;
127 mc.conditionId = link.condition();
128 translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
129 translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
130 if (!subsetDimensions(mc.metricFields, mInternalDimensions)) {
131 ALOGE(("Condition links must be a subset of the internal dimensions"));
132 // TODO: Add invalidConfigReason
133 mValid = false;
134 }
135 mMetric2ConditionLinks.push_back(mc);
136 }
137 mConditionSliced = true;
138 }
139 mUnSlicedPartCondition = ConditionState::kUnknown;
140
141 for (const auto& stateLink : metric.state_link()) {
142 Metric2State ms;
143 ms.stateAtomId = stateLink.state_atom_id();
144 translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
145 translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
146 if (!subsetDimensions(ms.metricFields, mInternalDimensions)) {
147 ALOGE(("State links must be a subset of the dimensions in what internal dimensions"));
148 // TODO: Add invalidConfigReason
149 mValid = false;
150 }
151 mMetric2StateLinks.push_back(ms);
152 }
153
154 mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
155 if (mWizard != nullptr && mConditionTrackerIndex >= 0 &&
156 mMetric2ConditionLinks.size() == 1) {
157 mHasLinksToAllConditionDimensionsInTracker = mWizard->equalOutputDimensions(
158 mConditionTrackerIndex, mMetric2ConditionLinks.begin()->conditionFields);
159 }
160 flushIfNeededLocked(startTimeNs);
161 // Adjust start for partial bucket
162 mCurrentBucketStartTimeNs = startTimeNs;
163 VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)mMetricId,
164 (long long)mBucketSizeNs, (long long)mTimeBaseNs);
165
166 initTrueDimensions(whatIndex, startTimeNs);
167 mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs, mCurrentBucketStartTimeNs);
168 mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
169 mCurrentBucketStartTimeNs);
170 }
171
~DurationMetricProducer()172 DurationMetricProducer::~DurationMetricProducer() {
173 VLOG("~DurationMetric() called");
174 }
175
onConfigUpdatedLocked(const StatsdConfig & config,const int configIndex,const int metricIndex,const vector<sp<AtomMatchingTracker>> & allAtomMatchingTrackers,const unordered_map<int64_t,int> & oldAtomMatchingTrackerMap,const unordered_map<int64_t,int> & newAtomMatchingTrackerMap,const sp<EventMatcherWizard> & matcherWizard,const vector<sp<ConditionTracker>> & allConditionTrackers,const unordered_map<int64_t,int> & conditionTrackerMap,const sp<ConditionWizard> & wizard,const unordered_map<int64_t,int> & metricToActivationMap,unordered_map<int,vector<int>> & trackerToMetricMap,unordered_map<int,vector<int>> & conditionToMetricMap,unordered_map<int,vector<int>> & activationAtomTrackerToMetricMap,unordered_map<int,vector<int>> & deactivationAtomTrackerToMetricMap,vector<int> & metricsWithActivation)176 optional<InvalidConfigReason> DurationMetricProducer::onConfigUpdatedLocked(
177 const StatsdConfig& config, const int configIndex, const int metricIndex,
178 const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
179 const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
180 const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
181 const sp<EventMatcherWizard>& matcherWizard,
182 const vector<sp<ConditionTracker>>& allConditionTrackers,
183 const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
184 const unordered_map<int64_t, int>& metricToActivationMap,
185 unordered_map<int, vector<int>>& trackerToMetricMap,
186 unordered_map<int, vector<int>>& conditionToMetricMap,
187 unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
188 unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
189 vector<int>& metricsWithActivation) {
190 optional<InvalidConfigReason> invalidConfigReason = MetricProducer::onConfigUpdatedLocked(
191 config, configIndex, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap,
192 newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, conditionTrackerMap,
193 wizard, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
194 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
195 metricsWithActivation);
196 if (invalidConfigReason.has_value()) {
197 return invalidConfigReason;
198 }
199
200 const DurationMetric& metric = config.duration_metric(configIndex);
201 const auto& what_it = conditionTrackerMap.find(metric.what());
202 if (what_it == conditionTrackerMap.end()) {
203 ALOGE("DurationMetric's \"what\" is not present in the config");
204 return createInvalidConfigReasonWithPredicate(
205 INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_FOUND, mMetricId, metric.what());
206 }
207
208 const Predicate& durationWhat = config.predicate(what_it->second);
209 if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
210 ALOGE("DurationMetric's \"what\" must be a simple condition");
211 return createInvalidConfigReasonWithPredicate(
212 INVALID_CONFIG_REASON_DURATION_METRIC_WHAT_NOT_SIMPLE, mMetricId, metric.what());
213 }
214
215 const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
216
217 // Update indices: mStartIndex, mStopIndex, mStopAllIndex, mConditionIndex and MetricsManager
218 // maps.
219 invalidConfigReason = handleMetricWithAtomMatchingTrackers(
220 simplePredicate.start(), mMetricId, metricIndex, metric.has_dimensions_in_what(),
221 allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap, mStartIndex);
222 if (invalidConfigReason.has_value()) {
223 ALOGE("Duration metrics must specify a valid start event matcher");
224 return invalidConfigReason;
225 }
226
227 if (simplePredicate.has_stop()) {
228 invalidConfigReason = handleMetricWithAtomMatchingTrackers(
229 simplePredicate.stop(), mMetricId, metricIndex, metric.has_dimensions_in_what(),
230 allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap, mStopIndex);
231 if (invalidConfigReason.has_value()) {
232 return invalidConfigReason;
233 }
234 }
235
236 if (simplePredicate.has_stop_all()) {
237 invalidConfigReason = handleMetricWithAtomMatchingTrackers(
238 simplePredicate.stop_all(), mMetricId, metricIndex, metric.has_dimensions_in_what(),
239 allAtomMatchingTrackers, newAtomMatchingTrackerMap, trackerToMetricMap,
240 mStopAllIndex);
241 if (invalidConfigReason.has_value()) {
242 return invalidConfigReason;
243 }
244 }
245
246 if (metric.has_condition()) {
247 invalidConfigReason = handleMetricWithConditions(
248 metric.condition(), mMetricId, metricIndex, conditionTrackerMap, metric.links(),
249 allConditionTrackers, mConditionTrackerIndex, conditionToMetricMap);
250 if (invalidConfigReason.has_value()) {
251 return invalidConfigReason;
252 }
253 }
254
255 for (const auto& it : mCurrentSlicedDurationTrackerMap) {
256 it.second->onConfigUpdated(wizard, mConditionTrackerIndex);
257 }
258
259 return nullopt;
260 }
261
initTrueDimensions(const int whatIndex,const int64_t startTimeNs)262 void DurationMetricProducer::initTrueDimensions(const int whatIndex, const int64_t startTimeNs) {
263 std::lock_guard<std::mutex> lock(mMutex);
264 // Currently whatIndex will only be -1 in tests. In the future, we might want to avoid creating
265 // a ConditionTracker if the condition is only used in the "what" of a duration metric. In that
266 // scenario, -1 can also be passed.
267 if (whatIndex == -1) {
268 return;
269 }
270 const map<HashableDimensionKey, int>* slicedWhatMap = mWizard->getSlicedDimensionMap(whatIndex);
271 for (const auto& [internalDimKey, count] : *slicedWhatMap) {
272 for (int i = 0; i < count; i++) {
273 // Fake start events.
274 handleMatchedLogEventValuesLocked(mStartIndex, internalDimKey.getValues(), startTimeNs);
275 }
276 }
277 }
278
addAnomalyTracker(const Alert & alert,const sp<AlarmMonitor> & anomalyAlarmMonitor,const UpdateStatus & updateStatus,const int64_t updateTimeNs)279 sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
280 const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor,
281 const UpdateStatus& updateStatus, const int64_t updateTimeNs) {
282 std::lock_guard<std::mutex> lock(mMutex);
283 if (mAggregationType == DurationMetric_AggregationType_SUM) {
284 if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) {
285 ALOGW("invalid alert for SUM: threshold (%f) > possible recordable value (%d x %lld)",
286 alert.trigger_if_sum_gt(), alert.num_buckets(), (long long)mBucketSizeNs);
287 return nullptr;
288 }
289 }
290 sp<AnomalyTracker> anomalyTracker =
291 new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
292 // The update status is either new or replaced.
293 addAnomalyTrackerLocked(anomalyTracker, updateStatus, updateTimeNs);
294 return anomalyTracker;
295 }
296
297 // Adds an AnomalyTracker that has already been created.
298 // Note: this gets called on config updates, and will only get called if the metric and the
299 // associated alert are preserved, which means the AnomalyTracker must be a DurationAnomalyTracker.
addAnomalyTracker(sp<AnomalyTracker> & anomalyTracker,const int64_t updateTimeNs)300 void DurationMetricProducer::addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker,
301 const int64_t updateTimeNs) {
302 std::lock_guard<std::mutex> lock(mMutex);
303 addAnomalyTrackerLocked(anomalyTracker, UpdateStatus::UPDATE_PRESERVE, updateTimeNs);
304 }
305
addAnomalyTrackerLocked(sp<AnomalyTracker> & anomalyTracker,const UpdateStatus & updateStatus,const int64_t updateTimeNs)306 void DurationMetricProducer::addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker,
307 const UpdateStatus& updateStatus,
308 const int64_t updateTimeNs) {
309 mAnomalyTrackers.push_back(anomalyTracker);
310 for (const auto& [_, durationTracker] : mCurrentSlicedDurationTrackerMap) {
311 durationTracker->addAnomalyTracker(anomalyTracker, updateStatus, updateTimeNs);
312 }
313 }
onStateChanged(const int64_t eventTimeNs,const int32_t atomId,const HashableDimensionKey & primaryKey,const FieldValue & oldState,const FieldValue & newState)314 void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
315 const HashableDimensionKey& primaryKey,
316 const FieldValue& oldState,
317 const FieldValue& newState) {
318 // Check if this metric has a StateMap. If so, map the new state value to
319 // the correct state group id.
320 FieldValue newStateCopy = newState;
321 mapStateValue(atomId, &newStateCopy);
322
323 flushIfNeededLocked(eventTimeNs);
324
325 // Each duration tracker is mapped to a different whatKey (a set of values from the
326 // dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the
327 // state change event are a subset of the tracker's whatKey field values.
328 //
329 // Ex. For a duration metric dimensioned on uid and tag:
330 // DurationTracker1 whatKey = uid: 1001, tag: 1
331 // DurationTracker2 whatKey = uid: 1002, tag 1
332 //
333 // If the state change primaryKey = uid: 1001, we only notify DurationTracker1 of a state
334 // change.
335 for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
336 if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) {
337 continue;
338 }
339 whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy);
340 }
341 }
342
createDurationTracker(const MetricDimensionKey & eventKey) const343 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
344 const MetricDimensionKey& eventKey) const {
345 switch (mAggregationType) {
346 case DurationMetric_AggregationType_SUM:
347 return make_unique<OringDurationTracker>(
348 mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
349 mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
350 mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
351 case DurationMetric_AggregationType_MAX_SPARSE:
352 return make_unique<MaxDurationTracker>(
353 mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
354 mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
355 mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
356 }
357 }
358
359 // SlicedConditionChange optimization case 1:
360 // 1. If combination condition, logical operation is AND, only one sliced child predicate.
361 // 2. The links covers all dimension fields in the sliced child condition predicate.
onSlicedConditionMayChangeLocked_opt1(const int64_t eventTime)362 void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(const int64_t eventTime) {
363 if (mMetric2ConditionLinks.size() != 1 ||
364 !mHasLinksToAllConditionDimensionsInTracker) {
365 return;
366 }
367
368 bool currentUnSlicedPartCondition = true;
369 if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
370 ConditionState unslicedPartState =
371 mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
372 // When the unsliced part is still false, return directly.
373 if (mUnSlicedPartCondition == ConditionState::kFalse &&
374 unslicedPartState == ConditionState::kFalse) {
375 return;
376 }
377 mUnSlicedPartCondition = unslicedPartState;
378 currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
379 }
380
381 auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
382 auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
383
384 // The condition change is from the unsliced predicates.
385 // We need to find out the true dimensions from the sliced predicate and flip their condition
386 // state based on the new unsliced condition state.
387 if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
388 (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
389 const map<HashableDimensionKey, int>* slicedConditionMap =
390 mWizard->getSlicedDimensionMap(mConditionTrackerIndex);
391 for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
392 HashableDimensionKey linkedConditionDimensionKey;
393 getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
394 &linkedConditionDimensionKey);
395 const auto& slicedConditionIt = slicedConditionMap->find(linkedConditionDimensionKey);
396 if (slicedConditionIt != slicedConditionMap->end() && slicedConditionIt->second > 0) {
397 whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
398 }
399 }
400 } else {
401 // Handle the condition change from the sliced predicate.
402 if (currentUnSlicedPartCondition) {
403 for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
404 HashableDimensionKey linkedConditionDimensionKey;
405 getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
406 &linkedConditionDimensionKey);
407 if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) !=
408 dimensionsChangedToTrue->end()) {
409 whatIt.second->onConditionChanged(true, eventTime);
410 }
411 if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
412 dimensionsChangedToFalse->end()) {
413 whatIt.second->onConditionChanged(false, eventTime);
414 }
415 }
416 }
417 }
418 }
419
onSlicedConditionMayChangeInternalLocked(const int64_t eventTimeNs)420 void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(const int64_t eventTimeNs) {
421 bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
422 if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker) {
423 onSlicedConditionMayChangeLocked_opt1(eventTimeNs);
424 return;
425 }
426
427 // Now for each of the on-going event, check if the condition has changed for them.
428 for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
429 whatIt.second->onSlicedConditionMayChange(eventTimeNs);
430 }
431 }
432
onSlicedConditionMayChangeLocked(bool overallCondition,const int64_t eventTime)433 void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
434 const int64_t eventTime) {
435 VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
436
437 if (!mIsActive) {
438 return;
439 }
440
441 flushIfNeededLocked(eventTime);
442
443 if (!mConditionSliced) {
444 return;
445 }
446
447 onSlicedConditionMayChangeInternalLocked(eventTime);
448 }
449
onActiveStateChangedLocked(const int64_t eventTimeNs,const bool isActive)450 void DurationMetricProducer::onActiveStateChangedLocked(const int64_t eventTimeNs,
451 const bool isActive) {
452 MetricProducer::onActiveStateChangedLocked(eventTimeNs, isActive);
453
454 if (!mConditionSliced) {
455 if (ConditionState::kTrue != mCondition) {
456 return;
457 }
458
459 if (isActive) {
460 flushIfNeededLocked(eventTimeNs);
461 }
462
463 for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
464 whatIt.second->onConditionChanged(isActive, eventTimeNs);
465 }
466 mConditionTimer.onConditionChanged(isActive, eventTimeNs);
467 } else if (isActive) {
468 flushIfNeededLocked(eventTimeNs);
469 onSlicedConditionMayChangeInternalLocked(eventTimeNs);
470 } else { // mConditionSliced == true && !isActive
471 for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
472 whatIt.second->onConditionChanged(isActive, eventTimeNs);
473 }
474 }
475 }
476
onConditionChangedLocked(const bool conditionMet,const int64_t eventTime)477 void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
478 const int64_t eventTime) {
479 VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
480 mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
481
482 if (!mIsActive) {
483 return;
484 }
485
486 flushIfNeededLocked(eventTime);
487 for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
488 whatIt.second->onConditionChanged(conditionMet, eventTime);
489 }
490
491 mConditionTimer.onConditionChanged(mCondition, eventTime);
492 }
493
dropDataLocked(const int64_t dropTimeNs)494 void DurationMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
495 flushIfNeededLocked(dropTimeNs);
496 StatsdStats::getInstance().noteBucketDropped(mMetricId);
497 mPastBuckets.clear();
498 }
499
clearPastBucketsLocked(const int64_t dumpTimeNs)500 void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
501 flushIfNeededLocked(dumpTimeNs);
502 mPastBuckets.clear();
503 }
504
onDumpReportLocked(const int64_t dumpTimeNs,const bool include_current_partial_bucket,const bool erase_data,const DumpLatency dumpLatency,std::set<string> * str_set,ProtoOutputStream * protoOutput)505 void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
506 const bool include_current_partial_bucket,
507 const bool erase_data,
508 const DumpLatency dumpLatency,
509 std::set<string> *str_set,
510 ProtoOutputStream* protoOutput) {
511 if (include_current_partial_bucket) {
512 flushLocked(dumpTimeNs);
513 } else {
514 flushIfNeededLocked(dumpTimeNs);
515 }
516 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
517 protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
518
519 if (mPastBuckets.empty()) {
520 VLOG(" Duration metric, empty return");
521 return;
522 }
523
524 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
525 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
526
527 if (!mShouldUseNestedDimensions) {
528 if (!mDimensionsInWhat.empty()) {
529 uint64_t dimenPathToken = protoOutput->start(
530 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
531 writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
532 protoOutput->end(dimenPathToken);
533 }
534 }
535
536 uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
537
538 VLOG("Duration metric %lld dump report now...", (long long)mMetricId);
539
540 for (const auto& pair : mPastBuckets) {
541 const MetricDimensionKey& dimensionKey = pair.first;
542 VLOG(" dimension key %s", dimensionKey.toString().c_str());
543
544 uint64_t wrapperToken =
545 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
546
547 // First fill dimension.
548 if (mShouldUseNestedDimensions) {
549 uint64_t dimensionToken = protoOutput->start(
550 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
551 writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
552 protoOutput->end(dimensionToken);
553 } else {
554 writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
555 FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
556 }
557 // Then fill slice_by_state.
558 for (auto state : dimensionKey.getStateValuesKey().getValues()) {
559 uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
560 FIELD_ID_SLICE_BY_STATE);
561 writeStateToProto(state, protoOutput);
562 protoOutput->end(stateToken);
563 }
564 // Then fill bucket_info (DurationBucketInfo).
565 for (const auto& bucket : pair.second) {
566 uint64_t bucketInfoToken = protoOutput->start(
567 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
568 if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
569 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
570 (long long)NanoToMillis(bucket.mBucketStartNs));
571 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
572 (long long)NanoToMillis(bucket.mBucketEndNs));
573 } else {
574 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
575 (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
576 }
577 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration);
578
579 // We only write the condition timer value if the metric has a
580 // condition and isn't sliced by state or condition.
581 // TODO(b/268531762): Slice the condition timer by state and condition
582 if (mConditionTrackerIndex >= 0 && mSlicedStateAtoms.empty() && !mConditionSliced) {
583 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
584 (long long)bucket.mConditionTrueNs);
585 }
586
587 protoOutput->end(bucketInfoToken);
588 VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs,
589 (long long)bucket.mBucketEndNs, (long long)bucket.mDuration);
590 }
591
592 protoOutput->end(wrapperToken);
593 }
594
595 protoOutput->end(protoToken);
596 if (erase_data) {
597 mPastBuckets.clear();
598 }
599 }
600
flushIfNeededLocked(const int64_t & eventTimeNs)601 void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
602 int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
603
604 if (currentBucketEndTimeNs > eventTimeNs) {
605 return;
606 }
607 VLOG("flushing...........");
608 int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
609 int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
610 flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
611
612 mCurrentBucketNum += numBucketsForward;
613 }
614
flushCurrentBucketLocked(const int64_t & eventTimeNs,const int64_t & nextBucketStartTimeNs)615 void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
616 const int64_t& nextBucketStartTimeNs) {
617 const auto [globalConditionTrueNs, globalConditionCorrectionNs] =
618 mConditionTimer.newBucketStart(eventTimeNs, nextBucketStartTimeNs);
619
620 for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
621 whatIt != mCurrentSlicedDurationTrackerMap.end();) {
622 if (whatIt->second->flushCurrentBucket(eventTimeNs, mUploadThreshold, globalConditionTrueNs,
623 &mPastBuckets)) {
624 VLOG("erase bucket for key %s", whatIt->first.toString().c_str());
625 whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
626 } else {
627 ++whatIt;
628 }
629 }
630
631 StatsdStats::getInstance().noteBucketCount(mMetricId);
632 mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
633 // Reset mHasHitGuardrail boolean since bucket was reset
634 mHasHitGuardrail = false;
635 }
636
dumpStatesLocked(FILE * out,bool verbose) const637 void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
638 if (mCurrentSlicedDurationTrackerMap.size() == 0) {
639 return;
640 }
641
642 fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
643 (unsigned long)mCurrentSlicedDurationTrackerMap.size());
644 if (verbose) {
645 for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
646 fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str());
647 whatIt.second->dumpStates(out, verbose);
648 }
649 }
650 }
651
hitGuardRailLocked(const MetricDimensionKey & newKey)652 bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
653 auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
654 if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
655 // 1. Report the tuple count if the tuple count > soft limit
656 if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
657 size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
658 StatsdStats::getInstance().noteMetricDimensionSize(
659 mConfigKey, mMetricId, newTupleCount);
660 // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
661 if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
662 if (!mHasHitGuardrail) {
663 ALOGE("DurationMetric %lld dropping data for what dimension key %s",
664 (long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
665 mHasHitGuardrail = true;
666 }
667 StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
668 return true;
669 }
670 }
671 }
672 return false;
673 }
674
handleStartEvent(const MetricDimensionKey & eventKey,const ConditionKey & conditionKeys,bool condition,const int64_t eventTimeNs,const vector<FieldValue> & eventValues)675 void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
676 const ConditionKey& conditionKeys, bool condition,
677 const int64_t eventTimeNs,
678 const vector<FieldValue>& eventValues) {
679 const auto& whatKey = eventKey.getDimensionKeyInWhat();
680 auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
681 if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
682 if (hitGuardRailLocked(eventKey)) {
683 return;
684 }
685 mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey);
686 }
687
688 auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
689 if (mUseWhatDimensionAsInternalDimension) {
690 it->second->noteStart(whatKey, condition, eventTimeNs, conditionKeys);
691 return;
692 }
693
694 if (mInternalDimensions.empty()) {
695 it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, eventTimeNs, conditionKeys);
696 } else {
697 HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
698 filterValues(mInternalDimensions, eventValues, &dimensionKey);
699 it->second->noteStart(dimensionKey, condition, eventTimeNs, conditionKeys);
700 }
701 }
702
onMatchedLogEventInternalLocked(const size_t matcherIndex,const MetricDimensionKey & eventKey,const ConditionKey & conditionKeys,bool condition,const LogEvent & event,const map<int,HashableDimensionKey> & statePrimaryKeys)703 void DurationMetricProducer::onMatchedLogEventInternalLocked(
704 const size_t matcherIndex, const MetricDimensionKey& eventKey,
705 const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
706 const map<int, HashableDimensionKey>& statePrimaryKeys) {
707 ALOGW("Not used in duration tracker.");
708 }
709
onMatchedLogEventLocked(const size_t matcherIndex,const LogEvent & event)710 void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
711 const LogEvent& event) {
712 handleMatchedLogEventValuesLocked(matcherIndex, event.getValues(),
713 event.GetElapsedTimestampNs());
714 }
715
handleMatchedLogEventValuesLocked(const size_t matcherIndex,const vector<FieldValue> & values,const int64_t eventTimeNs)716 void DurationMetricProducer::handleMatchedLogEventValuesLocked(const size_t matcherIndex,
717 const vector<FieldValue>& values,
718 const int64_t eventTimeNs) {
719 if (eventTimeNs < mTimeBaseNs) {
720 return;
721 }
722
723 if (mIsActive) {
724 flushIfNeededLocked(eventTimeNs);
725 }
726
727 // Handles Stopall events.
728 if ((int)matcherIndex == mStopAllIndex) {
729 for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
730 whatIt.second->noteStopAll(eventTimeNs);
731 }
732 return;
733 }
734
735 if (!passesSampleCheckLocked(values)) {
736 return;
737 }
738
739 HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
740 if (!mDimensionsInWhat.empty()) {
741 filterValues(mDimensionsInWhat, values, &dimensionInWhat);
742 }
743
744 // Stores atom id to primary key pairs for each state atom that the metric is
745 // sliced by.
746 std::map<int, HashableDimensionKey> statePrimaryKeys;
747
748 // For states with primary fields, use MetricStateLinks to get the primary
749 // field values from the log event. These values will form a primary key
750 // that will be used to query StateTracker for the correct state value.
751 for (const auto& stateLink : mMetric2StateLinks) {
752 getDimensionForState(values, stateLink, &statePrimaryKeys[stateLink.stateAtomId]);
753 }
754
755 // For each sliced state, query StateTracker for the state value using
756 // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
757 //
758 // Expected functionality: for any case where the MetricStateLinks are
759 // initialized incorrectly (ex. # of state links != # of primary fields, no
760 // links are provided for a state with primary fields, links are provided
761 // in the wrong order, etc.), StateTracker will simply return kStateUnknown
762 // when queried using an incorrect key.
763 HashableDimensionKey stateValuesKey = DEFAULT_DIMENSION_KEY;
764 for (auto atomId : mSlicedStateAtoms) {
765 FieldValue value;
766 if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
767 // found a primary key for this state, query using the key
768 queryStateValue(atomId, statePrimaryKeys[atomId], &value);
769 } else {
770 // if no MetricStateLinks exist for this state atom,
771 // query using the default dimension key (empty HashableDimensionKey)
772 queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
773 }
774 mapStateValue(atomId, &value);
775 stateValuesKey.addValue(value);
776 }
777
778 // Handles Stop events.
779 if ((int)matcherIndex == mStopIndex) {
780 if (mUseWhatDimensionAsInternalDimension) {
781 auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
782 if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
783 whatIt->second->noteStop(dimensionInWhat, eventTimeNs, false);
784 }
785 return;
786 }
787
788 HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
789 if (!mInternalDimensions.empty()) {
790 filterValues(mInternalDimensions, values, &internalDimensionKey);
791 }
792
793 auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
794 if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
795 whatIt->second->noteStop(internalDimensionKey, eventTimeNs, false);
796 }
797 return;
798 }
799
800 bool condition;
801 ConditionKey conditionKey;
802 if (mConditionSliced) {
803 for (const auto& link : mMetric2ConditionLinks) {
804 getDimensionForCondition(values, link, &conditionKey[link.conditionId]);
805 }
806
807 auto conditionState =
808 mWizard->query(mConditionTrackerIndex, conditionKey,
809 !mHasLinksToAllConditionDimensionsInTracker);
810 condition = conditionState == ConditionState::kTrue;
811 } else {
812 // TODO: The unknown condition state is not handled here, we should fix it.
813 condition = mCondition == ConditionState::kTrue;
814 }
815
816 condition = condition && mIsActive;
817
818 handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
819 eventTimeNs, values);
820 }
821
byteSizeLocked() const822 size_t DurationMetricProducer::byteSizeLocked() const {
823 size_t totalSize = 0;
824 for (const auto& pair : mPastBuckets) {
825 totalSize += pair.second.size() * kBucketSize;
826 }
827 return totalSize;
828 }
829
830 } // namespace statsd
831 } // namespace os
832 } // namespace android
833