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