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