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