• 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 #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 
40 namespace android {
41 namespace os {
42 namespace statsd {
43 
44 // for StatsLogReport
45 const int FIELD_ID_ID = 1;
46 const int FIELD_ID_DURATION_METRICS = 6;
47 const int FIELD_ID_TIME_BASE = 9;
48 const int FIELD_ID_BUCKET_SIZE = 10;
49 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
50 const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
51 // for DurationMetricDataWrapper
52 const int FIELD_ID_DATA = 1;
53 // for DurationMetricData
54 const int FIELD_ID_DIMENSION_IN_WHAT = 1;
55 const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
56 const int FIELD_ID_BUCKET_INFO = 3;
57 const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
58 const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
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 size_t startIndex,const size_t stopIndex,const size_t stopAllIndex,const bool nesting,const sp<ConditionWizard> & wizard,const FieldMatcher & internalDimensions,const int64_t startTimeNs)65 DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric,
66                                                const int conditionIndex, const size_t startIndex,
67                                                const size_t stopIndex, const size_t stopAllIndex,
68                                                const bool nesting,
69                                                const sp<ConditionWizard>& wizard,
70                                                const FieldMatcher& internalDimensions,
71                                                const int64_t startTimeNs)
72     : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
73       mAggregationType(metric.aggregation_type()),
74       mStartIndex(startIndex),
75       mStopIndex(stopIndex),
76       mStopAllIndex(stopAllIndex),
77       mNested(nesting),
78       mContainANYPositionInInternalDimensions(false) {
79     // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
80     // them in the base class, because the proto generated CountMetric, and DurationMetric are
81     // not related. Maybe we should add a template in the future??
82     if (metric.has_bucket()) {
83         mBucketSizeNs =
84                 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
85     } else {
86         mBucketSizeNs = LLONG_MAX;
87     }
88 
89     if (metric.has_dimensions_in_what()) {
90         translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
91         mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
92     }
93 
94     if (internalDimensions.has_field()) {
95         translateFieldMatcher(internalDimensions, &mInternalDimensions);
96         mContainANYPositionInInternalDimensions = HasPositionANY(internalDimensions);
97     }
98     if (mContainANYPositionInInternalDimensions) {
99         ALOGE("Position ANY in internal dimension not supported.");
100     }
101     if (mContainANYPositionInDimensionsInWhat) {
102         ALOGE("Position ANY in dimension_in_what not supported.");
103     }
104 
105     if (metric.has_dimensions_in_condition()) {
106         translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
107     }
108 
109     mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
110             HasPositionALL(metric.dimensions_in_condition());
111 
112     if (metric.links().size() > 0) {
113         for (const auto& link : metric.links()) {
114             Metric2Condition mc;
115             mc.conditionId = link.condition();
116             translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
117             translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
118             mMetric2ConditionLinks.push_back(mc);
119         }
120     }
121     mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
122     mUnSlicedPartCondition = ConditionState::kUnknown;
123 
124     mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
125     if (mWizard != nullptr && mConditionTrackerIndex >= 0) {
126         mSameConditionDimensionsInTracker =
127             mWizard->equalOutputDimensions(mConditionTrackerIndex, mDimensionsInCondition);
128         if (mMetric2ConditionLinks.size() == 1) {
129             mHasLinksToAllConditionDimensionsInTracker =
130                 mWizard->equalOutputDimensions(mConditionTrackerIndex,
131                                                mMetric2ConditionLinks.begin()->conditionFields);
132         }
133     }
134     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
135          (long long)mBucketSizeNs, (long long)mTimeBaseNs);
136 }
137 
~DurationMetricProducer()138 DurationMetricProducer::~DurationMetricProducer() {
139     VLOG("~DurationMetric() called");
140 }
141 
addAnomalyTracker(const Alert & alert,const sp<AlarmMonitor> & anomalyAlarmMonitor)142 sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
143         const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
144     std::lock_guard<std::mutex> lock(mMutex);
145     if (mAggregationType == DurationMetric_AggregationType_SUM) {
146         if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) {
147             ALOGW("invalid alert for SUM: threshold (%f) > possible recordable value (%d x %lld)",
148                   alert.trigger_if_sum_gt(), alert.num_buckets(), (long long)mBucketSizeNs);
149             return nullptr;
150         }
151     }
152     sp<DurationAnomalyTracker> anomalyTracker =
153         new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
154     if (anomalyTracker != nullptr) {
155         mAnomalyTrackers.push_back(anomalyTracker);
156     }
157     return anomalyTracker;
158 }
159 
createDurationTracker(const MetricDimensionKey & eventKey) const160 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
161         const MetricDimensionKey& eventKey) const {
162     switch (mAggregationType) {
163         case DurationMetric_AggregationType_SUM:
164             return make_unique<OringDurationTracker>(
165                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
166                     mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
167                     mTimeBaseNs, mBucketSizeNs, mConditionSliced,
168                     mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
169         case DurationMetric_AggregationType_MAX_SPARSE:
170             return make_unique<MaxDurationTracker>(
171                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
172                     mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
173                     mTimeBaseNs, mBucketSizeNs, mConditionSliced,
174                     mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
175     }
176 }
177 
178 // SlicedConditionChange optimization case 1:
179 // 1. If combination condition, logical operation is AND, only one sliced child predicate.
180 // 2. No condition in dimension
181 // 3. The links covers all dimension fields in the sliced child condition predicate.
onSlicedConditionMayChangeLocked_opt1(bool condition,const int64_t eventTime)182 void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool condition,
183                                                                    const int64_t eventTime) {
184     if (mMetric2ConditionLinks.size() != 1 ||
185         !mHasLinksToAllConditionDimensionsInTracker ||
186         !mDimensionsInCondition.empty()) {
187         return;
188     }
189 
190     bool  currentUnSlicedPartCondition = true;
191     if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
192         ConditionState unslicedPartState =
193             mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
194         // When the unsliced part is still false, return directly.
195         if (mUnSlicedPartCondition == ConditionState::kFalse &&
196             unslicedPartState == ConditionState::kFalse) {
197             return;
198         }
199         mUnSlicedPartCondition = unslicedPartState;
200         currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
201     }
202 
203     auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
204     auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
205 
206     // The condition change is from the unsliced predicates.
207     // We need to find out the true dimensions from the sliced predicate and flip their condition
208     // state based on the new unsliced condition state.
209     if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
210         (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
211         std::set<HashableDimensionKey> trueConditionDimensions;
212         mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions);
213         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
214             HashableDimensionKey linkedConditionDimensionKey;
215             getDimensionForCondition(whatIt.first.getValues(),
216                                      mMetric2ConditionLinks[0],
217                                      &linkedConditionDimensionKey);
218             if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
219                     trueConditionDimensions.end()) {
220                 for (auto& condIt : whatIt.second) {
221                     condIt.second->onConditionChanged(
222                         currentUnSlicedPartCondition, eventTime);
223                 }
224             }
225         }
226     } else {
227         // Handle the condition change from the sliced predicate.
228         if (currentUnSlicedPartCondition) {
229             for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
230                 HashableDimensionKey linkedConditionDimensionKey;
231                 getDimensionForCondition(whatIt.first.getValues(),
232                                          mMetric2ConditionLinks[0],
233                                          &linkedConditionDimensionKey);
234                 if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) !=
235                         dimensionsChangedToTrue->end()) {
236                     for (auto& condIt : whatIt.second) {
237                         condIt.second->onConditionChanged(true, eventTime);
238                     }
239                 }
240                 if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
241                         dimensionsChangedToFalse->end()) {
242                     for (auto& condIt : whatIt.second) {
243                         condIt.second->onConditionChanged(false, eventTime);
244                     }
245                 }
246             }
247         }
248     }
249 }
250 
251 
252 // SlicedConditionChange optimization case 2:
253 // 1. If combination condition, logical operation is AND, only one sliced child predicate.
254 // 2. Has dimensions_in_condition and it equals to the output dimensions of the sliced predicate.
onSlicedConditionMayChangeLocked_opt2(bool condition,const int64_t eventTime)255 void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(bool condition,
256                                                                    const int64_t eventTime) {
257     if (mMetric2ConditionLinks.size() > 1 || !mSameConditionDimensionsInTracker) {
258         return;
259     }
260 
261     auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
262     auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
263 
264     bool  currentUnSlicedPartCondition = true;
265     if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
266         ConditionState unslicedPartState =
267             mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
268         // When the unsliced part is still false, return directly.
269         if (mUnSlicedPartCondition == ConditionState::kFalse &&
270             unslicedPartState == ConditionState::kFalse) {
271             return;
272         }
273         mUnSlicedPartCondition = unslicedPartState;
274         currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
275     }
276 
277     const std::set<HashableDimensionKey>* trueDimensionsToProcess = nullptr;
278     const std::set<HashableDimensionKey>* falseDimensionsToProcess = nullptr;
279 
280     std::set<HashableDimensionKey> currentTrueConditionDimensions;
281     if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
282         (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
283         mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &currentTrueConditionDimensions);
284         trueDimensionsToProcess = &currentTrueConditionDimensions;
285     } else if (currentUnSlicedPartCondition) {
286         // Handles the condition change from the sliced predicate. If the unsliced condition state
287         // is not true, not need to do anything.
288         trueDimensionsToProcess = dimensionsChangedToTrue;
289         falseDimensionsToProcess = dimensionsChangedToFalse;
290     }
291 
292     if (trueDimensionsToProcess == nullptr && falseDimensionsToProcess == nullptr) {
293         return;
294     }
295 
296     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
297         if (falseDimensionsToProcess != nullptr) {
298             for (const auto& changedDim : *falseDimensionsToProcess) {
299                 auto condIt = whatIt.second.find(changedDim);
300                 if (condIt != whatIt.second.end()) {
301                     condIt->second->onConditionChanged(false, eventTime);
302                 }
303             }
304         }
305         if (trueDimensionsToProcess != nullptr) {
306             HashableDimensionKey linkedConditionDimensionKey;
307             if (!trueDimensionsToProcess->empty() && mMetric2ConditionLinks.size() == 1) {
308                 getDimensionForCondition(whatIt.first.getValues(),
309                                          mMetric2ConditionLinks[0],
310                                          &linkedConditionDimensionKey);
311             }
312             for (auto& trueDim : *trueDimensionsToProcess) {
313                 auto condIt = whatIt.second.find(trueDim);
314                 if (condIt != whatIt.second.end()) {
315                     condIt->second->onConditionChanged(
316                         currentUnSlicedPartCondition, eventTime);
317                 } else {
318                     if (mMetric2ConditionLinks.size() == 0 ||
319                         trueDim.contains(linkedConditionDimensionKey)) {
320                         if (!whatIt.second.empty()) {
321                             auto newEventKey = MetricDimensionKey(whatIt.first, trueDim);
322                             if (hitGuardRailLocked(newEventKey)) {
323                                 continue;
324                             }
325                             unique_ptr<DurationTracker> newTracker =
326                                 whatIt.second.begin()->second->clone(eventTime);
327                             if (newTracker != nullptr) {
328                                 newTracker->setEventKey(newEventKey);
329                                 newTracker->onConditionChanged(true, eventTime);
330                                 whatIt.second[trueDim] = std::move(newTracker);
331                             }
332                         }
333                     }
334                 }
335             }
336         }
337     }
338 }
339 
onSlicedConditionMayChangeLocked(bool overallCondition,const int64_t eventTime)340 void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
341                                                               const int64_t eventTime) {
342     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
343     flushIfNeededLocked(eventTime);
344 
345     if (!mConditionSliced) {
346         return;
347     }
348 
349     bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
350     if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker &&
351         mDimensionsInCondition.empty()) {
352         onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTime);
353         return;
354     }
355 
356     if (changeDimTrackable && mSameConditionDimensionsInTracker &&
357         mMetric2ConditionLinks.size() <= 1) {
358         onSlicedConditionMayChangeLocked_opt2(overallCondition, eventTime);
359         return;
360     }
361 
362     // Now for each of the on-going event, check if the condition has changed for them.
363     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
364         for (auto& pair : whatIt.second) {
365             pair.second->onSlicedConditionMayChange(overallCondition, eventTime);
366         }
367     }
368 
369     if (mDimensionsInCondition.empty()) {
370         return;
371     }
372 
373     if (mMetric2ConditionLinks.empty()) {
374         std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
375         mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition,
376                                           !mSameConditionDimensionsInTracker,
377                                           &conditionDimensionsKeySet);
378         for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
379             for (const auto& pair : whatIt.second) {
380                 conditionDimensionsKeySet.erase(pair.first);
381             }
382         }
383         for (const auto& conditionDimension : conditionDimensionsKeySet) {
384             for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
385                 if (!whatIt.second.empty()) {
386                     auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
387                     if (hitGuardRailLocked(newEventKey)) {
388                         continue;
389                     }
390                     unique_ptr<DurationTracker> newTracker =
391                         whatIt.second.begin()->second->clone(eventTime);
392                     if (newTracker != nullptr) {
393                         newTracker->setEventKey(MetricDimensionKey(newEventKey));
394                         newTracker->onSlicedConditionMayChange(overallCondition, eventTime);
395                         whatIt.second[conditionDimension] = std::move(newTracker);
396                     }
397                 }
398             }
399         }
400     } else {
401         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
402             ConditionKey conditionKey;
403             for (const auto& link : mMetric2ConditionLinks) {
404                 getDimensionForCondition(whatIt.first.getValues(), link,
405                                          &conditionKey[link.conditionId]);
406             }
407             std::unordered_set<HashableDimensionKey> conditionDimensionsKeys;
408             mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
409                            !mSameConditionDimensionsInTracker,
410                            !mHasLinksToAllConditionDimensionsInTracker,
411                            &conditionDimensionsKeys);
412 
413             for (const auto& conditionDimension : conditionDimensionsKeys) {
414                 if (!whatIt.second.empty() &&
415                     whatIt.second.find(conditionDimension) == whatIt.second.end()) {
416                     auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
417                     if (hitGuardRailLocked(newEventKey)) {
418                         continue;
419                     }
420                     auto newTracker = whatIt.second.begin()->second->clone(eventTime);
421                     if (newTracker != nullptr) {
422                         newTracker->setEventKey(newEventKey);
423                         newTracker->onSlicedConditionMayChange(overallCondition, eventTime);
424                         whatIt.second[conditionDimension] = std::move(newTracker);
425                     }
426                 }
427             }
428         }
429     }
430 }
431 
onConditionChangedLocked(const bool conditionMet,const int64_t eventTime)432 void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
433                                                       const int64_t eventTime) {
434     VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
435     mCondition = conditionMet;
436     flushIfNeededLocked(eventTime);
437     // TODO: need to populate the condition change time from the event which triggers the condition
438     // change, instead of using current time.
439     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
440         for (auto& pair : whatIt.second) {
441             pair.second->onConditionChanged(conditionMet, eventTime);
442         }
443     }
444 }
445 
dropDataLocked(const int64_t dropTimeNs)446 void DurationMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
447     flushIfNeededLocked(dropTimeNs);
448     mPastBuckets.clear();
449 }
450 
clearPastBucketsLocked(const int64_t dumpTimeNs)451 void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
452     flushIfNeededLocked(dumpTimeNs);
453     mPastBuckets.clear();
454 }
455 
onDumpReportLocked(const int64_t dumpTimeNs,const bool include_current_partial_bucket,std::set<string> * str_set,ProtoOutputStream * protoOutput)456 void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
457                                                 const bool include_current_partial_bucket,
458                                                 std::set<string> *str_set,
459                                                 ProtoOutputStream* protoOutput) {
460     if (include_current_partial_bucket) {
461         flushLocked(dumpTimeNs);
462     } else {
463         flushIfNeededLocked(dumpTimeNs);
464     }
465     if (mPastBuckets.empty()) {
466         VLOG(" Duration metric, empty return");
467         return;
468     }
469 
470     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
471     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
472     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
473 
474     if (!mSliceByPositionALL) {
475         if (!mDimensionsInWhat.empty()) {
476             uint64_t dimenPathToken = protoOutput->start(
477                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
478             writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
479             protoOutput->end(dimenPathToken);
480         }
481         if (!mDimensionsInCondition.empty()) {
482             uint64_t dimenPathToken = protoOutput->start(
483                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
484             writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
485             protoOutput->end(dimenPathToken);
486         }
487     }
488 
489     uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
490 
491     VLOG("Duration metric %lld dump report now...", (long long)mMetricId);
492 
493     for (const auto& pair : mPastBuckets) {
494         const MetricDimensionKey& dimensionKey = pair.first;
495         VLOG("  dimension key %s", dimensionKey.toString().c_str());
496 
497         uint64_t wrapperToken =
498                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
499 
500         // First fill dimension.
501         if (mSliceByPositionALL) {
502             uint64_t dimensionToken = protoOutput->start(
503                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
504             writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
505             protoOutput->end(dimensionToken);
506 
507             if (dimensionKey.hasDimensionKeyInCondition()) {
508                 uint64_t dimensionInConditionToken = protoOutput->start(
509                         FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
510                 writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
511                                       str_set, protoOutput);
512                 protoOutput->end(dimensionInConditionToken);
513             }
514         } else {
515             writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
516                                            FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
517             if (dimensionKey.hasDimensionKeyInCondition()) {
518                 writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
519                                                FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
520                                                str_set, protoOutput);
521             }
522         }
523         // Then fill bucket_info (DurationBucketInfo).
524         for (const auto& bucket : pair.second) {
525             uint64_t bucketInfoToken = protoOutput->start(
526                     FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
527             if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
528                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
529                                    (long long)NanoToMillis(bucket.mBucketStartNs));
530                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
531                                    (long long)NanoToMillis(bucket.mBucketEndNs));
532             } else {
533                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
534                                    (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
535             }
536             protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration);
537             protoOutput->end(bucketInfoToken);
538             VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs,
539                  (long long)bucket.mBucketEndNs, (long long)bucket.mDuration);
540         }
541 
542         protoOutput->end(wrapperToken);
543     }
544 
545     protoOutput->end(protoToken);
546     mPastBuckets.clear();
547 }
548 
flushIfNeededLocked(const int64_t & eventTimeNs)549 void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
550     int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
551 
552     if (currentBucketEndTimeNs > eventTimeNs) {
553         return;
554     }
555     VLOG("flushing...........");
556     for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
557             whatIt != mCurrentSlicedDurationTrackerMap.end();) {
558         for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
559             if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) {
560                 VLOG("erase bucket for key %s %s",
561                      whatIt->first.toString().c_str(), it->first.toString().c_str());
562                 it = whatIt->second.erase(it);
563             } else {
564                 ++it;
565             }
566         }
567         if (whatIt->second.empty()) {
568             whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
569         } else {
570             whatIt++;
571         }
572     }
573 
574     int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
575     mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
576     mCurrentBucketNum += numBucketsForward;
577 }
578 
flushCurrentBucketLocked(const int64_t & eventTimeNs)579 void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) {
580     for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
581             whatIt != mCurrentSlicedDurationTrackerMap.end();) {
582         for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
583             if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
584                 VLOG("erase bucket for key %s %s", whatIt->first.toString().c_str(),
585                      it->first.toString().c_str());
586                 it = whatIt->second.erase(it);
587             } else {
588                 ++it;
589             }
590         }
591         if (whatIt->second.empty()) {
592             whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
593         } else {
594             whatIt++;
595         }
596     }
597 }
598 
dumpStatesLocked(FILE * out,bool verbose) const599 void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
600     if (mCurrentSlicedDurationTrackerMap.size() == 0) {
601         return;
602     }
603 
604     fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
605             (unsigned long)mCurrentSlicedDurationTrackerMap.size());
606     if (verbose) {
607         for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
608             for (const auto& slice : whatIt.second) {
609                 fprintf(out, "\t(what)%s\t(condition)%s\n", whatIt.first.toString().c_str(),
610                         slice.first.toString().c_str());
611                 slice.second->dumpStates(out, verbose);
612             }
613         }
614     }
615 }
616 
hitGuardRailLocked(const MetricDimensionKey & newKey)617 bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
618     auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
619     if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
620         auto condIt = whatIt->second.find(newKey.getDimensionKeyInCondition());
621         if (condIt != whatIt->second.end()) {
622             return false;
623         }
624         if (whatIt->second.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
625             size_t newTupleCount = whatIt->second.size() + 1;
626             StatsdStats::getInstance().noteMetricDimensionInConditionSize(
627                     mConfigKey, mMetricId, newTupleCount);
628             // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
629             if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
630                 ALOGE("DurationMetric %lld dropping data for condition dimension key %s",
631                     (long long)mMetricId, newKey.getDimensionKeyInCondition().toString().c_str());
632                 return true;
633             }
634         }
635     } else {
636         // 1. Report the tuple count if the tuple count > soft limit
637         if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
638             size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
639             StatsdStats::getInstance().noteMetricDimensionSize(
640                     mConfigKey, mMetricId, newTupleCount);
641             // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
642             if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
643                 ALOGE("DurationMetric %lld dropping data for what dimension key %s",
644                     (long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
645                 return true;
646             }
647         }
648     }
649     return false;
650 }
651 
handleStartEvent(const MetricDimensionKey & eventKey,const ConditionKey & conditionKeys,bool condition,const LogEvent & event)652 void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
653                                               const ConditionKey& conditionKeys,
654                                               bool condition, const LogEvent& event) {
655     const auto& whatKey = eventKey.getDimensionKeyInWhat();
656     const auto& condKey = eventKey.getDimensionKeyInCondition();
657 
658     auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
659     if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
660         if (hitGuardRailLocked(eventKey)) {
661             return;
662         }
663         mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey);
664     } else {
665         if (whatIt->second.find(condKey) == whatIt->second.end()) {
666             if (hitGuardRailLocked(eventKey)) {
667                 return;
668             }
669             mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey);
670         }
671     }
672 
673     auto it = mCurrentSlicedDurationTrackerMap.find(whatKey)->second.find(condKey);
674     if (mUseWhatDimensionAsInternalDimension) {
675         it->second->noteStart(whatKey, condition,
676                               event.GetElapsedTimestampNs(), conditionKeys);
677         return;
678     }
679 
680     if (mInternalDimensions.empty()) {
681         it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
682                               event.GetElapsedTimestampNs(), conditionKeys);
683     } else {
684         HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
685         filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
686         it->second->noteStart(
687             dimensionKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
688     }
689 
690 }
691 
onMatchedLogEventInternalLocked(const size_t matcherIndex,const MetricDimensionKey & eventKey,const ConditionKey & conditionKeys,bool condition,const LogEvent & event)692 void DurationMetricProducer::onMatchedLogEventInternalLocked(
693         const size_t matcherIndex, const MetricDimensionKey& eventKey,
694         const ConditionKey& conditionKeys, bool condition,
695         const LogEvent& event) {
696     ALOGW("Not used in duration tracker.");
697 }
698 
onMatchedLogEventLocked(const size_t matcherIndex,const LogEvent & event)699 void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
700                                                      const LogEvent& event) {
701     int64_t eventTimeNs = event.GetElapsedTimestampNs();
702     if (eventTimeNs < mTimeBaseNs) {
703         return;
704     }
705 
706     flushIfNeededLocked(event.GetElapsedTimestampNs());
707 
708     // Handles Stopall events.
709     if (matcherIndex == mStopAllIndex) {
710         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
711             for (auto& pair : whatIt.second) {
712                 pair.second->noteStopAll(event.GetElapsedTimestampNs());
713             }
714         }
715         return;
716     }
717 
718     HashableDimensionKey dimensionInWhat;
719     if (!mDimensionsInWhat.empty()) {
720         filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
721     } else {
722        dimensionInWhat = DEFAULT_DIMENSION_KEY;
723     }
724 
725     // Handles Stop events.
726     if (matcherIndex == mStopIndex) {
727         if (mUseWhatDimensionAsInternalDimension) {
728             auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
729             if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
730                 for (const auto& condIt : whatIt->second) {
731                     condIt.second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
732                 }
733             }
734             return;
735         }
736 
737         HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
738         if (!mInternalDimensions.empty()) {
739             filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey);
740         }
741 
742         auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
743         if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
744             for (const auto& condIt : whatIt->second) {
745                 condIt.second->noteStop(
746                     internalDimensionKey, event.GetElapsedTimestampNs(), false);
747             }
748         }
749         return;
750     }
751 
752     bool condition;
753     ConditionKey conditionKey;
754     std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
755     if (mConditionSliced) {
756         for (const auto& link : mMetric2ConditionLinks) {
757             getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
758         }
759 
760         auto conditionState =
761             mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
762                            !mSameConditionDimensionsInTracker,
763                            !mHasLinksToAllConditionDimensionsInTracker,
764                            &dimensionKeysInCondition);
765         condition = (conditionState == ConditionState::kTrue);
766         if (mDimensionsInCondition.empty() && condition) {
767             dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
768         }
769     } else {
770         condition = mCondition;
771         if (condition) {
772             dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
773         }
774     }
775 
776     if (dimensionKeysInCondition.empty()) {
777         handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY),
778                          conditionKey, condition, event);
779     } else {
780         auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
781         // If the what dimension is already there, we should update all the trackers even
782         // the condition is false.
783         if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
784             for (const auto& condIt : whatIt->second) {
785                 const bool cond = dimensionKeysInCondition.find(condIt.first) !=
786                         dimensionKeysInCondition.end();
787                 handleStartEvent(MetricDimensionKey(dimensionInWhat, condIt.first),
788                     conditionKey, cond, event);
789                 dimensionKeysInCondition.erase(condIt.first);
790             }
791         }
792         for (const auto& conditionDimension : dimensionKeysInCondition) {
793             handleStartEvent(MetricDimensionKey(dimensionInWhat, conditionDimension), conditionKey,
794                              condition, event);
795         }
796     }
797 }
798 
byteSizeLocked() const799 size_t DurationMetricProducer::byteSizeLocked() const {
800     size_t totalSize = 0;
801     for (const auto& pair : mPastBuckets) {
802         totalSize += pair.second.size() * kBucketSize;
803     }
804     return totalSize;
805 }
806 
807 }  // namespace statsd
808 }  // namespace os
809 }  // namespace android
810