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