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