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 #include "DurationMetricProducer.h"
21 #include "guardrail/StatsdStats.h"
22 #include "stats_util.h"
23 #include "stats_log_util.h"
24 
25 #include <limits.h>
26 #include <stdlib.h>
27 
28 using android::util::FIELD_COUNT_REPEATED;
29 using android::util::FIELD_TYPE_BOOL;
30 using android::util::FIELD_TYPE_FLOAT;
31 using android::util::FIELD_TYPE_INT32;
32 using android::util::FIELD_TYPE_INT64;
33 using android::util::FIELD_TYPE_MESSAGE;
34 using android::util::FIELD_TYPE_STRING;
35 using android::util::ProtoOutputStream;
36 using std::string;
37 using std::unordered_map;
38 using std::vector;
39 using std::shared_ptr;
40 
41 namespace android {
42 namespace os {
43 namespace statsd {
44 
45 // for StatsLogReport
46 const int FIELD_ID_ID = 1;
47 const int FIELD_ID_DURATION_METRICS = 6;
48 const int FIELD_ID_TIME_BASE = 9;
49 const int FIELD_ID_BUCKET_SIZE = 10;
50 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
51 const int FIELD_ID_IS_ACTIVE = 14;
52 // for DurationMetricDataWrapper
53 const int FIELD_ID_DATA = 1;
54 // for DurationMetricData
55 const int FIELD_ID_DIMENSION_IN_WHAT = 1;
56 const int FIELD_ID_BUCKET_INFO = 3;
57 const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
58 const int FIELD_ID_SLICE_BY_STATE = 6;
59 // for DurationBucketInfo
60 const int FIELD_ID_DURATION = 3;
61 const int FIELD_ID_BUCKET_NUM = 4;
62 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
63 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
64 
DurationMetricProducer(const ConfigKey & key,const DurationMetric & metric,const int conditionIndex,const vector<ConditionState> & initialConditionCache,const size_t startIndex,const size_t stopIndex,const size_t stopAllIndex,const bool nesting,const sp<ConditionWizard> & wizard,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)65 DurationMetricProducer::DurationMetricProducer(
66         const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
67         const vector<ConditionState>& initialConditionCache, const size_t startIndex,
68         const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
69         const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
70         const int64_t timeBaseNs, const int64_t startTimeNs,
71         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
72         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
73         const vector<int>& slicedStateAtoms,
74         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
75     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
76                      eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
77       mAggregationType(metric.aggregation_type()),
78       mStartIndex(startIndex),
79       mStopIndex(stopIndex),
80       mStopAllIndex(stopAllIndex),
81       mNested(nesting),
82       mContainANYPositionInInternalDimensions(false) {
83     if (metric.has_bucket()) {
84         mBucketSizeNs =
85                 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
86     } else {
87         mBucketSizeNs = LLONG_MAX;
88     }
89 
90     if (metric.has_dimensions_in_what()) {
91         translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
92         mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
93     }
94 
95     if (internalDimensions.has_field()) {
96         translateFieldMatcher(internalDimensions, &mInternalDimensions);
97         mContainANYPositionInInternalDimensions = HasPositionANY(internalDimensions);
98     }
99     if (mContainANYPositionInInternalDimensions) {
100         ALOGE("Position ANY in internal dimension not supported.");
101     }
102     if (mContainANYPositionInDimensionsInWhat) {
103         ALOGE("Position ANY in dimension_in_what not supported.");
104     }
105 
106     mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
107 
108     if (metric.links().size() > 0) {
109         for (const auto& link : metric.links()) {
110             Metric2Condition mc;
111             mc.conditionId = link.condition();
112             translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
113             translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
114             mMetric2ConditionLinks.push_back(mc);
115         }
116         mConditionSliced = true;
117     }
118     mUnSlicedPartCondition = ConditionState::kUnknown;
119 
120     for (const auto& stateLink : metric.state_link()) {
121         Metric2State ms;
122         ms.stateAtomId = stateLink.state_atom_id();
123         translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
124         translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
125         mMetric2StateLinks.push_back(ms);
126     }
127 
128     mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
129     if (mWizard != nullptr && mConditionTrackerIndex >= 0 &&
130             mMetric2ConditionLinks.size() == 1) {
131         mHasLinksToAllConditionDimensionsInTracker = mWizard->equalOutputDimensions(
132                 mConditionTrackerIndex, mMetric2ConditionLinks.begin()->conditionFields);
133     }
134     flushIfNeededLocked(startTimeNs);
135     // Adjust start for partial bucket
136     mCurrentBucketStartTimeNs = startTimeNs;
137     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
138          (long long)mBucketSizeNs, (long long)mTimeBaseNs);
139 }
140 
~DurationMetricProducer()141 DurationMetricProducer::~DurationMetricProducer() {
142     VLOG("~DurationMetric() called");
143 }
144 
addAnomalyTracker(const Alert & alert,const sp<AlarmMonitor> & anomalyAlarmMonitor)145 sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
146         const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
147     std::lock_guard<std::mutex> lock(mMutex);
148     if (mAggregationType == DurationMetric_AggregationType_SUM) {
149         if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) {
150             ALOGW("invalid alert for SUM: threshold (%f) > possible recordable value (%d x %lld)",
151                   alert.trigger_if_sum_gt(), alert.num_buckets(), (long long)mBucketSizeNs);
152             return nullptr;
153         }
154     }
155     sp<DurationAnomalyTracker> anomalyTracker =
156         new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
157     if (anomalyTracker != nullptr) {
158         mAnomalyTrackers.push_back(anomalyTracker);
159     }
160     return anomalyTracker;
161 }
162 
onStateChanged(const int64_t eventTimeNs,const int32_t atomId,const HashableDimensionKey & primaryKey,const FieldValue & oldState,const FieldValue & newState)163 void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
164                                             const HashableDimensionKey& primaryKey,
165                                             const FieldValue& oldState,
166                                             const FieldValue& newState) {
167     // Check if this metric has a StateMap. If so, map the new state value to
168     // the correct state group id.
169     FieldValue newStateCopy = newState;
170     mapStateValue(atomId, &newStateCopy);
171 
172     flushIfNeededLocked(eventTimeNs);
173 
174     // Each duration tracker is mapped to a different whatKey (a set of values from the
175     // dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the
176     // state change event are a subset of the tracker's whatKey field values.
177     //
178     // Ex. For a duration metric dimensioned on uid and tag:
179     // DurationTracker1 whatKey = uid: 1001, tag: 1
180     // DurationTracker2 whatKey = uid: 1002, tag 1
181     //
182     // If the state change primaryKey = uid: 1001, we only notify DurationTracker1 of a state
183     // change.
184     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
185         if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) {
186             continue;
187         }
188         whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy);
189     }
190 }
191 
createDurationTracker(const MetricDimensionKey & eventKey) const192 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
193         const MetricDimensionKey& eventKey) const {
194     switch (mAggregationType) {
195         case DurationMetric_AggregationType_SUM:
196             return make_unique<OringDurationTracker>(
197                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
198                     mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
199                     mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
200         case DurationMetric_AggregationType_MAX_SPARSE:
201             return make_unique<MaxDurationTracker>(
202                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
203                     mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
204                     mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
205     }
206 }
207 
208 // SlicedConditionChange optimization case 1:
209 // 1. If combination condition, logical operation is AND, only one sliced child predicate.
210 // 2. The links covers all dimension fields in the sliced child condition predicate.
onSlicedConditionMayChangeLocked_opt1(bool condition,const int64_t eventTime)211 void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool condition,
212                                                                    const int64_t eventTime) {
213     if (mMetric2ConditionLinks.size() != 1 ||
214         !mHasLinksToAllConditionDimensionsInTracker) {
215         return;
216     }
217 
218     bool  currentUnSlicedPartCondition = true;
219     if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
220         ConditionState unslicedPartState =
221             mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
222         // When the unsliced part is still false, return directly.
223         if (mUnSlicedPartCondition == ConditionState::kFalse &&
224             unslicedPartState == ConditionState::kFalse) {
225             return;
226         }
227         mUnSlicedPartCondition = unslicedPartState;
228         currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
229     }
230 
231     auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
232     auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
233 
234     // The condition change is from the unsliced predicates.
235     // We need to find out the true dimensions from the sliced predicate and flip their condition
236     // state based on the new unsliced condition state.
237     if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
238         (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
239         std::set<HashableDimensionKey> trueConditionDimensions;
240         mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions);
241         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
242             HashableDimensionKey linkedConditionDimensionKey;
243             getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
244                                      &linkedConditionDimensionKey);
245             if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
246                     trueConditionDimensions.end()) {
247                 whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
248             }
249         }
250     } else {
251         // Handle the condition change from the sliced predicate.
252         if (currentUnSlicedPartCondition) {
253             for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
254                 HashableDimensionKey linkedConditionDimensionKey;
255                 getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
256                                          &linkedConditionDimensionKey);
257                 if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) !=
258                         dimensionsChangedToTrue->end()) {
259                     whatIt.second->onConditionChanged(true, eventTime);
260                 }
261                 if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
262                         dimensionsChangedToFalse->end()) {
263                     whatIt.second->onConditionChanged(false, eventTime);
264                 }
265             }
266         }
267     }
268 }
269 
onSlicedConditionMayChangeInternalLocked(bool overallCondition,const int64_t eventTimeNs)270 void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(bool overallCondition,
271         const int64_t eventTimeNs) {
272     bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
273     if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker) {
274         onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTimeNs);
275         return;
276     }
277 
278     // Now for each of the on-going event, check if the condition has changed for them.
279     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
280         whatIt.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
281     }
282 }
283 
onSlicedConditionMayChangeLocked(bool overallCondition,const int64_t eventTime)284 void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
285                                                               const int64_t eventTime) {
286     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
287 
288     if (!mIsActive) {
289         return;
290     }
291 
292     flushIfNeededLocked(eventTime);
293 
294     if (!mConditionSliced) {
295         return;
296     }
297 
298     onSlicedConditionMayChangeInternalLocked(overallCondition, eventTime);
299 }
300 
onActiveStateChangedLocked(const int64_t & eventTimeNs)301 void DurationMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
302     MetricProducer::onActiveStateChangedLocked(eventTimeNs);
303 
304     if (!mConditionSliced) {
305         if (ConditionState::kTrue != mCondition) {
306             return;
307         }
308 
309         if (mIsActive) {
310             flushIfNeededLocked(eventTimeNs);
311         }
312 
313         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
314             whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
315         }
316     } else if (mIsActive) {
317         flushIfNeededLocked(eventTimeNs);
318         onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs);
319     } else { // mConditionSliced == true && !mIsActive
320         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
321             whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
322         }
323     }
324 }
325 
onConditionChangedLocked(const bool conditionMet,const int64_t eventTime)326 void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
327                                                       const int64_t eventTime) {
328     VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
329     mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
330 
331     if (!mIsActive) {
332         return;
333     }
334 
335     flushIfNeededLocked(eventTime);
336     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
337         whatIt.second->onConditionChanged(conditionMet, eventTime);
338     }
339 }
340 
dropDataLocked(const int64_t dropTimeNs)341 void DurationMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
342     flushIfNeededLocked(dropTimeNs);
343     StatsdStats::getInstance().noteBucketDropped(mMetricId);
344     mPastBuckets.clear();
345 }
346 
clearPastBucketsLocked(const int64_t dumpTimeNs)347 void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
348     flushIfNeededLocked(dumpTimeNs);
349     mPastBuckets.clear();
350 }
351 
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)352 void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
353                                                 const bool include_current_partial_bucket,
354                                                 const bool erase_data,
355                                                 const DumpLatency dumpLatency,
356                                                 std::set<string> *str_set,
357                                                 ProtoOutputStream* protoOutput) {
358     if (include_current_partial_bucket) {
359         flushLocked(dumpTimeNs);
360     } else {
361         flushIfNeededLocked(dumpTimeNs);
362     }
363     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
364     protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
365 
366     if (mPastBuckets.empty()) {
367         VLOG(" Duration metric, empty return");
368         return;
369     }
370 
371     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
372     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
373 
374     if (!mSliceByPositionALL) {
375         if (!mDimensionsInWhat.empty()) {
376             uint64_t dimenPathToken = protoOutput->start(
377                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
378             writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
379             protoOutput->end(dimenPathToken);
380         }
381     }
382 
383     uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
384 
385     VLOG("Duration metric %lld dump report now...", (long long)mMetricId);
386 
387     for (const auto& pair : mPastBuckets) {
388         const MetricDimensionKey& dimensionKey = pair.first;
389         VLOG("  dimension key %s", dimensionKey.toString().c_str());
390 
391         uint64_t wrapperToken =
392                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
393 
394         // First fill dimension.
395         if (mSliceByPositionALL) {
396             uint64_t dimensionToken = protoOutput->start(
397                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
398             writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
399             protoOutput->end(dimensionToken);
400         } else {
401             writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
402                                            FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
403         }
404         // Then fill slice_by_state.
405         for (auto state : dimensionKey.getStateValuesKey().getValues()) {
406             uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
407                                                      FIELD_ID_SLICE_BY_STATE);
408             writeStateToProto(state, protoOutput);
409             protoOutput->end(stateToken);
410         }
411         // Then fill bucket_info (DurationBucketInfo).
412         for (const auto& bucket : pair.second) {
413             uint64_t bucketInfoToken = protoOutput->start(
414                     FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
415             if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
416                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
417                                    (long long)NanoToMillis(bucket.mBucketStartNs));
418                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
419                                    (long long)NanoToMillis(bucket.mBucketEndNs));
420             } else {
421                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
422                                    (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
423             }
424             protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration);
425             protoOutput->end(bucketInfoToken);
426             VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs,
427                  (long long)bucket.mBucketEndNs, (long long)bucket.mDuration);
428         }
429 
430         protoOutput->end(wrapperToken);
431     }
432 
433     protoOutput->end(protoToken);
434     if (erase_data) {
435         mPastBuckets.clear();
436     }
437 }
438 
flushIfNeededLocked(const int64_t & eventTimeNs)439 void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
440     int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
441 
442     if (currentBucketEndTimeNs > eventTimeNs) {
443         return;
444     }
445     VLOG("flushing...........");
446     int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
447     int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
448     flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
449 
450     mCurrentBucketNum += numBucketsForward;
451 }
452 
flushCurrentBucketLocked(const int64_t & eventTimeNs,const int64_t & nextBucketStartTimeNs)453 void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
454                                                       const int64_t& nextBucketStartTimeNs) {
455     for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
456             whatIt != mCurrentSlicedDurationTrackerMap.end();) {
457         if (whatIt->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
458             VLOG("erase bucket for key %s", whatIt->first.toString().c_str());
459             whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
460         } else {
461             ++whatIt;
462         }
463     }
464     StatsdStats::getInstance().noteBucketCount(mMetricId);
465     mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
466 }
467 
dumpStatesLocked(FILE * out,bool verbose) const468 void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
469     if (mCurrentSlicedDurationTrackerMap.size() == 0) {
470         return;
471     }
472 
473     fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
474             (unsigned long)mCurrentSlicedDurationTrackerMap.size());
475     if (verbose) {
476         for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
477             fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str());
478             whatIt.second->dumpStates(out, verbose);
479         }
480     }
481 }
482 
hitGuardRailLocked(const MetricDimensionKey & newKey)483 bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
484     auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
485     if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
486         // 1. Report the tuple count if the tuple count > soft limit
487         if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
488             size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
489             StatsdStats::getInstance().noteMetricDimensionSize(
490                     mConfigKey, mMetricId, newTupleCount);
491             // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
492             if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
493                 ALOGE("DurationMetric %lld dropping data for what dimension key %s",
494                     (long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
495                 StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
496                 return true;
497             }
498         }
499     }
500     return false;
501 }
502 
handleStartEvent(const MetricDimensionKey & eventKey,const ConditionKey & conditionKeys,bool condition,const LogEvent & event)503 void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
504                                               const ConditionKey& conditionKeys,
505                                               bool condition, const LogEvent& event) {
506     const auto& whatKey = eventKey.getDimensionKeyInWhat();
507     auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
508     if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
509         if (hitGuardRailLocked(eventKey)) {
510             return;
511         }
512         mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey);
513     }
514 
515     auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
516     if (mUseWhatDimensionAsInternalDimension) {
517         it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
518         return;
519     }
520 
521     if (mInternalDimensions.empty()) {
522         it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(),
523                               conditionKeys);
524     } else {
525         HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
526         filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
527         it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(),
528                               conditionKeys);
529     }
530 
531 }
532 
onMatchedLogEventInternalLocked(const size_t matcherIndex,const MetricDimensionKey & eventKey,const ConditionKey & conditionKeys,bool condition,const LogEvent & event,const map<int,HashableDimensionKey> & statePrimaryKeys)533 void DurationMetricProducer::onMatchedLogEventInternalLocked(
534         const size_t matcherIndex, const MetricDimensionKey& eventKey,
535         const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
536         const map<int, HashableDimensionKey>& statePrimaryKeys) {
537     ALOGW("Not used in duration tracker.");
538 }
539 
onMatchedLogEventLocked(const size_t matcherIndex,const LogEvent & event)540 void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
541                                                      const LogEvent& event) {
542     int64_t eventTimeNs = event.GetElapsedTimestampNs();
543     if (eventTimeNs < mTimeBaseNs) {
544         return;
545     }
546 
547     if (mIsActive) {
548         flushIfNeededLocked(event.GetElapsedTimestampNs());
549     }
550 
551     // Handles Stopall events.
552     if (matcherIndex == mStopAllIndex) {
553         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
554             whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
555         }
556         return;
557     }
558 
559     HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
560     if (!mDimensionsInWhat.empty()) {
561         filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
562     }
563 
564     // Stores atom id to primary key pairs for each state atom that the metric is
565     // sliced by.
566     std::map<int, HashableDimensionKey> statePrimaryKeys;
567 
568     // For states with primary fields, use MetricStateLinks to get the primary
569     // field values from the log event. These values will form a primary key
570     // that will be used to query StateTracker for the correct state value.
571     for (const auto& stateLink : mMetric2StateLinks) {
572         getDimensionForState(event.getValues(), stateLink,
573                              &statePrimaryKeys[stateLink.stateAtomId]);
574     }
575 
576     // For each sliced state, query StateTracker for the state value using
577     // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
578     //
579     // Expected functionality: for any case where the MetricStateLinks are
580     // initialized incorrectly (ex. # of state links != # of primary fields, no
581     // links are provided for a state with primary fields, links are provided
582     // in the wrong order, etc.), StateTracker will simply return kStateUnknown
583     // when queried using an incorrect key.
584     HashableDimensionKey stateValuesKey = DEFAULT_DIMENSION_KEY;
585     for (auto atomId : mSlicedStateAtoms) {
586         FieldValue value;
587         if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
588             // found a primary key for this state, query using the key
589             queryStateValue(atomId, statePrimaryKeys[atomId], &value);
590         } else {
591             // if no MetricStateLinks exist for this state atom,
592             // query using the default dimension key (empty HashableDimensionKey)
593             queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
594         }
595         mapStateValue(atomId, &value);
596         stateValuesKey.addValue(value);
597     }
598 
599     // Handles Stop events.
600     if (matcherIndex == mStopIndex) {
601         if (mUseWhatDimensionAsInternalDimension) {
602             auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
603             if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
604                 whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
605             }
606             return;
607         }
608 
609         HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
610         if (!mInternalDimensions.empty()) {
611             filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey);
612         }
613 
614         auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
615         if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
616             whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false);
617         }
618         return;
619     }
620 
621     bool condition;
622     ConditionKey conditionKey;
623     if (mConditionSliced) {
624         for (const auto& link : mMetric2ConditionLinks) {
625             getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
626         }
627 
628         auto conditionState =
629             mWizard->query(mConditionTrackerIndex, conditionKey,
630                            !mHasLinksToAllConditionDimensionsInTracker);
631         condition = conditionState == ConditionState::kTrue;
632     } else {
633         // TODO: The unknown condition state is not handled here, we should fix it.
634         condition = mCondition == ConditionState::kTrue;
635     }
636 
637     condition = condition && mIsActive;
638 
639     handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
640                      event);
641 }
642 
byteSizeLocked() const643 size_t DurationMetricProducer::byteSizeLocked() const {
644     size_t totalSize = 0;
645     for (const auto& pair : mPastBuckets) {
646         totalSize += pair.second.size() * kBucketSize;
647     }
648     return totalSize;
649 }
650 
651 }  // namespace statsd
652 }  // namespace os
653 }  // namespace android
654