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