From 3eab1b098351f421b71401f4a9282eedcdc039fa Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Fri, 4 Dec 2020 00:40:07 +0000 Subject: [PATCH] Migrate frameworks/base/cmds/statsd to packages/modules/StatsD/bin Add statsd protos to platform_protos rule BUG: 167962588 TEST: TH TEST: Local build [ m com.android.os.statsd ] Merged-In: I053f2a211ea28c2f181937af3d58ad16b235d096 Change-Id: I637a1af27d572924e88b1f8a930fc45d0794fa20 --- Android.bp | 1 + cmds/statsd/.clang-format | 17 - cmds/statsd/Android.bp | 412 -- cmds/statsd/OWNERS | 1 - cmds/statsd/TEST_MAPPING | 7 - .../benchmark/duration_metric_benchmark.cpp | 314 - .../benchmark/filter_value_benchmark.cpp | 72 - ...get_dimensions_for_condition_benchmark.cpp | 77 - .../benchmark/hello_world_benchmark.cpp | 29 - cmds/statsd/benchmark/log_event_benchmark.cpp | 50 - cmds/statsd/benchmark/main.cpp | 19 - cmds/statsd/benchmark/metric_util.cpp | 379 -- cmds/statsd/benchmark/metric_util.h | 140 - .../benchmark/stats_write_benchmark.cpp | 40 - cmds/statsd/src/FieldValue.cpp | 474 -- cmds/statsd/src/FieldValue.h | 462 -- cmds/statsd/src/HashableDimensionKey.cpp | 381 -- cmds/statsd/src/HashableDimensionKey.h | 238 - cmds/statsd/src/Log.h | 33 - cmds/statsd/src/StatsLogProcessor.cpp | 1132 ---- cmds/statsd/src/StatsLogProcessor.h | 374 -- cmds/statsd/src/StatsService.cpp | 1310 ----- cmds/statsd/src/StatsService.h | 416 -- cmds/statsd/src/active_config_list.proto | 57 - cmds/statsd/src/annotations.h | 33 - cmds/statsd/src/anomaly/AlarmMonitor.cpp | 139 - cmds/statsd/src/anomaly/AlarmMonitor.h | 162 - cmds/statsd/src/anomaly/AlarmTracker.cpp | 94 - cmds/statsd/src/anomaly/AlarmTracker.h | 80 - cmds/statsd/src/anomaly/AnomalyTracker.cpp | 321 -- cmds/statsd/src/anomaly/AnomalyTracker.h | 204 - .../src/anomaly/DurationAnomalyTracker.cpp | 115 - .../src/anomaly/DurationAnomalyTracker.h | 79 - .../src/anomaly/indexed_priority_queue.h | 224 - cmds/statsd/src/anomaly/subscriber_util.cpp | 70 - cmds/statsd/src/anomaly/subscriber_util.h | 33 - .../condition/CombinationConditionTracker.cpp | 187 - .../condition/CombinationConditionTracker.h | 111 - cmds/statsd/src/condition/ConditionTimer.h | 83 - cmds/statsd/src/condition/ConditionTracker.h | 156 - cmds/statsd/src/condition/ConditionWizard.cpp | 70 - cmds/statsd/src/condition/ConditionWizard.h | 68 - .../src/condition/SimpleConditionTracker.cpp | 386 -- .../src/condition/SimpleConditionTracker.h | 138 - cmds/statsd/src/condition/condition_util.cpp | 93 - cmds/statsd/src/condition/condition_util.h | 43 - cmds/statsd/src/config/ConfigKey.cpp | 54 - cmds/statsd/src/config/ConfigKey.h | 89 - cmds/statsd/src/config/ConfigListener.cpp | 31 - cmds/statsd/src/config/ConfigListener.h | 52 - cmds/statsd/src/config/ConfigManager.cpp | 375 -- cmds/statsd/src/config/ConfigManager.h | 181 - cmds/statsd/src/experiment_ids.proto | 29 - cmds/statsd/src/external/Perfetto.cpp | 139 - cmds/statsd/src/external/Perfetto.h | 37 - cmds/statsd/src/external/PullDataReceiver.h | 41 - .../src/external/PullResultReceiver.cpp | 39 - cmds/statsd/src/external/PullResultReceiver.h | 48 - cmds/statsd/src/external/PullUidProvider.h | 39 - .../src/external/StatsCallbackPuller.cpp | 116 - .../statsd/src/external/StatsCallbackPuller.h | 46 - cmds/statsd/src/external/StatsPuller.cpp | 126 - cmds/statsd/src/external/StatsPuller.h | 119 - .../src/external/StatsPullerManager.cpp | 390 -- cmds/statsd/src/external/StatsPullerManager.h | 190 - cmds/statsd/src/external/TrainInfoPuller.cpp | 55 - cmds/statsd/src/external/TrainInfoPuller.h | 38 - cmds/statsd/src/external/puller_util.cpp | 153 - cmds/statsd/src/external/puller_util.h | 34 - cmds/statsd/src/guardrail/StatsdStats.cpp | 1101 ---- cmds/statsd/src/guardrail/StatsdStats.h | 678 --- cmds/statsd/src/hash.cpp | 142 - cmds/statsd/src/hash.h | 46 - cmds/statsd/src/logd/LogEvent.cpp | 599 -- cmds/statsd/src/logd/LogEvent.h | 331 -- cmds/statsd/src/logd/LogEventQueue.cpp | 62 - cmds/statsd/src/logd/LogEventQueue.h | 57 - cmds/statsd/src/main.cpp | 113 - .../CombinationLogMatchingTracker.cpp | 120 - .../matchers/CombinationLogMatchingTracker.h | 53 - .../src/matchers/EventMatcherWizard.cpp | 35 - cmds/statsd/src/matchers/EventMatcherWizard.h | 41 - cmds/statsd/src/matchers/LogMatchingTracker.h | 96 - .../src/matchers/SimpleLogMatchingTracker.cpp | 73 - .../src/matchers/SimpleLogMatchingTracker.h | 55 - cmds/statsd/src/matchers/matcher_util.cpp | 372 -- cmds/statsd/src/matchers/matcher_util.h | 44 - cmds/statsd/src/metadata_util.cpp | 122 - cmds/statsd/src/metadata_util.h | 32 - .../src/metrics/CountMetricProducer.cpp | 397 -- cmds/statsd/src/metrics/CountMetricProducer.h | 123 - .../src/metrics/DurationMetricProducer.cpp | 653 --- .../src/metrics/DurationMetricProducer.h | 169 - .../src/metrics/EventMetricProducer.cpp | 170 - cmds/statsd/src/metrics/EventMetricProducer.h | 83 - .../src/metrics/GaugeMetricProducer.cpp | 620 -- cmds/statsd/src/metrics/GaugeMetricProducer.h | 211 - cmds/statsd/src/metrics/MetricProducer.cpp | 321 -- cmds/statsd/src/metrics/MetricProducer.h | 508 -- cmds/statsd/src/metrics/MetricsManager.cpp | 695 --- cmds/statsd/src/metrics/MetricsManager.h | 351 -- .../src/metrics/ValueMetricProducer.cpp | 1156 ---- cmds/statsd/src/metrics/ValueMetricProducer.h | 340 -- .../metrics/duration_helper/DurationTracker.h | 226 - .../duration_helper/MaxDurationTracker.cpp | 331 -- .../duration_helper/MaxDurationTracker.h | 94 - .../duration_helper/OringDurationTracker.cpp | 463 -- .../duration_helper/OringDurationTracker.h | 93 - .../src/metrics/metrics_manager_util.cpp | 983 ---- .../statsd/src/metrics/metrics_manager_util.h | 140 - .../statsd/src/packages/PackageInfoListener.h | 45 - cmds/statsd/src/packages/UidMap.cpp | 563 -- cmds/statsd/src/packages/UidMap.h | 227 - cmds/statsd/src/shell/ShellSubscriber.cpp | 245 - cmds/statsd/src/shell/ShellSubscriber.h | 146 - cmds/statsd/src/shell/shell_config.proto | 39 - cmds/statsd/src/shell/shell_data.proto | 29 - .../statsd/src/socket/StatsSocketListener.cpp | 156 - cmds/statsd/src/socket/StatsSocketListener.h | 54 - cmds/statsd/src/state/StateListener.h | 54 - cmds/statsd/src/state/StateManager.cpp | 112 - cmds/statsd/src/state/StateManager.h | 103 - cmds/statsd/src/state/StateTracker.cpp | 190 - cmds/statsd/src/state/StateTracker.h | 94 - cmds/statsd/src/stats_log.proto | 553 -- cmds/statsd/src/stats_log_util.cpp | 609 -- cmds/statsd/src/stats_log_util.h | 117 - cmds/statsd/src/stats_util.h | 36 - cmds/statsd/src/statscompanion_util.cpp | 35 - cmds/statsd/src/statscompanion_util.h | 33 - cmds/statsd/src/statsd_config.proto | 511 -- cmds/statsd/src/statsd_metadata.proto | 67 - cmds/statsd/src/storage/StorageManager.cpp | 781 --- cmds/statsd/src/storage/StorageManager.h | 168 - .../src/subscriber/IncidentdReporter.cpp | 169 - .../statsd/src/subscriber/IncidentdReporter.h | 36 - .../src/subscriber/SubscriberReporter.cpp | 171 - .../src/subscriber/SubscriberReporter.h | 109 - cmds/statsd/src/uid_data.proto | 36 - .../src/utils/MultiConditionTrigger.cpp | 57 - cmds/statsd/src/utils/MultiConditionTrigger.h | 55 - cmds/statsd/statsd_test.xml | 37 - cmds/statsd/tests/AlarmMonitor_test.cpp | 69 - cmds/statsd/tests/ConfigManager_test.cpp | 159 - cmds/statsd/tests/FieldValue_test.cpp | 659 --- .../tests/HashableDimensionKey_test.cpp | 137 - cmds/statsd/tests/LogEntryMatcher_test.cpp | 809 --- cmds/statsd/tests/LogEvent_test.cpp | 371 -- cmds/statsd/tests/LogReader_test.cpp | 21 - cmds/statsd/tests/MetricsManager_test.cpp | 799 --- cmds/statsd/tests/StatsLogProcessor_test.cpp | 1886 ------ cmds/statsd/tests/StatsService_test.cpp | 104 - cmds/statsd/tests/UidMap_test.cpp | 426 -- .../tests/anomaly/AlarmTracker_test.cpp | 94 - .../tests/anomaly/AnomalyTracker_test.cpp | 408 -- .../CombinationConditionTracker_test.cpp | 167 - .../tests/condition/ConditionTimer_test.cpp | 68 - .../condition/SimpleConditionTracker_test.cpp | 739 --- cmds/statsd/tests/e2e/Alarm_e2e_test.cpp | 92 - .../tests/e2e/Anomaly_count_e2e_test.cpp | 390 -- .../e2e/Anomaly_duration_sum_e2e_test.cpp | 527 -- .../statsd/tests/e2e/Attribution_e2e_test.cpp | 375 -- cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp | 114 - .../statsd/tests/e2e/CountMetric_e2e_test.cpp | 901 --- .../tests/e2e/DurationMetric_e2e_test.cpp | 1473 ----- .../tests/e2e/GaugeMetric_e2e_pull_test.cpp | 624 -- .../tests/e2e/GaugeMetric_e2e_push_test.cpp | 295 - .../tests/e2e/MetricActivation_e2e_test.cpp | 1833 ------ .../e2e/MetricConditionLink_e2e_test.cpp | 348 -- .../tests/e2e/PartialBucket_e2e_test.cpp | 433 -- .../tests/e2e/ValueMetric_pull_e2e_test.cpp | 679 --- .../tests/e2e/WakelockDuration_e2e_test.cpp | 355 -- .../external/StatsCallbackPuller_test.cpp | 219 - .../external/StatsPullerManager_test.cpp | 150 - .../tests/external/StatsPuller_test.cpp | 316 - .../tests/external/puller_util_test.cpp | 408 -- .../tests/guardrail/StatsdStats_test.cpp | 544 -- .../tests/indexed_priority_queue_test.cpp | 235 - .../tests/log_event/LogEventQueue_test.cpp | 115 - cmds/statsd/tests/metadata_util_test.cpp | 69 - .../metrics/CountMetricProducer_test.cpp | 476 -- .../metrics/DurationMetricProducer_test.cpp | 515 -- .../metrics/EventMetricProducer_test.cpp | 182 - .../metrics/GaugeMetricProducer_test.cpp | 865 --- .../tests/metrics/MaxDurationTracker_test.cpp | 424 -- .../metrics/OringDurationTracker_test.cpp | 576 -- .../metrics/ValueMetricProducer_test.cpp | 5061 ----------------- .../tests/metrics/metrics_test_helper.cpp | 57 - .../tests/metrics/metrics_test_helper.h | 64 - .../tests/shell/ShellSubscriber_test.cpp | 207 - cmds/statsd/tests/state/StateTracker_test.cpp | 571 -- cmds/statsd/tests/statsd_test_util.cpp | 1393 ----- cmds/statsd/tests/statsd_test_util.h | 479 -- .../tests/storage/StorageManager_test.cpp | 204 - .../utils/MultiConditionTrigger_test.cpp | 174 - cmds/statsd/tools/localtools/Android.bp | 46 - cmds/statsd/tools/localtools/TEST_MAPPING | 8 - .../tools/localtools/localdrive_manifest.txt | 1 - .../com/android/statsd/shelltools/Utils.java | 284 - .../shelltools/localdrive/LocalDrive.java | 379 -- .../shelltools/testdrive/TestDrive.java | 419 -- .../testdrive/ConfigurationTest.java | 326 -- .../shelltools/testdrive/TestDriveTest.java | 195 - .../tools/localtools/testdrive_manifest.txt | 1 - 204 files changed, 1 insertion(+), 59998 deletions(-) delete mode 100644 cmds/statsd/.clang-format delete mode 100644 cmds/statsd/Android.bp delete mode 100644 cmds/statsd/OWNERS delete mode 100644 cmds/statsd/TEST_MAPPING delete mode 100644 cmds/statsd/benchmark/duration_metric_benchmark.cpp delete mode 100644 cmds/statsd/benchmark/filter_value_benchmark.cpp delete mode 100644 cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp delete mode 100644 cmds/statsd/benchmark/hello_world_benchmark.cpp delete mode 100644 cmds/statsd/benchmark/log_event_benchmark.cpp delete mode 100644 cmds/statsd/benchmark/main.cpp delete mode 100644 cmds/statsd/benchmark/metric_util.cpp delete mode 100644 cmds/statsd/benchmark/metric_util.h delete mode 100644 cmds/statsd/benchmark/stats_write_benchmark.cpp delete mode 100644 cmds/statsd/src/FieldValue.cpp delete mode 100644 cmds/statsd/src/FieldValue.h delete mode 100644 cmds/statsd/src/HashableDimensionKey.cpp delete mode 100644 cmds/statsd/src/HashableDimensionKey.h delete mode 100644 cmds/statsd/src/Log.h delete mode 100644 cmds/statsd/src/StatsLogProcessor.cpp delete mode 100644 cmds/statsd/src/StatsLogProcessor.h delete mode 100644 cmds/statsd/src/StatsService.cpp delete mode 100644 cmds/statsd/src/StatsService.h delete mode 100644 cmds/statsd/src/active_config_list.proto delete mode 100644 cmds/statsd/src/annotations.h delete mode 100644 cmds/statsd/src/anomaly/AlarmMonitor.cpp delete mode 100644 cmds/statsd/src/anomaly/AlarmMonitor.h delete mode 100644 cmds/statsd/src/anomaly/AlarmTracker.cpp delete mode 100644 cmds/statsd/src/anomaly/AlarmTracker.h delete mode 100644 cmds/statsd/src/anomaly/AnomalyTracker.cpp delete mode 100644 cmds/statsd/src/anomaly/AnomalyTracker.h delete mode 100644 cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp delete mode 100644 cmds/statsd/src/anomaly/DurationAnomalyTracker.h delete mode 100644 cmds/statsd/src/anomaly/indexed_priority_queue.h delete mode 100644 cmds/statsd/src/anomaly/subscriber_util.cpp delete mode 100644 cmds/statsd/src/anomaly/subscriber_util.h delete mode 100644 cmds/statsd/src/condition/CombinationConditionTracker.cpp delete mode 100644 cmds/statsd/src/condition/CombinationConditionTracker.h delete mode 100644 cmds/statsd/src/condition/ConditionTimer.h delete mode 100644 cmds/statsd/src/condition/ConditionTracker.h delete mode 100644 cmds/statsd/src/condition/ConditionWizard.cpp delete mode 100644 cmds/statsd/src/condition/ConditionWizard.h delete mode 100644 cmds/statsd/src/condition/SimpleConditionTracker.cpp delete mode 100644 cmds/statsd/src/condition/SimpleConditionTracker.h delete mode 100644 cmds/statsd/src/condition/condition_util.cpp delete mode 100644 cmds/statsd/src/condition/condition_util.h delete mode 100644 cmds/statsd/src/config/ConfigKey.cpp delete mode 100644 cmds/statsd/src/config/ConfigKey.h delete mode 100644 cmds/statsd/src/config/ConfigListener.cpp delete mode 100644 cmds/statsd/src/config/ConfigListener.h delete mode 100644 cmds/statsd/src/config/ConfigManager.cpp delete mode 100644 cmds/statsd/src/config/ConfigManager.h delete mode 100644 cmds/statsd/src/experiment_ids.proto delete mode 100644 cmds/statsd/src/external/Perfetto.cpp delete mode 100644 cmds/statsd/src/external/Perfetto.h delete mode 100644 cmds/statsd/src/external/PullDataReceiver.h delete mode 100644 cmds/statsd/src/external/PullResultReceiver.cpp delete mode 100644 cmds/statsd/src/external/PullResultReceiver.h delete mode 100644 cmds/statsd/src/external/PullUidProvider.h delete mode 100644 cmds/statsd/src/external/StatsCallbackPuller.cpp delete mode 100644 cmds/statsd/src/external/StatsCallbackPuller.h delete mode 100644 cmds/statsd/src/external/StatsPuller.cpp delete mode 100644 cmds/statsd/src/external/StatsPuller.h delete mode 100644 cmds/statsd/src/external/StatsPullerManager.cpp delete mode 100644 cmds/statsd/src/external/StatsPullerManager.h delete mode 100644 cmds/statsd/src/external/TrainInfoPuller.cpp delete mode 100644 cmds/statsd/src/external/TrainInfoPuller.h delete mode 100644 cmds/statsd/src/external/puller_util.cpp delete mode 100644 cmds/statsd/src/external/puller_util.h delete mode 100644 cmds/statsd/src/guardrail/StatsdStats.cpp delete mode 100644 cmds/statsd/src/guardrail/StatsdStats.h delete mode 100644 cmds/statsd/src/hash.cpp delete mode 100644 cmds/statsd/src/hash.h delete mode 100644 cmds/statsd/src/logd/LogEvent.cpp delete mode 100644 cmds/statsd/src/logd/LogEvent.h delete mode 100644 cmds/statsd/src/logd/LogEventQueue.cpp delete mode 100644 cmds/statsd/src/logd/LogEventQueue.h delete mode 100644 cmds/statsd/src/main.cpp delete mode 100644 cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp delete mode 100644 cmds/statsd/src/matchers/CombinationLogMatchingTracker.h delete mode 100644 cmds/statsd/src/matchers/EventMatcherWizard.cpp delete mode 100644 cmds/statsd/src/matchers/EventMatcherWizard.h delete mode 100644 cmds/statsd/src/matchers/LogMatchingTracker.h delete mode 100644 cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp delete mode 100644 cmds/statsd/src/matchers/SimpleLogMatchingTracker.h delete mode 100644 cmds/statsd/src/matchers/matcher_util.cpp delete mode 100644 cmds/statsd/src/matchers/matcher_util.h delete mode 100644 cmds/statsd/src/metadata_util.cpp delete mode 100644 cmds/statsd/src/metadata_util.h delete mode 100644 cmds/statsd/src/metrics/CountMetricProducer.cpp delete mode 100644 cmds/statsd/src/metrics/CountMetricProducer.h delete mode 100644 cmds/statsd/src/metrics/DurationMetricProducer.cpp delete mode 100644 cmds/statsd/src/metrics/DurationMetricProducer.h delete mode 100644 cmds/statsd/src/metrics/EventMetricProducer.cpp delete mode 100644 cmds/statsd/src/metrics/EventMetricProducer.h delete mode 100644 cmds/statsd/src/metrics/GaugeMetricProducer.cpp delete mode 100644 cmds/statsd/src/metrics/GaugeMetricProducer.h delete mode 100644 cmds/statsd/src/metrics/MetricProducer.cpp delete mode 100644 cmds/statsd/src/metrics/MetricProducer.h delete mode 100644 cmds/statsd/src/metrics/MetricsManager.cpp delete mode 100644 cmds/statsd/src/metrics/MetricsManager.h delete mode 100644 cmds/statsd/src/metrics/ValueMetricProducer.cpp delete mode 100644 cmds/statsd/src/metrics/ValueMetricProducer.h delete mode 100644 cmds/statsd/src/metrics/duration_helper/DurationTracker.h delete mode 100644 cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp delete mode 100644 cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h delete mode 100644 cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp delete mode 100644 cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h delete mode 100644 cmds/statsd/src/metrics/metrics_manager_util.cpp delete mode 100644 cmds/statsd/src/metrics/metrics_manager_util.h delete mode 100644 cmds/statsd/src/packages/PackageInfoListener.h delete mode 100644 cmds/statsd/src/packages/UidMap.cpp delete mode 100644 cmds/statsd/src/packages/UidMap.h delete mode 100644 cmds/statsd/src/shell/ShellSubscriber.cpp delete mode 100644 cmds/statsd/src/shell/ShellSubscriber.h delete mode 100644 cmds/statsd/src/shell/shell_config.proto delete mode 100644 cmds/statsd/src/shell/shell_data.proto delete mode 100755 cmds/statsd/src/socket/StatsSocketListener.cpp delete mode 100644 cmds/statsd/src/socket/StatsSocketListener.h delete mode 100644 cmds/statsd/src/state/StateListener.h delete mode 100644 cmds/statsd/src/state/StateManager.cpp delete mode 100644 cmds/statsd/src/state/StateManager.h delete mode 100644 cmds/statsd/src/state/StateTracker.cpp delete mode 100644 cmds/statsd/src/state/StateTracker.h delete mode 100644 cmds/statsd/src/stats_log.proto delete mode 100644 cmds/statsd/src/stats_log_util.cpp delete mode 100644 cmds/statsd/src/stats_log_util.h delete mode 100644 cmds/statsd/src/stats_util.h delete mode 100644 cmds/statsd/src/statscompanion_util.cpp delete mode 100644 cmds/statsd/src/statscompanion_util.h delete mode 100644 cmds/statsd/src/statsd_config.proto delete mode 100644 cmds/statsd/src/statsd_metadata.proto delete mode 100644 cmds/statsd/src/storage/StorageManager.cpp delete mode 100644 cmds/statsd/src/storage/StorageManager.h delete mode 100644 cmds/statsd/src/subscriber/IncidentdReporter.cpp delete mode 100644 cmds/statsd/src/subscriber/IncidentdReporter.h delete mode 100644 cmds/statsd/src/subscriber/SubscriberReporter.cpp delete mode 100644 cmds/statsd/src/subscriber/SubscriberReporter.h delete mode 100644 cmds/statsd/src/uid_data.proto delete mode 100644 cmds/statsd/src/utils/MultiConditionTrigger.cpp delete mode 100644 cmds/statsd/src/utils/MultiConditionTrigger.h delete mode 100644 cmds/statsd/statsd_test.xml delete mode 100644 cmds/statsd/tests/AlarmMonitor_test.cpp delete mode 100644 cmds/statsd/tests/ConfigManager_test.cpp delete mode 100644 cmds/statsd/tests/FieldValue_test.cpp delete mode 100644 cmds/statsd/tests/HashableDimensionKey_test.cpp delete mode 100644 cmds/statsd/tests/LogEntryMatcher_test.cpp delete mode 100644 cmds/statsd/tests/LogEvent_test.cpp delete mode 100644 cmds/statsd/tests/LogReader_test.cpp delete mode 100644 cmds/statsd/tests/MetricsManager_test.cpp delete mode 100644 cmds/statsd/tests/StatsLogProcessor_test.cpp delete mode 100644 cmds/statsd/tests/StatsService_test.cpp delete mode 100644 cmds/statsd/tests/UidMap_test.cpp delete mode 100644 cmds/statsd/tests/anomaly/AlarmTracker_test.cpp delete mode 100644 cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp delete mode 100644 cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp delete mode 100644 cmds/statsd/tests/condition/ConditionTimer_test.cpp delete mode 100644 cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp delete mode 100644 cmds/statsd/tests/e2e/Alarm_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/Attribution_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp delete mode 100644 cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp delete mode 100644 cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp delete mode 100644 cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp delete mode 100644 cmds/statsd/tests/external/StatsCallbackPuller_test.cpp delete mode 100644 cmds/statsd/tests/external/StatsPullerManager_test.cpp delete mode 100644 cmds/statsd/tests/external/StatsPuller_test.cpp delete mode 100644 cmds/statsd/tests/external/puller_util_test.cpp delete mode 100644 cmds/statsd/tests/guardrail/StatsdStats_test.cpp delete mode 100644 cmds/statsd/tests/indexed_priority_queue_test.cpp delete mode 100644 cmds/statsd/tests/log_event/LogEventQueue_test.cpp delete mode 100644 cmds/statsd/tests/metadata_util_test.cpp delete mode 100644 cmds/statsd/tests/metrics/CountMetricProducer_test.cpp delete mode 100644 cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp delete mode 100644 cmds/statsd/tests/metrics/EventMetricProducer_test.cpp delete mode 100644 cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp delete mode 100644 cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp delete mode 100644 cmds/statsd/tests/metrics/OringDurationTracker_test.cpp delete mode 100644 cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp delete mode 100644 cmds/statsd/tests/metrics/metrics_test_helper.cpp delete mode 100644 cmds/statsd/tests/metrics/metrics_test_helper.h delete mode 100644 cmds/statsd/tests/shell/ShellSubscriber_test.cpp delete mode 100644 cmds/statsd/tests/state/StateTracker_test.cpp delete mode 100644 cmds/statsd/tests/statsd_test_util.cpp delete mode 100644 cmds/statsd/tests/statsd_test_util.h delete mode 100644 cmds/statsd/tests/storage/StorageManager_test.cpp delete mode 100644 cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp delete mode 100644 cmds/statsd/tools/localtools/Android.bp delete mode 100644 cmds/statsd/tools/localtools/TEST_MAPPING delete mode 100644 cmds/statsd/tools/localtools/localdrive_manifest.txt delete mode 100644 cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java delete mode 100644 cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java delete mode 100644 cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java delete mode 100644 cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java delete mode 100644 cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java delete mode 100644 cmds/statsd/tools/localtools/testdrive_manifest.txt diff --git a/Android.bp b/Android.bp index fdf38c6dee6f..2ac49be2d071 100644 --- a/Android.bp +++ b/Android.bp @@ -816,6 +816,7 @@ java_library_host { ":ipconnectivity-proto-src", ":libstats_atom_enum_protos", ":libstats_internal_protos", + ":statsd_internal_protos", "cmds/am/proto/instrumentation_data.proto", "cmds/statsd/src/**/*.proto", "core/proto/**/*.proto", diff --git a/cmds/statsd/.clang-format b/cmds/statsd/.clang-format deleted file mode 100644 index cead3a079435..000000000000 --- a/cmds/statsd/.clang-format +++ /dev/null @@ -1,17 +0,0 @@ -BasedOnStyle: Google -AllowShortIfStatementsOnASingleLine: true -AllowShortFunctionsOnASingleLine: false -AllowShortLoopsOnASingleLine: true -BinPackArguments: true -BinPackParameters: true -ColumnLimit: 100 -CommentPragmas: NOLINT:.* -ContinuationIndentWidth: 8 -DerivePointerAlignment: false -IndentWidth: 4 -PointerAlignment: Left -TabWidth: 4 -AccessModifierOffset: -4 -IncludeCategories: - - Regex: '^"Log\.h"' - Priority: -1 diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp deleted file mode 100644 index e1b7c5350546..000000000000 --- a/cmds/statsd/Android.bp +++ /dev/null @@ -1,412 +0,0 @@ -// -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -cc_defaults { - name: "statsd_defaults", - - srcs: [ - "src/active_config_list.proto", - "src/anomaly/AlarmMonitor.cpp", - "src/anomaly/AlarmTracker.cpp", - "src/anomaly/AnomalyTracker.cpp", - "src/anomaly/DurationAnomalyTracker.cpp", - "src/anomaly/subscriber_util.cpp", - "src/condition/CombinationConditionTracker.cpp", - "src/condition/condition_util.cpp", - "src/condition/ConditionWizard.cpp", - "src/condition/SimpleConditionTracker.cpp", - "src/config/ConfigKey.cpp", - "src/config/ConfigListener.cpp", - "src/config/ConfigManager.cpp", - "src/experiment_ids.proto", - "src/external/Perfetto.cpp", - "src/external/PullResultReceiver.cpp", - "src/external/puller_util.cpp", - "src/external/StatsCallbackPuller.cpp", - "src/external/StatsPuller.cpp", - "src/external/StatsPullerManager.cpp", - "src/external/TrainInfoPuller.cpp", - "src/FieldValue.cpp", - "src/guardrail/StatsdStats.cpp", - "src/hash.cpp", - "src/HashableDimensionKey.cpp", - "src/logd/LogEvent.cpp", - "src/logd/LogEventQueue.cpp", - "src/matchers/CombinationLogMatchingTracker.cpp", - "src/matchers/EventMatcherWizard.cpp", - "src/matchers/matcher_util.cpp", - "src/matchers/SimpleLogMatchingTracker.cpp", - "src/metadata_util.cpp", - "src/metrics/CountMetricProducer.cpp", - "src/metrics/duration_helper/MaxDurationTracker.cpp", - "src/metrics/duration_helper/OringDurationTracker.cpp", - "src/metrics/DurationMetricProducer.cpp", - "src/metrics/EventMetricProducer.cpp", - "src/metrics/GaugeMetricProducer.cpp", - "src/metrics/MetricProducer.cpp", - "src/metrics/metrics_manager_util.cpp", - "src/metrics/MetricsManager.cpp", - "src/metrics/ValueMetricProducer.cpp", - "src/packages/UidMap.cpp", - "src/shell/shell_config.proto", - "src/shell/ShellSubscriber.cpp", - "src/socket/StatsSocketListener.cpp", - "src/state/StateManager.cpp", - "src/state/StateTracker.cpp", - "src/stats_log_util.cpp", - "src/statscompanion_util.cpp", - "src/statsd_config.proto", - "src/statsd_metadata.proto", - "src/StatsLogProcessor.cpp", - "src/StatsService.cpp", - "src/storage/StorageManager.cpp", - "src/subscriber/IncidentdReporter.cpp", - "src/subscriber/SubscriberReporter.cpp", - "src/uid_data.proto", - "src/utils/MultiConditionTrigger.cpp", - ], - - local_include_dirs: [ - "src", - ], - - static_libs: [ - "libbase", - "libcutils", - "libgtest_prod", - "libprotoutil", - "libstatslog_statsd", - "libsysutils", - "libutils", - "statsd-aidl-ndk_platform", - ], - shared_libs: [ - "libbinder_ndk", - "libincident", - "liblog", - ], -} - -genrule { - name: "statslog_statsd.h", - tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsd.h --module statsd --namespace android,os,statsd,util", - out: [ - "statslog_statsd.h", - ], -} - -genrule { - name: "statslog_statsd.cpp", - tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsd.cpp --module statsd --namespace android,os,statsd,util --importHeader statslog_statsd.h", - out: [ - "statslog_statsd.cpp", - ], -} - -genrule { - name: "statslog_statsdtest.h", - tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsdtest.h --module statsdtest --namespace android,os,statsd,util", - out: [ - "statslog_statsdtest.h", - ], -} - -genrule { - name: "statslog_statsdtest.cpp", - tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsdtest.cpp --module statsdtest --namespace android,os,statsd,util --importHeader statslog_statsdtest.h", - out: [ - "statslog_statsdtest.cpp", - ], -} - -cc_library_static { - name: "libstatslog_statsdtest", - generated_sources: ["statslog_statsdtest.cpp"], - generated_headers: ["statslog_statsdtest.h"], - export_generated_headers: ["statslog_statsdtest.h"], - shared_libs: [ - "libstatssocket", - ] -} - -cc_library_static { - name: "libstatslog_statsd", - generated_sources: ["statslog_statsd.cpp"], - generated_headers: ["statslog_statsd.h"], - export_generated_headers: ["statslog_statsd.h"], - apex_available: [ - "com.android.os.statsd", - "test_com.android.os.statsd", - ], - shared_libs: [ - "libstatssocket", - ] -} - -// ========= -// statsd -// ========= - -cc_binary { - name: "statsd", - defaults: ["statsd_defaults"], - - srcs: ["src/main.cpp"], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - "-Wno-unused-parameter", - // optimize for size (protobuf glop can get big) - "-Os", - // "-g", - // "-O0", - ], - - product_variables: { - eng: { - // Enable sanitizer ONLY on eng builds - //sanitize: { - // address: true, - //}, - }, - debuggable: { - // Add a flag to enable stats log printing from statsd on debug builds. - cflags: ["-DVERY_VERBOSE_PRINTING"], - }, - }, - - proto: { - type: "lite", - static: true, - }, - stl: "libc++_static", - - shared_libs: [ - "libstatssocket", - ], - - apex_available: [ - "com.android.os.statsd", - "test_com.android.os.statsd", - ], -} - -// ============== -// statsd_test -// ============== - -cc_test { - name: "statsd_test", - defaults: ["statsd_defaults"], - test_suites: ["device-tests", "mts"], - test_config: "statsd_test.xml", - - //TODO(b/153588990): Remove when the build system properly separates - //32bit and 64bit architectures. - compile_multilib: "both", - multilib: { - lib64: { - suffix: "64", - }, - lib32: { - suffix: "32", - }, - }, - - cflags: [ - "-Wall", - "-Werror", - "-Wno-missing-field-initializers", - "-Wno-unused-variable", - "-Wno-unused-function", - "-Wno-unused-parameter", - ], - - require_root: true, - - srcs: [ - // atom_field_options.proto needs field_options.proto, but that is - // not included in libprotobuf-cpp-lite, so compile it here. - ":libprotobuf-internal-protos", - ":libstats_internal_protos", - - "src/shell/shell_data.proto", - "src/stats_log.proto", - "tests/AlarmMonitor_test.cpp", - "tests/anomaly/AlarmTracker_test.cpp", - "tests/anomaly/AnomalyTracker_test.cpp", - "tests/condition/CombinationConditionTracker_test.cpp", - "tests/condition/ConditionTimer_test.cpp", - "tests/condition/SimpleConditionTracker_test.cpp", - "tests/ConfigManager_test.cpp", - "tests/e2e/Alarm_e2e_test.cpp", - "tests/e2e/Anomaly_count_e2e_test.cpp", - "tests/e2e/Anomaly_duration_sum_e2e_test.cpp", - "tests/e2e/Attribution_e2e_test.cpp", - "tests/e2e/ConfigTtl_e2e_test.cpp", - "tests/e2e/CountMetric_e2e_test.cpp", - "tests/e2e/DurationMetric_e2e_test.cpp", - "tests/e2e/GaugeMetric_e2e_pull_test.cpp", - "tests/e2e/GaugeMetric_e2e_push_test.cpp", - "tests/e2e/MetricActivation_e2e_test.cpp", - "tests/e2e/MetricConditionLink_e2e_test.cpp", - "tests/e2e/PartialBucket_e2e_test.cpp", - "tests/e2e/ValueMetric_pull_e2e_test.cpp", - "tests/e2e/WakelockDuration_e2e_test.cpp", - "tests/external/puller_util_test.cpp", - "tests/external/StatsCallbackPuller_test.cpp", - "tests/external/StatsPuller_test.cpp", - "tests/external/StatsPullerManager_test.cpp", - "tests/FieldValue_test.cpp", - "tests/guardrail/StatsdStats_test.cpp", - "tests/HashableDimensionKey_test.cpp", - "tests/indexed_priority_queue_test.cpp", - "tests/log_event/LogEventQueue_test.cpp", - "tests/LogEntryMatcher_test.cpp", - "tests/LogEvent_test.cpp", - "tests/metadata_util_test.cpp", - "tests/metrics/CountMetricProducer_test.cpp", - "tests/metrics/DurationMetricProducer_test.cpp", - "tests/metrics/EventMetricProducer_test.cpp", - "tests/metrics/GaugeMetricProducer_test.cpp", - "tests/metrics/MaxDurationTracker_test.cpp", - "tests/metrics/metrics_test_helper.cpp", - "tests/metrics/OringDurationTracker_test.cpp", - "tests/metrics/ValueMetricProducer_test.cpp", - "tests/MetricsManager_test.cpp", - "tests/shell/ShellSubscriber_test.cpp", - "tests/state/StateTracker_test.cpp", - "tests/statsd_test_util.cpp", - "tests/StatsLogProcessor_test.cpp", - "tests/StatsService_test.cpp", - "tests/storage/StorageManager_test.cpp", - "tests/UidMap_test.cpp", - "tests/utils/MultiConditionTrigger_test.cpp", - ], - - static_libs: [ - "libgmock", - "libplatformprotos", - "libstatslog_statsdtest", - "libstatssocket_private", - ], - - proto: { - type: "lite", - include_dirs: [ - "external/protobuf/src", - "frameworks/proto_logging/stats", - ], - }, - -} - -//############################# -// statsd micro benchmark -//############################# - -cc_benchmark { - name: "statsd_benchmark", - defaults: ["statsd_defaults"], - - srcs: [ - // atom_field_options.proto needs field_options.proto, but that is - // not included in libprotobuf-cpp-lite, so compile it here. - ":libprotobuf-internal-protos", - ":libstats_internal_protos", - - "benchmark/duration_metric_benchmark.cpp", - "benchmark/filter_value_benchmark.cpp", - "benchmark/get_dimensions_for_condition_benchmark.cpp", - "benchmark/hello_world_benchmark.cpp", - "benchmark/log_event_benchmark.cpp", - "benchmark/main.cpp", - "benchmark/metric_util.cpp", - "benchmark/stats_write_benchmark.cpp", - "src/stats_log.proto", - ], - - proto: { - type: "lite", - include_dirs: [ - "external/protobuf/src", - "frameworks/proto_logging/stats", - ], - }, - - cflags: [ - "-Wall", - "-Werror", - "-Wno-unused-parameter", - "-Wno-unused-variable", - "-Wno-unused-function", - - // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374 - "-Wno-varargs", - ], - - static_libs: [ - "libplatformprotos", - "libstatssocket_private", - ], - - shared_libs: [ - "libgtest_prod", - "libprotobuf-cpp-lite", - "libstatslog", - ], -} - -// ==== java proto device library (for test only) ============================== -java_library { - name: "statsdprotolite", - sdk_version: "core_current", - proto: { - type: "lite", - include_dirs: [ - "external/protobuf/src", - "frameworks/proto_logging/stats", - ], - }, - - srcs: [ - ":libstats_atoms_proto", - "src/shell/shell_config.proto", - "src/shell/shell_data.proto", - "src/stats_log.proto", - "src/statsd_config.proto", - ], - - static_libs: [ - "platformprotoslite", - ], - // Protos have lots of MissingOverride and similar. - errorprone: { - javacflags: ["-XepDisableAllChecks"], - }, -} - -// Filegroup for statsd config proto definition. -filegroup { - name: "statsd-config-proto-def", - srcs: ["src/statsd_config.proto"], -} diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS deleted file mode 100644 index 4e4e11988b62..000000000000 --- a/cmds/statsd/OWNERS +++ /dev/null @@ -1 +0,0 @@ -baligh@google.com diff --git a/cmds/statsd/TEST_MAPPING b/cmds/statsd/TEST_MAPPING deleted file mode 100644 index 8dee073aca22..000000000000 --- a/cmds/statsd/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presubmit" : [ - { - "name" : "statsd_test" - } - ] -} \ No newline at end of file diff --git a/cmds/statsd/benchmark/duration_metric_benchmark.cpp b/cmds/statsd/benchmark/duration_metric_benchmark.cpp deleted file mode 100644 index 2d315d9395bc..000000000000 --- a/cmds/statsd/benchmark/duration_metric_benchmark.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include "benchmark/benchmark.h" -#include "FieldValue.h" -#include "HashableDimensionKey.h" -#include "logd/LogEvent.h" -#include "stats_log_util.h" -#include "metric_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -static StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition( - DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) { - StatsdConfig config; - *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); - *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); - *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); - *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto scheduledJobPredicate = CreateScheduledJobPredicate(); - auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); - dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED); - dimensions->add_child()->set_field(2); // job name field. - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - - auto isSyncingPredicate = CreateIsSyncingPredicate(); - auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, - {Position::FIRST}); - if (addExtraDimensionInCondition) { - syncDimension->add_child()->set_field(2 /* name field*/); - } - - *config.add_predicate() = scheduledJobPredicate; - *config.add_predicate() = screenIsOffPredicate; - *config.add_predicate() = isSyncingPredicate; - auto combinationPredicate = config.add_predicate(); - combinationPredicate->set_id(StringToId("CombinationPredicate")); - combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); - addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); - - auto metric = config.add_duration_metric(); - metric->set_bucket(FIVE_MINUTES); - metric->set_id(StringToId("scheduledJob")); - metric->set_what(scheduledJobPredicate.id()); - metric->set_condition(combinationPredicate->id()); - metric->set_aggregation_type(aggregationType); - auto dimensionWhat = metric->mutable_dimensions_in_what(); - dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED); - dimensionWhat->add_child()->set_field(2); // job name field. - *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); - return config; -} - -static StatsdConfig CreateDurationMetricConfig_Link_AND_CombinationCondition( - DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) { - StatsdConfig config; - *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); - *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); - *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); - *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto scheduledJobPredicate = CreateScheduledJobPredicate(); - auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); - *dimensions = CreateAttributionUidDimensions( - android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); - dimensions->add_child()->set_field(2); // job name field. - - auto isSyncingPredicate = CreateIsSyncingPredicate(); - auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); - if (addExtraDimensionInCondition) { - syncDimension->add_child()->set_field(2 /* name field*/); - } - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - - *config.add_predicate() = scheduledJobPredicate; - *config.add_predicate() = screenIsOffPredicate; - *config.add_predicate() = isSyncingPredicate; - auto combinationPredicate = config.add_predicate(); - combinationPredicate->set_id(StringToId("CombinationPredicate")); - combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); - addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); - - auto metric = config.add_duration_metric(); - metric->set_bucket(FIVE_MINUTES); - metric->set_id(StringToId("scheduledJob")); - metric->set_what(scheduledJobPredicate.id()); - metric->set_condition(combinationPredicate->id()); - metric->set_aggregation_type(aggregationType); - *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( - android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); - - auto links = metric->add_links(); - links->set_condition(isSyncingPredicate.id()); - *links->mutable_fields_in_what() = - CreateAttributionUidDimensions( - android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); - *links->mutable_fields_in_condition() = - CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); - return config; -} - -static void BM_DurationMetricNoLink(benchmark::State& state) { - ConfigKey cfgKey; - auto config = CreateDurationMetricConfig_NoLink_AND_CombinationCondition( - DurationMetric::SUM, false); - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - - std::vector> events; - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 11, - android::view::DISPLAY_STATE_OFF)); - events.push_back( - CreateScreenStateChangedEvent(bucketStartTimeNs + 40, android::view::DISPLAY_STATE_ON)); - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 102, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450, - android::view::DISPLAY_STATE_ON)); - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 650, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100, - android::view::DISPLAY_STATE_ON)); - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 640, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 650, - android::view::DISPLAY_STATE_ON)); - - vector attributionUids1 = {9999}; - vector attributionTags1 = {""}; - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 2, attributionUids1, - attributionTags1, "job0")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1, - attributionTags1, "job0")); - - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids1, - attributionTags1, "job2")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids1, - attributionTags1, "job2")); - - vector attributionUids2 = {8888}; - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2, - attributionTags1, "job2")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850, - attributionUids2, attributionTags1, "job2")); - - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 600, - attributionUids2, attributionTags1, "job1")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900, - attributionUids2, attributionTags1, "job1")); - - vector attributionUids3 = {111, 222, 222}; - vector attributionTags3 = {"App1", "GMSCoreModule1", "GMSCoreModule2"}; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 10, attributionUids3, - attributionTags3, "ReadEmail")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 50, attributionUids3, attributionTags3, - "ReadEmail")); - - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200, attributionUids3, - attributionTags3, "ReadEmail")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids3, - attributionTags3, "ReadEmail")); - - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids3, - attributionTags3, "ReadDoc")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids3, - attributionTags3, "ReadDoc")); - - vector attributionUids4 = {333, 222, 555}; - vector attributionTags4 = {"App2", "GMSCoreModule1", "GMSCoreModule2"}; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 401, attributionUids4, - attributionTags4, "ReadEmail")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids4, - attributionTags4, "ReadEmail")); - sortLogEventsByTimestamp(&events); - - while (state.KeepRunning()) { - auto processor = CreateStatsLogProcessor( - bucketStartTimeNs / NS_PER_SEC, config, cfgKey); - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - } -} - -BENCHMARK(BM_DurationMetricNoLink); - - -static void BM_DurationMetricLink(benchmark::State& state) { - ConfigKey cfgKey; - auto config = CreateDurationMetricConfig_Link_AND_CombinationCondition( - DurationMetric::SUM, false); - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - - std::vector> events; - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 55, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 120, - android::view::DISPLAY_STATE_ON)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 121, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450, - android::view::DISPLAY_STATE_ON)); - - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 501, - android::view::DISPLAY_STATE_OFF)); - events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100, - android::view::DISPLAY_STATE_ON)); - - vector attributionUids1 = {111}; - vector attributionTags1 = {"App1"}; - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 1, attributionUids1, - attributionTags1, "job1")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1, - attributionTags1, "job1")); - - vector attributionUids2 = {333}; - vector attributionTags2 = {"App2"}; - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids2, - attributionTags2, "job2")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids2, - attributionTags2, "job2")); - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2, - attributionTags2, "job2")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850, - attributionUids2, attributionTags2, "job2")); - - vector attributionUids3 = {444}; - vector attributionTags3 = {"App3"}; - events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs - 2, - attributionUids3, attributionTags3, "job3")); - events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900, - attributionUids3, attributionTags3, "job3")); - - vector attributionUids4 = {111, 222, 222}; - vector attributionTags4 = {"App1", "GMSCoreModule1", "GMSCoreModule2"}; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids4, - attributionTags4, "ReadEmail")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 110, attributionUids4, attributionTags4, - "ReadEmail")); - - vector attributionUids5 = {333, 222, 555}; - vector attributionTags5 = {"App2", "GMSCoreModule1", "GMSCoreModule2"}; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 300, attributionUids5, - attributionTags5, "ReadEmail")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids5, - attributionTags5, "ReadEmail")); - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids5, - attributionTags5, "ReadDoc")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids5, - attributionTags5, "ReadDoc")); - - vector attributionUids6 = {444, 222, 555}; - vector attributionTags6 = {"App3", "GMSCoreModule1", "GMSCoreModule2"}; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 550, attributionUids6, - attributionTags6, "ReadDoc")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 800, attributionUids6, attributionTags6, - "ReadDoc")); - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids6, - attributionTags6, "ReadDoc")); - events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids6, - attributionTags6, "ReadDoc")); - sortLogEventsByTimestamp(&events); - - while (state.KeepRunning()) { - auto processor = CreateStatsLogProcessor( - bucketStartTimeNs / NS_PER_SEC, config, cfgKey); - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - } -} - -BENCHMARK(BM_DurationMetricLink); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp deleted file mode 100644 index 743ccc4ed451..000000000000 --- a/cmds/statsd/benchmark/filter_value_benchmark.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include "FieldValue.h" -#include "HashableDimensionKey.h" -#include "benchmark/benchmark.h" -#include "logd/LogEvent.h" -#include "metric_util.h" -#include "stats_event.h" -#include "stats_log_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -static void createLogEventAndMatcher(LogEvent* event, FieldMatcher* field_matcher) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 1); - AStatsEvent_overwriteTimestamp(statsEvent, 100000); - - std::vector attributionUids = {100, 100}; - std::vector attributionTags = {"LOCATION", "LOCATION"}; - writeAttribution(statsEvent, attributionUids, attributionTags); - - AStatsEvent_writeFloat(statsEvent, 3.2f); - AStatsEvent_writeString(statsEvent, "LOCATION"); - AStatsEvent_writeInt64(statsEvent, 990); - - parseStatsEventToLogEvent(statsEvent, event); - - field_matcher->set_field(1); - auto child = field_matcher->add_child(); - child->set_field(1); - child->set_position(FIRST); - child->add_child()->set_field(1); -} - -static void BM_FilterValue(benchmark::State& state) { - LogEvent event(/*uid=*/0, /*pid=*/0); - FieldMatcher field_matcher; - createLogEventAndMatcher(&event, &field_matcher); - - std::vector matchers; - translateFieldMatcher(field_matcher, &matchers); - - while (state.KeepRunning()) { - HashableDimensionKey output; - filterValues(matchers, event.getValues(), &output); - } -} - -BENCHMARK(BM_FilterValue); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp deleted file mode 100644 index 7a455650a31b..000000000000 --- a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include "FieldValue.h" -#include "HashableDimensionKey.h" -#include "benchmark/benchmark.h" -#include "logd/LogEvent.h" -#include "metric_util.h" -#include "stats_event.h" -#include "stats_log_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 1); - AStatsEvent_overwriteTimestamp(statsEvent, 100000); - - std::vector attributionUids = {100, 100}; - std::vector attributionTags = {"LOCATION", "LOCATION"}; - writeAttribution(statsEvent, attributionUids, attributionTags); - - AStatsEvent_writeFloat(statsEvent, 3.2f); - AStatsEvent_writeString(statsEvent, "LOCATION"); - AStatsEvent_writeInt64(statsEvent, 990); - - parseStatsEventToLogEvent(statsEvent, event); - - link->conditionId = 1; - - FieldMatcher field_matcher; - field_matcher.set_field(event->GetTagId()); - auto child = field_matcher.add_child(); - child->set_field(1); - child->set_position(FIRST); - child->add_child()->set_field(1); - - translateFieldMatcher(field_matcher, &link->metricFields); - field_matcher.set_field(event->GetTagId() + 1); - translateFieldMatcher(field_matcher, &link->conditionFields); -} - -static void BM_GetDimensionInCondition(benchmark::State& state) { - Metric2Condition link; - LogEvent event(/*uid=*/0, /*pid=*/0); - createLogEventAndLink(&event, &link); - - while (state.KeepRunning()) { - HashableDimensionKey output; - getDimensionForCondition(event.getValues(), link, &output); - } -} - -BENCHMARK(BM_GetDimensionInCondition); - - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/hello_world_benchmark.cpp b/cmds/statsd/benchmark/hello_world_benchmark.cpp deleted file mode 100644 index c732d394bf64..000000000000 --- a/cmds/statsd/benchmark/hello_world_benchmark.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "benchmark/benchmark.h" - -static void BM_StringCreation(benchmark::State& state) { - while (state.KeepRunning()) std::string empty_string; -} -// Register the function as a benchmark -BENCHMARK(BM_StringCreation); - -// Define another benchmark -static void BM_StringCopy(benchmark::State& state) { - std::string x = "hello"; - while (state.KeepRunning()) std::string copy(x); -} -BENCHMARK(BM_StringCopy); diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp deleted file mode 100644 index 057e00bde202..000000000000 --- a/cmds/statsd/benchmark/log_event_benchmark.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include "benchmark/benchmark.h" -#include "logd/LogEvent.h" -#include "stats_event.h" - -namespace android { -namespace os { -namespace statsd { - -static size_t createAndParseStatsEvent(uint8_t* msg) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - AStatsEvent_writeInt32(event, 2); - AStatsEvent_writeFloat(event, 2.0); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - memcpy(msg, buf, size); - return size; -} - -static void BM_LogEventCreation(benchmark::State& state) { - uint8_t msg[LOGGER_ENTRY_MAX_PAYLOAD]; - size_t size = createAndParseStatsEvent(msg); - while (state.KeepRunning()) { - LogEvent event(/*uid=*/ 1000, /*pid=*/ 1001); - benchmark::DoNotOptimize(event.parseBuffer(msg, size)); - } -} -BENCHMARK(BM_LogEventCreation); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/main.cpp b/cmds/statsd/benchmark/main.cpp deleted file mode 100644 index 08921f3c3f52..000000000000 --- a/cmds/statsd/benchmark/main.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -BENCHMARK_MAIN(); diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp deleted file mode 100644 index 89fd3d9b29ab..000000000000 --- a/cmds/statsd/benchmark/metric_util.cpp +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "metric_util.h" - -#include "stats_event.h" - -namespace android { -namespace os { -namespace statsd { - -AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(atomId); - return atom_matcher; -} - -AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name, - ScheduledJobStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(3); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateStartScheduledJobAtomMatcher() { - return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart", - ScheduledJobStateChanged::STARTED); -} - -AtomMatcher CreateFinishScheduledJobAtomMatcher() { - return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish", - ScheduledJobStateChanged::FINISHED); -} - -AtomMatcher CreateScreenBrightnessChangedAtomMatcher() { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId("ScreenBrightnessChanged")); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED); - return atom_matcher; -} - -AtomMatcher CreateUidProcessStateChangedAtomMatcher() { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId("UidProcessStateChanged")); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED); - return atom_matcher; -} - -AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name, - WakelockStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(4); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateAcquireWakelockAtomMatcher() { - return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE); -} - -AtomMatcher CreateReleaseWakelockAtomMatcher() { - return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE); -} - -AtomMatcher CreateScreenStateChangedAtomMatcher( - const string& name, android::view::DisplayStateEnum state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(1); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateScreenTurnedOnAtomMatcher() { - return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", - android::view::DisplayStateEnum::DISPLAY_STATE_ON); -} - -AtomMatcher CreateScreenTurnedOffAtomMatcher() { - return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff", - ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF); -} - -AtomMatcher CreateSyncStateChangedAtomMatcher( - const string& name, SyncStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(3); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateSyncStartAtomMatcher() { - return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON); -} - -AtomMatcher CreateSyncEndAtomMatcher() { - return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF); -} - -AtomMatcher CreateActivityForegroundStateChangedAtomMatcher( - const string& name, ActivityForegroundStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(4); // Activity field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateMoveToBackgroundAtomMatcher() { - return CreateActivityForegroundStateChangedAtomMatcher( - "MoveToBackground", ActivityForegroundStateChanged::BACKGROUND); -} - -AtomMatcher CreateMoveToForegroundAtomMatcher() { - return CreateActivityForegroundStateChangedAtomMatcher( - "MoveToForeground", ActivityForegroundStateChanged::FOREGROUND); -} - -Predicate CreateScheduledJobPredicate() { - Predicate predicate; - predicate.set_id(StringToId("ScheduledJobRunningPredicate")); - predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish")); - return predicate; -} - -Predicate CreateBatterySaverModePredicate() { - Predicate predicate; - predicate.set_id(StringToId("BatterySaverIsOn")); - predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop")); - return predicate; -} - -Predicate CreateScreenIsOnPredicate() { - Predicate predicate; - predicate.set_id(StringToId("ScreenIsOn")); - predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff")); - return predicate; -} - -Predicate CreateScreenIsOffPredicate() { - Predicate predicate; - predicate.set_id(1111123); - predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn")); - return predicate; -} - -Predicate CreateHoldingWakelockPredicate() { - Predicate predicate; - predicate.set_id(StringToId("HoldingWakelock")); - predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock")); - return predicate; -} - -Predicate CreateIsSyncingPredicate() { - Predicate predicate; - predicate.set_id(33333333333333); - predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd")); - return predicate; -} - -Predicate CreateIsInBackgroundPredicate() { - Predicate predicate; - predicate.set_id(StringToId("IsInBackground")); - predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground")); - predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground")); - return predicate; -} - -void addPredicateToPredicateCombination(const Predicate& predicate, - Predicate* combinationPredicate) { - combinationPredicate->mutable_combination()->add_predicate(predicate.id()); -} - -FieldMatcher CreateAttributionUidDimensions(const int atomId, - const std::vector& positions) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const auto position : positions) { - auto child = dimensions.add_child(); - child->set_field(1); - child->set_position(position); - child->add_child()->set_field(1); - } - return dimensions; -} - -FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, - const std::vector& positions) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const auto position : positions) { - auto child = dimensions.add_child(); - child->set_field(1); - child->set_position(position); - child->add_child()->set_field(1); - child->add_child()->set_field(2); - } - return dimensions; -} - -FieldMatcher CreateDimensions(const int atomId, const std::vector& fields) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const int field : fields) { - dimensions.add_child()->set_field(field); - } - return dimensions; -} - -void writeAttribution(AStatsEvent* statsEvent, const vector& attributionUids, - const vector& attributionTags) { - vector cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast(attributionUids.data()), - cTags.data(), attributionUids.size()); -} - -void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) { - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - - AStatsEvent_release(statsEvent); -} - -std::unique_ptr CreateScreenStateChangedEvent( - uint64_t timestampNs, const android::view::DisplayStateEnum state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateScheduledJobStateChangedEvent( - const vector& attributionUids, const vector& attributionTags, - const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, jobName.c_str()); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateStartScheduledJobEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& jobName) { - return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, - ScheduledJobStateChanged::STARTED, timestampNs); -} - -// Create log event when scheduled job finishes. -std::unique_ptr CreateFinishScheduledJobEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& jobName) { - return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, - ScheduledJobStateChanged::FINISHED, timestampNs); -} - -std::unique_ptr CreateSyncStateChangedEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& name, - const SyncStateChanged::State state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, name.c_str()); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateSyncStartEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& name) { - return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, - SyncStateChanged::ON); -} - -std::unique_ptr CreateSyncEndEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& name) { - return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, - SyncStateChanged::OFF); -} - -sp CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, - const ConfigKey& key) { - sp uidMap = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - sp processor = - new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; }, - [](const int&, const vector&) { return true; }); - processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config); - return processor; -} - -void sortLogEventsByTimestamp(std::vector> *events) { - std::sort(events->begin(), events->end(), - [](const std::unique_ptr& a, const std::unique_ptr& b) { - return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs(); - }); -} - -int64_t StringToId(const string& str) { - return static_cast(std::hash()(str)); -} - - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h deleted file mode 100644 index 3efaa850a921..000000000000 --- a/cmds/statsd/benchmark/metric_util.h +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "src/StatsLogProcessor.h" -#include "src/logd/LogEvent.h" -#include "stats_event.h" -#include "statslog.h" - -namespace android { -namespace os { -namespace statsd { - -// Create AtomMatcher proto to simply match a specific atom type. -AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId); - -// Create AtomMatcher proto for scheduled job state changed. -AtomMatcher CreateScheduledJobStateChangedAtomMatcher(); - -// Create AtomMatcher proto for starting a scheduled job. -AtomMatcher CreateStartScheduledJobAtomMatcher(); - -// Create AtomMatcher proto for a scheduled job is done. -AtomMatcher CreateFinishScheduledJobAtomMatcher(); - -// Create AtomMatcher proto for screen brightness state changed. -AtomMatcher CreateScreenBrightnessChangedAtomMatcher(); - -// Create AtomMatcher proto for acquiring wakelock. -AtomMatcher CreateAcquireWakelockAtomMatcher(); - -// Create AtomMatcher proto for releasing wakelock. -AtomMatcher CreateReleaseWakelockAtomMatcher() ; - -// Create AtomMatcher proto for screen turned on. -AtomMatcher CreateScreenTurnedOnAtomMatcher(); - -// Create AtomMatcher proto for screen turned off. -AtomMatcher CreateScreenTurnedOffAtomMatcher(); - -// Create AtomMatcher proto for app sync turned on. -AtomMatcher CreateSyncStartAtomMatcher(); - -// Create AtomMatcher proto for app sync turned off. -AtomMatcher CreateSyncEndAtomMatcher(); - -// Create AtomMatcher proto for app sync moves to background. -AtomMatcher CreateMoveToBackgroundAtomMatcher(); - -// Create AtomMatcher proto for app sync moves to foreground. -AtomMatcher CreateMoveToForegroundAtomMatcher(); - -// Create Predicate proto for screen is off. -Predicate CreateScreenIsOffPredicate(); - -// Create Predicate proto for a running scheduled job. -Predicate CreateScheduledJobPredicate(); - -// Create Predicate proto for holding wakelock. -Predicate CreateHoldingWakelockPredicate(); - -// Create a Predicate proto for app syncing. -Predicate CreateIsSyncingPredicate(); - -// Create a Predicate proto for app is in background. -Predicate CreateIsInBackgroundPredicate(); - -// Add a predicate to the predicate combination. -void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination); - -// Create dimensions from primitive fields. -FieldMatcher CreateDimensions(const int atomId, const std::vector& fields); - -// Create dimensions by attribution uid and tag. -FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, - const std::vector& positions); - -// Create dimensions by attribution uid only. -FieldMatcher CreateAttributionUidDimensions(const int atomId, - const std::vector& positions); - -void writeAttribution(AStatsEvent* statsEvent, const vector& attributionUids, - const vector& attributionTags); - -void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent); - -// Create log event for screen state changed. -std::unique_ptr CreateScreenStateChangedEvent( - uint64_t timestampNs, const android::view::DisplayStateEnum state); - -// Create log event when scheduled job starts. -std::unique_ptr CreateStartScheduledJobEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& jobName); - -// Create log event when scheduled job finishes. -std::unique_ptr CreateFinishScheduledJobEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& jobName); - -// Create log event when the app sync starts. -std::unique_ptr CreateSyncStartEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& name); - -// Create log event when the app sync ends. -std::unique_ptr CreateSyncEndEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& name); - -// Create a statsd log event processor upon the start time in seconds, config and key. -sp CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, - const ConfigKey& key); - -// Util function to sort the log events by timestamp. -void sortLogEventsByTimestamp(std::vector> *events); - -int64_t StringToId(const string& str); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/benchmark/stats_write_benchmark.cpp b/cmds/statsd/benchmark/stats_write_benchmark.cpp deleted file mode 100644 index f5a0cd5dfb39..000000000000 --- a/cmds/statsd/benchmark/stats_write_benchmark.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "benchmark/benchmark.h" -#include - -namespace android { -namespace os { -namespace statsd { - -static void BM_StatsWrite(benchmark::State& state) { - const char* reason = "test"; - int64_t boot_end_time = 1234567; - int64_t total_duration = 100; - int64_t bootloader_duration = 10; - int64_t time_since_last_boot = 99999999; - while (state.KeepRunning()) { - android::util::stats_write( - android::util::BOOT_SEQUENCE_REPORTED, reason, reason, - boot_end_time, total_duration, bootloader_duration, time_since_last_boot); - total_duration++; - } -} -BENCHMARK(BM_StatsWrite); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp deleted file mode 100644 index c9ccfb93c86d..000000000000 --- a/cmds/statsd/src/FieldValue.cpp +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false -#include "Log.h" -#include "FieldValue.h" -#include "HashableDimensionKey.h" -#include "math.h" - -namespace android { -namespace os { -namespace statsd { - -int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth) { - int32_t field = 0; - for (int32_t i = 0; i <= depth; i++) { - int32_t shiftBits = 8 * (kMaxLogDepth - i); - field |= (pos[i] << shiftBits); - } - - if (includeDepth) { - field |= (depth << 24); - } - return field; -} - -int32_t encodeMatcherMask(int32_t mask[], int32_t depth) { - return getEncodedField(mask, depth, false) | 0xff000000; -} - -bool Field::matches(const Matcher& matcher) const { - if (mTag != matcher.mMatcher.getTag()) { - return false; - } - if ((mField & matcher.mMask) == matcher.mMatcher.getField()) { - return true; - } - - if (matcher.hasAllPositionMatcher() && - (mField & (matcher.mMask & kClearAllPositionMatcherMask)) == matcher.mMatcher.getField()) { - return true; - } - - return false; -} - -void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* pos, int* mask, - std::vector* output) { - if (depth > kMaxLogDepth) { - ALOGE("depth > 2"); - return; - } - - pos[depth] = matcher.field(); - mask[depth] = 0x7f; - - if (matcher.has_position()) { - depth++; - if (depth > 2) { - return; - } - switch (matcher.position()) { - case Position::ALL: - pos[depth] = 0x00; - mask[depth] = 0x7f; - break; - case Position::ANY: - pos[depth] = 0; - mask[depth] = 0; - break; - case Position::FIRST: - pos[depth] = 1; - mask[depth] = 0x7f; - break; - case Position::LAST: - pos[depth] = 0x80; - mask[depth] = 0x80; - break; - case Position::POSITION_UNKNOWN: - pos[depth] = 0; - mask[depth] = 0; - break; - } - } - - if (matcher.child_size() == 0) { - output->push_back(Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth))); - } else { - for (const auto& child : matcher.child()) { - translateFieldMatcher(tag, child, depth + 1, pos, mask, output); - } - } -} - -void translateFieldMatcher(const FieldMatcher& matcher, std::vector* output) { - int pos[] = {1, 1, 1}; - int mask[] = {0x7f, 0x7f, 0x7f}; - int tag = matcher.field(); - for (const auto& child : matcher.child()) { - translateFieldMatcher(tag, child, 0, pos, mask, output); - } -} - -bool isAttributionUidField(const FieldValue& value) { - return isAttributionUidField(value.mField, value.mValue); -} - -int32_t getUidIfExists(const FieldValue& value) { - // the field is uid field if the field is the uid field in attribution node - // or annotated as such in the atom - bool isUid = isAttributionUidField(value) || isUidField(value); - return isUid ? value.mValue.int_value : -1; -} - -bool isAttributionUidField(const Field& field, const Value& value) { - int f = field.getField() & 0xff007f; - if (f == 0x10001 && value.getType() == INT) { - return true; - } - return false; -} - -bool isUidField(const FieldValue& fieldValue) { - return fieldValue.mAnnotations.isUidField(); -} - -Value::Value(const Value& from) { - type = from.getType(); - switch (type) { - case INT: - int_value = from.int_value; - break; - case LONG: - long_value = from.long_value; - break; - case FLOAT: - float_value = from.float_value; - break; - case DOUBLE: - double_value = from.double_value; - break; - case STRING: - str_value = from.str_value; - break; - case STORAGE: - storage_value = from.storage_value; - break; - default: - break; - } -} - -std::string Value::toString() const { - switch (type) { - case INT: - return std::to_string(int_value) + "[I]"; - case LONG: - return std::to_string(long_value) + "[L]"; - case FLOAT: - return std::to_string(float_value) + "[F]"; - case DOUBLE: - return std::to_string(double_value) + "[D]"; - case STRING: - return str_value + "[S]"; - case STORAGE: - return "bytes of size " + std::to_string(storage_value.size()) + "[ST]"; - default: - return "[UNKNOWN]"; - } -} - -bool Value::isZero() const { - switch (type) { - case INT: - return int_value == 0; - case LONG: - return long_value == 0; - case FLOAT: - return fabs(float_value) <= std::numeric_limits::epsilon(); - case DOUBLE: - return fabs(double_value) <= std::numeric_limits::epsilon(); - case STRING: - return str_value.size() == 0; - case STORAGE: - return storage_value.size() == 0; - default: - return false; - } -} - -bool Value::operator==(const Value& that) const { - if (type != that.getType()) return false; - - switch (type) { - case INT: - return int_value == that.int_value; - case LONG: - return long_value == that.long_value; - case FLOAT: - return float_value == that.float_value; - case DOUBLE: - return double_value == that.double_value; - case STRING: - return str_value == that.str_value; - case STORAGE: - return storage_value == that.storage_value; - default: - return false; - } -} - -bool Value::operator!=(const Value& that) const { - if (type != that.getType()) return true; - switch (type) { - case INT: - return int_value != that.int_value; - case LONG: - return long_value != that.long_value; - case FLOAT: - return float_value != that.float_value; - case DOUBLE: - return double_value != that.double_value; - case STRING: - return str_value != that.str_value; - case STORAGE: - return storage_value != that.storage_value; - default: - return false; - } -} - -bool Value::operator<(const Value& that) const { - if (type != that.getType()) return type < that.getType(); - - switch (type) { - case INT: - return int_value < that.int_value; - case LONG: - return long_value < that.long_value; - case FLOAT: - return float_value < that.float_value; - case DOUBLE: - return double_value < that.double_value; - case STRING: - return str_value < that.str_value; - case STORAGE: - return storage_value < that.storage_value; - default: - return false; - } -} - -bool Value::operator>(const Value& that) const { - if (type != that.getType()) return type > that.getType(); - - switch (type) { - case INT: - return int_value > that.int_value; - case LONG: - return long_value > that.long_value; - case FLOAT: - return float_value > that.float_value; - case DOUBLE: - return double_value > that.double_value; - case STRING: - return str_value > that.str_value; - case STORAGE: - return storage_value > that.storage_value; - default: - return false; - } -} - -bool Value::operator>=(const Value& that) const { - if (type != that.getType()) return type >= that.getType(); - - switch (type) { - case INT: - return int_value >= that.int_value; - case LONG: - return long_value >= that.long_value; - case FLOAT: - return float_value >= that.float_value; - case DOUBLE: - return double_value >= that.double_value; - case STRING: - return str_value >= that.str_value; - case STORAGE: - return storage_value >= that.storage_value; - default: - return false; - } -} - -Value Value::operator-(const Value& that) const { - Value v; - if (type != that.type) { - ALOGE("Can't operate on different value types, %d, %d", type, that.type); - return v; - } - if (type == STRING) { - ALOGE("Can't operate on string value type"); - return v; - } - - if (type == STORAGE) { - ALOGE("Can't operate on storage value type"); - return v; - } - - switch (type) { - case INT: - v.setInt(int_value - that.int_value); - break; - case LONG: - v.setLong(long_value - that.long_value); - break; - case FLOAT: - v.setFloat(float_value - that.float_value); - break; - case DOUBLE: - v.setDouble(double_value - that.double_value); - break; - default: - break; - } - return v; -} - -Value& Value::operator=(const Value& that) { - type = that.type; - switch (type) { - case INT: - int_value = that.int_value; - break; - case LONG: - long_value = that.long_value; - break; - case FLOAT: - float_value = that.float_value; - break; - case DOUBLE: - double_value = that.double_value; - break; - case STRING: - str_value = that.str_value; - break; - case STORAGE: - storage_value = that.storage_value; - break; - default: - break; - } - return *this; -} - -Value& Value::operator+=(const Value& that) { - if (type != that.type) { - ALOGE("Can't operate on different value types, %d, %d", type, that.type); - return *this; - } - if (type == STRING) { - ALOGE("Can't operate on string value type"); - return *this; - } - if (type == STORAGE) { - ALOGE("Can't operate on storage value type"); - return *this; - } - - switch (type) { - case INT: - int_value += that.int_value; - break; - case LONG: - long_value += that.long_value; - break; - case FLOAT: - float_value += that.float_value; - break; - case DOUBLE: - double_value += that.double_value; - break; - default: - break; - } - return *this; -} - -double Value::getDouble() const { - switch (type) { - case INT: - return int_value; - case LONG: - return long_value; - case FLOAT: - return float_value; - case DOUBLE: - return double_value; - default: - return 0; - } -} - -bool equalDimensions(const std::vector& dimension_a, - const std::vector& dimension_b) { - bool eq = dimension_a.size() == dimension_b.size(); - for (size_t i = 0; eq && i < dimension_a.size(); ++i) { - if (dimension_b[i] != dimension_a[i]) { - eq = false; - } - } - return eq; -} - -bool subsetDimensions(const std::vector& dimension_a, - const std::vector& dimension_b) { - if (dimension_a.size() > dimension_b.size()) { - return false; - } - for (size_t i = 0; i < dimension_a.size(); ++i) { - bool found = false; - for (size_t j = 0; j < dimension_b.size(); ++j) { - if (dimension_a[i] == dimension_b[j]) { - found = true; - } - } - if (!found) { - return false; - } - } - return true; -} - -bool HasPositionANY(const FieldMatcher& matcher) { - if (matcher.has_position() && matcher.position() == Position::ANY) { - return true; - } - for (const auto& child : matcher.child()) { - if (HasPositionANY(child)) { - return true; - } - } - return false; -} - -bool HasPositionALL(const FieldMatcher& matcher) { - if (matcher.has_position() && matcher.position() == Position::ALL) { - return true; - } - for (const auto& child : matcher.child()) { - if (HasPositionALL(child)) { - return true; - } - } - return false; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h deleted file mode 100644 index fd86e3683970..000000000000 --- a/cmds/statsd/src/FieldValue.h +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "annotations.h" - -namespace android { -namespace os { -namespace statsd { - -class HashableDimensionKey; -struct Matcher; -struct Field; -struct FieldValue; - -const int32_t kMaxLogDepth = 2; -const int32_t kLastBitMask = 0x80; -const int32_t kClearLastBitDeco = 0x7f; -const int32_t kClearAllPositionMatcherMask = 0xffff00ff; - -enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING, STORAGE }; - -int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth); - -int32_t encodeMatcherMask(int32_t mask[], int32_t depth); - -// Get the encoded field for a leaf with a [field] number at depth 0; -inline int32_t getSimpleField(size_t field) { - return ((int32_t)field << 8 * 2); -} - -/** - * Field is a wrapper class for 2 integers that represents the field of a log element in its Atom - * proto. - * [mTag]: the atom id. - * [mField]: encoded path from the root (atom) to leaf. - * - * For example: - * WakeLockStateChanged { - * repeated AttributionNode = 1; - * int state = 2; - * string tag = 3; - * } - * Read from logd, the items are structured as below: - * [[[1000, "tag"], [2000, "tag2"],], 2,"hello"] - * - * When we read through the list, we will encode each field in a 32bit integer. - * 8bit segments |--------|--------|--------|--------| - * Depth field0 [L]field1 [L]field1 - * - * The first 8 bits are the depth of the field. for example, the uid 1000 has depth 2. - * The following 3 8-bit are for the item's position at each level. - * The first bit of each 8bits field is reserved to mark if the item is the last item at that level - * this is to make matching easier later. - * - * The above wakelock event is translated into FieldValue pairs. - * 0x02010101->1000 - * 0x02010182->tag - * 0x02018201->2000 - * 0x02018282->tag2 - * 0x00020000->2 - * 0x00030000->"hello" - * - * This encoding is the building block for the later operations. - * Please see the definition for Matcher below to see how the matching is done. - */ -struct Field { -private: - int32_t mTag; - int32_t mField; - -public: - Field() {} - - Field(int32_t tag, int32_t pos[], int32_t depth) : mTag(tag) { - mField = getEncodedField(pos, depth, true); - } - - Field(const Field& from) : mTag(from.getTag()), mField(from.getField()) { - } - - Field(int32_t tag, int32_t field) : mTag(tag), mField(field){}; - - inline void setField(int32_t field) { - mField = field; - } - - inline void setTag(int32_t tag) { - mTag = tag; - } - - inline void decorateLastPos(int32_t depth) { - int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth); - mField |= mask; - } - - inline int32_t getTag() const { - return mTag; - } - - inline int32_t getDepth() const { - return (mField >> 24); - } - - inline int32_t getPath(int32_t depth) const { - if (depth > 2 || depth < 0) return 0; - - int32_t field = (mField & 0x00ffffff); - int32_t mask = 0xffffffff; - return (field & (mask << 8 * (kMaxLogDepth - depth))); - } - - inline int32_t getPrefix(int32_t depth) const { - if (depth == 0) return 0; - return getPath(depth - 1); - } - - inline int32_t getField() const { - return mField; - } - - inline int32_t getRawPosAtDepth(int32_t depth) const { - int32_t field = (mField & 0x00ffffff); - int32_t shift = 8 * (kMaxLogDepth - depth); - int32_t mask = 0xff << shift; - - return (field & mask) >> shift; - } - - inline int32_t getPosAtDepth(int32_t depth) const { - return getRawPosAtDepth(depth) & kClearLastBitDeco; - } - - // Check if the first bit of the 8-bit segment for depth is 1 - inline bool isLastPos(int32_t depth) const { - int32_t field = (mField & 0x00ffffff); - int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth); - return (field & mask) != 0; - } - - // if the 8-bit segment is all 0's - inline bool isAnyPosMatcher(int32_t depth) const { - return getDepth() >= depth && getRawPosAtDepth(depth) == 0; - } - // if the 8bit is 0x80 (1000 0000) - inline bool isLastPosMatcher(int32_t depth) const { - return getDepth() >= depth && getRawPosAtDepth(depth) == kLastBitMask; - } - - inline bool operator==(const Field& that) const { - return mTag == that.getTag() && mField == that.getField(); - }; - - inline bool operator!=(const Field& that) const { - return mTag != that.getTag() || mField != that.getField(); - }; - - bool operator<(const Field& that) const { - if (mTag != that.getTag()) { - return mTag < that.getTag(); - } - - if (mField != that.getField()) { - return mField < that.getField(); - } - - return false; - } - - bool matches(const Matcher& that) const; -}; - -/** - * Matcher represents a leaf matcher in the FieldMatcher in statsd_config. - * - * It contains all information needed to match one or more leaf node. - * All information is encoded in a Field(2 ints) and a bit mask(1 int). - * - * For example, to match the first/any/last uid field in attribution chain in Atom 10, - * we have the following FieldMatcher in statsd_config - * FieldMatcher { - * field:10 - * FieldMatcher { - * field:1 - * position: any/last/first - * FieldMatcher { - * field:1 - * } - * } - * } - * - * We translate the FieldMatcher into a Field, and mask - * First: [Matcher Field] 0x02010101 [Mask]0xff7f7f7f - * Last: [Matcher Field] 0x02018001 [Mask]0xff7f807f - * Any: [Matcher Field] 0x02010001 [Mask]0xff7f007f - * All: [Matcher Field] 0x02010001 [Mask]0xff7f7f7f - * - * [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if - * the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are - * equal. Nothing can beat the performance of this matching algorithm. - * - * TODO(b/110561213): ADD EXAMPLE HERE. - */ -struct Matcher { - Matcher(const Field& matcher, int32_t mask) : mMatcher(matcher), mMask(mask){}; - - const Field mMatcher; - const int32_t mMask; - - inline const Field& getMatcher() const { - return mMatcher; - } - - inline int32_t getMask() const { - return mMask; - } - - inline int32_t getRawMaskAtDepth(int32_t depth) const { - int32_t field = (mMask & 0x00ffffff); - int32_t shift = 8 * (kMaxLogDepth - depth); - int32_t mask = 0xff << shift; - - return (field & mask) >> shift; - } - - bool hasAllPositionMatcher() const { - return mMatcher.getDepth() == 2 && getRawMaskAtDepth(1) == 0x7f; - } - - bool hasAnyPositionMatcher(int* prefix) const { - if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(1) == 0) { - (*prefix) = mMatcher.getPrefix(1); - return true; - } - return false; - } - - inline bool operator!=(const Matcher& that) const { - return mMatcher != that.getMatcher() || mMask != that.getMask(); - } - - inline bool operator==(const Matcher& that) const { - return mMatcher == that.mMatcher && mMask == that.mMask; - } -}; - -inline Matcher getSimpleMatcher(int32_t tag, size_t field) { - return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000); -} - -inline Matcher getFirstUidMatcher(int32_t atomId) { - int32_t pos[] = {1, 1, 1}; - return Matcher(Field(atomId, pos, 2), 0xff7f7f7f); -} - -/** - * A wrapper for a union type to contain multiple types of values. - * - */ -struct Value { - Value() : type(UNKNOWN) {} - - Value(int32_t v) { - int_value = v; - type = INT; - } - - Value(int64_t v) { - long_value = v; - type = LONG; - } - - Value(float v) { - float_value = v; - type = FLOAT; - } - - Value(double v) { - double_value = v; - type = DOUBLE; - } - - Value(const std::string& v) { - str_value = v; - type = STRING; - } - - Value(const std::vector& v) { - storage_value = v; - type = STORAGE; - } - - void setInt(int32_t v) { - int_value = v; - type = INT; - } - - void setLong(int64_t v) { - long_value = v; - type = LONG; - } - - void setFloat(float v) { - float_value = v; - type = FLOAT; - } - - void setDouble(double v) { - double_value = v; - type = DOUBLE; - } - - union { - int32_t int_value; - int64_t long_value; - float float_value; - double double_value; - }; - std::string str_value; - std::vector storage_value; - - Type type; - - std::string toString() const; - - bool isZero() const; - - Type getType() const { - return type; - } - - double getDouble() const; - - Value(const Value& from); - - bool operator==(const Value& that) const; - bool operator!=(const Value& that) const; - - bool operator<(const Value& that) const; - bool operator>(const Value& that) const; - bool operator>=(const Value& that) const; - Value operator-(const Value& that) const; - Value& operator+=(const Value& that); - Value& operator=(const Value& that); -}; - -class Annotations { -public: - Annotations() { - setNested(true); // Nested = true by default - } - - // This enum stores where particular annotations can be found in the - // bitmask. Note that these pos do not correspond to annotation ids. - enum { - NESTED_POS = 0x0, - PRIMARY_POS = 0x1, - EXCLUSIVE_POS = 0x2, - UID_POS = 0x3 - }; - - inline void setNested(bool nested) { setBitmaskAtPos(NESTED_POS, nested); } - - inline void setPrimaryField(bool primary) { setBitmaskAtPos(PRIMARY_POS, primary); } - - inline void setExclusiveState(bool exclusive) { setBitmaskAtPos(EXCLUSIVE_POS, exclusive); } - - inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); } - - // Default value = false - inline bool isNested() const { return getValueFromBitmask(NESTED_POS); } - - // Default value = false - inline bool isPrimaryField() const { return getValueFromBitmask(PRIMARY_POS); } - - // Default value = false - inline bool isExclusiveState() const { return getValueFromBitmask(EXCLUSIVE_POS); } - - // Default value = false - inline bool isUidField() const { return getValueFromBitmask(UID_POS); } - -private: - inline void setBitmaskAtPos(int pos, bool value) { - mBooleanBitmask &= ~(1 << pos); // clear - mBooleanBitmask |= (value << pos); // set - } - - inline bool getValueFromBitmask(int pos) const { - return (mBooleanBitmask >> pos) & 0x1; - } - - // This is a bitmask over all annotations stored in boolean form. Because - // there are only 4 booleans, just one byte is required. - uint8_t mBooleanBitmask = 0; -}; - -/** - * Represents a log item, or a dimension item (They are essentially the same). - */ -struct FieldValue { - FieldValue() {} - FieldValue(const Field& field, const Value& value) : mField(field), mValue(value) { - } - bool operator==(const FieldValue& that) const { - return mField == that.mField && mValue == that.mValue; - } - bool operator!=(const FieldValue& that) const { - return mField != that.mField || mValue != that.mValue; - } - bool operator<(const FieldValue& that) const { - if (mField != that.mField) { - return mField < that.mField; - } - - if (mValue != that.mValue) { - return mValue < that.mValue; - } - - return false; - } - - Field mField; - Value mValue; - Annotations mAnnotations; -}; - -bool HasPositionANY(const FieldMatcher& matcher); -bool HasPositionALL(const FieldMatcher& matcher); - -bool isAttributionUidField(const FieldValue& value); - -/* returns uid if the field is uid field, or -1 if the field is not a uid field */ -int getUidIfExists(const FieldValue& value); - -void translateFieldMatcher(const FieldMatcher& matcher, std::vector* output); - -bool isAttributionUidField(const Field& field, const Value& value); -bool isUidField(const FieldValue& fieldValue); - -bool equalDimensions(const std::vector& dimension_a, - const std::vector& dimension_b); - -// Returns true if dimension_a is a subset of dimension_b. -bool subsetDimensions(const std::vector& dimension_a, - const std::vector& dimension_b); -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp deleted file mode 100644 index eba66e0cb7b0..000000000000 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "HashableDimensionKey.h" -#include "FieldValue.h" - -namespace android { -namespace os { -namespace statsd { - -using std::string; -using std::vector; -using android::base::StringPrintf; - -// These constants must be kept in sync with those in StatsDimensionsValue.java -const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2; -const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3; -const static int STATS_DIMENSIONS_VALUE_LONG_TYPE = 4; -// const static int STATS_DIMENSIONS_VALUE_BOOL_TYPE = 5; (commented out because -// unused -- statsd does not correctly support bool types) -const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6; -const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7; - -/** - * Recursive helper function that populates a parent StatsDimensionsValueParcel - * with children StatsDimensionsValueParcels. - * - * \param parent parcel that will be populated with children - * \param childDepth depth of children FieldValues - * \param childPrefix expected FieldValue prefix of children - * \param dims vector of FieldValues stored by HashableDimensionKey - * \param index position in dims to start reading children from - */ -static void populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel& parent, - int childDepth, int childPrefix, - const vector& dims, - size_t& index) { - if (childDepth > 2) { - ALOGE("Depth > 2 not supported by StatsDimensionsValueParcel."); - return; - } - - while (index < dims.size()) { - const FieldValue& dim = dims[index]; - int fieldDepth = dim.mField.getDepth(); - int fieldPrefix = dim.mField.getPrefix(childDepth); - - StatsDimensionsValueParcel child; - child.field = dim.mField.getPosAtDepth(childDepth); - - if (fieldDepth == childDepth && fieldPrefix == childPrefix) { - switch (dim.mValue.getType()) { - case INT: - child.valueType = STATS_DIMENSIONS_VALUE_INT_TYPE; - child.intValue = dim.mValue.int_value; - break; - case LONG: - child.valueType = STATS_DIMENSIONS_VALUE_LONG_TYPE; - child.longValue = dim.mValue.long_value; - break; - case FLOAT: - child.valueType = STATS_DIMENSIONS_VALUE_FLOAT_TYPE; - child.floatValue = dim.mValue.float_value; - break; - case STRING: - child.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE; - child.stringValue = dim.mValue.str_value; - break; - default: - ALOGE("Encountered FieldValue with unsupported value type."); - break; - } - index++; - parent.tupleValue.push_back(child); - } else if (fieldDepth > childDepth && fieldPrefix == childPrefix) { - // This FieldValue is not a child of the current parent, but it is - // an indirect descendant. Thus, create a direct child of TUPLE_TYPE - // and recurse to parcel the indirect descendants. - child.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE; - populateStatsDimensionsValueParcelChildren(child, childDepth + 1, - dim.mField.getPrefix(childDepth + 1), dims, - index); - parent.tupleValue.push_back(child); - } else { - return; - } - } -} - -StatsDimensionsValueParcel HashableDimensionKey::toStatsDimensionsValueParcel() const { - StatsDimensionsValueParcel root; - if (mValues.size() == 0) { - return root; - } - - root.field = mValues[0].mField.getTag(); - root.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE; - - // Children of the root correspond to top-level (depth = 0) FieldValues. - int childDepth = 0; - int childPrefix = 0; - size_t index = 0; - populateStatsDimensionsValueParcelChildren(root, childDepth, childPrefix, mValues, index); - - return root; -} - -android::hash_t hashDimension(const HashableDimensionKey& value) { - android::hash_t hash = 0; - for (const auto& fieldValue : value.getValues()) { - hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getField())); - hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getTag())); - hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mValue.getType())); - switch (fieldValue.mValue.getType()) { - case INT: - hash = android::JenkinsHashMix(hash, - android::hash_type(fieldValue.mValue.int_value)); - break; - case LONG: - hash = android::JenkinsHashMix(hash, - android::hash_type(fieldValue.mValue.long_value)); - break; - case STRING: - hash = android::JenkinsHashMix(hash, static_cast(std::hash()( - fieldValue.mValue.str_value))); - break; - case FLOAT: { - hash = android::JenkinsHashMix(hash, - android::hash_type(fieldValue.mValue.float_value)); - break; - } - default: - break; - } - } - return JenkinsHashWhiten(hash); -} - -bool filterValues(const Matcher& matcherField, const vector& values, - FieldValue* output) { - for (const auto& value : values) { - if (value.mField.matches(matcherField)) { - (*output) = value; - return true; - } - } - return false; -} - -bool filterValues(const vector& matcherFields, const vector& values, - HashableDimensionKey* output) { - size_t num_matches = 0; - for (const auto& value : values) { - for (size_t i = 0; i < matcherFields.size(); ++i) { - const auto& matcher = matcherFields[i]; - if (value.mField.matches(matcher)) { - output->addValue(value); - output->mutableValue(num_matches)->mField.setTag(value.mField.getTag()); - output->mutableValue(num_matches)->mField.setField( - value.mField.getField() & matcher.mMask); - num_matches++; - } - } - } - return num_matches > 0; -} - -bool filterPrimaryKey(const std::vector& values, HashableDimensionKey* output) { - size_t num_matches = 0; - const int32_t simpleFieldMask = 0xff7f0000; - const int32_t attributionUidFieldMask = 0xff7f7f7f; - for (const auto& value : values) { - if (value.mAnnotations.isPrimaryField()) { - output->addValue(value); - output->mutableValue(num_matches)->mField.setTag(value.mField.getTag()); - const int32_t mask = - isAttributionUidField(value) ? attributionUidFieldMask : simpleFieldMask; - output->mutableValue(num_matches)->mField.setField(value.mField.getField() & mask); - num_matches++; - } - } - return num_matches > 0; -} - -void filterGaugeValues(const std::vector& matcherFields, - const std::vector& values, std::vector* output) { - for (const auto& field : matcherFields) { - for (const auto& value : values) { - if (value.mField.matches(field)) { - output->push_back(value); - } - } - } -} - -void getDimensionForCondition(const std::vector& eventValues, - const Metric2Condition& links, - HashableDimensionKey* conditionDimension) { - // Get the dimension first by using dimension from what. - filterValues(links.metricFields, eventValues, conditionDimension); - - size_t count = conditionDimension->getValues().size(); - if (count != links.conditionFields.size()) { - return; - } - - for (size_t i = 0; i < count; i++) { - conditionDimension->mutableValue(i)->mField.setField( - links.conditionFields[i].mMatcher.getField()); - conditionDimension->mutableValue(i)->mField.setTag( - links.conditionFields[i].mMatcher.getTag()); - } -} - -void getDimensionForState(const std::vector& eventValues, const Metric2State& link, - HashableDimensionKey* statePrimaryKey) { - // First, get the dimension from the event using the "what" fields from the - // MetricStateLinks. - filterValues(link.metricFields, eventValues, statePrimaryKey); - - // Then check that the statePrimaryKey size equals the number of state fields - size_t count = statePrimaryKey->getValues().size(); - if (count != link.stateFields.size()) { - return; - } - - // For each dimension Value in the statePrimaryKey, set the field and tag - // using the state atom fields from MetricStateLinks. - for (size_t i = 0; i < count; i++) { - statePrimaryKey->mutableValue(i)->mField.setField(link.stateFields[i].mMatcher.getField()); - statePrimaryKey->mutableValue(i)->mField.setTag(link.stateFields[i].mMatcher.getTag()); - } -} - -bool containsLinkedStateValues(const HashableDimensionKey& whatKey, - const HashableDimensionKey& primaryKey, - const vector& stateLinks, const int32_t stateAtomId) { - if (whatKey.getValues().size() < primaryKey.getValues().size()) { - ALOGE("Contains linked values false: whatKey is too small"); - return false; - } - - for (const auto& primaryValue : primaryKey.getValues()) { - bool found = false; - for (const auto& whatValue : whatKey.getValues()) { - if (linked(stateLinks, stateAtomId, primaryValue.mField, whatValue.mField) && - primaryValue.mValue == whatValue.mValue) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - return true; -} - -bool linked(const vector& stateLinks, const int32_t stateAtomId, - const Field& stateField, const Field& metricField) { - for (auto stateLink : stateLinks) { - if (stateLink.stateAtomId != stateAtomId) { - continue; - } - - for (size_t i = 0; i < stateLink.stateFields.size(); i++) { - if (stateLink.stateFields[i].mMatcher == stateField && - stateLink.metricFields[i].mMatcher == metricField) { - return true; - } - } - } - return false; -} - -bool LessThan(const vector& s1, const vector& s2) { - if (s1.size() != s2.size()) { - return s1.size() < s2.size(); - } - - size_t count = s1.size(); - for (size_t i = 0; i < count; i++) { - if (s1[i] != s2[i]) { - return s1[i] < s2[i]; - } - } - return false; -} - -bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const { - return !((*this) == that); -} - -bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const { - if (mValues.size() != that.getValues().size()) { - return false; - } - size_t count = mValues.size(); - for (size_t i = 0; i < count; i++) { - if (mValues[i] != (that.getValues())[i]) { - return false; - } - } - return true; -}; - -bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const { - return LessThan(getValues(), that.getValues()); -}; - -bool HashableDimensionKey::contains(const HashableDimensionKey& that) const { - if (mValues.size() < that.getValues().size()) { - return false; - } - - if (mValues.size() == that.getValues().size()) { - return (*this) == that; - } - - for (const auto& value : that.getValues()) { - bool found = false; - for (const auto& myValue : mValues) { - if (value.mField == myValue.mField && value.mValue == myValue.mValue) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - - return true; -} - -string HashableDimensionKey::toString() const { - std::string output; - for (const auto& value : mValues) { - output += StringPrintf("(%d)%#x->%s ", value.mField.getTag(), value.mField.getField(), - value.mValue.toString().c_str()); - } - return output; -} - -bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const { - return mDimensionKeyInWhat == that.getDimensionKeyInWhat() && - mStateValuesKey == that.getStateValuesKey(); -}; - -string MetricDimensionKey::toString() const { - return mDimensionKeyInWhat.toString() + mStateValuesKey.toString(); -} - -bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const { - if (mDimensionKeyInWhat < that.getDimensionKeyInWhat()) { - return true; - } else if (that.getDimensionKeyInWhat() < mDimensionKeyInWhat) { - return false; - } - - return mStateValuesKey < that.getStateValuesKey(); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h deleted file mode 100644 index bd011005a301..000000000000 --- a/cmds/statsd/src/HashableDimensionKey.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include "android-base/stringprintf.h" -#include "FieldValue.h" -#include "logd/LogEvent.h" - -namespace android { -namespace os { -namespace statsd { - -using ::aidl::android::os::StatsDimensionsValueParcel; - -struct Metric2Condition { - int64_t conditionId; - std::vector metricFields; - std::vector conditionFields; -}; - -struct Metric2State { - int32_t stateAtomId; - std::vector metricFields; - std::vector stateFields; -}; - -class HashableDimensionKey { -public: - explicit HashableDimensionKey(const std::vector& values) { - mValues = values; - } - - HashableDimensionKey() {}; - - HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){}; - - inline void addValue(const FieldValue& value) { - mValues.push_back(value); - } - - inline const std::vector& getValues() const { - return mValues; - } - - inline std::vector* mutableValues() { - return &mValues; - } - - inline FieldValue* mutableValue(size_t i) { - if (i >= 0 && i < mValues.size()) { - return &(mValues[i]); - } - return nullptr; - } - - StatsDimensionsValueParcel toStatsDimensionsValueParcel() const; - - std::string toString() const; - - bool operator!=(const HashableDimensionKey& that) const; - - bool operator==(const HashableDimensionKey& that) const; - - bool operator<(const HashableDimensionKey& that) const; - - bool contains(const HashableDimensionKey& that) const; - -private: - std::vector mValues; -}; - -class MetricDimensionKey { -public: - explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat, - const HashableDimensionKey& stateValuesKey) - : mDimensionKeyInWhat(dimensionKeyInWhat), mStateValuesKey(stateValuesKey){}; - - MetricDimensionKey(){}; - - MetricDimensionKey(const MetricDimensionKey& that) - : mDimensionKeyInWhat(that.getDimensionKeyInWhat()), - mStateValuesKey(that.getStateValuesKey()){}; - - MetricDimensionKey& operator=(const MetricDimensionKey& from) = default; - - std::string toString() const; - - inline const HashableDimensionKey& getDimensionKeyInWhat() const { - return mDimensionKeyInWhat; - } - - inline const HashableDimensionKey& getStateValuesKey() const { - return mStateValuesKey; - } - - inline HashableDimensionKey* getMutableStateValuesKey() { - return &mStateValuesKey; - } - - inline void setStateValuesKey(const HashableDimensionKey& key) { - mStateValuesKey = key; - } - - bool hasStateValuesKey() const { - return mStateValuesKey.getValues().size() > 0; - } - - bool operator==(const MetricDimensionKey& that) const; - - bool operator<(const MetricDimensionKey& that) const; - -private: - HashableDimensionKey mDimensionKeyInWhat; - HashableDimensionKey mStateValuesKey; -}; - -android::hash_t hashDimension(const HashableDimensionKey& key); - -/** - * Returns true if a FieldValue field matches the matcher field. - * The value of the FieldValue is output. - */ -bool filterValues(const Matcher& matcherField, const std::vector& values, - FieldValue* output); - -/** - * Creating HashableDimensionKeys from FieldValues using matcher. - * - * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL - * in it. This is because: for example, when we create dimension from last uid in attribution chain, - * In one event, uid 1000 is at position 5 and it's the last - * In another event, uid 1000 is at position 6, and it's the last - * these 2 events should be mapped to the same dimension. So we will remove the original position - * from the dimension key for the uid field (by applying 0x80 bit mask). - */ -bool filterValues(const std::vector& matcherFields, const std::vector& values, - HashableDimensionKey* output); - -/** - * Creating HashableDimensionKeys from State Primary Keys in FieldValues. - * - * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL - * in it. This is because: for example, when we create dimension from last uid in attribution chain, - * In one event, uid 1000 is at position 5 and it's the last - * In another event, uid 1000 is at position 6, and it's the last - * these 2 events should be mapped to the same dimension. So we will remove the original position - * from the dimension key for the uid field (by applying 0x80 bit mask). - */ -bool filterPrimaryKey(const std::vector& values, HashableDimensionKey* output); - -/** - * Filter the values from FieldValues using the matchers. - * - * In contrast to the above function, this function will not do any modification to the original - * data. Considering it as taking a snapshot on the atom event. - */ -void filterGaugeValues(const std::vector& matchers, const std::vector& values, - std::vector* output); - -void getDimensionForCondition(const std::vector& eventValues, - const Metric2Condition& links, - HashableDimensionKey* conditionDimension); - -/** - * Get dimension values using metric's "what" fields and fill statePrimaryKey's - * mField information using "state" fields. - */ -void getDimensionForState(const std::vector& eventValues, const Metric2State& link, - HashableDimensionKey* statePrimaryKey); - -/** - * Returns true if the primaryKey values are a subset of the whatKey values. - * The values from the primaryKey come from the state atom, so we need to - * check that a link exists between the state atom field and what atom field. - * - * Example: - * whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}] - * statePrimaryKey = [Atom: 27, {uid: 1005}] - * Returns true IF one of the Metric2State links Atom 10's uid to Atom 27's uid - * - * Example: - * whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}] - * statePrimaryKey = [Atom: 59, {uid: 1005, package_name: "system"}] - * Returns false - */ -bool containsLinkedStateValues(const HashableDimensionKey& whatKey, - const HashableDimensionKey& primaryKey, - const std::vector& stateLinks, - const int32_t stateAtomId); - -/** - * Returns true if there is a Metric2State link that links the stateField and - * the metricField (they are equal fields from different atoms). - */ -bool linked(const std::vector& stateLinks, const int32_t stateAtomId, - const Field& stateField, const Field& metricField); -} // namespace statsd -} // namespace os -} // namespace android - -namespace std { - -using android::os::statsd::HashableDimensionKey; -using android::os::statsd::MetricDimensionKey; - -template <> -struct hash { - std::size_t operator()(const HashableDimensionKey& key) const { - return hashDimension(key); - } -}; - -template <> -struct hash { - std::size_t operator()(const MetricDimensionKey& key) const { - android::hash_t hash = hashDimension(key.getDimensionKeyInWhat()); - hash = android::JenkinsHashMix(hash, hashDimension(key.getStateValuesKey())); - return android::JenkinsHashWhiten(hash); - } -}; -} // namespace std diff --git a/cmds/statsd/src/Log.h b/cmds/statsd/src/Log.h deleted file mode 100644 index 87f4cbaf02f6..000000000000 --- a/cmds/statsd/src/Log.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * This file must be included at the top of the file. Other header files - * occasionally include log.h, and if LOG_TAG isn't set when that happens - * we'll get a preprocesser error when we try to define it here. - */ - -#pragma once - -#define LOG_TAG "statsd" - -#include - -// Use the local value to turn on/off debug logs instead of using log.tag. properties. -// The advantage is that in production compiler can remove the logging code if the local -// DEBUG/VERBOSE is false. -#define VLOG(...) \ - if (DEBUG) ALOGD(__VA_ARGS__); diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp deleted file mode 100644 index c10f248f4702..000000000000 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ /dev/null @@ -1,1132 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsLogProcessor.h" - -#include -#include -#include -#include - -#include "android-base/stringprintf.h" -#include "external/StatsPullerManager.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "metrics/CountMetricProducer.h" -#include "StatsService.h" -#include "state/StateManager.h" -#include "stats_log_util.h" -#include "stats_util.h" -#include "statslog_statsd.h" -#include "storage/StorageManager.h" - -using namespace android; -using android::base::StringPrintf; -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -// for ConfigMetricsReportList -const int FIELD_ID_CONFIG_KEY = 1; -const int FIELD_ID_REPORTS = 2; -// for ConfigKey -const int FIELD_ID_UID = 1; -const int FIELD_ID_ID = 2; -// for ConfigMetricsReport -// const int FIELD_ID_METRICS = 1; // written in MetricsManager.cpp -const int FIELD_ID_UID_MAP = 2; -const int FIELD_ID_LAST_REPORT_ELAPSED_NANOS = 3; -const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4; -const int FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS = 5; -const int FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS = 6; -const int FIELD_ID_DUMP_REPORT_REASON = 8; -const int FIELD_ID_STRINGS = 9; - -// for ActiveConfigList -const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1; - -// for permissions checks -constexpr const char* kPermissionDump = "android.permission.DUMP"; -constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS"; - -#define NS_PER_HOUR 3600 * NS_PER_SEC - -#define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric" -#define STATS_METADATA_DIR "/data/misc/stats-metadata" - -// Cool down period for writing data to disk to avoid overwriting files. -#define WRITE_DATA_COOL_DOWN_SEC 5 - -StatsLogProcessor::StatsLogProcessor(const sp& uidMap, - const sp& pullerManager, - const sp& anomalyAlarmMonitor, - const sp& periodicAlarmMonitor, - const int64_t timeBaseNs, - const std::function& sendBroadcast, - const std::function&)>& activateBroadcast) - : mUidMap(uidMap), - mPullerManager(pullerManager), - mAnomalyAlarmMonitor(anomalyAlarmMonitor), - mPeriodicAlarmMonitor(periodicAlarmMonitor), - mSendBroadcast(sendBroadcast), - mSendActivationBroadcast(activateBroadcast), - mTimeBaseNs(timeBaseNs), - mLargestTimestampSeen(0), - mLastTimestampSeen(0) { - mPullerManager->ForceClearPullerCache(); - StateManager::getInstance().updateLogSources(uidMap); -} - -StatsLogProcessor::~StatsLogProcessor() { -} - -static void flushProtoToBuffer(ProtoOutputStream& proto, vector* outData) { - outData->clear(); - outData->resize(proto.size()); - size_t pos = 0; - sp reader = proto.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((*outData)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } -} - -void StatsLogProcessor::processFiredAnomalyAlarmsLocked( - const int64_t& timestampNs, - unordered_set, SpHash> alarmSet) { - for (const auto& itr : mMetricsManagers) { - itr.second->onAnomalyAlarmFired(timestampNs, alarmSet); - } -} -void StatsLogProcessor::onPeriodicAlarmFired( - const int64_t& timestampNs, - unordered_set, SpHash> alarmSet) { - - std::lock_guard lock(mMetricsMutex); - for (const auto& itr : mMetricsManagers) { - itr.second->onPeriodicAlarmFired(timestampNs, alarmSet); - } -} - -void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const { - if (std::pair indexRange; event->hasAttributionChain(&indexRange)) { - vector* const fieldValues = event->getMutableValues(); - for (int i = indexRange.first; i <= indexRange.second; i++) { - FieldValue& fieldValue = fieldValues->at(i); - if (isAttributionUidField(fieldValue)) { - const int hostUid = mUidMap->getHostUidOrSelf(fieldValue.mValue.int_value); - fieldValue.mValue.setInt(hostUid); - } - } - } else { - int uidFieldIndex = event->getUidFieldIndex(); - if (uidFieldIndex != -1) { - Value& value = (*event->getMutableValues())[uidFieldIndex].mValue; - const int hostUid = mUidMap->getHostUidOrSelf(value.int_value); - value.setInt(hostUid); - } - } -} - -void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) { - status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR; - bool is_create = event.GetBool(3, &err); - auto parent_uid = int(event.GetLong(1, &err2)); - auto isolated_uid = int(event.GetLong(2, &err3)); - if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) { - if (is_create) { - mUidMap->assignIsolatedUid(isolated_uid, parent_uid); - } else { - mUidMap->removeIsolatedUid(isolated_uid); - } - } else { - ALOGE("Failed to parse uid in the isolated uid change event."); - } -} - -void StatsLogProcessor::onBinaryPushStateChangedEventLocked(LogEvent* event) { - pid_t pid = event->GetPid(); - uid_t uid = event->GetUid(); - if (!checkPermissionForIds(kPermissionDump, pid, uid) || - !checkPermissionForIds(kPermissionUsage, pid, uid)) { - return; - } - // The Get* functions don't modify the status on success, they only write in - // failure statuses, so we can use one status variable for all calls then - // check if it is no longer NO_ERROR. - status_t err = NO_ERROR; - InstallTrainInfo trainInfo; - trainInfo.trainName = string(event->GetString(1 /*train name field id*/, &err)); - trainInfo.trainVersionCode = event->GetLong(2 /*train version field id*/, &err); - trainInfo.requiresStaging = event->GetBool(3 /*requires staging field id*/, &err); - trainInfo.rollbackEnabled = event->GetBool(4 /*rollback enabled field id*/, &err); - trainInfo.requiresLowLatencyMonitor = - event->GetBool(5 /*requires low latency monitor field id*/, &err); - trainInfo.status = int32_t(event->GetLong(6 /*state field id*/, &err)); - std::vector trainExperimentIdBytes = - event->GetStorage(7 /*experiment ids field id*/, &err); - bool is_rollback = event->GetBool(10 /*is rollback field id*/, &err); - - if (err != NO_ERROR) { - ALOGE("Failed to parse fields in binary push state changed log event"); - return; - } - ExperimentIds trainExperimentIds; - if (!trainExperimentIds.ParseFromArray(trainExperimentIdBytes.data(), - trainExperimentIdBytes.size())) { - ALOGE("Failed to parse experimentids in binary push state changed."); - return; - } - trainInfo.experimentIds = {trainExperimentIds.experiment_id().begin(), - trainExperimentIds.experiment_id().end()}; - - // Update the train info on disk and get any data the logevent is missing. - getAndUpdateTrainInfoOnDisk(is_rollback, &trainInfo); - - std::vector trainExperimentIdProto; - writeExperimentIdsToProto(trainInfo.experimentIds, &trainExperimentIdProto); - int32_t userId = multiuser_get_user_id(uid); - - event->updateValue(2 /*train version field id*/, trainInfo.trainVersionCode, LONG); - event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STORAGE); - event->updateValue(8 /*user id field id*/, userId, INT); - - // If this event is a rollback event, then the following bits in the event - // are invalid and we will need to update them with the values we pulled - // from disk. - if (is_rollback) { - int bit = trainInfo.requiresStaging ? 1 : 0; - event->updateValue(3 /*requires staging field id*/, bit, INT); - bit = trainInfo.rollbackEnabled ? 1 : 0; - event->updateValue(4 /*rollback enabled field id*/, bit, INT); - bit = trainInfo.requiresLowLatencyMonitor ? 1 : 0; - event->updateValue(5 /*requires low latency monitor field id*/, bit, INT); - } -} - -void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(bool is_rollback, - InstallTrainInfo* trainInfo) { - // If the train name is empty, we don't know which train to attribute the - // event to, so return early. - if (trainInfo->trainName.empty()) { - return; - } - bool readTrainInfoSuccess = false; - InstallTrainInfo trainInfoOnDisk; - readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo->trainName, trainInfoOnDisk); - - bool resetExperimentIds = false; - if (readTrainInfoSuccess) { - // Keep the old train version if we received an empty version. - if (trainInfo->trainVersionCode == -1) { - trainInfo->trainVersionCode = trainInfoOnDisk.trainVersionCode; - } else if (trainInfo->trainVersionCode != trainInfoOnDisk.trainVersionCode) { - // Reset experiment ids if we receive a new non-empty train version. - resetExperimentIds = true; - } - - // Reset if we received a different experiment id. - if (!trainInfo->experimentIds.empty() && - (trainInfoOnDisk.experimentIds.empty() || - trainInfo->experimentIds.at(0) != trainInfoOnDisk.experimentIds[0])) { - resetExperimentIds = true; - } - } - - // Find the right experiment IDs - if ((!resetExperimentIds || is_rollback) && readTrainInfoSuccess) { - trainInfo->experimentIds = trainInfoOnDisk.experimentIds; - } - - if (!trainInfo->experimentIds.empty()) { - int64_t firstId = trainInfo->experimentIds.at(0); - auto& ids = trainInfo->experimentIds; - switch (trainInfo->status) { - case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS: - if (find(ids.begin(), ids.end(), firstId + 1) == ids.end()) { - ids.push_back(firstId + 1); - } - break; - case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED: - if (find(ids.begin(), ids.end(), firstId + 2) == ids.end()) { - ids.push_back(firstId + 2); - } - break; - case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS: - if (find(ids.begin(), ids.end(), firstId + 3) == ids.end()) { - ids.push_back(firstId + 3); - } - break; - } - } - - // If this event is a rollback event, the following fields are invalid and - // need to be replaced by the fields stored to disk. - if (is_rollback) { - trainInfo->requiresStaging = trainInfoOnDisk.requiresStaging; - trainInfo->rollbackEnabled = trainInfoOnDisk.rollbackEnabled; - trainInfo->requiresLowLatencyMonitor = trainInfoOnDisk.requiresLowLatencyMonitor; - } - - StorageManager::writeTrainInfo(*trainInfo); -} - -void StatsLogProcessor::onWatchdogRollbackOccurredLocked(LogEvent* event) { - pid_t pid = event->GetPid(); - uid_t uid = event->GetUid(); - if (!checkPermissionForIds(kPermissionDump, pid, uid) || - !checkPermissionForIds(kPermissionUsage, pid, uid)) { - return; - } - // The Get* functions don't modify the status on success, they only write in - // failure statuses, so we can use one status variable for all calls then - // check if it is no longer NO_ERROR. - status_t err = NO_ERROR; - int32_t rollbackType = int32_t(event->GetInt(1 /*rollback type field id*/, &err)); - string packageName = string(event->GetString(2 /*package name field id*/, &err)); - - if (err != NO_ERROR) { - ALOGE("Failed to parse fields in watchdog rollback occurred log event"); - return; - } - - vector experimentIds = - processWatchdogRollbackOccurred(rollbackType, packageName); - vector experimentIdProto; - writeExperimentIdsToProto(experimentIds, &experimentIdProto); - - event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STORAGE); -} - -vector StatsLogProcessor::processWatchdogRollbackOccurred(const int32_t rollbackTypeIn, - const string& packageNameIn) { - // If the package name is empty, we can't attribute it to any train, so - // return early. - if (packageNameIn.empty()) { - return vector(); - } - bool readTrainInfoSuccess = false; - InstallTrainInfo trainInfoOnDisk; - // We use the package name of the event as the train name. - readTrainInfoSuccess = StorageManager::readTrainInfo(packageNameIn, trainInfoOnDisk); - - if (!readTrainInfoSuccess) { - return vector(); - } - - if (trainInfoOnDisk.experimentIds.empty()) { - return vector(); - } - - int64_t firstId = trainInfoOnDisk.experimentIds[0]; - auto& ids = trainInfoOnDisk.experimentIds; - switch (rollbackTypeIn) { - case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE: - if (find(ids.begin(), ids.end(), firstId + 4) == ids.end()) { - ids.push_back(firstId + 4); - } - StorageManager::writeTrainInfo(trainInfoOnDisk); - break; - case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS: - if (find(ids.begin(), ids.end(), firstId + 5) == ids.end()) { - ids.push_back(firstId + 5); - } - StorageManager::writeTrainInfo(trainInfoOnDisk); - break; - } - - return trainInfoOnDisk.experimentIds; -} - -void StatsLogProcessor::resetConfigs() { - std::lock_guard lock(mMetricsMutex); - resetConfigsLocked(getElapsedRealtimeNs()); -} - -void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs) { - std::vector configKeys; - for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) { - configKeys.push_back(it->first); - } - resetConfigsLocked(timestampNs, configKeys); -} - -void StatsLogProcessor::OnLogEvent(LogEvent* event) { - OnLogEvent(event, getElapsedRealtimeNs()); -} - -void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { - std::lock_guard lock(mMetricsMutex); - - // Tell StatsdStats about new event - const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs(); - int atomId = event->GetTagId(); - StatsdStats::getInstance().noteAtomLogged(atomId, eventElapsedTimeNs / NS_PER_SEC); - if (!event->isValid()) { - StatsdStats::getInstance().noteAtomError(atomId); - return; - } - - // Hard-coded logic to update train info on disk and fill in any information - // this log event may be missing. - if (atomId == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) { - onBinaryPushStateChangedEventLocked(event); - } - - // Hard-coded logic to update experiment ids on disk for certain rollback - // types and fill the rollback atom with experiment ids - if (atomId == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) { - onWatchdogRollbackOccurredLocked(event); - } - -#ifdef VERY_VERBOSE_PRINTING - if (mPrintAllLogs) { - ALOGI("%s", event->ToString().c_str()); - } -#endif - resetIfConfigTtlExpiredLocked(eventElapsedTimeNs); - - // Hard-coded logic to update the isolated uid's in the uid-map. - // The field numbers need to be currently updated by hand with atoms.proto - if (atomId == android::os::statsd::util::ISOLATED_UID_CHANGED) { - onIsolatedUidChangedEventLocked(*event); - } else { - // Map the isolated uid to host uid if necessary. - mapIsolatedUidToHostUidIfNecessaryLocked(event); - } - - StateManager::getInstance().onLogEvent(*event); - - if (mMetricsManagers.empty()) { - return; - } - - bool fireAlarm = false; - { - std::lock_guard anomalyLock(mAnomalyAlarmMutex); - if (mNextAnomalyAlarmTime != 0 && - MillisToNano(mNextAnomalyAlarmTime) <= elapsedRealtimeNs) { - mNextAnomalyAlarmTime = 0; - VLOG("informing anomaly alarm at time %lld", (long long)elapsedRealtimeNs); - fireAlarm = true; - } - } - if (fireAlarm) { - informAnomalyAlarmFiredLocked(NanoToMillis(elapsedRealtimeNs)); - } - - int64_t curTimeSec = getElapsedRealtimeSec(); - if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) { - mPullerManager->ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC); - mLastPullerCacheClearTimeSec = curTimeSec; - } - - std::unordered_set uidsWithActiveConfigsChanged; - std::unordered_map> activeConfigsPerUid; - // pass the event to metrics managers. - for (auto& pair : mMetricsManagers) { - int uid = pair.first.GetUid(); - int64_t configId = pair.first.GetId(); - bool isPrevActive = pair.second->isActive(); - pair.second->onLogEvent(*event); - bool isCurActive = pair.second->isActive(); - // Map all active configs by uid. - if (isCurActive) { - auto activeConfigs = activeConfigsPerUid.find(uid); - if (activeConfigs != activeConfigsPerUid.end()) { - activeConfigs->second.push_back(configId); - } else { - vector newActiveConfigs; - newActiveConfigs.push_back(configId); - activeConfigsPerUid[uid] = newActiveConfigs; - } - } - // The activation state of this config changed. - if (isPrevActive != isCurActive) { - VLOG("Active status changed for uid %d", uid); - uidsWithActiveConfigsChanged.insert(uid); - StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive); - } - flushIfNecessaryLocked(pair.first, *(pair.second)); - } - - // Don't use the event timestamp for the guardrail. - for (int uid : uidsWithActiveConfigsChanged) { - // Send broadcast so that receivers can pull data. - auto lastBroadcastTime = mLastActivationBroadcastTimes.find(uid); - if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) { - if (elapsedRealtimeNs - lastBroadcastTime->second < - StatsdStats::kMinActivationBroadcastPeriodNs) { - StatsdStats::getInstance().noteActivationBroadcastGuardrailHit(uid); - VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us."); - return; - } - } - auto activeConfigs = activeConfigsPerUid.find(uid); - if (activeConfigs != activeConfigsPerUid.end()) { - if (mSendActivationBroadcast(uid, activeConfigs->second)) { - VLOG("StatsD sent activation notice for uid %d", uid); - mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs; - } - } else { - std::vector emptyActiveConfigs; - if (mSendActivationBroadcast(uid, emptyActiveConfigs)) { - VLOG("StatsD sent EMPTY activation notice for uid %d", uid); - mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs; - } - } - } -} - -void StatsLogProcessor::GetActiveConfigs(const int uid, vector& outActiveConfigs) { - std::lock_guard lock(mMetricsMutex); - GetActiveConfigsLocked(uid, outActiveConfigs); -} - -void StatsLogProcessor::GetActiveConfigsLocked(const int uid, vector& outActiveConfigs) { - outActiveConfigs.clear(); - for (auto& pair : mMetricsManagers) { - if (pair.first.GetUid() == uid && pair.second->isActive()) { - outActiveConfigs.push_back(pair.first.GetId()); - } - } -} - -void StatsLogProcessor::OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config) { - std::lock_guard lock(mMetricsMutex); - WriteDataToDiskLocked(key, timestampNs, CONFIG_UPDATED, NO_TIME_CONSTRAINTS); - OnConfigUpdatedLocked(timestampNs, key, config); -} - -void StatsLogProcessor::OnConfigUpdatedLocked( - const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config) { - VLOG("Updated configuration for key %s", key.ToString().c_str()); - sp newMetricsManager = - new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager, - mAnomalyAlarmMonitor, mPeriodicAlarmMonitor); - if (newMetricsManager->isConfigValid()) { - newMetricsManager->init(); - mUidMap->OnConfigUpdated(key); - newMetricsManager->refreshTtl(timestampNs); - mMetricsManagers[key] = newMetricsManager; - VLOG("StatsdConfig valid"); - } else { - // If there is any error in the config, don't use it. - // Remove any existing config with the same key. - ALOGE("StatsdConfig NOT valid"); - mMetricsManagers.erase(key); - } -} - -size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const { - std::lock_guard lock(mMetricsMutex); - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - ALOGW("Config source %s does not exist", key.ToString().c_str()); - return 0; - } - return it->second->byteSize(); -} - -void StatsLogProcessor::dumpStates(int out, bool verbose) { - std::lock_guard lock(mMetricsMutex); - FILE* fout = fdopen(out, "w"); - if (fout == NULL) { - return; - } - fprintf(fout, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size()); - for (auto metricsManager : mMetricsManagers) { - metricsManager.second->dumpStates(fout, verbose); - } - - fclose(fout); -} - -/* - * onDumpReport dumps serialized ConfigMetricsReportList into proto. - */ -void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency, - ProtoOutputStream* proto) { - std::lock_guard lock(mMetricsMutex); - - // Start of ConfigKey. - uint64_t configKeyToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid()); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId()); - proto->end(configKeyToken); - // End of ConfigKey. - - bool keepFile = false; - auto it = mMetricsManagers.find(key); - if (it != mMetricsManagers.end() && it->second->shouldPersistLocalHistory()) { - keepFile = true; - } - - // Then, check stats-data directory to see there's any file containing - // ConfigMetricsReport from previous shutdowns to concatenate to reports. - StorageManager::appendConfigMetricsReport( - key, proto, erase_data && !keepFile /* should remove file after appending it */, - dumpReportReason == ADB_DUMP /*if caller is adb*/); - - if (it != mMetricsManagers.end()) { - // This allows another broadcast to be sent within the rate-limit period if we get close to - // filling the buffer again soon. - mLastBroadcastTimes.erase(key); - - vector buffer; - onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket, - erase_data, dumpReportReason, dumpLatency, - false /* is this data going to be saved on disk */, &buffer); - proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS, - reinterpret_cast(buffer.data()), buffer.size()); - } else { - ALOGW("Config source %s does not exist", key.ToString().c_str()); - } -} - -/* - * onDumpReport dumps serialized ConfigMetricsReportList into outData. - */ -void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency, - vector* outData) { - ProtoOutputStream proto; - onDumpReport(key, dumpTimeStampNs, include_current_partial_bucket, erase_data, - dumpReportReason, dumpLatency, &proto); - - if (outData != nullptr) { - flushProtoToBuffer(proto, outData); - VLOG("output data size %zu", outData->size()); - } - - StatsdStats::getInstance().noteMetricsReportSent(key, proto.size()); -} - -/* - * onConfigMetricsReportLocked dumps serialized ConfigMetricsReport into outData. - */ -void StatsLogProcessor::onConfigMetricsReportLocked( - const ConfigKey& key, const int64_t dumpTimeStampNs, - const bool include_current_partial_bucket, const bool erase_data, - const DumpReportReason dumpReportReason, const DumpLatency dumpLatency, - const bool dataSavedOnDisk, vector* buffer) { - // We already checked whether key exists in mMetricsManagers in - // WriteDataToDisk. - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - return; - } - int64_t lastReportTimeNs = it->second->getLastReportTimeNs(); - int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs(); - - std::set str_set; - - ProtoOutputStream tempProto; - // First, fill in ConfigMetricsReport using current data on memory, which - // starts from filling in StatsLogReport's. - it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data, - dumpLatency, &str_set, &tempProto); - - // Fill in UidMap if there is at least one metric to report. - // This skips the uid map if it's an empty config. - if (it->second->getNumMetrics() > 0) { - uint64_t uidMapToken = tempProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP); - mUidMap->appendUidMap( - dumpTimeStampNs, key, it->second->hashStringInReport() ? &str_set : nullptr, - it->second->versionStringsInReport(), it->second->installerInReport(), &tempProto); - tempProto.end(uidMapToken); - } - - // Fill in the timestamps. - tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS, - (long long)lastReportTimeNs); - tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS, - (long long)dumpTimeStampNs); - tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS, - (long long)lastReportWallClockNs); - tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS, - (long long)getWallClockNs()); - // Dump report reason - tempProto.write(FIELD_TYPE_INT32 | FIELD_ID_DUMP_REPORT_REASON, dumpReportReason); - - for (const auto& str : str_set) { - tempProto.write(FIELD_TYPE_STRING | FIELD_COUNT_REPEATED | FIELD_ID_STRINGS, str); - } - - flushProtoToBuffer(tempProto, buffer); - - // save buffer to disk if needed - if (erase_data && !dataSavedOnDisk && it->second->shouldPersistLocalHistory()) { - VLOG("save history to disk"); - string file_name = StorageManager::getDataHistoryFileName((long)getWallClockSec(), - key.GetUid(), key.GetId()); - StorageManager::writeFile(file_name.c_str(), buffer->data(), buffer->size()); - } -} - -void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs, - const std::vector& configs) { - for (const auto& key : configs) { - StatsdConfig config; - if (StorageManager::readConfigFromDisk(key, &config)) { - OnConfigUpdatedLocked(timestampNs, key, config); - StatsdStats::getInstance().noteConfigReset(key); - } else { - ALOGE("Failed to read backup config from disk for : %s", key.ToString().c_str()); - auto it = mMetricsManagers.find(key); - if (it != mMetricsManagers.end()) { - it->second->refreshTtl(timestampNs); - } - } - } -} - -void StatsLogProcessor::resetIfConfigTtlExpiredLocked(const int64_t timestampNs) { - std::vector configKeysTtlExpired; - for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) { - if (it->second != nullptr && !it->second->isInTtl(timestampNs)) { - configKeysTtlExpired.push_back(it->first); - } - } - if (configKeysTtlExpired.size() > 0) { - WriteDataToDiskLocked(CONFIG_RESET, NO_TIME_CONSTRAINTS); - resetConfigsLocked(timestampNs, configKeysTtlExpired); - } -} - -void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { - std::lock_guard lock(mMetricsMutex); - auto it = mMetricsManagers.find(key); - if (it != mMetricsManagers.end()) { - WriteDataToDiskLocked(key, getElapsedRealtimeNs(), CONFIG_REMOVED, - NO_TIME_CONSTRAINTS); - mMetricsManagers.erase(it); - mUidMap->OnConfigRemoved(key); - } - StatsdStats::getInstance().noteConfigRemoved(key); - - mLastBroadcastTimes.erase(key); - - int uid = key.GetUid(); - bool lastConfigForUid = true; - for (auto it : mMetricsManagers) { - if (it.first.GetUid() == uid) { - lastConfigForUid = false; - break; - } - } - if (lastConfigForUid) { - mLastActivationBroadcastTimes.erase(uid); - } - - if (mMetricsManagers.empty()) { - mPullerManager->ForceClearPullerCache(); - } -} - -void StatsLogProcessor::flushIfNecessaryLocked(const ConfigKey& key, - MetricsManager& metricsManager) { - int64_t elapsedRealtimeNs = getElapsedRealtimeNs(); - auto lastCheckTime = mLastByteSizeTimes.find(key); - if (lastCheckTime != mLastByteSizeTimes.end()) { - if (elapsedRealtimeNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) { - return; - } - } - - // We suspect that the byteSize() computation is expensive, so we set a rate limit. - size_t totalBytes = metricsManager.byteSize(); - mLastByteSizeTimes[key] = elapsedRealtimeNs; - bool requestDump = false; - if (totalBytes > StatsdStats::kMaxMetricsBytesPerConfig) { - // Too late. We need to start clearing data. - metricsManager.dropData(elapsedRealtimeNs); - StatsdStats::getInstance().noteDataDropped(key, totalBytes); - VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str()); - } else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) || - (mOnDiskDataConfigs.find(key) != mOnDiskDataConfigs.end())) { - // Request to send a broadcast if: - // 1. in memory data > threshold OR - // 2. config has old data report on disk. - requestDump = true; - } - - if (requestDump) { - // Send broadcast so that receivers can pull data. - auto lastBroadcastTime = mLastBroadcastTimes.find(key); - if (lastBroadcastTime != mLastBroadcastTimes.end()) { - if (elapsedRealtimeNs - lastBroadcastTime->second < - StatsdStats::kMinBroadcastPeriodNs) { - VLOG("StatsD would've sent a broadcast but the rate limit stopped us."); - return; - } - } - if (mSendBroadcast(key)) { - mOnDiskDataConfigs.erase(key); - VLOG("StatsD triggered data fetch for %s", key.ToString().c_str()); - mLastBroadcastTimes[key] = elapsedRealtimeNs; - StatsdStats::getInstance().noteBroadcastSent(key); - } - } -} - -void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key, - const int64_t timestampNs, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency) { - if (mMetricsManagers.find(key) == mMetricsManagers.end() || - !mMetricsManagers.find(key)->second->shouldWriteToDisk()) { - return; - } - vector buffer; - onConfigMetricsReportLocked(key, timestampNs, true /* include_current_partial_bucket*/, - true /* erase_data */, dumpReportReason, dumpLatency, true, - &buffer); - string file_name = - StorageManager::getDataFileName((long)getWallClockSec(), key.GetUid(), key.GetId()); - StorageManager::writeFile(file_name.c_str(), buffer.data(), buffer.size()); - - // We were able to write the ConfigMetricsReport to disk, so we should trigger collection ASAP. - mOnDiskDataConfigs.insert(key); -} - -void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) { - std::lock_guard lock(mMetricsMutex); - const int64_t timeNs = getElapsedRealtimeNs(); - // Do not write to disk if we already have in the last few seconds. - if (static_cast (timeNs) < - mLastActiveMetricsWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) { - ALOGI("Statsd skipping writing active metrics to disk. Already wrote data in last %d seconds", - WRITE_DATA_COOL_DOWN_SEC); - return; - } - mLastActiveMetricsWriteNs = timeNs; - - ProtoOutputStream proto; - WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, DEVICE_SHUTDOWN, &proto); - - string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); - StorageManager::deleteFile(file_name.c_str()); - android::base::unique_fd fd( - open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)); - if (fd == -1) { - ALOGE("Attempt to write %s but failed", file_name.c_str()); - return; - } - proto.flush(fd.get()); -} - -void StatsLogProcessor::SaveMetadataToDisk(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - std::lock_guard lock(mMetricsMutex); - // Do not write to disk if we already have in the last few seconds. - if (static_cast (systemElapsedTimeNs) < - mLastMetadataWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) { - ALOGI("Statsd skipping writing metadata to disk. Already wrote data in last %d seconds", - WRITE_DATA_COOL_DOWN_SEC); - return; - } - mLastMetadataWriteNs = systemElapsedTimeNs; - - metadata::StatsMetadataList metadataList; - WriteMetadataToProtoLocked( - currentWallClockTimeNs, systemElapsedTimeNs, &metadataList); - - string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR); - StorageManager::deleteFile(file_name.c_str()); - - if (metadataList.stats_metadata_size() == 0) { - // Skip the write if we have nothing to write. - return; - } - - std::string data; - metadataList.SerializeToString(&data); - StorageManager::writeFile(file_name.c_str(), data.c_str(), data.size()); -} - -void StatsLogProcessor::WriteMetadataToProto(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadataList* metadataList) { - std::lock_guard lock(mMetricsMutex); - WriteMetadataToProtoLocked(currentWallClockTimeNs, systemElapsedTimeNs, metadataList); -} - -void StatsLogProcessor::WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadataList* metadataList) { - for (const auto& pair : mMetricsManagers) { - const sp& metricsManager = pair.second; - metadata::StatsMetadata* statsMetadata = metadataList->add_stats_metadata(); - bool metadataWritten = metricsManager->writeMetadataToProto(currentWallClockTimeNs, - systemElapsedTimeNs, statsMetadata); - if (!metadataWritten) { - metadataList->mutable_stats_metadata()->RemoveLast(); - } - } -} - -void StatsLogProcessor::LoadMetadataFromDisk(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - std::lock_guard lock(mMetricsMutex); - string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR); - int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); - if (-1 == fd) { - VLOG("Attempt to read %s but failed", file_name.c_str()); - StorageManager::deleteFile(file_name.c_str()); - return; - } - string content; - if (!android::base::ReadFdToString(fd, &content)) { - ALOGE("Attempt to read %s but failed", file_name.c_str()); - close(fd); - StorageManager::deleteFile(file_name.c_str()); - return; - } - - close(fd); - - metadata::StatsMetadataList statsMetadataList; - if (!statsMetadataList.ParseFromString(content)) { - ALOGE("Attempt to read %s but failed; failed to metadata", file_name.c_str()); - StorageManager::deleteFile(file_name.c_str()); - return; - } - SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs); - StorageManager::deleteFile(file_name.c_str()); -} - -void StatsLogProcessor::SetMetadataState(const metadata::StatsMetadataList& statsMetadataList, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - std::lock_guard lock(mMetricsMutex); - SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs); -} - -void StatsLogProcessor::SetMetadataStateLocked( - const metadata::StatsMetadataList& statsMetadataList, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - for (const metadata::StatsMetadata& metadata : statsMetadataList.stats_metadata()) { - ConfigKey key(metadata.config_key().uid(), metadata.config_key().config_id()); - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - ALOGE("No config found for configKey %s", key.ToString().c_str()); - continue; - } - VLOG("Setting metadata %s", key.ToString().c_str()); - it->second->loadMetadata(metadata, currentWallClockTimeNs, systemElapsedTimeNs); - } - VLOG("Successfully loaded %d metadata.", statsMetadataList.stats_metadata_size()); -} - -void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { - std::lock_guard lock(mMetricsMutex); - WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, reason, proto); -} - -void StatsLogProcessor::WriteActiveConfigsToProtoOutputStreamLocked( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { - for (const auto& pair : mMetricsManagers) { - const sp& metricsManager = pair.second; - uint64_t configToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG); - metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, reason, proto); - proto->end(configToken); - } -} -void StatsLogProcessor::LoadActiveConfigsFromDisk() { - std::lock_guard lock(mMetricsMutex); - string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); - int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); - if (-1 == fd) { - VLOG("Attempt to read %s but failed", file_name.c_str()); - StorageManager::deleteFile(file_name.c_str()); - return; - } - string content; - if (!android::base::ReadFdToString(fd, &content)) { - ALOGE("Attempt to read %s but failed", file_name.c_str()); - close(fd); - StorageManager::deleteFile(file_name.c_str()); - return; - } - - close(fd); - - ActiveConfigList activeConfigList; - if (!activeConfigList.ParseFromString(content)) { - ALOGE("Attempt to read %s but failed; failed to load active configs", file_name.c_str()); - StorageManager::deleteFile(file_name.c_str()); - return; - } - // Passing in mTimeBaseNs only works as long as we only load from disk is when statsd starts. - SetConfigsActiveStateLocked(activeConfigList, mTimeBaseNs); - StorageManager::deleteFile(file_name.c_str()); -} - -void StatsLogProcessor::SetConfigsActiveState(const ActiveConfigList& activeConfigList, - int64_t currentTimeNs) { - std::lock_guard lock(mMetricsMutex); - SetConfigsActiveStateLocked(activeConfigList, currentTimeNs); -} - -void StatsLogProcessor::SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList, - int64_t currentTimeNs) { - for (int i = 0; i < activeConfigList.config_size(); i++) { - const auto& config = activeConfigList.config(i); - ConfigKey key(config.uid(), config.id()); - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - ALOGE("No config found for config %s", key.ToString().c_str()); - continue; - } - VLOG("Setting active config %s", key.ToString().c_str()); - it->second->loadActiveConfig(config, currentTimeNs); - } - VLOG("Successfully loaded %d active configs.", activeConfigList.config_size()); -} - -void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency) { - const int64_t timeNs = getElapsedRealtimeNs(); - // Do not write to disk if we already have in the last few seconds. - // This is to avoid overwriting files that would have the same name if we - // write twice in the same second. - if (static_cast (timeNs) < - mLastWriteTimeNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) { - ALOGI("Statsd skipping writing data to disk. Already wrote data in last %d seconds", - WRITE_DATA_COOL_DOWN_SEC); - return; - } - mLastWriteTimeNs = timeNs; - for (auto& pair : mMetricsManagers) { - WriteDataToDiskLocked(pair.first, timeNs, dumpReportReason, dumpLatency); - } -} - -void StatsLogProcessor::WriteDataToDisk(const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency) { - std::lock_guard lock(mMetricsMutex); - WriteDataToDiskLocked(dumpReportReason, dumpLatency); -} - -void StatsLogProcessor::informPullAlarmFired(const int64_t timestampNs) { - std::lock_guard lock(mMetricsMutex); - mPullerManager->OnAlarmFired(timestampNs); -} - -int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) { - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - return 0; - } else { - return it->second->getLastReportTimeNs(); - } -} - -void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, - const int uid, const int64_t version) { - std::lock_guard lock(mMetricsMutex); - VLOG("Received app upgrade"); - StateManager::getInstance().notifyAppChanged(apk, mUidMap); - for (const auto& it : mMetricsManagers) { - it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version); - } -} - -void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, - const int uid) { - std::lock_guard lock(mMetricsMutex); - VLOG("Received app removed"); - StateManager::getInstance().notifyAppChanged(apk, mUidMap); - for (const auto& it : mMetricsManagers) { - it.second->notifyAppRemoved(eventTimeNs, apk, uid); - } -} - -void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) { - std::lock_guard lock(mMetricsMutex); - VLOG("Received uid map"); - StateManager::getInstance().updateLogSources(mUidMap); - for (const auto& it : mMetricsManagers) { - it.second->onUidMapReceived(eventTimeNs); - } -} - -void StatsLogProcessor::onStatsdInitCompleted(const int64_t& elapsedTimeNs) { - std::lock_guard lock(mMetricsMutex); - VLOG("Received boot completed signal"); - for (const auto& it : mMetricsManagers) { - it.second->onStatsdInitCompleted(elapsedTimeNs); - } -} - -void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) { - std::lock_guard lock(mMetricsMutex); - mOnDiskDataConfigs.insert(key); -} - -void StatsLogProcessor::setAnomalyAlarm(const int64_t elapsedTimeMillis) { - std::lock_guard lock(mAnomalyAlarmMutex); - mNextAnomalyAlarmTime = elapsedTimeMillis; -} - -void StatsLogProcessor::cancelAnomalyAlarm() { - std::lock_guard lock(mAnomalyAlarmMutex); - mNextAnomalyAlarmTime = 0; -} - -void StatsLogProcessor::informAnomalyAlarmFiredLocked(const int64_t elapsedTimeMillis) { - VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called"); - std::unordered_set, SpHash> alarmSet = - mAnomalyAlarmMonitor->popSoonerThan(static_cast(elapsedTimeMillis / 1000)); - if (alarmSet.size() > 0) { - VLOG("Found periodic alarm fired."); - processFiredAnomalyAlarmsLocked(MillisToNano(elapsedTimeMillis), alarmSet); - } else { - ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled."); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h deleted file mode 100644 index 08f46688bbba..000000000000 --- a/cmds/statsd/src/StatsLogProcessor.h +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "config/ConfigListener.h" -#include "logd/LogEvent.h" -#include "metrics/MetricsManager.h" -#include "packages/UidMap.h" -#include "external/StatsPullerManager.h" - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" - -#include -#include - -namespace android { -namespace os { -namespace statsd { - - -class StatsLogProcessor : public ConfigListener, public virtual PackageInfoListener { -public: - StatsLogProcessor(const sp& uidMap, const sp& pullerManager, - const sp& anomalyAlarmMonitor, - const sp& subscriberTriggerAlarmMonitor, - const int64_t timeBaseNs, - const std::function& sendBroadcast, - const std::function&)>& sendActivationBroadcast); - virtual ~StatsLogProcessor(); - - void OnLogEvent(LogEvent* event); - - void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config); - void OnConfigRemoved(const ConfigKey& key); - - size_t GetMetricsSize(const ConfigKey& key) const; - - void GetActiveConfigs(const int uid, vector& outActiveConfigs); - - void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, - const bool include_current_partial_bucket, const bool erase_data, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency, - vector* outData); - void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, - const bool include_current_partial_bucket, const bool erase_data, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency, - ProtoOutputStream* proto); - - /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */ - void onPeriodicAlarmFired( - const int64_t& timestampNs, - unordered_set, SpHash> alarmSet); - - /* Flushes data to disk. Data on memory will be gone after written to disk. */ - void WriteDataToDisk(const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency); - - /* Persist configs containing metrics with active activations to disk. */ - void SaveActiveConfigsToDisk(int64_t currentTimeNs); - - /* Writes the current active status/ttl for all configs and metrics to ProtoOutputStream. */ - void WriteActiveConfigsToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); - - /* Load configs containing metrics with active activations from disk. */ - void LoadActiveConfigsFromDisk(); - - /* Persist metadata for configs and metrics to disk. */ - void SaveMetadataToDisk(int64_t currentWallClockTimeNs, int64_t systemElapsedTimeNs); - - /* Writes the statsd metadata for all configs and metrics to StatsMetadataList. */ - void WriteMetadataToProto(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadataList* metadataList); - - /* Load stats metadata for configs and metrics from disk. */ - void LoadMetadataFromDisk(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs); - - /* Sets the metadata for all configs and metrics */ - void SetMetadataState(const metadata::StatsMetadataList& statsMetadataList, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs); - - /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */ - void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs); - - /* Notify all MetricsManagers of app upgrades */ - void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version) override; - - /* Notify all MetricsManagers of app removals */ - void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override; - - /* Notify all MetricsManagers of uid map snapshots received */ - void onUidMapReceived(const int64_t& eventTimeNs) override; - - /* Notify all metrics managers of boot completed - * This will force a bucket split when the boot is finished. - */ - void onStatsdInitCompleted(const int64_t& elapsedTimeNs); - - // Reset all configs. - void resetConfigs(); - - inline sp getUidMap() { - return mUidMap; - } - - void dumpStates(int outFd, bool verbose); - - void informPullAlarmFired(const int64_t timestampNs); - - int64_t getLastReportTimeNs(const ConfigKey& key); - - inline void setPrintLogs(bool enabled) { -#ifdef VERY_VERBOSE_PRINTING - std::lock_guard lock(mMetricsMutex); - mPrintAllLogs = enabled; -#endif - } - - // Add a specific config key to the possible configs to dump ASAP. - void noteOnDiskData(const ConfigKey& key); - - void setAnomalyAlarm(const int64_t timeMillis); - - void cancelAnomalyAlarm(); - -private: - // For testing only. - inline sp getAnomalyAlarmMonitor() const { - return mAnomalyAlarmMonitor; - } - - inline sp getPeriodicAlarmMonitor() const { - return mPeriodicAlarmMonitor; - } - - mutable mutex mMetricsMutex; - - // Guards mNextAnomalyAlarmTime. A separate mutex is needed because alarms are set/cancelled - // in the onLogEvent code path, which is locked by mMetricsMutex. - // DO NOT acquire mMetricsMutex while holding mAnomalyAlarmMutex. This can lead to a deadlock. - mutable mutex mAnomalyAlarmMutex; - - std::unordered_map> mMetricsManagers; - - std::unordered_map mLastBroadcastTimes; - - // Last time we sent a broadcast to this uid that the active configs had changed. - std::unordered_map mLastActivationBroadcastTimes; - - // Tracks when we last checked the bytes consumed for each config key. - std::unordered_map mLastByteSizeTimes; - - // Tracks which config keys has metric reports on disk - std::set mOnDiskDataConfigs; - - sp mUidMap; // Reference to the UidMap to lookup app name and version for each uid. - - sp mPullerManager; // Reference to StatsPullerManager - - sp mAnomalyAlarmMonitor; - - sp mPeriodicAlarmMonitor; - - void OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs); - - void resetIfConfigTtlExpiredLocked(const int64_t timestampNs); - - void OnConfigUpdatedLocked( - const int64_t currentTimestampNs, const ConfigKey& key, const StatsdConfig& config); - - void GetActiveConfigsLocked(const int uid, vector& outActiveConfigs); - - void WriteActiveConfigsToProtoOutputStreamLocked( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); - - void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList, - int64_t currentTimeNs); - - void SetMetadataStateLocked(const metadata::StatsMetadataList& statsMetadataList, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs); - - void WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadataList* metadataList); - - void WriteDataToDiskLocked(const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency); - - void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs, - const DumpReportReason dumpReportReason, - const DumpLatency dumpLatency); - - void onConfigMetricsReportLocked( - const ConfigKey& key, const int64_t dumpTimeStampNs, - const bool include_current_partial_bucket, const bool erase_data, - const DumpReportReason dumpReportReason, const DumpLatency dumpLatency, - /*if dataSavedToDisk is true, it indicates the caller will write the data to disk - (e.g., before reboot). So no need to further persist local history.*/ - const bool dataSavedToDisk, vector* proto); - - /* Check if we should send a broadcast if approaching memory limits and if we're over, we - * actually delete the data. */ - void flushIfNecessaryLocked(const ConfigKey& key, MetricsManager& metricsManager); - - // Maps the isolated uid in the log event to host uid if the log event contains uid fields. - void mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const; - - // Handler over the isolated uid change event. - void onIsolatedUidChangedEventLocked(const LogEvent& event); - - // Handler over the binary push state changed event. - void onBinaryPushStateChangedEventLocked(LogEvent* event); - - // Handler over the watchdog rollback occurred event. - void onWatchdogRollbackOccurredLocked(LogEvent* event); - - // Updates train info on disk based on binary push state changed info and - // write disk info into parameters. - void getAndUpdateTrainInfoOnDisk(bool is_rollback, InstallTrainInfo* trainInfoIn); - - // Gets experiment ids on disk for associated train and updates them - // depending on rollback type. Then writes them back to disk and returns - // them. - std::vector processWatchdogRollbackOccurred(const int32_t rollbackTypeIn, - const string& packageName); - - // Reset all configs. - void resetConfigsLocked(const int64_t timestampNs); - // Reset the specified configs. - void resetConfigsLocked(const int64_t timestampNs, const std::vector& configs); - - // An anomaly alarm should have fired. - // Check with anomaly alarm manager to find the alarms and process the result. - void informAnomalyAlarmFiredLocked(const int64_t elapsedTimeMillis); - - /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */ - void processFiredAnomalyAlarmsLocked( - const int64_t& timestampNs, - unordered_set, SpHash> alarmSet); - - // Function used to send a broadcast so that receiver for the config key can call getData - // to retrieve the stored data. - std::function mSendBroadcast; - - // Function used to send a broadcast so that receiver can be notified of which configs - // are currently active. - std::function& configIds)> mSendActivationBroadcast; - - const int64_t mTimeBaseNs; - - // Largest timestamp of the events that we have processed. - int64_t mLargestTimestampSeen = 0; - - int64_t mLastTimestampSeen = 0; - - int64_t mLastPullerCacheClearTimeSec = 0; - - // Last time we wrote data to disk. - int64_t mLastWriteTimeNs = 0; - - // Last time we wrote active metrics to disk. - int64_t mLastActiveMetricsWriteNs = 0; - - //Last time we wrote metadata to disk. - int64_t mLastMetadataWriteNs = 0; - - // The time for the next anomaly alarm for alerts. - int64_t mNextAnomalyAlarmTime = 0; - -#ifdef VERY_VERBOSE_PRINTING - bool mPrintAllLogs = false; -#endif - - FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs); - FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize); - FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); - FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); - FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved); - FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); - FRIEND_TEST(StatsLogProcessorTest, - TestActivationOnBootMultipleActivationsDifferentActivationTypes); - FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); - - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3); - FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1); - FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2); - FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid); - FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain); - FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition); - FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents); - - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); - - FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); - FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); - - FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges); - FRIEND_TEST(CountMetricE2eTest, TestSlicedState); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); - FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields); - - FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); - FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); - FRIEND_TEST(DurationMetricE2eTest, TestWithCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped); - FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset); - - FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp deleted file mode 100644 index 368a6c40c746..000000000000 --- a/cmds/statsd/src/StatsService.cpp +++ /dev/null @@ -1,1310 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsService.h" -#include "stats_log_util.h" -#include "android-base/stringprintf.h" -#include "config/ConfigKey.h" -#include "config/ConfigManager.h" -#include "guardrail/StatsdStats.h" -#include "storage/StorageManager.h" -#include "subscriber/SubscriberReporter.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace android; - -using android::base::StringPrintf; -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_MESSAGE; - -using Status = ::ndk::ScopedAStatus; - -namespace android { -namespace os { -namespace statsd { - -constexpr const char* kPermissionDump = "android.permission.DUMP"; - -constexpr const char* kPermissionRegisterPullAtom = "android.permission.REGISTER_STATS_PULL_ATOM"; - -#define STATS_SERVICE_DIR "/data/misc/stats-service" - -// for StatsDataDumpProto -const int FIELD_ID_REPORTS_LIST = 1; - -static Status exception(int32_t code, const std::string& msg) { - ALOGE("%s (%d)", msg.c_str(), code); - return Status::fromExceptionCodeWithMessage(code, msg.c_str()); -} - -static bool checkPermission(const char* permission) { - pid_t pid = AIBinder_getCallingPid(); - uid_t uid = AIBinder_getCallingUid(); - return checkPermissionForIds(permission, pid, uid); -} - -Status checkUid(uid_t expectedUid) { - uid_t uid = AIBinder_getCallingUid(); - if (uid == expectedUid || uid == AID_ROOT) { - return Status::ok(); - } else { - return exception(EX_SECURITY, - StringPrintf("UID %d is not expected UID %d", uid, expectedUid)); - } -} - -#define ENFORCE_UID(uid) { \ - Status status = checkUid((uid)); \ - if (!status.isOk()) { \ - return status; \ - } \ -} - -StatsService::StatsService(const sp& handlerLooper, shared_ptr queue) - : mAnomalyAlarmMonitor(new AlarmMonitor( - MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, - [this](const shared_ptr& /*sc*/, int64_t timeMillis) { - mProcessor->setAnomalyAlarm(timeMillis); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - }, - [this](const shared_ptr& /*sc*/) { - mProcessor->cancelAnomalyAlarm(); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - })), - mPeriodicAlarmMonitor(new AlarmMonitor( - MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, - [](const shared_ptr& sc, int64_t timeMillis) { - if (sc != nullptr) { - sc->setAlarmForSubscriberTriggering(timeMillis); - StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); - } - }, - [](const shared_ptr& sc) { - if (sc != nullptr) { - sc->cancelAlarmForSubscriberTriggering(); - StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); - } - })), - mEventQueue(queue), - mBootCompleteTrigger({kBootCompleteTag, kUidMapReceivedTag, kAllPullersRegisteredTag}, - [this]() { mProcessor->onStatsdInitCompleted(getElapsedRealtimeNs()); }), - mStatsCompanionServiceDeathRecipient( - AIBinder_DeathRecipient_new(StatsService::statsCompanionServiceDied)) { - mUidMap = UidMap::getInstance(); - mPullerManager = new StatsPullerManager(); - StatsPuller::SetUidMap(mUidMap); - mConfigManager = new ConfigManager(); - mProcessor = new StatsLogProcessor( - mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor, - getElapsedRealtimeNs(), - [this](const ConfigKey& key) { - shared_ptr receiver = mConfigManager->GetConfigReceiver(key); - if (receiver == nullptr) { - VLOG("Could not find a broadcast receiver for %s", key.ToString().c_str()); - return false; - } else if (receiver->sendDataBroadcast( - mProcessor->getLastReportTimeNs(key)).isOk()) { - return true; - } else { - VLOG("Failed to send a broadcast for receiver %s", key.ToString().c_str()); - return false; - } - }, - [this](const int& uid, const vector& activeConfigs) { - shared_ptr receiver = - mConfigManager->GetActiveConfigsChangedReceiver(uid); - if (receiver == nullptr) { - VLOG("Could not find receiver for uid %d", uid); - return false; - } else if (receiver->sendActiveConfigsChangedBroadcast(activeConfigs).isOk()) { - VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid); - return true; - } else { - VLOG("StatsService::active configs broadcast failed for uid %d" , uid); - return false; - } - }); - - mUidMap->setListener(mProcessor); - mConfigManager->AddListener(mProcessor); - - init_system_properties(); - - if (mEventQueue != nullptr) { - std::thread pushedEventThread([this] { readLogs(); }); - pushedEventThread.detach(); - } -} - -StatsService::~StatsService() { -} - -/* Runs on a dedicated thread to process pushed events. */ -void StatsService::readLogs() { - // Read forever..... long live statsd - while (1) { - // Block until an event is available. - auto event = mEventQueue->waitPop(); - // Pass it to StatsLogProcess to all configs/metrics - // At this point, the LogEventQueue is not blocked, so that the socketListener - // can read events from the socket and write to buffer to avoid data drop. - mProcessor->OnLogEvent(event.get()); - // The ShellSubscriber is only used by shell for local debugging. - if (mShellSubscriber != nullptr) { - mShellSubscriber->onLogEvent(*event); - } - } -} - -void StatsService::init_system_properties() { - mEngBuild = false; - const prop_info* buildType = __system_property_find("ro.build.type"); - if (buildType != NULL) { - __system_property_read_callback(buildType, init_build_type_callback, this); - } -} - -void StatsService::init_build_type_callback(void* cookie, const char* /*name*/, const char* value, - uint32_t serial) { - if (0 == strcmp("eng", value) || 0 == strcmp("userdebug", value)) { - reinterpret_cast(cookie)->mEngBuild = true; - } -} - -/** - * Write data from statsd. - * Format for statsdStats: adb shell dumpsys stats --metadata [-v] [--proto] - * Format for data report: adb shell dumpsys stats [anything other than --metadata] [--proto] - * Anything ending in --proto will be in proto format. - * Anything without --metadata as the first argument will be report information. - * (bugreports call "adb shell dumpsys stats --dump-priority NORMAL -a --proto") - * TODO: Come up with a more robust method of enacting . - */ -status_t StatsService::dump(int fd, const char** args, uint32_t numArgs) { - if (!checkPermission(kPermissionDump)) { - return PERMISSION_DENIED; - } - - int lastArg = numArgs - 1; - bool asProto = false; - if (lastArg >= 0 && string(args[lastArg]) == "--proto") { // last argument - asProto = true; - lastArg--; - } - if (numArgs > 0 && string(args[0]) == "--metadata") { // first argument - // Request is to dump statsd stats. - bool verbose = false; - if (lastArg >= 0 && string(args[lastArg]) == "-v") { - verbose = true; - lastArg--; - } - dumpStatsdStats(fd, verbose, asProto); - } else { - // Request is to dump statsd report data. - if (asProto) { - dumpIncidentSection(fd); - } else { - dprintf(fd, "Non-proto format of stats data dump not available; see proto version.\n"); - } - } - - return NO_ERROR; -} - -/** - * Write debugging data about statsd in text or proto format. - */ -void StatsService::dumpStatsdStats(int out, bool verbose, bool proto) { - if (proto) { - vector data; - StatsdStats::getInstance().dumpStats(&data, false); // does not reset statsdStats. - for (size_t i = 0; i < data.size(); i ++) { - dprintf(out, "%c", data[i]); - } - } else { - StatsdStats::getInstance().dumpStats(out); - mProcessor->dumpStates(out, verbose); - } -} - -/** - * Write stats report data in StatsDataDumpProto incident section format. - */ -void StatsService::dumpIncidentSection(int out) { - ProtoOutputStream proto; - for (const ConfigKey& configKey : mConfigManager->GetAllConfigKeys()) { - uint64_t reportsListToken = - proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS_LIST); - // Don't include the current bucket to avoid skipping buckets. - // If we need to include the current bucket later, consider changing to NO_TIME_CONSTRAINTS - // or other alternatives to avoid skipping buckets for pulled metrics. - mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), - false /* includeCurrentBucket */, false /* erase_data */, - ADB_DUMP, - FAST, - &proto); - proto.end(reportsListToken); - proto.flush(out); - proto.clear(); - } -} - -/** - * Implementation of the adb shell cmd stats command. - */ -status_t StatsService::handleShellCommand(int in, int out, int err, const char** argv, - uint32_t argc) { - uid_t uid = AIBinder_getCallingUid(); - if (uid != AID_ROOT && uid != AID_SHELL) { - return PERMISSION_DENIED; - } - - Vector utf8Args; - utf8Args.setCapacity(argc); - for (uint32_t i = 0; i < argc; i++) { - utf8Args.push(String8(argv[i])); - } - - if (argc >= 1) { - // adb shell cmd stats config ... - if (!utf8Args[0].compare(String8("config"))) { - return cmd_config(in, out, err, utf8Args); - } - - if (!utf8Args[0].compare(String8("print-uid-map"))) { - return cmd_print_uid_map(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("dump-report"))) { - return cmd_dump_report(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("pull-source")) && argc > 1) { - return cmd_print_pulled_metrics(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("send-broadcast"))) { - return cmd_trigger_broadcast(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("print-stats"))) { - return cmd_print_stats(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("meminfo"))) { - return cmd_dump_memory_info(out); - } - - if (!utf8Args[0].compare(String8("write-to-disk"))) { - return cmd_write_data_to_disk(out); - } - - if (!utf8Args[0].compare(String8("log-app-breadcrumb"))) { - return cmd_log_app_breadcrumb(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("log-binary-push"))) { - return cmd_log_binary_push(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("clear-puller-cache"))) { - return cmd_clear_puller_cache(out); - } - - if (!utf8Args[0].compare(String8("print-logs"))) { - return cmd_print_logs(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("send-active-configs"))) { - return cmd_trigger_active_config_broadcast(out, utf8Args); - } - - if (!utf8Args[0].compare(String8("data-subscribe"))) { - { - std::lock_guard lock(mShellSubscriberMutex); - if (mShellSubscriber == nullptr) { - mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); - } - } - int timeoutSec = -1; - if (argc >= 2) { - timeoutSec = atoi(utf8Args[1].c_str()); - } - mShellSubscriber->startNewSubscription(in, out, timeoutSec); - return NO_ERROR; - } - } - - print_cmd_help(out); - return NO_ERROR; -} - -void StatsService::print_cmd_help(int out) { - dprintf(out, - "usage: adb shell cmd stats print-stats-log [tag_required] " - "[timestamp_nsec_optional]\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats meminfo\n"); - dprintf(out, "\n"); - dprintf(out, " Prints the malloc debug information. You need to run the following first: \n"); - dprintf(out, " # adb shell stop\n"); - dprintf(out, " # adb shell setprop libc.debug.malloc.program statsd \n"); - dprintf(out, " # adb shell setprop libc.debug.malloc.options backtrace \n"); - dprintf(out, " # adb shell start\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats print-uid-map [PKG]\n"); - dprintf(out, "\n"); - dprintf(out, " Prints the UID, app name, version mapping.\n"); - dprintf(out, " PKG Optional package name to print the uids of the package\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats pull-source ATOM_TAG [PACKAGE] \n"); - dprintf(out, "\n"); - dprintf(out, " Prints the output of a pulled atom\n"); - dprintf(out, " UID The atom to pull\n"); - dprintf(out, " PACKAGE The package to pull from. Default is AID_SYSTEM\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats write-to-disk \n"); - dprintf(out, "\n"); - dprintf(out, " Flushes all data on memory to disk.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n"); - dprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n"); - dprintf(out, " UID The uid to use. It is only possible to pass a UID\n"); - dprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); - dprintf(out, " uid is used.\n"); - dprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n"); - dprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, - "usage: adb shell cmd stats log-binary-push NAME VERSION STAGING ROLLBACK_ENABLED " - "LOW_LATENCY STATE EXPERIMENT_IDS\n"); - dprintf(out, " Log a binary push state changed event.\n"); - dprintf(out, " NAME The train name.\n"); - dprintf(out, " VERSION The train version code.\n"); - dprintf(out, " STAGING If this train requires a restart.\n"); - dprintf(out, " ROLLBACK_ENABLED If rollback should be enabled for this install.\n"); - dprintf(out, " LOW_LATENCY If the train requires low latency monitoring.\n"); - dprintf(out, " STATE The status of the train push.\n"); - dprintf(out, " Integer value of the enum in atoms.proto.\n"); - dprintf(out, " EXPERIMENT_IDS Comma separated list of experiment ids.\n"); - dprintf(out, " Leave blank for none.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n"); - dprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n"); - dprintf(out, "\n"); - dprintf(out, " Adds, updates or removes a configuration. The proto should be in\n"); - dprintf(out, " wire-encoded protobuf format and passed via stdin. If no UID and name is\n"); - dprintf(out, " provided, then all configs will be removed from memory and disk.\n"); - dprintf(out, "\n"); - dprintf(out, " UID The uid to use. It is only possible to pass the UID\n"); - dprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); - dprintf(out, " uid is used.\n"); - dprintf(out, " NAME The per-uid name to use\n"); - dprintf(out, "\n"); - dprintf(out, "\n *Note: If both UID and NAME are omitted then all configs will\n"); - dprintf(out, "\n be removed from memory and disk!\n"); - dprintf(out, "\n"); - dprintf(out, - "usage: adb shell cmd stats dump-report [UID] NAME [--keep_data] " - "[--include_current_bucket] [--proto]\n"); - dprintf(out, " Dump all metric data for a configuration.\n"); - dprintf(out, " UID The uid of the configuration. It is only possible to pass\n"); - dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); - dprintf(out, " calling uid is used.\n"); - dprintf(out, " NAME The name of the configuration\n"); - dprintf(out, " --keep_data Do NOT erase the data upon dumping it.\n"); - dprintf(out, " --proto Print proto binary.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n"); - dprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\n"); - dprintf(out, " UID The uid of the configuration. It is only possible to pass\n"); - dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); - dprintf(out, " calling uid is used.\n"); - dprintf(out, " NAME The name of the configuration\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, - "usage: adb shell cmd stats send-active-configs [--uid=UID] [--configs] " - "[NAME1] [NAME2] [NAME3..]\n"); - dprintf(out, " Send a broadcast that informs the subscriber of the current active configs.\n"); - dprintf(out, " --uid=UID The uid of the configurations. It is only possible to pass\n"); - dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); - dprintf(out, " calling uid is used.\n"); - dprintf(out, " --configs Send the list of configs in the name list instead of\n"); - dprintf(out, " the currently active configs\n"); - dprintf(out, " NAME LIST List of configuration names to be included in the broadcast.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats print-stats\n"); - dprintf(out, " Prints some basic stats.\n"); - dprintf(out, " --proto Print proto binary instead of string format.\n"); - dprintf(out, "\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats clear-puller-cache\n"); - dprintf(out, " Clear cached puller data.\n"); - dprintf(out, "\n"); - dprintf(out, "usage: adb shell cmd stats print-logs\n"); - dprintf(out, " Only works on eng build\n"); -} - -status_t StatsService::cmd_trigger_broadcast(int out, Vector& args) { - string name; - bool good = false; - int uid; - const int argCount = args.size(); - if (argCount == 2) { - // Automatically pick the UID - uid = AIBinder_getCallingUid(); - name.assign(args[1].c_str(), args[1].size()); - good = true; - } else if (argCount == 3) { - good = getUidFromArgs(args, 1, uid); - if (!good) { - dprintf(out, "Invalid UID. Note that the metrics can only be dumped for " - "other UIDs on eng or userdebug builds.\n"); - } - name.assign(args[2].c_str(), args[2].size()); - } - if (!good) { - print_cmd_help(out); - return UNKNOWN_ERROR; - } - ConfigKey key(uid, StrToInt64(name)); - shared_ptr receiver = mConfigManager->GetConfigReceiver(key); - if (receiver == nullptr) { - VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str()); - return UNKNOWN_ERROR; - } else if (receiver->sendDataBroadcast(mProcessor->getLastReportTimeNs(key)).isOk()) { - VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(), - args[2].c_str()); - } else { - VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(), args[2].c_str()); - return UNKNOWN_ERROR; - } - return NO_ERROR; -} - -status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector& args) { - const int argCount = args.size(); - int uid; - vector configIds; - if (argCount == 1) { - // Automatically pick the uid and send a broadcast that has no active configs. - uid = AIBinder_getCallingUid(); - mProcessor->GetActiveConfigs(uid, configIds); - } else { - int curArg = 1; - if(args[curArg].find("--uid=") == 0) { - string uidArgStr(args[curArg].c_str()); - string uidStr = uidArgStr.substr(6); - if (!getUidFromString(uidStr.c_str(), uid)) { - dprintf(out, "Invalid UID. Note that the config can only be set for " - "other UIDs on eng or userdebug builds.\n"); - return UNKNOWN_ERROR; - } - curArg++; - } else { - uid = AIBinder_getCallingUid(); - } - if (curArg == argCount || args[curArg] != "--configs") { - VLOG("Reached end of args, or specify configs not set. Sending actual active configs,"); - mProcessor->GetActiveConfigs(uid, configIds); - } else { - // Flag specified, use the given list of configs. - curArg++; - for (int i = curArg; i < argCount; i++) { - char* endp; - int64_t configID = strtoll(args[i].c_str(), &endp, 10); - if (endp == args[i].c_str() || *endp != '\0') { - dprintf(out, "Error parsing config ID.\n"); - return UNKNOWN_ERROR; - } - VLOG("Adding config id %ld", static_cast(configID)); - configIds.push_back(configID); - } - } - } - shared_ptr receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid); - if (receiver == nullptr) { - VLOG("Could not find receiver for uid %d", uid); - return UNKNOWN_ERROR; - } else if (receiver->sendActiveConfigsChangedBroadcast(configIds).isOk()) { - VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid); - } else { - VLOG("StatsService::trigger active configs changed broadcast failed for uid %d", uid); - return UNKNOWN_ERROR; - } - return NO_ERROR; -} - -status_t StatsService::cmd_config(int in, int out, int err, Vector& args) { - const int argCount = args.size(); - if (argCount >= 2) { - if (args[1] == "update" || args[1] == "remove") { - bool good = false; - int uid = -1; - string name; - - if (argCount == 3) { - // Automatically pick the UID - uid = AIBinder_getCallingUid(); - name.assign(args[2].c_str(), args[2].size()); - good = true; - } else if (argCount == 4) { - good = getUidFromArgs(args, 2, uid); - if (!good) { - dprintf(err, "Invalid UID. Note that the config can only be set for " - "other UIDs on eng or userdebug builds.\n"); - } - name.assign(args[3].c_str(), args[3].size()); - } else if (argCount == 2 && args[1] == "remove") { - good = true; - } - - if (!good) { - // If arg parsing failed, print the help text and return an error. - print_cmd_help(out); - return UNKNOWN_ERROR; - } - - if (args[1] == "update") { - char* endp; - int64_t configID = strtoll(name.c_str(), &endp, 10); - if (endp == name.c_str() || *endp != '\0') { - dprintf(err, "Error parsing config ID.\n"); - return UNKNOWN_ERROR; - } - - // Read stream into buffer. - string buffer; - if (!android::base::ReadFdToString(in, &buffer)) { - dprintf(err, "Error reading stream for StatsConfig.\n"); - return UNKNOWN_ERROR; - } - - // Parse buffer. - StatsdConfig config; - if (!config.ParseFromString(buffer)) { - dprintf(err, "Error parsing proto stream for StatsConfig.\n"); - return UNKNOWN_ERROR; - } - - // Add / update the config. - mConfigManager->UpdateConfig(ConfigKey(uid, configID), config); - } else { - if (argCount == 2) { - cmd_remove_all_configs(out); - } else { - // Remove the config. - mConfigManager->RemoveConfig(ConfigKey(uid, StrToInt64(name))); - } - } - - return NO_ERROR; - } - } - print_cmd_help(out); - return UNKNOWN_ERROR; -} - -status_t StatsService::cmd_dump_report(int out, const Vector& args) { - if (mProcessor != nullptr) { - int argCount = args.size(); - bool good = false; - bool proto = false; - bool includeCurrentBucket = false; - bool eraseData = true; - int uid; - string name; - if (!std::strcmp("--proto", args[argCount-1].c_str())) { - proto = true; - argCount -= 1; - } - if (!std::strcmp("--include_current_bucket", args[argCount-1].c_str())) { - includeCurrentBucket = true; - argCount -= 1; - } - if (!std::strcmp("--keep_data", args[argCount-1].c_str())) { - eraseData = false; - argCount -= 1; - } - if (argCount == 2) { - // Automatically pick the UID - uid = AIBinder_getCallingUid(); - name.assign(args[1].c_str(), args[1].size()); - good = true; - } else if (argCount == 3) { - good = getUidFromArgs(args, 1, uid); - if (!good) { - dprintf(out, "Invalid UID. Note that the metrics can only be dumped for " - "other UIDs on eng or userdebug builds.\n"); - } - name.assign(args[2].c_str(), args[2].size()); - } - if (good) { - vector data; - mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(), - includeCurrentBucket, eraseData, ADB_DUMP, - NO_TIME_CONSTRAINTS, - &data); - if (proto) { - for (size_t i = 0; i < data.size(); i ++) { - dprintf(out, "%c", data[i]); - } - } else { - dprintf(out, "Non-proto stats data dump not currently supported.\n"); - } - return android::OK; - } else { - // If arg parsing failed, print the help text and return an error. - print_cmd_help(out); - return UNKNOWN_ERROR; - } - } else { - dprintf(out, "Log processor does not exist...\n"); - return UNKNOWN_ERROR; - } -} - -status_t StatsService::cmd_print_stats(int out, const Vector& args) { - int argCount = args.size(); - bool proto = false; - if (!std::strcmp("--proto", args[argCount-1].c_str())) { - proto = true; - argCount -= 1; - } - StatsdStats& statsdStats = StatsdStats::getInstance(); - if (proto) { - vector data; - statsdStats.dumpStats(&data, false); // does not reset statsdStats. - for (size_t i = 0; i < data.size(); i ++) { - dprintf(out, "%c", data[i]); - } - - } else { - vector configs = mConfigManager->GetAllConfigKeys(); - for (const ConfigKey& key : configs) { - dprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(), - mProcessor->GetMetricsSize(key)); - } - statsdStats.dumpStats(out); - } - return NO_ERROR; -} - -status_t StatsService::cmd_print_uid_map(int out, const Vector& args) { - if (args.size() > 1) { - string pkg; - pkg.assign(args[1].c_str(), args[1].size()); - auto uids = mUidMap->getAppUid(pkg); - dprintf(out, "%s -> [ ", pkg.c_str()); - for (const auto& uid : uids) { - dprintf(out, "%d ", uid); - } - dprintf(out, "]\n"); - } else { - mUidMap->printUidMap(out); - } - return NO_ERROR; -} - -status_t StatsService::cmd_write_data_to_disk(int out) { - dprintf(out, "Writing data to disk\n"); - mProcessor->WriteDataToDisk(ADB_DUMP, NO_TIME_CONSTRAINTS); - return NO_ERROR; -} - -status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector& args) { - bool good = false; - int32_t uid; - int32_t label; - int32_t state; - const int argCount = args.size(); - if (argCount == 3) { - // Automatically pick the UID - uid = AIBinder_getCallingUid(); - label = atoi(args[1].c_str()); - state = atoi(args[2].c_str()); - good = true; - } else if (argCount == 4) { - good = getUidFromArgs(args, 1, uid); - if (!good) { - dprintf(out, - "Invalid UID. Note that selecting a UID for writing AppBreadcrumb can only be " - "done for other UIDs on eng or userdebug builds.\n"); - } - label = atoi(args[2].c_str()); - state = atoi(args[3].c_str()); - } - if (good) { - dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state); - android::os::statsd::util::stats_write( - android::os::statsd::util::APP_BREADCRUMB_REPORTED, uid, label, state); - } else { - print_cmd_help(out); - return UNKNOWN_ERROR; - } - return NO_ERROR; -} - -status_t StatsService::cmd_log_binary_push(int out, const Vector& args) { - // Security checks are done in the sendBinaryPushStateChanged atom. - const int argCount = args.size(); - if (argCount != 7 && argCount != 8) { - dprintf(out, "Incorrect number of argument supplied\n"); - return UNKNOWN_ERROR; - } - string trainName = string(args[1].c_str()); - int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10); - int32_t state = atoi(args[6].c_str()); - vector experimentIds; - if (argCount == 8) { - vector experimentIdsString = android::base::Split(string(args[7].c_str()), ","); - for (string experimentIdString : experimentIdsString) { - int64_t experimentId = strtoll(experimentIdString.c_str(), nullptr, 10); - experimentIds.push_back(experimentId); - } - } - dprintf(out, "Logging BinaryPushStateChanged\n"); - vector experimentIdBytes; - writeExperimentIdsToProto(experimentIds, &experimentIdBytes); - LogEvent event(trainName, trainVersion, args[3], args[4], args[5], state, experimentIdBytes, 0); - mProcessor->OnLogEvent(&event); - return NO_ERROR; -} - -status_t StatsService::cmd_print_pulled_metrics(int out, const Vector& args) { - int s = atoi(args[1].c_str()); - vector uids; - if (args.size() > 2) { - string package = string(args[2].c_str()); - auto it = UidMap::sAidToUidMapping.find(package); - if (it != UidMap::sAidToUidMapping.end()) { - uids.push_back(it->second); - } else { - set uids_set = mUidMap->getAppUid(package); - uids.insert(uids.end(), uids_set.begin(), uids_set.end()); - } - } else { - uids.push_back(AID_SYSTEM); - } - vector> stats; - if (mPullerManager->Pull(s, uids, getElapsedRealtimeNs(), &stats)) { - for (const auto& it : stats) { - dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str()); - } - dprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size()); - return NO_ERROR; - } - return UNKNOWN_ERROR; -} - -status_t StatsService::cmd_remove_all_configs(int out) { - dprintf(out, "Removing all configs...\n"); - VLOG("StatsService::cmd_remove_all_configs was called"); - mConfigManager->RemoveAllConfigs(); - StorageManager::deleteAllFiles(STATS_SERVICE_DIR); - return NO_ERROR; -} - -status_t StatsService::cmd_dump_memory_info(int out) { - dprintf(out, "meminfo not available.\n"); - return NO_ERROR; -} - -status_t StatsService::cmd_clear_puller_cache(int out) { - VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i", - AIBinder_getCallingPid(), AIBinder_getCallingUid()); - if (checkPermission(kPermissionDump)) { - int cleared = mPullerManager->ForceClearPullerCache(); - dprintf(out, "Puller removed %d cached data!\n", cleared); - return NO_ERROR; - } else { - return PERMISSION_DENIED; - } -} - -status_t StatsService::cmd_print_logs(int out, const Vector& args) { - VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(), - AIBinder_getCallingUid()); - if (checkPermission(kPermissionDump)) { - bool enabled = true; - if (args.size() >= 2) { - enabled = atoi(args[1].c_str()) != 0; - } - mProcessor->setPrintLogs(enabled); - return NO_ERROR; - } else { - return PERMISSION_DENIED; - } -} - -bool StatsService::getUidFromArgs(const Vector& args, size_t uidArgIndex, int32_t& uid) { - return getUidFromString(args[uidArgIndex].c_str(), uid); -} - -bool StatsService::getUidFromString(const char* s, int32_t& uid) { - if (*s == '\0') { - return false; - } - char* endc = NULL; - int64_t longUid = strtol(s, &endc, 0); - if (*endc != '\0') { - return false; - } - int32_t goodUid = static_cast(longUid); - if (longUid < 0 || static_cast(longUid) != static_cast(goodUid)) { - return false; // It was not of uid_t type. - } - uid = goodUid; - - int32_t callingUid = AIBinder_getCallingUid(); - return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids. - || (callingUid == goodUid) // Anyone can 'impersonate' themselves. - || (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL. -} - -Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) { - ENFORCE_UID(AID_SYSTEM); - // Read stream into buffer. - string buffer; - if (!android::base::ReadFdToString(fd.get(), &buffer)) { - return exception(EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe."); - } - - // Parse buffer. - UidData uidData; - if (!uidData.ParseFromString(buffer)) { - return exception(EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData."); - } - - vector versionStrings; - vector installers; - vector packageNames; - vector uids; - vector versions; - - const auto numEntries = uidData.app_info_size(); - versionStrings.reserve(numEntries); - installers.reserve(numEntries); - packageNames.reserve(numEntries); - uids.reserve(numEntries); - versions.reserve(numEntries); - - for (const auto& appInfo: uidData.app_info()) { - packageNames.emplace_back(String16(appInfo.package_name().c_str())); - uids.push_back(appInfo.uid()); - versions.push_back(appInfo.version()); - versionStrings.emplace_back(String16(appInfo.version_string().c_str())); - installers.emplace_back(String16(appInfo.installer().c_str())); - } - - mUidMap->updateMap(getElapsedRealtimeNs(), - uids, - versions, - versionStrings, - packageNames, - installers); - - mBootCompleteTrigger.markComplete(kUidMapReceivedTag); - VLOG("StatsService::informAllUidData UidData proto parsed successfully."); - return Status::ok(); -} - -Status StatsService::informOnePackage(const string& app, int32_t uid, int64_t version, - const string& versionString, const string& installer) { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::informOnePackage was called"); - String16 utf16App = String16(app.c_str()); - String16 utf16VersionString = String16(versionString.c_str()); - String16 utf16Installer = String16(installer.c_str()); - - mUidMap->updateApp(getElapsedRealtimeNs(), utf16App, uid, version, utf16VersionString, - utf16Installer); - return Status::ok(); -} - -Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::informOnePackageRemoved was called"); - String16 utf16App = String16(app.c_str()); - mUidMap->removeApp(getElapsedRealtimeNs(), utf16App, uid); - mConfigManager->RemoveConfigs(uid); - return Status::ok(); -} - -Status StatsService::informAlarmForSubscriberTriggeringFired() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called"); - int64_t currentTimeSec = getElapsedRealtimeSec(); - std::unordered_set, SpHash> alarmSet = - mPeriodicAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); - if (alarmSet.size() > 0) { - VLOG("Found periodic alarm fired."); - mProcessor->onPeriodicAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet); - } else { - ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled."); - } - return Status::ok(); -} - -Status StatsService::informPollAlarmFired() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::informPollAlarmFired was called"); - mProcessor->informPullAlarmFired(getElapsedRealtimeNs()); - VLOG("StatsService::informPollAlarmFired succeeded"); - return Status::ok(); -} - -Status StatsService::systemRunning() { - ENFORCE_UID(AID_SYSTEM); - - // When system_server is up and running, schedule the dropbox task to run. - VLOG("StatsService::systemRunning"); - sayHiToStatsCompanion(); - return Status::ok(); -} - -Status StatsService::informDeviceShutdown() { - ENFORCE_UID(AID_SYSTEM); - VLOG("StatsService::informDeviceShutdown"); - mProcessor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST); - mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs()); - mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs()); - return Status::ok(); -} - -void StatsService::sayHiToStatsCompanion() { - shared_ptr statsCompanion = getStatsCompanionService(); - if (statsCompanion != nullptr) { - VLOG("Telling statsCompanion that statsd is ready"); - statsCompanion->statsdReady(); - } else { - VLOG("Could not access statsCompanion"); - } -} - -Status StatsService::statsCompanionReady() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::statsCompanionReady was called"); - shared_ptr statsCompanion = getStatsCompanionService(); - if (statsCompanion == nullptr) { - return exception(EX_NULL_POINTER, - "StatsCompanion unavailable despite it contacting statsd."); - } - VLOG("StatsService::statsCompanionReady linking to statsCompanion."); - AIBinder_linkToDeath(statsCompanion->asBinder().get(), - mStatsCompanionServiceDeathRecipient.get(), this); - mPullerManager->SetStatsCompanionService(statsCompanion); - mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion); - mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion); - return Status::ok(); -} - -Status StatsService::bootCompleted() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::bootCompleted was called"); - mBootCompleteTrigger.markComplete(kBootCompleteTag); - return Status::ok(); -} - -void StatsService::Startup() { - mConfigManager->Startup(); - mProcessor->LoadActiveConfigsFromDisk(); - mProcessor->LoadMetadataFromDisk(getWallClockNs(), getElapsedRealtimeNs()); -} - -void StatsService::Terminate() { - ALOGI("StatsService::Terminating"); - if (mProcessor != nullptr) { - mProcessor->WriteDataToDisk(TERMINATION_SIGNAL_RECEIVED, FAST); - mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs()); - mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs()); - } -} - -// Test only interface!!! -void StatsService::OnLogEvent(LogEvent* event) { - mProcessor->OnLogEvent(event); - if (mShellSubscriber != nullptr) { - mShellSubscriber->onLogEvent(*event); - } -} - -Status StatsService::getData(int64_t key, const int32_t callingUid, vector* output) { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::getData with Uid %i", callingUid); - ConfigKey configKey(callingUid, key); - // The dump latency does not matter here since we do not include the current bucket, we do not - // need to pull any new data anyhow. - mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/, - true /* erase_data */, GET_DATA_CALLED, FAST, output); - return Status::ok(); -} - -Status StatsService::getMetadata(vector* output) { - ENFORCE_UID(AID_SYSTEM); - - StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters. - return Status::ok(); -} - -Status StatsService::addConfiguration(int64_t key, const vector & config, - const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - if (addConfigurationChecked(callingUid, key, config)) { - return Status::ok(); - } else { - return exception(EX_ILLEGAL_ARGUMENT, "Could not parse malformatted StatsdConfig."); - } -} - -bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector& config) { - ConfigKey configKey(uid, key); - StatsdConfig cfg; - if (config.size() > 0) { // If the config is empty, skip parsing. - if (!cfg.ParseFromArray(&config[0], config.size())) { - return false; - } - } - mConfigManager->UpdateConfig(configKey, cfg); - return true; -} - -Status StatsService::removeDataFetchOperation(int64_t key, - const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - ConfigKey configKey(callingUid, key); - mConfigManager->RemoveConfigReceiver(configKey); - return Status::ok(); -} - -Status StatsService::setDataFetchOperation(int64_t key, - const shared_ptr& pir, - const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - ConfigKey configKey(callingUid, key); - mConfigManager->SetConfigReceiver(configKey, pir); - if (StorageManager::hasConfigMetricsReport(configKey)) { - VLOG("StatsService::setDataFetchOperation marking configKey %s to dump reports on disk", - configKey.ToString().c_str()); - mProcessor->noteOnDiskData(configKey); - } - return Status::ok(); -} - -Status StatsService::setActiveConfigsChangedOperation(const shared_ptr& pir, - const int32_t callingUid, - vector* output) { - ENFORCE_UID(AID_SYSTEM); - - mConfigManager->SetActiveConfigsChangedReceiver(callingUid, pir); - if (output != nullptr) { - mProcessor->GetActiveConfigs(callingUid, *output); - } else { - ALOGW("StatsService::setActiveConfigsChanged output was nullptr"); - } - return Status::ok(); -} - -Status StatsService::removeActiveConfigsChangedOperation(const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - mConfigManager->RemoveActiveConfigsChangedReceiver(callingUid); - return Status::ok(); -} - -Status StatsService::removeConfiguration(int64_t key, const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - ConfigKey configKey(callingUid, key); - mConfigManager->RemoveConfig(configKey); - return Status::ok(); -} - -Status StatsService::setBroadcastSubscriber(int64_t configId, - int64_t subscriberId, - const shared_ptr& pir, - const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::setBroadcastSubscriber called."); - ConfigKey configKey(callingUid, configId); - SubscriberReporter::getInstance() - .setBroadcastSubscriber(configKey, subscriberId, pir); - return Status::ok(); -} - -Status StatsService::unsetBroadcastSubscriber(int64_t configId, - int64_t subscriberId, - const int32_t callingUid) { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::unsetBroadcastSubscriber called."); - ConfigKey configKey(callingUid, configId); - SubscriberReporter::getInstance() - .unsetBroadcastSubscriber(configKey, subscriberId); - return Status::ok(); -} - -Status StatsService::allPullersFromBootRegistered() { - ENFORCE_UID(AID_SYSTEM); - - VLOG("StatsService::allPullersFromBootRegistered was called"); - mBootCompleteTrigger.markComplete(kAllPullersRegisteredTag); - return Status::ok(); -} - -Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownMillis, - int64_t timeoutMillis, - const std::vector& additiveFields, - const shared_ptr& pullerCallback) { - ENFORCE_UID(AID_SYSTEM); - VLOG("StatsService::registerPullAtomCallback called."); - mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis), - MillisToNano(timeoutMillis), additiveFields, - pullerCallback); - return Status::ok(); -} - -Status StatsService::registerNativePullAtomCallback( - int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis, - const std::vector& additiveFields, - const shared_ptr& pullerCallback) { - if (!checkPermission(kPermissionRegisterPullAtom)) { - return exception( - EX_SECURITY, - StringPrintf("Uid %d does not have the %s permission when registering atom %d", - AIBinder_getCallingUid(), kPermissionRegisterPullAtom, atomTag)); - } - VLOG("StatsService::registerNativePullAtomCallback called."); - int32_t uid = AIBinder_getCallingUid(); - mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis), - MillisToNano(timeoutMillis), additiveFields, - pullerCallback); - return Status::ok(); -} - -Status StatsService::unregisterPullAtomCallback(int32_t uid, int32_t atomTag) { - ENFORCE_UID(AID_SYSTEM); - VLOG("StatsService::unregisterPullAtomCallback called."); - mPullerManager->UnregisterPullAtomCallback(uid, atomTag); - return Status::ok(); -} - -Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) { - if (!checkPermission(kPermissionRegisterPullAtom)) { - return exception( - EX_SECURITY, - StringPrintf("Uid %d does not have the %s permission when unregistering atom %d", - AIBinder_getCallingUid(), kPermissionRegisterPullAtom, atomTag)); - } - VLOG("StatsService::unregisterNativePullAtomCallback called."); - int32_t uid = AIBinder_getCallingUid(); - mPullerManager->UnregisterPullAtomCallback(uid, atomTag); - return Status::ok(); -} - -Status StatsService::getRegisteredExperimentIds(std::vector* experimentIdsOut) { - ENFORCE_UID(AID_SYSTEM); - // TODO: add verifier permission - - experimentIdsOut->clear(); - // Read the latest train info - vector trainInfoList = StorageManager::readAllTrainInfo(); - if (trainInfoList.empty()) { - // No train info means no experiment IDs, return an empty list - return Status::ok(); - } - - // Copy the experiment IDs to the out vector - for (InstallTrainInfo& trainInfo : trainInfoList) { - experimentIdsOut->insert(experimentIdsOut->end(), - trainInfo.experimentIds.begin(), - trainInfo.experimentIds.end()); - } - return Status::ok(); -} - -void StatsService::statsCompanionServiceDied(void* cookie) { - auto thiz = static_cast(cookie); - thiz->statsCompanionServiceDiedImpl(); -} - -void StatsService::statsCompanionServiceDiedImpl() { - ALOGW("statscompanion service died"); - StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec()); - if (mProcessor != nullptr) { - ALOGW("Reset statsd upon system server restarts."); - int64_t systemServerRestartNs = getElapsedRealtimeNs(); - ProtoOutputStream activeConfigsProto; - mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs, - STATSCOMPANION_DIED, &activeConfigsProto); - metadata::StatsMetadataList metadataList; - mProcessor->WriteMetadataToProto(getWallClockNs(), - systemServerRestartNs, &metadataList); - mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST); - mProcessor->resetConfigs(); - - std::string serializedActiveConfigs; - if (activeConfigsProto.serializeToString(&serializedActiveConfigs)) { - ActiveConfigList activeConfigs; - if (activeConfigs.ParseFromString(serializedActiveConfigs)) { - mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs); - } - } - mProcessor->SetMetadataState(metadataList, getWallClockNs(), systemServerRestartNs); - } - mAnomalyAlarmMonitor->setStatsCompanionService(nullptr); - mPeriodicAlarmMonitor->setStatsCompanionService(nullptr); - mPullerManager->SetStatsCompanionService(nullptr); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h deleted file mode 100644 index 479f4e87ec96..000000000000 --- a/cmds/statsd/src/StatsService.h +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef STATS_SERVICE_H -#define STATS_SERVICE_H - -#include -#include -#include -#include -#include - -#include - -#include "StatsLogProcessor.h" -#include "anomaly/AlarmMonitor.h" -#include "config/ConfigManager.h" -#include "external/StatsPullerManager.h" -#include "logd/LogEventQueue.h" -#include "packages/UidMap.h" -#include "shell/ShellSubscriber.h" -#include "statscompanion_util.h" -#include "utils/MultiConditionTrigger.h" - -using namespace android; -using namespace android::os; -using namespace std; - -using Status = ::ndk::ScopedAStatus; -using aidl::android::os::BnStatsd; -using aidl::android::os::IPendingIntentRef; -using aidl::android::os::IPullAtomCallback; -using ::ndk::ScopedAIBinder_DeathRecipient; -using ::ndk::ScopedFileDescriptor; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -class StatsService : public BnStatsd { -public: - StatsService(const sp& handlerLooper, std::shared_ptr queue); - virtual ~StatsService(); - - /** The anomaly alarm registered with AlarmManager won't be updated by less than this. */ - const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5; - - virtual status_t dump(int fd, const char** args, uint32_t numArgs) override; - virtual status_t handleShellCommand(int in, int out, int err, const char** argv, - uint32_t argc) override; - - virtual Status systemRunning(); - virtual Status statsCompanionReady(); - virtual Status bootCompleted(); - virtual Status informPollAlarmFired(); - virtual Status informAlarmForSubscriberTriggeringFired(); - - virtual Status informAllUidData(const ScopedFileDescriptor& fd); - virtual Status informOnePackage(const string& app, int32_t uid, int64_t version, - const string& versionString, const string& installer); - virtual Status informOnePackageRemoved(const string& app, int32_t uid); - virtual Status informDeviceShutdown(); - - /** - * Called right before we start processing events. - */ - void Startup(); - - /** - * Called when terminiation signal received. - */ - void Terminate(); - - /** - * Test ONLY interface. In real world, StatsService reads from LogEventQueue. - */ - virtual void OnLogEvent(LogEvent* event); - - /** - * Binder call for clients to request data for this configuration key. - */ - virtual Status getData(int64_t key, - const int32_t callingUid, - vector* output) override; - - - /** - * Binder call for clients to get metadata across all configs in statsd. - */ - virtual Status getMetadata(vector* output) override; - - - /** - * Binder call to let clients send a configuration and indicate they're interested when they - * should requestData for this configuration. - */ - virtual Status addConfiguration(int64_t key, - const vector& config, - const int32_t callingUid) override; - - /** - * Binder call to let clients register the data fetch operation for a configuration. - */ - virtual Status setDataFetchOperation(int64_t key, - const shared_ptr& pir, - const int32_t callingUid) override; - - /** - * Binder call to remove the data fetch operation for the specified config key. - */ - virtual Status removeDataFetchOperation(int64_t key, - const int32_t callingUid) override; - - /** - * Binder call to let clients register the active configs changed operation. - */ - virtual Status setActiveConfigsChangedOperation(const shared_ptr& pir, - const int32_t callingUid, - vector* output) override; - - /** - * Binder call to remove the active configs changed operation for the specified package.. - */ - virtual Status removeActiveConfigsChangedOperation(const int32_t callingUid) override; - /** - * Binder call to allow clients to remove the specified configuration. - */ - virtual Status removeConfiguration(int64_t key, - const int32_t callingUid) override; - - /** - * Binder call to associate the given config's subscriberId with the given pendingIntentRef. - */ - virtual Status setBroadcastSubscriber(int64_t configId, - int64_t subscriberId, - const shared_ptr& pir, - const int32_t callingUid) override; - - /** - * Binder call to unassociate the given config's subscriberId with any pendingIntentRef. - */ - virtual Status unsetBroadcastSubscriber(int64_t configId, - int64_t subscriberId, - const int32_t callingUid) override; - - /** Inform statsCompanion that statsd is ready. */ - virtual void sayHiToStatsCompanion(); - - /** - * Binder call to notify statsd that all pullers from boot have been registered. - */ - virtual Status allPullersFromBootRegistered(); - - /** - * Binder call to register a callback function for a pulled atom. - */ - virtual Status registerPullAtomCallback( - int32_t uid, int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis, - const std::vector& additiveFields, - const shared_ptr& pullerCallback) override; - - /** - * Binder call to register a callback function for a pulled atom. - */ - virtual Status registerNativePullAtomCallback( - int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis, - const std::vector& additiveFields, - const shared_ptr& pullerCallback) override; - - /** - * Binder call to unregister any existing callback for the given uid and atom. - */ - virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override; - - /** - * Binder call to unregister any existing callback for the given atom and calling uid. - */ - virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override; - - /** - * Binder call to get registered experiment IDs. - */ - virtual Status getRegisteredExperimentIds(std::vector* expIdsOut); - -private: - /** - * Load system properties at init. - */ - void init_system_properties(); - - /** - * Helper for loading system properties. - */ - static void init_build_type_callback(void* cookie, const char* name, const char* value, - uint32_t serial); - - /** - * Proto output of statsd report data dumpsys, wrapped in a StatsDataDumpProto. - */ - void dumpIncidentSection(int outFd); - - /** - * Text or proto output of statsdStats dumpsys. - */ - void dumpStatsdStats(int outFd, bool verbose, bool proto); - - /** - * Print usage information for the commands - */ - void print_cmd_help(int out); - - /* Runs on its dedicated thread to process pushed stats event from socket. */ - void readLogs(); - - /** - * Trigger a broadcast. - */ - status_t cmd_trigger_broadcast(int outFd, Vector& args); - - - /** - * Trigger an active configs changed broadcast. - */ - status_t cmd_trigger_active_config_broadcast(int outFd, Vector& args); - - /** - * Handle the config sub-command. - */ - status_t cmd_config(int inFd, int outFd, int err, Vector& args); - - /** - * Prints some basic stats to std out. - */ - status_t cmd_print_stats(int outFd, const Vector& args); - - /** - * Print the event log. - */ - status_t cmd_dump_report(int outFd, const Vector& args); - - /** - * Print the mapping of uids to package names. - */ - status_t cmd_print_uid_map(int outFd, const Vector& args); - - /** - * Flush the data to disk. - */ - status_t cmd_write_data_to_disk(int outFd); - - /** - * Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling - * StatsLog.write(APP_BREADCRUMB_REPORTED). - */ - status_t cmd_log_app_breadcrumb(int outFd, const Vector& args); - - /** - * Write an BinaryPushStateChanged event, as if calling StatsLog.logBinaryPushStateChanged(). - */ - status_t cmd_log_binary_push(int outFd, const Vector& args); - - /** - * Print contents of a pulled metrics source. - */ - status_t cmd_print_pulled_metrics(int outFd, const Vector& args); - - /** - * Removes all configs stored on disk and on memory. - */ - status_t cmd_remove_all_configs(int outFd); - - /* - * Dump memory usage by statsd. - */ - status_t cmd_dump_memory_info(int outFd); - - /* - * Clear all puller cached data - */ - status_t cmd_clear_puller_cache(int outFd); - - /** - * Print all stats logs received to logcat. - */ - status_t cmd_print_logs(int outFd, const Vector& args); - - /** - * Writes the value of args[uidArgIndex] into uid. - * Returns whether the uid is reasonable (type uid_t) and whether - * 1. it is equal to the calling uid, or - * 2. the device is mEngBuild, or - * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL). - */ - bool getUidFromArgs(const Vector& args, size_t uidArgIndex, int32_t& uid); - - /** - * Writes the value of uidSting into uid. - * Returns whether the uid is reasonable (type uid_t) and whether - * 1. it is equal to the calling uid, or - * 2. the device is mEngBuild, or - * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL). - */ - bool getUidFromString(const char* uidString, int32_t& uid); - - /** - * Adds a configuration after checking permissions and obtaining UID from binder call. - */ - bool addConfigurationChecked(int uid, int64_t key, const vector& config); - - /** - * Update a configuration. - */ - void set_config(int uid, const string& name, const StatsdConfig& config); - - /** - * Death recipient callback that is called when StatsCompanionService dies. - * The cookie is a pointer to a StatsService object. - */ - static void statsCompanionServiceDied(void* cookie); - - /** - * Implementation of statsCompanionServiceDied. - */ - void statsCompanionServiceDiedImpl(); - - /** - * Tracks the uid <--> package name mapping. - */ - sp mUidMap; - - /** - * Fetches external metrics - */ - sp mPullerManager; - - /** - * Tracks the configurations that have been passed to statsd. - */ - sp mConfigManager; - - /** - * The metrics recorder. - */ - sp mProcessor; - - /** - * The alarm monitor for anomaly detection. - */ - const sp mAnomalyAlarmMonitor; - - /** - * The alarm monitor for alarms to directly trigger subscriber. - */ - const sp mPeriodicAlarmMonitor; - - /** - * Whether this is an eng build. - */ - bool mEngBuild; - - sp mShellSubscriber; - - /** - * Mutex for setting the shell subscriber - */ - mutable mutex mShellSubscriberMutex; - std::shared_ptr mEventQueue; - - MultiConditionTrigger mBootCompleteTrigger; - static const inline string kBootCompleteTag = "BOOT_COMPLETE"; - static const inline string kUidMapReceivedTag = "UID_MAP"; - static const inline string kAllPullersRegisteredTag = "PULLERS_REGISTERED"; - - ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient; - - FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); - FRIEND_TEST(StatsServiceTest, TestAddConfig_simple); - FRIEND_TEST(StatsServiceTest, TestAddConfig_empty); - FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid); - FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs); - FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp); - FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot); - FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade); - FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval); - FRIEND_TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit); - FRIEND_TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket); - FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket); - FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket); - FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket); - FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket); - FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket); - - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // STATS_SERVICE_H diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto deleted file mode 100644 index 992983358ae6..000000000000 --- a/cmds/statsd/src/active_config_list.proto +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.os.statsd; -option java_package = "com.android.os"; -option java_multiple_files = true; -option java_outer_classname = "ActiveConfigProto"; - -message ActiveEventActivation { - optional int32 atom_matcher_index = 1; - - // Time left in activation. When this proto is loaded after device boot, - // the activation should be set to active for this duration. - // This field will only be set when the state is ACTIVE - optional int64 remaining_ttl_nanos = 2; - - enum State { - UNNKNOWN = 0; - // This metric should activate for remaining_ttl_nanos when we load the activations. - ACTIVE = 1; - // When we load the activations, this metric should activate on next boot for the tll - // specified in the config. - ACTIVATE_ON_BOOT = 2; - } - optional State state = 3; -} - -message ActiveMetric { - optional int64 id = 1; - repeated ActiveEventActivation activation = 2; -} - -message ActiveConfig { - optional int64 id = 1; - optional int32 uid = 2; - repeated ActiveMetric metric = 3; -} - -// all configs and their metrics on device. -message ActiveConfigList { - repeated ActiveConfig config = 1; -} diff --git a/cmds/statsd/src/annotations.h b/cmds/statsd/src/annotations.h deleted file mode 100644 index cf7f5433663f..000000000000 --- a/cmds/statsd/src/annotations.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -namespace android { -namespace os { -namespace statsd { - -const uint8_t ANNOTATION_ID_IS_UID = 1; -const uint8_t ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2; -const uint8_t ANNOTATION_ID_PRIMARY_FIELD = 3; -const uint8_t ANNOTATION_ID_EXCLUSIVE_STATE = 4; -const uint8_t ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5; -const uint8_t ANNOTATION_ID_TRIGGER_STATE_RESET = 7; -const uint8_t ANNOTATION_ID_STATE_NESTED = 8; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp deleted file mode 100644 index b632d040eb43..000000000000 --- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false -#include "Log.h" - -#include "anomaly/AlarmMonitor.h" -#include "guardrail/StatsdStats.h" - -namespace android { -namespace os { -namespace statsd { - -AlarmMonitor::AlarmMonitor( - uint32_t minDiffToUpdateRegisteredAlarmTimeSec, - const std::function&, int64_t)>& updateAlarm, - const std::function&)>& cancelAlarm) - : mRegisteredAlarmTimeSec(0), - mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec), - mUpdateAlarm(updateAlarm), - mCancelAlarm(cancelAlarm) {} - -AlarmMonitor::~AlarmMonitor() {} - -void AlarmMonitor::setStatsCompanionService( - shared_ptr statsCompanionService) { - std::lock_guard lock(mLock); - shared_ptr tmpForLock = mStatsCompanionService; - mStatsCompanionService = statsCompanionService; - if (statsCompanionService == nullptr) { - VLOG("Erasing link to statsCompanionService"); - return; - } - VLOG("Creating link to statsCompanionService"); - const sp top = mPq.top(); - if (top != nullptr) { - updateRegisteredAlarmTime_l(top->timestampSec); - } -} - -void AlarmMonitor::add(sp alarm) { - std::lock_guard lock(mLock); - if (alarm == nullptr) { - ALOGW("Asked to add a null alarm."); - return; - } - if (alarm->timestampSec < 1) { - // forbidden since a timestamp 0 is used to indicate no alarm registered - ALOGW("Asked to add a 0-time alarm."); - return; - } - // TODO(b/110563466): Ensure that refractory period is respected. - VLOG("Adding alarm with time %u", alarm->timestampSec); - mPq.push(alarm); - if (mRegisteredAlarmTimeSec < 1 || - alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) { - updateRegisteredAlarmTime_l(alarm->timestampSec); - } -} - -void AlarmMonitor::remove(sp alarm) { - std::lock_guard lock(mLock); - if (alarm == nullptr) { - ALOGW("Asked to remove a null alarm."); - return; - } - VLOG("Removing alarm with time %u", alarm->timestampSec); - bool wasPresent = mPq.remove(alarm); - if (!wasPresent) return; - if (mPq.empty()) { - VLOG("Queue is empty. Cancel any alarm."); - cancelRegisteredAlarmTime_l(); - return; - } - uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec; - VLOG("Soonest alarm is %u", soonestAlarmTimeSec); - if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) { - updateRegisteredAlarmTime_l(soonestAlarmTimeSec); - } -} - -// More efficient than repeatedly calling remove(mPq.top()) since it batches the -// updates to the registered alarm. -unordered_set, SpHash> AlarmMonitor::popSoonerThan( - uint32_t timestampSec) { - VLOG("Removing alarms with time <= %u", timestampSec); - unordered_set, SpHash> oldAlarms; - std::lock_guard lock(mLock); - - for (sp t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec; - t = mPq.top()) { - oldAlarms.insert(t); - mPq.pop(); // remove t - } - // Always update registered alarm time (if anything has changed). - if (!oldAlarms.empty()) { - if (mPq.empty()) { - VLOG("Queue is empty. Cancel any alarm."); - cancelRegisteredAlarmTime_l(); - } else { - // Always update the registered alarm in this case (unlike remove()). - updateRegisteredAlarmTime_l(mPq.top()->timestampSec); - } - } - return oldAlarms; -} - -void AlarmMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) { - VLOG("Updating reg alarm time to %u", timestampSec); - mRegisteredAlarmTimeSec = timestampSec; - mUpdateAlarm(mStatsCompanionService, secToMs(mRegisteredAlarmTimeSec)); -} - -void AlarmMonitor::cancelRegisteredAlarmTime_l() { - VLOG("Cancelling reg alarm."); - mRegisteredAlarmTimeSec = 0; - mCancelAlarm(mStatsCompanionService); -} - -int64_t AlarmMonitor::secToMs(uint32_t timeSec) { - return ((int64_t)timeSec) * 1000; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h deleted file mode 100644 index 5c34e381ba0b..000000000000 --- a/cmds/statsd/src/anomaly/AlarmMonitor.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "anomaly/indexed_priority_queue.h" - -#include -#include - -#include -#include - -using namespace android; - -using aidl::android::os::IStatsCompanionService; -using std::function; -using std::shared_ptr; -using std::unordered_set; - -namespace android { -namespace os { -namespace statsd { - -/** - * Represents an alarm, associated with some aggregate metric, holding a - * projected time at which the metric is expected to exceed its anomaly - * threshold. - * Timestamps are in seconds since epoch in a uint32, so will fail in year 2106. - */ -struct InternalAlarm : public RefBase { - explicit InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) { - } - - const uint32_t timestampSec; - - /** InternalAlarm a is smaller (higher priority) than b if its timestamp is sooner. */ - struct SmallerTimestamp { - bool operator()(sp a, sp b) const { - return (a->timestampSec < b->timestampSec); - } - }; -}; - -/** - * Manages internal alarms that may get registered with the AlarmManager. - */ -class AlarmMonitor : public RefBase { -public: - /** - * @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs - * from the registered alarm by more than this amount, update the registered - * alarm. - */ - AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec, - const function&, int64_t)>& - updateAlarm, - const function&)>& cancelAlarm); - ~AlarmMonitor(); - - /** - * Tells AnomalyMonitor what IStatsCompanionService to use and, if - * applicable, immediately registers an existing alarm with it. - * If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't - * update IStatsCompanionService (until such time as it is set non-null). - */ - void setStatsCompanionService(shared_ptr statsCompanionService); - - /** - * Adds the given alarm (reference) to the queue. - */ - void add(sp alarm); - - /** - * Removes the given alarm (reference) from the queue. - * Note that alarm comparison is reference-based; if another alarm exists - * with the same timestampSec, that alarm will still remain in the queue. - */ - void remove(sp alarm); - - /** - * Returns and removes all alarms whose timestamp <= the given timestampSec. - * Always updates the registered alarm if return is non-empty. - */ - unordered_set, SpHash> popSoonerThan( - uint32_t timestampSec); - - /** - * Returns the projected alarm timestamp that is registered with - * StatsCompanionService. This may not be equal to the soonest alarm, - * but should be within minDiffToUpdateRegisteredAlarmTimeSec of it. - */ - uint32_t getRegisteredAlarmTimeSec() const { - return mRegisteredAlarmTimeSec; - } - -private: - std::mutex mLock; - - /** - * Timestamp (seconds since epoch) of the alarm registered with - * StatsCompanionService. This, in general, may not be equal to the soonest - * alarm stored in mPq, but should be within minUpdateTimeSec of it. - * A value of 0 indicates that no alarm is currently registered. - */ - uint32_t mRegisteredAlarmTimeSec; - - /** - * Priority queue of alarms, prioritized by soonest alarm.timestampSec. - */ - indexed_priority_queue mPq; - - /** - * Binder interface for communicating with StatsCompanionService. - */ - shared_ptr mStatsCompanionService = nullptr; - - /** - * Amount by which the soonest projected alarm must differ from - * mRegisteredAlarmTimeSec before updateRegisteredAlarmTime_l is called. - */ - uint32_t mMinUpdateTimeSec; - - /** - * Updates the alarm registered with StatsCompanionService to the given time. - * Also correspondingly updates mRegisteredAlarmTimeSec. - */ - void updateRegisteredAlarmTime_l(uint32_t timestampSec); - - /** - * Cancels the alarm registered with StatsCompanionService. - * Also correspondingly sets mRegisteredAlarmTimeSec to 0. - */ - void cancelRegisteredAlarmTime_l(); - - /** Converts uint32 timestamp in seconds to a Java long in msec. */ - int64_t secToMs(uint32_t timeSec); - - // Callback function to update the alarm via StatsCompanionService. - std::function, int64_t)> mUpdateAlarm; - - // Callback function to cancel the alarm via StatsCompanionService. - std::function)> mCancelAlarm; - -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp deleted file mode 100644 index 6d9beb8f718d..000000000000 --- a/cmds/statsd/src/anomaly/AlarmTracker.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "anomaly/AlarmTracker.h" -#include "anomaly/subscriber_util.h" -#include "HashableDimensionKey.h" -#include "stats_util.h" -#include "storage/StorageManager.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -AlarmTracker::AlarmTracker(const int64_t startMillis, - const int64_t currentMillis, - const Alarm& alarm, const ConfigKey& configKey, - const sp& alarmMonitor) - : mAlarmConfig(alarm), - mConfigKey(configKey), - mAlarmMonitor(alarmMonitor) { - VLOG("AlarmTracker() called"); - mAlarmSec = (startMillis + mAlarmConfig.offset_millis()) / MS_PER_SEC; - // startMillis is the time statsd is created. We need to find the 1st alarm timestamp after - // the config is added to statsd. - mAlarmSec = findNextAlarmSec(currentMillis / MS_PER_SEC); // round up - mInternalAlarm = new InternalAlarm{static_cast(mAlarmSec)}; - VLOG("AlarmTracker sets the periodic alarm at: %lld", (long long)mAlarmSec); - if (mAlarmMonitor != nullptr) { - mAlarmMonitor->add(mInternalAlarm); - } -} - -AlarmTracker::~AlarmTracker() { - VLOG("~AlarmTracker() called"); - if (mInternalAlarm != nullptr && mAlarmMonitor != nullptr) { - mAlarmMonitor->remove(mInternalAlarm); - } -} - -void AlarmTracker::addSubscription(const Subscription& subscription) { - mSubscriptions.push_back(subscription); -} - -int64_t AlarmTracker::findNextAlarmSec(int64_t currentTimeSec) { - if (currentTimeSec < mAlarmSec) { - return mAlarmSec; - } - int64_t periodsForward = - ((currentTimeSec - mAlarmSec) * MS_PER_SEC) / mAlarmConfig.period_millis() + 1; - return mAlarmSec + periodsForward * mAlarmConfig.period_millis() / MS_PER_SEC; -} - -void AlarmTracker::informAlarmsFired( - const int64_t& timestampNs, - unordered_set, SpHash>& firedAlarms) { - if (firedAlarms.empty() || mInternalAlarm == nullptr || - firedAlarms.find(mInternalAlarm) == firedAlarms.end()) { - return; - } - if (!mSubscriptions.empty()) { - VLOG("AlarmTracker triggers the subscribers."); - triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY, - 0 /* metricValue N/A */, mConfigKey, mSubscriptions); - } - firedAlarms.erase(mInternalAlarm); - mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up - mInternalAlarm = new InternalAlarm{static_cast(mAlarmSec)}; - VLOG("AlarmTracker sets the periodic alarm at: %lld", (long long)mAlarmSec); - if (mAlarmMonitor != nullptr) { - mAlarmMonitor->add(mInternalAlarm); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h deleted file mode 100644 index 2da4a18682ae..000000000000 --- a/cmds/statsd/src/anomaly/AlarmTracker.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "AlarmMonitor.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alarm - -#include -#include - -namespace android { -namespace os { -namespace statsd { - -class AlarmTracker : public virtual RefBase { -public: - AlarmTracker(const int64_t startMillis, - const int64_t currentMillis, - const Alarm& alarm, const ConfigKey& configKey, - const sp& subscriberAlarmMonitor); - - virtual ~AlarmTracker(); - - void onAlarmFired(); - - void addSubscription(const Subscription& subscription); - - void informAlarmsFired(const int64_t& timestampNs, - unordered_set, SpHash>& firedAlarms); - -protected: - // For test only. Returns the alarm timestamp in seconds. Otherwise returns 0. - inline int32_t getAlarmTimestampSec() const { - return mInternalAlarm == nullptr ? 0 : mInternalAlarm->timestampSec; - } - - int64_t findNextAlarmSec(int64_t currentTimeMillis); - - // statsd_config.proto Alarm message that defines this tracker. - const Alarm mAlarmConfig; - - // A reference to the Alarm's config key. - const ConfigKey mConfigKey; - - // The subscriptions that depend on this alarm. - std::vector mSubscriptions; - - // Alarm monitor. - sp mAlarmMonitor; - - // The current expected alarm time in seconds. - int64_t mAlarmSec; - - // The current alarm. - sp mInternalAlarm; - - FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp); - FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp deleted file mode 100644 index 619752c7c44a..000000000000 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "AnomalyTracker.h" -#include "external/Perfetto.h" -#include "guardrail/StatsdStats.h" -#include "metadata_util.h" -#include "stats_log_util.h" -#include "subscriber_util.h" -#include "subscriber/IncidentdReporter.h" -#include "subscriber/SubscriberReporter.h" - -#include -#include -#include - -namespace android { -namespace os { -namespace statsd { - -AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey) - : mAlert(alert), mConfigKey(configKey), mNumOfPastBuckets(mAlert.num_buckets() - 1) { - VLOG("AnomalyTracker() called"); - if (mAlert.num_buckets() <= 0) { - ALOGE("Cannot create AnomalyTracker with %lld buckets", (long long)mAlert.num_buckets()); - return; - } - if (!mAlert.has_trigger_if_sum_gt()) { - ALOGE("Cannot create AnomalyTracker without threshold"); - return; - } - resetStorage(); // initialization -} - -AnomalyTracker::~AnomalyTracker() { - VLOG("~AnomalyTracker() called"); -} - -void AnomalyTracker::resetStorage() { - VLOG("resetStorage() called."); - mPastBuckets.clear(); - // Excludes the current bucket. - mPastBuckets.resize(mNumOfPastBuckets); - mSumOverPastBuckets.clear(); -} - -size_t AnomalyTracker::index(int64_t bucketNum) const { - if (bucketNum < 0) { - ALOGE("index() was passed a negative bucket number (%lld)!", (long long)bucketNum); - } - return bucketNum % mNumOfPastBuckets; -} - -void AnomalyTracker::advanceMostRecentBucketTo(const int64_t& bucketNum) { - VLOG("advanceMostRecentBucketTo() called."); - if (mNumOfPastBuckets <= 0) { - return; - } - if (bucketNum <= mMostRecentBucketNum) { - ALOGW("Cannot advance buckets backwards (bucketNum=%lld but mMostRecentBucketNum=%lld)", - (long long)bucketNum, (long long)mMostRecentBucketNum); - return; - } - // If in the future (i.e. buckets are ancient), just empty out all past info. - if (bucketNum >= mMostRecentBucketNum + mNumOfPastBuckets) { - resetStorage(); - mMostRecentBucketNum = bucketNum; - return; - } - - // Clear out space by emptying out old mPastBuckets[i] values and update mSumOverPastBuckets. - for (int64_t i = mMostRecentBucketNum + 1; i <= bucketNum; i++) { - const int idx = index(i); - subtractBucketFromSum(mPastBuckets[idx]); - mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket. - } - mMostRecentBucketNum = bucketNum; -} - -void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, - const int64_t& bucketValue, - const int64_t& bucketNum) { - VLOG("addPastBucket(bucketValue) called."); - if (mNumOfPastBuckets == 0 || - bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) { - return; - } - - const int bucketIndex = index(bucketNum); - if (bucketNum <= mMostRecentBucketNum && (mPastBuckets[bucketIndex] != nullptr)) { - // We need to insert into an already existing past bucket. - std::shared_ptr& bucket = mPastBuckets[bucketIndex]; - auto itr = bucket->find(key); - if (itr != bucket->end()) { - // Old entry already exists; update it. - subtractValueFromSum(key, itr->second); - itr->second = bucketValue; - } else { - bucket->insert({key, bucketValue}); - } - mSumOverPastBuckets[key] += bucketValue; - } else { - // Bucket does not exist yet (in future or was never made), so we must make it. - std::shared_ptr bucket = std::make_shared(); - bucket->insert({key, bucketValue}); - addPastBucket(bucket, bucketNum); - } -} - -void AnomalyTracker::addPastBucket(std::shared_ptr bucket, - const int64_t& bucketNum) { - VLOG("addPastBucket(bucket) called."); - if (mNumOfPastBuckets == 0 || - bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) { - return; - } - - if (bucketNum <= mMostRecentBucketNum) { - // We are updating an old bucket, not adding a new one. - subtractBucketFromSum(mPastBuckets[index(bucketNum)]); - } else { - // Clear space for the new bucket to be at bucketNum. - advanceMostRecentBucketTo(bucketNum); - } - mPastBuckets[index(bucketNum)] = bucket; - addBucketToSum(bucket); -} - -void AnomalyTracker::subtractBucketFromSum(const shared_ptr& bucket) { - if (bucket == nullptr) { - return; - } - for (const auto& keyValuePair : *bucket) { - subtractValueFromSum(keyValuePair.first, keyValuePair.second); - } -} - - -void AnomalyTracker::subtractValueFromSum(const MetricDimensionKey& key, - const int64_t& bucketValue) { - auto itr = mSumOverPastBuckets.find(key); - if (itr == mSumOverPastBuckets.end()) { - return; - } - itr->second -= bucketValue; - if (itr->second == 0) { - mSumOverPastBuckets.erase(itr); - } -} - -void AnomalyTracker::addBucketToSum(const shared_ptr& bucket) { - if (bucket == nullptr) { - return; - } - // For each dimension present in the bucket, add its value to its corresponding sum. - for (const auto& keyValuePair : *bucket) { - mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second; - } -} - -int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key, - const int64_t& bucketNum) const { - if (bucketNum < 0 || mMostRecentBucketNum < 0 - || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets - || bucketNum > mMostRecentBucketNum) { - return 0; - } - - const auto& bucket = mPastBuckets[index(bucketNum)]; - if (bucket == nullptr) { - return 0; - } - const auto& itr = bucket->find(key); - return itr == bucket->end() ? 0 : itr->second; -} - -int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const { - const auto& itr = mSumOverPastBuckets.find(key); - if (itr != mSumOverPastBuckets.end()) { - return itr->second; - } - return 0; -} - -bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, - const MetricDimensionKey& key, - const int64_t& currentBucketValue) { - - // currentBucketNum should be the next bucket after pastBuckets. If not, advance so that it is. - if (currentBucketNum > mMostRecentBucketNum + 1) { - advanceMostRecentBucketTo(currentBucketNum - 1); - } - return mAlert.has_trigger_if_sum_gt() && - getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); -} - -void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId, - const MetricDimensionKey& key, int64_t metricValue) { - // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on - // real time right now. - if (isInRefractoryPeriod(timestampNs, key)) { - VLOG("Skipping anomaly declaration since within refractory period"); - return; - } - if (mAlert.has_refractory_period_secs()) { - mRefractoryPeriodEndsSec[key] = ((timestampNs + NS_PER_SEC - 1) / NS_PER_SEC) // round up - + mAlert.refractory_period_secs(); - // TODO(b/110563466): If we had access to the bucket_size_millis, consider - // calling resetStorage() - // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) {resetStorage();} - } - - if (!mSubscriptions.empty()) { - ALOGI("An anomaly (%" PRId64 ") %s has occurred! Informing subscribers.", - mAlert.id(), key.toString().c_str()); - informSubscribers(key, metricId, metricValue); - } else { - ALOGI("An anomaly has occurred! (But no subscriber for that alert.)"); - } - - StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id()); - - // TODO(b/110564268): This should also take in the const MetricDimensionKey& key? - util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(), - mConfigKey.GetId(), mAlert.id()); -} - -void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs, - const int64_t& currBucketNum, int64_t metricId, - const MetricDimensionKey& key, - const int64_t& currentBucketValue) { - if (detectAnomaly(currBucketNum, key, currentBucketValue)) { - declareAnomaly(timestampNs, metricId, key, currentBucketValue); - } -} - -bool AnomalyTracker::isInRefractoryPeriod(const int64_t& timestampNs, - const MetricDimensionKey& key) const { - const auto& it = mRefractoryPeriodEndsSec.find(key); - if (it != mRefractoryPeriodEndsSec.end()) { - return timestampNs < (it->second * (int64_t)NS_PER_SEC); - } - return false; -} - -void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id, - int64_t metricValue) { - triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions); -} - -bool AnomalyTracker::writeAlertMetadataToProto(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::AlertMetadata* alertMetadata) { - bool metadataWritten = false; - - if (mRefractoryPeriodEndsSec.empty()) { - return false; - } - - for (const auto& it: mRefractoryPeriodEndsSec) { - // Do not write the timestamp to disk if it has already expired - if (it.second < systemElapsedTimeNs / NS_PER_SEC) { - continue; - } - - metadataWritten = true; - if (alertMetadata->alert_dim_keyed_data_size() == 0) { - alertMetadata->set_alert_id(mAlert.id()); - } - - metadata::AlertDimensionKeyedData* keyedData = alertMetadata->add_alert_dim_keyed_data(); - // We convert and write the refractory_end_sec to wall clock time because we do not know - // when statsd will start again. - int32_t refractoryEndWallClockSec = (int32_t) ((currentWallClockTimeNs / NS_PER_SEC) + - (it.second - systemElapsedTimeNs / NS_PER_SEC)); - - keyedData->set_last_refractory_ends_sec(refractoryEndWallClockSec); - writeMetricDimensionKeyToMetadataDimensionKey( - it.first, keyedData->mutable_dimension_key()); - } - - return metadataWritten; -} - -void AnomalyTracker::loadAlertMetadata( - const metadata::AlertMetadata& alertMetadata, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - for (const metadata::AlertDimensionKeyedData& keyedData : - alertMetadata.alert_dim_keyed_data()) { - if ((uint64_t) keyedData.last_refractory_ends_sec() < currentWallClockTimeNs / NS_PER_SEC) { - // Do not update the timestamp if it has already expired. - continue; - } - MetricDimensionKey metricKey = loadMetricDimensionKeyFromProto( - keyedData.dimension_key()); - int32_t refractoryPeriodEndsSec = (int32_t) keyedData.last_refractory_ends_sec() - - currentWallClockTimeNs / NS_PER_SEC + systemElapsedTimeNs / NS_PER_SEC; - mRefractoryPeriodEndsSec[metricKey] = refractoryPeriodEndsSec; - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h deleted file mode 100644 index bf36a3bc8990..000000000000 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include -#include - -#include "AlarmMonitor.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert -#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata -#include "stats_util.h" // HashableDimensionKey and DimToValMap - -namespace android { -namespace os { -namespace statsd { - -using std::shared_ptr; -using std::unordered_map; - -// Does NOT allow negative values. -class AnomalyTracker : public virtual RefBase { -public: - AnomalyTracker(const Alert& alert, const ConfigKey& configKey); - - virtual ~AnomalyTracker(); - - // Add subscriptions that depend on this alert. - void addSubscription(const Subscription& subscription) { - mSubscriptions.push_back(subscription); - } - - // Adds a bucket for the given bucketNum (index starting at 0). - // If a bucket for bucketNum already exists, it will be replaced. - // Also, advances to bucketNum (if not in the past), effectively filling any intervening - // buckets with 0s. - void addPastBucket(std::shared_ptr bucket, const int64_t& bucketNum); - - // Inserts (or replaces) the bucket entry for the given bucketNum at the given key to be the - // given bucketValue. If the bucket does not exist, it will be created. - // Also, advances to bucketNum (if not in the past), effectively filling any intervening - // buckets with 0s. - void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue, - const int64_t& bucketNum); - - // Returns true if, based on past buckets plus the new currentBucketValue (which generally - // represents the partially-filled current bucket), an anomaly has happened. - // Also advances to currBucketNum-1. - bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key, - const int64_t& currentBucketValue); - - // Informs incidentd about the detected alert. - void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key, - int64_t metricValue); - - // Detects if, based on past buckets plus the new currentBucketValue (which generally - // represents the partially-filled current bucket), an anomaly has happened, and if so, - // declares an anomaly and informs relevant subscribers. - // Also advances to currBucketNum-1. - void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum, - int64_t metricId, const MetricDimensionKey& key, - const int64_t& currentBucketValue); - - // Init the AlarmMonitor which is shared across anomaly trackers. - virtual void setAlarmMonitor(const sp& alarmMonitor) { - return; // Base AnomalyTracker class has no need for the AlarmMonitor. - } - - // Returns the sum of all past bucket values for the given dimension key. - int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const; - - // Returns the value for a past bucket, or 0 if that bucket doesn't exist. - int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const; - - // Returns the anomaly threshold set in the configuration. - inline int64_t getAnomalyThreshold() const { - return mAlert.trigger_if_sum_gt(); - } - - // Returns the refractory period ending timestamp (in seconds) for the given key. - // Before this moment, any detected anomaly will be ignored. - // If there is no stored refractory period ending timestamp, returns 0. - uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const { - const auto& it = mRefractoryPeriodEndsSec.find(key); - return it != mRefractoryPeriodEndsSec.end() ? it->second : 0; - } - - // Returns the (constant) number of past buckets this anomaly tracker can store. - inline int getNumOfPastBuckets() const { - return mNumOfPastBuckets; - } - - // Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker, - // and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor. - virtual void informAlarmsFired(const int64_t& timestampNs, - unordered_set, SpHash>& firedAlarms) { - return; // The base AnomalyTracker class doesn't have alarms. - } - - // Writes metadata of the alert (refractory_period_end_sec) to AlertMetadata. - // Returns true if at least one element is written to alertMetadata. - bool writeAlertMetadataToProto( - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, metadata::AlertMetadata* alertMetadata); - - void loadAlertMetadata( - const metadata::AlertMetadata& alertMetadata, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs); - -protected: - // For testing only. - // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise - // returns 0. - virtual uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const { - return 0; // The base AnomalyTracker class doesn't have alarms. - } - - // statsd_config.proto Alert message that defines this tracker. - const Alert mAlert; - - // The subscriptions that depend on this alert. - std::vector mSubscriptions; - - // A reference to the Alert's config key. - const ConfigKey mConfigKey; - - // Number of past buckets. One less than the total number of buckets needed - // for the anomaly detection (since the current bucket is not in the past). - const int mNumOfPastBuckets; - - // Values for each of the past mNumOfPastBuckets buckets. Always of size mNumOfPastBuckets. - // mPastBuckets[i] can be null, meaning that no data is present in that bucket. - std::vector> mPastBuckets; - - // Cached sum over all existing buckets in mPastBuckets. - // Its buckets never contain entries of 0. - DimToValMap mSumOverPastBuckets; - - // The bucket number of the last added bucket. - int64_t mMostRecentBucketNum = -1; - - // Map from each dimension to the timestamp that its refractory period (if this anomaly was - // declared for that dimension) ends, in seconds. From this moment and onwards, anomalies - // can be declared again. - // Entries may be, but are not guaranteed to be, removed after the period is finished. - unordered_map mRefractoryPeriodEndsSec; - - // Advances mMostRecentBucketNum to bucketNum, deleting any data that is now too old. - // Specifically, since it is now too old, removes the data for - // [mMostRecentBucketNum - mNumOfPastBuckets + 1, bucketNum - mNumOfPastBuckets]. - void advanceMostRecentBucketTo(const int64_t& bucketNum); - - // Add the information in the given bucket to mSumOverPastBuckets. - void addBucketToSum(const shared_ptr& bucket); - - // Subtract the information in the given bucket from mSumOverPastBuckets - // and remove any items with value 0. - void subtractBucketFromSum(const shared_ptr& bucket); - - // From mSumOverPastBuckets[key], subtracts bucketValue, removing it if it is now 0. - void subtractValueFromSum(const MetricDimensionKey& key, const int64_t& bucketValue); - - // Returns true if in the refractory period, else false. - bool isInRefractoryPeriod(const int64_t& timestampNs, const MetricDimensionKey& key) const; - - // Calculates the corresponding bucket index within the circular array. - // Requires bucketNum >= 0. - size_t index(int64_t bucketNum) const; - - // Resets all bucket data. For use when all the data gets stale. - virtual void resetStorage(); - - // Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred. - void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue); - - FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets); - FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); - FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection); - FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp deleted file mode 100644 index 2b56810170e5..000000000000 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "DurationAnomalyTracker.h" -#include "guardrail/StatsdStats.h" - -namespace android { -namespace os { -namespace statsd { - -DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey, - const sp& alarmMonitor) - : AnomalyTracker(alert, configKey), mAlarmMonitor(alarmMonitor) { - VLOG("DurationAnomalyTracker() called"); -} - -DurationAnomalyTracker::~DurationAnomalyTracker() { - VLOG("~DurationAnomalyTracker() called"); - cancelAllAlarms(); -} - -void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey, - const int64_t& timestampNs) { - // Alarms are stored in secs. Must round up, since if it fires early, it is ignored completely. - uint32_t timestampSec = static_cast((timestampNs -1) / NS_PER_SEC) + 1; // round up - if (isInRefractoryPeriod(timestampNs, dimensionKey)) { - VLOG("Not setting anomaly alarm since it would fall in the refractory period."); - return; - } - - auto itr = mAlarms.find(dimensionKey); - if (itr != mAlarms.end() && mAlarmMonitor != nullptr) { - mAlarmMonitor->remove(itr->second); - } - - sp alarm = new InternalAlarm{timestampSec}; - mAlarms[dimensionKey] = alarm; - if (mAlarmMonitor != nullptr) { - mAlarmMonitor->add(alarm); - } -} - -void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey, - const int64_t& timestampNs) { - const auto itr = mAlarms.find(dimensionKey); - if (itr == mAlarms.end()) { - return; - } - - // If the alarm is set in the past but hasn't fired yet (due to lag), catch it now. - if (itr->second != nullptr && timestampNs >= (int64_t)NS_PER_SEC * itr->second->timestampSec) { - declareAnomaly(timestampNs, mAlert.metric_id(), dimensionKey, - mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - - itr->second->timestampSec); - } - if (mAlarmMonitor != nullptr) { - mAlarmMonitor->remove(itr->second); - } - mAlarms.erase(dimensionKey); -} - -void DurationAnomalyTracker::cancelAllAlarms() { - if (mAlarmMonitor != nullptr) { - for (const auto& itr : mAlarms) { - mAlarmMonitor->remove(itr.second); - } - } - mAlarms.clear(); -} - -void DurationAnomalyTracker::informAlarmsFired(const int64_t& timestampNs, - unordered_set, SpHash>& firedAlarms) { - - if (firedAlarms.empty() || mAlarms.empty()) return; - // Find the intersection of firedAlarms and mAlarms. - // The for loop is inefficient, since it loops over all keys, but that's okay since it is very - // seldomly called. The alternative would be having InternalAlarms store information about the - // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that - // is rarely ever called. - unordered_map> matchedAlarms; - for (const auto& kv : mAlarms) { - if (firedAlarms.count(kv.second) > 0) { - matchedAlarms.insert({kv.first, kv.second}); - } - } - - // Now declare each of these alarms to have fired. - for (const auto& kv : matchedAlarms) { - declareAnomaly( - timestampNs, mAlert.metric_id(), kv.first, - mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - kv.second->timestampSec); - mAlarms.erase(kv.first); - firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it. - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h deleted file mode 100644 index 686d8f95c7f6..000000000000 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "AlarmMonitor.h" -#include "AnomalyTracker.h" - -namespace android { -namespace os { -namespace statsd { - -using std::unordered_map; - -class DurationAnomalyTracker : public virtual AnomalyTracker { -public: - DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey, - const sp& alarmMonitor); - - virtual ~DurationAnomalyTracker(); - - // Sets an alarm for the given timestamp. - // Replaces previous alarm if one already exists. - void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime); - - // Stops the alarm. - // If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed), - // declare the anomaly now. - void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs); - - // Stop all the alarms owned by this tracker. Does not declare any anomalies. - void cancelAllAlarms(); - - // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker - // and removes it from firedAlarms. The AlarmMonitor is not informed. - // Note that this will generally be called from a different thread from the other functions; - // the caller is responsible for thread safety. - void informAlarmsFired(const int64_t& timestampNs, - unordered_set, SpHash>& firedAlarms) override; - -protected: - // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise - // returns 0. - uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const override { - auto it = mAlarms.find(dimensionKey); - return it == mAlarms.end() ? 0 : it->second->timestampSec; - } - - // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they - // are still active. - std::unordered_map> mAlarms; - - // Anomaly alarm monitor. - sp mAlarmMonitor; - - FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/indexed_priority_queue.h b/cmds/statsd/src/anomaly/indexed_priority_queue.h deleted file mode 100644 index 99882d0337b1..000000000000 --- a/cmds/statsd/src/anomaly/indexed_priority_queue.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -using namespace android; - -namespace android { -namespace os { -namespace statsd { - -/** Defines a hash function for sp, returning the hash of the underlying pointer. */ -template -struct SpHash { - size_t operator()(const sp& k) const { - return std::hash()(k.get()); - } -}; - -/** - * Min priority queue for generic type AA. - * Unlike a regular priority queue, this class is also capable of removing interior elements. - * @tparam Comparator must implement [bool operator()(sp a, sp b)], returning - * whether a should be closer to the top of the queue than b. - */ -template -class indexed_priority_queue { -public: - indexed_priority_queue(); - /** Adds a into the priority queue. If already present or a==nullptr, does nothing. */ - void push(sp a); - /* - * Removes a from the priority queue. If not present or a==nullptr, does nothing. - * Returns true if a had been present (and is now removed), else false. - */ - bool remove(sp a); - /** Removes the top element, if there is one. */ - void pop(); - /** Removes all elements. */ - void clear(); - /** Returns whether priority queue contains a (not just a copy of a, but a itself). */ - bool contains(sp a) const; - /** Returns min element. Returns nullptr iff empty(). */ - sp top() const; - /** Returns number of elements in priority queue. */ - size_t size() const { - return pq.size() - 1; - } // pq is 1-indexed - /** Returns true iff priority queue is empty. */ - bool empty() const { - return size() < 1; - } - -private: - /** Vector representing a min-heap (1-indexed, with nullptr at 0). */ - std::vector> pq; - /** Mapping of each element in pq to its index in pq (i.e. the inverse of a=pq[i]). */ - std::unordered_map, size_t, SpHash> indices; - - void init(); - void sift_up(size_t idx); - void sift_down(size_t idx); - /** Returns whether pq[idx1] is considered higher than pq[idx2], according to Comparator. */ - bool higher(size_t idx1, size_t idx2) const; - void swap_indices(size_t i, size_t j); -}; - -// Implementation must be done in this file due to use of template. - -template -indexed_priority_queue::indexed_priority_queue() { - init(); -} - -template -void indexed_priority_queue::push(sp a) { - if (a == nullptr) return; - if (contains(a)) return; - pq.push_back(a); - size_t idx = size(); // index of last element since 1-indexed - indices.insert({a, idx}); - sift_up(idx); // get the pq back in order -} - -template -bool indexed_priority_queue::remove(sp a) { - if (a == nullptr) return false; - if (!contains(a)) return false; - size_t idx = indices[a]; - if (idx >= pq.size()) { - return false; - } - if (idx == size()) { // if a is the last element, i.e. at index idx == size() == (pq.size()-1) - pq.pop_back(); - indices.erase(a); - return true; - } - // move last element (guaranteed not to be at idx) to idx, then delete a - sp last_a = pq.back(); - pq[idx] = last_a; - pq.pop_back(); - indices[last_a] = idx; - indices.erase(a); - - // get the heap back in order (since the element at idx is not in order) - sift_up(idx); - sift_down(idx); - - return true; -} - -// The same as, but slightly more efficient than, remove(top()). -template -void indexed_priority_queue::pop() { - sp a = top(); - if (a == nullptr) return; - const size_t idx = 1; - if (idx == size()) { // if a is the last element - pq.pop_back(); - indices.erase(a); - return; - } - // move last element (guaranteed not to be at idx) to idx, then delete a - sp last_a = pq.back(); - pq[idx] = last_a; - pq.pop_back(); - indices[last_a] = idx; - indices.erase(a); - - // get the heap back in order (since the element at idx is not in order) - sift_down(idx); -} - -template -void indexed_priority_queue::clear() { - pq.clear(); - indices.clear(); - init(); -} - -template -sp indexed_priority_queue::top() const { - if (empty()) return nullptr; - return pq[1]; -} - -template -void indexed_priority_queue::init() { - pq.push_back(nullptr); // so that pq is 1-indexed. - indices.insert({nullptr, 0}); // just to be consistent with pq. -} - -template -void indexed_priority_queue::sift_up(size_t idx) { - while (idx > 1) { - size_t parent = idx / 2; - if (higher(idx, parent)) - swap_indices(idx, parent); - else - break; - idx = parent; - } -} - -template -void indexed_priority_queue::sift_down(size_t idx) { - while (2 * idx <= size()) { - size_t child = 2 * idx; - if (child < size() && higher(child + 1, child)) child++; - if (higher(child, idx)) - swap_indices(child, idx); - else - break; - idx = child; - } -} - -template -bool indexed_priority_queue::higher(size_t idx1, size_t idx2) const { - if (!(0u < idx1 && idx1 < pq.size() && 0u < idx2 && idx2 < pq.size())) { - return false; // got to do something. - } - return Comparator()(pq[idx1], pq[idx2]); -} - -template -bool indexed_priority_queue::contains(sp a) const { - if (a == nullptr) return false; // publicly, we pretend that nullptr is not actually in pq. - return indices.count(a) > 0; -} - -template -void indexed_priority_queue::swap_indices(size_t i, size_t j) { - if (!(0u < i && i < pq.size() && 0u < j && j < pq.size())) { - return; - } - sp val_i = pq[i]; - sp val_j = pq[j]; - pq[i] = val_j; - pq[j] = val_i; - indices[val_i] = j; - indices[val_j] = i; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp deleted file mode 100644 index 5a4a41d01de6..000000000000 --- a/cmds/statsd/src/anomaly/subscriber_util.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "external/Perfetto.h" -#include "subscriber/IncidentdReporter.h" -#include "subscriber/SubscriberReporter.h" - -namespace android { -namespace os { -namespace statsd { - -void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey, - int64_t metricValue, const ConfigKey& configKey, - const std::vector& subscriptions) { - VLOG("informSubscribers called."); - if (subscriptions.empty()) { - VLOG("No Subscriptions were associated."); - return; - } - - for (const Subscription& subscription : subscriptions) { - if (subscription.probability_of_informing() < 1 - && ((float)rand() / (float)RAND_MAX) >= subscription.probability_of_informing()) { - // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always. - // The config writer was advised to use -0.1 and 1.1 for never/always. - ALOGI("Fate decided that a subscriber would not be informed."); - continue; - } - switch (subscription.subscriber_information_case()) { - case Subscription::SubscriberInformationCase::kIncidentdDetails: - if (!GenerateIncidentReport(subscription.incidentd_details(), ruleId, metricId, - dimensionKey, metricValue, configKey)) { - ALOGW("Failed to generate incident report."); - } - break; - case Subscription::SubscriberInformationCase::kPerfettoDetails: - if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(), - subscription.id(), ruleId, configKey)) { - ALOGW("Failed to generate perfetto traces."); - } - break; - case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails: - SubscriberReporter::getInstance().alertBroadcastSubscriber(configKey, subscription, - dimensionKey); - break; - default: - break; - } - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h deleted file mode 100644 index 1df3c8991f94..000000000000 --- a/cmds/statsd/src/anomaly/subscriber_util.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "config/ConfigKey.h" -#include "HashableDimensionKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -namespace android { -namespace os { -namespace statsd { - -void triggerSubscribers(const int64_t ruleId, const int64_t metricId, - const MetricDimensionKey& dimensionKey, int64_t metricValue, - const ConfigKey& configKey, const std::vector& subscriptions); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp deleted file mode 100644 index e9875baf58c7..000000000000 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" -#include "CombinationConditionTracker.h" - -namespace android { -namespace os { -namespace statsd { - -using std::unordered_map; -using std::vector; - -CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index) - : ConditionTracker(id, index) { - VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId); -} - -CombinationConditionTracker::~CombinationConditionTracker() { - VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId); -} - -bool CombinationConditionTracker::init(const vector& allConditionConfig, - const vector>& allConditionTrackers, - const unordered_map& conditionIdIndexMap, - vector& stack, - vector& initialConditionCache) { - VLOG("Combination predicate init() %lld", (long long)mConditionId); - if (mInitialized) { - return true; - } - - // mark this node as visited in the recursion stack. - stack[mIndex] = true; - - Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination(); - - if (!combinationCondition.has_operation()) { - return false; - } - mLogicalOperation = combinationCondition.operation(); - - if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) { - return false; - } - - for (auto child : combinationCondition.predicate()) { - auto it = conditionIdIndexMap.find(child); - - if (it == conditionIdIndexMap.end()) { - ALOGW("Predicate %lld not found in the config", (long long)child); - return false; - } - - int childIndex = it->second; - const auto& childTracker = allConditionTrackers[childIndex]; - // if the child is a visited node in the recursion -> circle detected. - if (stack[childIndex]) { - ALOGW("Circle detected!!!"); - return false; - } - - bool initChildSucceeded = - childTracker->init(allConditionConfig, allConditionTrackers, conditionIdIndexMap, - stack, initialConditionCache); - - if (!initChildSucceeded) { - ALOGW("Child initialization failed %lld ", (long long)child); - return false; - } else { - VLOG("Child initialization success %lld ", (long long)child); - } - - if (allConditionTrackers[childIndex]->isSliced()) { - setSliced(true); - mSlicedChildren.push_back(childIndex); - } else { - mUnSlicedChildren.push_back(childIndex); - } - mChildren.push_back(childIndex); - mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(), - childTracker->getLogTrackerIndex().end()); - } - - mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, - initialConditionCache); - initialConditionCache[mIndex] = - evaluateCombinationCondition(mChildren, mLogicalOperation, initialConditionCache); - - // unmark this node in the recursion stack. - stack[mIndex] = false; - - mInitialized = true; - - return true; -} - -void CombinationConditionTracker::isConditionMet( - const ConditionKey& conditionParameters, const vector>& allConditions, - const bool isPartialLink, - vector& conditionCache) const { - // So far, this is fine as there is at most one child having sliced output. - for (const int childIndex : mChildren) { - if (conditionCache[childIndex] == ConditionState::kNotEvaluated) { - allConditions[childIndex]->isConditionMet(conditionParameters, allConditions, - isPartialLink, - conditionCache); - } - } - conditionCache[mIndex] = - evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); -} - -void CombinationConditionTracker::evaluateCondition( - const LogEvent& event, const std::vector& eventMatcherValues, - const std::vector>& mAllConditions, - std::vector& nonSlicedConditionCache, - std::vector& conditionChangedCache) { - // value is up to date. - if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) { - return; - } - - for (const int childIndex : mChildren) { - // So far, this is fine as there is at most one child having sliced output. - if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) { - const sp& child = mAllConditions[childIndex]; - child->evaluateCondition(event, eventMatcherValues, mAllConditions, - nonSlicedConditionCache, conditionChangedCache); - } - } - - ConditionState newCondition = - evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache); - if (!mSliced) { - bool nonSlicedChanged = (mUnSlicedPartCondition != newCondition); - mUnSlicedPartCondition = newCondition; - - nonSlicedConditionCache[mIndex] = mUnSlicedPartCondition; - conditionChangedCache[mIndex] = nonSlicedChanged; - } else { - mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, - nonSlicedConditionCache); - - for (const int childIndex : mChildren) { - // If any of the sliced condition in children condition changes, the combination - // condition may be changed too. - if (conditionChangedCache[childIndex]) { - conditionChangedCache[mIndex] = true; - break; - } - } - nonSlicedConditionCache[mIndex] = newCondition; - VLOG("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId, - conditionChangedCache[mIndex] == true); - } -} - -bool CombinationConditionTracker::equalOutputDimensions( - const std::vector>& allConditions, - const vector& dimensions) const { - if (mSlicedChildren.size() != 1 || - mSlicedChildren.front() >= (int)allConditions.size() || - mLogicalOperation != LogicalOperation::AND) { - return false; - } - const sp& slicedChild = allConditions.at(mSlicedChildren.front()); - return slicedChild->equalOutputDimensions(allConditions, dimensions); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h deleted file mode 100644 index 39ff0ab03266..000000000000 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef COMBINATION_CONDITION_TRACKER_H -#define COMBINATION_CONDITION_TRACKER_H - -#include "ConditionTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -namespace android { -namespace os { -namespace statsd { - -class CombinationConditionTracker : public virtual ConditionTracker { -public: - CombinationConditionTracker(const int64_t& id, const int index); - - ~CombinationConditionTracker(); - - bool init(const std::vector& allConditionConfig, - const std::vector>& allConditionTrackers, - const std::unordered_map& conditionIdIndexMap, std::vector& stack, - std::vector& initialConditionCache) override; - - void evaluateCondition(const LogEvent& event, - const std::vector& eventMatcherValues, - const std::vector>& mAllConditions, - std::vector& conditionCache, - std::vector& changedCache) override; - - void isConditionMet(const ConditionKey& conditionParameters, - const std::vector>& allConditions, - const bool isPartialLink, - std::vector& conditionCache) const override; - - // Only one child predicate can have dimension. - const std::set* getChangedToTrueDimensions( - const std::vector>& allConditions) const override { - for (const auto& child : mChildren) { - auto result = allConditions[child]->getChangedToTrueDimensions(allConditions); - if (result != nullptr) { - return result; - } - } - return nullptr; - } - - // Only one child predicate can have dimension. - const std::set* getChangedToFalseDimensions( - const std::vector>& allConditions) const override { - for (const auto& child : mChildren) { - auto result = allConditions[child]->getChangedToFalseDimensions(allConditions); - if (result != nullptr) { - return result; - } - } - return nullptr; - } - - bool IsSimpleCondition() const override { return false; } - - bool IsChangedDimensionTrackable() const override { - return mLogicalOperation == LogicalOperation::AND && mSlicedChildren.size() == 1; - } - - bool equalOutputDimensions( - const std::vector>& allConditions, - const vector& dimensions) const override; - - void getTrueSlicedDimensions( - const std::vector>& allConditions, - std::set* dimensions) const override { - if (mSlicedChildren.size() == 1) { - return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions( - allConditions, dimensions); - } - } - - -private: - LogicalOperation mLogicalOperation; - - // Store index of the children Predicates. - // We don't store string name of the Children, because we want to get rid of the hash map to - // map the name to object. We don't want to store smart pointers to children, because it - // increases the risk of circular dependency and memory leak. - std::vector mChildren; - - std::vector mSlicedChildren; - std::vector mUnSlicedChildren; - -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // COMBINATION_CONDITION_TRACKER_H diff --git a/cmds/statsd/src/condition/ConditionTimer.h b/cmds/statsd/src/condition/ConditionTimer.h deleted file mode 100644 index 442bc11934fe..000000000000 --- a/cmds/statsd/src/condition/ConditionTimer.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include - -namespace android { -namespace os { -namespace statsd { - -/** - * A simple stopwatch to time the duration of condition being true. - * - * The owner of the stopwatch (MetricProducer) is responsible to notify the stopwatch when condition - * changes (start/pause), and when to start a new bucket (a new lap basically). All timestamps - * should be elapsedRealTime in nano seconds. - * - * Keep the timer simple and inline everything. This class is *NOT* thread safe. Caller is - * responsible for thread safety. - */ -class ConditionTimer { -public: - explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) { - if (initCondition) { - mLastConditionTrueTimestampNs = bucketStartNs; - } - }; - - // Tracks how long the condition has been stayed true in the *current* bucket. - // When a new bucket is created, this value will be reset to 0. - int64_t mTimerNs = 0; - - // Last elapsed real timestamp when condition turned to true - // When a new bucket is created and the condition is true, then the timestamp is set - // to be the bucket start timestamp. - int64_t mLastConditionTrueTimestampNs = 0; - - bool mCondition = false; - - int64_t newBucketStart(int64_t nextBucketStartNs) { - if (mCondition) { - mTimerNs += (nextBucketStartNs - mLastConditionTrueTimestampNs); - mLastConditionTrueTimestampNs = nextBucketStartNs; - } - - int64_t temp = mTimerNs; - mTimerNs = 0; - return temp; - } - - void onConditionChanged(bool newCondition, int64_t timestampNs) { - if (newCondition == mCondition) { - return; - } - mCondition = newCondition; - if (newCondition) { - mLastConditionTrueTimestampNs = timestampNs; - } else { - mTimerNs += (timestampNs - mLastConditionTrueTimestampNs); - } - } - - FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False); - FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_True); -}; - -} // namespace statsd -} // namespace os -} // namespace android \ No newline at end of file diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h deleted file mode 100644 index 62736c8160bb..000000000000 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "condition/condition_util.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/LogMatchingTracker.h" -#include "matchers/matcher_util.h" - -#include - -#include - -namespace android { -namespace os { -namespace statsd { - -class ConditionTracker : public virtual RefBase { -public: - ConditionTracker(const int64_t& id, const int index) - : mConditionId(id), - mIndex(index), - mInitialized(false), - mTrackerIndex(), - mUnSlicedPartCondition(ConditionState::kUnknown), - mSliced(false){}; - - virtual ~ConditionTracker(){}; - - inline const int64_t& getId() { return mConditionId; } - - // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also - // be done in the constructor, but we do it separately because (1) easy to return a bool to - // indicate whether the initialization is successful. (2) makes unit test easier. - // allConditionConfig: the list of all Predicate config from statsd_config. - // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also - // need to call init() on children conditions) - // conditionIdIndexMap: the mapping from condition id to its index. - // stack: a bit map to keep track which nodes have been visited on the stack in the recursion. - // initialConditionCache: tracks initial conditions of all ConditionTrackers. - virtual bool init(const std::vector& allConditionConfig, - const std::vector>& allConditionTrackers, - const std::unordered_map& conditionIdIndexMap, - std::vector& stack, - std::vector& initialConditionCache) = 0; - - // evaluate current condition given the new event. - // event: the new log event - // eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process - // event before ConditionTrackers, because ConditionTracker depends on - // LogMatchingTrackers. - // mAllConditions: the list of all ConditionTracker - // conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event. - // conditionChanged: the bit map to record whether the condition has changed. - // If the condition has dimension, then any sub condition changes will report - // conditionChanged. - virtual void evaluateCondition(const LogEvent& event, - const std::vector& eventMatcherValues, - const std::vector>& mAllConditions, - std::vector& conditionCache, - std::vector& conditionChanged) = 0; - - // Query the condition with parameters. - // [conditionParameters]: a map from condition name to the HashableDimensionKey to query the - // condition. - // [allConditions]: all condition trackers. This is needed because the condition evaluation is - // done recursively - // [isPartialLink]: true if the link specified by 'conditionParameters' contains all the fields - // in the condition tracker output dimension. - // [conditionCache]: the cache holding the condition evaluation values. - virtual void isConditionMet( - const ConditionKey& conditionParameters, - const std::vector>& allConditions, - const bool isPartialLink, - std::vector& conditionCache) const = 0; - - // return the list of LogMatchingTracker index that this ConditionTracker uses. - virtual const std::set& getLogTrackerIndex() const { - return mTrackerIndex; - } - - virtual void setSliced(bool sliced) { - mSliced = mSliced | sliced; - } - - inline bool isSliced() const { - return mSliced; - } - - virtual const std::set* getChangedToTrueDimensions( - const std::vector>& allConditions) const = 0; - virtual const std::set* getChangedToFalseDimensions( - const std::vector>& allConditions) const = 0; - - inline int64_t getConditionId() const { - return mConditionId; - } - - virtual void getTrueSlicedDimensions( - const std::vector>& allConditions, - std::set* dimensions) const = 0; - - virtual bool IsChangedDimensionTrackable() const = 0; - - virtual bool IsSimpleCondition() const = 0; - - virtual bool equalOutputDimensions( - const std::vector>& allConditions, - const vector& dimensions) const = 0; - - // Return the current condition state of the unsliced part of the condition. - inline ConditionState getUnSlicedPartConditionState() const { - return mUnSlicedPartCondition; - } - -protected: - const int64_t mConditionId; - - // the index of this condition in the manager's condition list. - const int mIndex; - - // if it's properly initialized. - bool mInitialized; - - // the list of LogMatchingTracker index that this ConditionTracker uses. - std::set mTrackerIndex; - - // This variable is only used for CombinationConditionTrackers. - // SimpleConditionTrackers technically don't have an unsliced part because - // they are either sliced or unsliced. - // - // CombinationConditionTrackers have multiple children ConditionTrackers - // that can be a mixture of sliced or unsliced. This tracks the - // condition of the unsliced part of the combination condition. - ConditionState mUnSlicedPartCondition; - - bool mSliced; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp deleted file mode 100644 index c542032b48ea..000000000000 --- a/cmds/statsd/src/condition/ConditionWizard.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ConditionWizard.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters, - const bool isPartialLink) { - vector cache(mAllConditions.size(), ConditionState::kNotEvaluated); - - mAllConditions[index]->isConditionMet( - parameters, mAllConditions, isPartialLink, - cache); - return cache[index]; -} - -const set* ConditionWizard::getChangedToTrueDimensions( - const int index) const { - return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions); -} - -const set* ConditionWizard::getChangedToFalseDimensions( - const int index) const { - return mAllConditions[index]->getChangedToFalseDimensions(mAllConditions); -} - -bool ConditionWizard::IsChangedDimensionTrackable(const int index) { - if (index >= 0 && index < (int)mAllConditions.size()) { - return mAllConditions[index]->IsChangedDimensionTrackable(); - } else { - return false; - } -} - -bool ConditionWizard::IsSimpleCondition(const int index) { - if (index >= 0 && index < (int)mAllConditions.size()) { - return mAllConditions[index]->IsSimpleCondition(); - } else { - return false; - } -} - -bool ConditionWizard::equalOutputDimensions(const int index, const vector& dimensions) { - if (index >= 0 && index < (int)mAllConditions.size()) { - return mAllConditions[index]->equalOutputDimensions(mAllConditions, dimensions); - } else { - return false; - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h deleted file mode 100644 index 892647910d9f..000000000000 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CONDITION_WIZARD_H -#define CONDITION_WIZARD_H - -#include "ConditionTracker.h" -#include "condition_util.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -// Held by MetricProducer, to query a condition state with input defined in MetricConditionLink. -class ConditionWizard : public virtual android::RefBase { -public: - ConditionWizard(){}; // for testing - explicit ConditionWizard(std::vector>& conditionTrackers) - : mAllConditions(conditionTrackers){}; - - virtual ~ConditionWizard(){}; - - // Query condition state, for a ConditionTracker at [conditionIndex], with [conditionParameters] - // [conditionParameters] mapping from condition name to the HashableDimensionKey to query the - // condition. - // The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case, - // the conditionParameters contains the parameters for it's children SimpleConditionTrackers. - virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters, - const bool isPartialLink); - - virtual const std::set* getChangedToTrueDimensions(const int index) const; - virtual const std::set* getChangedToFalseDimensions( - const int index) const; - bool equalOutputDimensions(const int index, const vector& dimensions); - - bool IsChangedDimensionTrackable(const int index); - bool IsSimpleCondition(const int index); - - ConditionState getUnSlicedPartConditionState(const int index) { - return mAllConditions[index]->getUnSlicedPartConditionState(); - } - void getTrueSlicedDimensions(const int index, - std::set* trueDimensions) const { - return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions); - } - -private: - std::vector> mAllConditions; -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // CONDITION_WIZARD_H diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp deleted file mode 100644 index efb4d4989425..000000000000 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "SimpleConditionTracker.h" -#include "guardrail/StatsdStats.h" - -namespace android { -namespace os { -namespace statsd { - -using std::unordered_map; - -SimpleConditionTracker::SimpleConditionTracker( - const ConfigKey& key, const int64_t& id, const int index, - const SimplePredicate& simplePredicate, - const unordered_map& trackerNameIndexMap) - : ConditionTracker(id, index), mConfigKey(key), mContainANYPositionInInternalDimensions(false) { - VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId); - mCountNesting = simplePredicate.count_nesting(); - - if (simplePredicate.has_start()) { - auto pair = trackerNameIndexMap.find(simplePredicate.start()); - if (pair == trackerNameIndexMap.end()) { - ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start()); - return; - } - mStartLogMatcherIndex = pair->second; - mTrackerIndex.insert(mStartLogMatcherIndex); - } else { - mStartLogMatcherIndex = -1; - } - - if (simplePredicate.has_stop()) { - auto pair = trackerNameIndexMap.find(simplePredicate.stop()); - if (pair == trackerNameIndexMap.end()) { - ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop()); - return; - } - mStopLogMatcherIndex = pair->second; - mTrackerIndex.insert(mStopLogMatcherIndex); - } else { - mStopLogMatcherIndex = -1; - } - - if (simplePredicate.has_stop_all()) { - auto pair = trackerNameIndexMap.find(simplePredicate.stop_all()); - if (pair == trackerNameIndexMap.end()) { - ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all()); - return; - } - mStopAllLogMatcherIndex = pair->second; - mTrackerIndex.insert(mStopAllLogMatcherIndex); - } else { - mStopAllLogMatcherIndex = -1; - } - - if (simplePredicate.has_dimensions()) { - translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions); - if (mOutputDimensions.size() > 0) { - mSliced = true; - mDimensionTag = mOutputDimensions[0].mMatcher.getTag(); - } - mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions()); - } - - if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) { - mInitialValue = ConditionState::kFalse; - } else { - mInitialValue = ConditionState::kUnknown; - } - - mInitialized = true; -} - -SimpleConditionTracker::~SimpleConditionTracker() { - VLOG("~SimpleConditionTracker()"); -} - -bool SimpleConditionTracker::init(const vector& allConditionConfig, - const vector>& allConditionTrackers, - const unordered_map& conditionIdIndexMap, - vector& stack, - vector& initialConditionCache) { - // SimpleConditionTracker does not have dependency on other conditions, thus we just return - // if the initialization was successful. - initialConditionCache[mIndex] = mInitialValue; - return mInitialized; -} - -void SimpleConditionTracker::dumpState() { - VLOG("%lld DUMP:", (long long)mConditionId); - for (const auto& pair : mSlicedConditionState) { - VLOG("\t%s : %d", pair.first.toString().c_str(), pair.second); - } - - VLOG("Changed to true keys: \n"); - for (const auto& key : mLastChangedToTrueDimensions) { - VLOG("%s", key.toString().c_str()); - } - VLOG("Changed to false keys: \n"); - for (const auto& key : mLastChangedToFalseDimensions) { - VLOG("%s", key.toString().c_str()); - } -} - -void SimpleConditionTracker::handleStopAll(std::vector& conditionCache, - std::vector& conditionChangedCache) { - // Unless the default condition is false, and there was nothing started, otherwise we have - // triggered a condition change. - conditionChangedCache[mIndex] = - (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false - : true; - - for (const auto& cond : mSlicedConditionState) { - if (cond.second > 0) { - mLastChangedToFalseDimensions.insert(cond.first); - } - } - - // After StopAll, we know everything has stopped. From now on, default condition is false. - mInitialValue = ConditionState::kFalse; - mSlicedConditionState.clear(); - conditionCache[mIndex] = ConditionState::kFalse; -} - -bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) { - if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) { - // if the condition is not sliced or the key is not new, we are good! - return false; - } - // 1. Report the tuple count if the tuple count > soft limit - if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mSlicedConditionState.size() + 1; - StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("Predicate %lld dropping data for dimension key %s", - (long long)mConditionId, newKey.toString().c_str()); - return true; - } - } - return false; -} - -void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey, - bool matchStart, ConditionState* conditionCache, - bool* conditionChangedCache) { - bool changed = false; - auto outputIt = mSlicedConditionState.find(outputKey); - ConditionState newCondition; - if (hitGuardRail(outputKey)) { - (*conditionChangedCache) = false; - // Tells the caller it's evaluated. - (*conditionCache) = ConditionState::kUnknown; - return; - } - if (outputIt == mSlicedConditionState.end()) { - // We get a new output key. - newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse; - if (matchStart && mInitialValue != ConditionState::kTrue) { - mSlicedConditionState[outputKey] = 1; - changed = true; - mLastChangedToTrueDimensions.insert(outputKey); - } else if (mInitialValue != ConditionState::kFalse) { - // it's a stop and we don't have history about it. - // If the default condition is not false, it means this stop is valuable to us. - mSlicedConditionState[outputKey] = 0; - mLastChangedToFalseDimensions.insert(outputKey); - changed = true; - } - } else { - // we have history about this output key. - auto& startedCount = outputIt->second; - // assign the old value first. - newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse; - if (matchStart) { - if (startedCount == 0) { - mLastChangedToTrueDimensions.insert(outputKey); - // This condition for this output key will change from false -> true - changed = true; - } - - // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated - // as 1 if not counting nesting. - startedCount++; - newCondition = ConditionState::kTrue; - } else { - // This is a stop event. - if (startedCount > 0) { - if (mCountNesting) { - startedCount--; - if (startedCount == 0) { - newCondition = ConditionState::kFalse; - } - } else { - // not counting nesting, so ignore the number of starts, stop now. - startedCount = 0; - newCondition = ConditionState::kFalse; - } - // if everything has stopped for this output key, condition true -> false; - if (startedCount == 0) { - mLastChangedToFalseDimensions.insert(outputKey); - changed = true; - } - } - - // if default condition is false, it means we don't need to keep the false values. - if (mInitialValue == ConditionState::kFalse && startedCount == 0) { - mSlicedConditionState.erase(outputIt); - VLOG("erase key %s", outputKey.toString().c_str()); - } - } - } - - // dump all dimensions for debugging - if (DEBUG) { - dumpState(); - } - - (*conditionChangedCache) = changed; - (*conditionCache) = newCondition; - - VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId, - conditionChangedCache[mIndex] == true); -} - -void SimpleConditionTracker::evaluateCondition( - const LogEvent& event, - const vector& eventMatcherValues, - const vector>& mAllConditions, - vector& conditionCache, - vector& conditionChangedCache) { - if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { - // it has been evaluated. - VLOG("Yes, already evaluated, %lld %d", - (long long)mConditionId, conditionCache[mIndex]); - return; - } - mLastChangedToTrueDimensions.clear(); - mLastChangedToFalseDimensions.clear(); - - if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) && - eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) { - handleStopAll(conditionCache, conditionChangedCache); - return; - } - - int matchedState = -1; - // Note: The order to evaluate the following start, stop, stop_all matters. - // The priority of overwrite is stop_all > stop > start. - if (mStartLogMatcherIndex >= 0 && - eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) { - matchedState = 1; - } - - if (mStopLogMatcherIndex >= 0 && - eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) { - matchedState = 0; - } - - if (matchedState < 0) { - // The event doesn't match this condition. So we just report existing condition values. - conditionChangedCache[mIndex] = false; - if (mSliced) { - // if the condition result is sliced. The overall condition is true if any of the sliced - // condition is true - conditionCache[mIndex] = mInitialValue; - for (const auto& slicedCondition : mSlicedConditionState) { - if (slicedCondition.second > 0) { - conditionCache[mIndex] = ConditionState::kTrue; - break; - } - } - } else { - const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY); - if (itr == mSlicedConditionState.end()) { - // condition not sliced, but we haven't seen the matched start or stop yet. so - // return initial value. - conditionCache[mIndex] = mInitialValue; - } else { - // return the cached condition. - conditionCache[mIndex] = - itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - } - } - return; - } - - ConditionState overallState = mInitialValue; - bool overallChanged = false; - - if (mOutputDimensions.size() == 0) { - handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState, - &overallChanged); - } else if (!mContainANYPositionInInternalDimensions) { - HashableDimensionKey outputValue; - filterValues(mOutputDimensions, event.getValues(), &outputValue); - - // If this event has multiple nodes in the attribution chain, this log event probably will - // generate multiple dimensions. If so, we will find if the condition changes for any - // dimension and ask the corresponding metric producer to verify whether the actual sliced - // condition has changed or not. - // A high level assumption is that a predicate is either sliced or unsliced. We will never - // have both sliced and unsliced version of a predicate. - handleConditionEvent(outputValue, matchedState == 1, &overallState, &overallChanged); - } else { - ALOGE("The condition tracker should not be sliced by ANY position matcher."); - } - conditionCache[mIndex] = overallState; - conditionChangedCache[mIndex] = overallChanged; -} - -void SimpleConditionTracker::isConditionMet( - const ConditionKey& conditionParameters, const vector>& allConditions, - const bool isPartialLink, - vector& conditionCache) const { - - if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { - // it has been evaluated. - VLOG("Yes, already evaluated, %lld %d", - (long long)mConditionId, conditionCache[mIndex]); - return; - } - const auto pair = conditionParameters.find(mConditionId); - - if (pair == conditionParameters.end()) { - ConditionState conditionState = ConditionState::kNotEvaluated; - conditionState = conditionState | mInitialValue; - if (!mSliced) { - const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY); - if (itr != mSlicedConditionState.end()) { - ConditionState sliceState = - itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - conditionState = conditionState | sliceState; - } - } - conditionCache[mIndex] = conditionState; - return; - } - - ConditionState conditionState = ConditionState::kNotEvaluated; - const HashableDimensionKey& key = pair->second; - if (isPartialLink) { - // For unseen key, check whether the require dimensions are subset of sliced condition - // output. - conditionState = conditionState | mInitialValue; - for (const auto& slice : mSlicedConditionState) { - ConditionState sliceState = - slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - if (slice.first.contains(key)) { - conditionState = conditionState | sliceState; - } - } - } else { - auto startedCountIt = mSlicedConditionState.find(key); - conditionState = conditionState | mInitialValue; - if (startedCountIt != mSlicedConditionState.end()) { - ConditionState sliceState = - startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - conditionState = conditionState | sliceState; - } - - } - conditionCache[mIndex] = conditionState; - VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h deleted file mode 100644 index ea7f87bde2b8..000000000000 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIMPLE_CONDITION_TRACKER_H -#define SIMPLE_CONDITION_TRACKER_H - -#include -#include "ConditionTracker.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -class SimpleConditionTracker : public virtual ConditionTracker { -public: - SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const int index, - const SimplePredicate& simplePredicate, - const std::unordered_map& trackerNameIndexMap); - - ~SimpleConditionTracker(); - - bool init(const std::vector& allConditionConfig, - const std::vector>& allConditionTrackers, - const std::unordered_map& conditionIdIndexMap, std::vector& stack, - std::vector& initialConditionCache) override; - - void evaluateCondition(const LogEvent& event, - const std::vector& eventMatcherValues, - const std::vector>& mAllConditions, - std::vector& conditionCache, - std::vector& changedCache) override; - - void isConditionMet(const ConditionKey& conditionParameters, - const std::vector>& allConditions, - const bool isPartialLink, - std::vector& conditionCache) const override; - - virtual const std::set* getChangedToTrueDimensions( - const std::vector>& allConditions) const { - if (mSliced) { - return &mLastChangedToTrueDimensions; - } else { - return nullptr; - } - } - - virtual const std::set* getChangedToFalseDimensions( - const std::vector>& allConditions) const { - if (mSliced) { - return &mLastChangedToFalseDimensions; - } else { - return nullptr; - } - } - - void getTrueSlicedDimensions( - const std::vector>& allConditions, - std::set* dimensions) const override { - for (const auto& itr : mSlicedConditionState) { - if (itr.second > 0) { - dimensions->insert(itr.first); - } - } - } - - bool IsChangedDimensionTrackable() const override { return true; } - - bool IsSimpleCondition() const override { return true; } - - bool equalOutputDimensions( - const std::vector>& allConditions, - const vector& dimensions) const override { - return equalDimensions(mOutputDimensions, dimensions); - } - -private: - const ConfigKey mConfigKey; - // The index of the LogEventMatcher which defines the start. - int mStartLogMatcherIndex; - - // The index of the LogEventMatcher which defines the end. - int mStopLogMatcherIndex; - - // if the start end needs to be nested. - bool mCountNesting; - - // The index of the LogEventMatcher which defines the stop all. - int mStopAllLogMatcherIndex; - - ConditionState mInitialValue; - - std::vector mOutputDimensions; - - bool mContainANYPositionInInternalDimensions; - - std::set mLastChangedToTrueDimensions; - std::set mLastChangedToFalseDimensions; - - int mDimensionTag; - - std::map mSlicedConditionState; - - void handleStopAll(std::vector& conditionCache, - std::vector& changedCache); - - void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart, - ConditionState* conditionCache, bool* changedCache); - - bool hitGuardRail(const HashableDimensionKey& newKey); - - void dumpState(); - - FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition); - FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim); - FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // SIMPLE_CONDITION_TRACKER_H diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp deleted file mode 100644 index 60b8c53e91e1..000000000000 --- a/cmds/statsd/src/condition/condition_util.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Log.h" - -#include "condition_util.h" - -#include "../matchers/matcher_util.h" -#include "ConditionTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - - -ConditionState evaluateCombinationCondition(const std::vector& children, - const LogicalOperation& operation, - const std::vector& conditionCache) { - ConditionState newCondition; - - bool hasUnknown = false; - bool hasFalse = false; - bool hasTrue = false; - - for (auto childIndex : children) { - ConditionState childState = conditionCache[childIndex]; - if (childState == ConditionState::kUnknown) { - hasUnknown = true; - break; - } - if (childState == ConditionState::kFalse) { - hasFalse = true; - } - if (childState == ConditionState::kTrue) { - hasTrue = true; - } - } - - // If any child condition is in unknown state, the condition is unknown too. - if (hasUnknown) { - return ConditionState::kUnknown; - } - - switch (operation) { - case LogicalOperation::AND: { - newCondition = hasFalse ? ConditionState::kFalse : ConditionState::kTrue; - break; - } - case LogicalOperation::OR: { - newCondition = hasTrue ? ConditionState::kTrue : ConditionState::kFalse; - break; - } - case LogicalOperation::NOT: - newCondition = children.empty() ? ConditionState::kUnknown : - ((conditionCache[children[0]] == ConditionState::kFalse) ? - ConditionState::kTrue : ConditionState::kFalse); - break; - case LogicalOperation::NAND: - newCondition = hasFalse ? ConditionState::kTrue : ConditionState::kFalse; - break; - case LogicalOperation::NOR: - newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue; - break; - case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED: - newCondition = ConditionState::kFalse; - break; - } - return newCondition; -} - -ConditionState operator|(ConditionState l, ConditionState r) { - return l >= r ? l : r; -} -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h deleted file mode 100644 index fed90ec3da37..000000000000 --- a/cmds/statsd/src/condition/condition_util.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CONDITION_UTIL_H -#define CONDITION_UTIL_H - -#include -#include "../matchers/matcher_util.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -namespace android { -namespace os { -namespace statsd { - -enum ConditionState { - kNotEvaluated = -2, - kUnknown = -1, - kFalse = 0, - kTrue = 1, -}; - -ConditionState operator|(ConditionState l, ConditionState r); - -ConditionState evaluateCombinationCondition(const std::vector& children, - const LogicalOperation& operation, - const std::vector& conditionCache); -} // namespace statsd -} // namespace os -} // namespace android -#endif // CONDITION_UTIL_H diff --git a/cmds/statsd/src/config/ConfigKey.cpp b/cmds/statsd/src/config/ConfigKey.cpp deleted file mode 100644 index 4a2bd2799df8..000000000000 --- a/cmds/statsd/src/config/ConfigKey.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "config/ConfigKey.h" - -namespace android { -namespace os { -namespace statsd { - -ConfigKey::ConfigKey() { -} - -ConfigKey::ConfigKey(const ConfigKey& that) : mId(that.mId), mUid(that.mUid) { -} - -ConfigKey::ConfigKey(int uid, const int64_t& id) : mId(id), mUid(uid) { -} - -ConfigKey::~ConfigKey() { -} - -string ConfigKey::ToString() const { - string s; - s += "(" + std::to_string(mUid) + " " + std::to_string(mId) + ")"; - return s; -} - - -int64_t StrToInt64(const string& str) { - char* endp; - int64_t value; - value = strtoll(str.c_str(), &endp, 0); - if (endp == str.c_str() || *endp != '\0') { - value = 0; - } - return value; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h deleted file mode 100644 index 4cc9393fbd02..000000000000 --- a/cmds/statsd/src/config/ConfigKey.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -using std::hash; -using std::string; - -/** - * Uniquely identifies a configuration. - */ -class ConfigKey { -public: - ConfigKey(); - ConfigKey(const ConfigKey& that); - ConfigKey(int uid, const int64_t& id); - ~ConfigKey(); - - inline int GetUid() const { - return mUid; - } - inline const int64_t& GetId() const { - return mId; - } - - inline bool operator<(const ConfigKey& that) const { - if (mUid < that.mUid) { - return true; - } - if (mUid > that.mUid) { - return false; - } - return mId < that.mId; - }; - - inline bool operator==(const ConfigKey& that) const { - return mUid == that.mUid && mId == that.mId; - }; - - string ToString() const; - -private: - int64_t mId; - int mUid; -}; - -int64_t StrToInt64(const string& str); - -} // namespace statsd -} // namespace os -} // namespace android - -/** - * A hash function for ConfigKey so it can be used for unordered_map/set. - * Unfortunately this has to go in std namespace because C++ is fun! - */ -namespace std { - -using android::os::statsd::ConfigKey; - -template <> -struct hash { - std::size_t operator()(const ConfigKey& key) const { - return (7 * key.GetUid()) ^ ((hash()(key.GetId()))); - } -}; - -} // namespace std diff --git a/cmds/statsd/src/config/ConfigListener.cpp b/cmds/statsd/src/config/ConfigListener.cpp deleted file mode 100644 index 21a3f1673fd7..000000000000 --- a/cmds/statsd/src/config/ConfigListener.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "config/ConfigListener.h" - -namespace android { -namespace os { -namespace statsd { - -ConfigListener::ConfigListener() { -} - -ConfigListener::~ConfigListener() { -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/config/ConfigListener.h b/cmds/statsd/src/config/ConfigListener.h deleted file mode 100644 index dcd5e52feefd..000000000000 --- a/cmds/statsd/src/config/ConfigListener.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "config/ConfigKey.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -using android::RefBase; - -/** - * Callback for different subsystems inside statsd to implement to find out - * when a configuration has been added, updated or removed. - */ -class ConfigListener : public virtual RefBase { -public: - ConfigListener(); - virtual ~ConfigListener(); - - /** - * A configuration was added or updated. - */ - virtual void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config) = 0; - - /** - * A configuration was removed. - */ - virtual void OnConfigRemoved(const ConfigKey& key) = 0; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp deleted file mode 100644 index 13020e06dc5d..000000000000 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "config/ConfigManager.h" -#include "storage/StorageManager.h" - -#include "guardrail/StatsdStats.h" -#include "stats_log_util.h" -#include "stats_util.h" -#include "stats_log_util.h" - -#include -#include -#include "android-base/stringprintf.h" - -namespace android { -namespace os { -namespace statsd { - -using std::pair; -using std::string; -using std::vector; - -#define STATS_SERVICE_DIR "/data/misc/stats-service" - -using android::base::StringPrintf; -using std::unique_ptr; - -struct ConfigReceiverDeathCookie { - ConfigReceiverDeathCookie(const wp& configManager, const ConfigKey& configKey, - const shared_ptr& pir) : - mConfigManager(configManager), mConfigKey(configKey), mPir(pir) { - } - - wp mConfigManager; - ConfigKey mConfigKey; - shared_ptr mPir; -}; - -void ConfigManager::configReceiverDied(void* cookie) { - auto cookie_ = static_cast(cookie); - sp thiz = cookie_->mConfigManager.promote(); - if (!thiz) { - return; - } - - ConfigKey& configKey = cookie_->mConfigKey; - shared_ptr& pir = cookie_->mPir; - - // Erase the mapping from the config key to the config receiver (pir) if the - // mapping still exists. - lock_guard lock(thiz->mMutex); - auto it = thiz->mConfigReceivers.find(configKey); - if (it != thiz->mConfigReceivers.end() && it->second == pir) { - thiz->mConfigReceivers.erase(configKey); - } - - // The death recipient corresponding to this specific pir can never be - // triggered again, so free up resources. - delete cookie_; -} - -struct ActiveConfigChangedReceiverDeathCookie { - ActiveConfigChangedReceiverDeathCookie(const wp& configManager, const int uid, - const shared_ptr& pir) : - mConfigManager(configManager), mUid(uid), mPir(pir) { - } - - wp mConfigManager; - int mUid; - shared_ptr mPir; -}; - -void ConfigManager::activeConfigChangedReceiverDied(void* cookie) { - auto cookie_ = static_cast(cookie); - sp thiz = cookie_->mConfigManager.promote(); - if (!thiz) { - return; - } - - int uid = cookie_->mUid; - shared_ptr& pir = cookie_->mPir; - - // Erase the mapping from the config key to the active config changed - // receiver (pir) if the mapping still exists. - lock_guard lock(thiz->mMutex); - auto it = thiz->mActiveConfigsChangedReceivers.find(uid); - if (it != thiz->mActiveConfigsChangedReceivers.end() && it->second == pir) { - thiz->mActiveConfigsChangedReceivers.erase(uid); - } - - // The death recipient corresponding to this specific pir can never - // be triggered again, so free up resources. - delete cookie_; -} - -ConfigManager::ConfigManager() : - mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)), - mActiveConfigChangedReceiverDeathRecipient( - AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) { -} - -ConfigManager::~ConfigManager() { -} - -void ConfigManager::Startup() { - map configsFromDisk; - StorageManager::readConfigFromDisk(configsFromDisk); - for (const auto& pair : configsFromDisk) { - UpdateConfig(pair.first, pair.second); - } -} - -void ConfigManager::StartupForTest() { - // No-op function to avoid reading configs from disks for tests. -} - -void ConfigManager::AddListener(const sp& listener) { - lock_guard lock(mMutex); - mListeners.push_back(listener); -} - -void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& config) { - vector> broadcastList; - { - lock_guard lock(mMutex); - - const int numBytes = config.ByteSize(); - vector buffer(numBytes); - config.SerializeToArray(&buffer[0], numBytes); - - auto uidIt = mConfigs.find(key.GetUid()); - // GuardRail: Limit the number of configs per uid. - if (uidIt != mConfigs.end()) { - auto it = uidIt->second.find(key); - if (it == uidIt->second.end() && - uidIt->second.size() >= StatsdStats::kMaxConfigCountPerUid) { - ALOGE("ConfigManager: uid %d has exceeded the config count limit", key.GetUid()); - return; - } - } - - // Check if it's a duplicate config. - if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end() && - StorageManager::hasIdenticalConfig(key, buffer)) { - // This is a duplicate config. - ALOGI("ConfigManager This is a duplicate config %s", key.ToString().c_str()); - // Update saved file on disk. We still update timestamp of file when - // there exists a duplicate configuration to avoid garbage collection. - update_saved_configs_locked(key, buffer, numBytes); - return; - } - - // Update saved file on disk. - update_saved_configs_locked(key, buffer, numBytes); - - // Add to set. - mConfigs[key.GetUid()].insert(key); - - for (const sp& listener : mListeners) { - broadcastList.push_back(listener); - } - } - - const int64_t timestampNs = getElapsedRealtimeNs(); - // Tell everyone - for (const sp& listener : broadcastList) { - listener->OnConfigUpdated(timestampNs, key, config); - } -} - -void ConfigManager::SetConfigReceiver(const ConfigKey& key, - const shared_ptr& pir) { - lock_guard lock(mMutex); - mConfigReceivers[key] = pir; - AIBinder_linkToDeath(pir->asBinder().get(), mConfigReceiverDeathRecipient.get(), - new ConfigReceiverDeathCookie(this, key, pir)); -} - -void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) { - lock_guard lock(mMutex); - mConfigReceivers.erase(key); -} - -void ConfigManager::SetActiveConfigsChangedReceiver(const int uid, - const shared_ptr& pir) { - { - lock_guard lock(mMutex); - mActiveConfigsChangedReceivers[uid] = pir; - } - AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(), - new ActiveConfigChangedReceiverDeathCookie(this, uid, pir)); -} - -void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) { - lock_guard lock(mMutex); - mActiveConfigsChangedReceivers.erase(uid); -} - -void ConfigManager::RemoveConfig(const ConfigKey& key) { - vector> broadcastList; - { - lock_guard lock(mMutex); - - auto uid = key.GetUid(); - auto uidIt = mConfigs.find(uid); - if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) { - // Remove from map - uidIt->second.erase(key); - - for (const sp& listener : mListeners) { - broadcastList.push_back(listener); - } - } - - // Remove from disk. There can still be a lingering file on disk so we check - // whether or not the config was on memory. - remove_saved_configs(key); - } - - for (const sp& listener:broadcastList) { - listener->OnConfigRemoved(key); - } -} - -void ConfigManager::remove_saved_configs(const ConfigKey& key) { - string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()); - StorageManager::deleteSuffixedFiles(STATS_SERVICE_DIR, suffix.c_str()); -} - -void ConfigManager::RemoveConfigs(int uid) { - vector removed; - vector> broadcastList; - { - lock_guard lock(mMutex); - - auto uidIt = mConfigs.find(uid); - if (uidIt == mConfigs.end()) { - return; - } - - for (auto it = uidIt->second.begin(); it != uidIt->second.end(); ++it) { - // Remove from map - remove_saved_configs(*it); - removed.push_back(*it); - } - - mConfigs.erase(uidIt); - - for (const sp& listener : mListeners) { - broadcastList.push_back(listener); - } - } - - // Remove separately so if they do anything in the callback they can't mess up our iteration. - for (auto& key : removed) { - // Tell everyone - for (const sp& listener:broadcastList) { - listener->OnConfigRemoved(key); - } - } -} - -void ConfigManager::RemoveAllConfigs() { - vector removed; - vector> broadcastList; - { - lock_guard lock(mMutex); - - for (auto uidIt = mConfigs.begin(); uidIt != mConfigs.end();) { - for (auto it = uidIt->second.begin(); it != uidIt->second.end();) { - // Remove from map - removed.push_back(*it); - it = uidIt->second.erase(it); - } - uidIt = mConfigs.erase(uidIt); - } - - for (const sp& listener : mListeners) { - broadcastList.push_back(listener); - } - } - - // Remove separately so if they do anything in the callback they can't mess up our iteration. - for (auto& key : removed) { - // Tell everyone - for (const sp& listener:broadcastList) { - listener->OnConfigRemoved(key); - } - } -} - -vector ConfigManager::GetAllConfigKeys() const { - lock_guard lock(mMutex); - - vector ret; - for (auto uidIt = mConfigs.cbegin(); uidIt != mConfigs.cend(); ++uidIt) { - for (auto it = uidIt->second.cbegin(); it != uidIt->second.cend(); ++it) { - ret.push_back(*it); - } - } - return ret; -} - -const shared_ptr ConfigManager::GetConfigReceiver(const ConfigKey& key) const { - lock_guard lock(mMutex); - - auto it = mConfigReceivers.find(key); - if (it == mConfigReceivers.end()) { - return nullptr; - } else { - return it->second; - } -} - -const shared_ptr ConfigManager::GetActiveConfigsChangedReceiver(const int uid) - const { - lock_guard lock(mMutex); - - auto it = mActiveConfigsChangedReceivers.find(uid); - if (it == mActiveConfigsChangedReceivers.end()) { - return nullptr; - } else { - return it->second; - } -} - -void ConfigManager::Dump(FILE* out) { - lock_guard lock(mMutex); - - fprintf(out, "CONFIGURATIONS\n"); - fprintf(out, " uid name\n"); - for (auto uidIt = mConfigs.cbegin(); uidIt != mConfigs.cend(); ++uidIt) { - for (auto it = uidIt->second.cbegin(); it != uidIt->second.cend(); ++it) { - fprintf(out, " %6d %lld\n", it->GetUid(), (long long)it->GetId()); - auto receiverIt = mConfigReceivers.find(*it); - if (receiverIt != mConfigReceivers.end()) { - fprintf(out, " -> received by PendingIntent as binder\n"); - } - } - } -} - -void ConfigManager::update_saved_configs_locked(const ConfigKey& key, - const vector& buffer, - const int numBytes) { - // If there is a pre-existing config with same key we should first delete it. - remove_saved_configs(key); - - // Then we save the latest config. - string file_name = - StringPrintf("%s/%ld_%d_%lld", STATS_SERVICE_DIR, time(nullptr), - key.GetUid(), (long long)key.GetId()); - StorageManager::writeFile(file_name.c_str(), &buffer[0], numBytes); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h deleted file mode 100644 index bef057f96409..000000000000 --- a/cmds/statsd/src/config/ConfigManager.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "config/ConfigKey.h" -#include "config/ConfigListener.h" - -#include -#include -#include - -#include - -using aidl::android::os::IPendingIntentRef; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -/** - * Keeps track of which configurations have been set from various sources. - */ -class ConfigManager : public virtual android::RefBase { -public: - ConfigManager(); - virtual ~ConfigManager(); - - /** - * Initialize ConfigListener by reading from disk and get updates. - */ - void Startup(); - - /* - * No-op initializer for tests. - */ - void StartupForTest(); - - /** - * Someone else wants to know about the configs. - */ - void AddListener(const sp& listener); - - /** - * A configuration was added or updated. - * - * Reports this to listeners. - */ - void UpdateConfig(const ConfigKey& key, const StatsdConfig& data); - - /** - * Sets the broadcast receiver for a configuration key. - */ - void SetConfigReceiver(const ConfigKey& key, const shared_ptr& pir); - - /** - * Returns the package name and class name representing the broadcast receiver for this config. - */ - const shared_ptr GetConfigReceiver(const ConfigKey& key) const; - - /** - * Returns all config keys registered. - */ - std::vector GetAllConfigKeys() const; - - /** - * Erase any broadcast receiver associated with this config key. - */ - void RemoveConfigReceiver(const ConfigKey& key); - - /** - * Sets the broadcast receiver that is notified whenever the list of active configs - * changes for this uid. - */ - void SetActiveConfigsChangedReceiver(const int uid, const shared_ptr& pir); - - /** - * Returns the broadcast receiver for active configs changed for this uid. - */ - - const shared_ptr GetActiveConfigsChangedReceiver(const int uid) const; - - /** - * Erase any active configs changed broadcast receiver associated with this uid. - */ - void RemoveActiveConfigsChangedReceiver(const int uid); - - /** - * A configuration was removed. - * - * Reports this to listeners. - */ - void RemoveConfig(const ConfigKey& key); - - /** - * Remove all of the configs for the given uid. - */ - void RemoveConfigs(int uid); - - /** - * Remove all of the configs from memory. - */ - void RemoveAllConfigs(); - - /** - * Text dump of our state for debugging. - */ - void Dump(FILE* out); - -private: - mutable std::mutex mMutex; - - /** - * Save the configs to disk. - */ - void update_saved_configs_locked(const ConfigKey& key, - const std::vector& buffer, - const int numBytes); - - /** - * Remove saved configs from disk. - */ - void remove_saved_configs(const ConfigKey& key); - - /** - * Maps from uid to the config keys that have been set. - */ - std::map> mConfigs; - - /** - * Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef. - */ - std::map> mConfigReceivers; - - /** - * Each uid can be subscribed by up to one receiver to notify that the list of active configs - * for this uid has changed. The receiver is specified as IPendingIntentRef. - */ - std::map> mActiveConfigsChangedReceivers; - - /** - * The ConfigListeners that will be told about changes. - */ - std::vector> mListeners; - - // Death recipients that are triggered when the host process holding an - // IPendingIntentRef dies. - ::ndk::ScopedAIBinder_DeathRecipient mConfigReceiverDeathRecipient; - ::ndk::ScopedAIBinder_DeathRecipient mActiveConfigChangedReceiverDeathRecipient; - - /** - * Death recipient callback that is called when a config receiver dies. - * The cookie is a pointer to a ConfigReceiverDeathCookie. - */ - static void configReceiverDied(void* cookie); - - /** - * Death recipient callback that is called when an active config changed - * receiver dies. The cookie is a pointer to an - * ActiveConfigChangedReceiverDeathCookie. - */ - static void activeConfigChangedReceiverDied(void* cookie); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/experiment_ids.proto b/cmds/statsd/src/experiment_ids.proto deleted file mode 100644 index c2036314cf58..000000000000 --- a/cmds/statsd/src/experiment_ids.proto +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.internal.os"; -option java_outer_classname = "ExperimentIdsProto"; - -// StatsLogProcessor uses the proto to parse experiment ids from -// BinaryPushStateChanged atoms. This needs to be in sync with -// TrainExperimentIds in atoms.proto. -message ExperimentIds { - repeated int64 experiment_id = 1; -} diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp deleted file mode 100644 index 85b660efc956..000000000000 --- a/cmds/statsd/src/external/Perfetto.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "config/ConfigKey.h" -#include "Log.h" - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert - -#include -#include -#include - -#include - -namespace { -const char kDropboxTag[] = "perfetto"; -} - -namespace android { -namespace os { -namespace statsd { - -bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config, - int64_t subscription_id, - int64_t alert_id, - const ConfigKey& configKey) { - VLOG("Starting trace collection through perfetto"); - - if (!config.has_trace_config()) { - ALOGE("The perfetto trace config is empty, aborting"); - return false; - } - - char subscriptionId[25]; - char alertId[25]; - char configId[25]; - char configUid[25]; - snprintf(subscriptionId, sizeof(subscriptionId), "%" PRId64, subscription_id); - snprintf(alertId, sizeof(alertId), "%" PRId64, alert_id); - snprintf(configId, sizeof(configId), "%" PRId64, configKey.GetId()); - snprintf(configUid, sizeof(configUid), "%d", configKey.GetUid()); - - android::base::unique_fd readPipe; - android::base::unique_fd writePipe; - if (!android::base::Pipe(&readPipe, &writePipe)) { - ALOGE("pipe() failed while calling the Perfetto client: %s", strerror(errno)); - return false; - } - - pid_t pid = fork(); - if (pid < 0) { - ALOGE("fork() failed while calling the Perfetto client: %s", strerror(errno)); - return false; - } - - if (pid == 0) { - // Child process. - - // No malloc calls or library calls after this point. Remember that even - // ALOGx (aka android_printLog()) can use dynamic memory for vsprintf(). - - writePipe.reset(); // Close the write end (owned by the main process). - - // Replace stdin with |readPipe| so the main process can write into it. - if (dup2(readPipe.get(), STDIN_FILENO) < 0) _exit(1); - readPipe.reset(); - - // Replace stdout/stderr with /dev/null and close any other file - // descriptor. This is to avoid SELinux complaining about perfetto - // trying to access files accidentally left open by statsd (i.e. files - // that have been opened without the O_CLOEXEC flag). - int devNullFd = open("/dev/null", O_RDWR | O_CLOEXEC); - if (dup2(devNullFd, STDOUT_FILENO) < 0) _exit(2); - if (dup2(devNullFd, STDERR_FILENO) < 0) _exit(3); - close(devNullFd); - for (int i = 0; i < 1024; i++) { - if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) close(i); - } - - execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox", - kDropboxTag, "--alert-id", alertId, "--config-id", configId, "--config-uid", - configUid, "--subscription-id", subscriptionId, nullptr); - - // execl() doesn't return in case of success, if we get here something - // failed. - _exit(4); - } - - // Main process. - - readPipe.reset(); // Close the read end (owned by the child process). - - // Using fdopen() because fwrite() has the right logic to chunking write() - // over a pipe (see __sfvwrite()). - FILE* writePipeStream = android::base::Fdopen(std::move(writePipe), "wb"); - if (!writePipeStream) { - ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno)); - return false; - } - - const std::string& cfgProto = config.trace_config(); - size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream); - fclose(writePipeStream); - if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) { - ALOGE("fwrite() failed (ret: %zd) while calling the Perfetto client: %s", bytesWritten, - strerror(errno)); - return false; - } - - // This does NOT wait for the full duration of the trace. It just waits until - // the process has read the config from stdin and detached. - int childStatus = 0; - waitpid(pid, &childStatus, 0); - if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) { - ALOGE("Child process failed (0x%x) while calling the Perfetto client", childStatus); - return false; - } - - VLOG("CollectPerfettoTraceAndUploadToDropbox() succeeded"); - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h deleted file mode 100644 index 095782a49f9b..000000000000 --- a/cmds/statsd/src/external/Perfetto.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -namespace android { -namespace os { -namespace statsd { - -class ConfigKey; -class PerfettoDetails; // Declared in statsd_config.pb.h - -// Starts the collection of a Perfetto trace with the given |config|. -// The trace is uploaded to Dropbox by the perfetto cmdline util once done. -// This method returns immediately after passing the config and does NOT wait -// for the full duration of the trace. -bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config, - int64_t subscription_id, - int64_t alert_id, - const ConfigKey& configKey); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/PullDataReceiver.h b/cmds/statsd/src/external/PullDataReceiver.h deleted file mode 100644 index dd5c0cfa04c1..000000000000 --- a/cmds/statsd/src/external/PullDataReceiver.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include "StatsPuller.h" -#include "logd/LogEvent.h" - -namespace android { -namespace os { -namespace statsd { - -class PullDataReceiver : virtual public RefBase{ - public: - virtual ~PullDataReceiver() {} - /** - * @param data The pulled data. - * @param pullSuccess Whether the pull succeeded. If the pull does not succeed, the data for the - * bucket should be invalidated. - * @param originalPullTimeNs This is when all the pulls have been initiated (elapsed time). - */ - virtual void onDataPulled(const std::vector>& data, - bool pullSuccess, int64_t originalPullTimeNs) = 0; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/PullResultReceiver.cpp b/cmds/statsd/src/external/PullResultReceiver.cpp deleted file mode 100644 index 8aa4792dc179..000000000000 --- a/cmds/statsd/src/external/PullResultReceiver.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "PullResultReceiver.h" - -namespace android { -namespace os { -namespace statsd { - -PullResultReceiver::PullResultReceiver( - std::function&)> pullFinishCb) - : pullFinishCallback(std::move(pullFinishCb)) { -} - -Status PullResultReceiver::pullFinished(int32_t atomTag, bool success, - const vector& output) { - pullFinishCallback(atomTag, success, output); - return Status::ok(); -} - -PullResultReceiver::~PullResultReceiver() { -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/PullResultReceiver.h b/cmds/statsd/src/external/PullResultReceiver.h deleted file mode 100644 index ceaae801b2d5..000000000000 --- a/cmds/statsd/src/external/PullResultReceiver.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -using namespace std; - -using Status = ::ndk::ScopedAStatus; -using aidl::android::os::BnPullAtomResultReceiver; -using aidl::android::util::StatsEventParcel; - -namespace android { -namespace os { -namespace statsd { - -class PullResultReceiver : public BnPullAtomResultReceiver { -public: - PullResultReceiver(function&)> - pullFinishCallback); - ~PullResultReceiver(); - - /** - * Binder call for finishing a pull. - */ - Status pullFinished(int32_t atomTag, bool success, - const vector& output) override; - -private: - function&)> pullFinishCallback; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/PullUidProvider.h b/cmds/statsd/src/external/PullUidProvider.h deleted file mode 100644 index 2318c501ea4b..000000000000 --- a/cmds/statsd/src/external/PullUidProvider.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include - -#include "StatsPuller.h" -#include "logd/LogEvent.h" - -namespace android { -namespace os { -namespace statsd { - -class PullUidProvider : virtual public RefBase { -public: - virtual ~PullUidProvider() {} - - /** - * @param atomId The atom for which to get the uids. - */ - virtual vector getPullAtomUids(int32_t atomId) = 0; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp deleted file mode 100644 index 78e6f094db7e..000000000000 --- a/cmds/statsd/src/external/StatsCallbackPuller.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsCallbackPuller.h" -#include "PullResultReceiver.h" -#include "StatsPullerManager.h" -#include "logd/LogEvent.h" -#include "stats_log_util.h" - -#include - -using namespace std; - -using Status = ::ndk::ScopedAStatus; -using aidl::android::util::StatsEventParcel; -using ::ndk::SharedRefBase; - -namespace android { -namespace os { -namespace statsd { - -StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr& callback, - const int64_t coolDownNs, int64_t timeoutNs, - const vector additiveFields) - : StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) { - VLOG("StatsCallbackPuller created for tag %d", tagId); -} - -bool StatsCallbackPuller::PullInternal(vector>* data) { - VLOG("StatsCallbackPuller called for tag %d", mTagId); - if(mCallback == nullptr) { - ALOGW("No callback registered"); - return false; - } - - // Shared variables needed in the result receiver. - shared_ptr cv_mutex = make_shared(); - shared_ptr cv = make_shared(); - shared_ptr pullFinish = make_shared(false); - shared_ptr pullSuccess = make_shared(false); - shared_ptr>> sharedData = - make_shared>>(); - - shared_ptr resultReceiver = SharedRefBase::make( - [cv_mutex, cv, pullFinish, pullSuccess, sharedData]( - int32_t atomTag, bool success, const vector& output) { - // This is the result of the pull, executing in a statsd binder thread. - // The pull could have taken a long time, and we should only modify - // data (the output param) if the pointer is in scope and the pull did not time out. - { - lock_guard lk(*cv_mutex); - for (const StatsEventParcel& parcel: output) { - shared_ptr event = make_shared(/*uid=*/-1, /*pid=*/-1); - bool valid = event->parseBuffer((uint8_t*)parcel.buffer.data(), - parcel.buffer.size()); - if (valid) { - sharedData->push_back(event); - } else { - StatsdStats::getInstance().noteAtomError(event->GetTagId(), - /*pull=*/true); - } - } - *pullSuccess = success; - *pullFinish = true; - } - cv->notify_one(); - }); - - // Initiate the pull. This is a oneway call to a different process, except - // in unit tests. In process calls are not oneway. - Status status = mCallback->onPullAtom(mTagId, resultReceiver); - if (!status.isOk()) { - StatsdStats::getInstance().notePullBinderCallFailed(mTagId); - return false; - } - - { - unique_lock unique_lk(*cv_mutex); - // Wait until the pull finishes, or until the pull timeout. - cv->wait_for(unique_lk, chrono::nanoseconds(mPullTimeoutNs), - [pullFinish] { return *pullFinish; }); - if (!*pullFinish) { - // Note: The parent stats puller will also note that there was a timeout and that the - // cache should be cleared. Once we migrate all pullers to this callback, we could - // consolidate the logic. - return true; - } else { - // Only copy the data if we did not timeout and the pull was successful. - if (*pullSuccess) { - *data = std::move(*sharedData); - } - VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId); - return *pullSuccess; - } - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h deleted file mode 100644 index e82e8bb532be..000000000000 --- a/cmds/statsd/src/external/StatsCallbackPuller.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "StatsPuller.h" - -using aidl::android::os::IPullAtomCallback; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -class StatsCallbackPuller : public StatsPuller { -public: - explicit StatsCallbackPuller(int tagId, const shared_ptr& callback, - const int64_t coolDownNs, const int64_t timeoutNs, - const std::vector additiveFields); - -private: - bool PullInternal(vector>* data) override; - const shared_ptr mCallback; - - FRIEND_TEST(StatsCallbackPullerTest, PullFail); - FRIEND_TEST(StatsCallbackPullerTest, PullSuccess); - FRIEND_TEST(StatsCallbackPullerTest, PullTimeout); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp deleted file mode 100644 index bb5d0a6bab58..000000000000 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsPuller.h" -#include "StatsPullerManager.h" -#include "guardrail/StatsdStats.h" -#include "puller_util.h" -#include "stats_log_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::lock_guard; - -sp StatsPuller::mUidMap = nullptr; -void StatsPuller::SetUidMap(const sp& uidMap) { mUidMap = uidMap; } - -StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_t pullTimeoutNs, - const std::vector additiveFields) - : mTagId(tagId), - mPullTimeoutNs(pullTimeoutNs), - mCoolDownNs(coolDownNs), - mAdditiveFields(additiveFields), - mLastPullTimeNs(0), - mLastEventTimeNs(0) { -} - -bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector>* data) { - lock_guard lock(mLock); - const int64_t elapsedTimeNs = getElapsedRealtimeNs(); - const int64_t systemUptimeMillis = getSystemUptimeMillis(); - StatsdStats::getInstance().notePull(mTagId); - const bool shouldUseCache = - (mLastEventTimeNs == eventTimeNs) || (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs); - if (shouldUseCache) { - if (mHasGoodData) { - (*data) = mCachedData; - StatsdStats::getInstance().notePullFromCache(mTagId); - - } - return mHasGoodData; - } - if (mLastPullTimeNs > 0) { - StatsdStats::getInstance().updateMinPullIntervalSec( - mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC); - } - mCachedData.clear(); - mLastPullTimeNs = elapsedTimeNs; - mLastEventTimeNs = eventTimeNs; - mHasGoodData = PullInternal(&mCachedData); - if (!mHasGoodData) { - return mHasGoodData; - } - const int64_t pullElapsedDurationNs = getElapsedRealtimeNs() - elapsedTimeNs; - const int64_t pullSystemUptimeDurationMillis = getSystemUptimeMillis() - systemUptimeMillis; - StatsdStats::getInstance().notePullTime(mTagId, pullElapsedDurationNs); - const bool pullTimeOut = pullElapsedDurationNs > mPullTimeoutNs; - if (pullTimeOut) { - // Something went wrong. Discard the data. - mCachedData.clear(); - mHasGoodData = false; - StatsdStats::getInstance().notePullTimeout( - mTagId, pullSystemUptimeDurationMillis, NanoToMillis(pullElapsedDurationNs)); - ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId, - (long long)pullElapsedDurationNs); - return mHasGoodData; - } - - if (mCachedData.size() > 0) { - mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId, mAdditiveFields); - } - - if (mCachedData.empty()) { - VLOG("Data pulled is empty"); - StatsdStats::getInstance().noteEmptyData(mTagId); - } - - (*data) = mCachedData; - return mHasGoodData; -} - -int StatsPuller::ForceClearCache() { - return clearCache(); -} - -int StatsPuller::clearCache() { - lock_guard lock(mLock); - return clearCacheLocked(); -} - -int StatsPuller::clearCacheLocked() { - int ret = mCachedData.size(); - mCachedData.clear(); - mLastPullTimeNs = 0; - mLastEventTimeNs = 0; - return ret; -} - -int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) { - if (timestampNs - mLastPullTimeNs > mCoolDownNs) { - return clearCache(); - } else { - return 0; - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h deleted file mode 100644 index 470d15e6fbd1..000000000000 --- a/cmds/statsd/src/external/StatsPuller.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include "packages/UidMap.h" - -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "puller_util.h" - -using aidl::android::os::IStatsCompanionService; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -class StatsPuller : public virtual RefBase { -public: - explicit StatsPuller(const int tagId, - const int64_t coolDownNs = NS_PER_SEC, - const int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs, - const std::vector additiveFields = std::vector()); - - virtual ~StatsPuller() {} - - // Pulls the most recent data. - // The data may be served from cache if consecutive pulls come within - // predefined cooldown time. - // Returns true if the pull was successful. - // Returns false when - // 1) the pull fails - // 2) pull takes longer than mPullTimeoutNs (intrinsic to puller) - // If a metric wants to make any change to the data, like timestamps, it - // should make a copy as this data may be shared with multiple metrics. - bool Pull(const int64_t eventTimeNs, std::vector>* data); - - // Clear cache immediately - int ForceClearCache(); - - // Clear cache if elapsed time is more than cooldown time - int ClearCacheIfNecessary(int64_t timestampNs); - - static void SetUidMap(const sp& uidMap); - - virtual void SetStatsCompanionService( - shared_ptr statsCompanionService) {}; - -protected: - const int mTagId; - - // Max time allowed to pull this atom. - // We cannot reliably kill a pull thread. So we don't terminate the puller. - // The data is discarded if the pull takes longer than this and mHasGoodData - // marked as false. - const int64_t mPullTimeoutNs = StatsdStats::kPullMaxDelayNs; - -private: - mutable std::mutex mLock; - - // Real puller impl. - virtual bool PullInternal(std::vector>* data) = 0; - - bool mHasGoodData = false; - - // Minimum time before this puller does actual pull again. - // Pullers can cause significant impact to system health and battery. - // So that we don't pull too frequently. - // If a pull request comes before cooldown, a cached version from previous pull - // will be returned. - const int64_t mCoolDownNs = 1 * NS_PER_SEC; - - // The field numbers of the fields that need to be summed when merging - // isolated uid with host uid. - const std::vector mAdditiveFields; - - int64_t mLastPullTimeNs; - - // All pulls happen due to an event (app upgrade, bucket boundary, condition change, etc). - // If multiple pulls need to be done at the same event time, we will always use the cache after - // the first pull. - int64_t mLastEventTimeNs; - - // Cache of data from last pull. If next request comes before cool down finishes, - // cached data will be returned. - // Cached data is cleared when - // 1) A pull fails - // 2) A new pull request comes after cooldown time. - // 3) clearCache is called. - std::vector> mCachedData; - - int clearCache(); - - int clearCacheLocked(); - - static sp mUidMap; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp deleted file mode 100644 index 8a9ec7456e55..000000000000 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false -#include "Log.h" - -#include "StatsPullerManager.h" - -#include -#include -#include - -#include -#include - -#include "../StatsService.h" -#include "../logd/LogEvent.h" -#include "../stats_log_util.h" -#include "../statscompanion_util.h" -#include "StatsCallbackPuller.h" -#include "TrainInfoPuller.h" -#include "statslog_statsd.h" - -using std::shared_ptr; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -// Stores the puller as a wp to avoid holding a reference in case it is unregistered and -// pullAtomCallbackDied is never called. -struct PullAtomCallbackDeathCookie { - PullAtomCallbackDeathCookie(const wp& pullerManager, - const PullerKey& pullerKey, const wp& puller) : - mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) { - } - - wp mPullerManager; - PullerKey mPullerKey; - wp mPuller; -}; - -void StatsPullerManager::pullAtomCallbackDied(void* cookie) { - PullAtomCallbackDeathCookie* cookie_ = static_cast(cookie); - sp thiz = cookie_->mPullerManager.promote(); - if (!thiz) { - return; - } - - const PullerKey& pullerKey = cookie_->mPullerKey; - wp puller = cookie_->mPuller; - - // Erase the mapping from the puller key to the puller if the mapping still exists. - // Note that we are removing the StatsPuller object, which internally holds the binder - // IPullAtomCallback. However, each new registration creates a new StatsPuller, so this works. - lock_guard lock(thiz->mLock); - const auto& it = thiz->kAllPullAtomInfo.find(pullerKey); - if (it != thiz->kAllPullAtomInfo.end() && puller != nullptr && puller == it->second) { - StatsdStats::getInstance().notePullerCallbackRegistrationChanged(pullerKey.atomTag, - /*registered=*/false); - thiz->kAllPullAtomInfo.erase(pullerKey); - } - // The death recipient corresponding to this specific IPullAtomCallback can never - // be triggered again, so free up resources. - delete cookie_; -} - -// Values smaller than this may require to update the alarm. -const int64_t NO_ALARM_UPDATE = INT64_MAX; - -StatsPullerManager::StatsPullerManager() - : kAllPullAtomInfo({ - // TrainInfo. - {{.atomTag = util::TRAIN_INFO, .uid = AID_STATSD}, new TrainInfoPuller()}, - }), - mNextPullTimeNs(NO_ALARM_UPDATE), - mPullAtomCallbackDeathRecipient(AIBinder_DeathRecipient_new(pullAtomCallbackDied)) { -} - -bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs, - vector>* data, bool useUids) { - std::lock_guard _l(mLock); - return PullLocked(tagId, configKey, eventTimeNs, data, useUids); -} - -bool StatsPullerManager::Pull(int tagId, const vector& uids, const int64_t eventTimeNs, - vector>* data, bool useUids) { - std::lock_guard _l(mLock); - return PullLocked(tagId, uids, eventTimeNs, data, useUids); -} - -bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey, - const int64_t eventTimeNs, vector>* data, - bool useUids) { - vector uids; - if (useUids) { - auto uidProviderIt = mPullUidProviders.find(configKey); - if (uidProviderIt == mPullUidProviders.end()) { - ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId, - configKey.ToString().c_str()); - StatsdStats::getInstance().notePullUidProviderNotFound(tagId); - return false; - } - sp pullUidProvider = uidProviderIt->second.promote(); - if (pullUidProvider == nullptr) { - ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId, - configKey.ToString().c_str()); - StatsdStats::getInstance().notePullUidProviderNotFound(tagId); - return false; - } - uids = pullUidProvider->getPullAtomUids(tagId); - } - return PullLocked(tagId, uids, eventTimeNs, data, useUids); -} - -bool StatsPullerManager::PullLocked(int tagId, const vector& uids, - const int64_t eventTimeNs, vector>* data, - bool useUids) { - VLOG("Initiating pulling %d", tagId); - if (useUids) { - for (int32_t uid : uids) { - PullerKey key = {.atomTag = tagId, .uid = uid}; - auto pullerIt = kAllPullAtomInfo.find(key); - if (pullerIt != kAllPullAtomInfo.end()) { - bool ret = pullerIt->second->Pull(eventTimeNs, data); - VLOG("pulled %zu items", data->size()); - if (!ret) { - StatsdStats::getInstance().notePullFailed(tagId); - } - return ret; - } - } - StatsdStats::getInstance().notePullerNotFound(tagId); - ALOGW("StatsPullerManager: Unknown tagId %d", tagId); - return false; // Return early since we don't know what to pull. - } else { - PullerKey key = {.atomTag = tagId, .uid = -1}; - auto pullerIt = kAllPullAtomInfo.find(key); - if (pullerIt != kAllPullAtomInfo.end()) { - bool ret = pullerIt->second->Pull(eventTimeNs, data); - VLOG("pulled %zu items", data->size()); - if (!ret) { - StatsdStats::getInstance().notePullFailed(tagId); - } - return ret; - } - ALOGW("StatsPullerManager: Unknown tagId %d", tagId); - return false; // Return early since we don't know what to pull. - } -} - -bool StatsPullerManager::PullerForMatcherExists(int tagId) const { - // Pulled atoms might be registered after we parse the config, so just make sure the id is in - // an appropriate range. - return isVendorPulledAtom(tagId) || isPulledAtom(tagId); -} - -void StatsPullerManager::updateAlarmLocked() { - if (mNextPullTimeNs == NO_ALARM_UPDATE) { - VLOG("No need to set alarms. Skipping"); - return; - } - - // TODO(b/151045771): do not hold a lock while making a binder call - if (mStatsCompanionService != nullptr) { - mStatsCompanionService->setPullingAlarm(mNextPullTimeNs / 1000000); - } else { - VLOG("StatsCompanionService not available. Alarm not set."); - } - return; -} - -void StatsPullerManager::SetStatsCompanionService( - shared_ptr statsCompanionService) { - std::lock_guard _l(mLock); - shared_ptr tmpForLock = mStatsCompanionService; - mStatsCompanionService = statsCompanionService; - for (const auto& pulledAtom : kAllPullAtomInfo) { - pulledAtom.second->SetStatsCompanionService(statsCompanionService); - } - if (mStatsCompanionService != nullptr) { - updateAlarmLocked(); - } -} - -void StatsPullerManager::RegisterReceiver(int tagId, const ConfigKey& configKey, - wp receiver, int64_t nextPullTimeNs, - int64_t intervalNs) { - std::lock_guard _l(mLock); - auto& receivers = mReceivers[{.atomTag = tagId, .configKey = configKey}]; - for (auto it = receivers.begin(); it != receivers.end(); it++) { - if (it->receiver == receiver) { - VLOG("Receiver already registered of %d", (int)receivers.size()); - return; - } - } - ReceiverInfo receiverInfo; - receiverInfo.receiver = receiver; - - // Round it to the nearest minutes. This is the limit of alarm manager. - // In practice, we should always have larger buckets. - int64_t roundedIntervalNs = intervalNs / NS_PER_SEC / 60 * NS_PER_SEC * 60; - // Scheduled pulling should be at least 1 min apart. - // This can be lower in cts tests, in which case we round it to 1 min. - if (roundedIntervalNs < 60 * (int64_t)NS_PER_SEC) { - roundedIntervalNs = 60 * (int64_t)NS_PER_SEC; - } - - receiverInfo.intervalNs = roundedIntervalNs; - receiverInfo.nextPullTimeNs = nextPullTimeNs; - receivers.push_back(receiverInfo); - - // There is only one alarm for all pulled events. So only set it to the smallest denom. - if (nextPullTimeNs < mNextPullTimeNs) { - VLOG("Updating next pull time %lld", (long long)mNextPullTimeNs); - mNextPullTimeNs = nextPullTimeNs; - updateAlarmLocked(); - } - VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size()); -} - -void StatsPullerManager::UnRegisterReceiver(int tagId, const ConfigKey& configKey, - wp receiver) { - std::lock_guard _l(mLock); - auto receiversIt = mReceivers.find({.atomTag = tagId, .configKey = configKey}); - if (receiversIt == mReceivers.end()) { - VLOG("Unknown pull code or no receivers: %d", tagId); - return; - } - std::list& receivers = receiversIt->second; - for (auto it = receivers.begin(); it != receivers.end(); it++) { - if (receiver == it->receiver) { - receivers.erase(it); - VLOG("Puller for tagId %d unregistered of %d", tagId, (int)receivers.size()); - return; - } - } -} - -void StatsPullerManager::RegisterPullUidProvider(const ConfigKey& configKey, - wp provider) { - std::lock_guard _l(mLock); - mPullUidProviders[configKey] = provider; -} - -void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey, - wp provider) { - std::lock_guard _l(mLock); - const auto& it = mPullUidProviders.find(configKey); - if (it != mPullUidProviders.end() && it->second == provider) { - mPullUidProviders.erase(it); - } -} - -void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) { - std::lock_guard _l(mLock); - int64_t wallClockNs = getWallClockNs(); - - int64_t minNextPullTimeNs = NO_ALARM_UPDATE; - - vector>> needToPull; - for (auto& pair : mReceivers) { - vector receivers; - if (pair.second.size() != 0) { - for (ReceiverInfo& receiverInfo : pair.second) { - if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) { - receivers.push_back(&receiverInfo); - } else { - if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) { - minNextPullTimeNs = receiverInfo.nextPullTimeNs; - } - } - } - if (receivers.size() > 0) { - needToPull.push_back(make_pair(&pair.first, receivers)); - } - } - } - for (const auto& pullInfo : needToPull) { - vector> data; - bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey, - elapsedTimeNs, &data); - if (!pullSuccess) { - VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs); - } - - // Convention is to mark pull atom timestamp at request time. - // If we pull at t0, puller starts at t1, finishes at t2, and send back - // at t3, we mark t0 as its timestamp, which should correspond to its - // triggering event, such as condition change at t0. - // Here the triggering event is alarm fired from AlarmManager. - // In ValueMetricProducer and GaugeMetricProducer we do same thing - // when pull on condition change, etc. - for (auto& event : data) { - event->setElapsedTimestampNs(elapsedTimeNs); - event->setLogdWallClockTimestampNs(wallClockNs); - } - - for (const auto& receiverInfo : pullInfo.second) { - sp receiverPtr = receiverInfo->receiver.promote(); - if (receiverPtr != nullptr) { - receiverPtr->onDataPulled(data, pullSuccess, elapsedTimeNs); - // We may have just come out of a coma, compute next pull time. - int numBucketsAhead = - (elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs; - receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs; - if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) { - minNextPullTimeNs = receiverInfo->nextPullTimeNs; - } - } else { - VLOG("receiver already gone."); - } - } - } - - VLOG("mNextPullTimeNs: %lld updated to %lld", (long long)mNextPullTimeNs, - (long long)minNextPullTimeNs); - mNextPullTimeNs = minNextPullTimeNs; - updateAlarmLocked(); -} - -int StatsPullerManager::ForceClearPullerCache() { - int totalCleared = 0; - for (const auto& pulledAtom : kAllPullAtomInfo) { - totalCleared += pulledAtom.second->ForceClearCache(); - } - return totalCleared; -} - -int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) { - int totalCleared = 0; - for (const auto& pulledAtom : kAllPullAtomInfo) { - totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs); - } - return totalCleared; -} - -void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag, - const int64_t coolDownNs, const int64_t timeoutNs, - const vector& additiveFields, - const shared_ptr& callback, - bool useUid) { - std::lock_guard _l(mLock); - VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag); - - if (callback == nullptr) { - ALOGW("SetPullAtomCallback called with null callback for atom %d.", atomTag); - return; - } - - StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true); - int64_t actualCoolDownNs = coolDownNs < kMinCoolDownNs ? kMinCoolDownNs : coolDownNs; - int64_t actualTimeoutNs = timeoutNs > kMaxTimeoutNs ? kMaxTimeoutNs : timeoutNs; - - sp puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs, - actualTimeoutNs, additiveFields); - PullerKey key = {.atomTag = atomTag, .uid = useUid ? uid : -1}; - AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(), - new PullAtomCallbackDeathCookie(this, key, puller)); - kAllPullAtomInfo[key] = puller; -} - -void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag, - bool useUids) { - std::lock_guard _l(mLock); - PullerKey key = {.atomTag = atomTag, .uid = useUids ? uid : -1}; - if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) { - StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, - /*registered=*/false); - kAllPullAtomInfo.erase(key); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h deleted file mode 100644 index 194a0f5edba8..000000000000 --- a/cmds/statsd/src/external/StatsPullerManager.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include -#include - -#include "PullDataReceiver.h" -#include "PullUidProvider.h" -#include "StatsPuller.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "packages/UidMap.h" - -using aidl::android::os::IPullAtomCallback; -using aidl::android::os::IStatsCompanionService; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -typedef struct PullerKey { - // The uid of the process that registers this puller. - const int uid = -1; - // The atom that this puller is for. - const int atomTag; - - bool operator<(const PullerKey& that) const { - if (uid < that.uid) { - return true; - } - if (uid > that.uid) { - return false; - } - return atomTag < that.atomTag; - }; - - bool operator==(const PullerKey& that) const { - return uid == that.uid && atomTag == that.atomTag; - }; -} PullerKey; - -class StatsPullerManager : public virtual RefBase { -public: - StatsPullerManager(); - - virtual ~StatsPullerManager() { - } - - - // Registers a receiver for tagId. It will be pulled on the nextPullTimeNs - // and then every intervalNs thereafter. - virtual void RegisterReceiver(int tagId, const ConfigKey& configKey, - wp receiver, int64_t nextPullTimeNs, - int64_t intervalNs); - - // Stop listening on a tagId. - virtual void UnRegisterReceiver(int tagId, const ConfigKey& configKey, - wp receiver); - - // Registers a pull uid provider for the config key. When pulling atoms, it will be used to - // determine which uids to pull from. - virtual void RegisterPullUidProvider(const ConfigKey& configKey, wp provider); - - // Unregister a pull uid provider. - virtual void UnregisterPullUidProvider(const ConfigKey& configKey, - wp provider); - - // Verify if we know how to pull for this matcher - bool PullerForMatcherExists(int tagId) const; - - void OnAlarmFired(int64_t elapsedTimeNs); - - // Pulls the most recent data. - // The data may be served from cache if consecutive pulls come within - // mCoolDownNs. - // Returns true if the pull was successful. - // Returns false when - // 1) the pull fails - // 2) pull takes longer than mPullTimeoutNs (intrinsic to puller) - // 3) Either a PullUidProvider was not registered for the config, or the there was no puller - // registered for any of the uids for this atom. - // If the metric wants to make any change to the data, like timestamps, they - // should make a copy as this data may be shared with multiple metrics. - virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs, - vector>* data, bool useUids = true); - - // Same as above, but directly specify the allowed uids to pull from. - virtual bool Pull(int tagId, const vector& uids, const int64_t eventTimeNs, - vector>* data, bool useUids = true); - - // Clear pull data cache immediately. - int ForceClearPullerCache(); - - // Clear pull data cache if it is beyond respective cool down time. - int ClearPullerCacheIfNecessary(int64_t timestampNs); - - void SetStatsCompanionService(shared_ptr statsCompanionService); - - void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs, - const int64_t timeoutNs, const vector& additiveFields, - const shared_ptr& callback, - bool useUid = true); - - void UnregisterPullAtomCallback(const int uid, const int32_t atomTag, bool useUids = true); - - std::map> kAllPullAtomInfo; - -private: - const static int64_t kMinCoolDownNs = NS_PER_SEC; - const static int64_t kMaxTimeoutNs = 10 * NS_PER_SEC; - shared_ptr mStatsCompanionService = nullptr; - - // A struct containing an atom id and a Config Key - typedef struct ReceiverKey { - const int atomTag; - const ConfigKey configKey; - - inline bool operator<(const ReceiverKey& that) const { - return atomTag == that.atomTag ? configKey < that.configKey : atomTag < that.atomTag; - } - } ReceiverKey; - - typedef struct { - int64_t nextPullTimeNs; - int64_t intervalNs; - wp receiver; - } ReceiverInfo; - - // mapping from Receiver Key to receivers - std::map> mReceivers; - - // mapping from Config Key to the PullUidProvider for that config - std::map> mPullUidProviders; - - bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs, - vector>* data, bool useUids = true); - - bool PullLocked(int tagId, const vector& uids, const int64_t eventTimeNs, - vector>* data, bool useUids); - - // locks for data receiver and StatsCompanionService changes - std::mutex mLock; - - void updateAlarmLocked(); - - int64_t mNextPullTimeNs; - - // Death recipient that is triggered when the process holding the IPullAtomCallback has died. - ::ndk::ScopedAIBinder_DeathRecipient mPullAtomCallbackDeathRecipient; - - /** - * Death recipient callback that is called when a pull atom callback dies. - * The cookie is a pointer to a PullAtomCallbackDeathCookie. - */ - static void pullAtomCallbackDied(void* cookie); - - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); - - FRIEND_TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp deleted file mode 100644 index 3837f4a1a517..000000000000 --- a/cmds/statsd/src/external/TrainInfoPuller.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "external/StatsPuller.h" - -#include "TrainInfoPuller.h" -#include "logd/LogEvent.h" -#include "stats_log_util.h" -#include "statslog_statsd.h" -#include "storage/StorageManager.h" - -using std::make_shared; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -TrainInfoPuller::TrainInfoPuller() : - StatsPuller(util::TRAIN_INFO) { -} - -bool TrainInfoPuller::PullInternal(vector>* data) { - vector trainInfoList = - StorageManager::readAllTrainInfo(); - if (trainInfoList.empty()) { - ALOGW("Train info was empty."); - return true; - } - for (InstallTrainInfo& trainInfo : trainInfoList) { - auto event = make_shared(getWallClockNs(), getElapsedRealtimeNs(), trainInfo); - data->push_back(event); - } - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/TrainInfoPuller.h b/cmds/statsd/src/external/TrainInfoPuller.h deleted file mode 100644 index 615d02351fd3..000000000000 --- a/cmds/statsd/src/external/TrainInfoPuller.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "StatsPuller.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * Reads train info from disk. - */ -class TrainInfoPuller : public StatsPuller { - public: - TrainInfoPuller(); - - private: - bool PullInternal(vector>* data) override; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp deleted file mode 100644 index aa99d0082bdd..000000000000 --- a/cmds/statsd/src/external/puller_util.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "puller_util.h" - -namespace android { -namespace os { -namespace statsd { - -using namespace std; - -/** - * Process all data and merge isolated with host if necessary. - * For example: - * NetworkBytesAtom { - * int uid = 1; - * State process_state = 2; - * int byte_send = 3; - * int byte_recv = 4; - * } - * additive fields are {3, 4} - * If we pulled the following events (uid1_child is an isolated uid which maps to uid1): - * [uid1, fg, 100, 200] - * [uid1_child, fg, 100, 200] - * [uid1, bg, 100, 200] - * - * We want to merge them and results should be: - * [uid1, fg, 200, 400] - * [uid1, bg, 100, 200] - * - * All atoms should be of the same tagId. All fields should be present. - */ -void mapAndMergeIsolatedUidsToHostUid(vector>& data, const sp& uidMap, - int tagId, const vector& additiveFieldsVec) { - // Check the first LogEvent for attribution chain or a uid field as either all atoms with this - // tagId have them or none of them do. - std::pair attrIndexRange; - const bool hasAttributionChain = data[0]->hasAttributionChain(&attrIndexRange); - bool hasUidField = (data[0]->getUidFieldIndex() != -1); - - if (!hasAttributionChain && !hasUidField) { - VLOG("No uid or attribution chain to merge, atom %d", tagId); - return; - } - - // 1. Map all isolated uid in-place to host uid - for (shared_ptr& event : data) { - if (event->GetTagId() != tagId) { - ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId()); - return; - } - if (hasAttributionChain) { - vector* const fieldValues = event->getMutableValues(); - for (int i = attrIndexRange.first; i <= attrIndexRange.second; i++) { - FieldValue& fieldValue = fieldValues->at(i); - if (isAttributionUidField(fieldValue)) { - const int hostUid = uidMap->getHostUidOrSelf(fieldValue.mValue.int_value); - fieldValue.mValue.setInt(hostUid); - } - } - } else { - int uidFieldIndex = event->getUidFieldIndex(); - if (uidFieldIndex != -1) { - Value& value = (*event->getMutableValues())[uidFieldIndex].mValue; - const int hostUid = uidMap->getHostUidOrSelf(value.int_value); - value.setInt(hostUid); - } else { - ALOGE("Malformed log, uid not found. %s", event->ToString().c_str()); - } - } - } - - // 2. sort the data, bit-wise - sort(data.begin(), data.end(), - [](const shared_ptr& lhs, const shared_ptr& rhs) { - if (lhs->size() != rhs->size()) { - return lhs->size() < rhs->size(); - } - const std::vector& lhsValues = lhs->getValues(); - const std::vector& rhsValues = rhs->getValues(); - for (int i = 0; i < (int)lhs->size(); i++) { - if (lhsValues[i] != rhsValues[i]) { - return lhsValues[i] < rhsValues[i]; - } - } - return false; - }); - - vector> mergedData; - const set additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end()); - bool needMerge = true; - - // 3. do the merge. - // The loop invariant is this: for every event, check if it differs on - // non-additive fields, or have different attribution chain length. - // If so, no need to merge, add itself to the result. - // Otherwise, merge the value onto the one immediately next to it. - for (int i = 0; i < (int)data.size() - 1; i++) { - // Size different, must be different chains. - if (data[i]->size() != data[i + 1]->size()) { - mergedData.push_back(data[i]); - continue; - } - vector* lhsValues = data[i]->getMutableValues(); - vector* rhsValues = data[i + 1]->getMutableValues(); - needMerge = true; - for (int p = 0; p < (int)lhsValues->size(); p++) { - if ((*lhsValues)[p] != (*rhsValues)[p]) { - int pos = (*lhsValues)[p].mField.getPosAtDepth(0); - // Differ on non-additive field, abort. - if (additiveFields.find(pos) == additiveFields.end()) { - needMerge = false; - break; - } - } - } - if (!needMerge) { - mergedData.push_back(data[i]); - continue; - } - // This should be infrequent operation. - for (int p = 0; p < (int)lhsValues->size(); p++) { - int pos = (*lhsValues)[p].mField.getPosAtDepth(0); - if (additiveFields.find(pos) != additiveFields.end()) { - (*rhsValues)[p].mValue += (*lhsValues)[p].mValue; - } - } - } - mergedData.push_back(data.back()); - - data.clear(); - data = mergedData; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h deleted file mode 100644 index afcf68ca10ad..000000000000 --- a/cmds/statsd/src/external/puller_util.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "StatsPuller.h" -#include "logd/LogEvent.h" -#include "packages/UidMap.h" - -namespace android { -namespace os { -namespace statsd { - -void mapAndMergeIsolatedUidsToHostUid(std::vector>& data, - const sp& uidMap, int tagId, - const vector& additiveFieldsVec); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp deleted file mode 100644 index 6e89038f4152..000000000000 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ /dev/null @@ -1,1101 +0,0 @@ -/* - * Copyright 2017, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsdStats.h" - -#include -#include "../stats_log_util.h" -#include "statslog_statsd.h" -#include "storage/StorageManager.h" - -namespace android { -namespace os { -namespace statsd { - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::lock_guard; -using std::shared_ptr; -using std::string; -using std::to_string; -using std::vector; - -const int FIELD_ID_BEGIN_TIME = 1; -const int FIELD_ID_END_TIME = 2; -const int FIELD_ID_CONFIG_STATS = 3; -const int FIELD_ID_ATOM_STATS = 7; -const int FIELD_ID_UIDMAP_STATS = 8; -const int FIELD_ID_ANOMALY_ALARM_STATS = 9; -const int FIELD_ID_PERIODIC_ALARM_STATS = 12; -const int FIELD_ID_SYSTEM_SERVER_RESTART = 15; -const int FIELD_ID_LOGGER_ERROR_STATS = 16; -const int FIELD_ID_OVERFLOW = 18; -const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL = 19; - -const int FIELD_ID_ATOM_STATS_TAG = 1; -const int FIELD_ID_ATOM_STATS_COUNT = 2; -const int FIELD_ID_ATOM_STATS_ERROR_COUNT = 3; - -const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1; -const int FIELD_ID_PERIODIC_ALARMS_REGISTERED = 1; - -const int FIELD_ID_LOG_LOSS_STATS_TIME = 1; -const int FIELD_ID_LOG_LOSS_STATS_COUNT = 2; -const int FIELD_ID_LOG_LOSS_STATS_ERROR = 3; -const int FIELD_ID_LOG_LOSS_STATS_TAG = 4; -const int FIELD_ID_LOG_LOSS_STATS_UID = 5; -const int FIELD_ID_LOG_LOSS_STATS_PID = 6; - -const int FIELD_ID_OVERFLOW_COUNT = 1; -const int FIELD_ID_OVERFLOW_MAX_HISTORY = 2; -const int FIELD_ID_OVERFLOW_MIN_HISTORY = 3; - -const int FIELD_ID_CONFIG_STATS_UID = 1; -const int FIELD_ID_CONFIG_STATS_ID = 2; -const int FIELD_ID_CONFIG_STATS_CREATION = 3; -const int FIELD_ID_CONFIG_STATS_RESET = 19; -const int FIELD_ID_CONFIG_STATS_DELETION = 4; -const int FIELD_ID_CONFIG_STATS_METRIC_COUNT = 5; -const int FIELD_ID_CONFIG_STATS_CONDITION_COUNT = 6; -const int FIELD_ID_CONFIG_STATS_MATCHER_COUNT = 7; -const int FIELD_ID_CONFIG_STATS_ALERT_COUNT = 8; -const int FIELD_ID_CONFIG_STATS_VALID = 9; -const int FIELD_ID_CONFIG_STATS_BROADCAST = 10; -const int FIELD_ID_CONFIG_STATS_DATA_DROP_TIME = 11; -const int FIELD_ID_CONFIG_STATS_DATA_DROP_BYTES = 21; -const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME = 12; -const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES = 20; -const int FIELD_ID_CONFIG_STATS_MATCHER_STATS = 13; -const int FIELD_ID_CONFIG_STATS_CONDITION_STATS = 14; -const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15; -const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16; -const int FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS = 17; -const int FIELD_ID_CONFIG_STATS_ANNOTATION = 18; -const int FIELD_ID_CONFIG_STATS_ACTIVATION = 22; -const int FIELD_ID_CONFIG_STATS_DEACTIVATION = 23; -const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT64 = 1; -const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT32 = 2; - -const int FIELD_ID_MATCHER_STATS_ID = 1; -const int FIELD_ID_MATCHER_STATS_COUNT = 2; -const int FIELD_ID_CONDITION_STATS_ID = 1; -const int FIELD_ID_CONDITION_STATS_COUNT = 2; -const int FIELD_ID_METRIC_STATS_ID = 1; -const int FIELD_ID_METRIC_STATS_COUNT = 2; -const int FIELD_ID_ALERT_STATS_ID = 1; -const int FIELD_ID_ALERT_STATS_COUNT = 2; - -const int FIELD_ID_UID_MAP_CHANGES = 1; -const int FIELD_ID_UID_MAP_BYTES_USED = 2; -const int FIELD_ID_UID_MAP_DROPPED_CHANGES = 3; -const int FIELD_ID_UID_MAP_DELETED_APPS = 4; - -const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID = 1; -const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME = 2; - -const std::map> StatsdStats::kAtomDimensionKeySizeLimitMap = { - {util::BINDER_CALLS, {6000, 10000}}, - {util::LOOPER_STATS, {1500, 2500}}, - {util::CPU_TIME_PER_UID_FREQ, {6000, 10000}}, -}; - -StatsdStats::StatsdStats() { - mPushedAtomStats.resize(kMaxPushedAtomId + 1); - mStartTimeSec = getWallClockSec(); -} - -StatsdStats& StatsdStats::getInstance() { - static StatsdStats statsInstance; - return statsInstance; -} - -void StatsdStats::addToIceBoxLocked(shared_ptr& stats) { - // The size of mIceBox grows strictly by one at a time. It won't be > kMaxIceBoxSize. - if (mIceBox.size() == kMaxIceBoxSize) { - mIceBox.pop_front(); - } - mIceBox.push_back(stats); -} - -void StatsdStats::noteConfigReceived( - const ConfigKey& key, int metricsCount, int conditionsCount, int matchersCount, - int alertsCount, const std::list>& annotations, - bool isValid) { - lock_guard lock(mLock); - int32_t nowTimeSec = getWallClockSec(); - - // If there is an existing config for the same key, icebox the old config. - noteConfigRemovedInternalLocked(key); - - shared_ptr configStats = std::make_shared(); - configStats->uid = key.GetUid(); - configStats->id = key.GetId(); - configStats->creation_time_sec = nowTimeSec; - configStats->metric_count = metricsCount; - configStats->condition_count = conditionsCount; - configStats->matcher_count = matchersCount; - configStats->alert_count = alertsCount; - configStats->is_valid = isValid; - for (auto& v : annotations) { - configStats->annotations.emplace_back(v); - } - - if (isValid) { - mConfigStats[key] = configStats; - } else { - configStats->deletion_time_sec = nowTimeSec; - addToIceBoxLocked(configStats); - } -} - -void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) { - auto it = mConfigStats.find(key); - if (it != mConfigStats.end()) { - int32_t nowTimeSec = getWallClockSec(); - it->second->deletion_time_sec = nowTimeSec; - addToIceBoxLocked(it->second); - mConfigStats.erase(it); - } -} - -void StatsdStats::noteConfigRemoved(const ConfigKey& key) { - lock_guard lock(mLock); - noteConfigRemovedInternalLocked(key); -} - -void StatsdStats::noteConfigResetInternalLocked(const ConfigKey& key) { - auto it = mConfigStats.find(key); - if (it != mConfigStats.end()) { - it->second->reset_time_sec = getWallClockSec(); - } -} - -void StatsdStats::noteConfigReset(const ConfigKey& key) { - lock_guard lock(mLock); - noteConfigResetInternalLocked(key); -} - -void StatsdStats::noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError, - int32_t lastTag, int32_t uid, int32_t pid) { - lock_guard lock(mLock); - if (mLogLossStats.size() == kMaxLoggerErrors) { - mLogLossStats.pop_front(); - } - mLogLossStats.emplace_back(wallClockTimeSec, count, lastError, lastTag, uid, pid); -} - -void StatsdStats::noteBroadcastSent(const ConfigKey& key) { - noteBroadcastSent(key, getWallClockSec()); -} - -void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { - lock_guard lock(mLock); - auto it = mConfigStats.find(key); - if (it == mConfigStats.end()) { - ALOGE("Config key %s not found!", key.ToString().c_str()); - return; - } - if (it->second->broadcast_sent_time_sec.size() == kMaxTimestampCount) { - it->second->broadcast_sent_time_sec.pop_front(); - } - it->second->broadcast_sent_time_sec.push_back(timeSec); -} - -void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated) { - noteActiveStatusChanged(key, activated, getWallClockSec()); -} - -void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated, int32_t timeSec) { - lock_guard lock(mLock); - auto it = mConfigStats.find(key); - if (it == mConfigStats.end()) { - ALOGE("Config key %s not found!", key.ToString().c_str()); - return; - } - auto& vec = activated ? it->second->activation_time_sec - : it->second->deactivation_time_sec; - if (vec.size() == kMaxTimestampCount) { - vec.pop_front(); - } - vec.push_back(timeSec); -} - -void StatsdStats::noteActivationBroadcastGuardrailHit(const int uid) { - noteActivationBroadcastGuardrailHit(uid, getWallClockSec()); -} - -void StatsdStats::noteActivationBroadcastGuardrailHit(const int uid, const int32_t timeSec) { - lock_guard lock(mLock); - auto& guardrailTimes = mActivationBroadcastGuardrailStats[uid]; - if (guardrailTimes.size() == kMaxTimestampCount) { - guardrailTimes.pop_front(); - } - guardrailTimes.push_back(timeSec); -} - -void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes) { - noteDataDropped(key, totalBytes, getWallClockSec()); -} - -void StatsdStats::noteEventQueueOverflow(int64_t oldestEventTimestampNs) { - lock_guard lock(mLock); - - mOverflowCount++; - - int64_t history = getElapsedRealtimeNs() - oldestEventTimestampNs; - - if (history > mMaxQueueHistoryNs) { - mMaxQueueHistoryNs = history; - } - - if (history < mMinQueueHistoryNs) { - mMinQueueHistoryNs = history; - } -} - -void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec) { - lock_guard lock(mLock); - auto it = mConfigStats.find(key); - if (it == mConfigStats.end()) { - ALOGE("Config key %s not found!", key.ToString().c_str()); - return; - } - if (it->second->data_drop_time_sec.size() == kMaxTimestampCount) { - it->second->data_drop_time_sec.pop_front(); - it->second->data_drop_bytes.pop_front(); - } - it->second->data_drop_time_sec.push_back(timeSec); - it->second->data_drop_bytes.push_back(totalBytes); -} - -void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes) { - noteMetricsReportSent(key, num_bytes, getWallClockSec()); -} - -void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes, - int32_t timeSec) { - lock_guard lock(mLock); - auto it = mConfigStats.find(key); - if (it == mConfigStats.end()) { - ALOGE("Config key %s not found!", key.ToString().c_str()); - return; - } - if (it->second->dump_report_stats.size() == kMaxTimestampCount) { - it->second->dump_report_stats.pop_front(); - } - it->second->dump_report_stats.push_back(std::make_pair(timeSec, num_bytes)); -} - -void StatsdStats::noteUidMapDropped(int deltas) { - lock_guard lock(mLock); - mUidMapStats.dropped_changes += mUidMapStats.dropped_changes + deltas; -} - -void StatsdStats::noteUidMapAppDeletionDropped() { - lock_guard lock(mLock); - mUidMapStats.deleted_apps++; -} - -void StatsdStats::setUidMapChanges(int changes) { - lock_guard lock(mLock); - mUidMapStats.changes = changes; -} - -void StatsdStats::setCurrentUidMapMemory(int bytes) { - lock_guard lock(mLock); - mUidMapStats.bytes_used = bytes; -} - -void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) { - lock_guard lock(mLock); - // if name doesn't exist before, it will create the key with count 0. - auto statsIt = mConfigStats.find(key); - if (statsIt == mConfigStats.end()) { - return; - } - - auto& conditionSizeMap = statsIt->second->condition_stats; - if (size > conditionSizeMap[id]) { - conditionSizeMap[id] = size; - } -} - -void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) { - lock_guard lock(mLock); - // if name doesn't exist before, it will create the key with count 0. - auto statsIt = mConfigStats.find(key); - if (statsIt == mConfigStats.end()) { - return; - } - auto& metricsDimensionMap = statsIt->second->metric_stats; - if (size > metricsDimensionMap[id]) { - metricsDimensionMap[id] = size; - } -} - -void StatsdStats::noteMetricDimensionInConditionSize( - const ConfigKey& key, const int64_t& id, int size) { - lock_guard lock(mLock); - // if name doesn't exist before, it will create the key with count 0. - auto statsIt = mConfigStats.find(key); - if (statsIt == mConfigStats.end()) { - return; - } - auto& metricsDimensionMap = statsIt->second->metric_dimension_in_condition_stats; - if (size > metricsDimensionMap[id]) { - metricsDimensionMap[id] = size; - } -} - -void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) { - lock_guard lock(mLock); - - auto statsIt = mConfigStats.find(key); - if (statsIt == mConfigStats.end()) { - return; - } - statsIt->second->matcher_stats[id]++; -} - -void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) { - lock_guard lock(mLock); - auto statsIt = mConfigStats.find(key); - if (statsIt == mConfigStats.end()) { - return; - } - statsIt->second->alert_stats[id]++; -} - -void StatsdStats::noteRegisteredAnomalyAlarmChanged() { - lock_guard lock(mLock); - mAnomalyAlarmRegisteredStats++; -} - -void StatsdStats::noteRegisteredPeriodicAlarmChanged() { - lock_guard lock(mLock); - mPeriodicAlarmRegisteredStats++; -} - -void StatsdStats::updateMinPullIntervalSec(int pullAtomId, long intervalSec) { - lock_guard lock(mLock); - mPulledAtomStats[pullAtomId].minPullIntervalSec = - std::min(mPulledAtomStats[pullAtomId].minPullIntervalSec, intervalSec); -} - -void StatsdStats::notePull(int pullAtomId) { - lock_guard lock(mLock); - mPulledAtomStats[pullAtomId].totalPull++; -} - -void StatsdStats::notePullFromCache(int pullAtomId) { - lock_guard lock(mLock); - mPulledAtomStats[pullAtomId].totalPullFromCache++; -} - -void StatsdStats::notePullTime(int pullAtomId, int64_t pullTimeNs) { - lock_guard lock(mLock); - auto& pullStats = mPulledAtomStats[pullAtomId]; - pullStats.maxPullTimeNs = std::max(pullStats.maxPullTimeNs, pullTimeNs); - pullStats.avgPullTimeNs = (pullStats.avgPullTimeNs * pullStats.numPullTime + pullTimeNs) / - (pullStats.numPullTime + 1); - pullStats.numPullTime += 1; -} - -void StatsdStats::notePullDelay(int pullAtomId, int64_t pullDelayNs) { - lock_guard lock(mLock); - auto& pullStats = mPulledAtomStats[pullAtomId]; - pullStats.maxPullDelayNs = std::max(pullStats.maxPullDelayNs, pullDelayNs); - pullStats.avgPullDelayNs = - (pullStats.avgPullDelayNs * pullStats.numPullDelay + pullDelayNs) / - (pullStats.numPullDelay + 1); - pullStats.numPullDelay += 1; -} - -void StatsdStats::notePullDataError(int pullAtomId) { - lock_guard lock(mLock); - mPulledAtomStats[pullAtomId].dataError++; -} - -void StatsdStats::notePullTimeout(int pullAtomId, - int64_t pullUptimeMillis, - int64_t pullElapsedMillis) { - lock_guard lock(mLock); - PulledAtomStats& pulledAtomStats = mPulledAtomStats[pullAtomId]; - pulledAtomStats.pullTimeout++; - - if (pulledAtomStats.pullTimeoutMetadata.size() == kMaxTimestampCount) { - pulledAtomStats.pullTimeoutMetadata.pop_front(); - } - - pulledAtomStats.pullTimeoutMetadata.emplace_back(pullUptimeMillis, pullElapsedMillis); -} - -void StatsdStats::notePullExceedMaxDelay(int pullAtomId) { - lock_guard lock(mLock); - mPulledAtomStats[pullAtomId].pullExceedMaxDelay++; -} - -void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) { - lock_guard lock(mLock); - - if (atomId <= kMaxPushedAtomId) { - mPushedAtomStats[atomId]++; - } else { - if (mNonPlatformPushedAtomStats.size() < kMaxNonPlatformPushedAtoms) { - mNonPlatformPushedAtomStats[atomId]++; - } - } -} - -void StatsdStats::noteSystemServerRestart(int32_t timeSec) { - lock_guard lock(mLock); - - if (mSystemServerRestartSec.size() == kMaxSystemServerRestarts) { - mSystemServerRestartSec.pop_front(); - } - mSystemServerRestartSec.push_back(timeSec); -} - -void StatsdStats::notePullFailed(int atomId) { - lock_guard lock(mLock); - mPulledAtomStats[atomId].pullFailed++; -} - -void StatsdStats::notePullUidProviderNotFound(int atomId) { - lock_guard lock(mLock); - mPulledAtomStats[atomId].pullUidProviderNotFound++; -} - -void StatsdStats::notePullerNotFound(int atomId) { - lock_guard lock(mLock); - mPulledAtomStats[atomId].pullerNotFound++; -} - -void StatsdStats::notePullBinderCallFailed(int atomId) { - lock_guard lock(mLock); - mPulledAtomStats[atomId].binderCallFailCount++; -} - -void StatsdStats::noteEmptyData(int atomId) { - lock_guard lock(mLock); - mPulledAtomStats[atomId].emptyData++; -} - -void StatsdStats::notePullerCallbackRegistrationChanged(int atomId, bool registered) { - lock_guard lock(mLock); - if (registered) { - mPulledAtomStats[atomId].registeredCount++; - } else { - mPulledAtomStats[atomId].unregisteredCount++; - } -} - -void StatsdStats::noteHardDimensionLimitReached(int64_t metricId) { - lock_guard lock(mLock); - getAtomMetricStats(metricId).hardDimensionLimitReached++; -} - -void StatsdStats::noteLateLogEventSkipped(int64_t metricId) { - lock_guard lock(mLock); - getAtomMetricStats(metricId).lateLogEventSkipped++; -} - -void StatsdStats::noteSkippedForwardBuckets(int64_t metricId) { - lock_guard lock(mLock); - getAtomMetricStats(metricId).skippedForwardBuckets++; -} - -void StatsdStats::noteBadValueType(int64_t metricId) { - lock_guard lock(mLock); - getAtomMetricStats(metricId).badValueType++; -} - -void StatsdStats::noteBucketDropped(int64_t metricId) { - lock_guard lock(mLock); - getAtomMetricStats(metricId).bucketDropped++; -} - -void StatsdStats::noteBucketUnknownCondition(int64_t metricId) { - lock_guard lock(mLock); - getAtomMetricStats(metricId).bucketUnknownCondition++; -} - -void StatsdStats::noteConditionChangeInNextBucket(int64_t metricId) { - lock_guard lock(mLock); - getAtomMetricStats(metricId).conditionChangeInNextBucket++; -} - -void StatsdStats::noteInvalidatedBucket(int64_t metricId) { - lock_guard lock(mLock); - getAtomMetricStats(metricId).invalidatedBucket++; -} - -void StatsdStats::noteBucketCount(int64_t metricId) { - lock_guard lock(mLock); - getAtomMetricStats(metricId).bucketCount++; -} - -void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs) { - lock_guard lock(mLock); - AtomMetricStats& pullStats = getAtomMetricStats(metricId); - pullStats.maxBucketBoundaryDelayNs = - std::max(pullStats.maxBucketBoundaryDelayNs, timeDelayNs); - pullStats.minBucketBoundaryDelayNs = - std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs); -} - -void StatsdStats::noteAtomError(int atomTag, bool pull) { - lock_guard lock(mLock); - if (pull) { - mPulledAtomStats[atomTag].atomErrorCount++; - return; - } - - bool present = (mPushedAtomErrorStats.find(atomTag) != mPushedAtomErrorStats.end()); - bool full = (mPushedAtomErrorStats.size() >= (size_t)kMaxPushedAtomErrorStatsSize); - if (!full || present) { - mPushedAtomErrorStats[atomTag]++; - } -} - -StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) { - auto atomMetricStatsIter = mAtomMetricStats.find(metricId); - if (atomMetricStatsIter != mAtomMetricStats.end()) { - return atomMetricStatsIter->second; - } - auto emplaceResult = mAtomMetricStats.emplace(metricId, AtomMetricStats()); - return emplaceResult.first->second; -} - -void StatsdStats::reset() { - lock_guard lock(mLock); - resetInternalLocked(); -} - -void StatsdStats::resetInternalLocked() { - // Reset the historical data, but keep the active ConfigStats - mStartTimeSec = getWallClockSec(); - mIceBox.clear(); - std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0); - mNonPlatformPushedAtomStats.clear(); - mAnomalyAlarmRegisteredStats = 0; - mPeriodicAlarmRegisteredStats = 0; - mSystemServerRestartSec.clear(); - mLogLossStats.clear(); - mOverflowCount = 0; - mMinQueueHistoryNs = kInt64Max; - mMaxQueueHistoryNs = 0; - for (auto& config : mConfigStats) { - config.second->broadcast_sent_time_sec.clear(); - config.second->activation_time_sec.clear(); - config.second->deactivation_time_sec.clear(); - config.second->data_drop_time_sec.clear(); - config.second->data_drop_bytes.clear(); - config.second->dump_report_stats.clear(); - config.second->annotations.clear(); - config.second->matcher_stats.clear(); - config.second->condition_stats.clear(); - config.second->metric_stats.clear(); - config.second->metric_dimension_in_condition_stats.clear(); - config.second->alert_stats.clear(); - } - for (auto& pullStats : mPulledAtomStats) { - pullStats.second.totalPull = 0; - pullStats.second.totalPullFromCache = 0; - pullStats.second.minPullIntervalSec = LONG_MAX; - pullStats.second.avgPullTimeNs = 0; - pullStats.second.maxPullTimeNs = 0; - pullStats.second.numPullTime = 0; - pullStats.second.avgPullDelayNs = 0; - pullStats.second.maxPullDelayNs = 0; - pullStats.second.numPullDelay = 0; - pullStats.second.dataError = 0; - pullStats.second.pullTimeout = 0; - pullStats.second.pullExceedMaxDelay = 0; - pullStats.second.pullFailed = 0; - pullStats.second.pullUidProviderNotFound = 0; - pullStats.second.pullerNotFound = 0; - pullStats.second.registeredCount = 0; - pullStats.second.unregisteredCount = 0; - pullStats.second.atomErrorCount = 0; - pullStats.second.binderCallFailCount = 0; - pullStats.second.pullTimeoutMetadata.clear(); - } - mAtomMetricStats.clear(); - mActivationBroadcastGuardrailStats.clear(); - mPushedAtomErrorStats.clear(); -} - -string buildTimeString(int64_t timeSec) { - time_t t = timeSec; - struct tm* tm = localtime(&t); - char timeBuffer[80]; - strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p", tm); - return string(timeBuffer); -} - -int StatsdStats::getPushedAtomErrors(int atomId) const { - const auto& it = mPushedAtomErrorStats.find(atomId); - if (it != mPushedAtomErrorStats.end()) { - return it->second; - } else { - return 0; - } -} - -void StatsdStats::dumpStats(int out) const { - lock_guard lock(mLock); - time_t t = mStartTimeSec; - struct tm* tm = localtime(&t); - char timeBuffer[80]; - strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm); - dprintf(out, "Stats collection start second: %s\n", timeBuffer); - dprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size()); - for (const auto& configStats : mIceBox) { - dprintf(out, - "Config {%d_%lld}: creation=%d, deletion=%d, reset=%d, #metric=%d, #condition=%d, " - "#matcher=%d, #alert=%d, valid=%d\n", - configStats->uid, (long long)configStats->id, configStats->creation_time_sec, - configStats->deletion_time_sec, configStats->reset_time_sec, - configStats->metric_count, configStats->condition_count, configStats->matcher_count, - configStats->alert_count, configStats->is_valid); - - for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { - dprintf(out, "\tbroadcast time: %d\n", broadcastTime); - } - - for (const int& activationTime : configStats->activation_time_sec) { - dprintf(out, "\tactivation time: %d\n", activationTime); - } - - for (const int& deactivationTime : configStats->deactivation_time_sec) { - dprintf(out, "\tdeactivation time: %d\n", deactivationTime); - } - - auto dropTimePtr = configStats->data_drop_time_sec.begin(); - auto dropBytesPtr = configStats->data_drop_bytes.begin(); - for (int i = 0; i < (int)configStats->data_drop_time_sec.size(); - i++, dropTimePtr++, dropBytesPtr++) { - dprintf(out, "\tdata drop time: %d with size %lld", *dropTimePtr, - (long long)*dropBytesPtr); - } - } - dprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size()); - for (auto& pair : mConfigStats) { - auto& configStats = pair.second; - dprintf(out, - "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " - "#matcher=%d, #alert=%d, valid=%d\n", - configStats->uid, (long long)configStats->id, configStats->creation_time_sec, - configStats->deletion_time_sec, configStats->metric_count, - configStats->condition_count, configStats->matcher_count, configStats->alert_count, - configStats->is_valid); - for (const auto& annotation : configStats->annotations) { - dprintf(out, "\tannotation: %lld, %d\n", (long long)annotation.first, - annotation.second); - } - - for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { - dprintf(out, "\tbroadcast time: %s(%lld)\n", buildTimeString(broadcastTime).c_str(), - (long long)broadcastTime); - } - - for (const int& activationTime : configStats->activation_time_sec) { - dprintf(out, "\tactivation time: %d\n", activationTime); - } - - for (const int& deactivationTime : configStats->deactivation_time_sec) { - dprintf(out, "\tdeactivation time: %d\n", deactivationTime); - } - - auto dropTimePtr = configStats->data_drop_time_sec.begin(); - auto dropBytesPtr = configStats->data_drop_bytes.begin(); - for (int i = 0; i < (int)configStats->data_drop_time_sec.size(); - i++, dropTimePtr++, dropBytesPtr++) { - dprintf(out, "\tdata drop time: %s(%lld) with %lld bytes\n", - buildTimeString(*dropTimePtr).c_str(), (long long)*dropTimePtr, - (long long)*dropBytesPtr); - } - - for (const auto& dump : configStats->dump_report_stats) { - dprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n", - buildTimeString(dump.first).c_str(), (long long)dump.first, - (long long)dump.second); - } - - for (const auto& stats : pair.second->matcher_stats) { - dprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second); - } - - for (const auto& stats : pair.second->condition_stats) { - dprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first, - stats.second); - } - - for (const auto& stats : pair.second->condition_stats) { - dprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first, - stats.second); - } - - for (const auto& stats : pair.second->alert_stats) { - dprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second); - } - } - dprintf(out, "********Disk Usage stats***********\n"); - StorageManager::printStats(out); - dprintf(out, "********Pushed Atom stats***********\n"); - const size_t atomCounts = mPushedAtomStats.size(); - for (size_t i = 2; i < atomCounts; i++) { - if (mPushedAtomStats[i] > 0) { - dprintf(out, "Atom %zu->(total count)%d, (error count)%d\n", i, mPushedAtomStats[i], - getPushedAtomErrors((int)i)); - } - } - for (const auto& pair : mNonPlatformPushedAtomStats) { - dprintf(out, "Atom %d->(total count)%d, (error count)%d\n", pair.first, pair.second, - getPushedAtomErrors(pair.first)); - } - - dprintf(out, "********Pulled Atom stats***********\n"); - for (const auto& pair : mPulledAtomStats) { - dprintf(out, - "Atom %d->(total pull)%ld, (pull from cache)%ld, " - "(pull failed)%ld, (min pull interval)%ld \n" - " (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay " - "nanos)%lld, " - " (max pull delay nanos)%lld, (data error)%ld\n" - " (pull timeout)%ld, (pull exceed max delay)%ld" - " (no uid provider count)%ld, (no puller found count)%ld\n" - " (registered count) %ld, (unregistered count) %ld" - " (atom error count) %d\n", - (int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache, - (long)pair.second.pullFailed, (long)pair.second.minPullIntervalSec, - (long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs, - (long long)pair.second.avgPullDelayNs, (long long)pair.second.maxPullDelayNs, - pair.second.dataError, pair.second.pullTimeout, pair.second.pullExceedMaxDelay, - pair.second.pullUidProviderNotFound, pair.second.pullerNotFound, - pair.second.registeredCount, pair.second.unregisteredCount, - pair.second.atomErrorCount); - if (pair.second.pullTimeoutMetadata.size() > 0) { - string uptimeMillis = "(pull timeout system uptime millis) "; - string pullTimeoutMillis = "(pull timeout elapsed time millis) "; - for (const auto& stats : pair.second.pullTimeoutMetadata) { - uptimeMillis.append(to_string(stats.pullTimeoutUptimeMillis)).append(",");; - pullTimeoutMillis.append(to_string(stats.pullTimeoutElapsedMillis)).append(","); - } - uptimeMillis.pop_back(); - uptimeMillis.push_back('\n'); - pullTimeoutMillis.pop_back(); - pullTimeoutMillis.push_back('\n'); - dprintf(out, "%s", uptimeMillis.c_str()); - dprintf(out, "%s", pullTimeoutMillis.c_str()); - } - } - - if (mAnomalyAlarmRegisteredStats > 0) { - dprintf(out, "********AnomalyAlarmStats stats***********\n"); - dprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats); - } - - if (mPeriodicAlarmRegisteredStats > 0) { - dprintf(out, "********SubscriberAlarmStats stats***********\n"); - dprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats); - } - - dprintf(out, "UID map stats: bytes=%d, changes=%d, deleted=%d, changes lost=%d\n", - mUidMapStats.bytes_used, mUidMapStats.changes, mUidMapStats.deleted_apps, - mUidMapStats.dropped_changes); - - for (const auto& restart : mSystemServerRestartSec) { - dprintf(out, "System server restarts at %s(%lld)\n", buildTimeString(restart).c_str(), - (long long)restart); - } - - for (const auto& loss : mLogLossStats) { - dprintf(out, - "Log loss: %lld (wall clock sec) - %d (count), %d (last error), %d (last tag), %d " - "(uid), %d (pid)\n", - (long long)loss.mWallClockSec, loss.mCount, loss.mLastError, loss.mLastTag, - loss.mUid, loss.mPid); - } - - dprintf(out, "Event queue overflow: %d; MaxHistoryNs: %lld; MinHistoryNs: %lld\n", - mOverflowCount, (long long)mMaxQueueHistoryNs, (long long)mMinQueueHistoryNs); - - if (mActivationBroadcastGuardrailStats.size() > 0) { - dprintf(out, "********mActivationBroadcastGuardrail stats***********\n"); - for (const auto& pair: mActivationBroadcastGuardrailStats) { - dprintf(out, "Uid %d: Times: ", pair.first); - for (const auto& guardrailHitTime : pair.second) { - dprintf(out, "%d ", guardrailHitTime); - } - } - dprintf(out, "\n"); - } -} - -void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* proto) { - uint64_t token = - proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_UID, configStats.uid); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_ID, (long long)configStats.id); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CREATION, configStats.creation_time_sec); - if (configStats.reset_time_sec != 0) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_RESET, configStats.reset_time_sec); - } - if (configStats.deletion_time_sec != 0) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DELETION, - configStats.deletion_time_sec); - } - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_METRIC_COUNT, configStats.metric_count); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CONDITION_COUNT, - configStats.condition_count); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_MATCHER_COUNT, configStats.matcher_count); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ALERT_COUNT, configStats.alert_count); - proto->write(FIELD_TYPE_BOOL | FIELD_ID_CONFIG_STATS_VALID, configStats.is_valid); - - for (const auto& broadcast : configStats.broadcast_sent_time_sec) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_BROADCAST | FIELD_COUNT_REPEATED, - broadcast); - } - - for (const auto& activation : configStats.activation_time_sec) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ACTIVATION | FIELD_COUNT_REPEATED, - activation); - } - - for (const auto& deactivation : configStats.deactivation_time_sec) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DEACTIVATION | FIELD_COUNT_REPEATED, - deactivation); - } - - for (const auto& drop_time : configStats.data_drop_time_sec) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP_TIME | FIELD_COUNT_REPEATED, - drop_time); - } - - for (const auto& drop_bytes : configStats.data_drop_bytes) { - proto->write( - FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_DATA_DROP_BYTES | FIELD_COUNT_REPEATED, - (long long)drop_bytes); - } - - for (const auto& dump : configStats.dump_report_stats) { - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME | - FIELD_COUNT_REPEATED, - dump.first); - } - - for (const auto& dump : configStats.dump_report_stats) { - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES | - FIELD_COUNT_REPEATED, - (long long)dump.second); - } - - for (const auto& annotation : configStats.annotations) { - uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_ANNOTATION); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_ANNOTATION_INT64, - (long long)annotation.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ANNOTATION_INT32, annotation.second); - proto->end(token); - } - - for (const auto& pair : configStats.matcher_stats) { - uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_MATCHER_STATS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_MATCHER_STATS_ID, (long long)pair.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_MATCHER_STATS_COUNT, pair.second); - proto->end(tmpToken); - } - - for (const auto& pair : configStats.condition_stats) { - uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_CONDITION_STATS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_STATS_ID, (long long)pair.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONDITION_STATS_COUNT, pair.second); - proto->end(tmpToken); - } - - for (const auto& pair : configStats.metric_stats) { - uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_METRIC_STATS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second); - proto->end(tmpToken); - } - for (const auto& pair : configStats.metric_dimension_in_condition_stats) { - uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second); - proto->end(tmpToken); - } - - for (const auto& pair : configStats.alert_stats) { - uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_CONFIG_STATS_ALERT_STATS); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_STATS_ID, (long long)pair.first); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_ALERT_STATS_COUNT, pair.second); - proto->end(tmpToken); - } - - proto->end(token); -} - -void StatsdStats::dumpStats(std::vector* output, bool reset) { - lock_guard lock(mLock); - - ProtoOutputStream proto; - proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTimeSec); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)getWallClockSec()); - - for (const auto& configStats : mIceBox) { - addConfigStatsToProto(*configStats, &proto); - } - - for (auto& pair : mConfigStats) { - addConfigStatsToProto(*(pair.second), &proto); - } - - const size_t atomCounts = mPushedAtomStats.size(); - for (size_t i = 2; i < atomCounts; i++) { - if (mPushedAtomStats[i] > 0) { - uint64_t token = - proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]); - int errors = getPushedAtomErrors(i); - if (errors > 0) { - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors); - } - proto.end(token); - } - } - - for (const auto& pair : mNonPlatformPushedAtomStats) { - uint64_t token = - proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, pair.first); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, pair.second); - int errors = getPushedAtomErrors(pair.first); - if (errors > 0) { - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors); - } - proto.end(token); - } - - for (const auto& pair : mPulledAtomStats) { - android::os::statsd::writePullerStatsToStream(pair, &proto); - } - - for (const auto& pair : mAtomMetricStats) { - android::os::statsd::writeAtomMetricStatsToStream(pair, &proto); - } - - if (mAnomalyAlarmRegisteredStats > 0) { - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ANOMALY_ALARMS_REGISTERED, - mAnomalyAlarmRegisteredStats); - proto.end(token); - } - - if (mPeriodicAlarmRegisteredStats > 0) { - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PERIODIC_ALARM_STATS); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_PERIODIC_ALARMS_REGISTERED, - mPeriodicAlarmRegisteredStats); - proto.end(token); - } - - uint64_t uidMapToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_CHANGES, mUidMapStats.changes); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_BYTES_USED, mUidMapStats.bytes_used); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_CHANGES, mUidMapStats.dropped_changes); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DELETED_APPS, mUidMapStats.deleted_apps); - proto.end(uidMapToken); - - for (const auto& error : mLogLossStats) { - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_LOGGER_ERROR_STATS | - FIELD_COUNT_REPEATED); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_TIME, error.mWallClockSec); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_COUNT, error.mCount); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_ERROR, error.mLastError); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_TAG, error.mLastTag); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_UID, error.mUid); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_PID, error.mPid); - proto.end(token); - } - - if (mOverflowCount > 0) { - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_OVERFLOW); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_OVERFLOW_COUNT, (int32_t)mOverflowCount); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_OVERFLOW_MAX_HISTORY, - (long long)mMaxQueueHistoryNs); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_OVERFLOW_MIN_HISTORY, - (long long)mMinQueueHistoryNs); - proto.end(token); - } - - for (const auto& restart : mSystemServerRestartSec) { - proto.write(FIELD_TYPE_INT32 | FIELD_ID_SYSTEM_SERVER_RESTART | FIELD_COUNT_REPEATED, - restart); - } - - for (const auto& pair: mActivationBroadcastGuardrailStats) { - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | - FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL | - FIELD_COUNT_REPEATED); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID, - (int32_t) pair.first); - for (const auto& guardrailHitTime : pair.second) { - proto.write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME | - FIELD_COUNT_REPEATED, - guardrailHitTime); - } - proto.end(token); - } - - output->clear(); - size_t bufferSize = proto.size(); - output->resize(bufferSize); - - size_t pos = 0; - sp reader = proto.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((*output)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - if (reset) { - resetInternalLocked(); - } - - VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)bufferSize); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h deleted file mode 100644 index 005048446fc3..000000000000 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ /dev/null @@ -1,678 +0,0 @@ -/* - * Copyright 2017, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include "config/ConfigKey.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace android { -namespace os { -namespace statsd { - -struct ConfigStats { - int32_t uid; - int64_t id; - int32_t creation_time_sec; - int32_t deletion_time_sec = 0; - int32_t reset_time_sec = 0; - int32_t metric_count; - int32_t condition_count; - int32_t matcher_count; - int32_t alert_count; - bool is_valid; - - std::list broadcast_sent_time_sec; - - // Times at which this config is activated. - std::list activation_time_sec; - - // Times at which this config is deactivated. - std::list deactivation_time_sec; - - std::list data_drop_time_sec; - // Number of bytes dropped at corresponding time. - std::list data_drop_bytes; - std::list> dump_report_stats; - - // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount. - std::map matcher_stats; - - // Stores the number of output tuple of condition trackers when it's bigger than - // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, - // it means some data has been dropped. The map size is capped by kMaxConfigCount. - std::map condition_stats; - - // Stores the number of output tuple of metric producers when it's bigger than - // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, - // it means some data has been dropped. The map size is capped by kMaxConfigCount. - std::map metric_stats; - - // Stores the max number of output tuple of dimensions in condition across dimensions in what - // when it's bigger than kDimensionKeySizeSoftLimit. When you see the number is - // kDimensionKeySizeHardLimit +1, it means some data has been dropped. The map size is capped by - // kMaxConfigCount. - std::map metric_dimension_in_condition_stats; - - // Stores the number of times an anomaly detection alert has been declared. - // The map size is capped by kMaxConfigCount. - std::map alert_stats; - - // Stores the config ID for each sub-config used. - std::list> annotations; -}; - -struct UidMapStats { - int32_t changes; - int32_t bytes_used; - int32_t dropped_changes; - int32_t deleted_apps = 0; -}; - -// Keeps track of stats of statsd. -// Single instance shared across the process. All public methods are thread safe. -class StatsdStats { -public: - static StatsdStats& getInstance(); - ~StatsdStats(){}; - - const static int kDimensionKeySizeSoftLimit = 500; - const static int kDimensionKeySizeHardLimit = 800; - - // Per atom dimension key size limit - static const std::map> kAtomDimensionKeySizeLimitMap; - - const static int kMaxConfigCountPerUid = 20; - const static int kMaxAlertCountPerConfig = 100; - const static int kMaxConditionCountPerConfig = 300; - const static int kMaxMetricCountPerConfig = 1000; - const static int kMaxMatcherCountPerConfig = 800; - - // The max number of old config stats we keep. - const static int kMaxIceBoxSize = 20; - - const static int kMaxLoggerErrors = 20; - - const static int kMaxSystemServerRestarts = 20; - - const static int kMaxTimestampCount = 20; - - const static int kMaxLogSourceCount = 50; - - const static int kMaxPullAtomPackages = 100; - - // Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd - // drops the metrics data in memory. - static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024; - - // Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the - // data subscriber that it's time to call getData. - static const size_t kBytesPerConfigTriggerGetData = 192 * 1024; - - // Cap the UID map's memory usage to this. This should be fairly high since the UID information - // is critical for understanding the metrics. - const static size_t kMaxBytesUsedUidMap = 50 * 1024; - - // The number of deleted apps that are stored in the uid map. - const static int kMaxDeletedAppsInUidMap = 100; - - /* Minimum period between two broadcasts in nanoseconds. */ - static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC; - - /* Min period between two checks of byte size per config key in nanoseconds. */ - static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC; - - /* Minimum period between two activation broadcasts in nanoseconds. */ - static const int64_t kMinActivationBroadcastPeriodNs = 10 * NS_PER_SEC; - - // Maximum age (30 days) that files on disk can exist in seconds. - static const int kMaxAgeSecond = 60 * 60 * 24 * 30; - - // Maximum age (2 days) that local history files on disk can exist in seconds. - static const int kMaxLocalHistoryAgeSecond = 60 * 60 * 24 * 2; - - // Maximum number of files (1000) that can be in stats directory on disk. - static const int kMaxFileNumber = 1000; - - // Maximum size of all files that can be written to stats directory on disk. - static const int kMaxFileSize = 50 * 1024 * 1024; - - // How long to try to clear puller cache from last time - static const long kPullerCacheClearIntervalSec = 1; - - // Max time to do a pull. - static const int64_t kPullMaxDelayNs = 30 * NS_PER_SEC; - - // Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId. - static const int kMaxNonPlatformPushedAtoms = 100; - - // Maximum atom id value that we consider a platform pushed atom. - // This should be updated once highest pushed atom id in atoms.proto approaches this value. - static const int kMaxPushedAtomId = 500; - - // Atom id that is the start of the pulled atoms. - static const int kPullAtomStartTag = 10000; - - // Atom id that is the start of vendor atoms. - static const int kVendorAtomStartTag = 100000; - - // Vendor pulled atom start id. - static const int32_t kVendorPulledAtomStartTag = 150000; - - // Beginning of range for timestamp truncation. - static const int32_t kTimestampTruncationStartTag = 300000; - - // End of range for timestamp truncation. - static const int32_t kTimestampTruncationEndTag = 304999; - - // Max accepted atom id. - static const int32_t kMaxAtomTag = 200000; - - static const int64_t kInt64Max = 0x7fffffffffffffffLL; - - static const int32_t kMaxLoggedBucketDropEvents = 10; - - /** - * Report a new config has been received and report the static stats about the config. - * - * The static stats include: the count of metrics, conditions, matchers, and alerts. - * If the config is not valid, this config stats will be put into icebox immediately. - */ - void noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount, - int matchersCount, int alertCount, - const std::list>& annotations, - bool isValid); - /** - * Report a config has been removed. - */ - void noteConfigRemoved(const ConfigKey& key); - /** - * Report a config has been reset when ttl expires. - */ - void noteConfigReset(const ConfigKey& key); - - /** - * Report a broadcast has been sent to a config owner to collect the data. - */ - void noteBroadcastSent(const ConfigKey& key); - - /** - * Report that a config has become activated or deactivated. - * This can be different from whether or not a broadcast is sent if the - * guardrail prevented the broadcast from being sent. - */ - void noteActiveStatusChanged(const ConfigKey& key, bool activate); - - /** - * Report a config's metrics data has been dropped. - */ - void noteDataDropped(const ConfigKey& key, const size_t totalBytes); - - /** - * Report metrics data report has been sent. - * - * The report may be requested via StatsManager API, or through adb cmd. - */ - void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes); - - /** - * Report the size of output tuple of a condition. - * - * Note: only report when the condition has an output dimension, and the tuple - * count > kDimensionKeySizeSoftLimit. - * - * [key]: The config key that this condition belongs to. - * [id]: The id of the condition. - * [size]: The output tuple size. - */ - void noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size); - - /** - * Report the size of output tuple of a metric. - * - * Note: only report when the metric has an output dimension, and the tuple - * count > kDimensionKeySizeSoftLimit. - * - * [key]: The config key that this metric belongs to. - * [id]: The id of the metric. - * [size]: The output tuple size. - */ - void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size); - - /** - * Report the max size of output tuple of dimension in condition across dimensions in what. - * - * Note: only report when the metric has an output dimension in condition, and the max tuple - * count > kDimensionKeySizeSoftLimit. - * - * [key]: The config key that this metric belongs to. - * [id]: The id of the metric. - * [size]: The output tuple size. - */ - void noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t& id, int size); - - /** - * Report a matcher has been matched. - * - * [key]: The config key that this matcher belongs to. - * [id]: The id of the matcher. - */ - void noteMatcherMatched(const ConfigKey& key, const int64_t& id); - - /** - * Report that an anomaly detection alert has been declared. - * - * [key]: The config key that this alert belongs to. - * [id]: The id of the alert. - */ - void noteAnomalyDeclared(const ConfigKey& key, const int64_t& id); - - /** - * Report an atom event has been logged. - */ - void noteAtomLogged(int atomId, int32_t timeSec); - - /** - * Report that statsd modified the anomaly alarm registered with StatsCompanionService. - */ - void noteRegisteredAnomalyAlarmChanged(); - - /** - * Report that statsd modified the periodic alarm registered with StatsCompanionService. - */ - void noteRegisteredPeriodicAlarmChanged(); - - /** - * Records the number of delta entries that are being dropped from the uid map. - */ - void noteUidMapDropped(int deltas); - - /** - * Records that an app was deleted (from statsd's map). - */ - void noteUidMapAppDeletionDropped(); - - /** - * Updates the number of changes currently stored in the uid map. - */ - void setUidMapChanges(int changes); - void setCurrentUidMapMemory(int bytes); - - /* - * Updates minimum interval between pulls for an pulled atom. - */ - void updateMinPullIntervalSec(int pullAtomId, long intervalSec); - - /* - * Notes an atom is pulled. - */ - void notePull(int pullAtomId); - - /* - * Notes an atom is served from puller cache. - */ - void notePullFromCache(int pullAtomId); - - /* - * Notify data error for pulled atom. - */ - void notePullDataError(int pullAtomId); - - /* - * Records time for actual pulling, not including those served from cache and not including - * statsd processing delays. - */ - void notePullTime(int pullAtomId, int64_t pullTimeNs); - - /* - * Records pull delay for a pulled atom, including those served from cache and including statsd - * processing delays. - */ - void notePullDelay(int pullAtomId, int64_t pullDelayNs); - - /* - * Records pull exceeds timeout for the puller. - */ - void notePullTimeout(int pullAtomId, int64_t pullUptimeMillis, int64_t pullElapsedMillis); - - /* - * Records pull exceeds max delay for a metric. - */ - void notePullExceedMaxDelay(int pullAtomId); - - /* - * Records when system server restarts. - */ - void noteSystemServerRestart(int32_t timeSec); - - /** - * Records statsd skipped an event. - */ - void noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError, - int32_t lastAtomTag, int32_t uid, int32_t pid); - - /** - * Records that the pull of an atom has failed. Eg, if the client indicated the pull failed, if - * the pull timed out, or if the outgoing binder call failed. - * This count will only increment if the puller was actually invoked. - * - * It does not include a pull not occurring due to not finding the appropriate - * puller. These cases are covered in other counts. - */ - void notePullFailed(int atomId); - - /** - * Records that the pull of an atom has failed due to not having a uid provider. - */ - void notePullUidProviderNotFound(int atomId); - - /** - * Records that the pull of an atom has failed due not finding a puller registered by a - * trusted uid. - */ - void notePullerNotFound(int atomId); - - /** - * Records that the pull has failed due to the outgoing binder call failing. - */ - void notePullBinderCallFailed(int atomId); - - /** - * A pull with no data occurred - */ - void noteEmptyData(int atomId); - - /** - * Records that a puller callback for the given atomId was registered or unregistered. - * - * @param registered True if the callback was registered, false if was unregistered. - */ - void notePullerCallbackRegistrationChanged(int atomId, bool registered); - - /** - * Hard limit was reached in the cardinality of an atom - */ - void noteHardDimensionLimitReached(int64_t metricId); - - /** - * A log event was too late, arrived in the wrong bucket and was skipped - */ - void noteLateLogEventSkipped(int64_t metricId); - - /** - * Buckets were skipped as time elapsed without any data for them - */ - void noteSkippedForwardBuckets(int64_t metricId); - - /** - * An unsupported value type was received - */ - void noteBadValueType(int64_t metricId); - - /** - * Buckets were dropped due to reclaim memory. - */ - void noteBucketDropped(int64_t metricId); - - /** - * A condition change was too late, arrived in the wrong bucket and was skipped - */ - void noteConditionChangeInNextBucket(int64_t metricId); - - /** - * A bucket has been tagged as invalid. - */ - void noteInvalidatedBucket(int64_t metricId); - - /** - * Tracks the total number of buckets (include skipped/invalid buckets). - */ - void noteBucketCount(int64_t metricId); - - /** - * For pulls at bucket boundaries, it represents the misalignment between the real timestamp and - * the end of the bucket. - */ - void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs); - - /** - * Number of buckets with unknown condition. - */ - void noteBucketUnknownCondition(int64_t metricId); - - /* Reports one event has been dropped due to queue overflow, and the oldest event timestamp in - * the queue */ - void noteEventQueueOverflow(int64_t oldestEventTimestampNs); - - /** - * Reports that the activation broadcast guardrail was hit for this uid. Namely, the broadcast - * should have been sent, but instead was skipped due to hitting the guardrail. - */ - void noteActivationBroadcastGuardrailHit(const int uid); - - /** - * Reports that an atom is erroneous or cannot be parsed successfully by - * statsd. An atom tag of 0 indicates that the client did not supply the - * atom id within the encoding. - * - * For pushed atoms only, this call should be preceded by a call to - * noteAtomLogged. - */ - void noteAtomError(int atomTag, bool pull=false); - - /** - * Reset the historical stats. Including all stats in icebox, and the tracked stats about - * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue - * to collect stats after reset() has been called. - */ - void reset(); - - /** - * Output the stats in protobuf binary format to [buffer]. - * - * [reset]: whether to clear the historical stats after the call. - */ - void dumpStats(std::vector* buffer, bool reset); - - /** - * Output statsd stats in human readable format to [out] file descriptor. - */ - void dumpStats(int outFd) const; - - typedef struct PullTimeoutMetadata { - int64_t pullTimeoutUptimeMillis; - int64_t pullTimeoutElapsedMillis; - PullTimeoutMetadata(int64_t uptimeMillis, int64_t elapsedMillis) : - pullTimeoutUptimeMillis(uptimeMillis), - pullTimeoutElapsedMillis(elapsedMillis) {/* do nothing */} - } PullTimeoutMetadata; - - typedef struct { - long totalPull = 0; - long totalPullFromCache = 0; - long minPullIntervalSec = LONG_MAX; - int64_t avgPullTimeNs = 0; - int64_t maxPullTimeNs = 0; - long numPullTime = 0; - int64_t avgPullDelayNs = 0; - int64_t maxPullDelayNs = 0; - long numPullDelay = 0; - long dataError = 0; - long pullTimeout = 0; - long pullExceedMaxDelay = 0; - long pullFailed = 0; - long pullUidProviderNotFound = 0; - long pullerNotFound = 0; - long emptyData = 0; - long registeredCount = 0; - long unregisteredCount = 0; - int32_t atomErrorCount = 0; - long binderCallFailCount = 0; - std::list pullTimeoutMetadata; - } PulledAtomStats; - - typedef struct { - long hardDimensionLimitReached = 0; - long lateLogEventSkipped = 0; - long skippedForwardBuckets = 0; - long badValueType = 0; - long conditionChangeInNextBucket = 0; - long invalidatedBucket = 0; - long bucketDropped = 0; - int64_t minBucketBoundaryDelayNs = 0; - int64_t maxBucketBoundaryDelayNs = 0; - long bucketUnknownCondition = 0; - long bucketCount = 0; - } AtomMetricStats; - -private: - StatsdStats(); - - mutable std::mutex mLock; - - int32_t mStartTimeSec; - - // Track the number of dropped entries used by the uid map. - UidMapStats mUidMapStats; - - // The stats about the configs that are still in use. - // The map size is capped by kMaxConfigCount. - std::map> mConfigStats; - - // Stores the stats for the configs that are no longer in use. - // The size of the vector is capped by kMaxIceBoxSize. - std::list> mIceBox; - - // Stores the number of times a pushed atom is logged. - // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms - // out of that range will be put in mNonPlatformPushedAtomStats. - // This is a vector, not a map because it will be accessed A LOT -- for each stats log. - std::vector mPushedAtomStats; - - // Stores the number of times a pushed atom is logged for atom ids above kMaxPushedAtomId. - // The max size of the map is kMaxNonPlatformPushedAtoms. - std::unordered_map mNonPlatformPushedAtomStats; - - // Maps PullAtomId to its stats. The size is capped by the puller atom counts. - std::map mPulledAtomStats; - - // Stores the number of times a pushed atom was logged erroneously. The - // corresponding counts for pulled atoms are stored in PulledAtomStats. - // The max size of this map is kMaxAtomErrorsStatsSize. - std::map mPushedAtomErrorStats; - int kMaxPushedAtomErrorStatsSize = 100; - - // Maps metric ID to its stats. The size is capped by the number of metrics. - std::map mAtomMetricStats; - - // Maps uids to times when the activation changed broadcast not sent due to hitting the - // guardrail. The size is capped by the number of configs, and up to 20 times per uid. - std::map> mActivationBroadcastGuardrailStats; - - struct LogLossStats { - LogLossStats(int32_t sec, int32_t count, int32_t error, int32_t tag, int32_t uid, - int32_t pid) - : mWallClockSec(sec), - mCount(count), - mLastError(error), - mLastTag(tag), - mUid(uid), - mPid(pid) { - } - int32_t mWallClockSec; - int32_t mCount; - // error code defined in linux/errno.h - int32_t mLastError; - int32_t mLastTag; - int32_t mUid; - int32_t mPid; - }; - - // Max of {(now - oldestEventTimestamp) when overflow happens}. - // This number is helpful to understand how SLOW statsd can be. - int64_t mMaxQueueHistoryNs = 0; - - // Min of {(now - oldestEventTimestamp) when overflow happens}. - // This number is helpful to understand how FAST the events floods to statsd. - int64_t mMinQueueHistoryNs = kInt64Max; - - // Total number of events that are lost due to queue overflow. - int32_t mOverflowCount = 0; - - // Timestamps when we detect log loss, and the number of logs lost. - std::list mLogLossStats; - - std::list mSystemServerRestartSec; - - // Stores the number of times statsd modified the anomaly alarm registered with - // StatsCompanionService. - int mAnomalyAlarmRegisteredStats = 0; - - // Stores the number of times statsd registers the periodic alarm changes - int mPeriodicAlarmRegisteredStats = 0; - - void noteConfigResetInternalLocked(const ConfigKey& key); - - void noteConfigRemovedInternalLocked(const ConfigKey& key); - - void resetInternalLocked(); - - void noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec); - - void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes, int32_t timeSec); - - void noteBroadcastSent(const ConfigKey& key, int32_t timeSec); - - void noteActiveStatusChanged(const ConfigKey& key, bool activate, int32_t timeSec); - - void noteActivationBroadcastGuardrailHit(const int uid, int32_t timeSec); - - void addToIceBoxLocked(std::shared_ptr& stats); - - int getPushedAtomErrors(int atomId) const; - - /** - * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference - * will live as long as `this`. - */ - StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId); - - FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd); - FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd); - FRIEND_TEST(StatsdStatsTest, TestConfigRemove); - FRIEND_TEST(StatsdStatsTest, TestSubStats); - FRIEND_TEST(StatsdStatsTest, TestAtomLog); - FRIEND_TEST(StatsdStatsTest, TestNonPlatformAtomLog); - FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold); - FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor); - FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash); - FRIEND_TEST(StatsdStatsTest, TestPullAtomStats); - FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats); - FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit); - FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats); - - FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/hash.cpp b/cmds/statsd/src/hash.cpp deleted file mode 100644 index 543a748adedb..000000000000 --- a/cmds/statsd/src/hash.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "hash.h" - -#ifndef FALLTHROUGH_INTENDED -#define FALLTHROUGH_INTENDED [[fallthrough]] -#endif - -namespace android { -namespace os { -namespace statsd { - -namespace { -// Lower-level versions of Get... that read directly from a character buffer -// without any bounds checking. -inline uint32_t DecodeFixed32(const char *ptr) { - return ((static_cast(static_cast(ptr[0]))) | - (static_cast(static_cast(ptr[1])) << 8) | - (static_cast(static_cast(ptr[2])) << 16) | - (static_cast(static_cast(ptr[3])) << 24)); -} - -inline uint64_t DecodeFixed64(const char* ptr) { - uint64_t lo = DecodeFixed32(ptr); - uint64_t hi = DecodeFixed32(ptr + 4); - return (hi << 32) | lo; -} - -// 0xff is in case char is signed. -static inline uint32_t ByteAs32(char c) { return static_cast(c) & 0xff; } -static inline uint64_t ByteAs64(char c) { return static_cast(c) & 0xff; } - -} // namespace - -uint32_t Hash32(const char *data, size_t n, uint32_t seed) { - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - const uint32_t m = 0x5bd1e995; - const int r = 24; - - // Initialize the hash to a 'random' value - uint32_t h = static_cast(seed ^ n); - - // Mix 4 bytes at a time into the hash - while (n >= 4) { - uint32_t k = DecodeFixed32(data); - k *= m; - k ^= k >> r; - k *= m; - h *= m; - h ^= k; - data += 4; - n -= 4; - } - - // Handle the last few bytes of the input array - switch (n) { - case 3: - h ^= ByteAs32(data[2]) << 16; - FALLTHROUGH_INTENDED; - case 2: - h ^= ByteAs32(data[1]) << 8; - FALLTHROUGH_INTENDED; - case 1: - h ^= ByteAs32(data[0]); - h *= m; - } - - // Do a few final mixes of the hash to ensure the last few - // bytes are well-incorporated. - h ^= h >> 13; - h *= m; - h ^= h >> 15; - return h; -} - -uint64_t Hash64(const char* data, size_t n, uint64_t seed) { - const uint64_t m = 0xc6a4a7935bd1e995; - const int r = 47; - - uint64_t h = seed ^ (n * m); - - while (n >= 8) { - uint64_t k = DecodeFixed64(data); - data += 8; - n -= 8; - - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - } - - switch (n) { - case 7: - h ^= ByteAs64(data[6]) << 48; - FALLTHROUGH_INTENDED; - case 6: - h ^= ByteAs64(data[5]) << 40; - FALLTHROUGH_INTENDED; - case 5: - h ^= ByteAs64(data[4]) << 32; - FALLTHROUGH_INTENDED; - case 4: - h ^= ByteAs64(data[3]) << 24; - FALLTHROUGH_INTENDED; - case 3: - h ^= ByteAs64(data[2]) << 16; - FALLTHROUGH_INTENDED; - case 2: - h ^= ByteAs64(data[1]) << 8; - FALLTHROUGH_INTENDED; - case 1: - h ^= ByteAs64(data[0]); - h *= m; - } - - h ^= h >> r; - h *= m; - h ^= h >> r; - - return h; -} -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/hash.h b/cmds/statsd/src/hash.h deleted file mode 100644 index cfe869f60202..000000000000 --- a/cmds/statsd/src/hash.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace android { -namespace os { -namespace statsd { - -extern uint32_t Hash32(const char *data, size_t n, uint32_t seed); -extern uint64_t Hash64(const char* data, size_t n, uint64_t seed); - -inline uint32_t Hash32(const char *data, size_t n) { - return Hash32(data, n, 0xBEEF); -} - -inline uint32_t Hash32(const std::string &input) { - return Hash32(input.data(), input.size()); -} - -inline uint64_t Hash64(const char* data, size_t n) { - return Hash64(data, n, 0xDECAFCAFFE); -} - -inline uint64_t Hash64(const std::string& str) { - return Hash64(str.data(), str.size()); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp deleted file mode 100644 index f56fa6221bc9..000000000000 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "logd/LogEvent.h" - -#include -#include -#include - -#include "annotations.h" -#include "stats_log_util.h" -#include "statslog_statsd.h" - -namespace android { -namespace os { -namespace statsd { - -// for TrainInfo experiment id serialization -const int FIELD_ID_EXPERIMENT_ID = 1; - -using namespace android::util; -using android::base::StringPrintf; -using android::util::ProtoOutputStream; -using std::string; -using std::vector; - -// stats_event.h socket types. Keep in sync. -/* ERRORS */ -#define ERROR_NO_TIMESTAMP 0x1 -#define ERROR_NO_ATOM_ID 0x2 -#define ERROR_OVERFLOW 0x4 -#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8 -#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10 -#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20 -#define ERROR_INVALID_ANNOTATION_ID 0x40 -#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80 -#define ERROR_TOO_MANY_ANNOTATIONS 0x100 -#define ERROR_TOO_MANY_FIELDS 0x200 -#define ERROR_INVALID_VALUE_TYPE 0x400 -#define ERROR_STRING_NOT_NULL_TERMINATED 0x800 - -/* TYPE IDS */ -#define INT32_TYPE 0x00 -#define INT64_TYPE 0x01 -#define STRING_TYPE 0x02 -#define LIST_TYPE 0x03 -#define FLOAT_TYPE 0x04 -#define BOOL_TYPE 0x05 -#define BYTE_ARRAY_TYPE 0x06 -#define OBJECT_TYPE 0x07 -#define KEY_VALUE_PAIRS_TYPE 0x08 -#define ATTRIBUTION_CHAIN_TYPE 0x09 -#define ERROR_TYPE 0x0F - -LogEvent::LogEvent(int32_t uid, int32_t pid) - : mLogdTimestampNs(time(nullptr)), mLogUid(uid), mLogPid(pid) { -} - -LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging, - bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, - const std::vector& experimentIds, int32_t userId) { - mLogdTimestampNs = getWallClockNs(); - mElapsedTimestampNs = getElapsedRealtimeNs(); - mTagId = util::BINARY_PUSH_STATE_CHANGED; - mLogUid = AIBinder_getCallingUid(); - mLogPid = AIBinder_getCallingPid(); - - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled))); - mValues.push_back( - FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId))); -} - -LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, - const InstallTrainInfo& trainInfo) { - mLogdTimestampNs = wallClockTimestampNs; - mElapsedTimestampNs = elapsedTimestampNs; - mTagId = util::TRAIN_INFO; - - mValues.push_back( - FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode))); - std::vector experimentIdsProto; - writeExperimentIdsToProto(trainInfo.experimentIds, &experimentIdsProto); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(experimentIdsProto))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value(trainInfo.trainName))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status))); -} - -void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - int32_t value = readNextValue(); - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - int64_t value = readNextValue(); - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - int32_t numBytes = readNextValue(); - if ((uint32_t)numBytes > mRemainingLen) { - mValid = false; - return; - } - - string value = string((char*)mBuf, numBytes); - mBuf += numBytes; - mRemainingLen -= numBytes; - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - float value = readNextValue(); - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - // cast to int32_t because FieldValue does not support bools - int32_t value = (int32_t)readNextValue(); - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - int32_t numBytes = readNextValue(); - if ((uint32_t)numBytes > mRemainingLen) { - mValid = false; - return; - } - - vector value(mBuf, mBuf + numBytes); - mBuf += numBytes; - mRemainingLen -= numBytes; - addToValues(pos, depth, value, last); - parseAnnotations(numAnnotations); -} - -void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { - int32_t numPairs = readNextValue(); - - for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) { - last[1] = (pos[1] == numPairs); - - // parse key - pos[2] = 1; - parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0); - - // parse value - last[2] = true; - - uint8_t typeInfo = readNextValue(); - switch (getTypeId(typeInfo)) { - case INT32_TYPE: - pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto - parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0); - break; - case INT64_TYPE: - pos[2] = 3; - parseInt64(pos, /*depth=*/2, last, /*numAnnotations=*/0); - break; - case STRING_TYPE: - pos[2] = 4; - parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0); - break; - case FLOAT_TYPE: - pos[2] = 5; - parseFloat(pos, /*depth=*/2, last, /*numAnnotations=*/0); - break; - default: - mValid = false; - } - } - - parseAnnotations(numAnnotations); - - pos[1] = pos[2] = 1; - last[1] = last[2] = false; -} - -void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last, - uint8_t numAnnotations) { - const unsigned int firstUidInChainIndex = mValues.size(); - const int32_t numNodes = readNextValue(); - for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) { - last[1] = (pos[1] == numNodes); - - // parse uid - pos[2] = 1; - parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0); - - // parse tag - pos[2] = 2; - last[2] = true; - parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0); - } - // Check if at least one node was successfully parsed. - if (mValues.size() - 1 > firstUidInChainIndex) { - mAttributionChainStartIndex = static_cast(firstUidInChainIndex); - mAttributionChainEndIndex = static_cast(mValues.size() - 1); - } - - parseAnnotations(numAnnotations, firstUidInChainIndex); - - pos[1] = pos[2] = 1; - last[1] = last[2] = false; -} - -// Assumes that mValues is not empty -bool LogEvent::checkPreviousValueType(Type expected) { - return mValues[mValues.size() - 1].mValue.getType() == expected; -} - -void LogEvent::parseIsUidAnnotation(uint8_t annotationType) { - if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) { - mValid = false; - return; - } - - bool isUid = readNextValue(); - if (isUid) mUidFieldIndex = static_cast(mValues.size() - 1); - mValues[mValues.size() - 1].mAnnotations.setUidField(isUid); -} - -void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) { - if (!mValues.empty() || annotationType != BOOL_TYPE) { - mValid = false; - return; - } - - mTruncateTimestamp = readNextValue(); -} - -void LogEvent::parsePrimaryFieldAnnotation(uint8_t annotationType) { - if (mValues.empty() || annotationType != BOOL_TYPE) { - mValid = false; - return; - } - - const bool primaryField = readNextValue(); - mValues[mValues.size() - 1].mAnnotations.setPrimaryField(primaryField); -} - -void LogEvent::parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType, - int firstUidInChainIndex) { - if (mValues.empty() || annotationType != BOOL_TYPE || -1 == firstUidInChainIndex) { - mValid = false; - return; - } - - const bool primaryField = readNextValue(); - mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(primaryField); -} - -void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) { - if (mValues.empty() || annotationType != BOOL_TYPE) { - mValid = false; - return; - } - - const bool exclusiveState = readNextValue(); - mExclusiveStateFieldIndex = static_cast(mValues.size() - 1); - mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState); -} - -void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) { - if (mValues.empty() || annotationType != INT32_TYPE) { - mValid = false; - return; - } - - mResetState = readNextValue(); -} - -void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) { - if (mValues.empty() || annotationType != BOOL_TYPE) { - mValid = false; - return; - } - - bool nested = readNextValue(); - mValues[mValues.size() - 1].mAnnotations.setNested(nested); -} - -// firstUidInChainIndex is a default parameter that is only needed when parsing -// annotations for attribution chains. -void LogEvent::parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex) { - for (uint8_t i = 0; i < numAnnotations; i++) { - uint8_t annotationId = readNextValue(); - uint8_t annotationType = readNextValue(); - - switch (annotationId) { - case ANNOTATION_ID_IS_UID: - parseIsUidAnnotation(annotationType); - break; - case ANNOTATION_ID_TRUNCATE_TIMESTAMP: - parseTruncateTimestampAnnotation(annotationType); - break; - case ANNOTATION_ID_PRIMARY_FIELD: - parsePrimaryFieldAnnotation(annotationType); - break; - case ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID: - parsePrimaryFieldFirstUidAnnotation(annotationType, firstUidInChainIndex); - break; - case ANNOTATION_ID_EXCLUSIVE_STATE: - parseExclusiveStateAnnotation(annotationType); - break; - case ANNOTATION_ID_TRIGGER_STATE_RESET: - parseTriggerStateResetAnnotation(annotationType); - break; - case ANNOTATION_ID_STATE_NESTED: - parseStateNestedAnnotation(annotationType); - break; - default: - mValid = false; - return; - } - } -} - -// This parsing logic is tied to the encoding scheme used in StatsEvent.java and -// stats_event.c -bool LogEvent::parseBuffer(uint8_t* buf, size_t len) { - mBuf = buf; - mRemainingLen = (uint32_t)len; - - int32_t pos[] = {1, 1, 1}; - bool last[] = {false, false, false}; - - // Beginning of buffer is OBJECT_TYPE | NUM_FIELDS | TIMESTAMP | ATOM_ID - uint8_t typeInfo = readNextValue(); - if (getTypeId(typeInfo) != OBJECT_TYPE) mValid = false; - - uint8_t numElements = readNextValue(); - if (numElements < 2 || numElements > 127) mValid = false; - - typeInfo = readNextValue(); - if (getTypeId(typeInfo) != INT64_TYPE) mValid = false; - mElapsedTimestampNs = readNextValue(); - numElements--; - - typeInfo = readNextValue(); - if (getTypeId(typeInfo) != INT32_TYPE) mValid = false; - mTagId = readNextValue(); - numElements--; - parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations - - for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) { - last[0] = (pos[0] == numElements); - - typeInfo = readNextValue(); - uint8_t typeId = getTypeId(typeInfo); - - switch (typeId) { - case BOOL_TYPE: - parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case INT32_TYPE: - parseInt32(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case INT64_TYPE: - parseInt64(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case FLOAT_TYPE: - parseFloat(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case BYTE_ARRAY_TYPE: - parseByteArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case STRING_TYPE: - parseString(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case KEY_VALUE_PAIRS_TYPE: - parseKeyValuePairs(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case ATTRIBUTION_CHAIN_TYPE: - parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); - break; - case ERROR_TYPE: - /* mErrorBitmask =*/ readNextValue(); - mValid = false; - break; - default: - mValid = false; - break; - } - } - - if (mRemainingLen != 0) mValid = false; - mBuf = nullptr; - return mValid; -} - -uint8_t LogEvent::getTypeId(uint8_t typeInfo) { - return typeInfo & 0x0F; // type id in lower 4 bytes -} - -uint8_t LogEvent::getNumAnnotations(uint8_t typeInfo) { - return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes -} - -int64_t LogEvent::GetLong(size_t key, status_t* err) const { - // TODO(b/110561208): encapsulate the magical operations in Field struct as static functions - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == LONG) { - return value.mValue.long_value; - } else if (value.mValue.getType() == INT) { - return value.mValue.int_value; - } else { - *err = BAD_TYPE; - return 0; - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return 0; -} - -int LogEvent::GetInt(size_t key, status_t* err) const { - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == INT) { - return value.mValue.int_value; - } else { - *err = BAD_TYPE; - return 0; - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return 0; -} - -const char* LogEvent::GetString(size_t key, status_t* err) const { - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == STRING) { - return value.mValue.str_value.c_str(); - } else { - *err = BAD_TYPE; - return 0; - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return NULL; -} - -bool LogEvent::GetBool(size_t key, status_t* err) const { - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == INT) { - return value.mValue.int_value != 0; - } else if (value.mValue.getType() == LONG) { - return value.mValue.long_value != 0; - } else { - *err = BAD_TYPE; - return false; - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return false; -} - -float LogEvent::GetFloat(size_t key, status_t* err) const { - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == FLOAT) { - return value.mValue.float_value; - } else { - *err = BAD_TYPE; - return 0.0; - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return 0.0; -} - -std::vector LogEvent::GetStorage(size_t key, status_t* err) const { - int field = getSimpleField(key); - for (const auto& value : mValues) { - if (value.mField.getField() == field) { - if (value.mValue.getType() == STORAGE) { - return value.mValue.storage_value; - } else { - *err = BAD_TYPE; - return vector(); - } - } - if ((size_t)value.mField.getPosAtDepth(0) > key) { - break; - } - } - - *err = BAD_INDEX; - return vector(); -} - -string LogEvent::ToString() const { - string result; - result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs, - (long long)mElapsedTimestampNs, mTagId); - for (const auto& value : mValues) { - result += - StringPrintf("%#x", value.mField.getField()) + "->" + value.mValue.toString() + " "; - } - result += " }"; - return result; -} - -void LogEvent::ToProto(ProtoOutputStream& protoOutput) const { - writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput); -} - -bool LogEvent::hasAttributionChain(std::pair* indexRange) const { - if (mAttributionChainStartIndex == -1 || mAttributionChainEndIndex == -1) { - return false; - } - - if (nullptr != indexRange) { - indexRange->first = static_cast(mAttributionChainStartIndex); - indexRange->second = static_cast(mAttributionChainEndIndex); - } - - return true; -} - -void writeExperimentIdsToProto(const std::vector& experimentIds, - std::vector* protoOut) { - ProtoOutputStream proto; - for (const auto& expId : experimentIds) { - proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID, - (long long)expId); - } - - protoOut->resize(proto.size()); - size_t pos = 0; - sp reader = proto.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(protoOut->data() + pos, reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h deleted file mode 100644 index a5f24603585a..000000000000 --- a/cmds/statsd/src/logd/LogEvent.h +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "FieldValue.h" - -#include -#include - -#include -#include - -namespace android { -namespace os { -namespace statsd { - -struct InstallTrainInfo { - int64_t trainVersionCode; - std::string trainName; - int32_t status; - std::vector experimentIds; - bool requiresStaging; - bool rollbackEnabled; - bool requiresLowLatencyMonitor; -}; - -/** - * This class decodes the structured, serialized encoding of an atom into a - * vector of FieldValues. - */ -class LogEvent { -public: - /** - * \param uid user id of the logging caller - * \param pid process id of the logging caller - */ - explicit LogEvent(int32_t uid, int32_t pid); - - /** - * Parses the atomId, timestamp, and vector of values from a buffer - * containing the StatsEvent/AStatsEvent encoding of an atom. - * - * \param buf a buffer that begins at the start of the serialized atom (it - * should not include the android_log_header_t or the StatsEventTag) - * \param len size of the buffer - * - * \return success of the initialization - */ - bool parseBuffer(uint8_t* buf, size_t len); - - // Constructs a BinaryPushStateChanged LogEvent from API call. - explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging, - bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, - const std::vector& experimentIds, int32_t userId); - - explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, - const InstallTrainInfo& installTrainInfo); - - ~LogEvent() {} - - /** - * Get the timestamp associated with this event. - */ - inline int64_t GetLogdTimestampNs() const { return mLogdTimestampNs; } - inline int64_t GetElapsedTimestampNs() const { return mElapsedTimestampNs; } - - /** - * Get the tag for this event. - */ - inline int GetTagId() const { return mTagId; } - - /** - * Get the uid of the logging client. - * Returns -1 if the uid is unknown/has not been set. - */ - inline int32_t GetUid() const { return mLogUid; } - - /** - * Get the pid of the logging client. - * Returns -1 if the pid is unknown/has not been set. - */ - inline int32_t GetPid() const { return mLogPid; } - - /** - * Get the nth value, starting at 1. - * - * Returns BAD_INDEX if the index is larger than the number of elements. - * Returns BAD_TYPE if the index is available but the data is the wrong type. - */ - int64_t GetLong(size_t key, status_t* err) const; - int GetInt(size_t key, status_t* err) const; - const char* GetString(size_t key, status_t* err) const; - bool GetBool(size_t key, status_t* err) const; - float GetFloat(size_t key, status_t* err) const; - std::vector GetStorage(size_t key, status_t* err) const; - - /** - * Return a string representation of this event. - */ - std::string ToString() const; - - /** - * Write this object to a ProtoOutputStream. - */ - void ToProto(android::util::ProtoOutputStream& out) const; - - /** - * Set elapsed timestamp if the original timestamp is missing. - */ - void setElapsedTimestampNs(int64_t timestampNs) { - mElapsedTimestampNs = timestampNs; - } - - /** - * Set the timestamp if the original logd timestamp is missing. - */ - void setLogdWallClockTimestampNs(int64_t timestampNs) { - mLogdTimestampNs = timestampNs; - } - - inline int size() const { - return mValues.size(); - } - - const std::vector& getValues() const { - return mValues; - } - - std::vector* getMutableValues() { - return &mValues; - } - - // Default value = false - inline bool shouldTruncateTimestamp() const { - return mTruncateTimestamp; - } - - // Returns the index of the uid field within the FieldValues vector if the - // uid exists. If there is no uid field, returns -1. - // - // If the index within the atom definition is desired, do the following: - // int vectorIndex = LogEvent.getUidFieldIndex(); - // if (vectorIndex != -1) { - // FieldValue& v = LogEvent.getValues()[vectorIndex]; - // int atomIndex = v.mField.getPosAtDepth(0); - // } - // Note that atomIndex is 1-indexed. - inline int getUidFieldIndex() { - return static_cast(mUidFieldIndex); - } - - // Returns whether this LogEvent has an AttributionChain. - // If it does and indexRange is not a nullptr, populate indexRange with the start and end index - // of the AttributionChain within mValues. - bool hasAttributionChain(std::pair* indexRange = nullptr) const; - - // Returns the index of the exclusive state field within the FieldValues vector if - // an exclusive state exists. If there is no exclusive state field, returns -1. - // - // If the index within the atom definition is desired, do the following: - // int vectorIndex = LogEvent.getExclusiveStateFieldIndex(); - // if (vectorIndex != -1) { - // FieldValue& v = LogEvent.getValues()[vectorIndex]; - // int atomIndex = v.mField.getPosAtDepth(0); - // } - // Note that atomIndex is 1-indexed. - inline int getExclusiveStateFieldIndex() const { - return static_cast(mExclusiveStateFieldIndex); - } - - // If a reset state is not sent in the StatsEvent, returns -1. Note that a - // reset state is sent if and only if a reset should be triggered. - inline int getResetState() const { - return mResetState; - } - - inline LogEvent makeCopy() { - return LogEvent(*this); - } - - template - status_t updateValue(size_t key, T& value, Type type) { - int field = getSimpleField(key); - for (auto& fieldValue : mValues) { - if (fieldValue.mField.getField() == field) { - if (fieldValue.mValue.getType() == type) { - fieldValue.mValue = Value(value); - return OK; - } else { - return BAD_TYPE; - } - } - } - return BAD_INDEX; - } - - bool isValid() const { - return mValid; - } - -private: - /** - * Only use this if copy is absolutely needed. - */ - LogEvent(const LogEvent&) = default; - - void parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - void parseAttributionChain(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations); - - void parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex = -1); - void parseIsUidAnnotation(uint8_t annotationType); - void parseTruncateTimestampAnnotation(uint8_t annotationType); - void parsePrimaryFieldAnnotation(uint8_t annotationType); - void parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType, int firstUidInChainIndex); - void parseExclusiveStateAnnotation(uint8_t annotationType); - void parseTriggerStateResetAnnotation(uint8_t annotationType); - void parseStateNestedAnnotation(uint8_t annotationType); - bool checkPreviousValueType(Type expected); - - /** - * The below two variables are only valid during the execution of - * parseBuffer. There are no guarantees about the state of these variables - * before/after. - */ - uint8_t* mBuf; - uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed - - bool mValid = true; // stores whether the event we received from the socket is valid - - /** - * Side-effects: - * If there is enough space in buffer to read value of type T - * - move mBuf past the value that was just read - * - decrement mRemainingLen by size of T - * Else - * - set mValid to false - */ - template - T readNextValue() { - T value; - if (mRemainingLen < sizeof(T)) { - mValid = false; - value = 0; // all primitive types can successfully cast 0 - } else { - // When alignof(T) == 1, hopefully the compiler can optimize away - // this conditional as always true. - if ((reinterpret_cast(mBuf) % alignof(T)) == 0) { - // We're properly aligned, and can safely make this assignment. - value = *((T*)mBuf); - } else { - // We need to use memcpy. It's slower, but safe. - memcpy(&value, mBuf, sizeof(T)); - } - mBuf += sizeof(T); - mRemainingLen -= sizeof(T); - } - return value; - } - - template - void addToValues(int32_t* pos, int32_t depth, T& value, bool* last) { - Field f = Field(mTagId, pos, depth); - // do not decorate last position at depth 0 - for (int i = 1; i < depth; i++) { - if (last[i]) f.decorateLastPos(i); - } - - Value v = Value(value); - mValues.push_back(FieldValue(f, v)); - } - - uint8_t getTypeId(uint8_t typeInfo); - uint8_t getNumAnnotations(uint8_t typeInfo); - - // The items are naturally sorted in DFS order as we read them. this allows us to do fast - // matching. - std::vector mValues; - - // The timestamp set by the logd. - int64_t mLogdTimestampNs; - - // The elapsed timestamp set by statsd log writer. - int64_t mElapsedTimestampNs; - - // The atom tag of the event (defaults to 0 if client does not - // appropriately set the atom id). - int mTagId = 0; - - // The uid of the logging client (defaults to -1). - int32_t mLogUid = -1; - - // The pid of the logging client (defaults to -1). - int32_t mLogPid = -1; - - // Annotations - bool mTruncateTimestamp = false; - int mResetState = -1; - - // Indexes within the FieldValue vector can be stored in 7 bits because - // that's the assumption enforced by the encoding used in FieldValue. - int8_t mUidFieldIndex = -1; - int8_t mAttributionChainStartIndex = -1; - int8_t mAttributionChainEndIndex = -1; - int8_t mExclusiveStateFieldIndex = -1; -}; - -void writeExperimentIdsToProto(const std::vector& experimentIds, std::vector* protoOut); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/logd/LogEventQueue.cpp b/cmds/statsd/src/logd/LogEventQueue.cpp deleted file mode 100644 index 146464bbe774..000000000000 --- a/cmds/statsd/src/logd/LogEventQueue.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "LogEventQueue.h" - -namespace android { -namespace os { -namespace statsd { - -using std::unique_lock; -using std::unique_ptr; - -unique_ptr LogEventQueue::waitPop() { - std::unique_lock lock(mMutex); - - if (mQueue.empty()) { - mCondition.wait(lock, [this] { return !this->mQueue.empty(); }); - } - - unique_ptr item = std::move(mQueue.front()); - mQueue.pop(); - - return item; -} - -bool LogEventQueue::push(unique_ptr item, int64_t* oldestTimestampNs) { - bool success; - { - std::unique_lock lock(mMutex); - if (mQueue.size() < mQueueLimit) { - mQueue.push(std::move(item)); - success = true; - } else { - // safe operation as queue must not be empty. - *oldestTimestampNs = mQueue.front()->GetElapsedTimestampNs(); - success = false; - } - } - - mCondition.notify_one(); - return success; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/logd/LogEventQueue.h b/cmds/statsd/src/logd/LogEventQueue.h deleted file mode 100644 index 9dda3d24c571..000000000000 --- a/cmds/statsd/src/logd/LogEventQueue.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "LogEvent.h" - -#include -#include -#include - -namespace android { -namespace os { -namespace statsd { - -/** - * A zero copy thread safe queue buffer for producing and consuming LogEvent. - */ -class LogEventQueue { -public: - explicit LogEventQueue(size_t maxSize) : mQueueLimit(maxSize){}; - - /** - * Blocking read one event from the queue. - */ - std::unique_ptr waitPop(); - - /** - * Puts a LogEvent ptr to the end of the queue. - * Returns false on failure when the queue is full, and output the oldest event timestamp - * in the queue. - */ - bool push(std::unique_ptr event, int64_t* oldestTimestampNs); - -private: - const size_t mQueueLimit; - std::condition_variable mCondition; - std::mutex mMutex; - std::queue> mQueue; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp deleted file mode 100644 index cd9c4e5b947b..000000000000 --- a/cmds/statsd/src/main.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsService.h" -#include "socket/StatsSocketListener.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -using namespace android; -using namespace android::os::statsd; -using ::ndk::SharedRefBase; -using std::shared_ptr; -using std::make_shared; - -shared_ptr gStatsService = nullptr; -sp gSocketListener = nullptr; - -void signalHandler(int sig) { - if (sig == SIGPIPE) { - // ShellSubscriber uses SIGPIPE as a signal to detect the end of the - // client process. Don't prematurely exit(1) here. Instead, ignore the - // signal and allow the write call to return EPIPE. - ALOGI("statsd received SIGPIPE. Ignoring signal."); - return; - } - - if (gSocketListener != nullptr) gSocketListener->stopListener(); - if (gStatsService != nullptr) gStatsService->Terminate(); - ALOGW("statsd terminated on receiving signal %d.", sig); - exit(1); -} - -void registerSignalHandlers() -{ - struct sigaction sa; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = signalHandler; - sigaction(SIGPIPE, &sa, nullptr); - sigaction(SIGHUP, &sa, nullptr); - sigaction(SIGINT, &sa, nullptr); - sigaction(SIGQUIT, &sa, nullptr); - sigaction(SIGTERM, &sa, nullptr); -} - -int main(int /*argc*/, char** /*argv*/) { - // Set up the looper - sp looper(Looper::prepare(0 /* opts */)); - - // Set up the binder - ABinderProcess_setThreadPoolMaxThreadCount(9); - ABinderProcess_startThreadPool(); - - std::shared_ptr eventQueue = - std::make_shared(2000 /*buffer limit. Buffer is NOT pre-allocated*/); - - // Create the service - gStatsService = SharedRefBase::make(looper, eventQueue); - // TODO(b/149582373): Set DUMP_FLAG_PROTO once libbinder_ndk supports - // setting dumpsys priorities. - binder_status_t status = AServiceManager_addService(gStatsService->asBinder().get(), "stats"); - if (status != STATUS_OK) { - ALOGE("Failed to add service as AIDL service"); - return -1; - } - - registerSignalHandlers(); - - gStatsService->sayHiToStatsCompanion(); - - gStatsService->Startup(); - - gSocketListener = new StatsSocketListener(eventQueue); - - ALOGI("Statsd starts to listen to socket."); - // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value - if (gSocketListener->startListener(600)) { - exit(1); - } - - // Loop forever -- the reports run on this thread in a handler, and the - // binder calls remain responsive in their pool of one thread. - while (true) { - looper->pollAll(-1 /* timeoutMillis */); - } - ALOGW("statsd escaped from its loop."); - - return 1; -} diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp deleted file mode 100644 index b94a9572113e..000000000000 --- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Log.h" - -#include "CombinationLogMatchingTracker.h" -#include "matchers/matcher_util.h" - -namespace android { -namespace os { -namespace statsd { - -using std::set; -using std::unordered_map; -using std::vector; - -CombinationLogMatchingTracker::CombinationLogMatchingTracker(const int64_t& id, const int index) - : LogMatchingTracker(id, index) { -} - -CombinationLogMatchingTracker::~CombinationLogMatchingTracker() { -} - -bool CombinationLogMatchingTracker::init(const vector& allLogMatchers, - const vector>& allTrackers, - const unordered_map& matcherMap, - vector& stack) { - if (mInitialized) { - return true; - } - - // mark this node as visited in the recursion stack. - stack[mIndex] = true; - - AtomMatcher_Combination matcher = allLogMatchers[mIndex].combination(); - - // LogicalOperation is missing in the config - if (!matcher.has_operation()) { - return false; - } - - mLogicalOperation = matcher.operation(); - - if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) { - return false; - } - - for (const auto& child : matcher.matcher()) { - auto pair = matcherMap.find(child); - if (pair == matcherMap.end()) { - ALOGW("Matcher %lld not found in the config", (long long)child); - return false; - } - - int childIndex = pair->second; - - // if the child is a visited node in the recursion -> circle detected. - if (stack[childIndex]) { - ALOGE("Circle detected in matcher config"); - return false; - } - - if (!allTrackers[childIndex]->init(allLogMatchers, allTrackers, matcherMap, stack)) { - ALOGW("child matcher init failed %lld", (long long)child); - return false; - } - - mChildren.push_back(childIndex); - - const set& childTagIds = allTrackers[childIndex]->getAtomIds(); - mAtomIds.insert(childTagIds.begin(), childTagIds.end()); - } - - mInitialized = true; - // unmark this node in the recursion stack. - stack[mIndex] = false; - return true; -} - -void CombinationLogMatchingTracker::onLogEvent(const LogEvent& event, - const vector>& allTrackers, - vector& matcherResults) { - // this event has been processed. - if (matcherResults[mIndex] != MatchingState::kNotComputed) { - return; - } - - if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) { - matcherResults[mIndex] = MatchingState::kNotMatched; - return; - } - - // evaluate children matchers if they haven't been evaluated. - for (const int childIndex : mChildren) { - if (matcherResults[childIndex] == MatchingState::kNotComputed) { - const sp& child = allTrackers[childIndex]; - child->onLogEvent(event, allTrackers, matcherResults); - } - } - - bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults); - matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h deleted file mode 100644 index 55bc46059fc1..000000000000 --- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef COMBINATION_LOG_MATCHING_TRACKER_H -#define COMBINATION_LOG_MATCHING_TRACKER_H - -#include -#include -#include "LogMatchingTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -namespace android { -namespace os { -namespace statsd { - -// Represents a AtomMatcher_Combination in the StatsdConfig. -class CombinationLogMatchingTracker : public virtual LogMatchingTracker { -public: - CombinationLogMatchingTracker(const int64_t& id, const int index); - - bool init(const std::vector& allLogMatchers, - const std::vector>& allTrackers, - const std::unordered_map& matcherMap, - std::vector& stack); - - ~CombinationLogMatchingTracker(); - - void onLogEvent(const LogEvent& event, - const std::vector>& allTrackers, - std::vector& matcherResults) override; - -private: - LogicalOperation mLogicalOperation; - - std::vector mChildren; -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // COMBINATION_LOG_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.cpp b/cmds/statsd/src/matchers/EventMatcherWizard.cpp deleted file mode 100644 index 025c9a87b16b..000000000000 --- a/cmds/statsd/src/matchers/EventMatcherWizard.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "EventMatcherWizard.h" - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -MatchingState EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcher_index) { - if (matcher_index < 0 || matcher_index >= (int)mAllEventMatchers.size()) { - return MatchingState::kNotComputed; - } - vector matcherCache(mAllEventMatchers.size(), MatchingState::kNotComputed); - mAllEventMatchers[matcher_index]->onLogEvent(event, mAllEventMatchers, matcherCache); - return matcherCache[matcher_index]; -} - -} // namespace statsd -} // namespace os -} // namespace android \ No newline at end of file diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.h b/cmds/statsd/src/matchers/EventMatcherWizard.h deleted file mode 100644 index 57ec2b35ba32..000000000000 --- a/cmds/statsd/src/matchers/EventMatcherWizard.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "LogMatchingTracker.h" - -namespace android { -namespace os { -namespace statsd { - -class EventMatcherWizard : public virtual android::RefBase { -public: - EventMatcherWizard(){}; // for testing - EventMatcherWizard(const std::vector>& eventTrackers) - : mAllEventMatchers(eventTrackers){}; - - virtual ~EventMatcherWizard(){}; - - MatchingState matchLogEvent(const LogEvent& event, int matcher_index); - -private: - std::vector> mAllEventMatchers; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h deleted file mode 100644 index 88ab4e6f683a..000000000000 --- a/cmds/statsd/src/matchers/LogMatchingTracker.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LOG_MATCHING_TRACKER_H -#define LOG_MATCHING_TRACKER_H - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "logd/LogEvent.h" -#include "matchers/matcher_util.h" - -#include - -#include -#include -#include - -namespace android { -namespace os { -namespace statsd { - -class LogMatchingTracker : public virtual RefBase { -public: - LogMatchingTracker(const int64_t& id, const int index) - : mId(id), mIndex(index), mInitialized(false){}; - - virtual ~LogMatchingTracker(){}; - - // Initialize this LogMatchingTracker. - // allLogMatchers: the list of the AtomMatcher proto config. This is needed because we don't - // store the proto object in memory. We only need it during initilization. - // allTrackers: the list of the LogMatchingTracker objects. It's a one-to-one mapping with - // allLogMatchers. This is needed because the initialization is done recursively - // for CombinationLogMatchingTrackers using DFS. - // stack: a bit map to record which matcher has been visited on the stack. This is for detecting - // circle dependency. - virtual bool init(const std::vector& allLogMatchers, - const std::vector>& allTrackers, - const std::unordered_map& matcherMap, - std::vector& stack) = 0; - - // Called when a log event comes. - // event: the log event. - // allTrackers: the list of all LogMatchingTrackers. This is needed because the log processing - // is done recursively. - // matcherResults: The cached results for all matchers for this event. Parent matchers can - // directly access the children's matching results if they have been evaluated. - // Otherwise, call children matchers' onLogEvent. - virtual void onLogEvent(const LogEvent& event, - const std::vector>& allTrackers, - std::vector& matcherResults) = 0; - - // Get the tagIds that this matcher cares about. The combined collection is stored - // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses - // some memory but hopefully it can save us much CPU time when there is flood of events. - virtual const std::set& getAtomIds() const { - return mAtomIds; - } - - const int64_t& getId() const { - return mId; - } - -protected: - // Name of this matching. We don't really need the name, but it makes log message easy to debug. - const int64_t mId; - - // Index of this LogMatchingTracker in MetricsManager's container. - const int mIndex; - - // Whether this LogMatchingTracker has been properly initialized. - bool mInitialized; - - // The collection of the event tag ids that this LogMatchingTracker cares. So we can quickly - // return kNotMatched when we receive an event with an id not in the list. This is especially - // useful when we have a complex CombinationLogMatcherTracker. - std::set mAtomIds; -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // LOG_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp deleted file mode 100644 index 082daf5a1916..000000000000 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "SimpleLogMatchingTracker.h" - -namespace android { -namespace os { -namespace statsd { - -using std::unordered_map; -using std::vector; - - -SimpleLogMatchingTracker::SimpleLogMatchingTracker(const int64_t& id, const int index, - const SimpleAtomMatcher& matcher, - const UidMap& uidMap) - : LogMatchingTracker(id, index), mMatcher(matcher), mUidMap(uidMap) { - if (!matcher.has_atom_id()) { - mInitialized = false; - } else { - mAtomIds.insert(matcher.atom_id()); - mInitialized = true; - } -} - -SimpleLogMatchingTracker::~SimpleLogMatchingTracker() { -} - -bool SimpleLogMatchingTracker::init(const vector& allLogMatchers, - const vector>& allTrackers, - const unordered_map& matcherMap, - vector& stack) { - // no need to do anything. - return mInitialized; -} - -void SimpleLogMatchingTracker::onLogEvent(const LogEvent& event, - const vector>& allTrackers, - vector& matcherResults) { - if (matcherResults[mIndex] != MatchingState::kNotComputed) { - VLOG("Matcher %lld already evaluated ", (long long)mId); - return; - } - - if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) { - matcherResults[mIndex] = MatchingState::kNotMatched; - return; - } - - bool matched = matchesSimple(mUidMap, mMatcher, event); - matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched; - VLOG("Stats SimpleLogMatcher %lld matched? %d", (long long)mId, matched); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h deleted file mode 100644 index a0f6a888bd44..000000000000 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SIMPLE_LOG_MATCHING_TRACKER_H -#define SIMPLE_LOG_MATCHING_TRACKER_H - -#include -#include -#include "LogMatchingTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "packages/UidMap.h" - -namespace android { -namespace os { -namespace statsd { - -class SimpleLogMatchingTracker : public virtual LogMatchingTracker { -public: - SimpleLogMatchingTracker(const int64_t& id, const int index, - const SimpleAtomMatcher& matcher, - const UidMap& uidMap); - - ~SimpleLogMatchingTracker(); - - bool init(const std::vector& allLogMatchers, - const std::vector>& allTrackers, - const std::unordered_map& matcherMap, - std::vector& stack) override; - - void onLogEvent(const LogEvent& event, - const std::vector>& allTrackers, - std::vector& matcherResults) override; - -private: - const SimpleAtomMatcher mMatcher; - const UidMap& mUidMap; -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // SIMPLE_LOG_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp deleted file mode 100644 index 2b4c6a3cbf1e..000000000000 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/LogMatchingTracker.h" -#include "matchers/matcher_util.h" -#include "stats_util.h" - -using std::set; -using std::string; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -bool combinationMatch(const vector& children, const LogicalOperation& operation, - const vector& matcherResults) { - bool matched; - switch (operation) { - case LogicalOperation::AND: { - matched = true; - for (const int childIndex : children) { - if (matcherResults[childIndex] != MatchingState::kMatched) { - matched = false; - break; - } - } - break; - } - case LogicalOperation::OR: { - matched = false; - for (const int childIndex : children) { - if (matcherResults[childIndex] == MatchingState::kMatched) { - matched = true; - break; - } - } - break; - } - case LogicalOperation::NOT: - matched = matcherResults[children[0]] == MatchingState::kNotMatched; - break; - case LogicalOperation::NAND: - matched = false; - for (const int childIndex : children) { - if (matcherResults[childIndex] != MatchingState::kMatched) { - matched = true; - break; - } - } - break; - case LogicalOperation::NOR: - matched = true; - for (const int childIndex : children) { - if (matcherResults[childIndex] == MatchingState::kMatched) { - matched = false; - break; - } - } - break; - case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED: - matched = false; - break; - } - return matched; -} - -bool tryMatchString(const UidMap& uidMap, const FieldValue& fieldValue, const string& str_match) { - if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) { - int uid = fieldValue.mValue.int_value; - auto aidIt = UidMap::sAidToUidMapping.find(str_match); - if (aidIt != UidMap::sAidToUidMapping.end()) { - return ((int)aidIt->second) == uid; - } - std::set packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/); - return packageNames.find(str_match) != packageNames.end(); - } else if (fieldValue.mValue.getType() == STRING) { - return fieldValue.mValue.str_value == str_match; - } - return false; -} - -bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, - const vector& values, int start, int end, int depth) { - if (depth > 2) { - ALOGE("Depth > 3 not supported"); - return false; - } - - if (start >= end) { - return false; - } - - // Filter by entry field first - int newStart = -1; - int newEnd = end; - // because the fields are naturally sorted in the DFS order. we can safely - // break when pos is larger than the one we are searching for. - for (int i = start; i < end; i++) { - int pos = values[i].mField.getPosAtDepth(depth); - if (pos == matcher.field()) { - if (newStart == -1) { - newStart = i; - } - newEnd = i + 1; - } else if (pos > matcher.field()) { - break; - } - } - - // Now we have zoomed in to a new range - start = newStart; - end = newEnd; - - if (start == -1) { - // No such field found. - return false; - } - - vector> ranges; // the ranges are for matching ANY position - if (matcher.has_position()) { - // Repeated fields position is stored as a node in the path. - depth++; - if (depth > 2) { - return false; - } - switch (matcher.position()) { - case Position::FIRST: { - for (int i = start; i < end; i++) { - int pos = values[i].mField.getPosAtDepth(depth); - if (pos != 1) { - // Again, the log elements are stored in sorted order. so - // once the position is > 1, we break; - end = i; - break; - } - } - ranges.push_back(std::make_pair(start, end)); - break; - } - case Position::LAST: { - // move the starting index to the first LAST field at the depth. - for (int i = start; i < end; i++) { - if (values[i].mField.isLastPos(depth)) { - start = i; - break; - } - } - ranges.push_back(std::make_pair(start, end)); - break; - } - case Position::ANY: { - // ANY means all the children matchers match in any of the sub trees, it's a match - newStart = start; - newEnd = end; - // Here start is guaranteed to be a valid index. - int currentPos = values[start].mField.getPosAtDepth(depth); - // Now find all sub trees ranges. - for (int i = start; i < end; i++) { - int newPos = values[i].mField.getPosAtDepth(depth); - if (newPos != currentPos) { - ranges.push_back(std::make_pair(newStart, i)); - newStart = i; - currentPos = newPos; - } - } - ranges.push_back(std::make_pair(newStart, end)); - break; - } - case Position::ALL: - ALOGE("Not supported: field matcher with ALL position."); - break; - case Position::POSITION_UNKNOWN: - break; - } - } else { - // No position - ranges.push_back(std::make_pair(start, end)); - } - // start and end are still pointing to the matched range. - switch (matcher.value_matcher_case()) { - case FieldValueMatcher::kMatchesTuple: { - ++depth; - // If any range matches all matchers, good. - for (const auto& range : ranges) { - bool matched = true; - for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) { - if (!matchesSimple(uidMap, subMatcher, values, range.first, range.second, - depth)) { - matched = false; - break; - } - } - if (matched) return true; - } - return false; - } - // Finally, we get to the point of real value matching. - // If the field matcher ends with ANY, then we have [start, end) range > 1. - // In the following, we should return true, when ANY of the values matches. - case FieldValueMatcher::ValueMatcherCase::kEqBool: { - for (int i = start; i < end; i++) { - if ((values[i].mValue.getType() == INT && - (values[i].mValue.int_value != 0) == matcher.eq_bool()) || - (values[i].mValue.getType() == LONG && - (values[i].mValue.long_value != 0) == matcher.eq_bool())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kEqString: { - for (int i = start; i < end; i++) { - if (tryMatchString(uidMap, values[i], matcher.eq_string())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kNeqAnyString: { - const auto& str_list = matcher.neq_any_string(); - for (int i = start; i < end; i++) { - bool notEqAll = true; - for (const auto& str : str_list.str_value()) { - if (tryMatchString(uidMap, values[i], str)) { - notEqAll = false; - break; - } - } - if (notEqAll) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kEqAnyString: { - const auto& str_list = matcher.eq_any_string(); - for (int i = start; i < end; i++) { - for (const auto& str : str_list.str_value()) { - if (tryMatchString(uidMap, values[i], str)) { - return true; - } - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kEqInt: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == INT && - (matcher.eq_int() == values[i].mValue.int_value)) { - return true; - } - // eq_int covers both int and long. - if (values[i].mValue.getType() == LONG && - (matcher.eq_int() == values[i].mValue.long_value)) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kLtInt: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == INT && - (values[i].mValue.int_value < matcher.lt_int())) { - return true; - } - // lt_int covers both int and long. - if (values[i].mValue.getType() == LONG && - (values[i].mValue.long_value < matcher.lt_int())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kGtInt: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == INT && - (values[i].mValue.int_value > matcher.gt_int())) { - return true; - } - // gt_int covers both int and long. - if (values[i].mValue.getType() == LONG && - (values[i].mValue.long_value > matcher.gt_int())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kLtFloat: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == FLOAT && - (values[i].mValue.float_value < matcher.lt_float())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kGtFloat: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == FLOAT && - (values[i].mValue.float_value > matcher.gt_float())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kLteInt: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == INT && - (values[i].mValue.int_value <= matcher.lte_int())) { - return true; - } - // lte_int covers both int and long. - if (values[i].mValue.getType() == LONG && - (values[i].mValue.long_value <= matcher.lte_int())) { - return true; - } - } - return false; - } - case FieldValueMatcher::ValueMatcherCase::kGteInt: { - for (int i = start; i < end; i++) { - if (values[i].mValue.getType() == INT && - (values[i].mValue.int_value >= matcher.gte_int())) { - return true; - } - // gte_int covers both int and long. - if (values[i].mValue.getType() == LONG && - (values[i].mValue.long_value >= matcher.gte_int())) { - return true; - } - } - return false; - } - default: - return false; - } -} - -bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher, - const LogEvent& event) { - if (event.GetTagId() != simpleMatcher.atom_id()) { - return false; - } - - for (const auto& matcher : simpleMatcher.field_value_matcher()) { - if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) { - return false; - } - } - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h deleted file mode 100644 index 1ab3e87b5fed..000000000000 --- a/cmds/statsd/src/matchers/matcher_util.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "logd/LogEvent.h" - -#include -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "packages/UidMap.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -enum MatchingState { - kNotComputed = -1, - kNotMatched = 0, - kMatched = 1, -}; - -bool combinationMatch(const std::vector& children, const LogicalOperation& operation, - const std::vector& matcherResults); - -bool matchesSimple(const UidMap& uidMap, - const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metadata_util.cpp b/cmds/statsd/src/metadata_util.cpp deleted file mode 100644 index 27ee59b36242..000000000000 --- a/cmds/statsd/src/metadata_util.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "FieldValue.h" -#include "metadata_util.h" - -namespace android { -namespace os { -namespace statsd { - -using google::protobuf::RepeatedPtrField; - -void writeValueToProto(metadata::FieldValue* metadataFieldValue, const Value& value) { - std::string storage_value; - switch (value.getType()) { - case INT: - metadataFieldValue->set_value_int(value.int_value); - break; - case LONG: - metadataFieldValue->set_value_long(value.long_value); - break; - case FLOAT: - metadataFieldValue->set_value_float(value.float_value); - break; - case DOUBLE: - metadataFieldValue->set_value_double(value.double_value); - break; - case STRING: - metadataFieldValue->set_value_str(value.str_value.c_str()); - break; - case STORAGE: // byte array - storage_value = ((char*) value.storage_value.data()); - metadataFieldValue->set_value_storage(storage_value); - break; - default: - break; - } -} - -void writeMetricDimensionKeyToMetadataDimensionKey( - const MetricDimensionKey& metricKey, - metadata::MetricDimensionKey* metadataMetricKey) { - for (const FieldValue& fieldValue : metricKey.getDimensionKeyInWhat().getValues()) { - metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_dimension_key_in_what(); - metadata::Field* metadataField = metadataFieldValue->mutable_field(); - metadataField->set_tag(fieldValue.mField.getTag()); - metadataField->set_field(fieldValue.mField.getField()); - writeValueToProto(metadataFieldValue, fieldValue.mValue); - } - - for (const FieldValue& fieldValue : metricKey.getStateValuesKey().getValues()) { - metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_state_values_key(); - metadata::Field* metadataField = metadataFieldValue->mutable_field(); - metadataField->set_tag(fieldValue.mField.getTag()); - metadataField->set_field(fieldValue.mField.getField()); - writeValueToProto(metadataFieldValue, fieldValue.mValue); - } -} - -void writeFieldValuesFromMetadata( - const RepeatedPtrField& repeatedFieldValueList, - std::vector* fieldValues) { - for (const metadata::FieldValue& metadataFieldValue : repeatedFieldValueList) { - Field field(metadataFieldValue.field().tag(), metadataFieldValue.field().field()); - Value value; - switch (metadataFieldValue.value_case()) { - case metadata::FieldValue::ValueCase::kValueInt: - value = Value(metadataFieldValue.value_int()); - break; - case metadata::FieldValue::ValueCase::kValueLong: - value = Value(metadataFieldValue.value_long()); - break; - case metadata::FieldValue::ValueCase::kValueFloat: - value = Value(metadataFieldValue.value_float()); - break; - case metadata::FieldValue::ValueCase::kValueDouble: - value = Value(metadataFieldValue.value_double()); - break; - case metadata::FieldValue::ValueCase::kValueStr: - value = Value(metadataFieldValue.value_str()); - break; - case metadata::FieldValue::ValueCase::kValueStorage: - value = Value(metadataFieldValue.value_storage()); - break; - default: - break; - } - FieldValue fieldValue(field, value); - fieldValues->emplace_back(field, value); - } -} - -MetricDimensionKey loadMetricDimensionKeyFromProto( - const metadata::MetricDimensionKey& metricDimensionKey) { - std::vector dimKeyInWhatFieldValues; - writeFieldValuesFromMetadata(metricDimensionKey.dimension_key_in_what(), - &dimKeyInWhatFieldValues); - std::vector stateValuesFieldValues; - writeFieldValuesFromMetadata(metricDimensionKey.state_values_key(), &stateValuesFieldValues); - - HashableDimensionKey dimKeyInWhat(dimKeyInWhatFieldValues); - HashableDimensionKey stateValues(stateValuesFieldValues); - MetricDimensionKey metricKey(dimKeyInWhat, stateValues); - return metricKey; -} - -} // namespace statsd -} // namespace os -} // namespace android \ No newline at end of file diff --git a/cmds/statsd/src/metadata_util.h b/cmds/statsd/src/metadata_util.h deleted file mode 100644 index 84a39ff872b5..000000000000 --- a/cmds/statsd/src/metadata_util.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "HashableDimensionKey.h" - -#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata - -namespace android { -namespace os { -namespace statsd { - -void writeMetricDimensionKeyToMetadataDimensionKey(const MetricDimensionKey& metricKey, - metadata::MetricDimensionKey* metadataMetricKey); - -MetricDimensionKey loadMetricDimensionKeyFromProto( - const metadata::MetricDimensionKey& metricDimensionKey); - -} // namespace statsd -} // namespace os -} // namespace android \ No newline at end of file diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp deleted file mode 100644 index 573961276e5b..000000000000 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "CountMetricProducer.h" - -#include -#include -#include - -#include "guardrail/StatsdStats.h" -#include "stats_log_util.h" -#include "stats_util.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::map; -using std::string; -using std::unordered_map; -using std::vector; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -// for StatsLogReport -const int FIELD_ID_ID = 1; -const int FIELD_ID_COUNT_METRICS = 5; -const int FIELD_ID_TIME_BASE = 9; -const int FIELD_ID_BUCKET_SIZE = 10; -const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; -const int FIELD_ID_IS_ACTIVE = 14; - -// for CountMetricDataWrapper -const int FIELD_ID_DATA = 1; -// for CountMetricData -const int FIELD_ID_DIMENSION_IN_WHAT = 1; -const int FIELD_ID_SLICE_BY_STATE = 6; -const int FIELD_ID_BUCKET_INFO = 3; -const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4; -// for CountBucketInfo -const int FIELD_ID_COUNT = 3; -const int FIELD_ID_BUCKET_NUM = 4; -const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5; -const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; - -CountMetricProducer::CountMetricProducer( - const ConfigKey& key, const CountMetric& metric, const int conditionIndex, - const vector& initialConditionCache, const sp& wizard, - const int64_t timeBaseNs, const int64_t startTimeNs, - const unordered_map>& eventActivationMap, - const unordered_map>>& eventDeactivationMap, - const vector& slicedStateAtoms, - const unordered_map>& stateGroupMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) { - if (metric.has_bucket()) { - mBucketSizeNs = - TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; - } else { - mBucketSizeNs = LLONG_MAX; - } - - if (metric.has_dimensions_in_what()) { - translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); - mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); - } - - mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()); - - if (metric.links().size() > 0) { - for (const auto& link : metric.links()) { - Metric2Condition mc; - mc.conditionId = link.condition(); - translateFieldMatcher(link.fields_in_what(), &mc.metricFields); - translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); - mMetric2ConditionLinks.push_back(mc); - } - mConditionSliced = true; - } - - for (const auto& stateLink : metric.state_link()) { - Metric2State ms; - ms.stateAtomId = stateLink.state_atom_id(); - translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields); - translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields); - mMetric2StateLinks.push_back(ms); - } - - flushIfNeededLocked(startTimeNs); - // Adjust start for partial bucket - mCurrentBucketStartTimeNs = startTimeNs; - - VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), - (long long)mBucketSizeNs, (long long)mTimeBaseNs); -} - -CountMetricProducer::~CountMetricProducer() { - VLOG("~CountMetricProducer() called"); -} - -void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, - const FieldValue& oldState, const FieldValue& newState) { - VLOG("CountMetric %lld onStateChanged time %lld, State%d, key %s, %d -> %d", - (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(), - oldState.mValue.int_value, newState.mValue.int_value); -} - -void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { - if (mCurrentSlicedCounter == nullptr || - mCurrentSlicedCounter->size() == 0) { - return; - } - - fprintf(out, "CountMetric %lld dimension size %lu\n", (long long)mMetricId, - (unsigned long)mCurrentSlicedCounter->size()); - if (verbose) { - for (const auto& it : *mCurrentSlicedCounter) { - fprintf(out, "\t(what)%s\t(state)%s %lld\n", - it.first.getDimensionKeyInWhat().toString().c_str(), - it.first.getStateValuesKey().toString().c_str(), (unsigned long long)it.second); - } - } -} - -void CountMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) { - VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); -} - - -void CountMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - mPastBuckets.clear(); -} - -void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - ProtoOutputStream* protoOutput) { - if (include_current_partial_bucket) { - flushLocked(dumpTimeNs); - } else { - flushIfNeededLocked(dumpTimeNs); - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); - - - if (mPastBuckets.empty()) { - return; - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs); - - // Fills the dimension path if not slicing by ALL. - if (!mSliceByPositionALL) { - if (!mDimensionsInWhat.empty()) { - uint64_t dimenPathToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT); - writeDimensionPathToProto(mDimensionsInWhat, protoOutput); - protoOutput->end(dimenPathToken); - } - } - - uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS); - - for (const auto& counter : mPastBuckets) { - const MetricDimensionKey& dimensionKey = counter.first; - VLOG(" dimension key %s", dimensionKey.toString().c_str()); - - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - - // First fill dimension. - if (mSliceByPositionALL) { - uint64_t dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput); - protoOutput->end(dimensionToken); - } else { - writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(), - FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput); - } - // Then fill slice_by_state. - for (auto state : dimensionKey.getStateValuesKey().getValues()) { - uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SLICE_BY_STATE); - writeStateToProto(state, protoOutput); - protoOutput->end(stateToken); - } - // Then fill bucket_info (CountBucketInfo). - for (const auto& bucket : counter.second) { - uint64_t bucketInfoToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - // Partial bucket. - if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketStartNs)); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketEndNs)); - } else { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM, - (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs))); - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount); - protoOutput->end(bucketInfoToken); - VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs, - (long long)bucket.mBucketEndNs, (long long)bucket.mCount); - } - protoOutput->end(wrapperToken); - } - - protoOutput->end(protoToken); - - if (erase_data) { - mPastBuckets.clear(); - } -} - -void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - flushIfNeededLocked(dropTimeNs); - StatsdStats::getInstance().noteBucketDropped(mMetricId); - mPastBuckets.clear(); -} - -void CountMetricProducer::onConditionChangedLocked(const bool conditionMet, - const int64_t eventTime) { - VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; -} - -bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { - if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) { - return false; - } - // ===========GuardRail============== - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mCurrentSlicedCounter->size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("CountMetric %lld dropping data for dimension key %s", - (long long)mMetricId, newKey.toString().c_str()); - StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); - return true; - } - } - - return false; -} - -void CountMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const map& statePrimaryKeys) { - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - flushIfNeededLocked(eventTimeNs); - - if (!condition) { - return; - } - - auto it = mCurrentSlicedCounter->find(eventKey); - if (it == mCurrentSlicedCounter->end()) { - // ===========GuardRail============== - if (hitGuardRailLocked(eventKey)) { - return; - } - // create a counter for the new key - (*mCurrentSlicedCounter)[eventKey] = 1; - } else { - // increment the existing value - auto& count = it->second; - count++; - } - for (auto& tracker : mAnomalyTrackers) { - int64_t countWholeBucket = mCurrentSlicedCounter->find(eventKey)->second; - auto prev = mCurrentFullCounters->find(eventKey); - if (prev != mCurrentFullCounters->end()) { - countWholeBucket += prev->second; - } - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey, - countWholeBucket); - } - - VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.toString().c_str(), - (long long)(*mCurrentSlicedCounter)[eventKey]); -} - -// When a new matched event comes in, we check if event falls into the current -// bucket. If not, flush the old counter to past buckets and initialize the new bucket. -void CountMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { - int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - if (eventTimeNs < currentBucketEndTimeNs) { - return; - } - - // Setup the bucket start time and number. - int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; - int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; - flushCurrentBucketLocked(eventTimeNs, nextBucketNs); - - mCurrentBucketNum += numBucketsForward; - VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, - (long long)mCurrentBucketStartTimeNs); -} - -void CountMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) { - int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); - CountBucket info; - info.mBucketStartNs = mCurrentBucketStartTimeNs; - if (eventTimeNs < fullBucketEndTimeNs) { - info.mBucketEndNs = eventTimeNs; - } else { - info.mBucketEndNs = fullBucketEndTimeNs; - } - for (const auto& counter : *mCurrentSlicedCounter) { - info.mCount = counter.second; - auto& bucketList = mPastBuckets[counter.first]; - bucketList.push_back(info); - VLOG("metric %lld, dump key value: %s -> %lld", (long long)mMetricId, - counter.first.toString().c_str(), - (long long)counter.second); - } - - // If we have finished a full bucket, then send this to anomaly tracker. - if (eventTimeNs > fullBucketEndTimeNs) { - // Accumulate partial buckets with current value and then send to anomaly tracker. - if (mCurrentFullCounters->size() > 0) { - for (const auto& keyValuePair : *mCurrentSlicedCounter) { - (*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second; - } - for (auto& tracker : mAnomalyTrackers) { - tracker->addPastBucket(mCurrentFullCounters, mCurrentBucketNum); - } - mCurrentFullCounters = std::make_shared(); - } else { - // Skip aggregating the partial buckets since there's no previous partial bucket. - for (auto& tracker : mAnomalyTrackers) { - tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum); - } - } - } else { - // Accumulate partial bucket. - for (const auto& keyValuePair : *mCurrentSlicedCounter) { - (*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second; - } - } - - StatsdStats::getInstance().noteBucketCount(mMetricId); - // Only resets the counters, but doesn't setup the times nor numbers. - // (Do not clear since the old one is still referenced in mAnomalyTrackers). - mCurrentSlicedCounter = std::make_shared(); - mCurrentBucketStartTimeNs = nextBucketStartTimeNs; -} - -// Rough estimate of CountMetricProducer buffer stored. This number will be -// greater than actual data size as it contains each dimension of -// CountMetricData is duplicated. -size_t CountMetricProducer::byteSizeLocked() const { - size_t totalSize = 0; - for (const auto& pair : mPastBuckets) { - totalSize += pair.second.size() * kBucketSize; - } - return totalSize; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h deleted file mode 100644 index f05fb061ccc1..000000000000 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef COUNT_METRIC_PRODUCER_H -#define COUNT_METRIC_PRODUCER_H - -#include -#include - -#include - -#include "MetricProducer.h" -#include "anomaly/AnomalyTracker.h" -#include "condition/ConditionTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/matcher_util.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -struct CountBucket { - int64_t mBucketStartNs; - int64_t mBucketEndNs; - int64_t mCount; -}; - -class CountMetricProducer : public MetricProducer { -public: - CountMetricProducer( - const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex, - const vector& initialConditionCache, const sp& wizard, - const int64_t timeBaseNs, const int64_t startTimeNs, - const std::unordered_map>& eventActivationMap = {}, - const std::unordered_map>>& - eventDeactivationMap = {}, - const vector& slicedStateAtoms = {}, - const unordered_map>& stateGroupMap = {}); - - virtual ~CountMetricProducer(); - - void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const FieldValue& oldState, - const FieldValue& newState) override; - -protected: - void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const std::map& statePrimaryKeys) override; - -private: - - void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - android::util::ProtoOutputStream* protoOutput) override; - - void clearPastBucketsLocked(const int64_t dumpTimeNs) override; - - // Internal interface to handle condition change. - void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override; - - // Internal interface to handle sliced condition change. - void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - - // Internal function to calculate the current used bytes. - size_t byteSizeLocked() const override; - - void dumpStatesLocked(FILE* out, bool verbose) const override; - - void dropDataLocked(const int64_t dropTimeNs) override; - - // Util function to flush the old packet. - void flushIfNeededLocked(const int64_t& newEventTime) override; - - void flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) override; - - std::unordered_map> mPastBuckets; - - // The current bucket (may be a partial bucket). - std::shared_ptr mCurrentSlicedCounter = std::make_shared(); - - // The sum of previous partial buckets in the current full bucket (excluding the current - // partial bucket). This is only updated while flushing the current bucket. - std::shared_ptr mCurrentFullCounters = std::make_shared(); - - static const size_t kBucketSize = sizeof(CountBucket{}); - - bool hitGuardRailLocked(const MetricDimensionKey& newKey); - - FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents); - FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition); - FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition); - FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); - FRIEND_TEST(CountMetricProducerTest, TestFirstBucket); - FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit); - - FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket); - FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket); -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // COUNT_METRIC_PRODUCER_H diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp deleted file mode 100644 index e9b043876d3d..000000000000 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false - -#include "Log.h" -#include "DurationMetricProducer.h" -#include "guardrail/StatsdStats.h" -#include "stats_util.h" -#include "stats_log_util.h" - -#include -#include - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::string; -using std::unordered_map; -using std::vector; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -// for StatsLogReport -const int FIELD_ID_ID = 1; -const int FIELD_ID_DURATION_METRICS = 6; -const int FIELD_ID_TIME_BASE = 9; -const int FIELD_ID_BUCKET_SIZE = 10; -const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; -const int FIELD_ID_IS_ACTIVE = 14; -// for DurationMetricDataWrapper -const int FIELD_ID_DATA = 1; -// for DurationMetricData -const int FIELD_ID_DIMENSION_IN_WHAT = 1; -const int FIELD_ID_BUCKET_INFO = 3; -const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4; -const int FIELD_ID_SLICE_BY_STATE = 6; -// for DurationBucketInfo -const int FIELD_ID_DURATION = 3; -const int FIELD_ID_BUCKET_NUM = 4; -const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5; -const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; - -DurationMetricProducer::DurationMetricProducer( - const ConfigKey& key, const DurationMetric& metric, const int conditionIndex, - const vector& initialConditionCache, const size_t startIndex, - const size_t stopIndex, const size_t stopAllIndex, const bool nesting, - const sp& wizard, const FieldMatcher& internalDimensions, - const int64_t timeBaseNs, const int64_t startTimeNs, - const unordered_map>& eventActivationMap, - const unordered_map>>& eventDeactivationMap, - const vector& slicedStateAtoms, - const unordered_map>& stateGroupMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap), - mAggregationType(metric.aggregation_type()), - mStartIndex(startIndex), - mStopIndex(stopIndex), - mStopAllIndex(stopAllIndex), - mNested(nesting), - mContainANYPositionInInternalDimensions(false) { - if (metric.has_bucket()) { - mBucketSizeNs = - TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; - } else { - mBucketSizeNs = LLONG_MAX; - } - - if (metric.has_dimensions_in_what()) { - translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); - mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); - } - - if (internalDimensions.has_field()) { - translateFieldMatcher(internalDimensions, &mInternalDimensions); - mContainANYPositionInInternalDimensions = HasPositionANY(internalDimensions); - } - if (mContainANYPositionInInternalDimensions) { - ALOGE("Position ANY in internal dimension not supported."); - } - if (mContainANYPositionInDimensionsInWhat) { - ALOGE("Position ANY in dimension_in_what not supported."); - } - - mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()); - - if (metric.links().size() > 0) { - for (const auto& link : metric.links()) { - Metric2Condition mc; - mc.conditionId = link.condition(); - translateFieldMatcher(link.fields_in_what(), &mc.metricFields); - translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); - mMetric2ConditionLinks.push_back(mc); - } - mConditionSliced = true; - } - mUnSlicedPartCondition = ConditionState::kUnknown; - - for (const auto& stateLink : metric.state_link()) { - Metric2State ms; - ms.stateAtomId = stateLink.state_atom_id(); - translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields); - translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields); - mMetric2StateLinks.push_back(ms); - } - - mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions); - if (mWizard != nullptr && mConditionTrackerIndex >= 0 && - mMetric2ConditionLinks.size() == 1) { - mHasLinksToAllConditionDimensionsInTracker = mWizard->equalOutputDimensions( - mConditionTrackerIndex, mMetric2ConditionLinks.begin()->conditionFields); - } - flushIfNeededLocked(startTimeNs); - // Adjust start for partial bucket - mCurrentBucketStartTimeNs = startTimeNs; - VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), - (long long)mBucketSizeNs, (long long)mTimeBaseNs); -} - -DurationMetricProducer::~DurationMetricProducer() { - VLOG("~DurationMetric() called"); -} - -sp DurationMetricProducer::addAnomalyTracker( - const Alert &alert, const sp& anomalyAlarmMonitor) { - std::lock_guard lock(mMutex); - if (mAggregationType == DurationMetric_AggregationType_SUM) { - if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) { - ALOGW("invalid alert for SUM: threshold (%f) > possible recordable value (%d x %lld)", - alert.trigger_if_sum_gt(), alert.num_buckets(), (long long)mBucketSizeNs); - return nullptr; - } - } - sp anomalyTracker = - new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor); - if (anomalyTracker != nullptr) { - mAnomalyTrackers.push_back(anomalyTracker); - } - return anomalyTracker; -} - -void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, - const FieldValue& oldState, - const FieldValue& newState) { - // Check if this metric has a StateMap. If so, map the new state value to - // the correct state group id. - FieldValue newStateCopy = newState; - mapStateValue(atomId, &newStateCopy); - - flushIfNeededLocked(eventTimeNs); - - // Each duration tracker is mapped to a different whatKey (a set of values from the - // dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the - // state change event are a subset of the tracker's whatKey field values. - // - // Ex. For a duration metric dimensioned on uid and tag: - // DurationTracker1 whatKey = uid: 1001, tag: 1 - // DurationTracker2 whatKey = uid: 1002, tag 1 - // - // If the state change primaryKey = uid: 1001, we only notify DurationTracker1 of a state - // change. - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) { - continue; - } - whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy); - } -} - -unique_ptr DurationMetricProducer::createDurationTracker( - const MetricDimensionKey& eventKey) const { - switch (mAggregationType) { - case DurationMetric_AggregationType_SUM: - return make_unique( - mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested, - mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs, - mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers); - case DurationMetric_AggregationType_MAX_SPARSE: - return make_unique( - mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested, - mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs, - mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers); - } -} - -// SlicedConditionChange optimization case 1: -// 1. If combination condition, logical operation is AND, only one sliced child predicate. -// 2. The links covers all dimension fields in the sliced child condition predicate. -void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool condition, - const int64_t eventTime) { - if (mMetric2ConditionLinks.size() != 1 || - !mHasLinksToAllConditionDimensionsInTracker) { - return; - } - - bool currentUnSlicedPartCondition = true; - if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) { - ConditionState unslicedPartState = - mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex); - // When the unsliced part is still false, return directly. - if (mUnSlicedPartCondition == ConditionState::kFalse && - unslicedPartState == ConditionState::kFalse) { - return; - } - mUnSlicedPartCondition = unslicedPartState; - currentUnSlicedPartCondition = mUnSlicedPartCondition > 0; - } - - auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex); - auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex); - - // The condition change is from the unsliced predicates. - // We need to find out the true dimensions from the sliced predicate and flip their condition - // state based on the new unsliced condition state. - if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr || - (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) { - std::set trueConditionDimensions; - mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions); - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - HashableDimensionKey linkedConditionDimensionKey; - getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0], - &linkedConditionDimensionKey); - if (trueConditionDimensions.find(linkedConditionDimensionKey) != - trueConditionDimensions.end()) { - whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime); - } - } - } else { - // Handle the condition change from the sliced predicate. - if (currentUnSlicedPartCondition) { - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - HashableDimensionKey linkedConditionDimensionKey; - getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0], - &linkedConditionDimensionKey); - if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) != - dimensionsChangedToTrue->end()) { - whatIt.second->onConditionChanged(true, eventTime); - } - if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) != - dimensionsChangedToFalse->end()) { - whatIt.second->onConditionChanged(false, eventTime); - } - } - } - } -} - -void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(bool overallCondition, - const int64_t eventTimeNs) { - bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex); - if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker) { - onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTimeNs); - return; - } - - // Now for each of the on-going event, check if the condition has changed for them. - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->onSlicedConditionMayChange(overallCondition, eventTimeNs); - } -} - -void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) { - VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); - - if (!mIsActive) { - return; - } - - flushIfNeededLocked(eventTime); - - if (!mConditionSliced) { - return; - } - - onSlicedConditionMayChangeInternalLocked(overallCondition, eventTime); -} - -void DurationMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) { - MetricProducer::onActiveStateChangedLocked(eventTimeNs); - - if (!mConditionSliced) { - if (ConditionState::kTrue != mCondition) { - return; - } - - if (mIsActive) { - flushIfNeededLocked(eventTimeNs); - } - - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->onConditionChanged(mIsActive, eventTimeNs); - } - } else if (mIsActive) { - flushIfNeededLocked(eventTimeNs); - onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs); - } else { // mConditionSliced == true && !mIsActive - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->onConditionChanged(mIsActive, eventTimeNs); - } - } -} - -void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, - const int64_t eventTime) { - VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; - - if (!mIsActive) { - return; - } - - flushIfNeededLocked(eventTime); - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->onConditionChanged(conditionMet, eventTime); - } -} - -void DurationMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - flushIfNeededLocked(dropTimeNs); - StatsdStats::getInstance().noteBucketDropped(mMetricId); - mPastBuckets.clear(); -} - -void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - flushIfNeededLocked(dumpTimeNs); - mPastBuckets.clear(); -} - -void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - ProtoOutputStream* protoOutput) { - if (include_current_partial_bucket) { - flushLocked(dumpTimeNs); - } else { - flushIfNeededLocked(dumpTimeNs); - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); - - if (mPastBuckets.empty()) { - VLOG(" Duration metric, empty return"); - return; - } - - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs); - - if (!mSliceByPositionALL) { - if (!mDimensionsInWhat.empty()) { - uint64_t dimenPathToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT); - writeDimensionPathToProto(mDimensionsInWhat, protoOutput); - protoOutput->end(dimenPathToken); - } - } - - uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS); - - VLOG("Duration metric %lld dump report now...", (long long)mMetricId); - - for (const auto& pair : mPastBuckets) { - const MetricDimensionKey& dimensionKey = pair.first; - VLOG(" dimension key %s", dimensionKey.toString().c_str()); - - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - - // First fill dimension. - if (mSliceByPositionALL) { - uint64_t dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput); - protoOutput->end(dimensionToken); - } else { - writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(), - FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput); - } - // Then fill slice_by_state. - for (auto state : dimensionKey.getStateValuesKey().getValues()) { - uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SLICE_BY_STATE); - writeStateToProto(state, protoOutput); - protoOutput->end(stateToken); - } - // Then fill bucket_info (DurationBucketInfo). - for (const auto& bucket : pair.second) { - uint64_t bucketInfoToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketStartNs)); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketEndNs)); - } else { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM, - (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs))); - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration); - protoOutput->end(bucketInfoToken); - VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs, - (long long)bucket.mBucketEndNs, (long long)bucket.mDuration); - } - - protoOutput->end(wrapperToken); - } - - protoOutput->end(protoToken); - if (erase_data) { - mPastBuckets.clear(); - } -} - -void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { - int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - - if (currentBucketEndTimeNs > eventTimeNs) { - return; - } - VLOG("flushing..........."); - int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; - int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; - flushCurrentBucketLocked(eventTimeNs, nextBucketNs); - - mCurrentBucketNum += numBucketsForward; -} - -void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) { - for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin(); - whatIt != mCurrentSlicedDurationTrackerMap.end();) { - if (whatIt->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) { - VLOG("erase bucket for key %s", whatIt->first.toString().c_str()); - whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt); - } else { - ++whatIt; - } - } - StatsdStats::getInstance().noteBucketCount(mMetricId); - mCurrentBucketStartTimeNs = nextBucketStartTimeNs; -} - -void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { - if (mCurrentSlicedDurationTrackerMap.size() == 0) { - return; - } - - fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId, - (unsigned long)mCurrentSlicedDurationTrackerMap.size()); - if (verbose) { - for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) { - fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str()); - whatIt.second->dumpStates(out, verbose); - } - } -} - -bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { - auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat()); - if (whatIt == mCurrentSlicedDurationTrackerMap.end()) { - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize( - mConfigKey, mMetricId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("DurationMetric %lld dropping data for what dimension key %s", - (long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str()); - StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); - return true; - } - } - } - return false; -} - -void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, - bool condition, const LogEvent& event) { - const auto& whatKey = eventKey.getDimensionKeyInWhat(); - auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey); - if (whatIt == mCurrentSlicedDurationTrackerMap.end()) { - if (hitGuardRailLocked(eventKey)) { - return; - } - mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey); - } - - auto it = mCurrentSlicedDurationTrackerMap.find(whatKey); - if (mUseWhatDimensionAsInternalDimension) { - it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys); - return; - } - - if (mInternalDimensions.empty()) { - it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(), - conditionKeys); - } else { - HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY; - filterValues(mInternalDimensions, event.getValues(), &dimensionKey); - it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(), - conditionKeys); - } - -} - -void DurationMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, bool condition, const LogEvent& event, - const map& statePrimaryKeys) { - ALOGW("Not used in duration tracker."); -} - -void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, - const LogEvent& event) { - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - if (eventTimeNs < mTimeBaseNs) { - return; - } - - if (mIsActive) { - flushIfNeededLocked(event.GetElapsedTimestampNs()); - } - - // Handles Stopall events. - if (matcherIndex == mStopAllIndex) { - for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->noteStopAll(event.GetElapsedTimestampNs()); - } - return; - } - - HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY; - if (!mDimensionsInWhat.empty()) { - filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); - } - - // Stores atom id to primary key pairs for each state atom that the metric is - // sliced by. - std::map statePrimaryKeys; - - // For states with primary fields, use MetricStateLinks to get the primary - // field values from the log event. These values will form a primary key - // that will be used to query StateTracker for the correct state value. - for (const auto& stateLink : mMetric2StateLinks) { - getDimensionForState(event.getValues(), stateLink, - &statePrimaryKeys[stateLink.stateAtomId]); - } - - // For each sliced state, query StateTracker for the state value using - // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY. - // - // Expected functionality: for any case where the MetricStateLinks are - // initialized incorrectly (ex. # of state links != # of primary fields, no - // links are provided for a state with primary fields, links are provided - // in the wrong order, etc.), StateTracker will simply return kStateUnknown - // when queried using an incorrect key. - HashableDimensionKey stateValuesKey = DEFAULT_DIMENSION_KEY; - for (auto atomId : mSlicedStateAtoms) { - FieldValue value; - if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) { - // found a primary key for this state, query using the key - queryStateValue(atomId, statePrimaryKeys[atomId], &value); - } else { - // if no MetricStateLinks exist for this state atom, - // query using the default dimension key (empty HashableDimensionKey) - queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value); - } - mapStateValue(atomId, &value); - stateValuesKey.addValue(value); - } - - // Handles Stop events. - if (matcherIndex == mStopIndex) { - if (mUseWhatDimensionAsInternalDimension) { - auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); - if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false); - } - return; - } - - HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY; - if (!mInternalDimensions.empty()) { - filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey); - } - - auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); - if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false); - } - return; - } - - bool condition; - ConditionKey conditionKey; - if (mConditionSliced) { - for (const auto& link : mMetric2ConditionLinks) { - getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]); - } - - auto conditionState = - mWizard->query(mConditionTrackerIndex, conditionKey, - !mHasLinksToAllConditionDimensionsInTracker); - condition = conditionState == ConditionState::kTrue; - } else { - // TODO: The unknown condition state is not handled here, we should fix it. - condition = mCondition == ConditionState::kTrue; - } - - condition = condition && mIsActive; - - handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition, - event); -} - -size_t DurationMetricProducer::byteSizeLocked() const { - size_t totalSize = 0; - for (const auto& pair : mPastBuckets) { - totalSize += pair.second.size() * kBucketSize; - } - return totalSize; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h deleted file mode 100644 index bfe1010c89de..000000000000 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - - -#include - -#include -#include "../anomaly/DurationAnomalyTracker.h" -#include "../condition/ConditionTracker.h" -#include "../matchers/matcher_util.h" -#include "MetricProducer.h" -#include "duration_helper/DurationTracker.h" -#include "duration_helper/MaxDurationTracker.h" -#include "duration_helper/OringDurationTracker.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "stats_util.h" - -using namespace std; - -namespace android { -namespace os { -namespace statsd { - -class DurationMetricProducer : public MetricProducer { -public: - DurationMetricProducer( - const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex, - const vector& initialConditionCache, const size_t startIndex, - const size_t stopIndex, const size_t stopAllIndex, const bool nesting, - const sp& wizard, const FieldMatcher& internalDimensions, - const int64_t timeBaseNs, const int64_t startTimeNs, - const unordered_map>& eventActivationMap = {}, - const unordered_map>>& eventDeactivationMap = {}, - const vector& slicedStateAtoms = {}, - const unordered_map>& stateGroupMap = {}); - - virtual ~DurationMetricProducer(); - - sp addAnomalyTracker(const Alert &alert, - const sp& anomalyAlarmMonitor) override; - - void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const FieldValue& oldState, - const FieldValue& newState) override; - -protected: - void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override; - - void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, bool condition, const LogEvent& event, - const std::map& statePrimaryKeys) override; - -private: - void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, - bool condition, const LogEvent& event); - - void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - android::util::ProtoOutputStream* protoOutput) override; - - void clearPastBucketsLocked(const int64_t dumpTimeNs) override; - - // Internal interface to handle condition change. - void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override; - - // Internal interface to handle active state change. - void onActiveStateChangedLocked(const int64_t& eventTimeNs) override; - - // Internal interface to handle sliced condition change. - void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - - void onSlicedConditionMayChangeInternalLocked(bool overallCondition, - const int64_t eventTimeNs); - - void onSlicedConditionMayChangeLocked_opt1(bool overallCondition, const int64_t eventTime); - void onSlicedConditionMayChangeLocked_opt2(bool overallCondition, const int64_t eventTime); - - // Internal function to calculate the current used bytes. - size_t byteSizeLocked() const override; - - void dumpStatesLocked(FILE* out, bool verbose) const override; - - void dropDataLocked(const int64_t dropTimeNs) override; - - // Util function to flush the old packet. - void flushIfNeededLocked(const int64_t& eventTime); - - void flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) override; - - const DurationMetric_AggregationType mAggregationType; - - // Index of the SimpleAtomMatcher which defines the start. - const size_t mStartIndex; - - // Index of the SimpleAtomMatcher which defines the stop. - const size_t mStopIndex; - - // Index of the SimpleAtomMatcher which defines the stop all for all dimensions. - const size_t mStopAllIndex; - - // nest counting -- for the same key, stops must match the number of starts to make real stop - const bool mNested; - - // The dimension from the atom predicate. e.g., uid, wakelock name. - vector mInternalDimensions; - - bool mContainANYPositionInInternalDimensions; - - // This boolean is true iff When mInternalDimensions == mDimensionsInWhat - bool mUseWhatDimensionAsInternalDimension; - - // Caches the current unsliced part condition. - ConditionState mUnSlicedPartCondition; - - // Save the past buckets and we can clear when the StatsLogReport is dumped. - std::unordered_map> mPastBuckets; - - // The duration trackers in the current bucket. - std::unordered_map> - mCurrentSlicedDurationTrackerMap; - - // Helper function to create a duration tracker given the metric aggregation type. - std::unique_ptr createDurationTracker( - const MetricDimensionKey& eventKey) const; - - // This hides the base class's std::vector> mAnomalyTrackers - std::vector> mAnomalyTrackers; - - // Util function to check whether the specified dimension hits the guardrail. - bool hitGuardRailLocked(const MetricDimensionKey& newKey); - - static const size_t kBucketSize = sizeof(DurationBucket{}); - - FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition); - FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition); - FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates); - FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket); - - FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestSumDuration); - FRIEND_TEST(DurationMetricProducerTest_PartialBucket, - TestSumDurationWithSplitInFollowingBucket); - FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration); - FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp deleted file mode 100644 index dc0036a687f3..000000000000 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "EventMetricProducer.h" -#include "stats_util.h" -#include "stats_log_util.h" - -#include -#include - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_STRING; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::ProtoOutputStream; -using std::map; -using std::string; -using std::unordered_map; -using std::vector; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -// for StatsLogReport -const int FIELD_ID_ID = 1; -const int FIELD_ID_EVENT_METRICS = 4; -const int FIELD_ID_IS_ACTIVE = 14; -// for EventMetricDataWrapper -const int FIELD_ID_DATA = 1; -// for EventMetricData -const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1; -const int FIELD_ID_ATOMS = 2; - -EventMetricProducer::EventMetricProducer( - const ConfigKey& key, const EventMetric& metric, const int conditionIndex, - const vector& initialConditionCache, const sp& wizard, - const int64_t startTimeNs, - const unordered_map>& eventActivationMap, - const unordered_map>>& eventDeactivationMap, - const vector& slicedStateAtoms, - const unordered_map>& stateGroupMap) - : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, initialConditionCache, wizard, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) { - if (metric.links().size() > 0) { - for (const auto& link : metric.links()) { - Metric2Condition mc; - mc.conditionId = link.condition(); - translateFieldMatcher(link.fields_in_what(), &mc.metricFields); - translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); - mMetric2ConditionLinks.push_back(mc); - } - mConditionSliced = true; - } - mProto = std::make_unique(); - VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), - (long long)mBucketSizeNs, (long long)mTimeBaseNs); -} - -EventMetricProducer::~EventMetricProducer() { - VLOG("~EventMetricProducer() called"); -} - -void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - mProto->clear(); - StatsdStats::getInstance().noteBucketDropped(mMetricId); -} - -void EventMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) { -} - -std::unique_ptr> serializeProtoLocked(ProtoOutputStream& protoOutput) { - size_t bufferSize = protoOutput.size(); - - std::unique_ptr> buffer(new std::vector(bufferSize)); - - size_t pos = 0; - sp reader = protoOutput.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((*buffer)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - return buffer; -} - -void EventMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - mProto->clear(); -} - -void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - ProtoOutputStream* protoOutput) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); - if (mProto->size() <= 0) { - return; - } - - size_t bufferSize = mProto->size(); - VLOG("metric %lld dump report now... proto size: %zu ", - (long long)mMetricId, bufferSize); - std::unique_ptr> buffer = serializeProtoLocked(*mProto); - - protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS, - reinterpret_cast(buffer.get()->data()), buffer.get()->size()); - - if (erase_data) { - mProto->clear(); - } -} - -void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, - const int64_t eventTime) { - VLOG("Metric %lld onConditionChanged", (long long)mMetricId); - mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; -} - -void EventMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const map& statePrimaryKeys) { - if (!condition) { - return; - } - - uint64_t wrapperToken = - mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - const int64_t elapsedTimeNs = truncateTimestampIfNecessary(event); - mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long) elapsedTimeNs); - - uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS); - event.ToProto(*mProto); - mProto->end(eventToken); - mProto->end(wrapperToken); -} - -size_t EventMetricProducer::byteSizeLocked() const { - return mProto->bytesWritten(); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h deleted file mode 100644 index bfb2de36fad4..000000000000 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef EVENT_METRIC_PRODUCER_H -#define EVENT_METRIC_PRODUCER_H - -#include - -#include - -#include "../condition/ConditionTracker.h" -#include "../matchers/matcher_util.h" -#include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -class EventMetricProducer : public MetricProducer { -public: - EventMetricProducer( - const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex, - const vector& initialConditionCache, const sp& wizard, - const int64_t startTimeNs, - const std::unordered_map>& eventActivationMap = {}, - const std::unordered_map>>& - eventDeactivationMap = {}, - const vector& slicedStateAtoms = {}, - const unordered_map>& stateGroupMap = {}); - - virtual ~EventMetricProducer(); - -private: - void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const std::map& statePrimaryKeys) override; - - void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - android::util::ProtoOutputStream* protoOutput) override; - void clearPastBucketsLocked(const int64_t dumpTimeNs) override; - - // Internal interface to handle condition change. - void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override; - - // Internal interface to handle sliced condition change. - void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - - void dropDataLocked(const int64_t dropTimeNs) override; - - // Internal function to calculate the current used bytes. - size_t byteSizeLocked() const override; - - void dumpStatesLocked(FILE* out, bool verbose) const override{}; - - // Maps to a EventMetricDataWrapper. Storing atom events in ProtoOutputStream - // is more space efficient than storing LogEvent. - std::unique_ptr mProto; -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // EVENT_METRIC_PRODUCER_H diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp deleted file mode 100644 index 020f4b638f4d..000000000000 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ /dev/null @@ -1,620 +0,0 @@ -/* -* Copyright (C) 2017 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "../guardrail/StatsdStats.h" -#include "GaugeMetricProducer.h" -#include "../stats_log_util.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::map; -using std::string; -using std::unordered_map; -using std::vector; -using std::make_shared; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -// for StatsLogReport -const int FIELD_ID_ID = 1; -const int FIELD_ID_GAUGE_METRICS = 8; -const int FIELD_ID_TIME_BASE = 9; -const int FIELD_ID_BUCKET_SIZE = 10; -const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; -const int FIELD_ID_IS_ACTIVE = 14; -// for GaugeMetricDataWrapper -const int FIELD_ID_DATA = 1; -const int FIELD_ID_SKIPPED = 2; -// for SkippedBuckets -const int FIELD_ID_SKIPPED_START_MILLIS = 3; -const int FIELD_ID_SKIPPED_END_MILLIS = 4; -const int FIELD_ID_SKIPPED_DROP_EVENT = 5; -// for DumpEvent Proto -const int FIELD_ID_BUCKET_DROP_REASON = 1; -const int FIELD_ID_DROP_TIME = 2; -// for GaugeMetricData -const int FIELD_ID_DIMENSION_IN_WHAT = 1; -const int FIELD_ID_BUCKET_INFO = 3; -const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4; -// for GaugeBucketInfo -const int FIELD_ID_ATOM = 3; -const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4; -const int FIELD_ID_BUCKET_NUM = 6; -const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 7; -const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8; - -GaugeMetricProducer::GaugeMetricProducer( - const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex, - const vector& initialConditionCache, const sp& wizard, - const int whatMatcherIndex, const sp& matcherWizard, - const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs, - const int64_t startTimeNs, const sp& pullerManager, - const unordered_map>& eventActivationMap, - const unordered_map>>& eventDeactivationMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard, - eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{}, - /*stateGroupMap=*/{}), - mWhatMatcherIndex(whatMatcherIndex), - mEventMatcherWizard(matcherWizard), - mPullerManager(pullerManager), - mPullTagId(pullTagId), - mTriggerAtomId(triggerAtomId), - mAtomId(atomId), - mIsPulled(pullTagId != -1), - mMinBucketSizeNs(metric.min_bucket_size_nanos()), - mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC - : StatsdStats::kPullMaxDelayNs), - mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) != - StatsdStats::kAtomDimensionKeySizeLimitMap.end() - ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first - : StatsdStats::kDimensionKeySizeSoftLimit), - mDimensionHardLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) != - StatsdStats::kAtomDimensionKeySizeLimitMap.end() - ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second - : StatsdStats::kDimensionKeySizeHardLimit), - mGaugeAtomsPerDimensionLimit(metric.max_num_gauge_atoms_per_bucket()), - mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) { - mCurrentSlicedBucket = std::make_shared(); - mCurrentSlicedBucketForAnomaly = std::make_shared(); - int64_t bucketSizeMills = 0; - if (metric.has_bucket()) { - bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()); - } else { - bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR); - } - mBucketSizeNs = bucketSizeMills * 1000000; - - mSamplingType = metric.sampling_type(); - if (!metric.gauge_fields_filter().include_all()) { - translateFieldMatcher(metric.gauge_fields_filter().fields(), &mFieldMatchers); - } - - if (metric.has_dimensions_in_what()) { - translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); - mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); - } - - if (metric.links().size() > 0) { - for (const auto& link : metric.links()) { - Metric2Condition mc; - mc.conditionId = link.condition(); - translateFieldMatcher(link.fields_in_what(), &mc.metricFields); - translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); - mMetric2ConditionLinks.push_back(mc); - } - mConditionSliced = true; - } - mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()); - - flushIfNeededLocked(startTimeNs); - // Kicks off the puller immediately. - if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(), - mBucketSizeNs); - } - - // Adjust start for partial first bucket and then pull if needed - mCurrentBucketStartTimeNs = startTimeNs; - - VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d", - (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs, - mConditionSliced); -} - -GaugeMetricProducer::~GaugeMetricProducer() { - VLOG("~GaugeMetricProducer() called"); - if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this); - } -} - -void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { - if (mCurrentSlicedBucket == nullptr || - mCurrentSlicedBucket->size() == 0) { - return; - } - - fprintf(out, "GaugeMetric %lld dimension size %lu\n", (long long)mMetricId, - (unsigned long)mCurrentSlicedBucket->size()); - if (verbose) { - for (const auto& it : *mCurrentSlicedBucket) { - fprintf(out, "\t(what)%s\t(states)%s %d atoms\n", - it.first.getDimensionKeyInWhat().toString().c_str(), - it.first.getStateValuesKey().toString().c_str(), (int)it.second.size()); - } - } -} - -void GaugeMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - flushIfNeededLocked(dumpTimeNs); - mPastBuckets.clear(); - mSkippedBuckets.clear(); -} - -void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - ProtoOutputStream* protoOutput) { - VLOG("Gauge metric %lld report now...", (long long)mMetricId); - if (include_current_partial_bucket) { - flushLocked(dumpTimeNs); - } else { - flushIfNeededLocked(dumpTimeNs); - } - - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); - - if (mPastBuckets.empty() && mSkippedBuckets.empty()) { - return; - } - - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs); - - // Fills the dimension path if not slicing by ALL. - if (!mSliceByPositionALL) { - if (!mDimensionsInWhat.empty()) { - uint64_t dimenPathToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT); - writeDimensionPathToProto(mDimensionsInWhat, protoOutput); - protoOutput->end(dimenPathToken); - } - } - - uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS); - - for (const auto& skippedBucket : mSkippedBuckets) { - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS, - (long long)(NanoToMillis(skippedBucket.bucketStartTimeNs))); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS, - (long long)(NanoToMillis(skippedBucket.bucketEndTimeNs))); - - for (const auto& dropEvent : skippedBucket.dropEvents) { - uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SKIPPED_DROP_EVENT); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME, (long long) (NanoToMillis(dropEvent.dropTimeNs))); - protoOutput->end(dropEventToken); - } - protoOutput->end(wrapperToken); - } - - for (const auto& pair : mPastBuckets) { - const MetricDimensionKey& dimensionKey = pair.first; - - VLOG("Gauge dimension key %s", dimensionKey.toString().c_str()); - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - - // First fill dimension. - if (mSliceByPositionALL) { - uint64_t dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput); - protoOutput->end(dimensionToken); - } else { - writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(), - FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput); - } - - // Then fill bucket_info (GaugeBucketInfo). - for (const auto& bucket : pair.second) { - uint64_t bucketInfoToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - - if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketStartNs)); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketEndNs)); - } else { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM, - (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs))); - } - - if (!bucket.mGaugeAtoms.empty()) { - for (const auto& atom : bucket.mGaugeAtoms) { - uint64_t atomsToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ATOM); - writeFieldValueTreeToStream(mAtomId, *(atom.mFields), protoOutput); - protoOutput->end(atomsToken); - } - for (const auto& atom : bucket.mGaugeAtoms) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | - FIELD_ID_ELAPSED_ATOM_TIMESTAMP, - (long long)atom.mElapsedTimestampNs); - } - } - protoOutput->end(bucketInfoToken); - VLOG("Gauge \t bucket [%lld - %lld] includes %d atoms.", - (long long)bucket.mBucketStartNs, (long long)bucket.mBucketEndNs, - (int)bucket.mGaugeAtoms.size()); - } - protoOutput->end(wrapperToken); - } - protoOutput->end(protoToken); - - - if (erase_data) { - mPastBuckets.clear(); - mSkippedBuckets.clear(); - } -} - -void GaugeMetricProducer::prepareFirstBucketLocked() { - if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - pullAndMatchEventsLocked(mCurrentBucketStartTimeNs); - } -} - -void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { - bool triggerPuller = false; - switch(mSamplingType) { - // When the metric wants to do random sampling and there is already one gauge atom for the - // current bucket, do not do it again. - case GaugeMetric::RANDOM_ONE_SAMPLE: { - triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty(); - break; - } - case GaugeMetric::CONDITION_CHANGE_TO_TRUE: { - triggerPuller = mCondition == ConditionState::kTrue; - break; - } - case GaugeMetric::FIRST_N_SAMPLES: { - triggerPuller = mCondition == ConditionState::kTrue; - break; - } - default: - break; - } - if (!triggerPuller) { - return; - } - vector> allData; - if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) { - ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs); - return; - } - const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs; - StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs); - if (pullDelayNs > mMaxPullDelayNs) { - ALOGE("Pull finish too late for atom %d", mPullTagId); - StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId); - return; - } - for (const auto& data : allData) { - LogEvent localCopy = data->makeCopy(); - localCopy.setElapsedTimestampNs(timestampNs); - if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) == - MatchingState::kMatched) { - onMatchedLogEventLocked(mWhatMatcherIndex, localCopy); - } - } -} - -void GaugeMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) { - MetricProducer::onActiveStateChangedLocked(eventTimeNs); - if (ConditionState::kTrue != mCondition || !mIsPulled) { - return; - } - if (mTriggerAtomId == -1 || (mIsActive && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE)) { - pullAndMatchEventsLocked(eventTimeNs); - } - -} - -void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, - const int64_t eventTimeNs) { - VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId); - - mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse; - if (!mIsActive) { - return; - } - - flushIfNeededLocked(eventTimeNs); - if (mIsPulled && mTriggerAtomId == -1) { - pullAndMatchEventsLocked(eventTimeNs); - } // else: Push mode. No need to proactively pull the gauge data. -} - -void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTimeNs) { - VLOG("GaugeMetric %lld onSlicedConditionMayChange overall condition %d", (long long)mMetricId, - overallCondition); - mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse; - if (!mIsActive) { - return; - } - - flushIfNeededLocked(eventTimeNs); - // If the condition is sliced, mCondition is true if any of the dimensions is true. And we will - // pull for every dimension. - if (mIsPulled && mTriggerAtomId == -1) { - pullAndMatchEventsLocked(eventTimeNs); - } // else: Push mode. No need to proactively pull the gauge data. -} - -std::shared_ptr> GaugeMetricProducer::getGaugeFields(const LogEvent& event) { - std::shared_ptr> gaugeFields; - if (mFieldMatchers.size() > 0) { - gaugeFields = std::make_shared>(); - filterGaugeValues(mFieldMatchers, event.getValues(), gaugeFields.get()); - } else { - gaugeFields = std::make_shared>(event.getValues()); - } - // Trim all dimension fields from output. Dimensions will appear in output report and will - // benefit from dictionary encoding. For large pulled atoms, this can give the benefit of - // optional repeated field. - for (const auto& field : mDimensionsInWhat) { - for (auto it = gaugeFields->begin(); it != gaugeFields->end();) { - if (it->mField.matches(field)) { - it = gaugeFields->erase(it); - } else { - it++; - } - } - } - return gaugeFields; -} - -void GaugeMetricProducer::onDataPulled(const std::vector>& allData, - bool pullSuccess, int64_t originalPullTimeNs) { - std::lock_guard lock(mMutex); - if (!pullSuccess || allData.size() == 0) { - return; - } - const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs; - StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs); - if (pullDelayNs > mMaxPullDelayNs) { - ALOGE("Pull finish too late for atom %d", mPullTagId); - StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId); - return; - } - for (const auto& data : allData) { - if (mEventMatcherWizard->matchLogEvent( - *data, mWhatMatcherIndex) == MatchingState::kMatched) { - onMatchedLogEventLocked(mWhatMatcherIndex, *data); - } - } -} - -bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { - if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) { - return false; - } - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentSlicedBucket->size() > mDimensionSoftLimit - 1) { - size_t newTupleCount = mCurrentSlicedBucket->size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > mDimensionHardLimit) { - ALOGE("GaugeMetric %lld dropping data for dimension key %s", - (long long)mMetricId, newKey.toString().c_str()); - StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); - return true; - } - } - - return false; -} - -void GaugeMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const map& statePrimaryKeys) { - if (condition == false) { - return; - } - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - if (eventTimeNs < mCurrentBucketStartTimeNs) { - VLOG("Gauge Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, - (long long)mCurrentBucketStartTimeNs); - return; - } - flushIfNeededLocked(eventTimeNs); - - if (mTriggerAtomId == event.GetTagId()) { - pullAndMatchEventsLocked(eventTimeNs); - return; - } - - // When gauge metric wants to randomly sample the output atom, we just simply use the first - // gauge in the given bucket. - if (mCurrentSlicedBucket->find(eventKey) != mCurrentSlicedBucket->end() && - mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - return; - } - if (hitGuardRailLocked(eventKey)) { - return; - } - if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) { - return; - } - - const int64_t truncatedElapsedTimestampNs = truncateTimestampIfNecessary(event); - GaugeAtom gaugeAtom(getGaugeFields(event), truncatedElapsedTimestampNs); - (*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom); - // Anomaly detection on gauge metric only works when there is one numeric - // field specified. - if (mAnomalyTrackers.size() > 0) { - if (gaugeAtom.mFields->size() == 1) { - const Value& value = gaugeAtom.mFields->begin()->mValue; - long gaugeVal = 0; - if (value.getType() == INT) { - gaugeVal = (long)value.int_value; - } else if (value.getType() == LONG) { - gaugeVal = value.long_value; - } - for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, - eventKey, gaugeVal); - } - } - } -} - -void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() { - for (const auto& slice : *mCurrentSlicedBucket) { - if (slice.second.empty()) { - continue; - } - const Value& value = slice.second.front().mFields->front().mValue; - long gaugeVal = 0; - if (value.getType() == INT) { - gaugeVal = (long)value.int_value; - } else if (value.getType() == LONG) { - gaugeVal = value.long_value; - } - (*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal; - } -} - -void GaugeMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - flushIfNeededLocked(dropTimeNs); - StatsdStats::getInstance().noteBucketDropped(mMetricId); - mPastBuckets.clear(); -} - -// When a new matched event comes in, we check if event falls into the current -// bucket. If not, flush the old counter to past buckets and initialize the new -// bucket. -// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside -// the GaugeMetricProducer while holding the lock. -void GaugeMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { - int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - - if (eventTimeNs < currentBucketEndTimeNs) { - VLOG("Gauge eventTime is %lld, less than next bucket start time %lld", - (long long)eventTimeNs, (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs)); - return; - } - - // Adjusts the bucket start and end times. - int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; - int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; - flushCurrentBucketLocked(eventTimeNs, nextBucketNs); - - mCurrentBucketNum += numBucketsForward; - VLOG("Gauge metric %lld: new bucket start time: %lld", (long long)mMetricId, - (long long)mCurrentBucketStartTimeNs); -} - -void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) { - int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); - int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs; - - GaugeBucket info; - info.mBucketStartNs = mCurrentBucketStartTimeNs; - info.mBucketEndNs = bucketEndTime; - - // Add bucket to mPastBuckets if bucket is large enough. - // Otherwise, drop the bucket data and add bucket metadata to mSkippedBuckets. - bool isBucketLargeEnough = info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs; - if (isBucketLargeEnough) { - for (const auto& slice : *mCurrentSlicedBucket) { - info.mGaugeAtoms = slice.second; - auto& bucketList = mPastBuckets[slice.first]; - bucketList.push_back(info); - VLOG("Gauge gauge metric %lld, dump key value: %s", (long long)mMetricId, - slice.first.toString().c_str()); - } - } else { - mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs; - mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime; - if (!maxDropEventsReached()) { - mCurrentSkippedBucket.dropEvents.emplace_back( - buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL)); - } - mSkippedBuckets.emplace_back(mCurrentSkippedBucket); - } - - // If we have anomaly trackers, we need to update the partial bucket values. - if (mAnomalyTrackers.size() > 0) { - updateCurrentSlicedBucketForAnomaly(); - - if (eventTimeNs > fullBucketEndTimeNs) { - // This is known to be a full bucket, so send this data to the anomaly tracker. - for (auto& tracker : mAnomalyTrackers) { - tracker->addPastBucket(mCurrentSlicedBucketForAnomaly, mCurrentBucketNum); - } - mCurrentSlicedBucketForAnomaly = std::make_shared(); - } - } - - StatsdStats::getInstance().noteBucketCount(mMetricId); - mCurrentSlicedBucket = std::make_shared(); - mCurrentBucketStartTimeNs = nextBucketStartTimeNs; - mCurrentSkippedBucket.reset(); -} - -size_t GaugeMetricProducer::byteSizeLocked() const { - size_t totalSize = 0; - for (const auto& pair : mPastBuckets) { - for (const auto& bucket : pair.second) { - totalSize += bucket.mGaugeAtoms.size() * sizeof(GaugeAtom); - for (const auto& atom : bucket.mGaugeAtoms) { - if (atom.mFields != nullptr) { - totalSize += atom.mFields->size() * sizeof(FieldValue); - } - } - } - } - return totalSize; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h deleted file mode 100644 index ef3a24a43dcc..000000000000 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include -#include -#include "../condition/ConditionTracker.h" -#include "../external/PullDataReceiver.h" -#include "../external/StatsPullerManager.h" -#include "../matchers/matcher_util.h" -#include "../matchers/EventMatcherWizard.h" -#include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "../stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -struct GaugeAtom { - GaugeAtom(std::shared_ptr> fields, int64_t elapsedTimeNs) - : mFields(fields), mElapsedTimestampNs(elapsedTimeNs) { - } - std::shared_ptr> mFields; - int64_t mElapsedTimestampNs; -}; - -struct GaugeBucket { - int64_t mBucketStartNs; - int64_t mBucketEndNs; - std::vector mGaugeAtoms; -}; - -typedef std::unordered_map> - DimToGaugeAtomsMap; - -// This gauge metric producer first register the puller to automatically pull the gauge at the -// beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise -// proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric -// producer always reports the guage at the earliest time of the bucket when the condition is met. -class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver { -public: - GaugeMetricProducer( - const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex, - const vector& initialConditionCache, - const sp& conditionWizard, const int whatMatcherIndex, - const sp& matcherWizard, const int pullTagId, - const int triggerAtomId, const int atomId, const int64_t timeBaseNs, - const int64_t startTimeNs, const sp& pullerManager, - const std::unordered_map>& eventActivationMap = {}, - const std::unordered_map>>& - eventDeactivationMap = {}); - - virtual ~GaugeMetricProducer(); - - // Handles when the pulled data arrives. - void onDataPulled(const std::vector>& data, - bool pullSuccess, int64_t originalPullTimeNs) override; - - // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. - void notifyAppUpgrade(const int64_t& eventTimeNs) override { - std::lock_guard lock(mMutex); - - if (!mSplitBucketForAppUpgrade) { - return; - } - flushLocked(eventTimeNs); - if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - pullAndMatchEventsLocked(eventTimeNs); - } - }; - - // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. - void onStatsdInitCompleted(const int64_t& eventTimeNs) override { - std::lock_guard lock(mMutex); - - flushLocked(eventTimeNs); - if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - pullAndMatchEventsLocked(eventTimeNs); - } - }; - -protected: - void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const std::map& statePrimaryKeys) override; - -private: - void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - android::util::ProtoOutputStream* protoOutput) override; - void clearPastBucketsLocked(const int64_t dumpTimeNs) override; - - // Internal interface to handle condition change. - void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override; - - // Internal interface to handle active state change. - void onActiveStateChangedLocked(const int64_t& eventTimeNs) override; - - // Internal interface to handle sliced condition change. - void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - - // Internal function to calculate the current used bytes. - size_t byteSizeLocked() const override; - - void dumpStatesLocked(FILE* out, bool verbose) const override; - - void dropDataLocked(const int64_t dropTimeNs) override; - - // Util function to flush the old packet. - void flushIfNeededLocked(const int64_t& eventTime) override; - - void flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) override; - - void prepareFirstBucketLocked() override; - - void pullAndMatchEventsLocked(const int64_t timestampNs); - - const int mWhatMatcherIndex; - - sp mEventMatcherWizard; - - sp mPullerManager; - // tagId for pulled data. -1 if this is not pulled - const int mPullTagId; - - // tagId for atoms that trigger the pulling, if any - const int mTriggerAtomId; - - // tagId for output atom - const int mAtomId; - - // if this is pulled metric - const bool mIsPulled; - - // Save the past buckets and we can clear when the StatsLogReport is dumped. - std::unordered_map> mPastBuckets; - - // The current partial bucket. - std::shared_ptr mCurrentSlicedBucket; - - // The current full bucket for anomaly detection. This is updated to the latest value seen for - // this slice (ie, for partial buckets, we use the last partial bucket in this full bucket). - std::shared_ptr mCurrentSlicedBucketForAnomaly; - - const int64_t mMinBucketSizeNs; - - // Translate Atom based bucket to single numeric value bucket for anomaly and updates the map - // for each slice with the latest value. - void updateCurrentSlicedBucketForAnomaly(); - - // Allowlist of fields to report. Empty means all are reported. - std::vector mFieldMatchers; - - GaugeMetric::SamplingType mSamplingType; - - const int64_t mMaxPullDelayNs; - - // apply an allowlist on the original input - std::shared_ptr> getGaugeFields(const LogEvent& event); - - // Util function to check whether the specified dimension hits the guardrail. - bool hitGuardRailLocked(const MetricDimensionKey& newKey); - - static const size_t kBucketSize = sizeof(GaugeBucket{}); - - const size_t mDimensionSoftLimit; - - const size_t mDimensionHardLimit; - - const size_t mGaugeAtomsPerDimensionLimit; - - const bool mSplitBucketForAppUpgrade; - - FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition); - FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition); - FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition); - FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled); - FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection); - FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket); - FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger); - FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput); - - FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents); - FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp deleted file mode 100644 index fe143e496373..000000000000 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "MetricProducer.h" - -#include "../guardrail/StatsdStats.h" -#include "state/StateTracker.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_ENUM; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::ProtoOutputStream; - -namespace android { -namespace os { -namespace statsd { - - -// for ActiveMetric -const int FIELD_ID_ACTIVE_METRIC_ID = 1; -const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2; - -// for ActiveEventActivation -const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1; -const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2; -const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3; - -MetricProducer::MetricProducer( - const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, - const int conditionIndex, const vector& initialConditionCache, - const sp& wizard, - const std::unordered_map>& eventActivationMap, - const std::unordered_map>>& - eventDeactivationMap, - const vector& slicedStateAtoms, - const unordered_map>& stateGroupMap) - : mMetricId(metricId), - mConfigKey(key), - mTimeBaseNs(timeBaseNs), - mCurrentBucketStartTimeNs(timeBaseNs), - mCurrentBucketNum(0), - mCondition(initialCondition(conditionIndex, initialConditionCache)), - mConditionTrackerIndex(conditionIndex), - mConditionSliced(false), - mWizard(wizard), - mContainANYPositionInDimensionsInWhat(false), - mSliceByPositionALL(false), - mHasLinksToAllConditionDimensionsInTracker(false), - mEventActivationMap(eventActivationMap), - mEventDeactivationMap(eventDeactivationMap), - mIsActive(mEventActivationMap.empty()), - mSlicedStateAtoms(slicedStateAtoms), - mStateGroupMap(stateGroupMap) { -} - -void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { - if (!mIsActive) { - return; - } - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - // this is old event, maybe statsd restarted? - if (eventTimeNs < mTimeBaseNs) { - return; - } - - bool condition; - ConditionKey conditionKey; - if (mConditionSliced) { - for (const auto& link : mMetric2ConditionLinks) { - getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]); - } - auto conditionState = - mWizard->query(mConditionTrackerIndex, conditionKey, - !mHasLinksToAllConditionDimensionsInTracker); - condition = (conditionState == ConditionState::kTrue); - } else { - // TODO: The unknown condition state is not handled here, we should fix it. - condition = mCondition == ConditionState::kTrue; - } - - // Stores atom id to primary key pairs for each state atom that the metric is - // sliced by. - std::map statePrimaryKeys; - - // For states with primary fields, use MetricStateLinks to get the primary - // field values from the log event. These values will form a primary key - // that will be used to query StateTracker for the correct state value. - for (const auto& stateLink : mMetric2StateLinks) { - getDimensionForState(event.getValues(), stateLink, - &statePrimaryKeys[stateLink.stateAtomId]); - } - - // For each sliced state, query StateTracker for the state value using - // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY. - // - // Expected functionality: for any case where the MetricStateLinks are - // initialized incorrectly (ex. # of state links != # of primary fields, no - // links are provided for a state with primary fields, links are provided - // in the wrong order, etc.), StateTracker will simply return kStateUnknown - // when queried using an incorrect key. - HashableDimensionKey stateValuesKey; - for (auto atomId : mSlicedStateAtoms) { - FieldValue value; - if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) { - // found a primary key for this state, query using the key - queryStateValue(atomId, statePrimaryKeys[atomId], &value); - } else { - // if no MetricStateLinks exist for this state atom, - // query using the default dimension key (empty HashableDimensionKey) - queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value); - } - mapStateValue(atomId, &value); - stateValuesKey.addValue(value); - } - - HashableDimensionKey dimensionInWhat; - filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); - MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey); - onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event, - statePrimaryKeys); -} - -bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) { - bool isActive = mEventActivationMap.empty(); - for (auto& it : mEventActivationMap) { - if (it.second->state == ActivationState::kActive && - elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) { - it.second->state = ActivationState::kNotActive; - } - if (it.second->state == ActivationState::kActive) { - isActive = true; - } - } - return isActive; -} - -void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { - std::lock_guard lock(mMutex); - if (!mIsActive) { - return; - } - mIsActive = evaluateActiveStateLocked(elapsedTimestampNs); - if (!mIsActive) { - onActiveStateChangedLocked(elapsedTimestampNs); - } -} - -void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) { - auto it = mEventActivationMap.find(activationTrackerIndex); - if (it == mEventActivationMap.end()) { - return; - } - auto& activation = it->second; - if (ACTIVATE_ON_BOOT == activation->activationType) { - if (ActivationState::kNotActive == activation->state) { - activation->state = ActivationState::kActiveOnBoot; - } - // If the Activation is already active or set to kActiveOnBoot, do nothing. - return; - } - activation->start_ns = elapsedTimestampNs; - activation->state = ActivationState::kActive; - bool oldActiveState = mIsActive; - mIsActive = true; - if (!oldActiveState) { // Metric went from not active to active. - onActiveStateChangedLocked(elapsedTimestampNs); - } -} - -void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) { - auto it = mEventDeactivationMap.find(deactivationTrackerIndex); - if (it == mEventDeactivationMap.end()) { - return; - } - for (auto activationToCancelIt : it->second) { - activationToCancelIt->state = ActivationState::kNotActive; - } -} - -void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric, - int64_t currentTimeNs) { - if (mEventActivationMap.size() == 0) { - return; - } - for (int i = 0; i < activeMetric.activation_size(); i++) { - const auto& activeEventActivation = activeMetric.activation(i); - auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index()); - if (it == mEventActivationMap.end()) { - ALOGE("Saved event activation not found"); - continue; - } - auto& activation = it->second; - // If the event activation does not have a state, assume it is active. - if (!activeEventActivation.has_state() || - activeEventActivation.state() == ActiveEventActivation::ACTIVE) { - // We don't want to change the ttl for future activations, so we set the start_ns - // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos - activation->start_ns = - currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns; - activation->state = ActivationState::kActive; - mIsActive = true; - } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) { - activation->state = ActivationState::kActiveOnBoot; - } - } -} - -void MetricProducer::writeActiveMetricToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId); - for (auto& it : mEventActivationMap) { - const int atom_matcher_index = it.first; - const std::shared_ptr& activation = it.second; - - if (ActivationState::kNotActive == activation->state || - (ActivationState::kActive == activation->state && - activation->start_ns + activation->ttl_ns < currentTimeNs)) { - continue; - } - - const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ACTIVE_METRIC_ACTIVATION); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX, - atom_matcher_index); - if (ActivationState::kActive == activation->state) { - const int64_t remainingTtlNs = - activation->start_ns + activation->ttl_ns - currentTimeNs; - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, - (long long)remainingTtlNs); - proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, - ActiveEventActivation::ACTIVE); - - } else if (ActivationState::kActiveOnBoot == activation->state) { - if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) { - proto->write( - FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, - (long long)activation->ttl_ns); - proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, - ActiveEventActivation::ACTIVE); - } else if (reason == STATSCOMPANION_DIED) { - // We are saving because of system server death, not due to a device shutdown. - // Next time we load, we do not want to activate metrics that activate on boot. - proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE, - ActiveEventActivation::ACTIVATE_ON_BOOT); - } - } - proto->end(activationToken); - } -} - -void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey, - FieldValue* value) { - if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) { - value->mValue = Value(StateTracker::kStateUnknown); - value->mField.setTag(atomId); - ALOGW("StateTracker not found for state atom %d", atomId); - return; - } -} - -void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) { - // check if there is a state map for this atom - auto atomIt = mStateGroupMap.find(atomId); - if (atomIt == mStateGroupMap.end()) { - return; - } - auto valueIt = atomIt->second.find(value->mValue.int_value); - if (valueIt == atomIt->second.end()) { - // state map exists, but value was not put in a state group - // so set mValue to kStateUnknown - // TODO(tsaichristine): handle incomplete state maps - value->mValue.setInt(StateTracker::kStateUnknown); - } else { - // set mValue to group_id - value->mValue.setLong(valueIt->second); - } -} - -HashableDimensionKey MetricProducer::getUnknownStateKey() { - HashableDimensionKey stateKey; - for (auto atom : mSlicedStateAtoms) { - FieldValue fieldValue; - fieldValue.mField.setTag(atom); - fieldValue.mValue.setInt(StateTracker::kStateUnknown); - stateKey.addValue(fieldValue); - } - return stateKey; -} - -DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason) { - DropEvent event; - event.reason = reason; - event.dropTimeNs = dropTimeNs; - return event; -} - -bool MetricProducer::maxDropEventsReached() { - return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h deleted file mode 100644 index be4cd6724bb1..000000000000 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef METRIC_PRODUCER_H -#define METRIC_PRODUCER_H - -#include -#include - -#include - -#include "HashableDimensionKey.h" -#include "anomaly/AnomalyTracker.h" -#include "condition/ConditionWizard.h" -#include "config/ConfigKey.h" -#include "matchers/matcher_util.h" -#include "packages/PackageInfoListener.h" -#include "state/StateListener.h" -#include "state/StateManager.h" - -namespace android { -namespace os { -namespace statsd { - -// Keep this in sync with DumpReportReason enum in stats_log.proto -enum DumpReportReason { - DEVICE_SHUTDOWN = 1, - CONFIG_UPDATED = 2, - CONFIG_REMOVED = 3, - GET_DATA_CALLED = 4, - ADB_DUMP = 5, - CONFIG_RESET = 6, - STATSCOMPANION_DIED = 7, - TERMINATION_SIGNAL_RECEIVED = 8 -}; - -// If the metric has no activation requirement, it will be active once the metric producer is -// created. -// If the metric needs to be activated by atoms, the metric producer will start -// with kNotActive state, turn to kActive or kActiveOnBoot when the activation event arrives, become -// kNotActive when it reaches the duration limit (timebomb). If the activation event arrives again -// before or after it expires, the event producer will be re-activated and ttl will be reset. -enum ActivationState { - kNotActive = 0, - kActive = 1, - kActiveOnBoot = 2, -}; - -enum DumpLatency { - // In some cases, we only have a short time range to do the dump, e.g. statsd is being killed. - // We might be able to return all the data in this mode. For instance, pull metrics might need - // to be pulled when the current bucket is requested. - FAST = 1, - // In other cases, it is fine for a dump to take more than a few milliseconds, e.g. config - // updates. - NO_TIME_CONSTRAINTS = 2 -}; - -// Keep this in sync with BucketDropReason enum in stats_log.proto -enum BucketDropReason { - // For ValueMetric, a bucket is dropped during a dump report request iff - // current bucket should be included, a pull is needed (pulled metric and - // condition is true), and we are under fast time constraints. - DUMP_REPORT_REQUESTED = 1, - EVENT_IN_WRONG_BUCKET = 2, - CONDITION_UNKNOWN = 3, - PULL_FAILED = 4, - PULL_DELAYED = 5, - DIMENSION_GUARDRAIL_REACHED = 6, - MULTIPLE_BUCKETS_SKIPPED = 7, - // Not an invalid bucket case, but the bucket is dropped. - BUCKET_TOO_SMALL = 8, - // Not an invalid bucket case, but the bucket is skipped. - NO_DATA = 9 -}; - -struct Activation { - Activation(const ActivationType& activationType, const int64_t ttlNs) - : ttl_ns(ttlNs), - start_ns(0), - state(ActivationState::kNotActive), - activationType(activationType) {} - - const int64_t ttl_ns; - int64_t start_ns; - ActivationState state; - const ActivationType activationType; -}; - -struct DropEvent { - // Reason for dropping the bucket and/or marking the bucket invalid. - BucketDropReason reason; - // The timestamp of the drop event. - int64_t dropTimeNs; -}; - -struct SkippedBucket { - // Start time of the dropped bucket. - int64_t bucketStartTimeNs; - // End time of the dropped bucket. - int64_t bucketEndTimeNs; - // List of events that invalidated this bucket. - std::vector dropEvents; - - void reset() { - bucketStartTimeNs = 0; - bucketEndTimeNs = 0; - dropEvents.clear(); - } -}; - -// A MetricProducer is responsible for compute one single metrics, creating stats log report, and -// writing the report to dropbox. MetricProducers should respond to package changes as required in -// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can -// be a no-op. -class MetricProducer : public virtual android::RefBase, public virtual StateListener { -public: - MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, - const int conditionIndex, const vector& initialConditionCache, - const sp& wizard, - const std::unordered_map>& eventActivationMap, - const std::unordered_map>>& - eventDeactivationMap, - const vector& slicedStateAtoms, - const unordered_map>& stateGroupMap); - - virtual ~MetricProducer(){}; - - ConditionState initialCondition(const int conditionIndex, - const vector& initialConditionCache) const { - return conditionIndex >= 0 ? initialConditionCache[conditionIndex] : ConditionState::kTrue; - } - - /** - * Force a partial bucket split on app upgrade - */ - virtual void notifyAppUpgrade(const int64_t& eventTimeNs) { - std::lock_guard lock(mMutex); - flushLocked(eventTimeNs); - }; - - void notifyAppRemoved(const int64_t& eventTimeNs) { - // Force buckets to split on removal also. - notifyAppUpgrade(eventTimeNs); - }; - - /** - * Force a partial bucket split on boot complete. - */ - virtual void onStatsdInitCompleted(const int64_t& eventTimeNs) { - std::lock_guard lock(mMutex); - flushLocked(eventTimeNs); - } - // Consume the parsed stats log entry that already matched the "what" of the metric. - void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) { - std::lock_guard lock(mMutex); - onMatchedLogEventLocked(matcherIndex, event); - } - - void onConditionChanged(const bool condition, const int64_t eventTime) { - std::lock_guard lock(mMutex); - onConditionChangedLocked(condition, eventTime); - } - - void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) { - std::lock_guard lock(mMutex); - onSlicedConditionMayChangeLocked(overallCondition, eventTime); - } - - bool isConditionSliced() const { - std::lock_guard lock(mMutex); - return mConditionSliced; - }; - - void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const FieldValue& oldState, - const FieldValue& newState){}; - - // Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp. - // This method clears all the past buckets. - void onDumpReport(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - android::util::ProtoOutputStream* protoOutput) { - std::lock_guard lock(mMutex); - return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, erase_data, - dumpLatency, str_set, protoOutput); - } - - void clearPastBuckets(const int64_t dumpTimeNs) { - std::lock_guard lock(mMutex); - return clearPastBucketsLocked(dumpTimeNs); - } - - void prepareFirstBucket() { - std::lock_guard lock(mMutex); - prepareFirstBucketLocked(); - } - - // Returns the memory in bytes currently used to store this metric's data. Does not change - // state. - size_t byteSize() const { - std::lock_guard lock(mMutex); - return byteSizeLocked(); - } - - void dumpStates(FILE* out, bool verbose) const { - std::lock_guard lock(mMutex); - dumpStatesLocked(out, verbose); - } - - // Let MetricProducer drop in-memory data to save memory. - // We still need to keep future data valid and anomaly tracking work, which means we will - // have to flush old data, informing anomaly trackers then safely drop old data. - // We still keep current bucket data for future metrics' validity. - void dropData(const int64_t dropTimeNs) { - std::lock_guard lock(mMutex); - dropDataLocked(dropTimeNs); - } - - void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) { - std::lock_guard lock(mMutex); - loadActiveMetricLocked(activeMetric, currentTimeNs); - } - - void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) { - std::lock_guard lock(mMutex); - activateLocked(activationTrackerIndex, elapsedTimestampNs); - } - - void cancelEventActivation(int deactivationTrackerIndex) { - std::lock_guard lock(mMutex); - cancelEventActivationLocked(deactivationTrackerIndex); - } - - bool isActive() const { - std::lock_guard lock(mMutex); - return isActiveLocked(); - } - - void flushIfExpire(int64_t elapsedTimestampNs); - - void writeActiveMetricToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); - - // Start: getters/setters - inline const int64_t& getMetricId() const { - return mMetricId; - } - - // For test only. - inline int64_t getCurrentBucketNum() const { - return mCurrentBucketNum; - } - - int64_t getBucketSizeInNs() const { - std::lock_guard lock(mMutex); - return mBucketSizeNs; - } - - inline const std::vector getSlicedStateAtoms() { - std::lock_guard lock(mMutex); - return mSlicedStateAtoms; - } - - /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */ - virtual sp addAnomalyTracker(const Alert &alert, - const sp& anomalyAlarmMonitor) { - std::lock_guard lock(mMutex); - sp anomalyTracker = new AnomalyTracker(alert, mConfigKey); - if (anomalyTracker != nullptr) { - mAnomalyTrackers.push_back(anomalyTracker); - } - return anomalyTracker; - } - // End: getters/setters -protected: - /** - * Flushes the current bucket if the eventTime is after the current bucket's end time. - */ - virtual void flushIfNeededLocked(const int64_t& eventTime){}; - - /** - * For metrics that aggregate (ie, every metric producer except for EventMetricProducer), - * we need to be able to flush the current buckets on demand (ie, end the current bucket and - * start new bucket). If this function is called when eventTimeNs is greater than the current - * bucket's end timestamp, than we flush up to the end of the latest full bucket; otherwise, - * we assume that we want to flush a partial bucket. The bucket start timestamp and bucket - * number are not changed by this function. This method should only be called by - * flushIfNeededLocked or flushLocked or the app upgrade handler; the caller MUST update the - * bucket timestamp and bucket number as needed. - */ - virtual void flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) {}; - - /** - * Flushes all the data including the current partial bucket. - */ - virtual void flushLocked(const int64_t& eventTimeNs) { - flushIfNeededLocked(eventTimeNs); - flushCurrentBucketLocked(eventTimeNs, eventTimeNs); - }; - - /* - * Individual metrics can implement their own business logic here. All pre-processing is done. - * - * [matcherIndex]: the index of the matcher which matched this event. This is interesting to - * DurationMetric, because it has start/stop/stop_all 3 matchers. - * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have - * dimensions, it will be DEFAULT_DIMENSION_KEY - * [conditionKey]: the keys of conditions which should be used to query the condition for this - * target event (from MetricConditionLink). This is passed to individual metrics - * because DurationMetric needs it to be cached. - * [condition]: whether condition is met. If condition is sliced, this is the result coming from - * query with ConditionWizard; If condition is not sliced, this is the - * nonSlicedCondition. - * [event]: the log event, just in case the metric needs its data, e.g., EventMetric. - */ - virtual void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const map& statePrimaryKeys) = 0; - - // Consume the parsed stats log entry that already matched the "what" of the metric. - virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event); - virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0; - virtual void onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) = 0; - virtual void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - android::util::ProtoOutputStream* protoOutput) = 0; - virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0; - virtual void prepareFirstBucketLocked(){}; - virtual size_t byteSizeLocked() const = 0; - virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0; - virtual void dropDataLocked(const int64_t dropTimeNs) = 0; - void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs); - void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs); - void cancelEventActivationLocked(int deactivationTrackerIndex); - - bool evaluateActiveStateLocked(int64_t elapsedTimestampNs); - - virtual void onActiveStateChangedLocked(const int64_t& eventTimeNs) { - if (!mIsActive) { - flushLocked(eventTimeNs); - } - } - - inline bool isActiveLocked() const { - return mIsActive; - } - - // Convenience to compute the current bucket's end time, which is always aligned with the - // start time of the metric. - int64_t getCurrentBucketEndTimeNs() const { - return mTimeBaseNs + (mCurrentBucketNum + 1) * mBucketSizeNs; - } - - int64_t getBucketNumFromEndTimeNs(const int64_t endNs) { - return (endNs - mTimeBaseNs) / mBucketSizeNs - 1; - } - - // Query StateManager for original state value using the queryKey. - // The field and value are output. - void queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey, - FieldValue* value); - - // If a state map exists for the given atom, replace the original state - // value with the group id mapped to the value. - // If no state map exists, keep the original state value. - void mapStateValue(const int32_t atomId, FieldValue* value); - - // Returns a HashableDimensionKey with unknown state value for each state - // atom. - HashableDimensionKey getUnknownStateKey(); - - DropEvent buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason); - - // Returns true if the number of drop events in the current bucket has - // exceeded the maximum number allowed, which is currently capped at 10. - bool maxDropEventsReached(); - - const int64_t mMetricId; - - const ConfigKey mConfigKey; - - // The time when this metric producer was first created. The end time for the current bucket - // can be computed from this based on mCurrentBucketNum. - int64_t mTimeBaseNs; - - // Start time may not be aligned with the start of statsd if there is an app upgrade in the - // middle of a bucket. - int64_t mCurrentBucketStartTimeNs; - - // Used by anomaly detector to track which bucket we are in. This is not sent with the produced - // report. - int64_t mCurrentBucketNum; - - int64_t mBucketSizeNs; - - ConditionState mCondition; - - int mConditionTrackerIndex; - - bool mConditionSliced; - - sp mWizard; - - bool mContainANYPositionInDimensionsInWhat; - - bool mSliceByPositionALL; - - vector mDimensionsInWhat; // The dimensions_in_what defined in statsd_config - - // True iff the metric to condition links cover all dimension fields in the condition tracker. - // This field is always false for combinational condition trackers. - bool mHasLinksToAllConditionDimensionsInTracker; - - std::vector mMetric2ConditionLinks; - - std::vector> mAnomalyTrackers; - - mutable std::mutex mMutex; - - // When the metric producer has multiple activations, these activations are ORed to determine - // whether the metric producer is ready to generate metrics. - std::unordered_map> mEventActivationMap; - - // Maps index of atom matcher for deactivation to a list of Activation structs. - std::unordered_map>> mEventDeactivationMap; - - bool mIsActive; - - // The slice_by_state atom ids defined in statsd_config. - const std::vector mSlicedStateAtoms; - - // Maps atom ids and state values to group_ids (>). - const std::unordered_map> mStateGroupMap; - - // MetricStateLinks defined in statsd_config that link fields in the state - // atom to fields in the "what" atom. - std::vector mMetric2StateLinks; - - SkippedBucket mCurrentSkippedBucket; - // Buckets that were invalidated and had their data dropped. - std::vector mSkippedBuckets; - - FRIEND_TEST(CountMetricE2eTest, TestSlicedState); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); - FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields); - FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges); - - FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); - FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); - FRIEND_TEST(DurationMetricE2eTest, TestWithCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped); - FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset); - - FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); - - FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); - FRIEND_TEST(StatsLogProcessorTest, - TestActivationOnBootMultipleActivationsDifferentActivationTypes); - FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); - - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); - FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); - - FRIEND_TEST(MetricsManagerTest, TestInitialConditions); -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // METRIC_PRODUCER_H diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp deleted file mode 100644 index 2d5aa56bf9ca..000000000000 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ /dev/null @@ -1,695 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "MetricsManager.h" - -#include - -#include "CountMetricProducer.h" -#include "condition/CombinationConditionTracker.h" -#include "condition/SimpleConditionTracker.h" -#include "guardrail/StatsdStats.h" -#include "matchers/CombinationLogMatchingTracker.h" -#include "matchers/SimpleLogMatchingTracker.h" -#include "metrics_manager_util.h" -#include "state/StateManager.h" -#include "stats_log_util.h" -#include "stats_util.h" -#include "statslog_statsd.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; - -using std::set; -using std::string; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -const int FIELD_ID_METRICS = 1; -const int FIELD_ID_ANNOTATIONS = 7; -const int FIELD_ID_ANNOTATIONS_INT64 = 1; -const int FIELD_ID_ANNOTATIONS_INT32 = 2; - -// for ActiveConfig -const int FIELD_ID_ACTIVE_CONFIG_ID = 1; -const int FIELD_ID_ACTIVE_CONFIG_UID = 2; -const int FIELD_ID_ACTIVE_CONFIG_METRIC = 3; - -MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, - const int64_t timeBaseNs, const int64_t currentTimeNs, - const sp& uidMap, - const sp& pullerManager, - const sp& anomalyAlarmMonitor, - const sp& periodicAlarmMonitor) - : mConfigKey(key), - mUidMap(uidMap), - mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1), - mTtlEndNs(-1), - mLastReportTimeNs(currentTimeNs), - mLastReportWallClockNs(getWallClockNs()), - mPullerManager(pullerManager), - mWhitelistedAtomIds(config.whitelisted_atom_ids().begin(), - config.whitelisted_atom_ids().end()), - mShouldPersistHistory(config.persist_locally()) { - // Init the ttl end timestamp. - refreshTtl(timeBaseNs); - - mConfigValid = initStatsdConfig( - key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers, - mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, - mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, - mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap, - mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds); - - mHashStringsInReport = config.hash_strings_in_metric_report(); - mVersionStringsInReport = config.version_strings_in_metric_report(); - mInstallerInReport = config.installer_in_metric_report(); - - // Init allowed pushed atom uids. - if (config.allowed_log_source_size() == 0) { - mConfigValid = false; - ALOGE("Log source allowlist is empty! This config won't get any data. Suggest adding at " - "least AID_SYSTEM and AID_STATSD to the allowed_log_source field."); - } else { - for (const auto& source : config.allowed_log_source()) { - auto it = UidMap::sAidToUidMapping.find(source); - if (it != UidMap::sAidToUidMapping.end()) { - mAllowedUid.push_back(it->second); - } else { - mAllowedPkg.push_back(source); - } - } - - if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) { - ALOGE("Too many log sources. This is likely to be an error in the config."); - mConfigValid = false; - } else { - initLogSourceWhiteList(); - } - } - - // Init default allowed pull atom uids. - int numPullPackages = 0; - for (const string& pullSource : config.default_pull_packages()) { - auto it = UidMap::sAidToUidMapping.find(pullSource); - if (it != UidMap::sAidToUidMapping.end()) { - numPullPackages++; - mDefaultPullUids.insert(it->second); - } else { - ALOGE("Default pull atom packages must be in sAidToUidMapping"); - mConfigValid = false; - } - } - // Init per-atom pull atom packages. - for (const PullAtomPackages& pullAtomPackages : config.pull_atom_packages()) { - int32_t atomId = pullAtomPackages.atom_id(); - for (const string& pullPackage : pullAtomPackages.packages()) { - numPullPackages++; - auto it = UidMap::sAidToUidMapping.find(pullPackage); - if (it != UidMap::sAidToUidMapping.end()) { - mPullAtomUids[atomId].insert(it->second); - } else { - mPullAtomPackages[atomId].insert(pullPackage); - } - } - } - if (numPullPackages > StatsdStats::kMaxPullAtomPackages) { - ALOGE("Too many sources in default_pull_packages and pull_atom_packages. This is likely to " - "be an error in the config"); - mConfigValid = false; - } else { - initPullAtomSources(); - } - mPullerManager->RegisterPullUidProvider(mConfigKey, this); - - // Store the sub-configs used. - for (const auto& annotation : config.annotation()) { - mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32()); - } - - // Guardrail. Reject the config if it's too big. - if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig || - mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig || - mAllAtomMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) { - ALOGE("This config is too big! Reject!"); - mConfigValid = false; - } - if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) { - ALOGE("This config has too many alerts! Reject!"); - mConfigValid = false; - } - - mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) || - (mAllMetricProducers.size() == 0); - bool isActive = mIsAlwaysActive; - for (int metric : mMetricIndexesWithActivation) { - isActive |= mAllMetricProducers[metric]->isActive(); - } - mIsActive = isActive; - VLOG("mIsActive is initialized to %d", mIsActive) - - // no matter whether this config is valid, log it in the stats. - StatsdStats::getInstance().noteConfigReceived( - key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(), - mAllAnomalyTrackers.size(), mAnnotations, mConfigValid); - // Check active - for (const auto& metric : mAllMetricProducers) { - if (metric->isActive()) { - mIsActive = true; - break; - } - } -} - -MetricsManager::~MetricsManager() { - for (auto it : mAllMetricProducers) { - for (int atomId : it->getSlicedStateAtoms()) { - StateManager::getInstance().unregisterListener(atomId, it); - } - } - mPullerManager->UnregisterPullUidProvider(mConfigKey, this); - - VLOG("~MetricsManager()"); -} - -void MetricsManager::initLogSourceWhiteList() { - std::lock_guard lock(mAllowedLogSourcesMutex); - mAllowedLogSources.clear(); - mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end()); - - for (const auto& pkg : mAllowedPkg) { - auto uids = mUidMap->getAppUid(pkg); - mAllowedLogSources.insert(uids.begin(), uids.end()); - } - if (DEBUG) { - for (const auto& uid : mAllowedLogSources) { - VLOG("Allowed uid %d", uid); - } - } -} - -void MetricsManager::initPullAtomSources() { - std::lock_guard lock(mAllowedLogSourcesMutex); - mCombinedPullAtomUids.clear(); - for (const auto& [atomId, uids] : mPullAtomUids) { - mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end()); - } - for (const auto& [atomId, packages] : mPullAtomPackages) { - for (const string& pkg : packages) { - set uids = mUidMap->getAppUid(pkg); - mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end()); - } - } -} - -bool MetricsManager::isConfigValid() const { - return mConfigValid; -} - -void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version) { - // Inform all metric producers. - for (const auto& it : mAllMetricProducers) { - it->notifyAppUpgrade(eventTimeNs); - } - // check if we care this package - if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) { - // We will re-initialize the whole list because we don't want to keep the multi mapping of - // UID<->pkg inside MetricsManager to reduce the memory usage. - initLogSourceWhiteList(); - } - - for (const auto& it : mPullAtomPackages) { - if (it.second.find(apk) != it.second.end()) { - initPullAtomSources(); - return; - } - } -} - -void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, - const int uid) { - // Inform all metric producers. - for (const auto& it : mAllMetricProducers) { - it->notifyAppRemoved(eventTimeNs); - } - // check if we care this package - if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) { - // We will re-initialize the whole list because we don't want to keep the multi mapping of - // UID<->pkg inside MetricsManager to reduce the memory usage. - initLogSourceWhiteList(); - } - - for (const auto& it : mPullAtomPackages) { - if (it.second.find(apk) != it.second.end()) { - initPullAtomSources(); - return; - } - } -} - -void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) { - // Purposefully don't inform metric producers on a new snapshot - // because we don't need to flush partial buckets. - // This occurs if a new user is added/removed or statsd crashes. - initPullAtomSources(); - - if (mAllowedPkg.size() == 0) { - return; - } - initLogSourceWhiteList(); -} - -void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) { - // Inform all metric producers. - for (const auto& it : mAllMetricProducers) { - it->onStatsdInitCompleted(eventTimeNs); - } -} - -void MetricsManager::init() { - for (const auto& producer : mAllMetricProducers) { - producer->prepareFirstBucket(); - } -} - -vector MetricsManager::getPullAtomUids(int32_t atomId) { - std::lock_guard lock(mAllowedLogSourcesMutex); - vector uids; - const auto& it = mCombinedPullAtomUids.find(atomId); - if (it != mCombinedPullAtomUids.end()) { - uids.insert(uids.end(), it->second.begin(), it->second.end()); - } - uids.insert(uids.end(), mDefaultPullUids.begin(), mDefaultPullUids.end()); - return uids; -} - -void MetricsManager::dumpStates(FILE* out, bool verbose) { - fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str()); - { - std::lock_guard lock(mAllowedLogSourcesMutex); - for (const auto& source : mAllowedLogSources) { - fprintf(out, "%d ", source); - } - } - fprintf(out, "\n"); - for (const auto& producer : mAllMetricProducers) { - producer->dumpStates(out, verbose); - } -} - -void MetricsManager::dropData(const int64_t dropTimeNs) { - for (const auto& producer : mAllMetricProducers) { - producer->dropData(dropTimeNs); - } -} - -void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - ProtoOutputStream* protoOutput) { - VLOG("=========================Metric Reports Start=========================="); - // one StatsLogReport per MetricProduer - for (const auto& producer : mAllMetricProducers) { - if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { - uint64_t token = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS); - if (mHashStringsInReport) { - producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data, - dumpLatency, str_set, protoOutput); - } else { - producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data, - dumpLatency, nullptr, protoOutput); - } - protoOutput->end(token); - } else { - producer->clearPastBuckets(dumpTimeStampNs); - } - } - for (const auto& annotation : mAnnotations) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ANNOTATIONS); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ANNOTATIONS_INT64, - (long long)annotation.first); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ANNOTATIONS_INT32, annotation.second); - protoOutput->end(token); - } - - // Do not update the timestamps when data is not cleared to avoid timestamps from being - // misaligned. - if (erase_data) { - mLastReportTimeNs = dumpTimeStampNs; - mLastReportWallClockNs = getWallClockNs(); - } - VLOG("=========================Metric Reports End=========================="); -} - - -bool MetricsManager::checkLogCredentials(const LogEvent& event) { - if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) { - return true; - } - std::lock_guard lock(mAllowedLogSourcesMutex); - if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) { - VLOG("log source %d not on the whitelist", event.GetUid()); - return false; - } - return true; -} - -bool MetricsManager::eventSanityCheck(const LogEvent& event) { - if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) { - // Check that app breadcrumb reported fields are valid. - status_t err = NO_ERROR; - - // Uid is 3rd from last field and must match the caller's uid, - // unless that caller is statsd itself (statsd is allowed to spoof uids). - long appHookUid = event.GetLong(event.size()-2, &err); - if (err != NO_ERROR) { - VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid"); - return false; - } - - // Because the uid within the LogEvent may have been mapped from - // isolated to host, map the loggerUid similarly before comparing. - int32_t loggerUid = mUidMap->getHostUidOrSelf(event.GetUid()); - if (loggerUid != appHookUid && loggerUid != AID_STATSD) { - VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d", - appHookUid, loggerUid); - return false; - } - - // The state must be from 0,3. This part of code must be manually updated. - long appHookState = event.GetLong(event.size(), &err); - if (err != NO_ERROR) { - VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field"); - return false; - } else if (appHookState < 0 || appHookState > 3) { - VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState); - return false; - } - } else if (event.GetTagId() == util::DAVEY_OCCURRED) { - // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp. - // Check that the davey duration is reasonable. Max length check is for privacy. - status_t err = NO_ERROR; - - // Uid is the first field provided. - long jankUid = event.GetLong(1, &err); - if (err != NO_ERROR) { - VLOG("Davey occurred had error when parsing the uid"); - return false; - } - int32_t loggerUid = event.GetUid(); - if (loggerUid != jankUid && loggerUid != AID_STATSD) { - VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid, - loggerUid); - return false; - } - - long duration = event.GetLong(event.size(), &err); - if (err != NO_ERROR) { - VLOG("Davey occurred had error when parsing the duration"); - return false; - } else if (duration > 100000) { - VLOG("Davey duration is unreasonably long: %ld", duration); - return false; - } - } - - return true; -} - -// Consume the stats log if it's interesting to this metric. -void MetricsManager::onLogEvent(const LogEvent& event) { - if (!mConfigValid) { - return; - } - - if (!checkLogCredentials(event)) { - return; - } - - if (!eventSanityCheck(event)) { - return; - } - - int tagId = event.GetTagId(); - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - - bool isActive = mIsAlwaysActive; - - // Set of metrics that are still active after flushing. - unordered_set activeMetricsIndices; - - // Update state of all metrics w/ activation conditions as of eventTimeNs. - for (int metricIndex : mMetricIndexesWithActivation) { - const sp& metric = mAllMetricProducers[metricIndex]; - metric->flushIfExpire(eventTimeNs); - if (metric->isActive()) { - // If this metric w/ activation condition is still active after - // flushing, remember it. - activeMetricsIndices.insert(metricIndex); - } - } - - mIsActive = isActive || !activeMetricsIndices.empty(); - - if (mTagIds.find(tagId) == mTagIds.end()) { - // Not interesting... - return; - } - - vector matcherCache(mAllAtomMatchers.size(), MatchingState::kNotComputed); - - // Evaluate all atom matchers. - for (auto& matcher : mAllAtomMatchers) { - matcher->onLogEvent(event, mAllAtomMatchers, matcherCache); - } - - // Set of metrics that received an activation cancellation. - unordered_set metricIndicesWithCanceledActivations; - - // Determine which metric activations received a cancellation and cancel them. - for (const auto& it : mDeactivationAtomTrackerToMetricMap) { - if (matcherCache[it.first] == MatchingState::kMatched) { - for (int metricIndex : it.second) { - mAllMetricProducers[metricIndex]->cancelEventActivation(it.first); - metricIndicesWithCanceledActivations.insert(metricIndex); - } - } - } - - // Determine whether any metrics are no longer active after cancelling metric activations. - for (const int metricIndex : metricIndicesWithCanceledActivations) { - const sp& metric = mAllMetricProducers[metricIndex]; - metric->flushIfExpire(eventTimeNs); - if (!metric->isActive()) { - activeMetricsIndices.erase(metricIndex); - } - } - - isActive |= !activeMetricsIndices.empty(); - - - // Determine which metric activations should be turned on and turn them on - for (const auto& it : mActivationAtomTrackerToMetricMap) { - if (matcherCache[it.first] == MatchingState::kMatched) { - for (int metricIndex : it.second) { - mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs); - isActive |= mAllMetricProducers[metricIndex]->isActive(); - } - } - } - - mIsActive = isActive; - - // A bitmap to see which ConditionTracker needs to be re-evaluated. - vector conditionToBeEvaluated(mAllConditionTrackers.size(), false); - - for (const auto& pair : mTrackerToConditionMap) { - if (matcherCache[pair.first] == MatchingState::kMatched) { - const auto& conditionList = pair.second; - for (const int conditionIndex : conditionList) { - conditionToBeEvaluated[conditionIndex] = true; - } - } - } - - vector conditionCache(mAllConditionTrackers.size(), - ConditionState::kNotEvaluated); - // A bitmap to track if a condition has changed value. - vector changedCache(mAllConditionTrackers.size(), false); - for (size_t i = 0; i < mAllConditionTrackers.size(); i++) { - if (conditionToBeEvaluated[i] == false) { - continue; - } - sp& condition = mAllConditionTrackers[i]; - condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache, - changedCache); - } - - for (size_t i = 0; i < mAllConditionTrackers.size(); i++) { - if (changedCache[i] == false) { - continue; - } - auto pair = mConditionToMetricMap.find(i); - if (pair != mConditionToMetricMap.end()) { - auto& metricList = pair->second; - for (auto metricIndex : metricList) { - // Metric cares about non sliced condition, and it's changed. - // Push the new condition to it directly. - if (!mAllMetricProducers[metricIndex]->isConditionSliced()) { - mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i], - eventTimeNs); - // Metric cares about sliced conditions, and it may have changed. Send - // notification, and the metric can query the sliced conditions that are - // interesting to it. - } else { - mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i], - eventTimeNs); - } - } - } - } - - // For matched AtomMatchers, tell relevant metrics that a matched event has come. - for (size_t i = 0; i < mAllAtomMatchers.size(); i++) { - if (matcherCache[i] == MatchingState::kMatched) { - StatsdStats::getInstance().noteMatcherMatched(mConfigKey, - mAllAtomMatchers[i]->getId()); - auto pair = mTrackerToMetricMap.find(i); - if (pair != mTrackerToMetricMap.end()) { - auto& metricList = pair->second; - for (const int metricIndex : metricList) { - // pushed metrics are never scheduled pulls - mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event); - } - } - } - } -} - -void MetricsManager::onAnomalyAlarmFired( - const int64_t& timestampNs, - unordered_set, SpHash>& alarmSet) { - for (const auto& itr : mAllAnomalyTrackers) { - itr->informAlarmsFired(timestampNs, alarmSet); - } -} - -void MetricsManager::onPeriodicAlarmFired( - const int64_t& timestampNs, - unordered_set, SpHash>& alarmSet) { - for (const auto& itr : mAllPeriodicAlarmTrackers) { - itr->informAlarmsFired(timestampNs, alarmSet); - } -} - -// Returns the total byte size of all metrics managed by a single config source. -size_t MetricsManager::byteSize() { - size_t totalSize = 0; - for (const auto& metricProducer : mAllMetricProducers) { - totalSize += metricProducer->byteSize(); - } - return totalSize; -} - -void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs) { - if (config.metric_size() == 0) { - ALOGW("No active metric for config %s", mConfigKey.ToString().c_str()); - return; - } - - for (int i = 0; i < config.metric_size(); i++) { - const auto& activeMetric = config.metric(i); - for (int metricIndex : mMetricIndexesWithActivation) { - const auto& metric = mAllMetricProducers[metricIndex]; - if (metric->getMetricId() == activeMetric.id()) { - VLOG("Setting active metric: %lld", (long long)metric->getMetricId()); - metric->loadActiveMetric(activeMetric, currentTimeNs); - if (!mIsActive && metric->isActive()) { - StatsdStats::getInstance().noteActiveStatusChanged(mConfigKey, - /*activate=*/ true); - } - mIsActive |= metric->isActive(); - } - } - } -} - -void MetricsManager::writeActiveConfigToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { - proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId()); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid()); - for (int metricIndex : mMetricIndexesWithActivation) { - const auto& metric = mAllMetricProducers[metricIndex]; - const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ACTIVE_CONFIG_METRIC); - metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto); - proto->end(metricToken); - } -} - -bool MetricsManager::writeMetadataToProto(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadata* statsMetadata) { - bool metadataWritten = false; - metadata::ConfigKey* configKey = statsMetadata->mutable_config_key(); - configKey->set_config_id(mConfigKey.GetId()); - configKey->set_uid(mConfigKey.GetUid()); - for (const auto& anomalyTracker : mAllAnomalyTrackers) { - metadata::AlertMetadata* alertMetadata = statsMetadata->add_alert_metadata(); - bool alertWritten = anomalyTracker->writeAlertMetadataToProto(currentWallClockTimeNs, - systemElapsedTimeNs, alertMetadata); - if (!alertWritten) { - statsMetadata->mutable_alert_metadata()->RemoveLast(); - } - metadataWritten |= alertWritten; - } - return metadataWritten; -} - -void MetricsManager::loadMetadata(const metadata::StatsMetadata& metadata, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs) { - for (const metadata::AlertMetadata& alertMetadata : metadata.alert_metadata()) { - int64_t alertId = alertMetadata.alert_id(); - auto it = mAlertTrackerMap.find(alertId); - if (it == mAlertTrackerMap.end()) { - ALOGE("No anomalyTracker found for alertId %lld", (long long) alertId); - continue; - } - mAllAnomalyTrackers[it->second]->loadAlertMetadata(alertMetadata, - currentWallClockTimeNs, - systemElapsedTimeNs); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h deleted file mode 100644 index ad30a88c5d19..000000000000 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "anomaly/AlarmMonitor.h" -#include "anomaly/AlarmTracker.h" -#include "anomaly/AnomalyTracker.h" -#include "condition/ConditionTracker.h" -#include "config/ConfigKey.h" -#include "external/StatsPullerManager.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" -#include "logd/LogEvent.h" -#include "matchers/LogMatchingTracker.h" -#include "metrics/MetricProducer.h" -#include "packages/UidMap.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -// A MetricsManager is responsible for managing metrics from one single config source. -class MetricsManager : public virtual android::RefBase, public virtual PullUidProvider { -public: - MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs, - const int64_t currentTimeNs, const sp& uidMap, - const sp& pullerManager, - const sp& anomalyAlarmMonitor, - const sp& periodicAlarmMonitor); - - virtual ~MetricsManager(); - - // Return whether the configuration is valid. - bool isConfigValid() const; - - bool checkLogCredentials(const LogEvent& event); - - bool eventSanityCheck(const LogEvent& event); - - void onLogEvent(const LogEvent& event); - - void onAnomalyAlarmFired( - const int64_t& timestampNs, - unordered_set, SpHash>& alarmSet); - - void onPeriodicAlarmFired( - const int64_t& timestampNs, - unordered_set, SpHash>& alarmSet); - - void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version); - - void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid); - - void onUidMapReceived(const int64_t& eventTimeNs); - - void onStatsdInitCompleted(const int64_t& elapsedTimeNs); - - void init(); - - vector getPullAtomUids(int32_t atomId) override; - - bool shouldWriteToDisk() const { - return mNoReportMetricIds.size() != mAllMetricProducers.size(); - } - - bool shouldPersistLocalHistory() const { - return mShouldPersistHistory; - } - - void dumpStates(FILE* out, bool verbose); - - inline bool isInTtl(const int64_t timestampNs) const { - return mTtlNs <= 0 || timestampNs < mTtlEndNs; - }; - - inline bool hashStringInReport() const { - return mHashStringsInReport; - }; - - inline bool versionStringsInReport() const { - return mVersionStringsInReport; - }; - - inline bool installerInReport() const { - return mInstallerInReport; - }; - - void refreshTtl(const int64_t currentTimestampNs) { - if (mTtlNs > 0) { - mTtlEndNs = currentTimestampNs + mTtlNs; - } - }; - - // Returns the elapsed realtime when this metric manager last reported metrics. If this config - // has not yet dumped any reports, this is the time the metricsmanager was initialized. - inline int64_t getLastReportTimeNs() const { - return mLastReportTimeNs; - }; - - inline int64_t getLastReportWallClockNs() const { - return mLastReportWallClockNs; - }; - - inline size_t getNumMetrics() const { - return mAllMetricProducers.size(); - } - - virtual void dropData(const int64_t dropTimeNs); - - virtual void onDumpReport(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - android::util::ProtoOutputStream* protoOutput); - - // Computes the total byte size of all metrics managed by a single config source. - // Does not change the state. - virtual size_t byteSize(); - - // Returns whether or not this config is active. - // The config is active if any metric in the config is active. - inline bool isActive() const { - return mIsActive; - } - - void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs); - - void writeActiveConfigToProtoOutputStream( - int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); - - // Returns true if at least one piece of metadata is written. - bool writeMetadataToProto(int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs, - metadata::StatsMetadata* statsMetadata); - - void loadMetadata(const metadata::StatsMetadata& metadata, - int64_t currentWallClockTimeNs, - int64_t systemElapsedTimeNs); -private: - // For test only. - inline int64_t getTtlEndNs() const { return mTtlEndNs; } - - const ConfigKey mConfigKey; - - sp mUidMap; - - bool mConfigValid = false; - - bool mHashStringsInReport = false; - bool mVersionStringsInReport = false; - bool mInstallerInReport = false; - - const int64_t mTtlNs; - int64_t mTtlEndNs; - - int64_t mLastReportTimeNs; - int64_t mLastReportWallClockNs; - - sp mPullerManager; - - // The uid log sources from StatsdConfig. - std::vector mAllowedUid; - - // The pkg log sources from StatsdConfig. - std::vector mAllowedPkg; - - // The combined uid sources (after translating pkg name to uid). - // Logs from uids that are not in the list will be ignored to avoid spamming. - std::set mAllowedLogSources; - - // To guard access to mAllowedLogSources - mutable std::mutex mAllowedLogSourcesMutex; - - const std::set mWhitelistedAtomIds; - - // We can pull any atom from these uids. - std::set mDefaultPullUids; - - // Uids that specific atoms can pull from. - // This is a map> - std::map> mPullAtomUids; - - // Packages that specific atoms can be pulled from. - std::map> mPullAtomPackages; - - // All uids to pull for this atom. NOTE: Does not include the default uids for memory. - std::map> mCombinedPullAtomUids; - - // Contains the annotations passed in with StatsdConfig. - std::list> mAnnotations; - - const bool mShouldPersistHistory; - - - // All event tags that are interesting to my metrics. - std::set mTagIds; - - // We only store the sp of LogMatchingTracker, MetricProducer, and ConditionTracker in - // MetricsManager. There are relationships between them, and the relationships are denoted by - // index instead of pointers. The reasons for this are: (1) the relationship between them are - // complicated, so storing index instead of pointers reduces the risk that A holds B's sp, and B - // holds A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get - // the related results from a cache using the index. - - // Hold all the atom matchers from the config. - std::vector> mAllAtomMatchers; - - // Hold all the conditions from the config. - std::vector> mAllConditionTrackers; - - // Hold all metrics from the config. - std::vector> mAllMetricProducers; - - // Hold all alert trackers. - std::vector> mAllAnomalyTrackers; - - // Hold all periodic alarm trackers. - std::vector> mAllPeriodicAlarmTrackers; - - // To make the log processing more efficient, we want to do as much filtering as possible - // before we go into individual trackers and conditions to match. - - // 1st filter: check if the event tag id is in mTagIds. - // 2nd filter: if it is, we parse the event because there is at least one member is interested. - // then pass to all LogMatchingTrackers (itself also filter events by ids). - // 3nd filter: for LogMatchingTrackers that matched this event, we pass this event to the - // ConditionTrackers and MetricProducers that use this matcher. - // 4th filter: for ConditionTrackers that changed value due to this event, we pass - // new conditions to metrics that use this condition. - - // The following map is initialized from the statsd_config. - - // Maps from the index of the LogMatchingTracker to index of MetricProducer. - std::unordered_map> mTrackerToMetricMap; - - // Maps from LogMatchingTracker to ConditionTracker - std::unordered_map> mTrackerToConditionMap; - - // Maps from ConditionTracker to MetricProducer - std::unordered_map> mConditionToMetricMap; - - // Maps from life span triggering event to MetricProducers. - std::unordered_map> mActivationAtomTrackerToMetricMap; - - // Maps deactivation triggering event to MetricProducers. - std::unordered_map> mDeactivationAtomTrackerToMetricMap; - - // Maps AlertIds to the index of the corresponding AnomalyTracker stored in mAllAnomalyTrackers. - // The map is used in LoadMetadata to more efficiently lookup AnomalyTrackers from an AlertId. - std::unordered_map mAlertTrackerMap; - - std::vector mMetricIndexesWithActivation; - - void initLogSourceWhiteList(); - - void initPullAtomSources(); - - // The metrics that don't need to be uploaded or even reported. - std::set mNoReportMetricIds; - - // The config is active if any metric in the config is active. - bool mIsActive; - - // The config is always active if any metric in the config does not have an activation signal. - bool mIsAlwaysActive; - - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); - FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); - FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid); - FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain); - FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation); - FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition); - FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents); - - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); - FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); - - FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); - FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); - FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); - - FRIEND_TEST(MetricsManagerTest, TestLogSources); - - FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); - FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); - FRIEND_TEST(StatsLogProcessorTest, - TestActivationOnBootMultipleActivationsDifferentActivationTypes); - FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); - - FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges); - FRIEND_TEST(CountMetricE2eTest, TestSlicedState); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); - FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates); - FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields); - - FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); - FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); - FRIEND_TEST(DurationMetricE2eTest, TestWithCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSuperset); - FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset); - - FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); - FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); - FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp deleted file mode 100644 index 5987a723a421..000000000000 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ /dev/null @@ -1,1156 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "ValueMetricProducer.h" -#include "../guardrail/StatsdStats.h" -#include "../stats_log_util.h" - -#include -#include - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_DOUBLE; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; -using std::map; -using std::shared_ptr; -using std::unordered_map; - -namespace android { -namespace os { -namespace statsd { - -// for StatsLogReport -const int FIELD_ID_ID = 1; -const int FIELD_ID_VALUE_METRICS = 7; -const int FIELD_ID_TIME_BASE = 9; -const int FIELD_ID_BUCKET_SIZE = 10; -const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; -const int FIELD_ID_IS_ACTIVE = 14; -// for ValueMetricDataWrapper -const int FIELD_ID_DATA = 1; -const int FIELD_ID_SKIPPED = 2; -// for SkippedBuckets -const int FIELD_ID_SKIPPED_START_MILLIS = 3; -const int FIELD_ID_SKIPPED_END_MILLIS = 4; -const int FIELD_ID_SKIPPED_DROP_EVENT = 5; -// for DumpEvent Proto -const int FIELD_ID_BUCKET_DROP_REASON = 1; -const int FIELD_ID_DROP_TIME = 2; -// for ValueMetricData -const int FIELD_ID_DIMENSION_IN_WHAT = 1; -const int FIELD_ID_BUCKET_INFO = 3; -const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4; -const int FIELD_ID_SLICE_BY_STATE = 6; -// for ValueBucketInfo -const int FIELD_ID_VALUE_INDEX = 1; -const int FIELD_ID_VALUE_LONG = 2; -const int FIELD_ID_VALUE_DOUBLE = 3; -const int FIELD_ID_VALUES = 9; -const int FIELD_ID_BUCKET_NUM = 4; -const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5; -const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; -const int FIELD_ID_CONDITION_TRUE_NS = 10; - -const Value ZERO_LONG((int64_t)0); -const Value ZERO_DOUBLE((int64_t)0); - -// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently -ValueMetricProducer::ValueMetricProducer( - const ConfigKey& key, const ValueMetric& metric, const int conditionIndex, - const vector& initialConditionCache, - const sp& conditionWizard, const int whatMatcherIndex, - const sp& matcherWizard, const int pullTagId, const int64_t timeBaseNs, - const int64_t startTimeNs, const sp& pullerManager, - const unordered_map>& eventActivationMap, - const unordered_map>>& eventDeactivationMap, - const vector& slicedStateAtoms, - const unordered_map>& stateGroupMap) - : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, - conditionWizard, eventActivationMap, eventDeactivationMap, slicedStateAtoms, - stateGroupMap), - mWhatMatcherIndex(whatMatcherIndex), - mEventMatcherWizard(matcherWizard), - mPullerManager(pullerManager), - mPullTagId(pullTagId), - mIsPulled(pullTagId != -1), - mMinBucketSizeNs(metric.min_bucket_size_nanos()), - mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) != - StatsdStats::kAtomDimensionKeySizeLimitMap.end() - ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first - : StatsdStats::kDimensionKeySizeSoftLimit), - mDimensionHardLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) != - StatsdStats::kAtomDimensionKeySizeLimitMap.end() - ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second - : StatsdStats::kDimensionKeySizeHardLimit), - mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()), - mAggregationType(metric.aggregation_type()), - mUseDiff(metric.has_use_diff() ? metric.use_diff() : (mIsPulled ? true : false)), - mValueDirection(metric.value_direction()), - mSkipZeroDiffOutput(metric.skip_zero_diff_output()), - mUseZeroDefaultBase(metric.use_zero_default_base()), - mHasGlobalBase(false), - mCurrentBucketIsSkipped(false), - mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC - : StatsdStats::kPullMaxDelayNs), - mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()), - // Condition timer will be set later within the constructor after pulling events - mConditionTimer(false, timeBaseNs) { - int64_t bucketSizeMills = 0; - if (metric.has_bucket()) { - bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()); - } else { - bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR); - } - - mBucketSizeNs = bucketSizeMills * 1000000; - - translateFieldMatcher(metric.value_field(), &mFieldMatchers); - - if (metric.has_dimensions_in_what()) { - translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); - mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); - mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()); - } - - if (metric.links().size() > 0) { - for (const auto& link : metric.links()) { - Metric2Condition mc; - mc.conditionId = link.condition(); - translateFieldMatcher(link.fields_in_what(), &mc.metricFields); - translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); - mMetric2ConditionLinks.push_back(mc); - } - mConditionSliced = true; - } - - for (const auto& stateLink : metric.state_link()) { - Metric2State ms; - ms.stateAtomId = stateLink.state_atom_id(); - translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields); - translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields); - mMetric2StateLinks.push_back(ms); - } - - int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs); - mCurrentBucketNum += numBucketsForward; - - flushIfNeededLocked(startTimeNs); - - if (mIsPulled) { - mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(), - mBucketSizeNs); - } - - // Only do this for partial buckets like first bucket. All other buckets should use - // flushIfNeeded to adjust start and end to bucket boundaries. - // Adjust start for partial bucket - mCurrentBucketStartTimeNs = startTimeNs; - mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs); - - // Now that activations are processed, start the condition timer if needed. - mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue, - mCurrentBucketStartTimeNs); - - VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), - (long long)mBucketSizeNs, (long long)mTimeBaseNs); -} - -ValueMetricProducer::~ValueMetricProducer() { - VLOG("~ValueMetricProducer() called"); - if (mIsPulled) { - mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this); - } -} - -void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId, - const HashableDimensionKey& primaryKey, - const FieldValue& oldState, const FieldValue& newState) { - VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d", - (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(), - oldState.mValue.int_value, newState.mValue.int_value); - - // If old and new states are in the same StateGroup, then we do not need to - // pull for this state change. - FieldValue oldStateCopy = oldState; - FieldValue newStateCopy = newState; - mapStateValue(atomId, &oldStateCopy); - mapStateValue(atomId, &newStateCopy); - if (oldStateCopy == newStateCopy) { - return; - } - - // If condition is not true or metric is not active, we do not need to pull - // for this state change. - if (mCondition != ConditionState::kTrue || !mIsActive) { - return; - } - - bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs; - if (isEventLate) { - VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, - (long long)mCurrentBucketStartTimeNs); - invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET); - return; - } - mStateChangePrimaryKey.first = atomId; - mStateChangePrimaryKey.second = primaryKey; - if (mIsPulled) { - pullAndMatchEventsLocked(eventTimeNs); - } - mStateChangePrimaryKey.first = 0; - mStateChangePrimaryKey.second = DEFAULT_DIMENSION_KEY; - flushIfNeededLocked(eventTimeNs); -} - -void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) { - VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); -} - -void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - StatsdStats::getInstance().noteBucketDropped(mMetricId); - - // The current partial bucket is not flushed and does not require a pull, - // so the data is still valid. - flushIfNeededLocked(dropTimeNs); - clearPastBucketsLocked(dropTimeNs); -} - -void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - mPastBuckets.clear(); - mSkippedBuckets.clear(); -} - -void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - ProtoOutputStream* protoOutput) { - VLOG("metric %lld dump report now...", (long long)mMetricId); - if (include_current_partial_bucket) { - // For pull metrics, we need to do a pull at bucket boundaries. If we do not do that the - // current bucket will have incomplete data and the next will have the wrong snapshot to do - // a diff against. If the condition is false, we are fine since the base data is reset and - // we are not tracking anything. - bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue; - if (pullNeeded) { - switch (dumpLatency) { - case FAST: - invalidateCurrentBucket(dumpTimeNs, BucketDropReason::DUMP_REPORT_REQUESTED); - break; - case NO_TIME_CONSTRAINTS: - pullAndMatchEventsLocked(dumpTimeNs); - break; - } - } - flushCurrentBucketLocked(dumpTimeNs, dumpTimeNs); - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); - - if (mPastBuckets.empty() && mSkippedBuckets.empty()) { - return; - } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs); - // Fills the dimension path if not slicing by ALL. - if (!mSliceByPositionALL) { - if (!mDimensionsInWhat.empty()) { - uint64_t dimenPathToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT); - writeDimensionPathToProto(mDimensionsInWhat, protoOutput); - protoOutput->end(dimenPathToken); - } - } - - uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS); - - for (const auto& skippedBucket : mSkippedBuckets) { - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS, - (long long)(NanoToMillis(skippedBucket.bucketStartTimeNs))); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS, - (long long)(NanoToMillis(skippedBucket.bucketEndTimeNs))); - for (const auto& dropEvent : skippedBucket.dropEvents) { - uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SKIPPED_DROP_EVENT); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME, - (long long)(NanoToMillis(dropEvent.dropTimeNs))); - ; - protoOutput->end(dropEventToken); - } - protoOutput->end(wrapperToken); - } - - for (const auto& pair : mPastBuckets) { - const MetricDimensionKey& dimensionKey = pair.first; - VLOG(" dimension key %s", dimensionKey.toString().c_str()); - uint64_t wrapperToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - - // First fill dimension. - if (mSliceByPositionALL) { - uint64_t dimensionToken = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput); - protoOutput->end(dimensionToken); - } else { - writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(), - FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput); - } - - // Then fill slice_by_state. - for (auto state : dimensionKey.getStateValuesKey().getValues()) { - uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SLICE_BY_STATE); - writeStateToProto(state, protoOutput); - protoOutput->end(stateToken); - } - - // Then fill bucket_info (ValueBucketInfo). - for (const auto& bucket : pair.second) { - uint64_t bucketInfoToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - - if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketStartNs)); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS, - (long long)NanoToMillis(bucket.mBucketEndNs)); - } else { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM, - (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs))); - } - // only write the condition timer value if the metric has a condition. - if (mConditionTrackerIndex >= 0) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS, - (long long)bucket.mConditionTrueNs); - } - for (int i = 0; i < (int)bucket.valueIndex.size(); i++) { - int index = bucket.valueIndex[i]; - const Value& value = bucket.values[i]; - uint64_t valueToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_VALUES); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_INDEX, - index); - if (value.getType() == LONG) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_LONG, - (long long)value.long_value); - VLOG("\t bucket [%lld - %lld] value %d: %lld", (long long)bucket.mBucketStartNs, - (long long)bucket.mBucketEndNs, index, (long long)value.long_value); - } else if (value.getType() == DOUBLE) { - protoOutput->write(FIELD_TYPE_DOUBLE | FIELD_ID_VALUE_DOUBLE, - value.double_value); - VLOG("\t bucket [%lld - %lld] value %d: %.2f", (long long)bucket.mBucketStartNs, - (long long)bucket.mBucketEndNs, index, value.double_value); - } else { - VLOG("Wrong value type for ValueMetric output: %d", value.getType()); - } - protoOutput->end(valueToken); - } - protoOutput->end(bucketInfoToken); - } - protoOutput->end(wrapperToken); - } - protoOutput->end(protoToken); - - VLOG("metric %lld dump report now...", (long long)mMetricId); - if (erase_data) { - mPastBuckets.clear(); - mSkippedBuckets.clear(); - } -} - -void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs, - const BucketDropReason reason) { - if (!mCurrentBucketIsSkipped) { - // Only report to StatsdStats once per invalid bucket. - StatsdStats::getInstance().noteInvalidatedBucket(mMetricId); - } - - skipCurrentBucket(dropTimeNs, reason); -} - -void ValueMetricProducer::invalidateCurrentBucket(const int64_t dropTimeNs, - const BucketDropReason reason) { - invalidateCurrentBucketWithoutResetBase(dropTimeNs, reason); - resetBase(); -} - -void ValueMetricProducer::skipCurrentBucket(const int64_t dropTimeNs, - const BucketDropReason reason) { - if (!maxDropEventsReached()) { - mCurrentSkippedBucket.dropEvents.emplace_back(buildDropEvent(dropTimeNs, reason)); - } - mCurrentBucketIsSkipped = true; -} - -void ValueMetricProducer::resetBase() { - for (auto& slice : mCurrentBaseInfo) { - for (auto& baseInfo : slice.second) { - baseInfo.hasBase = false; - } - } - mHasGlobalBase = false; -} - -// Handle active state change. Active state change is treated like a condition change: -// - drop bucket if active state change event arrives too late -// - if condition is true, pull data on active state changes -// - ConditionTimer tracks changes based on AND of condition and active state. -void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) { - bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs; - if (isEventTooLate) { - // Drop bucket because event arrived too late, ie. we are missing data for this bucket. - StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); - invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET); - } - - // Call parent method once we've verified the validity of current bucket. - MetricProducer::onActiveStateChangedLocked(eventTimeNs); - - if (ConditionState::kTrue != mCondition) { - return; - } - - // Pull on active state changes. - if (!isEventTooLate) { - if (mIsPulled) { - pullAndMatchEventsLocked(eventTimeNs); - } - // When active state changes from true to false, clear diff base but don't - // reset other counters as we may accumulate more value in the bucket. - if (mUseDiff && !mIsActive) { - resetBase(); - } - } - - flushIfNeededLocked(eventTimeNs); - - // Let condition timer know of new active state. - mConditionTimer.onConditionChanged(mIsActive, eventTimeNs); -} - -void ValueMetricProducer::onConditionChangedLocked(const bool condition, - const int64_t eventTimeNs) { - ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse; - bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs; - - // If the config is not active, skip the event. - if (!mIsActive) { - mCondition = isEventTooLate ? ConditionState::kUnknown : newCondition; - return; - } - - // If the event arrived late, mark the bucket as invalid and skip the event. - if (isEventTooLate) { - VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, - (long long)mCurrentBucketStartTimeNs); - StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); - StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId); - invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET); - mCondition = ConditionState::kUnknown; - mConditionTimer.onConditionChanged(mCondition, eventTimeNs); - return; - } - - // If the previous condition was unknown, mark the bucket as invalid - // because the bucket will contain partial data. For example, the condition - // change might happen close to the end of the bucket and we might miss a - // lot of data. - // - // We still want to pull to set the base. - if (mCondition == ConditionState::kUnknown) { - invalidateCurrentBucket(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN); - } - - // Pull and match for the following condition change cases: - // unknown/false -> true - condition changed - // true -> false - condition changed - // true -> true - old condition was true so we can flush the bucket at the - // end if needed. - // - // We don’t need to pull for unknown -> false or false -> false. - // - // onConditionChangedLocked might happen on bucket boundaries if this is - // called before #onDataPulled. - if (mIsPulled && - (newCondition == ConditionState::kTrue || mCondition == ConditionState::kTrue)) { - pullAndMatchEventsLocked(eventTimeNs); - } - - // For metrics that use diff, when condition changes from true to false, - // clear diff base but don't reset other counts because we may accumulate - // more value in the bucket. - if (mUseDiff && - (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)) { - resetBase(); - } - - // Update condition state after pulling. - mCondition = newCondition; - - flushIfNeededLocked(eventTimeNs); - mConditionTimer.onConditionChanged(mCondition, eventTimeNs); -} - -void ValueMetricProducer::prepareFirstBucketLocked() { - // Kicks off the puller immediately if condition is true and diff based. - if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { - pullAndMatchEventsLocked(mCurrentBucketStartTimeNs); - } -} - -void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { - vector> allData; - if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) { - ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs); - invalidateCurrentBucket(timestampNs, BucketDropReason::PULL_FAILED); - return; - } - - accumulateEvents(allData, timestampNs, timestampNs); -} - -int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) { - return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs; -} - -// By design, statsd pulls data at bucket boundaries using AlarmManager. These pulls are likely -// to be delayed. Other events like condition changes or app upgrade which are not based on -// AlarmManager might have arrived earlier and close the bucket. -void ValueMetricProducer::onDataPulled(const std::vector>& allData, - bool pullSuccess, int64_t originalPullTimeNs) { - std::lock_guard lock(mMutex); - if (mCondition == ConditionState::kTrue) { - // If the pull failed, we won't be able to compute a diff. - if (!pullSuccess) { - invalidateCurrentBucket(originalPullTimeNs, BucketDropReason::PULL_FAILED); - } else { - bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs(); - if (isEventLate) { - // If the event is late, we are in the middle of a bucket. Just - // process the data without trying to snap the data to the nearest bucket. - accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs); - } else { - // For scheduled pulled data, the effective event time is snap to the nearest - // bucket end. In the case of waking up from a deep sleep state, we will - // attribute to the previous bucket end. If the sleep was long but not very - // long, we will be in the immediate next bucket. Previous bucket may get a - // larger number as we pull at a later time than real bucket end. - // - // If the sleep was very long, we skip more than one bucket before sleep. In - // this case, if the diff base will be cleared and this new data will serve as - // new diff base. - int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1; - StatsdStats::getInstance().noteBucketBoundaryDelayNs( - mMetricId, originalPullTimeNs - bucketEndTime); - accumulateEvents(allData, originalPullTimeNs, bucketEndTime); - } - } - } - - // We can probably flush the bucket. Since we used bucketEndTime when calling - // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed. - flushIfNeededLocked(originalPullTimeNs); -} - -void ValueMetricProducer::accumulateEvents(const std::vector>& allData, - int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) { - bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs; - if (isEventLate) { - VLOG("Skip bucket end pull due to late arrival: %lld vs %lld", - (long long)eventElapsedTimeNs, (long long)mCurrentBucketStartTimeNs); - StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); - invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET); - return; - } - - const int64_t elapsedRealtimeNs = getElapsedRealtimeNs(); - const int64_t pullDelayNs = elapsedRealtimeNs - originalPullTimeNs; - StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs); - if (pullDelayNs > mMaxPullDelayNs) { - ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId, - (long long)mMaxPullDelayNs); - StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId); - // We are missing one pull from the bucket which means we will not have a complete view of - // what's going on. - invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::PULL_DELAYED); - return; - } - - mMatchedMetricDimensionKeys.clear(); - for (const auto& data : allData) { - LogEvent localCopy = data->makeCopy(); - if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) == - MatchingState::kMatched) { - localCopy.setElapsedTimestampNs(eventElapsedTimeNs); - onMatchedLogEventLocked(mWhatMatcherIndex, localCopy); - } - } - // If a key that is: - // 1. Tracked in mCurrentSlicedBucket and - // 2. A superset of the current mStateChangePrimaryKey - // was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys) - // then we need to reset the base. - for (auto& slice : mCurrentSlicedBucket) { - const auto& whatKey = slice.first.getDimensionKeyInWhat(); - bool presentInPulledData = - mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end(); - if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) { - auto it = mCurrentBaseInfo.find(whatKey); - for (auto& baseInfo : it->second) { - baseInfo.hasBase = false; - } - } - } - mMatchedMetricDimensionKeys.clear(); - mHasGlobalBase = true; - - // If we reach the guardrail, we might have dropped some data which means the bucket is - // incomplete. - // - // The base also needs to be reset. If we do not have the full data, we might - // incorrectly compute the diff when mUseZeroDefaultBase is true since an existing key - // might be missing from mCurrentSlicedBucket. - if (hasReachedGuardRailLimit()) { - invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::DIMENSION_GUARDRAIL_REACHED); - mCurrentSlicedBucket.clear(); - } -} - -void ValueMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { - if (mCurrentSlicedBucket.size() == 0) { - return; - } - - fprintf(out, "ValueMetric %lld dimension size %lu\n", (long long)mMetricId, - (unsigned long)mCurrentSlicedBucket.size()); - if (verbose) { - for (const auto& it : mCurrentSlicedBucket) { - for (const auto& interval : it.second) { - fprintf(out, "\t(what)%s\t(states)%s (value)%s\n", - it.first.getDimensionKeyInWhat().toString().c_str(), - it.first.getStateValuesKey().toString().c_str(), - interval.value.toString().c_str()); - } - } - } -} - -bool ValueMetricProducer::hasReachedGuardRailLimit() const { - return mCurrentSlicedBucket.size() >= mDimensionHardLimit; -} - -bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { - // ===========GuardRail============== - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) { - return false; - } - if (mCurrentSlicedBucket.size() > mDimensionSoftLimit - 1) { - size_t newTupleCount = mCurrentSlicedBucket.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (hasReachedGuardRailLimit()) { - ALOGE("ValueMetric %lld dropping data for dimension key %s", (long long)mMetricId, - newKey.toString().c_str()); - StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); - return true; - } - } - - return false; -} - -bool ValueMetricProducer::hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey) { - // ===========GuardRail============== - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentFullBucket.find(newKey) != mCurrentFullBucket.end()) { - return false; - } - if (mCurrentFullBucket.size() > mDimensionSoftLimit - 1) { - size_t newTupleCount = mCurrentFullBucket.size() + 1; - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > mDimensionHardLimit) { - ALOGE("ValueMetric %lld dropping data for full bucket dimension key %s", - (long long)mMetricId, - newKey.toString().c_str()); - return true; - } - } - - return false; -} - -bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret) { - for (const FieldValue& value : event.getValues()) { - if (value.mField.matches(matcher)) { - switch (value.mValue.type) { - case INT: - ret.setLong(value.mValue.int_value); - break; - case LONG: - ret.setLong(value.mValue.long_value); - break; - case FLOAT: - ret.setDouble(value.mValue.float_value); - break; - case DOUBLE: - ret.setDouble(value.mValue.double_value); - break; - default: - return false; - break; - } - return true; - } - } - return false; -} - -void ValueMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const map& statePrimaryKeys) { - auto whatKey = eventKey.getDimensionKeyInWhat(); - auto stateKey = eventKey.getStateValuesKey(); - - // Skip this event if a state changed occurred for a different primary key. - auto it = statePrimaryKeys.find(mStateChangePrimaryKey.first); - // Check that both the atom id and the primary key are equal. - if (it != statePrimaryKeys.end() && it->second != mStateChangePrimaryKey.second) { - VLOG("ValueMetric skip event with primary key %s because state change primary key " - "is %s", - it->second.toString().c_str(), mStateChangePrimaryKey.second.toString().c_str()); - return; - } - - int64_t eventTimeNs = event.GetElapsedTimestampNs(); - if (eventTimeNs < mCurrentBucketStartTimeNs) { - VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, - (long long)mCurrentBucketStartTimeNs); - return; - } - mMatchedMetricDimensionKeys.insert(whatKey); - - if (!mIsPulled) { - // We cannot flush without doing a pull first. - flushIfNeededLocked(eventTimeNs); - } - - // We should not accumulate the data for pushed metrics when the condition is false. - bool shouldSkipForPushMetric = !mIsPulled && !condition; - // For pulled metrics, there are two cases: - // - to compute diffs, we need to process all the state changes - // - for non-diffs metrics, we should ignore the data if the condition wasn't true. If we have a - // state change from - // + True -> True: we should process the data, it might be a bucket boundary - // + True -> False: we als need to process the data. - bool shouldSkipForPulledMetric = mIsPulled && !mUseDiff - && mCondition != ConditionState::kTrue; - if (shouldSkipForPushMetric || shouldSkipForPulledMetric) { - VLOG("ValueMetric skip event because condition is false and we are not using diff (for " - "pulled metric)"); - return; - } - - if (hitGuardRailLocked(eventKey)) { - return; - } - - vector& baseInfos = mCurrentBaseInfo[whatKey]; - if (baseInfos.size() < mFieldMatchers.size()) { - VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size()); - baseInfos.resize(mFieldMatchers.size()); - } - - for (BaseInfo& baseInfo : baseInfos) { - if (!baseInfo.hasCurrentState) { - baseInfo.currentState = getUnknownStateKey(); - baseInfo.hasCurrentState = true; - } - } - - // We need to get the intervals stored with the previous state key so we can - // close these value intervals. - const auto oldStateKey = baseInfos[0].currentState; - vector& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)]; - if (intervals.size() < mFieldMatchers.size()) { - VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size()); - intervals.resize(mFieldMatchers.size()); - } - - // We only use anomaly detection under certain cases. - // N.B.: The anomaly detection cases were modified in order to fix an issue with value metrics - // containing multiple values. We tried to retain all previous behaviour, but we are unsure the - // previous behaviour was correct. At the time of the fix, anomaly detection had no owner. - // Whoever next works on it should look into the cases where it is triggered in this function. - // Discussion here: http://ag/6124370. - bool useAnomalyDetection = true; - - for (int i = 0; i < (int)mFieldMatchers.size(); i++) { - const Matcher& matcher = mFieldMatchers[i]; - BaseInfo& baseInfo = baseInfos[i]; - Interval& interval = intervals[i]; - interval.valueIndex = i; - Value value; - baseInfo.hasCurrentState = true; - baseInfo.currentState = stateKey; - if (!getDoubleOrLong(event, matcher, value)) { - VLOG("Failed to get value %d from event %s", i, event.ToString().c_str()); - StatsdStats::getInstance().noteBadValueType(mMetricId); - return; - } - interval.seenNewData = true; - - if (mUseDiff) { - if (!baseInfo.hasBase) { - if (mHasGlobalBase && mUseZeroDefaultBase) { - // The bucket has global base. This key does not. - // Optionally use zero as base. - baseInfo.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE); - baseInfo.hasBase = true; - } else { - // no base. just update base and return. - baseInfo.base = value; - baseInfo.hasBase = true; - // If we're missing a base, do not use anomaly detection on incomplete data - useAnomalyDetection = false; - // Continue (instead of return) here in order to set baseInfo.base and - // baseInfo.hasBase for other baseInfos - continue; - } - } - - Value diff; - switch (mValueDirection) { - case ValueMetric::INCREASING: - if (value >= baseInfo.base) { - diff = value - baseInfo.base; - } else if (mUseAbsoluteValueOnReset) { - diff = value; - } else { - VLOG("Unexpected decreasing value"); - StatsdStats::getInstance().notePullDataError(mPullTagId); - baseInfo.base = value; - // If we've got bad data, do not use anomaly detection - useAnomalyDetection = false; - continue; - } - break; - case ValueMetric::DECREASING: - if (baseInfo.base >= value) { - diff = baseInfo.base - value; - } else if (mUseAbsoluteValueOnReset) { - diff = value; - } else { - VLOG("Unexpected increasing value"); - StatsdStats::getInstance().notePullDataError(mPullTagId); - baseInfo.base = value; - // If we've got bad data, do not use anomaly detection - useAnomalyDetection = false; - continue; - } - break; - case ValueMetric::ANY: - diff = value - baseInfo.base; - break; - default: - break; - } - baseInfo.base = value; - value = diff; - } - - if (interval.hasValue) { - switch (mAggregationType) { - case ValueMetric::SUM: - // for AVG, we add up and take average when flushing the bucket - case ValueMetric::AVG: - interval.value += value; - break; - case ValueMetric::MIN: - interval.value = std::min(value, interval.value); - break; - case ValueMetric::MAX: - interval.value = std::max(value, interval.value); - break; - default: - break; - } - } else { - interval.value = value; - interval.hasValue = true; - } - interval.sampleSize += 1; - } - - // Only trigger the tracker if all intervals are correct - if (useAnomalyDetection) { - // TODO: propgate proper values down stream when anomaly support doubles - long wholeBucketVal = intervals[0].value.long_value; - auto prev = mCurrentFullBucket.find(eventKey); - if (prev != mCurrentFullBucket.end()) { - wholeBucketVal += prev->second; - } - for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey, - wholeBucketVal); - } - } -} - -// For pulled metrics, we always need to make sure we do a pull before flushing the bucket -// if mCondition is true! -void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { - int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - if (eventTimeNs < currentBucketEndTimeNs) { - VLOG("eventTime is %lld, less than current bucket end time %lld", (long long)eventTimeNs, - (long long)(currentBucketEndTimeNs)); - return; - } - int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs); - int64_t nextBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; - flushCurrentBucketLocked(eventTimeNs, nextBucketStartTimeNs); -} - -int64_t ValueMetricProducer::calcBucketsForwardCount(const int64_t& eventTimeNs) const { - int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - if (eventTimeNs < currentBucketEndTimeNs) { - return 0; - } - return 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; -} - -void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) { - if (mCondition == ConditionState::kUnknown) { - StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId); - invalidateCurrentBucketWithoutResetBase(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN); - } - - VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, - (int)mCurrentSlicedBucket.size()); - - int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); - int64_t bucketEndTime = fullBucketEndTimeNs; - int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs); - - // Skip buckets if this is a pulled metric or a pushed metric that is diffed. - if (numBucketsForward > 1 && (mIsPulled || mUseDiff)) { - - VLOG("Skipping forward %lld buckets", (long long)numBucketsForward); - StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId); - // Something went wrong. Maybe the device was sleeping for a long time. It is better - // to mark the current bucket as invalid. The last pull might have been successful through. - invalidateCurrentBucketWithoutResetBase(eventTimeNs, - BucketDropReason::MULTIPLE_BUCKETS_SKIPPED); - // End the bucket at the next bucket start time so the entire interval is skipped. - bucketEndTime = nextBucketStartTimeNs; - } else if (eventTimeNs < fullBucketEndTimeNs) { - bucketEndTime = eventTimeNs; - } - - // Close the current bucket. - int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime); - bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs; - if (!isBucketLargeEnough) { - skipCurrentBucket(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL); - } - if (!mCurrentBucketIsSkipped) { - bool bucketHasData = false; - // The current bucket is large enough to keep. - for (const auto& slice : mCurrentSlicedBucket) { - ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second); - bucket.mConditionTrueNs = conditionTrueDuration; - // it will auto create new vector of ValuebucketInfo if the key is not found. - if (bucket.valueIndex.size() > 0) { - auto& bucketList = mPastBuckets[slice.first]; - bucketList.push_back(bucket); - bucketHasData = true; - } - } - if (!bucketHasData) { - skipCurrentBucket(eventTimeNs, BucketDropReason::NO_DATA); - } - } - - if (mCurrentBucketIsSkipped) { - mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs; - mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime; - mSkippedBuckets.emplace_back(mCurrentSkippedBucket); - } - - // This means that the current bucket was not flushed before a forced bucket split. - // This can happen if an app update or a dump report with include_current_partial_bucket is - // requested before we get a chance to flush the bucket due to receiving new data, either from - // the statsd socket or the StatsPullerManager. - if (bucketEndTime < nextBucketStartTimeNs) { - SkippedBucket bucketInGap; - bucketInGap.bucketStartTimeNs = bucketEndTime; - bucketInGap.bucketEndTimeNs = nextBucketStartTimeNs; - bucketInGap.dropEvents.emplace_back( - buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA)); - mSkippedBuckets.emplace_back(bucketInGap); - } - - appendToFullBucket(eventTimeNs > fullBucketEndTimeNs); - initCurrentSlicedBucket(nextBucketStartTimeNs); - // Update the condition timer again, in case we skipped buckets. - mConditionTimer.newBucketStart(nextBucketStartTimeNs); - mCurrentBucketNum += numBucketsForward; -} - -ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime, - const std::vector& intervals) { - ValueBucket bucket; - bucket.mBucketStartNs = mCurrentBucketStartTimeNs; - bucket.mBucketEndNs = bucketEndTime; - for (const auto& interval : intervals) { - if (interval.hasValue) { - // skip the output if the diff is zero - if (mSkipZeroDiffOutput && mUseDiff && interval.value.isZero()) { - continue; - } - bucket.valueIndex.push_back(interval.valueIndex); - if (mAggregationType != ValueMetric::AVG) { - bucket.values.push_back(interval.value); - } else { - double sum = interval.value.type == LONG ? (double)interval.value.long_value - : interval.value.double_value; - bucket.values.push_back(Value((double)sum / interval.sampleSize)); - } - } - } - return bucket; -} - -void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) { - StatsdStats::getInstance().noteBucketCount(mMetricId); - // Cleanup data structure to aggregate values. - for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) { - bool obsolete = true; - for (auto& interval : it->second) { - interval.hasValue = false; - interval.sampleSize = 0; - if (interval.seenNewData) { - obsolete = false; - } - interval.seenNewData = false; - } - - if (obsolete) { - it = mCurrentSlicedBucket.erase(it); - } else { - it++; - } - // TODO(b/157655103): remove mCurrentBaseInfo entries when obsolete - } - - mCurrentBucketIsSkipped = false; - mCurrentSkippedBucket.reset(); - - // If we do not have a global base when the condition is true, - // we will have incomplete bucket for the next bucket. - if (mUseDiff && !mHasGlobalBase && mCondition) { - mCurrentBucketIsSkipped = false; - } - mCurrentBucketStartTimeNs = nextBucketStartTimeNs; - VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, - (long long)mCurrentBucketStartTimeNs); -} - -void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) { - if (mCurrentBucketIsSkipped) { - if (isFullBucketReached) { - // If the bucket is invalid, we ignore the full bucket since it contains invalid data. - mCurrentFullBucket.clear(); - } - // Current bucket is invalid, we do not add it to the full bucket. - return; - } - - if (isFullBucketReached) { // If full bucket, send to anomaly tracker. - // Accumulate partial buckets with current value and then send to anomaly tracker. - if (mCurrentFullBucket.size() > 0) { - for (const auto& slice : mCurrentSlicedBucket) { - if (hitFullBucketGuardRailLocked(slice.first)) { - continue; - } - // TODO: fix this when anomaly can accept double values - auto& interval = slice.second[0]; - if (interval.hasValue) { - mCurrentFullBucket[slice.first] += interval.value.long_value; - } - } - for (const auto& slice : mCurrentFullBucket) { - for (auto& tracker : mAnomalyTrackers) { - if (tracker != nullptr) { - tracker->addPastBucket(slice.first, slice.second, mCurrentBucketNum); - } - } - } - mCurrentFullBucket.clear(); - } else { - // Skip aggregating the partial buckets since there's no previous partial bucket. - for (const auto& slice : mCurrentSlicedBucket) { - for (auto& tracker : mAnomalyTrackers) { - if (tracker != nullptr) { - // TODO: fix this when anomaly can accept double values - auto& interval = slice.second[0]; - if (interval.hasValue) { - tracker->addPastBucket(slice.first, interval.value.long_value, - mCurrentBucketNum); - } - } - } - } - } - } else { - // Accumulate partial bucket. - for (const auto& slice : mCurrentSlicedBucket) { - // TODO: fix this when anomaly can accept double values - auto& interval = slice.second[0]; - if (interval.hasValue) { - mCurrentFullBucket[slice.first] += interval.value.long_value; - } - } - } -} - -size_t ValueMetricProducer::byteSizeLocked() const { - size_t totalSize = 0; - for (const auto& pair : mPastBuckets) { - totalSize += pair.second.size() * kBucketSize; - } - return totalSize; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h deleted file mode 100644 index b359af745c91..000000000000 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "anomaly/AnomalyTracker.h" -#include "condition/ConditionTimer.h" -#include "condition/ConditionTracker.h" -#include "external/PullDataReceiver.h" -#include "external/StatsPullerManager.h" -#include "matchers/EventMatcherWizard.h" -#include "stats_log_util.h" -#include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -namespace android { -namespace os { -namespace statsd { - -struct ValueBucket { - int64_t mBucketStartNs; - int64_t mBucketEndNs; - std::vector valueIndex; - std::vector values; - // If the metric has no condition, then this field is just wasted. - // When we tune statsd memory usage in the future, this is a candidate to optimize. - int64_t mConditionTrueNs; -}; - - -// Aggregates values within buckets. -// -// There are different events that might complete a bucket -// - a condition change -// - an app upgrade -// - an alarm set to the end of the bucket -class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver { -public: - ValueMetricProducer( - const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex, - const vector& initialConditionCache, - const sp& conditionWizard, const int whatMatcherIndex, - const sp& matcherWizard, const int pullTagId, - const int64_t timeBaseNs, const int64_t startTimeNs, - const sp& pullerManager, - const std::unordered_map>& eventActivationMap = {}, - const std::unordered_map>>& - eventDeactivationMap = {}, - const vector& slicedStateAtoms = {}, - const unordered_map>& stateGroupMap = {}); - - virtual ~ValueMetricProducer(); - - // Process data pulled on bucket boundary. - void onDataPulled(const std::vector>& data, - bool pullSuccess, int64_t originalPullTimeNs) override; - - // ValueMetric needs special logic if it's a pulled atom. - void notifyAppUpgrade(const int64_t& eventTimeNs) override { - std::lock_guard lock(mMutex); - if (!mSplitBucketForAppUpgrade) { - return; - } - if (mIsPulled && mCondition == ConditionState::kTrue) { - pullAndMatchEventsLocked(eventTimeNs); - } - flushCurrentBucketLocked(eventTimeNs, eventTimeNs); - }; - - // ValueMetric needs special logic if it's a pulled atom. - void onStatsdInitCompleted(const int64_t& eventTimeNs) override { - std::lock_guard lock(mMutex); - if (mIsPulled && mCondition == ConditionState::kTrue) { - pullAndMatchEventsLocked(eventTimeNs); - } - flushCurrentBucketLocked(eventTimeNs, eventTimeNs); - }; - - void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey, - const FieldValue& oldState, const FieldValue& newState) override; - -protected: - void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, const LogEvent& event, - const std::map& statePrimaryKeys) override; - -private: - void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set *str_set, - android::util::ProtoOutputStream* protoOutput) override; - void clearPastBucketsLocked(const int64_t dumpTimeNs) override; - - // Internal interface to handle active state change. - void onActiveStateChangedLocked(const int64_t& eventTimeNs) override; - - // Internal interface to handle condition change. - void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override; - - // Internal interface to handle sliced condition change. - void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; - - // Internal function to calculate the current used bytes. - size_t byteSizeLocked() const override; - - void dumpStatesLocked(FILE* out, bool verbose) const override; - - // For pulled metrics, this method should only be called if a pull has be done. Else we will - // not have complete data for the bucket. - void flushIfNeededLocked(const int64_t& eventTime) override; - - // For pulled metrics, this method should only be called if a pulled have be done. Else we will - // not have complete data for the bucket. - void flushCurrentBucketLocked(const int64_t& eventTimeNs, - const int64_t& nextBucketStartTimeNs) override; - - void prepareFirstBucketLocked() override; - - void dropDataLocked(const int64_t dropTimeNs) override; - - // Calculate previous bucket end time based on current time. - int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs); - - // Calculate how many buckets are present between the current bucket and eventTimeNs. - int64_t calcBucketsForwardCount(const int64_t& eventTimeNs) const; - - // Mark the data as invalid. - void invalidateCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason); - - void invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs, - const BucketDropReason reason); - - // Skips the current bucket without notifying StatsdStats of the skipped bucket. - // This should only be called from #flushCurrentBucketLocked. Otherwise, a future event that - // causes the bucket to be invalidated will not notify StatsdStats. - void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason); - - const int mWhatMatcherIndex; - - sp mEventMatcherWizard; - - sp mPullerManager; - - // Value fields for matching. - std::vector mFieldMatchers; - - // Value fields for matching. - std::set mMatchedMetricDimensionKeys; - - // Holds the atom id, primary key pair from a state change. - pair mStateChangePrimaryKey; - - // tagId for pulled data. -1 if this is not pulled - const int mPullTagId; - - // if this is pulled metric - const bool mIsPulled; - - // internal state of an ongoing aggregation bucket. - typedef struct { - // Index in multi value aggregation. - int valueIndex; - // Current value, depending on the aggregation type. - Value value; - // Number of samples collected. - int sampleSize; - // If this dimension has any non-tainted value. If not, don't report the - // dimension. - bool hasValue = false; - // Whether new data is seen in the bucket. - bool seenNewData = false; - } Interval; - - typedef struct { - // Holds current base value of the dimension. Take diff and update if necessary. - Value base; - // Whether there is a base to diff to. - bool hasBase; - // Last seen state value(s). - HashableDimensionKey currentState; - // Whether this dimensions in what key has a current state key. - bool hasCurrentState; - } BaseInfo; - - std::unordered_map> mCurrentSlicedBucket; - - std::unordered_map> mCurrentBaseInfo; - - std::unordered_map mCurrentFullBucket; - - // Save the past buckets and we can clear when the StatsLogReport is dumped. - std::unordered_map> mPastBuckets; - - const int64_t mMinBucketSizeNs; - - // Util function to check whether the specified dimension hits the guardrail. - bool hitGuardRailLocked(const MetricDimensionKey& newKey); - - bool hasReachedGuardRailLimit() const; - - bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey); - - void pullAndMatchEventsLocked(const int64_t timestampNs); - - void accumulateEvents(const std::vector>& allData, - int64_t originalPullTimeNs, int64_t eventElapsedTimeNs); - - ValueBucket buildPartialBucket(int64_t bucketEndTime, - const std::vector& intervals); - - void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs); - - void appendToFullBucket(const bool isFullBucketReached); - - // Reset diff base and mHasGlobalBase - void resetBase(); - - static const size_t kBucketSize = sizeof(ValueBucket{}); - - const size_t mDimensionSoftLimit; - - const size_t mDimensionHardLimit; - - const bool mUseAbsoluteValueOnReset; - - const ValueMetric::AggregationType mAggregationType; - - const bool mUseDiff; - - const ValueMetric::ValueDirection mValueDirection; - - const bool mSkipZeroDiffOutput; - - // If true, use a zero value as base to compute the diff. - // This is used for new keys which are present in the new data but was not - // present in the base data. - // The default base will only be used if we have a global base. - const bool mUseZeroDefaultBase; - - // For pulled metrics, this is always set to true whenever a pull succeeds. - // It is set to false when a pull fails, or upon condition change to false. - // This is used to decide if we have the right base data to compute the - // diff against. - bool mHasGlobalBase; - - // This is to track whether or not the bucket is skipped for any of the reasons listed in - // BucketDropReason, many of which make the bucket potentially invalid. - bool mCurrentBucketIsSkipped; - - const int64_t mMaxPullDelayNs; - - const bool mSplitBucketForAppUpgrade; - - ConditionTimer mConditionTimer; - - FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); - FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2); - FRIEND_TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet); - FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime); - FRIEND_TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged); - FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary); - FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged); - FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled); - FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition); - FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket); - FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff); - FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff); - FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries); - FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse); - FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue); - FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure); - FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges); - FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset); - FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset); - FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering); - FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled); - FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg); - FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax); - FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin); - FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum); - FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate); - FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput); - FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedState); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMap); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions); - FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithCondition); - FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey); - FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase); - FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures); - - FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed); - FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed); - FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed); - FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit); - FRIEND_TEST(ValueMetricProducerTest_BucketDrop, - TestInvalidBucketWhenAccumulateEventWrongBucket); - - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket); - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid); - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated); - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPushedEvents); - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValue); - FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse); - - friend class ValueMetricProducerTestHelper; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h deleted file mode 100644 index 8d59d1362919..000000000000 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef DURATION_TRACKER_H -#define DURATION_TRACKER_H - -#include "anomaly/DurationAnomalyTracker.h" -#include "condition/ConditionWizard.h" -#include "config/ConfigKey.h" -#include "stats_util.h" - -namespace android { -namespace os { -namespace statsd { - -enum DurationState { - kStopped = 0, // The event is stopped. - kStarted = 1, // The event is on going. - kPaused = 2, // The event is started, but condition is false, clock is paused. When condition - // turns to true, kPaused will become kStarted. -}; - -// Hold duration information for one atom level duration in current on-going bucket. -struct DurationInfo { - DurationState state; - - // the number of starts seen. - int32_t startCount; - - // most recent start time. - int64_t lastStartTime; - // existing duration in current bucket. - int64_t lastDuration; - // cache the HashableDimensionKeys we need to query the condition for this duration event. - ConditionKey conditionKeys; - - DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){}; -}; - -struct DurationBucket { - int64_t mBucketStartNs; - int64_t mBucketEndNs; - int64_t mDuration; -}; - -struct DurationValues { - // Recorded duration for current partial bucket. - int64_t mDuration; - - // Sum of past partial bucket durations in current full bucket. - // Used for anomaly detection. - int64_t mDurationFullBucket; -}; - -class DurationTracker { -public: - DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, - sp wizard, int conditionIndex, bool nesting, - int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs, - int64_t bucketSizeNs, bool conditionSliced, bool fullLink, - const std::vector>& anomalyTrackers) - : mConfigKey(key), - mTrackerId(id), - mEventKey(eventKey), - mWizard(wizard), - mConditionTrackerIndex(conditionIndex), - mBucketSizeNs(bucketSizeNs), - mNested(nesting), - mCurrentBucketStartTimeNs(currentBucketStartNs), - mDuration(0), - mCurrentBucketNum(currentBucketNum), - mStartTimeNs(startTimeNs), - mConditionSliced(conditionSliced), - mHasLinksToAllConditionDimensionsInTracker(fullLink), - mAnomalyTrackers(anomalyTrackers){}; - - virtual ~DurationTracker(){}; - - virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime, - const ConditionKey& conditionKey) = 0; - virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime, - const bool stopAll) = 0; - virtual void noteStopAll(const int64_t eventTime) = 0; - - virtual void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) = 0; - virtual void onConditionChanged(bool condition, const int64_t timestamp) = 0; - - virtual void onStateChanged(const int64_t timestamp, const int32_t atomId, - const FieldValue& newState) = 0; - - // Flush stale buckets if needed, and return true if the tracker has no on-going duration - // events, so that the owner can safely remove the tracker. - virtual bool flushIfNeeded( - int64_t timestampNs, - std::unordered_map>* output) = 0; - - // Should only be called during an app upgrade or from this tracker's flushIfNeeded. If from - // an app upgrade, we assume that we're trying to form a partial bucket. - virtual bool flushCurrentBucket( - const int64_t& eventTimeNs, - std::unordered_map>* output) = 0; - - // Predict the anomaly timestamp given the current status. - virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker, - const int64_t currentTimestamp) const = 0; - // Dump internal states for debugging - virtual void dumpStates(FILE* out, bool verbose) const = 0; - - virtual int64_t getCurrentStateKeyDuration() const = 0; - - virtual int64_t getCurrentStateKeyFullBucketDuration() const = 0; - - // Replace old value with new value for the given state atom. - virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0; - -protected: - int64_t getCurrentBucketEndTimeNs() const { - return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs; - } - - // Starts the anomaly alarm. - void startAnomalyAlarm(const int64_t eventTime) { - for (auto& anomalyTracker : mAnomalyTrackers) { - if (anomalyTracker != nullptr) { - const int64_t alarmTimestampNs = - predictAnomalyTimestampNs(*anomalyTracker, eventTime); - if (alarmTimestampNs > 0) { - anomalyTracker->startAlarm(mEventKey, alarmTimestampNs); - } - } - } - } - - // Stops the anomaly alarm. If it should have already fired, declare the anomaly now. - void stopAnomalyAlarm(const int64_t timestamp) { - for (auto& anomalyTracker : mAnomalyTrackers) { - if (anomalyTracker != nullptr) { - anomalyTracker->stopAlarm(mEventKey, timestamp); - } - } - } - - void addPastBucketToAnomalyTrackers(const MetricDimensionKey eventKey, - const int64_t& bucketValue, const int64_t& bucketNum) { - for (auto& anomalyTracker : mAnomalyTrackers) { - if (anomalyTracker != nullptr) { - anomalyTracker->addPastBucket(eventKey, bucketValue, bucketNum); - } - } - } - - void detectAndDeclareAnomaly(const int64_t& timestamp, const int64_t& currBucketNum, - const int64_t& currentBucketValue) { - for (auto& anomalyTracker : mAnomalyTrackers) { - if (anomalyTracker != nullptr) { - anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId, - mEventKey, currentBucketValue); - } - } - } - - // Convenience to compute the current bucket's end time, which is always aligned with the - // start time of the metric. - int64_t getCurrentBucketEndTimeNs() { - return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs; - } - - void setEventKey(const MetricDimensionKey& eventKey) { - mEventKey = eventKey; - } - - // A reference to the DurationMetricProducer's config key. - const ConfigKey& mConfigKey; - - const int64_t mTrackerId; - - MetricDimensionKey mEventKey; - - sp mWizard; - - const int mConditionTrackerIndex; - - const int64_t mBucketSizeNs; - - const bool mNested; - - int64_t mCurrentBucketStartTimeNs; - - int64_t mDuration; // current recorded duration result (for partial bucket) - - // Recorded duration results for each state key in the current partial bucket. - std::unordered_map mStateKeyDurationMap; - - int64_t mCurrentBucketNum; - - const int64_t mStartTimeNs; - - const bool mConditionSliced; - - bool mHasLinksToAllConditionDimensionsInTracker; - - std::vector> mAnomalyTrackers; - - FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // DURATION_TRACKER_H diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp deleted file mode 100644 index ee4e1672411f..000000000000 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false - -#include "Log.h" -#include "MaxDurationTracker.h" -#include "guardrail/StatsdStats.h" - -namespace android { -namespace os { -namespace statsd { - -MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id, - const MetricDimensionKey& eventKey, - sp wizard, int conditionIndex, bool nesting, - int64_t currentBucketStartNs, int64_t currentBucketNum, - int64_t startTimeNs, int64_t bucketSizeNs, - bool conditionSliced, bool fullLink, - const vector>& anomalyTrackers) - : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink, - anomalyTrackers) { -} - -bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { - // ===========GuardRail============== - if (mInfos.find(newKey) != mInfos.end()) { - // if the key existed, we are good! - return false; - } - // 1. Report the tuple count if the tuple count > soft limit - if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mInfos.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("MaxDurTracker %lld dropping data for dimension key %s", - (long long)mTrackerId, newKey.toString().c_str()); - return true; - } - } - return false; -} - -void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition, - const int64_t eventTime, const ConditionKey& conditionKey) { - // this will construct a new DurationInfo if this key didn't exist. - if (hitGuardRail(key)) { - return; - } - - DurationInfo& duration = mInfos[key]; - if (mConditionSliced) { - duration.conditionKeys = conditionKey; - } - VLOG("MaxDuration: key %s start condition %d", key.toString().c_str(), condition); - - switch (duration.state) { - case kStarted: - duration.startCount++; - break; - case kPaused: - duration.startCount++; - break; - case kStopped: - if (!condition) { - // event started, but we need to wait for the condition to become true. - duration.state = DurationState::kPaused; - } else { - duration.state = DurationState::kStarted; - duration.lastStartTime = eventTime; - startAnomalyAlarm(eventTime); - } - duration.startCount = 1; - break; - } -} - -void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const int64_t eventTime, - bool forceStop) { - VLOG("MaxDuration: key %s stop", key.toString().c_str()); - if (mInfos.find(key) == mInfos.end()) { - // we didn't see a start event before. do nothing. - return; - } - DurationInfo& duration = mInfos[key]; - - switch (duration.state) { - case DurationState::kStopped: - // already stopped, do nothing. - break; - case DurationState::kStarted: { - duration.startCount--; - if (forceStop || !mNested || duration.startCount <= 0) { - stopAnomalyAlarm(eventTime); - duration.state = DurationState::kStopped; - int64_t durationTime = eventTime - duration.lastStartTime; - VLOG("Max, key %s, Stop %lld %lld %lld", key.toString().c_str(), - (long long)duration.lastStartTime, (long long)eventTime, - (long long)durationTime); - duration.lastDuration += durationTime; - if (anyStarted()) { - // In case any other dimensions are still started, we need to keep the alarm - // set. - startAnomalyAlarm(eventTime); - } - VLOG(" record duration: %lld ", (long long)duration.lastDuration); - } - break; - } - case DurationState::kPaused: { - duration.startCount--; - if (forceStop || !mNested || duration.startCount <= 0) { - duration.state = DurationState::kStopped; - } - break; - } - } - - if (duration.lastDuration > mDuration) { - mDuration = duration.lastDuration; - VLOG("Max: new max duration: %lld", (long long)mDuration); - } - // Once an atom duration ends, we erase it. Next time, if we see another atom event with the - // same name, they are still considered as different atom durations. - if (duration.state == DurationState::kStopped) { - mInfos.erase(key); - } -} - -bool MaxDurationTracker::anyStarted() { - for (auto& pair : mInfos) { - if (pair.second.state == kStarted) { - return true; - } - } - return false; -} - -void MaxDurationTracker::noteStopAll(const int64_t eventTime) { - std::set keys; - for (const auto& pair : mInfos) { - keys.insert(pair.first); - } - for (auto& key : keys) { - noteStop(key, eventTime, true); - } -} - -bool MaxDurationTracker::flushCurrentBucket( - const int64_t& eventTimeNs, - std::unordered_map>* output) { - VLOG("MaxDurationTracker flushing....."); - - // adjust the bucket start time - int numBucketsForward = 0; - int64_t fullBucketEnd = getCurrentBucketEndTimeNs(); - int64_t currentBucketEndTimeNs; - if (eventTimeNs >= fullBucketEnd) { - numBucketsForward = 1 + (eventTimeNs - fullBucketEnd) / mBucketSizeNs; - currentBucketEndTimeNs = fullBucketEnd; - } else { - // This must be a partial bucket. - currentBucketEndTimeNs = eventTimeNs; - } - - bool hasPendingEvent = - false; // has either a kStarted or kPaused event across bucket boundaries - // meaning we need to carry them over to the new bucket. - for (auto it = mInfos.begin(); it != mInfos.end();) { - if (it->second.state == DurationState::kStopped) { - // No need to keep buckets for events that were stopped before. - it = mInfos.erase(it); - } else { - ++it; - hasPendingEvent = true; - } - } - - // mDuration is updated in noteStop to the maximum duration that ended in the current bucket. - if (mDuration != 0) { - DurationBucket info; - info.mBucketStartNs = mCurrentBucketStartTimeNs; - info.mBucketEndNs = currentBucketEndTimeNs; - info.mDuration = mDuration; - (*output)[mEventKey].push_back(info); - VLOG(" final duration for last bucket: %lld", (long long)mDuration); - } - - if (numBucketsForward > 0) { - mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs; - mCurrentBucketNum += numBucketsForward; - } else { // We must be forming a partial bucket. - mCurrentBucketStartTimeNs = eventTimeNs; - } - - mDuration = 0; - // If this tracker has no pending events, tell owner to remove. - return !hasPendingEvent; -} - -bool MaxDurationTracker::flushIfNeeded( - int64_t eventTimeNs, unordered_map>* output) { - if (eventTimeNs < getCurrentBucketEndTimeNs()) { - return false; - } - return flushCurrentBucket(eventTimeNs, output); -} - -void MaxDurationTracker::onSlicedConditionMayChange(bool overallCondition, - const int64_t timestamp) { - // Now for each of the on-going event, check if the condition has changed for them. - for (auto& pair : mInfos) { - if (pair.second.state == kStopped) { - continue; - } - ConditionState conditionState = mWizard->query( - mConditionTrackerIndex, pair.second.conditionKeys, - !mHasLinksToAllConditionDimensionsInTracker); - bool conditionMet = (conditionState == ConditionState::kTrue); - - VLOG("key: %s, condition: %d", pair.first.toString().c_str(), conditionMet); - noteConditionChanged(pair.first, conditionMet, timestamp); - } -} - -void MaxDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId, - const FieldValue& newState) { - ALOGE("MaxDurationTracker does not handle sliced state changes."); -} - -void MaxDurationTracker::onConditionChanged(bool condition, const int64_t timestamp) { - for (auto& pair : mInfos) { - noteConditionChanged(pair.first, condition, timestamp); - } -} - -void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet, - const int64_t timestamp) { - auto it = mInfos.find(key); - if (it == mInfos.end()) { - return; - } - - switch (it->second.state) { - case kStarted: - // If condition becomes false, kStarted -> kPaused. Record the current duration and - // stop anomaly alarm. - if (!conditionMet) { - stopAnomalyAlarm(timestamp); - it->second.state = DurationState::kPaused; - it->second.lastDuration += (timestamp - it->second.lastStartTime); - if (anyStarted()) { - // In case any other dimensions are still started, we need to set the alarm. - startAnomalyAlarm(timestamp); - } - VLOG("MaxDurationTracker Key: %s Started->Paused ", key.toString().c_str()); - } - break; - case kStopped: - // Nothing to do if it's stopped. - break; - case kPaused: - // If condition becomes true, kPaused -> kStarted. and the start time is the condition - // change time. - if (conditionMet) { - it->second.state = DurationState::kStarted; - it->second.lastStartTime = timestamp; - startAnomalyAlarm(timestamp); - VLOG("MaxDurationTracker Key: %s Paused->Started", key.toString().c_str()); - } - break; - } - // Note that we don't update mDuration here since it's only updated during noteStop. -} - -int64_t MaxDurationTracker::predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker, - const int64_t currentTimestamp) const { - // The allowed time we can continue in the current state is the - // (anomaly threshold) - max(elapsed time of the started mInfos). - int64_t maxElapsed = 0; - for (auto it = mInfos.begin(); it != mInfos.end(); ++it) { - if (it->second.state == DurationState::kStarted) { - int64_t duration = - it->second.lastDuration + (currentTimestamp - it->second.lastStartTime); - if (duration > maxElapsed) { - maxElapsed = duration; - } - } - } - int64_t anomalyTimeNs = currentTimestamp + anomalyTracker.getAnomalyThreshold() - maxElapsed; - int64_t refractoryEndNs = anomalyTracker.getRefractoryPeriodEndsSec(mEventKey) * NS_PER_SEC; - return std::max(anomalyTimeNs, refractoryEndNs); -} - -void MaxDurationTracker::dumpStates(FILE* out, bool verbose) const { - fprintf(out, "\t\t sub-durations %lu\n", (unsigned long)mInfos.size()); - fprintf(out, "\t\t current duration %lld\n", (long long)mDuration); -} - -int64_t MaxDurationTracker::getCurrentStateKeyDuration() const { - ALOGE("MaxDurationTracker does not handle sliced state changes."); - return -1; -} - -int64_t MaxDurationTracker::getCurrentStateKeyFullBucketDuration() const { - ALOGE("MaxDurationTracker does not handle sliced state changes."); - return -1; -} - -void MaxDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) { - ALOGE("MaxDurationTracker does not handle sliced state changes."); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h deleted file mode 100644 index 2891c6e1138a..000000000000 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MAX_DURATION_TRACKER_H -#define MAX_DURATION_TRACKER_H - -#include "DurationTracker.h" - -namespace android { -namespace os { -namespace statsd { - -// Tracks a pool of atom durations, and output the max duration for each bucket. -// To get max duration, we need to keep track of each individual durations, and compare them when -// they stop or bucket expires. -class MaxDurationTracker : public DurationTracker { -public: - MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, - sp wizard, int conditionIndex, - bool nesting, - int64_t currentBucketStartNs, int64_t currentBucketNum, - int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced, - bool fullLink, - const std::vector>& anomalyTrackers); - - MaxDurationTracker(const MaxDurationTracker& tracker) = default; - - void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime, - const ConditionKey& conditionKey) override; - void noteStop(const HashableDimensionKey& key, const int64_t eventTime, - const bool stopAll) override; - void noteStopAll(const int64_t eventTime) override; - - bool flushIfNeeded( - int64_t timestampNs, - std::unordered_map>* output) override; - bool flushCurrentBucket( - const int64_t& eventTimeNs, - std::unordered_map>*) override; - - void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override; - void onConditionChanged(bool condition, const int64_t timestamp) override; - - void onStateChanged(const int64_t timestamp, const int32_t atomId, - const FieldValue& newState) override; - - int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker, - const int64_t currentTimestamp) const override; - void dumpStates(FILE* out, bool verbose) const override; - - int64_t getCurrentStateKeyDuration() const override; - - int64_t getCurrentStateKeyFullBucketDuration() const override; - - void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState); - -private: - // Returns true if at least one of the mInfos is started. - bool anyStarted(); - - std::unordered_map mInfos; - - void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet, - const int64_t timestamp); - - // return true if we should not allow newKey to be tracked because we are above the threshold - bool hitGuardRail(const HashableDimensionKey& newKey); - - FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration); - FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary); - FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition); - FRIEND_TEST(MaxDurationTrackerTest, TestStopAll); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // MAX_DURATION_TRACKER_H diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp deleted file mode 100644 index 0d49bbc269a3..000000000000 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false -#include "Log.h" -#include "OringDurationTracker.h" -#include "guardrail/StatsdStats.h" - -namespace android { -namespace os { -namespace statsd { - -using std::pair; - -OringDurationTracker::OringDurationTracker( - const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, - sp wizard, int conditionIndex, bool nesting, int64_t currentBucketStartNs, - int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced, - bool fullLink, const vector>& anomalyTrackers) - : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink, - anomalyTrackers), - mStarted(), - mPaused() { - mLastStartTime = 0; -} - -bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { - // ===========GuardRail============== - // 1. Report the tuple count if the tuple count > soft limit - if (mConditionKeyMap.find(newKey) != mConditionKeyMap.end()) { - return false; - } - if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mConditionKeyMap.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("OringDurTracker %lld dropping data for dimension key %s", - (long long)mTrackerId, newKey.toString().c_str()); - return true; - } - } - return false; -} - -void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition, - const int64_t eventTime, const ConditionKey& conditionKey) { - if (hitGuardRail(key)) { - return; - } - if (condition) { - if (mStarted.size() == 0) { - mLastStartTime = eventTime; - VLOG("record first start...."); - startAnomalyAlarm(eventTime); - } - mStarted[key]++; - } else { - mPaused[key]++; - } - - if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) { - mConditionKeyMap[key] = conditionKey; - } - VLOG("Oring: %s start, condition %d", key.toString().c_str(), condition); -} - -void OringDurationTracker::noteStop(const HashableDimensionKey& key, const int64_t timestamp, - const bool stopAll) { - VLOG("Oring: %s stop", key.toString().c_str()); - auto it = mStarted.find(key); - if (it != mStarted.end()) { - (it->second)--; - if (stopAll || !mNested || it->second <= 0) { - mStarted.erase(it); - mConditionKeyMap.erase(key); - } - if (mStarted.empty()) { - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += - (timestamp - mLastStartTime); - detectAndDeclareAnomaly( - timestamp, mCurrentBucketNum, - getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration()); - VLOG("record duration %lld, total duration %lld for state key %s", - (long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(), - mEventKey.getStateValuesKey().toString().c_str()); - } - } - - auto pausedIt = mPaused.find(key); - if (pausedIt != mPaused.end()) { - (pausedIt->second)--; - if (stopAll || !mNested || pausedIt->second <= 0) { - mPaused.erase(pausedIt); - mConditionKeyMap.erase(key); - } - } - if (mStarted.empty()) { - stopAnomalyAlarm(timestamp); - } -} - -void OringDurationTracker::noteStopAll(const int64_t timestamp) { - if (!mStarted.empty()) { - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += - (timestamp - mLastStartTime); - VLOG("Oring Stop all: record duration %lld, total duration %lld for state key %s", - (long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(), - mEventKey.getStateValuesKey().toString().c_str()); - detectAndDeclareAnomaly( - timestamp, mCurrentBucketNum, - getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration()); - } - - stopAnomalyAlarm(timestamp); - mStarted.clear(); - mPaused.clear(); - mConditionKeyMap.clear(); -} - -bool OringDurationTracker::flushCurrentBucket( - const int64_t& eventTimeNs, - std::unordered_map>* output) { - VLOG("OringDurationTracker Flushing............."); - - // Note that we have to mimic the bucket time changes we do in the - // MetricProducer#notifyAppUpgrade. - - int numBucketsForward = 0; - int64_t fullBucketEnd = getCurrentBucketEndTimeNs(); - int64_t currentBucketEndTimeNs; - - if (eventTimeNs >= fullBucketEnd) { - numBucketsForward = 1 + (eventTimeNs - fullBucketEnd) / mBucketSizeNs; - currentBucketEndTimeNs = fullBucketEnd; - } else { - // This must be a partial bucket. - currentBucketEndTimeNs = eventTimeNs; - } - - // Process the current bucket. - if (mStarted.size() > 0) { - // Calculate the duration for the current state key. - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += - (currentBucketEndTimeNs - mLastStartTime); - } - // Store DurationBucket info for each whatKey, stateKey pair. - // Note: The whatKey stored in mEventKey is constant for each DurationTracker, while the - // stateKey stored in mEventKey is only the current stateKey. mStateKeyDurationMap is used to - // store durations for each stateKey, so we need to flush the bucket by creating a - // DurationBucket for each stateKey. - for (auto& durationIt : mStateKeyDurationMap) { - if (durationIt.second.mDuration > 0) { - DurationBucket current_info; - current_info.mBucketStartNs = mCurrentBucketStartTimeNs; - current_info.mBucketEndNs = currentBucketEndTimeNs; - current_info.mDuration = durationIt.second.mDuration; - (*output)[MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first)] - .push_back(current_info); - - durationIt.second.mDurationFullBucket += durationIt.second.mDuration; - VLOG(" duration: %lld", (long long)current_info.mDuration); - } - - if (eventTimeNs > fullBucketEnd) { - // End of full bucket, can send to anomaly tracker now. - addPastBucketToAnomalyTrackers( - MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first), - getCurrentStateKeyFullBucketDuration(), mCurrentBucketNum); - durationIt.second.mDurationFullBucket = 0; - } - durationIt.second.mDuration = 0; - } - - if (mStarted.size() > 0) { - for (int i = 1; i < numBucketsForward; i++) { - DurationBucket info; - info.mBucketStartNs = fullBucketEnd + mBucketSizeNs * (i - 1); - info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs; - info.mDuration = mBucketSizeNs; - // Full duration buckets are attributed to the current stateKey. - (*output)[mEventKey].push_back(info); - // Safe to send these buckets to anomaly tracker since they must be full buckets. - // If it's a partial bucket, numBucketsForward would be 0. - addPastBucketToAnomalyTrackers(mEventKey, info.mDuration, mCurrentBucketNum + i); - VLOG(" add filling bucket with duration %lld", (long long)info.mDuration); - } - } else { - if (numBucketsForward >= 2) { - addPastBucketToAnomalyTrackers(mEventKey, 0, mCurrentBucketNum + numBucketsForward - 1); - } - } - - if (numBucketsForward > 0) { - mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs; - mCurrentBucketNum += numBucketsForward; - } else { // We must be forming a partial bucket. - mCurrentBucketStartTimeNs = eventTimeNs; - } - mLastStartTime = mCurrentBucketStartTimeNs; - - // if all stopped, then tell owner it's safe to remove this tracker. - return mStarted.empty() && mPaused.empty(); -} - -bool OringDurationTracker::flushIfNeeded( - int64_t eventTimeNs, unordered_map>* output) { - if (eventTimeNs < getCurrentBucketEndTimeNs()) { - return false; - } - return flushCurrentBucket(eventTimeNs, output); -} - -void OringDurationTracker::onSlicedConditionMayChange(bool overallCondition, - const int64_t timestamp) { - vector> startedToPaused; - vector> pausedToStarted; - if (!mStarted.empty()) { - for (auto it = mStarted.begin(); it != mStarted.end();) { - const auto& key = it->first; - const auto& condIt = mConditionKeyMap.find(key); - if (condIt == mConditionKeyMap.end()) { - VLOG("Key %s dont have condition key", key.toString().c_str()); - ++it; - continue; - } - ConditionState conditionState = - mWizard->query(mConditionTrackerIndex, condIt->second, - !mHasLinksToAllConditionDimensionsInTracker); - if (conditionState != ConditionState::kTrue) { - startedToPaused.push_back(*it); - it = mStarted.erase(it); - VLOG("Key %s started -> paused", key.toString().c_str()); - } else { - ++it; - } - } - - if (mStarted.empty()) { - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += - (timestamp - mLastStartTime); - VLOG("record duration %lld, total duration %lld for state key %s", - (long long)(timestamp - mLastStartTime), (long long)getCurrentStateKeyDuration(), - mEventKey.getStateValuesKey().toString().c_str()); - detectAndDeclareAnomaly( - timestamp, mCurrentBucketNum, - getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration()); - } - } - - if (!mPaused.empty()) { - for (auto it = mPaused.begin(); it != mPaused.end();) { - const auto& key = it->first; - if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) { - VLOG("Key %s dont have condition key", key.toString().c_str()); - ++it; - continue; - } - ConditionState conditionState = - mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key], - !mHasLinksToAllConditionDimensionsInTracker); - if (conditionState == ConditionState::kTrue) { - pausedToStarted.push_back(*it); - it = mPaused.erase(it); - VLOG("Key %s paused -> started", key.toString().c_str()); - } else { - ++it; - } - } - - if (mStarted.empty() && pausedToStarted.size() > 0) { - mLastStartTime = timestamp; - } - } - - if (mStarted.empty() && !pausedToStarted.empty()) { - startAnomalyAlarm(timestamp); - } - mStarted.insert(pausedToStarted.begin(), pausedToStarted.end()); - mPaused.insert(startedToPaused.begin(), startedToPaused.end()); - - if (mStarted.empty()) { - stopAnomalyAlarm(timestamp); - } -} - -void OringDurationTracker::onConditionChanged(bool condition, const int64_t timestamp) { - if (condition) { - if (!mPaused.empty()) { - VLOG("Condition true, all started"); - if (mStarted.empty()) { - mLastStartTime = timestamp; - } - if (mStarted.empty() && !mPaused.empty()) { - startAnomalyAlarm(timestamp); - } - mStarted.insert(mPaused.begin(), mPaused.end()); - mPaused.clear(); - } - } else { - if (!mStarted.empty()) { - VLOG("Condition false, all paused"); - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += - (timestamp - mLastStartTime); - mPaused.insert(mStarted.begin(), mStarted.end()); - mStarted.clear(); - detectAndDeclareAnomaly( - timestamp, mCurrentBucketNum, - getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration()); - } - } - if (mStarted.empty()) { - stopAnomalyAlarm(timestamp); - } -} - -void OringDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId, - const FieldValue& newState) { - // Nothing needs to be done on a state change if we have not seen a start - // event, the metric is currently not active, or condition is false. - // For these cases, no keys are being tracked in mStarted, so update - // the current state key and return. - if (mStarted.empty()) { - updateCurrentStateKey(atomId, newState); - return; - } - // Add the current duration length to the previous state key and then update - // the last start time and current state key. - mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += (timestamp - mLastStartTime); - mLastStartTime = timestamp; - updateCurrentStateKey(atomId, newState); -} - -int64_t OringDurationTracker::predictAnomalyTimestampNs( - const DurationAnomalyTracker& anomalyTracker, const int64_t eventTimestampNs) const { - - // The anomaly threshold. - const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold(); - - // The timestamp of the current bucket end. - const int64_t currentBucketEndNs = getCurrentBucketEndTimeNs(); - - // The past duration ns for the current bucket of the current stateKey. - int64_t currentStateBucketPastNs = - getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration(); - - // As we move into the future, old buckets get overwritten (so their old data is erased). - // Sum of past durations. Will change as we overwrite old buckets. - int64_t pastNs = currentStateBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey); - - // The refractory period end timestamp for dimension mEventKey. - const int64_t refractoryPeriodEndNs = - anomalyTracker.getRefractoryPeriodEndsSec(mEventKey) * NS_PER_SEC; - - // The anomaly should happen when accumulated wakelock duration is above the threshold and - // not within the refractory period. - int64_t anomalyTimestampNs = - std::max(eventTimestampNs + thresholdNs - pastNs, refractoryPeriodEndNs); - // If the predicted the anomaly timestamp is within the current bucket, return it directly. - if (anomalyTimestampNs <= currentBucketEndNs) { - return std::max(eventTimestampNs, anomalyTimestampNs); - } - - // Remove the old bucket. - if (anomalyTracker.getNumOfPastBuckets() > 0) { - pastNs -= anomalyTracker.getPastBucketValue( - mEventKey, - mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets()); - // Add the remaining of the current bucket to the accumulated wakelock duration. - pastNs += (currentBucketEndNs - eventTimestampNs); - } else { - // The anomaly depends on only one bucket. - pastNs = 0; - } - - // The anomaly will not happen in the current bucket. We need to iterate over the future buckets - // to predict the accumulated wakelock duration and determine the anomaly timestamp accordingly. - for (int futureBucketIdx = 1; futureBucketIdx <= anomalyTracker.getNumOfPastBuckets() + 1; - futureBucketIdx++) { - // The alarm candidate timestamp should meet two requirements: - // 1. the accumulated wakelock duration is above the threshold. - // 2. it is not within the refractory period. - // 3. the alarm timestamp falls in this bucket. Otherwise we need to flush the past buckets, - // find the new alarm candidate timestamp and check these requirements again. - const int64_t bucketEndNs = currentBucketEndNs + futureBucketIdx * mBucketSizeNs; - int64_t anomalyTimestampNs = - std::max(bucketEndNs - mBucketSizeNs + thresholdNs - pastNs, refractoryPeriodEndNs); - if (anomalyTimestampNs <= bucketEndNs) { - return anomalyTimestampNs; - } - if (anomalyTracker.getNumOfPastBuckets() <= 0) { - continue; - } - - // No valid alarm timestamp is found in this bucket. The clock moves to the end of the - // bucket. Update the pastNs. - pastNs += mBucketSizeNs; - // 1. If the oldest past bucket is still in the past bucket window, we could fetch the past - // bucket and erase it from pastNs. - // 2. If the oldest past bucket is the current bucket, we should compute the - // wakelock duration in the current bucket and erase it from pastNs. - // 3. Otherwise all othe past buckets are ancient. - if (futureBucketIdx < anomalyTracker.getNumOfPastBuckets()) { - pastNs -= anomalyTracker.getPastBucketValue( - mEventKey, - mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futureBucketIdx); - } else if (futureBucketIdx == anomalyTracker.getNumOfPastBuckets()) { - pastNs -= (currentStateBucketPastNs + (currentBucketEndNs - eventTimestampNs)); - } - } - - return std::max(eventTimestampNs + thresholdNs, refractoryPeriodEndNs); -} - -void OringDurationTracker::dumpStates(FILE* out, bool verbose) const { - fprintf(out, "\t\t started count %lu\n", (unsigned long)mStarted.size()); - fprintf(out, "\t\t paused count %lu\n", (unsigned long)mPaused.size()); - fprintf(out, "\t\t current duration %lld\n", (long long)getCurrentStateKeyDuration()); -} - -int64_t OringDurationTracker::getCurrentStateKeyDuration() const { - auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey()); - if (it == mStateKeyDurationMap.end()) { - return 0; - } else { - return it->second.mDuration; - } -} - -int64_t OringDurationTracker::getCurrentStateKeyFullBucketDuration() const { - auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey()); - if (it == mStateKeyDurationMap.end()) { - return 0; - } else { - return it->second.mDurationFullBucket; - } -} - -void OringDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) { - HashableDimensionKey* stateValuesKey = mEventKey.getMutableStateValuesKey(); - for (size_t i = 0; i < stateValuesKey->getValues().size(); i++) { - if (stateValuesKey->getValues()[i].mField.getTag() == atomId) { - stateValuesKey->mutableValue(i)->mValue = newState.mValue; - } - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h deleted file mode 100644 index bd8017a7decd..000000000000 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ORING_DURATION_TRACKER_H -#define ORING_DURATION_TRACKER_H - -#include "DurationTracker.h" - -namespace android { -namespace os { -namespace statsd { - -// Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted. -class OringDurationTracker : public DurationTracker { -public: - OringDurationTracker(const ConfigKey& key, const int64_t& id, - const MetricDimensionKey& eventKey, sp wizard, - int conditionIndex, bool nesting, int64_t currentBucketStartNs, - int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, - bool conditionSliced, bool fullLink, - const std::vector>& anomalyTrackers); - - OringDurationTracker(const OringDurationTracker& tracker) = default; - - void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime, - const ConditionKey& conditionKey) override; - void noteStop(const HashableDimensionKey& key, const int64_t eventTime, - const bool stopAll) override; - void noteStopAll(const int64_t eventTime) override; - - void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override; - void onConditionChanged(bool condition, const int64_t timestamp) override; - - void onStateChanged(const int64_t timestamp, const int32_t atomId, - const FieldValue& newState) override; - - bool flushCurrentBucket( - const int64_t& eventTimeNs, - std::unordered_map>* output) override; - bool flushIfNeeded( - int64_t timestampNs, - std::unordered_map>* output) override; - - int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker, - const int64_t currentTimestamp) const override; - void dumpStates(FILE* out, bool verbose) const override; - - int64_t getCurrentStateKeyDuration() const override; - - int64_t getCurrentStateKeyFullBucketDuration() const override; - - void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState); - -private: - // We don't need to keep track of individual durations. The information that's needed is: - // 1) which keys are started. We record the first start time. - // 2) which keys are paused (started but condition was false) - // 3) whenever a key stops, we remove it from the started set. And if the set becomes empty, - // it means everything has stopped, we then record the end time. - std::unordered_map mStarted; - std::unordered_map mPaused; - int64_t mLastStartTime; - std::unordered_map mConditionKeyMap; - - // return true if we should not allow newKey to be tracked because we are above the threshold - bool hitGuardRail(const HashableDimensionKey& newKey); - - FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap); - FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary); - FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange); - FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm); - FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm); -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // ORING_DURATION_TRACKER_H diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp deleted file mode 100644 index 8917c36bb608..000000000000 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ /dev/null @@ -1,983 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "metrics_manager_util.h" - -#include - -#include "FieldValue.h" -#include "MetricProducer.h" -#include "condition/CombinationConditionTracker.h" -#include "condition/SimpleConditionTracker.h" -#include "external/StatsPullerManager.h" -#include "matchers/CombinationLogMatchingTracker.h" -#include "matchers/EventMatcherWizard.h" -#include "matchers/SimpleLogMatchingTracker.h" -#include "metrics/CountMetricProducer.h" -#include "metrics/DurationMetricProducer.h" -#include "metrics/EventMetricProducer.h" -#include "metrics/GaugeMetricProducer.h" -#include "metrics/ValueMetricProducer.h" -#include "state/StateManager.h" -#include "stats_util.h" - -using std::set; -using std::unordered_map; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -namespace { - -bool hasLeafNode(const FieldMatcher& matcher) { - if (!matcher.has_field()) { - return false; - } - for (int i = 0; i < matcher.child_size(); ++i) { - if (hasLeafNode(matcher.child(i))) { - return true; - } - } - return true; -} - -} // namespace - -bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex, - const bool usedForDimension, - const vector>& allAtomMatchers, - const unordered_map& logTrackerMap, - unordered_map>& trackerToMetricMap, - int& logTrackerIndex) { - auto logTrackerIt = logTrackerMap.find(what); - if (logTrackerIt == logTrackerMap.end()) { - ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)what); - return false; - } - if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) { - ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, " - "the \"what\" can only about one atom type.", - (long long)what); - return false; - } - logTrackerIndex = logTrackerIt->second; - auto& metric_list = trackerToMetricMap[logTrackerIndex]; - metric_list.push_back(metricIndex); - return true; -} - -bool handlePullMetricTriggerWithLogTrackers( - const int64_t trigger, const int metricIndex, - const vector>& allAtomMatchers, - const unordered_map& logTrackerMap, - unordered_map>& trackerToMetricMap, int& logTrackerIndex) { - auto logTrackerIt = logTrackerMap.find(trigger); - if (logTrackerIt == logTrackerMap.end()) { - ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)trigger); - return false; - } - if (allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) { - ALOGE("AtomMatcher \"%lld\" has more than one tag ids." - "Trigger can only be one atom type.", - (long long)trigger); - return false; - } - logTrackerIndex = logTrackerIt->second; - auto& metric_list = trackerToMetricMap[logTrackerIndex]; - metric_list.push_back(metricIndex); - return true; -} - -bool handleMetricWithConditions( - const int64_t condition, const int metricIndex, - const unordered_map& conditionTrackerMap, - const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>& - links, - vector>& allConditionTrackers, int& conditionIndex, - unordered_map>& conditionToMetricMap) { - auto condition_it = conditionTrackerMap.find(condition); - if (condition_it == conditionTrackerMap.end()) { - ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition); - return false; - } - - for (const auto& link : links) { - auto it = conditionTrackerMap.find(link.condition()); - if (it == conditionTrackerMap.end()) { - ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition()); - return false; - } - allConditionTrackers[condition_it->second]->setSliced(true); - allConditionTrackers[it->second]->setSliced(true); - } - conditionIndex = condition_it->second; - - // will create new vector if not exist before. - auto& metricList = conditionToMetricMap[condition_it->second]; - metricList.push_back(metricIndex); - return true; -} - -// Initializes state data structures for a metric. -// input: -// [config]: the input config -// [stateIds]: the slice_by_state ids for this metric -// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids -// [allStateGroupMaps]: this map contains the mapping from state ids and state -// values to state group ids for all states -// output: -// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states -// [stateGroupMap]: this map should contain the mapping from states ids and state -// values to state group ids for all states that this metric -// is interested in -bool handleMetricWithStates( - const StatsdConfig& config, const ::google::protobuf::RepeatedField& stateIds, - const unordered_map& stateAtomIdMap, - const unordered_map>& allStateGroupMaps, - vector& slicedStateAtoms, - unordered_map>& stateGroupMap) { - for (const auto& stateId : stateIds) { - auto it = stateAtomIdMap.find(stateId); - if (it == stateAtomIdMap.end()) { - ALOGW("cannot find State %" PRId64 " in the config", stateId); - return false; - } - int atomId = it->second; - slicedStateAtoms.push_back(atomId); - - auto stateIt = allStateGroupMaps.find(stateId); - if (stateIt != allStateGroupMaps.end()) { - stateGroupMap[atomId] = stateIt->second; - } - } - return true; -} - -bool handleMetricWithStateLink(const FieldMatcher& stateMatcher, - const vector& dimensionsInWhat) { - vector stateMatchers; - translateFieldMatcher(stateMatcher, &stateMatchers); - - return subsetDimensions(stateMatchers, dimensionsInWhat); -} - -// Validates a metricActivation and populates state. -// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer -// to provide the producer with state about its activators and deactivators. -// Returns false if there are errors. -bool handleMetricActivation( - const StatsdConfig& config, - const int64_t metricId, - const int metricIndex, - const unordered_map& metricToActivationMap, - const unordered_map& logTrackerMap, - unordered_map>& activationAtomTrackerToMetricMap, - unordered_map>& deactivationAtomTrackerToMetricMap, - vector& metricsWithActivation, - unordered_map>& eventActivationMap, - unordered_map>>& eventDeactivationMap) { - // Check if metric has an associated activation - auto itr = metricToActivationMap.find(metricId); - if (itr == metricToActivationMap.end()) return true; - - int activationIndex = itr->second; - const MetricActivation& metricActivation = config.metric_activation(activationIndex); - - for (int i = 0; i < metricActivation.event_activation_size(); i++) { - const EventActivation& activation = metricActivation.event_activation(i); - - auto itr = logTrackerMap.find(activation.atom_matcher_id()); - if (itr == logTrackerMap.end()) { - ALOGE("Atom matcher not found for event activation."); - return false; - } - - ActivationType activationType = (activation.has_activation_type()) ? - activation.activation_type() : metricActivation.activation_type(); - std::shared_ptr activationWrapper = std::make_shared( - activationType, activation.ttl_seconds() * NS_PER_SEC); - - int atomMatcherIndex = itr->second; - activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex); - eventActivationMap.emplace(atomMatcherIndex, activationWrapper); - - if (activation.has_deactivation_atom_matcher_id()) { - itr = logTrackerMap.find(activation.deactivation_atom_matcher_id()); - if (itr == logTrackerMap.end()) { - ALOGE("Atom matcher not found for event deactivation."); - return false; - } - int deactivationAtomMatcherIndex = itr->second; - deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex); - eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper); - } - } - - metricsWithActivation.push_back(metricIndex); - return true; -} - -bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap, - unordered_map& logTrackerMap, - vector>& allAtomMatchers, set& allTagIds) { - vector matcherConfigs; - const int atomMatcherCount = config.atom_matcher_size(); - matcherConfigs.reserve(atomMatcherCount); - allAtomMatchers.reserve(atomMatcherCount); - - for (int i = 0; i < atomMatcherCount; i++) { - const AtomMatcher& logMatcher = config.atom_matcher(i); - - int index = allAtomMatchers.size(); - switch (logMatcher.contents_case()) { - case AtomMatcher::ContentsCase::kSimpleAtomMatcher: - allAtomMatchers.push_back(new SimpleLogMatchingTracker( - logMatcher.id(), index, logMatcher.simple_atom_matcher(), uidMap)); - break; - case AtomMatcher::ContentsCase::kCombination: - allAtomMatchers.push_back( - new CombinationLogMatchingTracker(logMatcher.id(), index)); - break; - default: - ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id()); - return false; - // continue; - } - if (logTrackerMap.find(logMatcher.id()) != logTrackerMap.end()) { - ALOGE("Duplicate AtomMatcher found!"); - return false; - } - logTrackerMap[logMatcher.id()] = index; - matcherConfigs.push_back(logMatcher); - } - - vector stackTracker2(allAtomMatchers.size(), false); - for (auto& matcher : allAtomMatchers) { - if (!matcher->init(matcherConfigs, allAtomMatchers, logTrackerMap, stackTracker2)) { - return false; - } - // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only. - const set& tagIds = matcher->getAtomIds(); - allTagIds.insert(tagIds.begin(), tagIds.end()); - } - return true; -} - -bool initConditions(const ConfigKey& key, const StatsdConfig& config, - const unordered_map& logTrackerMap, - unordered_map& conditionTrackerMap, - vector>& allConditionTrackers, - unordered_map>& trackerToConditionMap, - vector& initialConditionCache) { - vector conditionConfigs; - const int conditionTrackerCount = config.predicate_size(); - conditionConfigs.reserve(conditionTrackerCount); - allConditionTrackers.reserve(conditionTrackerCount); - initialConditionCache.reserve(conditionTrackerCount); - std::fill(initialConditionCache.begin(), initialConditionCache.end(), ConditionState::kUnknown); - - for (int i = 0; i < conditionTrackerCount; i++) { - const Predicate& condition = config.predicate(i); - int index = allConditionTrackers.size(); - switch (condition.contents_case()) { - case Predicate::ContentsCase::kSimplePredicate: { - allConditionTrackers.push_back(new SimpleConditionTracker( - key, condition.id(), index, condition.simple_predicate(), logTrackerMap)); - break; - } - case Predicate::ContentsCase::kCombination: { - allConditionTrackers.push_back( - new CombinationConditionTracker(condition.id(), index)); - break; - } - default: - ALOGE("Predicate \"%lld\" malformed", (long long)condition.id()); - return false; - } - if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) { - ALOGE("Duplicate Predicate found!"); - return false; - } - conditionTrackerMap[condition.id()] = index; - conditionConfigs.push_back(condition); - } - - vector stackTracker(allConditionTrackers.size(), false); - for (size_t i = 0; i < allConditionTrackers.size(); i++) { - auto& conditionTracker = allConditionTrackers[i]; - if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap, - stackTracker, initialConditionCache)) { - return false; - } - for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) { - auto& conditionList = trackerToConditionMap[trackerIndex]; - conditionList.push_back(i); - } - } - return true; -} - -bool initStates(const StatsdConfig& config, unordered_map& stateAtomIdMap, - unordered_map>& allStateGroupMaps) { - for (int i = 0; i < config.state_size(); i++) { - const State& state = config.state(i); - const int64_t stateId = state.id(); - stateAtomIdMap[stateId] = state.atom_id(); - - const StateMap& stateMap = state.map(); - for (auto group : stateMap.group()) { - for (auto value : group.value()) { - allStateGroupMaps[stateId][value] = group.group_id(); - } - } - } - - return true; -} - -bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, - const int64_t currentTimeNs, const sp& pullerManager, - const unordered_map& logTrackerMap, - const unordered_map& conditionTrackerMap, - const vector>& allAtomMatchers, - const unordered_map& stateAtomIdMap, - const unordered_map>& allStateGroupMaps, - vector>& allConditionTrackers, - const vector& initialConditionCache, - vector>& allMetricProducers, - unordered_map>& conditionToMetricMap, - unordered_map>& trackerToMetricMap, - unordered_map& metricMap, std::set& noReportMetricIds, - unordered_map>& activationAtomTrackerToMetricMap, - unordered_map>& deactivationAtomTrackerToMetricMap, - vector& metricsWithActivation) { - sp wizard = new ConditionWizard(allConditionTrackers); - sp matcherWizard = new EventMatcherWizard(allAtomMatchers); - const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + - config.event_metric_size() + config.gauge_metric_size() + - config.value_metric_size(); - allMetricProducers.reserve(allMetricsCount); - - // Construct map from metric id to metric activation index. The map will be used to determine - // the metric activation corresponding to a metric. - unordered_map metricToActivationMap; - for (int i = 0; i < config.metric_activation_size(); i++) { - const MetricActivation& metricActivation = config.metric_activation(i); - int64_t metricId = metricActivation.metric_id(); - if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) { - ALOGE("Metric %lld has multiple MetricActivations", (long long) metricId); - return false; - } - metricToActivationMap.insert({metricId, i}); - } - - // Build MetricProducers for each metric defined in config. - // build CountMetricProducer - for (int i = 0; i < config.count_metric_size(); i++) { - const CountMetric& metric = config.count_metric(i); - if (!metric.has_what()) { - ALOGW("cannot find \"what\" in CountMetric \"%lld\"", (long long)metric.id()); - return false; - } - - int metricIndex = allMetricProducers.size(); - metricMap.insert({metric.id(), metricIndex}); - int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchers, logTrackerMap, trackerToMetricMap, - trackerIndex)) { - return false; - } - - int conditionIndex = -1; - if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { - return false; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return false; - } - } - - std::vector slicedStateAtoms; - unordered_map> stateGroupMap; - if (metric.slice_by_state_size() > 0) { - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { - return false; - } - } else { - if (metric.state_link_size() > 0) { - ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state"); - return false; - } - } - - unordered_map> eventActivationMap; - unordered_map>> eventDeactivationMap; - bool success = handleMetricActivation(config, metric.id(), metricIndex, - metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, - eventDeactivationMap); - if (!success) return false; - - sp countProducer = - new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, - timeBaseTimeNs, currentTimeNs, eventActivationMap, - eventDeactivationMap, slicedStateAtoms, stateGroupMap); - allMetricProducers.push_back(countProducer); - } - - // build DurationMetricProducer - for (int i = 0; i < config.duration_metric_size(); i++) { - int metricIndex = allMetricProducers.size(); - const DurationMetric& metric = config.duration_metric(i); - metricMap.insert({metric.id(), metricIndex}); - - auto what_it = conditionTrackerMap.find(metric.what()); - if (what_it == conditionTrackerMap.end()) { - ALOGE("DurationMetric's \"what\" is invalid"); - return false; - } - - const Predicate& durationWhat = config.predicate(what_it->second); - - if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { - ALOGE("DurationMetric's \"what\" must be a simple condition"); - return false; - } - - const auto& simplePredicate = durationWhat.simple_predicate(); - - bool nesting = simplePredicate.count_nesting(); - - int trackerIndices[3] = {-1, -1, -1}; - if (!simplePredicate.has_start() || - !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex, - metric.has_dimensions_in_what(), allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndices[0])) { - ALOGE("Duration metrics must specify a valid the start event matcher"); - return false; - } - - if (simplePredicate.has_stop() && - !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex, - metric.has_dimensions_in_what(), allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndices[1])) { - return false; - } - - if (simplePredicate.has_stop_all() && - !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex, - metric.has_dimensions_in_what(), allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndices[2])) { - return false; - } - - FieldMatcher internalDimensions = simplePredicate.dimensions(); - - int conditionIndex = -1; - - if (metric.has_condition()) { - bool good = handleMetricWithConditions( - metric.condition(), metricIndex, conditionTrackerMap, metric.links(), - allConditionTrackers, conditionIndex, conditionToMetricMap); - if (!good) { - return false; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return false; - } - } - - std::vector slicedStateAtoms; - unordered_map> stateGroupMap; - if (metric.slice_by_state_size() > 0) { - if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) { - ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state"); - return false; - } - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { - return false; - } - } else { - if (metric.state_link_size() > 0) { - ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state"); - return false; - } - } - - // Check that all metric state links are a subset of dimensions_in_what fields. - std::vector dimensionsInWhat; - translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); - for (const auto& stateLink : metric.state_link()) { - if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { - return false; - } - } - - unordered_map> eventActivationMap; - unordered_map>> eventDeactivationMap; - bool success = handleMetricActivation(config, metric.id(), metricIndex, - metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, - eventDeactivationMap); - if (!success) return false; - - sp durationMetric = new DurationMetricProducer( - key, metric, conditionIndex, initialConditionCache, trackerIndices[0], - trackerIndices[1], trackerIndices[2], nesting, wizard, internalDimensions, - timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap, - slicedStateAtoms, stateGroupMap); - - allMetricProducers.push_back(durationMetric); - } - - // build EventMetricProducer - for (int i = 0; i < config.event_metric_size(); i++) { - int metricIndex = allMetricProducers.size(); - const EventMetric& metric = config.event_metric(i); - metricMap.insert({metric.id(), metricIndex}); - if (!metric.has_id() || !metric.has_what()) { - ALOGW("cannot find the metric name or what in config"); - return false; - } - int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, false, allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndex)) { - return false; - } - - int conditionIndex = -1; - if (metric.has_condition()) { - bool good = handleMetricWithConditions( - metric.condition(), metricIndex, conditionTrackerMap, metric.links(), - allConditionTrackers, conditionIndex, conditionToMetricMap); - if (!good) { - return false; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return false; - } - } - - unordered_map> eventActivationMap; - unordered_map>> eventDeactivationMap; - bool success = handleMetricActivation(config, metric.id(), metricIndex, - metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, - eventDeactivationMap); - if (!success) return false; - - sp eventMetric = - new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, - timeBaseTimeNs, eventActivationMap, eventDeactivationMap); - - allMetricProducers.push_back(eventMetric); - } - - // build ValueMetricProducer - for (int i = 0; i < config.value_metric_size(); i++) { - const ValueMetric& metric = config.value_metric(i); - if (!metric.has_what()) { - ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id()); - return false; - } - if (!metric.has_value_field()) { - ALOGW("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id()); - return false; - } - std::vector fieldMatchers; - translateFieldMatcher(metric.value_field(), &fieldMatchers); - if (fieldMatchers.size() < 1) { - ALOGW("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id()); - return false; - } - - int metricIndex = allMetricProducers.size(); - metricMap.insert({metric.id(), metricIndex}); - int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchers, logTrackerMap, trackerToMetricMap, - trackerIndex)) { - return false; - } - - sp atomMatcher = allAtomMatchers.at(trackerIndex); - // If it is pulled atom, it should be simple matcher with one tagId. - if (atomMatcher->getAtomIds().size() != 1) { - return false; - } - int atomTagId = *(atomMatcher->getAtomIds().begin()); - int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1; - - int conditionIndex = -1; - if (metric.has_condition()) { - bool good = handleMetricWithConditions( - metric.condition(), metricIndex, conditionTrackerMap, metric.links(), - allConditionTrackers, conditionIndex, conditionToMetricMap); - if (!good) { - return false; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return false; - } - } - - std::vector slicedStateAtoms; - unordered_map> stateGroupMap; - if (metric.slice_by_state_size() > 0) { - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { - return false; - } - } else { - if (metric.state_link_size() > 0) { - ALOGW("ValueMetric has a MetricStateLink but doesn't have a sliced state"); - return false; - } - } - - // Check that all metric state links are a subset of dimensions_in_what fields. - std::vector dimensionsInWhat; - translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); - for (const auto& stateLink : metric.state_link()) { - if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { - return false; - } - } - - unordered_map> eventActivationMap; - unordered_map>> eventDeactivationMap; - bool success = handleMetricActivation( - config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap); - if (!success) return false; - - sp valueProducer = new ValueMetricProducer( - key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex, - matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, pullerManager, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap); - allMetricProducers.push_back(valueProducer); - } - - // Gauge metrics. - for (int i = 0; i < config.gauge_metric_size(); i++) { - const GaugeMetric& metric = config.gauge_metric(i); - if (!metric.has_what()) { - ALOGW("cannot find \"what\" in GaugeMetric \"%lld\"", (long long)metric.id()); - return false; - } - - if ((!metric.gauge_fields_filter().has_include_all() || - (metric.gauge_fields_filter().include_all() == false)) && - !hasLeafNode(metric.gauge_fields_filter().fields())) { - ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id()); - return false; - } - if ((metric.gauge_fields_filter().has_include_all() && - metric.gauge_fields_filter().include_all() == true) && - hasLeafNode(metric.gauge_fields_filter().fields())) { - ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id()); - return false; - } - - int metricIndex = allMetricProducers.size(); - metricMap.insert({metric.id(), metricIndex}); - int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchers, logTrackerMap, trackerToMetricMap, - trackerIndex)) { - return false; - } - - sp atomMatcher = allAtomMatchers.at(trackerIndex); - // For GaugeMetric atom, it should be simple matcher with one tagId. - if (atomMatcher->getAtomIds().size() != 1) { - return false; - } - int atomTagId = *(atomMatcher->getAtomIds().begin()); - int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1; - - int triggerTrackerIndex; - int triggerAtomId = -1; - if (metric.has_trigger_event()) { - if (pullTagId == -1) { - ALOGW("Pull atom not specified for trigger"); - return false; - } - // event_trigger should be used with FIRST_N_SAMPLES - if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) { - return false; - } - if (!handlePullMetricTriggerWithLogTrackers(metric.trigger_event(), metricIndex, - allAtomMatchers, logTrackerMap, - trackerToMetricMap, triggerTrackerIndex)) { - return false; - } - sp triggerAtomMatcher = allAtomMatchers.at(triggerTrackerIndex); - triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin()); - } - - if (!metric.has_trigger_event() && pullTagId != -1 && - metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) { - ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger"); - return false; - } - - int conditionIndex = -1; - if (metric.has_condition()) { - bool good = handleMetricWithConditions( - metric.condition(), metricIndex, conditionTrackerMap, metric.links(), - allConditionTrackers, conditionIndex, conditionToMetricMap); - if (!good) { - return false; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return false; - } - } - - unordered_map> eventActivationMap; - unordered_map>> eventDeactivationMap; - bool success = handleMetricActivation(config, metric.id(), metricIndex, - metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, - eventDeactivationMap); - if (!success) return false; - - sp gaugeProducer = new GaugeMetricProducer( - key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex, - matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs, - pullerManager, eventActivationMap, eventDeactivationMap); - allMetricProducers.push_back(gaugeProducer); - } - for (int i = 0; i < config.no_report_metric_size(); ++i) { - const auto no_report_metric = config.no_report_metric(i); - if (metricMap.find(no_report_metric) == metricMap.end()) { - ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric); - return false; - } - noReportMetricIds.insert(no_report_metric); - } - - const set whitelistedAtomIds(config.whitelisted_atom_ids().begin(), - config.whitelisted_atom_ids().end()); - for (const auto& it : allMetricProducers) { - // Register metrics to StateTrackers - for (int atomId : it->getSlicedStateAtoms()) { - // Register listener for non-whitelisted atoms only. Using whitelisted atom as a sliced - // state atom is not allowed. - if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) { - StateManager::getInstance().registerListener(atomId, it); - } else { - return false; - } - } - } - return true; -} - -bool initAlerts(const StatsdConfig& config, - const unordered_map& metricProducerMap, - unordered_map& alertTrackerMap, - const sp& anomalyAlarmMonitor, - vector>& allMetricProducers, - vector>& allAnomalyTrackers) { - for (int i = 0; i < config.alert_size(); i++) { - const Alert& alert = config.alert(i); - const auto& itr = metricProducerMap.find(alert.metric_id()); - if (itr == metricProducerMap.end()) { - ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(), - (long long)alert.metric_id()); - return false; - } - if (!alert.has_trigger_if_sum_gt()) { - ALOGW("invalid alert: missing threshold"); - return false; - } - if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) { - ALOGW("invalid alert: threshold=%f num_buckets= %d", - alert.trigger_if_sum_gt(), alert.num_buckets()); - return false; - } - const int metricIndex = itr->second; - sp metric = allMetricProducers[metricIndex]; - sp anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor); - if (anomalyTracker == nullptr) { - // The ALOGW for this invalid alert was already displayed in addAnomalyTracker(). - return false; - } - alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); - allAnomalyTrackers.push_back(anomalyTracker); - } - for (int i = 0; i < config.subscription_size(); ++i) { - const Subscription& subscription = config.subscription(i); - if (subscription.rule_type() != Subscription::ALERT) { - continue; - } - if (subscription.subscriber_information_case() == - Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) { - ALOGW("subscription \"%lld\" has no subscriber info.\"", - (long long)subscription.id()); - return false; - } - const auto& itr = alertTrackerMap.find(subscription.rule_id()); - if (itr == alertTrackerMap.end()) { - ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"", - (long long)subscription.id(), (long long)subscription.rule_id()); - return false; - } - const int anomalyTrackerIndex = itr->second; - allAnomalyTrackers[anomalyTrackerIndex]->addSubscription(subscription); - } - return true; -} - -bool initAlarms(const StatsdConfig& config, const ConfigKey& key, - const sp& periodicAlarmMonitor, - const int64_t timeBaseNs, const int64_t currentTimeNs, - vector>& allAlarmTrackers) { - unordered_map alarmTrackerMap; - int64_t startMillis = timeBaseNs / 1000 / 1000; - int64_t currentTimeMillis = currentTimeNs / 1000 /1000; - for (int i = 0; i < config.alarm_size(); i++) { - const Alarm& alarm = config.alarm(i); - if (alarm.offset_millis() <= 0) { - ALOGW("Alarm offset_millis should be larger than 0."); - return false; - } - if (alarm.period_millis() <= 0) { - ALOGW("Alarm period_millis should be larger than 0."); - return false; - } - alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size())); - allAlarmTrackers.push_back( - new AlarmTracker(startMillis, currentTimeMillis, - alarm, key, periodicAlarmMonitor)); - } - for (int i = 0; i < config.subscription_size(); ++i) { - const Subscription& subscription = config.subscription(i); - if (subscription.rule_type() != Subscription::ALARM) { - continue; - } - if (subscription.subscriber_information_case() == - Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) { - ALOGW("subscription \"%lld\" has no subscriber info.\"", - (long long)subscription.id()); - return false; - } - const auto& itr = alarmTrackerMap.find(subscription.rule_id()); - if (itr == alarmTrackerMap.end()) { - ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"", - (long long)subscription.id(), (long long)subscription.rule_id()); - return false; - } - const int trackerIndex = itr->second; - allAlarmTrackers[trackerIndex]->addSubscription(subscription); - } - return true; -} - -bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap, - const sp& pullerManager, - const sp& anomalyAlarmMonitor, - const sp& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, set& allTagIds, - vector>& allAtomMatchers, - vector>& allConditionTrackers, - vector>& allMetricProducers, - vector>& allAnomalyTrackers, - vector>& allPeriodicAlarmTrackers, - unordered_map>& conditionToMetricMap, - unordered_map>& trackerToMetricMap, - unordered_map>& trackerToConditionMap, - unordered_map>& activationAtomTrackerToMetricMap, - unordered_map>& deactivationAtomTrackerToMetricMap, - unordered_map& alertTrackerMap, - vector& metricsWithActivation, - std::set& noReportMetricIds) { - unordered_map logTrackerMap; - unordered_map conditionTrackerMap; - vector initialConditionCache; - unordered_map metricProducerMap; - unordered_map stateAtomIdMap; - unordered_map> allStateGroupMaps; - - if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) { - ALOGE("initLogMatchingTrackers failed"); - return false; - } - VLOG("initLogMatchingTrackers succeed..."); - - if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers, - trackerToConditionMap, initialConditionCache)) { - ALOGE("initConditionTrackers failed"); - return false; - } - - if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) { - ALOGE("initStates failed"); - return false; - } - if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap, - conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps, - allConditionTrackers, initialConditionCache, allMetricProducers, - conditionToMetricMap, trackerToMetricMap, metricProducerMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation)) { - ALOGE("initMetricProducers failed"); - return false; - } - if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor, - allMetricProducers, allAnomalyTrackers)) { - ALOGE("initAlerts failed"); - return false; - } - if (!initAlarms(config, key, periodicAlarmMonitor, - timeBaseNs, currentTimeNs, allPeriodicAlarmTrackers)) { - ALOGE("initAlarms failed"); - return false; - } - - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h deleted file mode 100644 index 96b5c26ff789..000000000000 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "../anomaly/AlarmTracker.h" -#include "../condition/ConditionTracker.h" -#include "../external/StatsPullerManager.h" -#include "../matchers/LogMatchingTracker.h" -#include "../metrics/MetricProducer.h" - -namespace android { -namespace os { -namespace statsd { - -// Helper functions for MetricsManager to initialize from StatsdConfig. -// *Note*: only initStatsdConfig() should be called from outside. -// All other functions are intermediate -// steps, created to make unit tests easier. And most of the parameters in these -// functions are temporary objects in the initialization phase. - -// Initialize the LogMatchingTrackers. -// input: -// [key]: the config key that this config belongs to -// [config]: the input StatsdConfig -// output: -// [logTrackerMap]: this map should contain matcher name to index mapping -// [allAtomMatchers]: should store the sp to all the LogMatchingTracker -// [allTagIds]: contains the set of all interesting tag ids to this config. -bool initLogTrackers(const StatsdConfig& config, - const UidMap& uidMap, - std::unordered_map& logTrackerMap, - std::vector>& allAtomMatchers, - std::set& allTagIds); - -// Initialize ConditionTrackers -// input: -// [key]: the config key that this config belongs to -// [config]: the input config -// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step. -// output: -// [conditionTrackerMap]: this map should contain condition name to index mapping -// [allConditionTrackers]: stores the sp to all the ConditionTrackers -// [trackerToConditionMap]: contain the mapping from index of -// log tracker to condition trackers that use the log tracker -// [initialConditionCache]: stores the initial conditions for each ConditionTracker -bool initConditions(const ConfigKey& key, const StatsdConfig& config, - const std::unordered_map& logTrackerMap, - std::unordered_map& conditionTrackerMap, - std::vector>& allConditionTrackers, - std::unordered_map>& trackerToConditionMap, - std::unordered_map>& eventConditionLinks, - std::vector& initialConditionCache); - -// Initialize State maps using State protos in the config. These maps will -// eventually be passed to MetricProducers to initialize their state info. -// input: -// [config]: the input config -// output: -// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids -// [allStateGroupMaps]: this map should contain the mapping from states ids and state -// values to state group ids for all states -bool initStates(const StatsdConfig& config, unordered_map& stateAtomIdMap, - unordered_map>& allStateGroupMaps); - -// Initialize MetricProducers. -// input: -// [key]: the config key that this config belongs to -// [config]: the input config -// [timeBaseSec]: start time base for all metrics -// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step. -// [conditionTrackerMap]: condition name to index mapping -// [stateAtomIdMap]: contains the mapping from state ids to atom ids -// [allStateGroupMaps]: contains the mapping from atom ids and state values to -// state group ids for all states -// output: -// [allMetricProducers]: contains the list of sp to the MetricProducers created. -// [conditionToMetricMap]: contains the mapping from condition tracker index to -// the list of MetricProducer index -// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index. -bool initMetrics( - const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, - const int64_t currentTimeNs, UidMap& uidMap, const sp& pullerManager, - const std::unordered_map& logTrackerMap, - const std::unordered_map& conditionTrackerMap, - const std::unordered_map>& eventConditionLinks, - const vector>& allAtomMatchers, - const unordered_map& stateAtomIdMap, - const unordered_map>& allStateGroupMaps, - vector>& allConditionTrackers, - const std::vector& initialConditionCache, - std::vector>& allMetricProducers, - std::unordered_map>& conditionToMetricMap, - std::unordered_map>& trackerToMetricMap, - std::set& noReportMetricIds, - std::unordered_map>& activationAtomTrackerToMetricMap, - std::unordered_map>& deactivationAtomTrackerToMetricMap, - std::vector& metricsWithActivation); - -// Initialize MetricsManager from StatsdConfig. -// Parameters are the members of MetricsManager. See MetricsManager for declaration. -bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap, - const sp& pullerManager, - const sp& anomalyAlarmMonitor, - const sp& periodicAlarmMonitor, const int64_t timeBaseNs, - const int64_t currentTimeNs, std::set& allTagIds, - std::vector>& allAtomMatchers, - std::vector>& allConditionTrackers, - std::vector>& allMetricProducers, - vector>& allAnomalyTrackers, - vector>& allPeriodicAlarmTrackers, - std::unordered_map>& conditionToMetricMap, - std::unordered_map>& trackerToMetricMap, - std::unordered_map>& trackerToConditionMap, - unordered_map>& activationAtomTrackerToMetricMap, - unordered_map>& deactivationAtomTrackerToMetricMap, - std::unordered_map& alertTrackerMap, - vector& metricsWithActivation, - std::set& noReportMetricIds); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h deleted file mode 100644 index 6c50a8c41770..000000000000 --- a/cmds/statsd/src/packages/PackageInfoListener.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef STATSD_PACKAGE_INFO_LISTENER_H -#define STATSD_PACKAGE_INFO_LISTENER_H - -#include - -namespace android { -namespace os { -namespace statsd { - -class PackageInfoListener : public virtual android::RefBase { -public: - // Uid map will notify this listener that the app with apk name and uid has been upgraded to - // the specified version. - virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const std::string& apk, - const int uid, const int64_t version) = 0; - - // Notify interested listeners that the given apk and uid combination no longer exits. - virtual void notifyAppRemoved(const int64_t& eventTimeNs, const std::string& apk, - const int uid) = 0; - - // Notify the listener that the UidMap snapshot is available. - virtual void onUidMapReceived(const int64_t& eventTimeNs) = 0; -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // STATSD_PACKAGE_INFO_LISTENER_H diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp deleted file mode 100644 index acf40c88a00d..000000000000 --- a/cmds/statsd/src/packages/UidMap.cpp +++ /dev/null @@ -1,563 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, versionCode 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "hash.h" -#include "stats_log_util.h" -#include "guardrail/StatsdStats.h" -#include "packages/UidMap.h" - -#include - -using namespace android; - -using android::base::StringPrintf; -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_UINT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::ProtoOutputStream; - -namespace android { -namespace os { -namespace statsd { - -const int FIELD_ID_SNAPSHOT_PACKAGE_NAME = 1; -const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION = 2; -const int FIELD_ID_SNAPSHOT_PACKAGE_UID = 3; -const int FIELD_ID_SNAPSHOT_PACKAGE_DELETED = 4; -const int FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH = 5; -const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING = 6; -const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH = 7; -const int FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER = 8; -const int FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH = 9; -const int FIELD_ID_SNAPSHOT_TIMESTAMP = 1; -const int FIELD_ID_SNAPSHOT_PACKAGE_INFO = 2; -const int FIELD_ID_SNAPSHOTS = 1; -const int FIELD_ID_CHANGES = 2; -const int FIELD_ID_CHANGE_DELETION = 1; -const int FIELD_ID_CHANGE_TIMESTAMP = 2; -const int FIELD_ID_CHANGE_PACKAGE = 3; -const int FIELD_ID_CHANGE_UID = 4; -const int FIELD_ID_CHANGE_NEW_VERSION = 5; -const int FIELD_ID_CHANGE_PREV_VERSION = 6; -const int FIELD_ID_CHANGE_PACKAGE_HASH = 7; -const int FIELD_ID_CHANGE_NEW_VERSION_STRING = 8; -const int FIELD_ID_CHANGE_PREV_VERSION_STRING = 9; -const int FIELD_ID_CHANGE_NEW_VERSION_STRING_HASH = 10; -const int FIELD_ID_CHANGE_PREV_VERSION_STRING_HASH = 11; - -UidMap::UidMap() : mBytesUsed(0) {} - -UidMap::~UidMap() {} - -sp UidMap::getInstance() { - static sp sInstance = new UidMap(); - return sInstance; -} - -bool UidMap::hasApp(int uid, const string& packageName) const { - lock_guard lock(mMutex); - - auto it = mMap.find(std::make_pair(uid, packageName)); - return it != mMap.end() && !it->second.deleted; -} - -string UidMap::normalizeAppName(const string& appName) const { - string normalizedName = appName; - std::transform(normalizedName.begin(), normalizedName.end(), normalizedName.begin(), ::tolower); - return normalizedName; -} - -std::set UidMap::getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const { - lock_guard lock(mMutex); - return getAppNamesFromUidLocked(uid,returnNormalized); -} - -std::set UidMap::getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const { - std::set names; - for (const auto& kv : mMap) { - if (kv.first.first == uid && !kv.second.deleted) { - names.insert(returnNormalized ? normalizeAppName(kv.first.second) : kv.first.second); - } - } - return names; -} - -int64_t UidMap::getAppVersion(int uid, const string& packageName) const { - lock_guard lock(mMutex); - - auto it = mMap.find(std::make_pair(uid, packageName)); - if (it == mMap.end() || it->second.deleted) { - return 0; - } - return it->second.versionCode; -} - -void UidMap::updateMap(const int64_t& timestamp, const vector& uid, - const vector& versionCode, const vector& versionString, - const vector& packageName, const vector& installer) { - wp broadcast = NULL; - { - lock_guard lock(mMutex); // Exclusively lock for updates. - - std::unordered_map, AppData, PairHash> deletedApps; - - // Copy all the deleted apps. - for (const auto& kv : mMap) { - if (kv.second.deleted) { - deletedApps[kv.first] = kv.second; - } - } - - mMap.clear(); - for (size_t j = 0; j < uid.size(); j++) { - string package = string(String8(packageName[j]).string()); - mMap[std::make_pair(uid[j], package)] = - AppData(versionCode[j], string(String8(versionString[j]).string()), - string(String8(installer[j]).string())); - } - - for (const auto& kv : deletedApps) { - auto mMapIt = mMap.find(kv.first); - if (mMapIt != mMap.end()) { - // Insert this deleted app back into the current map. - mMap[kv.first] = kv.second; - } - } - - ensureBytesUsedBelowLimit(); - StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); - broadcast = mSubscriber; - } - // To avoid invoking callback while holding the internal lock. we get a copy of the listener - // and invoke the callback. It's still possible that after we copy the listener, it removes - // itself before we call it. It's then the listener's job to handle it (expect the callback to - // be called after listener is removed, and the listener should properly ignore it). - auto strongPtr = broadcast.promote(); - if (strongPtr != NULL) { - strongPtr->onUidMapReceived(timestamp); - } -} - -void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid, - const int64_t& versionCode, const String16& versionString, - const String16& installer) { - wp broadcast = NULL; - string appName = string(String8(app_16).string()); - { - lock_guard lock(mMutex); - int32_t prevVersion = 0; - string prevVersionString = ""; - string newVersionString = string(String8(versionString).string()); - bool found = false; - auto it = mMap.find(std::make_pair(uid, appName)); - if (it != mMap.end()) { - found = true; - prevVersion = it->second.versionCode; - prevVersionString = it->second.versionString; - it->second.versionCode = versionCode; - it->second.versionString = newVersionString; - it->second.installer = string(String8(installer).string()); - it->second.deleted = false; - } - if (!found) { - // Otherwise, we need to add an app at this uid. - mMap[std::make_pair(uid, appName)] = - AppData(versionCode, newVersionString, string(String8(installer).string())); - } else { - // Only notify the listeners if this is an app upgrade. If this app is being installed - // for the first time, then we don't notify the listeners. - // It's also OK to split again if we're forming a partial bucket after re-installing an - // app after deletion. - broadcast = mSubscriber; - } - mChanges.emplace_back(false, timestamp, appName, uid, versionCode, newVersionString, - prevVersion, prevVersionString); - mBytesUsed += kBytesChangeRecord; - ensureBytesUsedBelowLimit(); - StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); - StatsdStats::getInstance().setUidMapChanges(mChanges.size()); - } - - auto strongPtr = broadcast.promote(); - if (strongPtr != NULL) { - strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode); - } -} - -void UidMap::ensureBytesUsedBelowLimit() { - size_t limit; - if (maxBytesOverride <= 0) { - limit = StatsdStats::kMaxBytesUsedUidMap; - } else { - limit = maxBytesOverride; - } - while (mBytesUsed > limit) { - ALOGI("Bytes used %zu is above limit %zu, need to delete something", mBytesUsed, limit); - if (mChanges.size() > 0) { - mBytesUsed -= kBytesChangeRecord; - mChanges.pop_front(); - StatsdStats::getInstance().noteUidMapDropped(1); - } - } -} - -void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) { - wp broadcast = NULL; - string app = string(String8(app_16).string()); - { - lock_guard lock(mMutex); - - int64_t prevVersion = 0; - string prevVersionString = ""; - auto key = std::make_pair(uid, app); - auto it = mMap.find(key); - if (it != mMap.end() && !it->second.deleted) { - prevVersion = it->second.versionCode; - prevVersionString = it->second.versionString; - it->second.deleted = true; - mDeletedApps.push_back(key); - } - if (mDeletedApps.size() > StatsdStats::kMaxDeletedAppsInUidMap) { - // Delete the oldest one. - auto oldest = mDeletedApps.front(); - mDeletedApps.pop_front(); - mMap.erase(oldest); - StatsdStats::getInstance().noteUidMapAppDeletionDropped(); - } - mChanges.emplace_back(true, timestamp, app, uid, 0, "", prevVersion, prevVersionString); - mBytesUsed += kBytesChangeRecord; - ensureBytesUsedBelowLimit(); - StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); - StatsdStats::getInstance().setUidMapChanges(mChanges.size()); - broadcast = mSubscriber; - } - - auto strongPtr = broadcast.promote(); - if (strongPtr != NULL) { - strongPtr->notifyAppRemoved(timestamp, app, uid); - } -} - -void UidMap::setListener(wp listener) { - lock_guard lock(mMutex); // Lock for updates - mSubscriber = listener; -} - -void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) { - lock_guard lock(mIsolatedMutex); - - mIsolatedUidMap[isolatedUid] = parentUid; -} - -void UidMap::removeIsolatedUid(int isolatedUid) { - lock_guard lock(mIsolatedMutex); - - auto it = mIsolatedUidMap.find(isolatedUid); - if (it != mIsolatedUidMap.end()) { - mIsolatedUidMap.erase(it); - } -} - -int UidMap::getHostUidOrSelf(int uid) const { - lock_guard lock(mIsolatedMutex); - - auto it = mIsolatedUidMap.find(uid); - if (it != mIsolatedUidMap.end()) { - return it->second; - } - return uid; -} - -void UidMap::clearOutput() { - mChanges.clear(); - // Also update the guardrail trackers. - StatsdStats::getInstance().setUidMapChanges(0); - mBytesUsed = 0; - StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); -} - -int64_t UidMap::getMinimumTimestampNs() { - int64_t m = 0; - for (const auto& kv : mLastUpdatePerConfigKey) { - if (m == 0) { - m = kv.second; - } else if (kv.second < m) { - m = kv.second; - } - } - return m; -} - -size_t UidMap::getBytesUsed() const { - return mBytesUsed; -} - -void UidMap::writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, - bool includeInstaller, const std::set& interestingUids, - std::set* str_set, ProtoOutputStream* proto) { - lock_guard lock(mMutex); - - writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, interestingUids, - str_set, proto); -} - -void UidMap::writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings, - bool includeInstaller, - const std::set& interestingUids, - std::set* str_set, ProtoOutputStream* proto) { - proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp); - for (const auto& kv : mMap) { - if (!interestingUids.empty() && - interestingUids.find(kv.first.first) == interestingUids.end()) { - continue; - } - uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SNAPSHOT_PACKAGE_INFO); - if (str_set != nullptr) { - str_set->insert(kv.first.second); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH, - (long long)Hash64(kv.first.second)); - if (includeVersionStrings) { - str_set->insert(kv.second.versionString); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH, - (long long)Hash64(kv.second.versionString)); - } - if (includeInstaller) { - str_set->insert(kv.second.installer); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH, - (long long)Hash64(kv.second.installer)); - } - } else { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second); - if (includeVersionStrings) { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING, - kv.second.versionString); - } - if (includeInstaller) { - proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER, - kv.second.installer); - } - } - - proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, - (long long)kv.second.versionCode); - proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first); - proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted); - proto->end(token); - } -} - -void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set* str_set, - bool includeVersionStrings, bool includeInstaller, - ProtoOutputStream* proto) { - lock_guard lock(mMutex); // Lock for updates - - for (const ChangeRecord& record : mChanges) { - if (record.timestampNs > mLastUpdatePerConfigKey[key]) { - uint64_t changesToken = - proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CHANGES); - proto->write(FIELD_TYPE_BOOL | FIELD_ID_CHANGE_DELETION, (bool)record.deletion); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_TIMESTAMP, - (long long)record.timestampNs); - if (str_set != nullptr) { - str_set->insert(record.package); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PACKAGE_HASH, - (long long)Hash64(record.package)); - if (includeVersionStrings) { - str_set->insert(record.versionString); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_NEW_VERSION_STRING_HASH, - (long long)Hash64(record.versionString)); - str_set->insert(record.prevVersionString); - proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PREV_VERSION_STRING_HASH, - (long long)Hash64(record.prevVersionString)); - } - } else { - proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package); - if (includeVersionStrings) { - proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_NEW_VERSION_STRING, - record.versionString); - proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PREV_VERSION_STRING, - record.prevVersionString); - } - } - - proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_NEW_VERSION, (long long)record.version); - proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_PREV_VERSION, - (long long)record.prevVersion); - proto->end(changesToken); - } - } - - // Write snapshot from current uid map state. - uint64_t snapshotsToken = - proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS); - writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, - std::set() /*empty uid set means including every uid*/, - str_set, proto); - proto->end(snapshotsToken); - - int64_t prevMin = getMinimumTimestampNs(); - mLastUpdatePerConfigKey[key] = timestamp; - int64_t newMin = getMinimumTimestampNs(); - - if (newMin > prevMin) { // Delete anything possible now that the minimum has - // moved forward. - int64_t cutoff_nanos = newMin; - for (auto it_changes = mChanges.begin(); it_changes != mChanges.end();) { - if (it_changes->timestampNs < cutoff_nanos) { - mBytesUsed -= kBytesChangeRecord; - it_changes = mChanges.erase(it_changes); - } else { - ++it_changes; - } - } - } - StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); - StatsdStats::getInstance().setUidMapChanges(mChanges.size()); -} - -void UidMap::printUidMap(int out) const { - lock_guard lock(mMutex); - - for (const auto& kv : mMap) { - if (!kv.second.deleted) { - dprintf(out, "%s, v%" PRId64 ", %s, %s (%i)\n", kv.first.second.c_str(), - kv.second.versionCode, kv.second.versionString.c_str(), - kv.second.installer.c_str(), kv.first.first); - } - } -} - -void UidMap::OnConfigUpdated(const ConfigKey& key) { - mLastUpdatePerConfigKey[key] = -1; -} - -void UidMap::OnConfigRemoved(const ConfigKey& key) { - mLastUpdatePerConfigKey.erase(key); -} - -set UidMap::getAppUid(const string& package) const { - lock_guard lock(mMutex); - - set results; - for (const auto& kv : mMap) { - if (kv.first.second == package && !kv.second.deleted) { - results.insert(kv.first.first); - } - } - return results; -} - -// Note not all the following AIDs are used as uids. Some are used only for gids. -// It's ok to leave them in the map, but we won't ever see them in the log's uid field. -// App's uid starts from 10000, and will not overlap with the following AIDs. -const std::map UidMap::sAidToUidMapping = {{"AID_ROOT", 0}, - {"AID_SYSTEM", 1000}, - {"AID_RADIO", 1001}, - {"AID_BLUETOOTH", 1002}, - {"AID_GRAPHICS", 1003}, - {"AID_INPUT", 1004}, - {"AID_AUDIO", 1005}, - {"AID_CAMERA", 1006}, - {"AID_LOG", 1007}, - {"AID_COMPASS", 1008}, - {"AID_MOUNT", 1009}, - {"AID_WIFI", 1010}, - {"AID_ADB", 1011}, - {"AID_INSTALL", 1012}, - {"AID_MEDIA", 1013}, - {"AID_DHCP", 1014}, - {"AID_SDCARD_RW", 1015}, - {"AID_VPN", 1016}, - {"AID_KEYSTORE", 1017}, - {"AID_USB", 1018}, - {"AID_DRM", 1019}, - {"AID_MDNSR", 1020}, - {"AID_GPS", 1021}, - // {"AID_UNUSED1", 1022}, - {"AID_MEDIA_RW", 1023}, - {"AID_MTP", 1024}, - // {"AID_UNUSED2", 1025}, - {"AID_DRMRPC", 1026}, - {"AID_NFC", 1027}, - {"AID_SDCARD_R", 1028}, - {"AID_CLAT", 1029}, - {"AID_LOOP_RADIO", 1030}, - {"AID_MEDIA_DRM", 1031}, - {"AID_PACKAGE_INFO", 1032}, - {"AID_SDCARD_PICS", 1033}, - {"AID_SDCARD_AV", 1034}, - {"AID_SDCARD_ALL", 1035}, - {"AID_LOGD", 1036}, - {"AID_SHARED_RELRO", 1037}, - {"AID_DBUS", 1038}, - {"AID_TLSDATE", 1039}, - {"AID_MEDIA_EX", 1040}, - {"AID_AUDIOSERVER", 1041}, - {"AID_METRICS_COLL", 1042}, - {"AID_METRICSD", 1043}, - {"AID_WEBSERV", 1044}, - {"AID_DEBUGGERD", 1045}, - {"AID_MEDIA_CODEC", 1046}, - {"AID_CAMERASERVER", 1047}, - {"AID_FIREWALL", 1048}, - {"AID_TRUNKS", 1049}, - {"AID_NVRAM", 1050}, - {"AID_DNS", 1051}, - {"AID_DNS_TETHER", 1052}, - {"AID_WEBVIEW_ZYGOTE", 1053}, - {"AID_VEHICLE_NETWORK", 1054}, - {"AID_MEDIA_AUDIO", 1055}, - {"AID_MEDIA_VIDEO", 1056}, - {"AID_MEDIA_IMAGE", 1057}, - {"AID_TOMBSTONED", 1058}, - {"AID_MEDIA_OBB", 1059}, - {"AID_ESE", 1060}, - {"AID_OTA_UPDATE", 1061}, - {"AID_AUTOMOTIVE_EVS", 1062}, - {"AID_LOWPAN", 1063}, - {"AID_HSM", 1064}, - {"AID_RESERVED_DISK", 1065}, - {"AID_STATSD", 1066}, - {"AID_INCIDENTD", 1067}, - {"AID_SECURE_ELEMENT", 1068}, - {"AID_LMKD", 1069}, - {"AID_LLKD", 1070}, - {"AID_IORAPD", 1071}, - {"AID_GPU_SERVICE", 1072}, - {"AID_NETWORK_STACK", 1073}, - {"AID_GSID", 1074}, - {"AID_FSVERITY_CERT", 1075}, - {"AID_CREDSTORE", 1076}, - {"AID_EXTERNAL_STORAGE", 1077}, - {"AID_EXT_DATA_RW", 1078}, - {"AID_EXT_OBB_RW", 1079}, - {"AID_CONTEXT_HUB", 1080}, - {"AID_SHELL", 2000}, - {"AID_CACHE", 2001}, - {"AID_DIAG", 2002}}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h deleted file mode 100644 index 22250aee402e..000000000000 --- a/cmds/statsd/src/packages/UidMap.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "config/ConfigKey.h" -#include "config/ConfigListener.h" -#include "packages/PackageInfoListener.h" -#include "stats_util.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace android; -using namespace std; - -using android::util::ProtoOutputStream; - -namespace android { -namespace os { -namespace statsd { - -struct AppData { - int64_t versionCode; - string versionString; - string installer; - bool deleted; - - // Empty constructor needed for unordered map. - AppData() { - } - - AppData(const int64_t v, const string& versionString, const string& installer) - : versionCode(v), versionString(versionString), installer(installer), deleted(false){}; -}; - -// When calling appendUidMap, we retrieve all the ChangeRecords since the last -// timestamp we called appendUidMap for this configuration key. -struct ChangeRecord { - const bool deletion; - const int64_t timestampNs; - const string package; - const int32_t uid; - const int64_t version; - const int64_t prevVersion; - const string versionString; - const string prevVersionString; - - ChangeRecord(const bool isDeletion, const int64_t timestampNs, const string& package, - const int32_t uid, const int64_t version, const string versionString, - const int64_t prevVersion, const string prevVersionString) - : deletion(isDeletion), - timestampNs(timestampNs), - package(package), - uid(uid), - version(version), - prevVersion(prevVersion), - versionString(versionString), - prevVersionString(prevVersionString) { - } -}; - -const unsigned int kBytesChangeRecord = sizeof(struct ChangeRecord); - -// UidMap keeps track of what the corresponding app name (APK name) and version code for every uid -// at any given moment. This map must be updated by StatsCompanionService. -class UidMap : public virtual android::RefBase { -public: - UidMap(); - ~UidMap(); - static const std::map sAidToUidMapping; - - static sp getInstance(); - /* - * All three inputs must be the same size, and the jth element in each array refers to the same - * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j]. - */ - void updateMap(const int64_t& timestamp, const vector& uid, - const vector& versionCode, const vector& versionString, - const vector& packageName, const vector& installer); - - void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid, - const int64_t& versionCode, const String16& versionString, - const String16& installer); - void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid); - - // Returns true if the given uid contains the specified app (eg. com.google.android.gms). - bool hasApp(int uid, const string& packageName) const; - - // Returns the app names from uid. - std::set getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const; - - int64_t getAppVersion(int uid, const string& packageName) const; - - // Helper for debugging contents of this uid map. Can be triggered with: - // adb shell cmd stats print-uid-map - void printUidMap(int outFd) const; - - // Command for indicating to the map that StatsLogProcessor should be notified if an app is - // updated. This allows metric producers and managers to distinguish when the same uid or app - // represents a different version of an app. - void setListener(wp listener); - - // Informs uid map that a config is added/updated. Used for keeping mConfigKeys up to date. - void OnConfigUpdated(const ConfigKey& key); - - // Informs uid map that a config is removed. Used for keeping mConfigKeys up to date. - void OnConfigRemoved(const ConfigKey& key); - - void assignIsolatedUid(int isolatedUid, int parentUid); - void removeIsolatedUid(int isolatedUid); - - // Returns the host uid if it exists. Otherwise, returns the same uid that was passed-in. - virtual int getHostUidOrSelf(int uid) const; - - // Gets all snapshots and changes that have occurred since the last output. - // If every config key has received a change or snapshot record, then this - // record is deleted. - void appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set* str_set, - bool includeVersionStrings, bool includeInstaller, - ProtoOutputStream* proto); - - // Forces the output to be cleared. We still generate a snapshot based on the current state. - // This results in extra data uploaded but helps us reconstruct the uid mapping on the server - // in case we lose a previous upload. - void clearOutput(); - - // Get currently cached value of memory used by UID map. - size_t getBytesUsed() const; - - virtual std::set getAppUid(const string& package) const; - - // Write current PackageInfoSnapshot to ProtoOutputStream. - // interestingUids: If not empty, only write the package info for these uids. If empty, write - // package info for all uids. - // str_set: if not null, add new string to the set and write str_hash to proto - // if null, write string to proto. - void writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, bool includeInstaller, - const std::set& interestingUids, std::set* str_set, - ProtoOutputStream* proto); - -private: - std::set getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const; - string normalizeAppName(const string& appName) const; - - void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings, - bool includeInstaller, const std::set& interestingUids, - std::set* str_set, ProtoOutputStream* proto); - - mutable mutex mMutex; - mutable mutex mIsolatedMutex; - - struct PairHash { - size_t operator()(std::pair p) const noexcept { - std::hash hash_fn; - return hash_fn(std::to_string(p.first) + p.second); - } - }; - // Maps uid and package name to application data. - std::unordered_map, AppData, PairHash> mMap; - - // Maps isolated uid to the parent uid. Any metrics for an isolated uid will instead contribute - // to the parent uid. - std::unordered_map mIsolatedUidMap; - - // Record the changes that can be provided with the uploads. - std::list mChanges; - - // Store which uid and apps represent deleted ones. - std::list> mDeletedApps; - - // Notify StatsLogProcessor if there's an upgrade/removal in any app. - wp mSubscriber; - - // Mapping of config keys we're aware of to the epoch time they last received an update. This - // lets us know it's safe to delete events older than the oldest update. The value is nanosec. - // Value of -1 denotes this config key has never received an upload. - std::unordered_map mLastUpdatePerConfigKey; - - // Returns the minimum value from mConfigKeys. - int64_t getMinimumTimestampNs(); - - // If our current used bytes is above the limit, then we clear out the earliest snapshot. If - // there are no more snapshots, then we clear out the earliest delta. We repeat the deletions - // until the memory consumed by mOutput is below the specified limit. - void ensureBytesUsedBelowLimit(); - - // Override used for testing the max memory allowed by uid map. 0 means we use the value - // specified in StatsdStats.h with the rest of the guardrails. - size_t maxBytesOverride = 0; - - // Cache the size of mOutput; - size_t mBytesUsed; - - // Allows unit-test to access private methods. - FRIEND_TEST(UidMapTest, TestClearingOutput); - FRIEND_TEST(UidMapTest, TestRemovedAppRetained); - FRIEND_TEST(UidMapTest, TestRemovedAppOverGuardrail); - FRIEND_TEST(UidMapTest, TestOutputIncludesAtLeastOneSnapshot); - FRIEND_TEST(UidMapTest, TestMemoryComputed); - FRIEND_TEST(UidMapTest, TestMemoryGuardrail); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp deleted file mode 100644 index fd883c29dba0..000000000000 --- a/cmds/statsd/src/shell/ShellSubscriber.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "ShellSubscriber.h" - -#include - -#include "matchers/matcher_util.h" -#include "stats_log_util.h" - -using android::util::ProtoOutputStream; - -namespace android { -namespace os { -namespace statsd { - -const static int FIELD_ID_ATOM = 1; - -void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) { - int myToken = claimToken(); - VLOG("ShellSubscriber: new subscription %d has come in", myToken); - mSubscriptionShouldEnd.notify_one(); - - shared_ptr mySubscriptionInfo = make_shared(in, out); - if (!readConfig(mySubscriptionInfo)) return; - - { - std::unique_lock lock(mMutex); - mSubscriptionInfo = mySubscriptionInfo; - spawnHelperThread(myToken); - waitForSubscriptionToEndLocked(mySubscriptionInfo, myToken, lock, timeoutSec); - - if (mSubscriptionInfo == mySubscriptionInfo) { - mSubscriptionInfo = nullptr; - } - - } -} - -void ShellSubscriber::spawnHelperThread(int myToken) { - std::thread t([this, myToken] { pullAndSendHeartbeats(myToken); }); - t.detach(); -} - -void ShellSubscriber::waitForSubscriptionToEndLocked(shared_ptr myInfo, - int myToken, - std::unique_lock& lock, - int timeoutSec) { - if (timeoutSec > 0) { - mSubscriptionShouldEnd.wait_for(lock, timeoutSec * 1s, [this, myToken, &myInfo] { - return mToken != myToken || !myInfo->mClientAlive; - }); - } else { - mSubscriptionShouldEnd.wait(lock, [this, myToken, &myInfo] { - return mToken != myToken || !myInfo->mClientAlive; - }); - } -} - -// Atomically claim the next token. Token numbers denote subscriber ordering. -int ShellSubscriber::claimToken() { - std::unique_lock lock(mMutex); - int myToken = ++mToken; - return myToken; -} - -// Read and parse single config. There should only one config per input. -bool ShellSubscriber::readConfig(shared_ptr subscriptionInfo) { - // Read the size of the config. - size_t bufferSize; - if (!android::base::ReadFully(subscriptionInfo->mInputFd, &bufferSize, sizeof(bufferSize))) { - return false; - } - - // Read the config. - vector buffer(bufferSize); - if (!android::base::ReadFully(subscriptionInfo->mInputFd, buffer.data(), bufferSize)) { - return false; - } - - // Parse the config. - ShellSubscription config; - if (!config.ParseFromArray(buffer.data(), bufferSize)) { - return false; - } - - // Update SubscriptionInfo with state from config - for (const auto& pushed : config.pushed()) { - subscriptionInfo->mPushedMatchers.push_back(pushed); - } - - for (const auto& pulled : config.pulled()) { - vector packages; - vector uids; - for (const string& pkg : pulled.packages()) { - auto it = UidMap::sAidToUidMapping.find(pkg); - if (it != UidMap::sAidToUidMapping.end()) { - uids.push_back(it->second); - } else { - packages.push_back(pkg); - } - } - - subscriptionInfo->mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis(), packages, - uids); - VLOG("adding matcher for pulled atom %d", pulled.matcher().atom_id()); - } - - return true; -} - -void ShellSubscriber::pullAndSendHeartbeats(int myToken) { - VLOG("ShellSubscriber: helper thread %d starting", myToken); - while (true) { - int64_t sleepTimeMs = INT_MAX; - { - std::lock_guard lock(mMutex); - if (!mSubscriptionInfo || mToken != myToken) { - VLOG("ShellSubscriber: helper thread %d done!", myToken); - return; - } - - int64_t nowMillis = getElapsedRealtimeMillis(); - int64_t nowNanos = getElapsedRealtimeNs(); - for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) { - if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval >= nowMillis) { - continue; - } - - vector uids; - getUidsForPullAtom(&uids, pullInfo); - - vector> data; - mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, nowNanos, &data); - VLOG("Pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id()); - writePulledAtomsLocked(data, pullInfo.mPullerMatcher); - - pullInfo.mPrevPullElapsedRealtimeMs = nowMillis; - } - - // Send a heartbeat, consisting of a data size of 0, if perfd hasn't recently received - // data from statsd. When it receives the data size of 0, perfd will not expect any - // atoms and recheck whether the subscription should end. - if (nowMillis - mLastWriteMs > kMsBetweenHeartbeats) { - attemptWriteToPipeLocked(/*dataSize=*/0); - } - - // Determine how long to sleep before doing more work. - for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) { - int64_t nextPullTime = pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval; - int64_t timeBeforePull = nextPullTime - nowMillis; // guaranteed to be non-negative - if (timeBeforePull < sleepTimeMs) sleepTimeMs = timeBeforePull; - } - int64_t timeBeforeHeartbeat = (mLastWriteMs + kMsBetweenHeartbeats) - nowMillis; - if (timeBeforeHeartbeat < sleepTimeMs) sleepTimeMs = timeBeforeHeartbeat; - } - - VLOG("ShellSubscriber: helper thread %d sleeping for %lld ms", myToken, - (long long)sleepTimeMs); - std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs)); - } -} - -void ShellSubscriber::getUidsForPullAtom(vector* uids, const PullInfo& pullInfo) { - uids->insert(uids->end(), pullInfo.mPullUids.begin(), pullInfo.mPullUids.end()); - // This is slow. Consider storing the uids per app and listening to uidmap updates. - for (const string& pkg : pullInfo.mPullPackages) { - set uidsForPkg = mUidMap->getAppUid(pkg); - uids->insert(uids->end(), uidsForPkg.begin(), uidsForPkg.end()); - } - uids->push_back(DEFAULT_PULL_UID); -} - -void ShellSubscriber::writePulledAtomsLocked(const vector>& data, - const SimpleAtomMatcher& matcher) { - mProto.clear(); - int count = 0; - for (const auto& event : data) { - if (matchesSimple(*mUidMap, matcher, *event)) { - count++; - uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE | - util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM); - event->ToProto(mProto); - mProto.end(atomToken); - } - } - - if (count > 0) attemptWriteToPipeLocked(mProto.size()); -} - -void ShellSubscriber::onLogEvent(const LogEvent& event) { - std::lock_guard lock(mMutex); - if (!mSubscriptionInfo) return; - - mProto.clear(); - for (const auto& matcher : mSubscriptionInfo->mPushedMatchers) { - if (matchesSimple(*mUidMap, matcher, event)) { - uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE | - util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM); - event.ToProto(mProto); - mProto.end(atomToken); - attemptWriteToPipeLocked(mProto.size()); - } - } -} - -// Tries to write the atom encoded in mProto to the pipe. If the write fails -// because the read end of the pipe has closed, signals to other threads that -// the subscription should end. -void ShellSubscriber::attemptWriteToPipeLocked(size_t dataSize) { - // First, write the payload size. - if (!android::base::WriteFully(mSubscriptionInfo->mOutputFd, &dataSize, sizeof(dataSize))) { - mSubscriptionInfo->mClientAlive = false; - mSubscriptionShouldEnd.notify_one(); - return; - } - - // Then, write the payload if this is not just a heartbeat. - if (dataSize > 0 && !mProto.flush(mSubscriptionInfo->mOutputFd)) { - mSubscriptionInfo->mClientAlive = false; - mSubscriptionShouldEnd.notify_one(); - return; - } - - mLastWriteMs = getElapsedRealtimeMillis(); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h deleted file mode 100644 index 4c05fa7f71c2..000000000000 --- a/cmds/statsd/src/shell/ShellSubscriber.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include -#include - -#include "external/StatsPullerManager.h" -#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "logd/LogEvent.h" -#include "packages/UidMap.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * Handles atoms subscription via shell cmd. - * - * A shell subscription lasts *until shell exits*. Unlike config based clients, a shell client - * communicates with statsd via file descriptors. They can subscribe pushed and pulled atoms. - * The atoms are sent back to the client in real time, as opposed to keeping the data in memory. - * Shell clients do not subscribe aggregated metrics, as they are responsible for doing the - * aggregation after receiving the atom events. - * - * Shell clients pass ShellSubscription in the proto binary format. Clients can update the - * subscription by sending a new subscription. The new subscription would replace the old one. - * Input data stream format is: - * - * |size_t|subscription proto|size_t|subscription proto|.... - * - * statsd sends the events back in Atom proto binary format. Each Atom message is preceded - * with sizeof(size_t) bytes indicating the size of the proto message payload. - * - * The stream would be in the following format: - * |size_t|shellData proto|size_t|shellData proto|.... - * - * Only one shell subscriber is allowed at a time because each shell subscriber blocks one thread - * until it exits. - */ -class ShellSubscriber : public virtual RefBase { -public: - ShellSubscriber(sp uidMap, sp pullerMgr) - : mUidMap(uidMap), mPullerMgr(pullerMgr){}; - - void startNewSubscription(int inFd, int outFd, int timeoutSec); - - void onLogEvent(const LogEvent& event); - -private: - struct PullInfo { - PullInfo(const SimpleAtomMatcher& matcher, int64_t interval, - const std::vector& packages, const std::vector& uids) - : mPullerMatcher(matcher), - mInterval(interval), - mPrevPullElapsedRealtimeMs(0), - mPullPackages(packages), - mPullUids(uids) { - } - SimpleAtomMatcher mPullerMatcher; - int64_t mInterval; - int64_t mPrevPullElapsedRealtimeMs; - std::vector mPullPackages; - std::vector mPullUids; - }; - - struct SubscriptionInfo { - SubscriptionInfo(const int& inputFd, const int& outputFd) - : mInputFd(inputFd), mOutputFd(outputFd), mClientAlive(true) { - } - - int mInputFd; - int mOutputFd; - std::vector mPushedMatchers; - std::vector mPulledInfo; - bool mClientAlive; - }; - - int claimToken(); - - bool readConfig(std::shared_ptr subscriptionInfo); - - void spawnHelperThread(int myToken); - - void waitForSubscriptionToEndLocked(std::shared_ptr myInfo, - int myToken, - std::unique_lock& lock, - int timeoutSec); - - // Helper thread that pulls atoms at a regular frequency and sends - // heartbeats to perfd if statsd hasn't recently sent any data. Statsd must - // send heartbeats for perfd to escape a blocking read call and recheck if - // the user has terminated the subscription. - void pullAndSendHeartbeats(int myToken); - - void writePulledAtomsLocked(const vector>& data, - const SimpleAtomMatcher& matcher); - - void getUidsForPullAtom(vector* uids, const PullInfo& pullInfo); - - void attemptWriteToPipeLocked(size_t dataSize); - - sp mUidMap; - - sp mPullerMgr; - - android::util::ProtoOutputStream mProto; - - mutable std::mutex mMutex; - - std::condition_variable mSubscriptionShouldEnd; - - std::shared_ptr mSubscriptionInfo = nullptr; - - int mToken = 0; - - const int32_t DEFAULT_PULL_UID = AID_SYSTEM; - - // Tracks when we last send data to perfd. We need that time to determine - // when next to send a heartbeat. - int64_t mLastWriteMs = 0; - const int64_t kMsBetweenHeartbeats = 1000; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto deleted file mode 100644 index 07d0310ef2dd..000000000000 --- a/cmds/statsd/src/shell/shell_config.proto +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.os"; -option java_outer_classname = "ShellConfig"; - -import "frameworks/base/cmds/statsd/src/statsd_config.proto"; - -message PulledAtomSubscription { - optional SimpleAtomMatcher matcher = 1; - - /* gap between two pulls in milliseconds */ - optional int32 freq_millis = 2; - - /* Packages that the pull is requested from */ - repeated string packages = 3; -} - -message ShellSubscription { - repeated SimpleAtomMatcher pushed = 1; - repeated PulledAtomSubscription pulled = 2; -} \ No newline at end of file diff --git a/cmds/statsd/src/shell/shell_data.proto b/cmds/statsd/src/shell/shell_data.proto deleted file mode 100644 index ec41cbc5caff..000000000000 --- a/cmds/statsd/src/shell/shell_data.proto +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.os.statsd"; -option java_outer_classname = "ShellDataProto"; - -import "frameworks/proto_logging/stats/atoms.proto"; - -// The output of shell subscription, including both pulled and pushed subscriptions. -message ShellData { - repeated Atom atom = 1; -} diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp deleted file mode 100755 index b877cc9c352f..000000000000 --- a/cmds/statsd/src/socket/StatsSocketListener.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "StatsSocketListener.h" -#include "guardrail/StatsdStats.h" -#include "stats_log_util.h" - -namespace android { -namespace os { -namespace statsd { - -StatsSocketListener::StatsSocketListener(std::shared_ptr queue) - : SocketListener(getLogSocket(), false /*start listen*/), mQueue(queue) { -} - -StatsSocketListener::~StatsSocketListener() { -} - -bool StatsSocketListener::onDataAvailable(SocketClient* cli) { - static bool name_set; - if (!name_set) { - prctl(PR_SET_NAME, "statsd.writer"); - name_set = true; - } - - // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received - char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1]; - struct iovec iov = {buffer, sizeof(buffer) - 1}; - - alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))]; - struct msghdr hdr = { - NULL, 0, &iov, 1, control, sizeof(control), 0, - }; - - int socket = cli->getSocket(); - - // To clear the entire buffer is secure/safe, but this contributes to 1.68% - // overhead under logging load. We are safe because we check counts, but - // still need to clear null terminator - // memset(buffer, 0, sizeof(buffer)); - ssize_t n = recvmsg(socket, &hdr, 0); - if (n <= (ssize_t)(sizeof(android_log_header_t))) { - return false; - } - - buffer[n] = 0; - - struct ucred* cred = NULL; - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); - while (cmsg != NULL) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { - cred = (struct ucred*)CMSG_DATA(cmsg); - break; - } - cmsg = CMSG_NXTHDR(&hdr, cmsg); - } - - struct ucred fake_cred; - if (cred == NULL) { - cred = &fake_cred; - cred->pid = 0; - cred->uid = DEFAULT_OVERFLOWUID; - } - - uint8_t* ptr = ((uint8_t*)buffer) + sizeof(android_log_header_t); - n -= sizeof(android_log_header_t); - - // When a log failed to write to statsd socket (e.g., due ot EBUSY), a special message would - // be sent to statsd when the socket communication becomes available again. - // The format is android_log_event_int_t with a single integer in the payload indicating the - // number of logs that failed. (*FORMAT MUST BE IN SYNC WITH system/core/libstats*) - // Note that all normal stats logs are in the format of event_list, so there won't be confusion. - // - // TODO(b/80538532): In addition to log it in StatsdStats, we should properly reset the config. - if (n == sizeof(android_log_event_long_t)) { - android_log_event_long_t* long_event = reinterpret_cast(ptr); - if (long_event->payload.type == EVENT_TYPE_LONG) { - int64_t composed_long = long_event->payload.data; - - // format: - // |last_tag|dropped_count| - int32_t dropped_count = (int32_t)(0xffffffff & composed_long); - int32_t last_atom_tag = (int32_t)((0xffffffff00000000 & (uint64_t)composed_long) >> 32); - - ALOGE("Found dropped events: %d error %d last atom tag %d from uid %d", dropped_count, - long_event->header.tag, last_atom_tag, cred->uid); - StatsdStats::getInstance().noteLogLost((int32_t)getWallClockSec(), dropped_count, - long_event->header.tag, last_atom_tag, cred->uid, - cred->pid); - return true; - } - } - - // move past the 4-byte StatsEventTag - uint8_t* msg = ptr + sizeof(uint32_t); - uint32_t len = n - sizeof(uint32_t); - uint32_t uid = cred->uid; - uint32_t pid = cred->pid; - - int64_t oldestTimestamp; - std::unique_ptr logEvent = std::make_unique(uid, pid); - logEvent->parseBuffer(msg, len); - - if (!mQueue->push(std::move(logEvent), &oldestTimestamp)) { - StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp); - } - - return true; -} - -int StatsSocketListener::getLogSocket() { - static const char socketName[] = "statsdw"; - int sock = android_get_control_socket(socketName); - - if (sock < 0) { // statsd started up in init.sh - sock = socket_local_server(socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM); - - int on = 1; - if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { - return -1; - } - } - return sock; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/socket/StatsSocketListener.h b/cmds/statsd/src/socket/StatsSocketListener.h deleted file mode 100644 index 2167a56445b9..000000000000 --- a/cmds/statsd/src/socket/StatsSocketListener.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include "logd/LogEventQueue.h" - -// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of -// the uapi headers for userspace to use. This value is filled in on the -// out-of-band socket credentials if the OS fails to find one available. -// One of the causes of this is if SO_PASSCRED is set, all the packets before -// that point will have this value. We also use it in a fake credential if -// no socket credentials are supplied. -#ifndef DEFAULT_OVERFLOWUID -#define DEFAULT_OVERFLOWUID 65534 -#endif - -namespace android { -namespace os { -namespace statsd { - -class StatsSocketListener : public SocketListener, public virtual android::RefBase { -public: - explicit StatsSocketListener(std::shared_ptr queue); - - virtual ~StatsSocketListener(); - -protected: - virtual bool onDataAvailable(SocketClient* cli); - -private: - static int getLogSocket(); - /** - * Who is going to get the events when they're read. - */ - std::shared_ptr mQueue; -}; -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h deleted file mode 100644 index 63880017ca18..000000000000 --- a/cmds/statsd/src/state/StateListener.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include - -#include "HashableDimensionKey.h" - -namespace android { -namespace os { -namespace statsd { - -class StateListener : public virtual RefBase { -public: - StateListener(){}; - - virtual ~StateListener(){}; - - /** - * Interface for handling a state change. - * - * The old and new state values map to the original state values. - * StateTrackers only track the original state values and are unaware - * of higher-level state groups. MetricProducers hold information on - * state groups and are responsible for mapping original state values to - * the correct state group. - * - * [eventTimeNs]: Time of the state change log event. - * [atomId]: The id of the state atom - * [primaryKey]: The primary field values of the state atom - * [oldState]: Previous state value before state change - * [newState]: Current state value after state change - */ - virtual void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const FieldValue& oldState, - const FieldValue& newState) = 0; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp deleted file mode 100644 index c29afeb794fa..000000000000 --- a/cmds/statsd/src/state/StateManager.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StateManager.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -StateManager::StateManager() - : mAllowedPkg({ - "com.android.systemui", - }) { -} - -StateManager& StateManager::getInstance() { - static StateManager sStateManager; - return sStateManager; -} - -void StateManager::clear() { - mStateTrackers.clear(); -} - -void StateManager::onLogEvent(const LogEvent& event) { - // Only process state events from uids in AID_* and packages that are whitelisted in - // mAllowedPkg. - // Whitelisted AIDs are AID_ROOT and all AIDs in [1000, 2000) - if (event.GetUid() == AID_ROOT || (event.GetUid() >= 1000 && event.GetUid() < 2000) || - mAllowedLogSources.find(event.GetUid()) != mAllowedLogSources.end()) { - if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) { - mStateTrackers[event.GetTagId()]->onLogEvent(event); - } - } -} - -void StateManager::registerListener(const int32_t atomId, wp listener) { - // Check if state tracker already exists. - if (mStateTrackers.find(atomId) == mStateTrackers.end()) { - mStateTrackers[atomId] = new StateTracker(atomId); - } - mStateTrackers[atomId]->registerListener(listener); -} - -void StateManager::unregisterListener(const int32_t atomId, wp listener) { - std::unique_lock lock(mMutex); - - // Hold the sp<> until the lock is released so that ~StateTracker() is - // not called while the lock is held. - sp toRemove; - - // Unregister listener from correct StateTracker - auto it = mStateTrackers.find(atomId); - if (it != mStateTrackers.end()) { - it->second->unregisterListener(listener); - - // Remove the StateTracker if it has no listeners - if (it->second->getListenersCount() == 0) { - toRemove = it->second; - mStateTrackers.erase(it); - } - } else { - ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist", - atomId); - } - lock.unlock(); -} - -bool StateManager::getStateValue(const int32_t atomId, const HashableDimensionKey& key, - FieldValue* output) const { - auto it = mStateTrackers.find(atomId); - if (it != mStateTrackers.end()) { - return it->second->getStateValue(key, output); - } - return false; -} - -void StateManager::updateLogSources(const sp& uidMap) { - mAllowedLogSources.clear(); - for (const auto& pkg : mAllowedPkg) { - auto uids = uidMap->getAppUid(pkg); - mAllowedLogSources.insert(uids.begin(), uids.end()); - } -} - -void StateManager::notifyAppChanged(const string& apk, const sp& uidMap) { - if (mAllowedPkg.find(apk) != mAllowedPkg.end()) { - updateLogSources(uidMap); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h deleted file mode 100644 index 18c404c29c4e..000000000000 --- a/cmds/statsd/src/state/StateManager.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include - -#include -#include -#include - -#include "HashableDimensionKey.h" -#include "packages/UidMap.h" -#include "state/StateListener.h" -#include "state/StateTracker.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * This class is NOT thread safe. - * It should only be used while StatsLogProcessor's lock is held. - */ -class StateManager : public virtual RefBase { -public: - StateManager(); - - ~StateManager(){}; - - // Returns a pointer to the single, shared StateManager object. - static StateManager& getInstance(); - - // Unregisters all listeners and removes all trackers from StateManager. - void clear(); - - // Notifies the correct StateTracker of an event. - void onLogEvent(const LogEvent& event); - - // Notifies the StateTracker for the given atomId to register listener. - // If the correct StateTracker does not exist, a new StateTracker is created. - // Note: StateTrackers can be created for non-state atoms. They are essentially empty and - // do not perform any actions. - void registerListener(const int32_t atomId, wp listener); - - // Notifies the correct StateTracker to unregister a listener - // and removes the tracker if it no longer has any listeners. - void unregisterListener(const int32_t atomId, wp listener); - - // Returns true if the StateTracker exists and queries for the - // original state value mapped to the given query key. The state value is - // stored and output in a FieldValue class. - // Returns false if the StateTracker doesn't exist. - bool getStateValue(const int32_t atomId, const HashableDimensionKey& queryKey, - FieldValue* output) const; - - // Updates mAllowedLogSources with the latest uids for the packages that are allowed to log. - void updateLogSources(const sp& uidMap); - - void notifyAppChanged(const string& apk, const sp& uidMap); - - inline int getStateTrackersCount() const { - return mStateTrackers.size(); - } - - inline int getListenersCount(const int32_t atomId) const { - auto it = mStateTrackers.find(atomId); - if (it != mStateTrackers.end()) { - return it->second->getListenersCount(); - } - return -1; - } - -private: - mutable std::mutex mMutex; - - // Maps state atom ids to StateTrackers - std::unordered_map> mStateTrackers; - - // The package names that can log state events. - const std::set mAllowedPkg; - - // The combined uid sources (after translating pkg name to uid). - // State events from uids that are not in the list will be ignored to avoid state pollution. - std::set mAllowedLogSources; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp deleted file mode 100644 index 41e525c343ba..000000000000 --- a/cmds/statsd/src/state/StateTracker.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG true // STOPSHIP if true -#include "Log.h" - -#include "stats_util.h" - -#include "StateTracker.h" - -namespace android { -namespace os { -namespace statsd { - -StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) { -} - -void StateTracker::onLogEvent(const LogEvent& event) { - const int64_t eventTimeNs = event.GetElapsedTimestampNs(); - - // Parse event for primary field values i.e. primary key. - HashableDimensionKey primaryKey; - filterPrimaryKey(event.getValues(), &primaryKey); - - FieldValue newState; - if (!getStateFieldValueFromLogEvent(event, &newState)) { - ALOGE("StateTracker error extracting state from log event. Missing exclusive state field."); - clearStateForPrimaryKey(eventTimeNs, primaryKey); - return; - } - - mField.setField(newState.mField.getField()); - - if (newState.mValue.getType() != INT) { - ALOGE("StateTracker error extracting state from log event. Type: %d", - newState.mValue.getType()); - clearStateForPrimaryKey(eventTimeNs, primaryKey); - return; - } - - if (int resetState = event.getResetState(); resetState != -1) { - VLOG("StateTracker new reset state: %d", resetState); - const FieldValue resetStateFieldValue(mField, Value(resetState)); - handleReset(eventTimeNs, resetStateFieldValue); - return; - } - - const bool nested = newState.mAnnotations.isNested(); - StateValueInfo* stateValueInfo = &mStateMap[primaryKey]; - updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo); -} - -void StateTracker::registerListener(wp listener) { - mListeners.insert(listener); -} - -void StateTracker::unregisterListener(wp listener) { - mListeners.erase(listener); -} - -bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const { - output->mField = mField; - - if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) { - output->mValue = it->second.state; - return true; - } - - // Set the state value to kStateUnknown if query key is not found in state map. - output->mValue = kStateUnknown; - return false; -} - -void StateTracker::handleReset(const int64_t eventTimeNs, const FieldValue& newState) { - VLOG("StateTracker handle reset"); - for (auto& [primaryKey, stateValueInfo] : mStateMap) { - updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, - false /* nested; treat this state change as not nested */, - &stateValueInfo); - } -} - -void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs, - const HashableDimensionKey& primaryKey) { - VLOG("StateTracker clear state for primary key"); - const std::unordered_map::iterator it = - mStateMap.find(primaryKey); - - // If there is no entry for the primaryKey in mStateMap, then the state is already - // kStateUnknown. - const FieldValue state(mField, Value(kStateUnknown)); - if (it != mStateMap.end()) { - updateStateForPrimaryKey(eventTimeNs, primaryKey, state, - false /* nested; treat this state change as not nested */, - &it->second); - } -} - -void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs, - const HashableDimensionKey& primaryKey, - const FieldValue& newState, const bool nested, - StateValueInfo* stateValueInfo) { - FieldValue oldState; - oldState.mField = mField; - oldState.mValue.setInt(stateValueInfo->state); - const int32_t oldStateValue = stateValueInfo->state; - const int32_t newStateValue = newState.mValue.int_value; - - if (kStateUnknown == newStateValue) { - mStateMap.erase(primaryKey); - } - - // Update state map for non-nested counting case. - // Every state event triggers a state overwrite. - if (!nested) { - stateValueInfo->state = newStateValue; - stateValueInfo->count = 1; - - // Notify listeners if state has changed. - if (oldStateValue != newStateValue) { - notifyListeners(eventTimeNs, primaryKey, oldState, newState); - } - return; - } - - // Update state map for nested counting case. - // - // Nested counting is only allowed for binary state events such as ON/OFF or - // ACQUIRE/RELEASE. For example, WakelockStateChanged might have the state - // events: ON, ON, OFF. The state will still be ON until we see the same - // number of OFF events as ON events. - // - // In atoms.proto, a state atom with nested counting enabled - // must only have 2 states. There is no enforcemnt here of this requirement. - // The atom must be logged correctly. - if (kStateUnknown == newStateValue) { - if (kStateUnknown != oldStateValue) { - notifyListeners(eventTimeNs, primaryKey, oldState, newState); - } - } else if (oldStateValue == kStateUnknown) { - stateValueInfo->state = newStateValue; - stateValueInfo->count = 1; - notifyListeners(eventTimeNs, primaryKey, oldState, newState); - } else if (oldStateValue == newStateValue) { - stateValueInfo->count++; - } else if (--stateValueInfo->count == 0) { - stateValueInfo->state = newStateValue; - stateValueInfo->count = 1; - notifyListeners(eventTimeNs, primaryKey, oldState, newState); - } -} - -void StateTracker::notifyListeners(const int64_t eventTimeNs, - const HashableDimensionKey& primaryKey, - const FieldValue& oldState, const FieldValue& newState) { - for (auto l : mListeners) { - auto sl = l.promote(); - if (sl != nullptr) { - sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState); - } - } -} - -bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output) { - const int exclusiveStateFieldIndex = event.getExclusiveStateFieldIndex(); - if (-1 == exclusiveStateFieldIndex) { - ALOGE("error extracting state from log event. Missing exclusive state field."); - return false; - } - - *output = event.getValues()[exclusiveStateFieldIndex]; - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h deleted file mode 100644 index abd579e7e302..000000000000 --- a/cmds/statsd/src/state/StateTracker.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include "HashableDimensionKey.h" -#include "logd/LogEvent.h" - -#include "state/StateListener.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -class StateTracker : public virtual RefBase { -public: - StateTracker(const int32_t atomId); - - virtual ~StateTracker(){}; - - // Updates state map and notifies all listeners if a state change occurs. - // Checks if a state change has occurred by getting the state value from - // the log event and comparing the old and new states. - void onLogEvent(const LogEvent& event); - - // Adds new listeners to set of StateListeners. If a listener is already - // registered, it is ignored. - void registerListener(wp listener); - - void unregisterListener(wp listener); - - // The output is a FieldValue object that has mStateField as the field and - // the original state value (found using the given query key) as the value. - // - // If the key isn't mapped to a state or the key size doesn't match the - // number of primary fields, the output value is set to kStateUnknown. - bool getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const; - - inline int getListenersCount() const { - return mListeners.size(); - } - - const static int kStateUnknown = -1; - -private: - struct StateValueInfo { - int32_t state = kStateUnknown; // state value - int count = 0; // nested count (only used for binary states) - }; - - Field mField; - - // Maps primary key to state value info - std::unordered_map mStateMap; - - // Set of all StateListeners (objects listening for state changes) - std::set> mListeners; - - // Reset all state values in map to the given state. - void handleReset(const int64_t eventTimeNs, const FieldValue& newState); - - // Clears the state value mapped to the given primary key by setting it to kStateUnknown. - void clearStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey); - - // Update the StateMap based on the received state value. - void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey, - const FieldValue& newState, const bool nested, - StateValueInfo* stateValueInfo); - - // Notify registered state listeners of state change. - void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey, - const FieldValue& oldState, const FieldValue& newState); -}; - -bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto deleted file mode 100644 index bb0796326f01..000000000000 --- a/cmds/statsd/src/stats_log.proto +++ /dev/null @@ -1,553 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.os"; -option java_outer_classname = "StatsLog"; - -import "frameworks/proto_logging/stats/atoms.proto"; - -message DimensionsValue { - optional int32 field = 1; - - oneof value { - string value_str = 2; - int32 value_int = 3; - int64 value_long = 4; - bool value_bool = 5; - float value_float = 6; - DimensionsValueTuple value_tuple = 7; - uint64 value_str_hash = 8; - } -} - -message DimensionsValueTuple { - repeated DimensionsValue dimensions_value = 1; -} - -message StateValue { - optional int32 atom_id = 1; - - oneof contents { - int64 group_id = 2; - int32 value = 3; - } -} - -message EventMetricData { - optional int64 elapsed_timestamp_nanos = 1; - - optional Atom atom = 2; - - optional int64 wall_clock_timestamp_nanos = 3 [deprecated = true]; -} - -message CountBucketInfo { - optional int64 start_bucket_elapsed_nanos = 1; - - optional int64 end_bucket_elapsed_nanos = 2; - - optional int64 count = 3; - - optional int64 bucket_num = 4; - - optional int64 start_bucket_elapsed_millis = 5; - - optional int64 end_bucket_elapsed_millis = 6; -} - -message CountMetricData { - optional DimensionsValue dimensions_in_what = 1; - - repeated StateValue slice_by_state = 6; - - repeated CountBucketInfo bucket_info = 3; - - repeated DimensionsValue dimension_leaf_values_in_what = 4; - - optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; - - repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; -} - -message DurationBucketInfo { - optional int64 start_bucket_elapsed_nanos = 1; - - optional int64 end_bucket_elapsed_nanos = 2; - - optional int64 duration_nanos = 3; - - optional int64 bucket_num = 4; - - optional int64 start_bucket_elapsed_millis = 5; - - optional int64 end_bucket_elapsed_millis = 6; -} - -message DurationMetricData { - optional DimensionsValue dimensions_in_what = 1; - - repeated StateValue slice_by_state = 6; - - repeated DurationBucketInfo bucket_info = 3; - - repeated DimensionsValue dimension_leaf_values_in_what = 4; - - optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; - - repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; -} - -message ValueBucketInfo { - optional int64 start_bucket_elapsed_nanos = 1; - - optional int64 end_bucket_elapsed_nanos = 2; - - optional int64 value = 3 [deprecated = true]; - - oneof single_value { - int64 value_long = 7 [deprecated = true]; - - double value_double = 8 [deprecated = true]; - } - - message Value { - optional int32 index = 1; - oneof value { - int64 value_long = 2; - double value_double = 3; - } - } - - repeated Value values = 9; - - optional int64 bucket_num = 4; - - optional int64 start_bucket_elapsed_millis = 5; - - optional int64 end_bucket_elapsed_millis = 6; - - optional int64 condition_true_nanos = 10; -} - -message ValueMetricData { - optional DimensionsValue dimensions_in_what = 1; - - repeated StateValue slice_by_state = 6; - - repeated ValueBucketInfo bucket_info = 3; - - repeated DimensionsValue dimension_leaf_values_in_what = 4; - - optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; - - repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; -} - -message GaugeBucketInfo { - optional int64 start_bucket_elapsed_nanos = 1; - - optional int64 end_bucket_elapsed_nanos = 2; - - repeated Atom atom = 3; - - repeated int64 elapsed_timestamp_nanos = 4; - - repeated int64 wall_clock_timestamp_nanos = 5 [deprecated = true]; - - optional int64 bucket_num = 6; - - optional int64 start_bucket_elapsed_millis = 7; - - optional int64 end_bucket_elapsed_millis = 8; -} - -message GaugeMetricData { - optional DimensionsValue dimensions_in_what = 1; - - // Currently unsupported - repeated StateValue slice_by_state = 6; - - repeated GaugeBucketInfo bucket_info = 3; - - repeated DimensionsValue dimension_leaf_values_in_what = 4; - - optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; - - repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; -} - -message StatsLogReport { - optional int64 metric_id = 1; - - // Fields 2 and 3 are reserved. - - // Keep this in sync with BucketDropReason enum in MetricProducer.h. - enum BucketDropReason { - // For ValueMetric, a bucket is dropped during a dump report request iff - // current bucket should be included, a pull is needed (pulled metric and - // condition is true), and we are under fast time constraints. - DUMP_REPORT_REQUESTED = 1; - EVENT_IN_WRONG_BUCKET = 2; - CONDITION_UNKNOWN = 3; - PULL_FAILED = 4; - PULL_DELAYED = 5; - DIMENSION_GUARDRAIL_REACHED = 6; - MULTIPLE_BUCKETS_SKIPPED = 7; - // Not an invalid bucket case, but the bucket is dropped. - BUCKET_TOO_SMALL = 8; - // Not an invalid bucket case, but the bucket is skipped. - NO_DATA = 9; - }; - - message DropEvent { - optional BucketDropReason drop_reason = 1; - - optional int64 drop_time_millis = 2; - } - - message SkippedBuckets { - optional int64 start_bucket_elapsed_nanos = 1; - - optional int64 end_bucket_elapsed_nanos = 2; - - optional int64 start_bucket_elapsed_millis = 3; - - optional int64 end_bucket_elapsed_millis = 4; - - // The number of drop events is capped by StatsdStats::kMaxLoggedBucketDropEvents. - // The current maximum is 10 drop events. - repeated DropEvent drop_event = 5; - } - - message EventMetricDataWrapper { - repeated EventMetricData data = 1; - } - message CountMetricDataWrapper { - repeated CountMetricData data = 1; - } - message DurationMetricDataWrapper { - repeated DurationMetricData data = 1; - } - message ValueMetricDataWrapper { - repeated ValueMetricData data = 1; - repeated SkippedBuckets skipped = 2; - } - - message GaugeMetricDataWrapper { - repeated GaugeMetricData data = 1; - repeated SkippedBuckets skipped = 2; - } - - oneof data { - EventMetricDataWrapper event_metrics = 4; - CountMetricDataWrapper count_metrics = 5; - DurationMetricDataWrapper duration_metrics = 6; - ValueMetricDataWrapper value_metrics = 7; - GaugeMetricDataWrapper gauge_metrics = 8; - } - - optional int64 time_base_elapsed_nano_seconds = 9; - - optional int64 bucket_size_nano_seconds = 10; - - optional DimensionsValue dimensions_path_in_what = 11; - - optional DimensionsValue dimensions_path_in_condition = 12 [deprecated = true]; - - // DO NOT USE field 13. - - optional bool is_active = 14; -} - -message UidMapping { - message PackageInfoSnapshot { - message PackageInfo { - optional string name = 1; - - optional int64 version = 2; - - optional int32 uid = 3; - - optional bool deleted = 4; - - optional uint64 name_hash = 5; - - optional string version_string = 6; - - optional uint64 version_string_hash = 7; - - optional string installer = 8; - - optional uint64 installer_hash = 9; - } - optional int64 elapsed_timestamp_nanos = 1; - - repeated PackageInfo package_info = 2; - } - repeated PackageInfoSnapshot snapshots = 1; - - message Change { - optional bool deletion = 1; - - optional int64 elapsed_timestamp_nanos = 2; - optional string app = 3; - optional int32 uid = 4; - - optional int64 new_version = 5; - optional int64 prev_version = 6; - optional uint64 app_hash = 7; - optional string new_version_string = 8; - optional string prev_version_string = 9; - optional uint64 new_version_string_hash = 10; - optional uint64 prev_version_string_hash = 11; - } - repeated Change changes = 2; -} - -message ConfigMetricsReport { - repeated StatsLogReport metrics = 1; - - optional UidMapping uid_map = 2; - - optional int64 last_report_elapsed_nanos = 3; - - optional int64 current_report_elapsed_nanos = 4; - - optional int64 last_report_wall_clock_nanos = 5; - - optional int64 current_report_wall_clock_nanos = 6; - - message Annotation { - optional int64 field_int64 = 1; - optional int32 field_int32 = 2; - } - repeated Annotation annotation = 7; - - enum DumpReportReason { - DEVICE_SHUTDOWN = 1; - CONFIG_UPDATED = 2; - CONFIG_REMOVED = 3; - GET_DATA_CALLED = 4; - ADB_DUMP = 5; - CONFIG_RESET = 6; - STATSCOMPANION_DIED = 7; - TERMINATION_SIGNAL_RECEIVED = 8; - } - optional DumpReportReason dump_report_reason = 8; - - repeated string strings = 9; -} - -message ConfigMetricsReportList { - message ConfigKey { - optional int32 uid = 1; - optional int64 id = 2; - } - optional ConfigKey config_key = 1; - - repeated ConfigMetricsReport reports = 2; - - reserved 10; -} - -message StatsdStatsReport { - optional int32 stats_begin_time_sec = 1; - - optional int32 stats_end_time_sec = 2; - - message MatcherStats { - optional int64 id = 1; - optional int32 matched_times = 2; - } - - message ConditionStats { - optional int64 id = 1; - optional int32 max_tuple_counts = 2; - } - - message MetricStats { - optional int64 id = 1; - optional int32 max_tuple_counts = 2; - } - - message AlertStats { - optional int64 id = 1; - optional int32 alerted_times = 2; - } - - message ConfigStats { - optional int32 uid = 1; - optional int64 id = 2; - optional int32 creation_time_sec = 3; - optional int32 deletion_time_sec = 4; - optional int32 reset_time_sec = 19; - optional int32 metric_count = 5; - optional int32 condition_count = 6; - optional int32 matcher_count = 7; - optional int32 alert_count = 8; - optional bool is_valid = 9; - repeated int32 broadcast_sent_time_sec = 10; - repeated int32 data_drop_time_sec = 11; - repeated int64 data_drop_bytes = 21; - repeated int32 dump_report_time_sec = 12; - repeated int32 dump_report_data_size = 20; - repeated MatcherStats matcher_stats = 13; - repeated ConditionStats condition_stats = 14; - repeated MetricStats metric_stats = 15; - repeated AlertStats alert_stats = 16; - repeated MetricStats metric_dimension_in_condition_stats = 17 [deprecated = true]; - message Annotation { - optional int64 field_int64 = 1; - optional int32 field_int32 = 2; - } - repeated Annotation annotation = 18; - repeated int32 activation_time_sec = 22; - repeated int32 deactivation_time_sec = 23; - } - - repeated ConfigStats config_stats = 3; - - message AtomStats { - optional int32 tag = 1; - optional int32 count = 2; - optional int32 error_count = 3; - } - - repeated AtomStats atom_stats = 7; - - message UidMapStats { - optional int32 changes = 1; - optional int32 bytes_used = 2; - optional int32 dropped_changes = 3; - optional int32 deleted_apps = 4; - } - optional UidMapStats uidmap_stats = 8; - - message AnomalyAlarmStats { - optional int32 alarms_registered = 1; - } - optional AnomalyAlarmStats anomaly_alarm_stats = 9; - - message PulledAtomStats { - optional int32 atom_id = 1; - optional int64 total_pull = 2; - optional int64 total_pull_from_cache = 3; - optional int64 min_pull_interval_sec = 4; - optional int64 average_pull_time_nanos = 5; - optional int64 max_pull_time_nanos = 6; - optional int64 average_pull_delay_nanos = 7; - optional int64 max_pull_delay_nanos = 8; - optional int64 data_error = 9; - optional int64 pull_timeout = 10; - optional int64 pull_exceed_max_delay = 11; - optional int64 pull_failed = 12; - optional int64 stats_companion_pull_failed = 13 [deprecated = true]; - optional int64 stats_companion_pull_binder_transaction_failed = 14 [deprecated = true]; - optional int64 empty_data = 15; - optional int64 registered_count = 16; - optional int64 unregistered_count = 17; - optional int32 atom_error_count = 18; - optional int64 binder_call_failed = 19; - optional int64 failed_uid_provider_not_found = 20; - optional int64 puller_not_found = 21; - message PullTimeoutMetadata { - optional int64 pull_timeout_uptime_millis = 1; - optional int64 pull_timeout_elapsed_millis = 2; - } - repeated PullTimeoutMetadata pull_atom_metadata = 22; - } - repeated PulledAtomStats pulled_atom_stats = 10; - - message AtomMetricStats { - optional int64 metric_id = 1; - optional int64 hard_dimension_limit_reached = 2; - optional int64 late_log_event_skipped = 3; - optional int64 skipped_forward_buckets = 4; - optional int64 bad_value_type = 5; - optional int64 condition_change_in_next_bucket = 6; - optional int64 invalidated_bucket = 7; - optional int64 bucket_dropped = 8; - optional int64 min_bucket_boundary_delay_ns = 9; - optional int64 max_bucket_boundary_delay_ns = 10; - optional int64 bucket_unknown_condition = 11; - optional int64 bucket_count = 12; - } - repeated AtomMetricStats atom_metric_stats = 17; - - message LoggerErrorStats { - optional int32 logger_disconnection_sec = 1; - optional int32 error_code = 2; - } - repeated LoggerErrorStats logger_error_stats = 11; - - message PeriodicAlarmStats { - optional int32 alarms_registered = 1; - } - optional PeriodicAlarmStats periodic_alarm_stats = 12; - - message SkippedLogEventStats { - optional int32 tag = 1; - optional int64 elapsed_timestamp_nanos = 2; - } - repeated SkippedLogEventStats skipped_log_event_stats = 13; - - repeated int64 log_loss_stats = 14; - - repeated int32 system_restart_sec = 15; - - message LogLossStats { - optional int32 detected_time_sec = 1; - optional int32 count = 2; - optional int32 last_error = 3; - optional int32 last_tag = 4; - optional int32 uid = 5; - optional int32 pid = 6; - } - repeated LogLossStats detected_log_loss = 16; - - message EventQueueOverflow { - optional int32 count = 1; - optional int64 max_queue_history_ns = 2; - optional int64 min_queue_history_ns = 3; - } - - optional EventQueueOverflow queue_overflow = 18; - - message ActivationBroadcastGuardrail { - optional int32 uid = 1; - repeated int32 guardrail_met_sec = 2; - } - - repeated ActivationBroadcastGuardrail activation_guardrail_stats = 19; -} - -message AlertTriggerDetails { - message MetricValue { - optional int64 metric_id = 1; - optional DimensionsValue dimension_in_what = 2; - optional DimensionsValue dimension_in_condition = 3 [deprecated = true]; - optional int64 value = 4; - } - oneof value { - MetricValue trigger_metric = 1; - EventMetricData trigger_event = 2; - } - optional UidMapping.PackageInfoSnapshot package_info = 3; -} diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp deleted file mode 100644 index 423bae8bc0a4..000000000000 --- a/cmds/statsd/src/stats_log_util.cpp +++ /dev/null @@ -1,609 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "hash.h" -#include "stats_log_util.h" - -#include -#include -#include -#include - -#include "statscompanion_util.h" - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_BOOL; -using android::util::FIELD_TYPE_FIXED64; -using android::util::FIELD_TYPE_FLOAT; -using android::util::FIELD_TYPE_INT32; -using android::util::FIELD_TYPE_INT64; -using android::util::FIELD_TYPE_MESSAGE; -using android::util::FIELD_TYPE_STRING; -using android::util::FIELD_TYPE_UINT64; -using android::util::ProtoOutputStream; - -using aidl::android::os::IStatsCompanionService; -using std::shared_ptr; -using std::string; - -namespace android { -namespace os { -namespace statsd { - -// for DimensionsValue Proto -const int DIMENSIONS_VALUE_FIELD = 1; -const int DIMENSIONS_VALUE_VALUE_STR = 2; -const int DIMENSIONS_VALUE_VALUE_INT = 3; -const int DIMENSIONS_VALUE_VALUE_LONG = 4; -// const int DIMENSIONS_VALUE_VALUE_BOOL = 5; // logd doesn't have bool data type. -const int DIMENSIONS_VALUE_VALUE_FLOAT = 6; -const int DIMENSIONS_VALUE_VALUE_TUPLE = 7; -const int DIMENSIONS_VALUE_VALUE_STR_HASH = 8; - -const int DIMENSIONS_VALUE_TUPLE_VALUE = 1; - -// for StateValue Proto -const int STATE_VALUE_ATOM_ID = 1; -const int STATE_VALUE_CONTENTS_GROUP_ID = 2; -const int STATE_VALUE_CONTENTS_VALUE = 3; - -// for PulledAtomStats proto -const int FIELD_ID_PULLED_ATOM_STATS = 10; -const int FIELD_ID_PULL_ATOM_ID = 1; -const int FIELD_ID_TOTAL_PULL = 2; -const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3; -const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4; -const int FIELD_ID_AVERAGE_PULL_TIME_NANOS = 5; -const int FIELD_ID_MAX_PULL_TIME_NANOS = 6; -const int FIELD_ID_AVERAGE_PULL_DELAY_NANOS = 7; -const int FIELD_ID_MAX_PULL_DELAY_NANOS = 8; -const int FIELD_ID_DATA_ERROR = 9; -const int FIELD_ID_PULL_TIMEOUT = 10; -const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11; -const int FIELD_ID_PULL_FAILED = 12; -const int FIELD_ID_EMPTY_DATA = 15; -const int FIELD_ID_PULL_REGISTERED_COUNT = 16; -const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17; -const int FIELD_ID_ATOM_ERROR_COUNT = 18; -const int FIELD_ID_BINDER_CALL_FAIL_COUNT = 19; -const int FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND = 20; -const int FIELD_ID_PULLER_NOT_FOUND = 21; -const int FIELD_ID_PULL_TIMEOUT_METADATA = 22; -const int FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS = 1; -const int FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS = 2; - -// for AtomMetricStats proto -const int FIELD_ID_ATOM_METRIC_STATS = 17; -const int FIELD_ID_METRIC_ID = 1; -const int FIELD_ID_HARD_DIMENSION_LIMIT_REACHED = 2; -const int FIELD_ID_LATE_LOG_EVENT_SKIPPED = 3; -const int FIELD_ID_SKIPPED_FORWARD_BUCKETS = 4; -const int FIELD_ID_BAD_VALUE_TYPE = 5; -const int FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET = 6; -const int FIELD_ID_INVALIDATED_BUCKET = 7; -const int FIELD_ID_BUCKET_DROPPED = 8; -const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9; -const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10; -const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11; -const int FIELD_ID_BUCKET_COUNT = 12; - -namespace { - -void writeDimensionToProtoHelper(const std::vector& dims, size_t* index, int depth, - int prefix, std::set *str_set, - ProtoOutputStream* protoOutput) { - size_t count = dims.size(); - while (*index < count) { - const auto& dim = dims[*index]; - const int valueDepth = dim.mField.getDepth(); - const int valuePrefix = dim.mField.getPrefix(depth); - const int fieldNum = dim.mField.getPosAtDepth(depth); - if (valueDepth > 2) { - ALOGE("Depth > 2 not supported"); - return; - } - - if (depth == valueDepth && valuePrefix == prefix) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - DIMENSIONS_VALUE_TUPLE_VALUE); - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); - switch (dim.mValue.getType()) { - case INT: - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT, - dim.mValue.int_value); - break; - case LONG: - protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG, - (long long)dim.mValue.long_value); - break; - case FLOAT: - protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT, - dim.mValue.float_value); - break; - case STRING: - if (str_set == nullptr) { - protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR, - dim.mValue.str_value); - } else { - str_set->insert(dim.mValue.str_value); - protoOutput->write( - FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH, - (long long)Hash64(dim.mValue.str_value)); - } - break; - default: - break; - } - if (token != 0) { - protoOutput->end(token); - } - (*index)++; - } else if (valueDepth > depth && valuePrefix == prefix) { - // Writing the sub tree - uint64_t dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE); - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); - uint64_t tupleToken = - protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); - writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth), - str_set, protoOutput); - protoOutput->end(tupleToken); - protoOutput->end(dimensionToken); - } else { - // Done with the prev sub tree - return; - } - } -} - -void writeDimensionLeafToProtoHelper(const std::vector& dims, - const int dimensionLeafField, - size_t* index, int depth, - int prefix, std::set *str_set, - ProtoOutputStream* protoOutput) { - size_t count = dims.size(); - while (*index < count) { - const auto& dim = dims[*index]; - const int valueDepth = dim.mField.getDepth(); - const int valuePrefix = dim.mField.getPrefix(depth); - if (valueDepth > 2) { - ALOGE("Depth > 2 not supported"); - return; - } - - if (depth == valueDepth && valuePrefix == prefix) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - dimensionLeafField); - switch (dim.mValue.getType()) { - case INT: - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT, - dim.mValue.int_value); - break; - case LONG: - protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG, - (long long)dim.mValue.long_value); - break; - case FLOAT: - protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT, - dim.mValue.float_value); - break; - case STRING: - if (str_set == nullptr) { - protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR, - dim.mValue.str_value); - } else { - str_set->insert(dim.mValue.str_value); - protoOutput->write( - FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH, - (long long)Hash64(dim.mValue.str_value)); - } - break; - default: - break; - } - if (token != 0) { - protoOutput->end(token); - } - (*index)++; - } else if (valueDepth > depth && valuePrefix == prefix) { - writeDimensionLeafToProtoHelper(dims, dimensionLeafField, - index, valueDepth, dim.mField.getPrefix(valueDepth), - str_set, protoOutput); - } else { - // Done with the prev sub tree - return; - } - } -} - -void writeDimensionPathToProtoHelper(const std::vector& fieldMatchers, - size_t* index, int depth, int prefix, - ProtoOutputStream* protoOutput) { - size_t count = fieldMatchers.size(); - while (*index < count) { - const Field& field = fieldMatchers[*index].mMatcher; - const int valueDepth = field.getDepth(); - const int valuePrefix = field.getPrefix(depth); - const int fieldNum = field.getPosAtDepth(depth); - if (valueDepth > 2) { - ALOGE("Depth > 2 not supported"); - return; - } - - if (depth == valueDepth && valuePrefix == prefix) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - DIMENSIONS_VALUE_TUPLE_VALUE); - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); - if (token != 0) { - protoOutput->end(token); - } - (*index)++; - } else if (valueDepth > depth && valuePrefix == prefix) { - // Writing the sub tree - uint64_t dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE); - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); - uint64_t tupleToken = - protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); - writeDimensionPathToProtoHelper(fieldMatchers, index, valueDepth, - field.getPrefix(valueDepth), protoOutput); - protoOutput->end(tupleToken); - protoOutput->end(dimensionToken); - } else { - // Done with the prev sub tree - return; - } - } -} - -} // namespace - -void writeDimensionToProto(const HashableDimensionKey& dimension, std::set *str_set, - ProtoOutputStream* protoOutput) { - if (dimension.getValues().size() == 0) { - return; - } - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, - dimension.getValues()[0].mField.getTag()); - uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); - size_t index = 0; - writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, str_set, protoOutput); - protoOutput->end(topToken); -} - -void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension, - const int dimensionLeafFieldId, - std::set *str_set, - ProtoOutputStream* protoOutput) { - if (dimension.getValues().size() == 0) { - return; - } - size_t index = 0; - writeDimensionLeafToProtoHelper(dimension.getValues(), dimensionLeafFieldId, - &index, 0, 0, str_set, protoOutput); -} - -void writeDimensionPathToProto(const std::vector& fieldMatchers, - ProtoOutputStream* protoOutput) { - if (fieldMatchers.size() == 0) { - return; - } - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, - fieldMatchers[0].mMatcher.getTag()); - uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); - size_t index = 0; - writeDimensionPathToProtoHelper(fieldMatchers, &index, 0, 0, protoOutput); - protoOutput->end(topToken); -} - -// Supported Atoms format -// XYZ_Atom { -// repeated SubMsg field_1 = 1; -// SubMsg2 field_2 = 2; -// int32/float/string/int63 field_3 = 3; -// } -// logd's msg format, doesn't allow us to distinguish between the 2 cases below -// Case (1): -// Atom { -// SubMsg { -// int i = 1; -// int j = 2; -// } -// repeated SubMsg -// } -// -// and case (2): -// Atom { -// SubMsg { -// repeated int i = 1; -// repeated int j = 2; -// } -// optional SubMsg = 1; -// } -// -// -void writeFieldValueTreeToStreamHelper(int tagId, const std::vector& dims, - size_t* index, int depth, int prefix, - ProtoOutputStream* protoOutput) { - size_t count = dims.size(); - while (*index < count) { - const auto& dim = dims[*index]; - const int valueDepth = dim.mField.getDepth(); - const int valuePrefix = dim.mField.getPrefix(depth); - const int fieldNum = dim.mField.getPosAtDepth(depth); - if (valueDepth > 2) { - ALOGE("Depth > 2 not supported"); - return; - } - - if (depth == valueDepth && valuePrefix == prefix) { - switch (dim.mValue.getType()) { - case INT: - protoOutput->write(FIELD_TYPE_INT32 | fieldNum, dim.mValue.int_value); - break; - case LONG: - protoOutput->write(FIELD_TYPE_INT64 | fieldNum, - (long long)dim.mValue.long_value); - break; - case FLOAT: - protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value); - break; - case STRING: { - protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value); - break; - } - case STORAGE: - protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum, - (const char*)dim.mValue.storage_value.data(), - dim.mValue.storage_value.size()); - break; - default: - break; - } - (*index)++; - } else if (valueDepth > depth && valuePrefix == prefix) { - // Writing the sub tree - uint64_t msg_token = 0ULL; - if (valueDepth == depth + 2) { - msg_token = - protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum); - } else if (valueDepth == depth + 1) { - msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum); - } - // Directly jump to the leaf value because the repeated position field is implied - // by the position of the sub msg in the parent field. - writeFieldValueTreeToStreamHelper(tagId, dims, index, valueDepth, - dim.mField.getPrefix(valueDepth), protoOutput); - if (msg_token != 0) { - protoOutput->end(msg_token); - } - } else { - // Done with the prev sub tree - return; - } - } -} - -void writeFieldValueTreeToStream(int tagId, const std::vector& values, - util::ProtoOutputStream* protoOutput) { - uint64_t atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId); - - size_t index = 0; - writeFieldValueTreeToStreamHelper(tagId, values, &index, 0, 0, protoOutput); - protoOutput->end(atomToken); -} - -void writeStateToProto(const FieldValue& state, util::ProtoOutputStream* protoOutput) { - protoOutput->write(FIELD_TYPE_INT32 | STATE_VALUE_ATOM_ID, state.mField.getTag()); - - switch (state.mValue.getType()) { - case INT: - protoOutput->write(FIELD_TYPE_INT32 | STATE_VALUE_CONTENTS_VALUE, - state.mValue.int_value); - break; - case LONG: - protoOutput->write(FIELD_TYPE_INT64 | STATE_VALUE_CONTENTS_GROUP_ID, - state.mValue.long_value); - break; - default: - break; - } -} - -int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) { - int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit); - if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL && - uid != AID_ROOT) { - bucketSizeMillis = 5 * 60 * 1000LL; - } - return bucketSizeMillis; -} - -int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) { - switch (unit) { - case ONE_MINUTE: - return 60 * 1000LL; - case FIVE_MINUTES: - return 5 * 60 * 1000LL; - case TEN_MINUTES: - return 10 * 60 * 1000LL; - case THIRTY_MINUTES: - return 30 * 60 * 1000LL; - case ONE_HOUR: - return 60 * 60 * 1000LL; - case THREE_HOURS: - return 3 * 60 * 60 * 1000LL; - case SIX_HOURS: - return 6 * 60 * 60 * 1000LL; - case TWELVE_HOURS: - return 12 * 60 * 60 * 1000LL; - case ONE_DAY: - return 24 * 60 * 60 * 1000LL; - case ONE_WEEK: - return 7 * 24 * 60 * 60 * 1000LL; - case CTS: - return 1000; - case TIME_UNIT_UNSPECIFIED: - default: - return -1; - } -} - -void writePullerStatsToStream(const std::pair& pair, - util::ProtoOutputStream* protoOutput) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS | - FIELD_COUNT_REPEATED); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_PULL_ATOM_ID, (int32_t)pair.first); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL, (long long)pair.second.totalPull); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL_FROM_CACHE, - (long long)pair.second.totalPullFromCache); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_PULL_INTERVAL_SEC, - (long long)pair.second.minPullIntervalSec); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_AVERAGE_PULL_TIME_NANOS, - (long long)pair.second.avgPullTimeNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_TIME_NANOS, - (long long)pair.second.maxPullTimeNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_AVERAGE_PULL_DELAY_NANOS, - (long long)pair.second.avgPullDelayNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_DELAY_NANOS, - (long long)pair.second.maxPullDelayNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DATA_ERROR, (long long)pair.second.dataError); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT, - (long long)pair.second.pullTimeout); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_EXCEED_MAX_DELAY, - (long long)pair.second.pullExceedMaxDelay); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED, - (long long)pair.second.pullFailed); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA, - (long long)pair.second.emptyData); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT, - (long long) pair.second.registeredCount); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT, - (long long) pair.second.unregisteredCount); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BINDER_CALL_FAIL_COUNT, - (long long)pair.second.binderCallFailCount); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND, - (long long)pair.second.pullUidProviderNotFound); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULLER_NOT_FOUND, - (long long)pair.second.pullerNotFound); - for (const auto& pullTimeoutMetadata : pair.second.pullTimeoutMetadata) { - uint64_t timeoutMetadataToken = protoOutput->start(FIELD_TYPE_MESSAGE | - FIELD_ID_PULL_TIMEOUT_METADATA | - FIELD_COUNT_REPEATED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS, - pullTimeoutMetadata.pullTimeoutUptimeMillis); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS, - pullTimeoutMetadata.pullTimeoutElapsedMillis); - protoOutput->end(timeoutMetadataToken); - } - protoOutput->end(token); -} - -void writeAtomMetricStatsToStream(const std::pair &pair, - util::ProtoOutputStream *protoOutput) { - uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS | - FIELD_COUNT_REPEATED); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED, - (long long)pair.second.hardDimensionLimitReached); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED, - (long long)pair.second.lateLogEventSkipped); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_FORWARD_BUCKETS, - (long long)pair.second.skippedForwardBuckets); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BAD_VALUE_TYPE, - (long long)pair.second.badValueType); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET, - (long long)pair.second.conditionChangeInNextBucket); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_INVALIDATED_BUCKET, - (long long)pair.second.invalidatedBucket); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_DROPPED, - (long long)pair.second.bucketDropped); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS, - (long long)pair.second.minBucketBoundaryDelayNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS, - (long long)pair.second.maxBucketBoundaryDelayNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION, - (long long)pair.second.bucketUnknownCondition); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_COUNT, - (long long)pair.second.bucketCount); - protoOutput->end(token); -} - -int64_t getElapsedRealtimeNs() { - return ::android::elapsedRealtimeNano(); -} - -int64_t getElapsedRealtimeSec() { - return ::android::elapsedRealtimeNano() / NS_PER_SEC; -} - -int64_t getElapsedRealtimeMillis() { - return ::android::elapsedRealtime(); -} - -int64_t getSystemUptimeMillis() { - return ::android::uptimeMillis(); -} - -int64_t getWallClockNs() { - return time(nullptr) * NS_PER_SEC; -} - -int64_t getWallClockSec() { - return time(nullptr); -} - -int64_t getWallClockMillis() { - return time(nullptr) * MS_PER_SEC; -} - -int64_t truncateTimestampIfNecessary(const LogEvent& event) { - if (event.shouldTruncateTimestamp() || - (event.GetTagId() >= StatsdStats::kTimestampTruncationStartTag && - event.GetTagId() <= StatsdStats::kTimestampTruncationEndTag)) { - return event.GetElapsedTimestampNs() / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60); - } else { - return event.GetElapsedTimestampNs(); - } -} - -int64_t NanoToMillis(const int64_t nano) { - return nano / 1000000; -} - -int64_t MillisToNano(const int64_t millis) { - return millis * 1000000; -} - -bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid) { - shared_ptr scs = getStatsCompanionService(); - if (scs == nullptr) { - return false; - } - - bool success; - ::ndk::ScopedAStatus status = scs->checkPermission(string(permission), pid, uid, &success); - if (!status.isOk()) { - return false; - } - - return success; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h deleted file mode 100644 index 10e065e4113f..000000000000 --- a/cmds/statsd/src/stats_log_util.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "FieldValue.h" -#include "HashableDimensionKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" - -using android::util::ProtoOutputStream; - -namespace android { -namespace os { -namespace statsd { - -void writeFieldValueTreeToStream(int tagId, const std::vector& values, - ProtoOutputStream* protoOutput); -void writeDimensionToProto(const HashableDimensionKey& dimension, std::set *str_set, - ProtoOutputStream* protoOutput); - -void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension, - const int dimensionLeafFieldId, - std::set *str_set, - ProtoOutputStream* protoOutput); - -void writeDimensionPathToProto(const std::vector& fieldMatchers, - ProtoOutputStream* protoOutput); - -void writeStateToProto(const FieldValue& state, ProtoOutputStream* protoOutput); - -// Convert the TimeUnit enum to the bucket size in millis with a guardrail on -// bucket size. -int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit); - -// Convert the TimeUnit enum to the bucket size in millis. -int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit); - -// Gets the elapsed timestamp in ns. -int64_t getElapsedRealtimeNs(); - -// Gets the elapsed timestamp in millis. -int64_t getElapsedRealtimeMillis(); - -// Gets the elapsed timestamp in seconds. -int64_t getElapsedRealtimeSec(); - -// Gets the system uptime in millis. -int64_t getSystemUptimeMillis(); - -// Gets the wall clock timestamp in ns. -int64_t getWallClockNs(); - -// Gets the wall clock timestamp in millis. -int64_t getWallClockMillis(); - -// Gets the wall clock timestamp in seconds. -int64_t getWallClockSec(); - -int64_t NanoToMillis(const int64_t nano); - -int64_t MillisToNano(const int64_t millis); - -// Helper function to write PulledAtomStats to ProtoOutputStream -void writePullerStatsToStream(const std::pair& pair, - ProtoOutputStream* protoOutput); - -// Helper function to write AtomMetricStats to ProtoOutputStream -void writeAtomMetricStatsToStream(const std::pair &pair, - ProtoOutputStream *protoOutput); - -template -bool parseProtoOutputStream(ProtoOutputStream& protoOutput, T* message) { - std::string pbBytes; - sp reader = protoOutput.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - pbBytes.append(reinterpret_cast(reader->readBuffer()), toRead); - reader->move(toRead); - } - return message->ParseFromArray(pbBytes.c_str(), pbBytes.size()); -} - -// Checks the truncate timestamp annotation as well as the restricted range of 300,000 - 304,999. -// Returns the truncated timestamp to the nearest 5 minutes if needed. -int64_t truncateTimestampIfNecessary(const LogEvent& event); - -// Checks permission for given pid and uid. -bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid); - -inline bool isVendorPulledAtom(int atomId) { - return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag; -} - -inline bool isPulledAtom(int atomId) { - return atomId >= StatsdStats::kPullAtomStartTag && atomId < StatsdStats::kVendorAtomStartTag; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h deleted file mode 100644 index cfc411fdd25f..000000000000 --- a/cmds/statsd/src/stats_util.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "HashableDimensionKey.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey(); -const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey(); - -typedef std::map ConditionKey; - -typedef std::unordered_map DimToValMap; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/statscompanion_util.cpp b/cmds/statsd/src/statscompanion_util.cpp deleted file mode 100644 index ce07ec0ea884..000000000000 --- a/cmds/statsd/src/statscompanion_util.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "statscompanion_util.h" -#include -#include - -namespace android { -namespace os { -namespace statsd { - -shared_ptr getStatsCompanionService() { - ::ndk::SpAIBinder binder(AServiceManager_getService("statscompanion")); - return IStatsCompanionService::fromBinder(binder); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/statscompanion_util.h b/cmds/statsd/src/statscompanion_util.h deleted file mode 100644 index e20c40bba104..000000000000 --- a/cmds/statsd/src/statscompanion_util.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -using aidl::android::os::IStatsCompanionService; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -/** Fetches and returns the StatsCompanionService. */ -shared_ptr getStatsCompanionService(); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto deleted file mode 100644 index acdffd3d4712..000000000000 --- a/cmds/statsd/src/statsd_config.proto +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.internal.os"; -option java_outer_classname = "StatsdConfigProto"; - -enum Position { - POSITION_UNKNOWN = 0; - - FIRST = 1; - - LAST = 2; - - ANY = 3; - - ALL = 4; -} - -enum TimeUnit { - TIME_UNIT_UNSPECIFIED = 0; - ONE_MINUTE = 1; // WILL BE GUARDRAILED TO 5 MINS UNLESS UID = SHELL OR ROOT - FIVE_MINUTES = 2; - TEN_MINUTES = 3; - THIRTY_MINUTES = 4; - ONE_HOUR = 5; - THREE_HOURS = 6; - SIX_HOURS = 7; - TWELVE_HOURS = 8; - ONE_DAY = 9; - ONE_WEEK = 10; - CTS = 1000; -} - -message FieldMatcher { - optional int32 field = 1; - - optional Position position = 2; - - repeated FieldMatcher child = 3; -} - -message FieldValueMatcher { - optional int32 field = 1; - - optional Position position = 2; - - oneof value_matcher { - bool eq_bool = 3; - string eq_string = 4; - int64 eq_int = 5; - - int64 lt_int = 6; - int64 gt_int = 7; - float lt_float = 8; - float gt_float = 9; - - int64 lte_int = 10; - int64 gte_int = 11; - - MessageMatcher matches_tuple = 12; - - StringListMatcher eq_any_string = 13; - StringListMatcher neq_any_string = 14; - } -} - -message MessageMatcher { - repeated FieldValueMatcher field_value_matcher = 1; -} - -message StringListMatcher { - repeated string str_value = 1; -} - -enum LogicalOperation { - LOGICAL_OPERATION_UNSPECIFIED = 0; - AND = 1; - OR = 2; - NOT = 3; - NAND = 4; - NOR = 5; -} - -message SimpleAtomMatcher { - optional int32 atom_id = 1; - - repeated FieldValueMatcher field_value_matcher = 2; -} - -message AtomMatcher { - optional int64 id = 1; - - message Combination { - optional LogicalOperation operation = 1; - - repeated int64 matcher = 2; - } - oneof contents { - SimpleAtomMatcher simple_atom_matcher = 2; - Combination combination = 3; - } -} - -message SimplePredicate { - optional int64 start = 1; - - optional int64 stop = 2; - - optional bool count_nesting = 3 [default = true]; - - optional int64 stop_all = 4; - - enum InitialValue { - UNKNOWN = 0; - FALSE = 1; - } - optional InitialValue initial_value = 5 [default = UNKNOWN]; - - optional FieldMatcher dimensions = 6; -} - -message Predicate { - optional int64 id = 1; - - message Combination { - optional LogicalOperation operation = 1; - - repeated int64 predicate = 2; - } - - oneof contents { - SimplePredicate simple_predicate = 2; - Combination combination = 3; - } -} - -message StateMap { - message StateGroup { - optional int64 group_id = 1; - - repeated int32 value = 2; - } - - repeated StateGroup group = 1; -} - -message State { - optional int64 id = 1; - - optional int32 atom_id = 2; - - optional StateMap map = 3; -} - -message MetricConditionLink { - optional int64 condition = 1; - - optional FieldMatcher fields_in_what = 2; - - optional FieldMatcher fields_in_condition = 3; -} - -message MetricStateLink { - optional int32 state_atom_id = 1; - - optional FieldMatcher fields_in_what = 2; - - optional FieldMatcher fields_in_state = 3; -} - -message FieldFilter { - optional bool include_all = 1 [default = false]; - optional FieldMatcher fields = 2; -} - -message EventMetric { - optional int64 id = 1; - - optional int64 what = 2; - - optional int64 condition = 3; - - repeated MetricConditionLink links = 4; - - reserved 100; - reserved 101; -} - -message CountMetric { - optional int64 id = 1; - - optional int64 what = 2; - - optional int64 condition = 3; - - optional FieldMatcher dimensions_in_what = 4; - - repeated int64 slice_by_state = 8; - - optional TimeUnit bucket = 5; - - repeated MetricConditionLink links = 6; - - repeated MetricStateLink state_link = 9; - - optional FieldMatcher dimensions_in_condition = 7 [deprecated = true]; - - reserved 100; - reserved 101; -} - -message DurationMetric { - optional int64 id = 1; - - optional int64 what = 2; - - optional int64 condition = 3; - - repeated int64 slice_by_state = 9; - - repeated MetricConditionLink links = 4; - - repeated MetricStateLink state_link = 10; - - enum AggregationType { - SUM = 1; - - MAX_SPARSE = 2; - } - optional AggregationType aggregation_type = 5 [default = SUM]; - - optional FieldMatcher dimensions_in_what = 6; - - optional TimeUnit bucket = 7; - - optional FieldMatcher dimensions_in_condition = 8 [deprecated = true]; - - reserved 100; - reserved 101; -} - -message GaugeMetric { - optional int64 id = 1; - - optional int64 what = 2; - - optional int64 trigger_event = 12; - - optional FieldFilter gauge_fields_filter = 3; - - optional int64 condition = 4; - - optional FieldMatcher dimensions_in_what = 5; - - optional FieldMatcher dimensions_in_condition = 8 [deprecated = true]; - - optional TimeUnit bucket = 6; - - repeated MetricConditionLink links = 7; - - enum SamplingType { - RANDOM_ONE_SAMPLE = 1; - ALL_CONDITION_CHANGES = 2 [deprecated = true]; - CONDITION_CHANGE_TO_TRUE = 3; - FIRST_N_SAMPLES = 4; - } - optional SamplingType sampling_type = 9 [default = RANDOM_ONE_SAMPLE] ; - - optional int64 min_bucket_size_nanos = 10; - - optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10]; - - optional int32 max_pull_delay_sec = 13 [default = 30]; - - optional bool split_bucket_for_app_upgrade = 14 [default = true]; - - reserved 100; - reserved 101; -} - -message ValueMetric { - optional int64 id = 1; - - optional int64 what = 2; - - optional FieldMatcher value_field = 3; - - optional int64 condition = 4; - - optional FieldMatcher dimensions_in_what = 5; - - repeated int64 slice_by_state = 18; - - optional TimeUnit bucket = 6; - - repeated MetricConditionLink links = 7; - - repeated MetricStateLink state_link = 19; - - enum AggregationType { - SUM = 1; - MIN = 2; - MAX = 3; - AVG = 4; - } - optional AggregationType aggregation_type = 8 [default = SUM]; - - optional int64 min_bucket_size_nanos = 10; - - optional bool use_absolute_value_on_reset = 11 [default = false]; - - optional bool use_diff = 12; - - optional bool use_zero_default_base = 15 [default = false]; - - enum ValueDirection { - UNKNOWN = 0; - INCREASING = 1; - DECREASING = 2; - ANY = 3; - } - optional ValueDirection value_direction = 13 [default = INCREASING]; - - optional bool skip_zero_diff_output = 14 [default = true]; - - optional int32 max_pull_delay_sec = 16 [default = 30]; - - optional bool split_bucket_for_app_upgrade = 17 [default = true]; - - optional FieldMatcher dimensions_in_condition = 9 [deprecated = true]; - - reserved 100; - reserved 101; -} - -message Alert { - optional int64 id = 1; - - optional int64 metric_id = 2; - - optional int32 num_buckets = 3; - - optional int32 refractory_period_secs = 4; - - optional double trigger_if_sum_gt = 5; -} - -message Alarm { - optional int64 id = 1; - - optional int64 offset_millis = 2; - - optional int64 period_millis = 3; -} - -message IncidentdDetails { - repeated int32 section = 1; - - enum Destination { - AUTOMATIC = 0; - EXPLICIT = 1; - } - optional Destination dest = 2; - - // Package name of the incident report receiver. - optional string receiver_pkg = 3; - - // Class name of the incident report receiver. - optional string receiver_cls = 4; - - optional string alert_description = 5; -} - -message PerfettoDetails { - // The |trace_config| field is a proto-encoded message of type - // perfetto.protos.TraceConfig defined in - // //external/perfetto/protos/perfetto/config/. On device, - // statsd doesn't need to deserialize the message as it's just - // passed binary-encoded to the perfetto cmdline client. - optional bytes trace_config = 1; -} - -message BroadcastSubscriberDetails { - optional int64 subscriber_id = 1; - repeated string cookie = 2; -} - -message Subscription { - optional int64 id = 1; - - enum RuleType { - RULE_TYPE_UNSPECIFIED = 0; - ALARM = 1; - ALERT = 2; - } - optional RuleType rule_type = 2; - - optional int64 rule_id = 3; - - oneof subscriber_information { - IncidentdDetails incidentd_details = 4; - PerfettoDetails perfetto_details = 5; - BroadcastSubscriberDetails broadcast_subscriber_details = 6; - } - - optional float probability_of_informing = 7 [default = 1.1]; - - // This was used for perfprofd historically. - reserved 8; -} - -enum ActivationType { - ACTIVATION_TYPE_UNKNOWN = 0; - ACTIVATE_IMMEDIATELY = 1; - ACTIVATE_ON_BOOT = 2; -} - -message EventActivation { - optional int64 atom_matcher_id = 1; - optional int64 ttl_seconds = 2; - optional int64 deactivation_atom_matcher_id = 3; - optional ActivationType activation_type = 4; -} - -message MetricActivation { - optional int64 metric_id = 1; - - optional ActivationType activation_type = 3 [deprecated = true]; - - repeated EventActivation event_activation = 2; -} - -message PullAtomPackages { - optional int32 atom_id = 1; - - repeated string packages = 2; -} - -message StatsdConfig { - optional int64 id = 1; - - repeated EventMetric event_metric = 2; - - repeated CountMetric count_metric = 3; - - repeated ValueMetric value_metric = 4; - - repeated GaugeMetric gauge_metric = 5; - - repeated DurationMetric duration_metric = 6; - - repeated AtomMatcher atom_matcher = 7; - - repeated Predicate predicate = 8; - - repeated Alert alert = 9; - - repeated Alarm alarm = 10; - - repeated Subscription subscription = 11; - - repeated string allowed_log_source = 12; - - repeated int64 no_report_metric = 13; - - message Annotation { - optional int64 field_int64 = 1; - optional int32 field_int32 = 2; - } - repeated Annotation annotation = 14; - - optional int64 ttl_in_seconds = 15; - - optional bool hash_strings_in_metric_report = 16 [default = true]; - - repeated MetricActivation metric_activation = 17; - - optional bool version_strings_in_metric_report = 18; - - optional bool installer_in_metric_report = 19; - - optional bool persist_locally = 20 [default = false]; - - repeated State state = 21; - - repeated string default_pull_packages = 22; - - repeated PullAtomPackages pull_atom_packages = 23; - - repeated int32 whitelisted_atom_ids = 24; - - // Field number 1000 is reserved for later use. - reserved 1000; -} diff --git a/cmds/statsd/src/statsd_metadata.proto b/cmds/statsd/src/statsd_metadata.proto deleted file mode 100644 index 200b392f7542..000000000000 --- a/cmds/statsd/src/statsd_metadata.proto +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.os.statsd.metadata; - -message ConfigKey { - optional int64 config_id = 1; - optional int32 uid = 2; -} - -message Field { - optional int32 tag = 1; - optional int32 field = 2; -} - -message FieldValue { - optional Field field = 1; - oneof value { - int32 value_int = 2; - int64 value_long = 3; - float value_float = 4; - double value_double = 5; - string value_str = 6; - bytes value_storage = 7; - } -} - -message MetricDimensionKey { - repeated FieldValue dimension_key_in_what = 1; - repeated FieldValue state_values_key = 2; -} - -message AlertDimensionKeyedData { - // The earliest time the alert can be fired again in wall clock time. - optional int32 last_refractory_ends_sec = 1; - optional MetricDimensionKey dimension_key = 2; -} - -message AlertMetadata { - optional int64 alert_id = 1; - repeated AlertDimensionKeyedData alert_dim_keyed_data = 2; -} - -// All metadata for a config in statsd -message StatsMetadata { - optional ConfigKey config_key = 1; - repeated AlertMetadata alert_metadata = 2; -} - -message StatsMetadataList { - repeated StatsMetadata stats_metadata = 1; -} \ No newline at end of file diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp deleted file mode 100644 index dcfdfe3aae53..000000000000 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ /dev/null @@ -1,781 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "android-base/stringprintf.h" -#include "guardrail/StatsdStats.h" -#include "storage/StorageManager.h" -#include "stats_log_util.h" - -#include -#include -#include - -namespace android { -namespace os { -namespace statsd { - -using android::util::FIELD_COUNT_REPEATED; -using android::util::FIELD_TYPE_MESSAGE; -using std::map; - -/** - * NOTE: these directories are protected by SELinux, any changes here must also update - * the SELinux policies. - */ -#define STATS_DATA_DIR "/data/misc/stats-data" -#define STATS_SERVICE_DIR "/data/misc/stats-service" -#define TRAIN_INFO_DIR "/data/misc/train-info" -#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin" - -// Magic word at the start of the train info file, change this if changing the file format -const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf; - -// for ConfigMetricsReportList -const int FIELD_ID_REPORTS = 2; - -std::mutex StorageManager::sTrainInfoMutex; - -using android::base::StringPrintf; -using std::unique_ptr; - -struct FileName { - int64_t mTimestampSec; - int mUid; - int64_t mConfigId; - bool mIsHistory; - string getFullFileName(const char* path) { - return StringPrintf("%s/%lld_%d_%lld%s", path, (long long)mTimestampSec, (int)mUid, - (long long)mConfigId, (mIsHistory ? "_history" : "")); - }; -}; - -string StorageManager::getDataFileName(long wallClockSec, int uid, int64_t id) { - return StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, wallClockSec, uid, - (long long)id); -} - -string StorageManager::getDataHistoryFileName(long wallClockSec, int uid, int64_t id) { - return StringPrintf("%s/%ld_%d_%lld_history", STATS_DATA_DIR, wallClockSec, uid, - (long long)id); -} - -static string findTrainInfoFileNameLocked(const string& trainName) { - unique_ptr dir(opendir(TRAIN_INFO_DIR), closedir); - if (dir == NULL) { - VLOG("Path %s does not exist", TRAIN_INFO_DIR); - return ""; - } - dirent* de; - while ((de = readdir(dir.get()))) { - char* fileName = de->d_name; - if (fileName[0] == '.') continue; - - size_t fileNameLength = strlen(fileName); - if (fileNameLength >= trainName.length()) { - if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(), - trainName.length())) { - return string(fileName); - } - } - } - - return ""; -} - -// Returns array of int64_t which contains timestamp in seconds, uid, -// configID and whether the file is a local history file. -static void parseFileName(char* name, FileName* output) { - int64_t result[3]; - int index = 0; - char* substr = strtok(name, "_"); - while (substr != nullptr && index < 3) { - result[index] = StrToInt64(substr); - index++; - substr = strtok(nullptr, "_"); - } - // When index ends before hitting 3, file name is corrupted. We - // intentionally put -1 at index 0 to indicate the error to caller. - // TODO(b/110563137): consider removing files with unexpected name format. - if (index < 3) { - result[0] = -1; - } - - output->mTimestampSec = result[0]; - output->mUid = result[1]; - output->mConfigId = result[2]; - // check if the file is a local history. - output->mIsHistory = (substr != nullptr && strcmp("history", substr) == 0); -} - -void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) { - int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); - if (fd == -1) { - VLOG("Attempt to access %s but failed", file); - return; - } - trimToFit(STATS_SERVICE_DIR); - trimToFit(STATS_DATA_DIR); - - if (android::base::WriteFully(fd, buffer, numBytes)) { - VLOG("Successfully wrote %s", file); - } else { - ALOGE("Failed to write %s", file); - } - - int result = fchown(fd, AID_STATSD, AID_STATSD); - if (result) { - VLOG("Failed to chown %s to statsd", file); - } - - close(fd); -} - -bool StorageManager::writeTrainInfo(const InstallTrainInfo& trainInfo) { - std::lock_guard lock(sTrainInfoMutex); - - if (trainInfo.trainName.empty()) { - return false; - } - deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str()); - - std::string fileName = - StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long) getWallClockSec(), - trainInfo.trainName.c_str()); - - int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); - if (fd == -1) { - VLOG("Attempt to access %s but failed", fileName.c_str()); - return false; - } - - size_t result; - // Write the magic word - result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC)); - if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) { - VLOG("Failed to wrtie train info magic"); - close(fd); - return false; - } - - // Write the train version - const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode); - result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount); - if (result != trainVersionCodeByteCount) { - VLOG("Failed to wrtie train version code"); - close(fd); - return false; - } - - // Write # of bytes in trainName to file - const size_t trainNameSize = trainInfo.trainName.size(); - const size_t trainNameSizeByteCount = sizeof(trainNameSize); - result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount); - if (result != trainNameSizeByteCount) { - VLOG("Failed to write train name size"); - close(fd); - return false; - } - - // Write trainName to file - result = write(fd, trainInfo.trainName.c_str(), trainNameSize); - if (result != trainNameSize) { - VLOG("Failed to write train name"); - close(fd); - return false; - } - - // Write status to file - const size_t statusByteCount = sizeof(trainInfo.status); - result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount); - if (result != statusByteCount) { - VLOG("Failed to write status"); - close(fd); - return false; - } - - // Write experiment id count to file. - const size_t experimentIdsCount = trainInfo.experimentIds.size(); - const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount); - result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount); - if (result != experimentIdsCountByteCount) { - VLOG("Failed to write experiment id count"); - close(fd); - return false; - } - - // Write experimentIds to file - for (size_t i = 0; i < experimentIdsCount; i++) { - const int64_t experimentId = trainInfo.experimentIds[i]; - const size_t experimentIdByteCount = sizeof(experimentId); - result = write(fd, &experimentId, experimentIdByteCount); - if (result == experimentIdByteCount) { - VLOG("Successfully wrote experiment IDs"); - } else { - VLOG("Failed to write experiment ids"); - close(fd); - return false; - } - } - - // Write bools to file - const size_t boolByteCount = sizeof(trainInfo.requiresStaging); - result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to write requires staging"); - close(fd); - return false; - } - - result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to write rollback enabled"); - close(fd); - return false; - } - - result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to write requires log latency monitor"); - close(fd); - return false; - } - - close(fd); - return true; -} - -bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo) { - std::lock_guard lock(sTrainInfoMutex); - return readTrainInfoLocked(trainName, trainInfo); -} - -bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) { - trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true); - string fileName = findTrainInfoFileNameLocked(trainName); - if (fileName.empty()) { - return false; - } - int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName.c_str()).c_str(), O_RDONLY | O_CLOEXEC); - if (fd == -1) { - VLOG("Failed to open %s", fileName.c_str()); - return false; - } - - // Read the magic word - uint32_t magic; - size_t result = read(fd, &magic, sizeof(magic)); - if (result != sizeof(magic)) { - VLOG("Failed to read train info magic"); - close(fd); - return false; - } - - if (magic != TRAIN_INFO_FILE_MAGIC) { - VLOG("Train info magic was 0x%08x, expected 0x%08x", magic, TRAIN_INFO_FILE_MAGIC); - close(fd); - return false; - } - - // Read the train version code - const size_t trainVersionCodeByteCount(sizeof(trainInfo.trainVersionCode)); - result = read(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount); - if (result != trainVersionCodeByteCount) { - VLOG("Failed to read train version code from train info file"); - close(fd); - return false; - } - - // Read # of bytes taken by trainName in the file. - size_t trainNameSize; - result = read(fd, &trainNameSize, sizeof(size_t)); - if (result != sizeof(size_t)) { - VLOG("Failed to read train name size from train info file"); - close(fd); - return false; - } - - // Read trainName - trainInfo.trainName.resize(trainNameSize); - result = read(fd, trainInfo.trainName.data(), trainNameSize); - if (result != trainNameSize) { - VLOG("Failed to read train name from train info file"); - close(fd); - return false; - } - - // Read status - const size_t statusByteCount = sizeof(trainInfo.status); - result = read(fd, &trainInfo.status, statusByteCount); - if (result != statusByteCount) { - VLOG("Failed to read train status from train info file"); - close(fd); - return false; - } - - // Read experiment ids count. - size_t experimentIdsCount; - result = read(fd, &experimentIdsCount, sizeof(size_t)); - if (result != sizeof(size_t)) { - VLOG("Failed to read train experiment id count from train info file"); - close(fd); - return false; - } - - // Read experimentIds - for (size_t i = 0; i < experimentIdsCount; i++) { - int64_t experimentId; - result = read(fd, &experimentId, sizeof(experimentId)); - if (result != sizeof(experimentId)) { - VLOG("Failed to read train experiment id from train info file"); - close(fd); - return false; - } - trainInfo.experimentIds.push_back(experimentId); - } - - // Read bools - const size_t boolByteCount = sizeof(trainInfo.requiresStaging); - result = read(fd, &trainInfo.requiresStaging, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to read requires requires staging from train info file"); - close(fd); - return false; - } - - result = read(fd, &trainInfo.rollbackEnabled, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to read requires rollback enabled from train info file"); - close(fd); - return false; - } - - result = read(fd, &trainInfo.requiresLowLatencyMonitor, boolByteCount); - if (result != boolByteCount) { - VLOG("Failed to read requires requires low latency monitor from train info file"); - close(fd); - return false; - } - - // Expect to be at EOF. - char c; - result = read(fd, &c, 1); - if (result != 0) { - VLOG("Failed to read train info from file. Did not get expected EOF."); - close(fd); - return false; - } - - VLOG("Read train info file successful"); - close(fd); - return true; -} - -vector StorageManager::readAllTrainInfo() { - std::lock_guard lock(sTrainInfoMutex); - vector trainInfoList; - unique_ptr dir(opendir(TRAIN_INFO_DIR), closedir); - if (dir == NULL) { - VLOG("Directory does not exist: %s", TRAIN_INFO_DIR); - return trainInfoList; - } - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') { - continue; - } - - InstallTrainInfo trainInfo; - bool readSuccess = StorageManager::readTrainInfoLocked(name, trainInfo); - if (!readSuccess) { - continue; - } - trainInfoList.push_back(trainInfo); - } - return trainInfoList; -} - -void StorageManager::deleteFile(const char* file) { - if (remove(file) != 0) { - VLOG("Attempt to delete %s but is not found", file); - } else { - VLOG("Successfully deleted %s", file); - } -} - -void StorageManager::deleteAllFiles(const char* path) { - unique_ptr dir(opendir(path), closedir); - if (dir == NULL) { - VLOG("Directory does not exist: %s", path); - return; - } - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') continue; - deleteFile(StringPrintf("%s/%s", path, name).c_str()); - } -} - -void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) { - unique_ptr dir(opendir(path), closedir); - if (dir == NULL) { - VLOG("Directory does not exist: %s", path); - return; - } - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') { - continue; - } - size_t nameLen = strlen(name); - size_t suffixLen = strlen(suffix); - if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) { - deleteFile(StringPrintf("%s/%s", path, name).c_str()); - } - } -} - -void StorageManager::sendBroadcast(const char* path, - const std::function& sendBroadcast) { - unique_ptr dir(opendir(path), closedir); - if (dir == NULL) { - VLOG("no stats-data directory on disk"); - return; - } - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') continue; - VLOG("file %s", name); - - FileName output; - parseFileName(name, &output); - if (output.mTimestampSec == -1 || output.mIsHistory) continue; - sendBroadcast(ConfigKey((int)output.mUid, output.mConfigId)); - } -} - -bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) { - unique_ptr dir(opendir(STATS_DATA_DIR), closedir); - if (dir == NULL) { - VLOG("Path %s does not exist", STATS_DATA_DIR); - return false; - } - - string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()); - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') continue; - - size_t nameLen = strlen(name); - size_t suffixLen = suffix.length(); - if (suffixLen <= nameLen && - strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) { - // Check again that the file name is parseable. - FileName output; - parseFileName(name, &output); - if (output.mTimestampSec == -1 || output.mIsHistory) continue; - return true; - } - } - return false; -} - -void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto, - bool erase_data, bool isAdb) { - unique_ptr dir(opendir(STATS_DATA_DIR), closedir); - if (dir == NULL) { - VLOG("Path %s does not exist", STATS_DATA_DIR); - return; - } - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - string fileName(name); - if (name[0] == '.') continue; - FileName output; - parseFileName(name, &output); - - if (output.mTimestampSec == -1 || (output.mIsHistory && !isAdb) || - output.mUid != key.GetUid() || output.mConfigId != key.GetId()) { - continue; - } - - auto fullPathName = StringPrintf("%s/%s", STATS_DATA_DIR, fileName.c_str()); - int fd = open(fullPathName.c_str(), O_RDONLY | O_CLOEXEC); - if (fd != -1) { - string content; - if (android::base::ReadFdToString(fd, &content)) { - proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS, - content.c_str(), content.size()); - } - close(fd); - } else { - ALOGE("file cannot be opened"); - } - - if (erase_data) { - remove(fullPathName.c_str()); - } else if (!output.mIsHistory && !isAdb) { - // This means a real data owner has called to get this data. But the config says it - // wants to keep a local history. So now this file must be renamed as a history file. - // So that next time, when owner calls getData() again, this data won't be uploaded - // again. rename returns 0 on success - if (rename(fullPathName.c_str(), (fullPathName + "_history").c_str())) { - ALOGE("Failed to rename file %s", fullPathName.c_str()); - } - } - } -} - -bool StorageManager::readFileToString(const char* file, string* content) { - int fd = open(file, O_RDONLY | O_CLOEXEC); - bool res = false; - if (fd != -1) { - if (android::base::ReadFdToString(fd, content)) { - res = true; - } else { - VLOG("Failed to read file %s\n", file); - } - close(fd); - } - return res; -} - -void StorageManager::readConfigFromDisk(map& configsMap) { - unique_ptr dir(opendir(STATS_SERVICE_DIR), closedir); - if (dir == NULL) { - VLOG("no default config on disk"); - return; - } - trimToFit(STATS_SERVICE_DIR); - - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') continue; - - FileName output; - parseFileName(name, &output); - if (output.mTimestampSec == -1) continue; - string file_name = output.getFullFileName(STATS_SERVICE_DIR); - int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); - if (fd != -1) { - string content; - if (android::base::ReadFdToString(fd, &content)) { - StatsdConfig config; - if (config.ParseFromString(content)) { - configsMap[ConfigKey(output.mUid, output.mConfigId)] = config; - VLOG("map key uid=%lld|configID=%lld", (long long)output.mUid, - (long long)output.mConfigId); - } - } - close(fd); - } - } -} - -bool StorageManager::readConfigFromDisk(const ConfigKey& key, StatsdConfig* config) { - string content; - return config != nullptr && - StorageManager::readConfigFromDisk(key, &content) && config->ParseFromString(content); -} - -bool StorageManager::readConfigFromDisk(const ConfigKey& key, string* content) { - unique_ptr dir(opendir(STATS_SERVICE_DIR), - closedir); - if (dir == NULL) { - VLOG("Directory does not exist: %s", STATS_SERVICE_DIR); - return false; - } - - string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()); - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') { - continue; - } - size_t nameLen = strlen(name); - size_t suffixLen = suffix.length(); - // There can be at most one file that matches this suffix (config key). - if (suffixLen <= nameLen && - strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) { - int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(), - O_RDONLY | O_CLOEXEC); - if (fd != -1) { - if (android::base::ReadFdToString(fd, content)) { - return true; - } - close(fd); - } - } - } - return false; -} - -bool StorageManager::hasIdenticalConfig(const ConfigKey& key, - const vector& config) { - string content; - if (StorageManager::readConfigFromDisk(key, &content)) { - vector vec(content.begin(), content.end()); - if (vec == config) { - return true; - } - } - return false; -} - -void StorageManager::sortFiles(vector* fileNames) { - // Reverse sort to effectively remove from the back (oldest entries). - // This will sort files in reverse-chronological order. Local history files have lower - // priority than regular data files. - sort(fileNames->begin(), fileNames->end(), [](FileInfo& lhs, FileInfo& rhs) { - // first consider if the file is a local history - if (lhs.mIsHistory && !rhs.mIsHistory) { - return false; - } else if (rhs.mIsHistory && !lhs.mIsHistory) { - return true; - } - - // then consider the age. - if (lhs.mFileAgeSec < rhs.mFileAgeSec) { - return true; - } else if (lhs.mFileAgeSec > rhs.mFileAgeSec) { - return false; - } - - // then good luck.... use string::compare - return lhs.mFileName.compare(rhs.mFileName) > 0; - }); -} - -void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) { - unique_ptr dir(opendir(path), closedir); - if (dir == NULL) { - VLOG("Path %s does not exist", path); - return; - } - dirent* de; - int totalFileSize = 0; - vector fileNames; - auto nowSec = getWallClockSec(); - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') continue; - - FileName output; - string file_name; - if (parseTimestampOnly) { - file_name = StringPrintf("%s/%s", path, name); - output.mTimestampSec = StrToInt64(strtok(name, "_")); - output.mIsHistory = false; - } else { - parseFileName(name, &output); - file_name = output.getFullFileName(path); - } - if (output.mTimestampSec == -1) continue; - - // Check for timestamp and delete if it's too old. - long fileAge = nowSec - output.mTimestampSec; - if (fileAge > StatsdStats::kMaxAgeSecond || - (output.mIsHistory && fileAge > StatsdStats::kMaxLocalHistoryAgeSecond)) { - deleteFile(file_name.c_str()); - continue; - } - - ifstream file(file_name.c_str(), ifstream::in | ifstream::binary); - int fileSize = 0; - if (file.is_open()) { - file.seekg(0, ios::end); - fileSize = file.tellg(); - file.close(); - totalFileSize += fileSize; - } - fileNames.emplace_back(file_name, output.mIsHistory, fileSize, fileAge); - } - - if (fileNames.size() > StatsdStats::kMaxFileNumber || - totalFileSize > StatsdStats::kMaxFileSize) { - sortFiles(&fileNames); - } - - // Start removing files from oldest to be under the limit. - while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber || - totalFileSize > StatsdStats::kMaxFileSize)) { - totalFileSize -= fileNames.at(fileNames.size() - 1).mFileSizeBytes; - deleteFile(fileNames.at(fileNames.size() - 1).mFileName.c_str()); - fileNames.pop_back(); - } -} - -void StorageManager::printStats(int outFd) { - printDirStats(outFd, STATS_SERVICE_DIR); - printDirStats(outFd, STATS_DATA_DIR); -} - -void StorageManager::printDirStats(int outFd, const char* path) { - dprintf(outFd, "Printing stats of %s\n", path); - unique_ptr dir(opendir(path), closedir); - if (dir == NULL) { - VLOG("Path %s does not exist", path); - return; - } - dirent* de; - int fileCount = 0; - int totalFileSize = 0; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') { - continue; - } - FileName output; - parseFileName(name, &output); - if (output.mTimestampSec == -1) continue; - dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld, %s", fileCount + 1, - (long long)output.mTimestampSec, output.mUid, (long long)output.mConfigId, - (output.mIsHistory ? "local history" : "")); - string file_name = output.getFullFileName(path); - ifstream file(file_name.c_str(), ifstream::in | ifstream::binary); - if (file.is_open()) { - file.seekg(0, ios::end); - int fileSize = file.tellg(); - file.close(); - dprintf(outFd, ", File Size: %d bytes", fileSize); - totalFileSize += fileSize; - } - dprintf(outFd, "\n"); - fileCount++; - } - dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount, - totalFileSize); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h deleted file mode 100644 index d59046dfbb99..000000000000 --- a/cmds/statsd/src/storage/StorageManager.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef STORAGE_MANAGER_H -#define STORAGE_MANAGER_H - -#include -#include -#include - -#include "packages/UidMap.h" - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoOutputStream; - -class StorageManager : public virtual RefBase { -public: - struct FileInfo { - FileInfo(std::string name, bool isHistory, int fileSize, long fileAge) - : mFileName(name), - mIsHistory(isHistory), - mFileSizeBytes(fileSize), - mFileAgeSec(fileAge) { - } - std::string mFileName; - bool mIsHistory; - int mFileSizeBytes; - long mFileAgeSec; - }; - - /** - * Writes a given byte array as a file to the specified file path. - */ - static void writeFile(const char* file, const void* buffer, int numBytes); - - /** - * Writes train info. - */ - static bool writeTrainInfo(const InstallTrainInfo& trainInfo); - - /** - * Reads train info. - */ - static bool readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo); - - /** - * Reads train info assuming lock is obtained. - */ - static bool readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo); - - /** - * Reads all train info and returns a vector of train info. - */ - static vector readAllTrainInfo(); - - /** - * Reads the file content to the buffer. - */ - static bool readFileToString(const char* file, string* content); - - /** - * Deletes a single file given a file name. - */ - static void deleteFile(const char* file); - - /** - * Deletes all files in a given directory. - */ - static void deleteAllFiles(const char* path); - - /** - * Deletes all files whose name matches with a provided suffix. - */ - static void deleteSuffixedFiles(const char* path, const char* suffix); - - /** - * Send broadcasts to relevant receiver for each data stored on disk. - */ - static void sendBroadcast(const char* path, - const std::function& sendBroadcast); - - /** - * Returns true if there's at least one report on disk. - */ - static bool hasConfigMetricsReport(const ConfigKey& key); - - /** - * Appends the ConfigMetricsReport found on disk to the specifid proto - * and, if erase_data, deletes it from disk. - * - * [isAdb]: if the caller is adb dump. This includes local adb dump or dumpsys by - * bugreport or incidentd. When true, we will append any local history data too. - * - * When - * erase_data=true, isAdb=true: append history data to output, remove all data after read - * erase_data=false, isAdb=true: append history data to output, keep data after read - * erase_data=true, isAdb=false: do not append history data, and remove data after read - * erase_data=false, isAdb=false: do not append history data and *rename* all data files to - * history files. - */ - static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto, - bool erase_data, bool isAdb); - - /** - * Call to load the saved configs from disk. - */ - static void readConfigFromDisk(std::map& configsMap); - - /** - * Call to load the specified config from disk. Returns false if the config file does not - * exist or error occurs when reading the file. - */ - static bool readConfigFromDisk(const ConfigKey& key, StatsdConfig* config); - static bool readConfigFromDisk(const ConfigKey& key, string* config); - - /** - * Trims files in the provided directory to limit the total size, number of - * files, accumulation of outdated files. - */ - static void trimToFit(const char* dir, bool parseTimestampOnly = false); - - /** - * Returns true if there already exists identical configuration on device. - */ - static bool hasIdenticalConfig(const ConfigKey& key, - const vector& config); - - /** - * Prints disk usage statistics related to statsd. - */ - static void printStats(int out); - - static string getDataFileName(long wallClockSec, int uid, int64_t id); - - static string getDataHistoryFileName(long wallClockSec, int uid, int64_t id); - - static void sortFiles(vector* fileNames); - -private: - /** - * Prints disk usage statistics about a directory related to statsd. - */ - static void printDirStats(int out, const char* path); - - static std::mutex sTrainInfoMutex; -}; - -} // namespace statsd -} // namespace os -} // namespace android - -#endif // STORAGE_MANAGER_H diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp deleted file mode 100644 index 1d77513d9d33..000000000000 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false -#include "Log.h" - -#include "FieldValue.h" -#include "IncidentdReporter.h" -#include "packages/UidMap.h" -#include "stats_log_util.h" - -#include -#include - -#include - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoOutputStream; -using std::vector; - -using util::FIELD_TYPE_INT32; -using util::FIELD_TYPE_INT64; -using util::FIELD_TYPE_MESSAGE; -using util::FIELD_TYPE_STRING; - -// field ids in IncidentHeaderProto -const int FIELD_ID_ALERT_ID = 1; -const int FIELD_ID_REASON = 2; -const int FIELD_ID_CONFIG_KEY = 3; -const int FIELD_ID_CONFIG_KEY_UID = 1; -const int FIELD_ID_CONFIG_KEY_ID = 2; - -const int FIELD_ID_TRIGGER_DETAILS = 4; -const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1; -const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1; -const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2; -const int FIELD_ID_METRIC_VALUE_VALUE = 4; - -const int FIELD_ID_PACKAGE_INFO = 3; - -namespace { -void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey, - int64_t metricValue, const ConfigKey& configKey, const string& reason, - vector* protoData) { - ProtoOutputStream headerProto; - headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id); - headerProto.write(FIELD_TYPE_STRING | FIELD_ID_REASON, reason); - uint64_t token = - headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY); - headerProto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_KEY_UID, configKey.GetUid()); - headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId()); - headerProto.end(token); - - token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS); - - // MetricValue trigger_metric = 1; - uint64_t metricToken = - headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC); - // message MetricValue { - // optional int64 metric_id = 1; - headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_METRIC_ID, (long long)metricId); - // optional DimensionsValue dimension_in_what = 2; - uint64_t dimToken = - headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT); - writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto); - headerProto.end(dimToken); - - // deprecated field - // optional DimensionsValue dimension_in_condition = 3; - - // optional int64 value = 4; - headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue); - - // } - headerProto.end(metricToken); - - // write relevant uid package info - std::set uids; - - for (const auto& dim : dimensionKey.getDimensionKeyInWhat().getValues()) { - int uid = getUidIfExists(dim); - // any uid <= 2000 are predefined AID_* - if (uid > 2000) { - uids.insert(uid); - } - } - - if (!uids.empty()) { - uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO); - UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids, - nullptr /*string set*/, &headerProto); - headerProto.end(token); - } - - headerProto.end(token); - - protoData->resize(headerProto.size()); - size_t pos = 0; - sp reader = headerProto.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((*protoData)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } -} -} // namespace - -bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId, - const MetricDimensionKey& dimensionKey, int64_t metricValue, - const ConfigKey& configKey) { - if (config.section_size() == 0) { - VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id, - configKey.GetUid(), (long long)configKey.GetId()); - return false; - } - - AIncidentReportArgs* args = AIncidentReportArgs_init(); - - vector protoData; - getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, - config.alert_description(), &protoData); - AIncidentReportArgs_addHeader(args, protoData.data(), protoData.size()); - - for (int i = 0; i < config.section_size(); i++) { - AIncidentReportArgs_addSection(args, config.section(i)); - } - - uint8_t dest; - switch (config.dest()) { - case IncidentdDetails_Destination_AUTOMATIC: - dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC; - break; - case IncidentdDetails_Destination_EXPLICIT: - dest = INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT; - break; - default: - dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC; - } - AIncidentReportArgs_setPrivacyPolicy(args, dest); - - AIncidentReportArgs_setReceiverPackage(args, config.receiver_pkg().c_str()); - - AIncidentReportArgs_setReceiverClass(args, config.receiver_cls().c_str()); - - int err = AIncidentReportArgs_takeReport(args); - AIncidentReportArgs_delete(args); - - return err == NO_ERROR; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h deleted file mode 100644 index e78a4d98dcd8..000000000000 --- a/cmds/statsd/src/subscriber/IncidentdReporter.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "HashableDimensionKey.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert, IncidentdDetails - -namespace android { -namespace os { -namespace statsd { - -/** - * Calls incidentd to trigger an incident report and put in dropbox for uploading. - */ -bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId, - const MetricDimensionKey& dimensionKey, int64_t metricValue, - const ConfigKey& configKey); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp deleted file mode 100644 index c915ef3bf069..000000000000 --- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "SubscriberReporter.h" - -using std::lock_guard; - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -struct BroadcastSubscriberDeathCookie { - BroadcastSubscriberDeathCookie(const ConfigKey& configKey, int64_t subscriberId, - const shared_ptr& pir): - mConfigKey(configKey), - mSubscriberId(subscriberId), - mPir(pir) {} - - ConfigKey mConfigKey; - int64_t mSubscriberId; - shared_ptr mPir; -}; - -void SubscriberReporter::broadcastSubscriberDied(void* cookie) { - auto cookie_ = static_cast(cookie); - ConfigKey& configKey = cookie_->mConfigKey; - int64_t subscriberId = cookie_->mSubscriberId; - shared_ptr& pir = cookie_->mPir; - - SubscriberReporter& thiz = getInstance(); - - // Erase the mapping from a (config_key, subscriberId) to a pir if the - // mapping exists. - lock_guard lock(thiz.mLock); - auto subscriberMapIt = thiz.mIntentMap.find(configKey); - if (subscriberMapIt != thiz.mIntentMap.end()) { - auto subscriberMap = subscriberMapIt->second; - auto pirIt = subscriberMap.find(subscriberId); - if (pirIt != subscriberMap.end() && pirIt->second == pir) { - subscriberMap.erase(subscriberId); - if (subscriberMap.empty()) { - thiz.mIntentMap.erase(configKey); - } - } - } - - // The death recipient corresponding to this specific pir can never be - // triggered again, so free up resources. - delete cookie_; -} - -SubscriberReporter::SubscriberReporter() : - mBroadcastSubscriberDeathRecipient(AIBinder_DeathRecipient_new(broadcastSubscriberDied)) { -} - -void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey, - int64_t subscriberId, - const shared_ptr& pir) { - VLOG("SubscriberReporter::setBroadcastSubscriber called."); - { - lock_guard lock(mLock); - mIntentMap[configKey][subscriberId] = pir; - } - AIBinder_linkToDeath(pir->asBinder().get(), mBroadcastSubscriberDeathRecipient.get(), - new BroadcastSubscriberDeathCookie(configKey, subscriberId, pir)); -} - -void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey, - int64_t subscriberId) { - VLOG("SubscriberReporter::unsetBroadcastSubscriber called."); - lock_guard lock(mLock); - auto subscriberMapIt = mIntentMap.find(configKey); - if (subscriberMapIt != mIntentMap.end()) { - subscriberMapIt->second.erase(subscriberId); - if (subscriberMapIt->second.empty()) { - mIntentMap.erase(configKey); - } - } -} - -void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey, - const Subscription& subscription, - const MetricDimensionKey& dimKey) const { - // Reminder about ids: - // subscription id - name of the Subscription (that ties the Alert to the broadcast) - // subscription rule_id - the name of the Alert (that triggers the broadcast) - // subscriber_id - name of the PendingIntent to use to send the broadcast - // config uid - the uid that uploaded the config (and therefore gave the PendingIntent, - // although the intent may be to broadcast to a different uid) - // config id - the name of this config (for this particular uid) - - VLOG("SubscriberReporter::alertBroadcastSubscriber called."); - lock_guard lock(mLock); - - if (!subscription.has_broadcast_subscriber_details() - || !subscription.broadcast_subscriber_details().has_subscriber_id()) { - ALOGE("Broadcast subscriber does not have an id."); - return; - } - int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id(); - - vector cookies; - cookies.reserve(subscription.broadcast_subscriber_details().cookie_size()); - for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) { - cookies.push_back(cookie); - } - - auto it1 = mIntentMap.find(configKey); - if (it1 == mIntentMap.end()) { - ALOGW("Cannot inform subscriber for missing config key %s ", configKey.ToString().c_str()); - return; - } - auto it2 = it1->second.find(subscriberId); - if (it2 == it1->second.end()) { - ALOGW("Cannot inform subscriber of config %s for missing subscriberId %lld ", - configKey.ToString().c_str(), (long long)subscriberId); - return; - } - sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey); -} - -void SubscriberReporter::sendBroadcastLocked(const shared_ptr& pir, - const ConfigKey& configKey, - const Subscription& subscription, - const vector& cookies, - const MetricDimensionKey& dimKey) const { - VLOG("SubscriberReporter::sendBroadcastLocked called."); - pir->sendSubscriberBroadcast( - configKey.GetUid(), - configKey.GetId(), - subscription.id(), - subscription.rule_id(), - cookies, - dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel()); -} - -shared_ptr SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey, - int64_t subscriberId) { - lock_guard lock(mLock); - auto subscriberMapIt = mIntentMap.find(configKey); - if (subscriberMapIt == mIntentMap.end()) { - return nullptr; - } - auto pirMapIt = subscriberMapIt->second.find(subscriberId); - if (pirMapIt == subscriberMapIt->second.end()) { - return nullptr; - } - return pirMapIt->second; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h deleted file mode 100644 index 4fe428198e71..000000000000 --- a/cmds/statsd/src/subscriber/SubscriberReporter.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // subscription -#include "HashableDimensionKey.h" - -#include -#include -#include - -using aidl::android::os::IPendingIntentRef; -using std::mutex; -using std::shared_ptr; -using std::string; -using std::unordered_map; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -// Reports information to subscribers. -// Single instance shared across the process. All methods are thread safe. -class SubscriberReporter { -public: - /** Get (singleton) instance of SubscriberReporter. */ - static SubscriberReporter& getInstance() { - static SubscriberReporter subscriberReporter; - return subscriberReporter; - } - - ~SubscriberReporter(){}; - SubscriberReporter(SubscriberReporter const&) = delete; - void operator=(SubscriberReporter const&) = delete; - - /** - * Stores the given intentSender, associating it with the given (configKey, subscriberId) pair. - */ - void setBroadcastSubscriber(const ConfigKey& configKey, - int64_t subscriberId, - const shared_ptr& pir); - - /** - * Erases any intentSender information from the given (configKey, subscriberId) pair. - */ - void unsetBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId); - - /** - * Sends a broadcast via the intentSender previously stored for the - * given (configKey, subscriberId) pair by setBroadcastSubscriber. - * Information about the subscriber, as well as information extracted from the dimKey, is sent. - */ - void alertBroadcastSubscriber(const ConfigKey& configKey, - const Subscription& subscription, - const MetricDimensionKey& dimKey) const; - - shared_ptr getBroadcastSubscriber(const ConfigKey& configKey, - int64_t subscriberId); - -private: - SubscriberReporter(); - - mutable mutex mLock; - - /** Maps -> IPendingIntentRef (which represents a PendingIntent). */ - unordered_map>> mIntentMap; - - /** - * Sends a broadcast via the given intentSender (using mStatsCompanionService), along - * with the information in the other parameters. - */ - void sendBroadcastLocked(const shared_ptr& pir, - const ConfigKey& configKey, - const Subscription& subscription, - const vector& cookies, - const MetricDimensionKey& dimKey) const; - - ::ndk::ScopedAIBinder_DeathRecipient mBroadcastSubscriberDeathRecipient; - - /** - * Death recipient callback that is called when a broadcast subscriber dies. - * The cookie is a pointer to a BroadcastSubscriberDeathCookie. - */ - static void broadcastSubscriberDied(void* cookie); -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/uid_data.proto b/cmds/statsd/src/uid_data.proto deleted file mode 100644 index a6fa26cd412e..000000000000 --- a/cmds/statsd/src/uid_data.proto +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.os.statsd; - -option java_package = "com.android.internal.os"; -option java_outer_classname = "UidDataProto"; - -message ApplicationInfo { - optional int32 uid = 1; - optional int64 version = 2; - optional string version_string = 3; - optional string package_name = 4; - optional string installer = 5; -} - -// StatsServiceCompanion uses the proto to supply statsd with uid-package -// mapping updates. -message UidData { - repeated ApplicationInfo app_info = 1; -} diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.cpp b/cmds/statsd/src/utils/MultiConditionTrigger.cpp deleted file mode 100644 index 43a69337f368..000000000000 --- a/cmds/statsd/src/utils/MultiConditionTrigger.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false // STOPSHIP if true - -#include "MultiConditionTrigger.h" - -#include - -using namespace std; - -namespace android { -namespace os { -namespace statsd { - -MultiConditionTrigger::MultiConditionTrigger(const set& conditionNames, - function trigger) - : mRemainingConditionNames(conditionNames), - mTrigger(trigger), - mCompleted(mRemainingConditionNames.empty()) { - if (mCompleted) { - thread executorThread([this] { mTrigger(); }); - executorThread.detach(); - } -} - -void MultiConditionTrigger::markComplete(const string& conditionName) { - bool doTrigger = false; - { - lock_guard lg(mMutex); - if (mCompleted) { - return; - } - mRemainingConditionNames.erase(conditionName); - mCompleted = mRemainingConditionNames.empty(); - doTrigger = mCompleted; - } - if (doTrigger) { - std::thread executorThread([this] { mTrigger(); }); - executorThread.detach(); - } -} -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.h b/cmds/statsd/src/utils/MultiConditionTrigger.h deleted file mode 100644 index 51f6029915be..000000000000 --- a/cmds/statsd/src/utils/MultiConditionTrigger.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include - -#include -#include - -namespace android { -namespace os { -namespace statsd { - -/** - * This class provides a utility to wait for a set of named conditions to occur. - * - * It will execute the trigger runnable in a detached thread once all conditions have been marked - * true. - */ -class MultiConditionTrigger { -public: - explicit MultiConditionTrigger(const std::set& conditionNames, - std::function trigger); - - MultiConditionTrigger(const MultiConditionTrigger&) = delete; - MultiConditionTrigger& operator=(const MultiConditionTrigger&) = delete; - - // Mark a specific condition as true. If this condition has called markComplete already or if - // the event was not specified in the constructor, the function is a no-op. - void markComplete(const std::string& eventName); - -private: - mutable std::mutex mMutex; - std::set mRemainingConditionNames; - std::function mTrigger; - bool mCompleted; - - FRIEND_TEST(MultiConditionTriggerTest, TestCountDownCalledBySameEventName); -}; -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/statsd_test.xml b/cmds/statsd/statsd_test.xml deleted file mode 100644 index 8f9bb1cb6b2a..000000000000 --- a/cmds/statsd/statsd_test.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - diff --git a/cmds/statsd/tests/AlarmMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp deleted file mode 100644 index 1dc9795dcf16..000000000000 --- a/cmds/statsd/tests/AlarmMonitor_test.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "anomaly/AlarmMonitor.h" - -#include - -using namespace android::os::statsd; -using std::shared_ptr; - -#ifdef __ANDROID__ -TEST(AlarmMonitor, popSoonerThan) { - std::string emptyMetricId; - std::string emptyDimensionId; - unordered_set, SpHash> set; - AlarmMonitor am(2, - [](const shared_ptr&, int64_t){}, - [](const shared_ptr&){}); - - set = am.popSoonerThan(5); - EXPECT_TRUE(set.empty()); - - sp a = new InternalAlarm{10}; - sp b = new InternalAlarm{20}; - sp c = new InternalAlarm{20}; - sp d = new InternalAlarm{30}; - sp e = new InternalAlarm{40}; - sp f = new InternalAlarm{50}; - - am.add(a); - am.add(b); - am.add(c); - am.add(d); - am.add(e); - am.add(f); - - set = am.popSoonerThan(5); - EXPECT_TRUE(set.empty()); - - set = am.popSoonerThan(30); - ASSERT_EQ(4u, set.size()); - EXPECT_EQ(1u, set.count(a)); - EXPECT_EQ(1u, set.count(b)); - EXPECT_EQ(1u, set.count(c)); - EXPECT_EQ(1u, set.count(d)); - - set = am.popSoonerThan(60); - ASSERT_EQ(2u, set.size()); - EXPECT_EQ(1u, set.count(e)); - EXPECT_EQ(1u, set.count(f)); - - set = am.popSoonerThan(80); - ASSERT_EQ(0u, set.size()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp deleted file mode 100644 index 9455304a1af6..000000000000 --- a/cmds/statsd/tests/ConfigManager_test.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/config/ConfigManager.h" -#include "src/metrics/MetricsManager.h" -#include "statsd_test_util.h" - -#include -#include - -#include -#include - -using namespace android; -using namespace android::os::statsd; -using namespace testing; -using namespace std; - -namespace android { -namespace os { -namespace statsd { - -static ostream& operator<<(ostream& os, const StatsdConfig& config) { - return os << "StatsdConfig{id=" << config.id() << "}"; -} - -} // namespace statsd -} // namespace os -} // namespace android - -/** - * Mock ConfigListener - */ -class MockListener : public ConfigListener { -public: - MOCK_METHOD3(OnConfigUpdated, void(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config)); - MOCK_METHOD1(OnConfigRemoved, void(const ConfigKey& key)); -}; - -/** - * Validate that the ConfigKey is the one we wanted. - */ -MATCHER_P2(ConfigKeyEq, uid, id, "") { - return arg.GetUid() == uid && (long long)arg.GetId() == (long long)id; -} - -/** - * Validate that the StatsdConfig is the one we wanted. - */ -MATCHER_P(StatsdConfigEq, id, 0) { - return (long long)arg.id() == (long long)id; -} - -const int64_t testConfigId = 12345; - -/** - * Test the addOrUpdate and remove methods - */ -TEST(ConfigManagerTest, TestAddUpdateRemove) { - sp listener = new StrictMock(); - - sp manager = new ConfigManager(); - manager->AddListener(listener); - - StatsdConfig config91; - config91.set_id(91); - StatsdConfig config92; - config92.set_id(92); - StatsdConfig config93; - config93.set_id(93); - StatsdConfig config94; - config94.set_id(94); - - { - InSequence s; - - manager->StartupForTest(); - - // Add another one - EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), - StatsdConfigEq(91))) - .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config91); - - // Update It - EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), - StatsdConfigEq(92))) - .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config92); - - // Add one with the same uid but a different name - EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("yyy")), - StatsdConfigEq(93))) - .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(1, StringToId("yyy")), config93); - - // Add one with the same name but a different uid - EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(2, StringToId("zzz")), - StatsdConfigEq(94))) - .RetiresOnSaturation(); - manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config94); - - // Remove (1,yyy) - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("yyy")))) - .RetiresOnSaturation(); - manager->RemoveConfig(ConfigKey(1, StringToId("yyy"))); - - // Remove (2,zzz) - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz")))) - .RetiresOnSaturation(); - manager->RemoveConfig(ConfigKey(2, StringToId("zzz"))); - - // Remove (1,zzz) - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("zzz")))) - .RetiresOnSaturation(); - manager->RemoveConfig(ConfigKey(1, StringToId("zzz"))); - - // Remove (2,zzz) again and we shouldn't get the callback - manager->RemoveConfig(ConfigKey(2, StringToId("zzz"))); - } -} - -/** - * Test removing all of the configs for a uid. - */ -TEST(ConfigManagerTest, TestRemoveUid) { - sp listener = new StrictMock(); - - sp manager = new ConfigManager(); - manager->AddListener(listener); - - StatsdConfig config; - - EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _, _)).Times(5); - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("xxx")))); - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("yyy")))); - EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz")))); - - manager->StartupForTest(); - manager->UpdateConfig(ConfigKey(1, StringToId("aaa")), config); - manager->UpdateConfig(ConfigKey(2, StringToId("xxx")), config); - manager->UpdateConfig(ConfigKey(2, StringToId("yyy")), config); - manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config); - manager->UpdateConfig(ConfigKey(3, StringToId("bbb")), config); - - manager->RemoveConfigs(2); -} diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp deleted file mode 100644 index a21eb9b9147f..000000000000 --- a/cmds/statsd/tests/FieldValue_test.cpp +++ /dev/null @@ -1,659 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/matcher_util.h" -#include "src/logd/LogEvent.h" -#include "stats_event.h" -#include "stats_log_util.h" -#include "stats_util.h" -#include "subscriber/SubscriberReporter.h" -#include "tests/statsd_test_util.h" - -#ifdef __ANDROID__ - -using android::util::ProtoReader; - -namespace android { -namespace os { -namespace statsd { - -// These constants must be kept in sync with those in StatsDimensionsValue.java. -const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2; -const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3; -const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6; -const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7; - -namespace { -void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const vector& attributionUids, const vector& attributionTags, - const string& name) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, name.c_str()); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const vector& attributionUids, const vector& attributionTags, - const int32_t value) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeInt32(statsEvent, value); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} -} // anonymous namespace - -TEST(AtomMatcherTest, TestFieldTranslation) { - FieldMatcher matcher1; - matcher1.set_field(10); - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - child->set_position(Position::ANY); - - child = child->add_child(); - child->set_field(1); - - vector output; - translateFieldMatcher(matcher1, &output); - - ASSERT_EQ((size_t)1, output.size()); - - const auto& matcher12 = output[0]; - EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag()); - EXPECT_EQ((int32_t)0x02010001, matcher12.mMatcher.getField()); - EXPECT_EQ((int32_t)0xff7f007f, matcher12.mMask); -} - -TEST(AtomMatcherTest, TestFieldTranslation_ALL) { - FieldMatcher matcher1; - matcher1.set_field(10); - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - child->set_position(Position::ALL); - - child = child->add_child(); - child->set_field(1); - - vector output; - translateFieldMatcher(matcher1, &output); - - ASSERT_EQ((size_t)1, output.size()); - - const auto& matcher12 = output[0]; - EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag()); - EXPECT_EQ((int32_t)0x02010001, matcher12.mMatcher.getField()); - EXPECT_EQ((int32_t)0xff7f7f7f, matcher12.mMask); -} - -TEST(AtomMatcherTest, TestFilter_ALL) { - FieldMatcher matcher1; - matcher1.set_field(10); - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - child->set_position(Position::ALL); - - child->add_child()->set_field(1); - child->add_child()->set_field(2); - - child = matcher1.add_child(); - child->set_field(2); - - vector matchers; - translateFieldMatcher(matcher1, &matchers); - - std::vector attributionUids = {1111, 2222, 3333}; - std::vector attributionTags = {"location1", "location2", "location3"}; - - LogEvent event(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value"); - HashableDimensionKey output; - - filterValues(matchers, event.getValues(), &output); - - ASSERT_EQ((size_t)7, output.getValues().size()); - EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField()); - EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value); - EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField()); - EXPECT_EQ("location1", output.getValues()[1].mValue.str_value); - - EXPECT_EQ((int32_t)0x02010201, output.getValues()[2].mField.getField()); - EXPECT_EQ((int32_t)2222, output.getValues()[2].mValue.int_value); - EXPECT_EQ((int32_t)0x02010202, output.getValues()[3].mField.getField()); - EXPECT_EQ("location2", output.getValues()[3].mValue.str_value); - - EXPECT_EQ((int32_t)0x02010301, output.getValues()[4].mField.getField()); - EXPECT_EQ((int32_t)3333, output.getValues()[4].mValue.int_value); - EXPECT_EQ((int32_t)0x02010302, output.getValues()[5].mField.getField()); - EXPECT_EQ("location3", output.getValues()[5].mValue.str_value); - - EXPECT_EQ((int32_t)0x00020000, output.getValues()[6].mField.getField()); - EXPECT_EQ("some value", output.getValues()[6].mValue.str_value); -} - -TEST(AtomMatcherTest, TestSubDimension) { - HashableDimensionKey dim; - - int pos1[] = {1, 1, 1}; - int pos2[] = {1, 1, 2}; - int pos3[] = {1, 1, 3}; - int pos4[] = {2, 0, 0}; - Field field1(10, pos1, 2); - Field field2(10, pos2, 2); - - Field field3(10, pos3, 2); - Field field4(10, pos4, 0); - - Value value1((int32_t)10025); - Value value2("tag"); - - Value value11((int32_t)10026); - Value value22("tag2"); - - dim.addValue(FieldValue(field1, value1)); - dim.addValue(FieldValue(field2, value2)); - - HashableDimensionKey subDim1; - subDim1.addValue(FieldValue(field1, value1)); - - HashableDimensionKey subDim2; - subDim1.addValue(FieldValue(field2, value2)); - - EXPECT_TRUE(dim.contains(dim)); - EXPECT_TRUE(dim.contains(subDim1)); - EXPECT_TRUE(dim.contains(subDim2)); - - HashableDimensionKey subDim3; - subDim3.addValue(FieldValue(field1, value11)); - EXPECT_FALSE(dim.contains(subDim3)); - - HashableDimensionKey subDim4; - // Empty dimension is always a sub dimension of other dimensions - EXPECT_TRUE(dim.contains(subDim4)); -} - -TEST(AtomMatcherTest, TestMetric2ConditionLink) { - std::vector attributionUids = {1111, 2222, 3333}; - std::vector attributionTags = {"location1", "location2", "location3"}; - - LogEvent event(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event, 10 /*atomId*/, 12345, attributionUids, attributionTags, "some value"); - - FieldMatcher whatMatcher; - whatMatcher.set_field(10); - FieldMatcher* child11 = whatMatcher.add_child(); - child11->set_field(1); - child11->set_position(Position::ANY); - child11 = child11->add_child(); - child11->set_field(1); - - FieldMatcher conditionMatcher; - conditionMatcher.set_field(27); - FieldMatcher* child2 = conditionMatcher.add_child(); - child2->set_field(2); - child2->set_position(Position::LAST); - - child2 = child2->add_child(); - child2->set_field(2); - - Metric2Condition link; - - translateFieldMatcher(whatMatcher, &link.metricFields); - translateFieldMatcher(conditionMatcher, &link.conditionFields); - - ASSERT_EQ((size_t)1, link.metricFields.size()); - EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField()); - EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask); - EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag()); - - ASSERT_EQ((size_t)1, link.conditionFields.size()); - EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField()); - EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask); - EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag()); -} - -TEST(AtomMatcherTest, TestWriteDimensionPath) { - for (auto position : {Position::ANY, Position::ALL, Position::FIRST, Position::LAST}) { - FieldMatcher matcher1; - matcher1.set_field(10); - FieldMatcher* child = matcher1.add_child(); - child->set_field(2); - child->set_position(position); - child->add_child()->set_field(1); - child->add_child()->set_field(3); - - child = matcher1.add_child(); - child->set_field(4); - - child = matcher1.add_child(); - child->set_field(6); - child->add_child()->set_field(2); - - vector matchers; - translateFieldMatcher(matcher1, &matchers); - - android::util::ProtoOutputStream protoOut; - writeDimensionPathToProto(matchers, &protoOut); - - vector outData; - outData.resize(protoOut.size()); - size_t pos = 0; - sp reader = protoOut.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - DimensionsValue result; - ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); - - EXPECT_EQ(10, result.field()); - EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case()); - ASSERT_EQ(3, result.value_tuple().dimensions_value_size()); - - const auto& dim1 = result.value_tuple().dimensions_value(0); - EXPECT_EQ(2, dim1.field()); - ASSERT_EQ(2, dim1.value_tuple().dimensions_value_size()); - - const auto& dim11 = dim1.value_tuple().dimensions_value(0); - EXPECT_EQ(1, dim11.field()); - - const auto& dim12 = dim1.value_tuple().dimensions_value(1); - EXPECT_EQ(3, dim12.field()); - - const auto& dim2 = result.value_tuple().dimensions_value(1); - EXPECT_EQ(4, dim2.field()); - - const auto& dim3 = result.value_tuple().dimensions_value(2); - EXPECT_EQ(6, dim3.field()); - ASSERT_EQ(1, dim3.value_tuple().dimensions_value_size()); - const auto& dim31 = dim3.value_tuple().dimensions_value(0); - EXPECT_EQ(2, dim31.field()); - } -} - -void checkAttributionNodeInDimensionsValueParcel(StatsDimensionsValueParcel& attributionNodeParcel, - int32_t nodeDepthInAttributionChain, - int32_t uid, string tag) { - EXPECT_EQ(attributionNodeParcel.field, nodeDepthInAttributionChain /*position at depth 1*/); - ASSERT_EQ(attributionNodeParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE); - ASSERT_EQ(attributionNodeParcel.tupleValue.size(), 2); - - StatsDimensionsValueParcel uidParcel = attributionNodeParcel.tupleValue[0]; - EXPECT_EQ(uidParcel.field, 1 /*position at depth 2*/); - EXPECT_EQ(uidParcel.valueType, STATS_DIMENSIONS_VALUE_INT_TYPE); - EXPECT_EQ(uidParcel.intValue, uid); - - StatsDimensionsValueParcel tagParcel = attributionNodeParcel.tupleValue[1]; - EXPECT_EQ(tagParcel.field, 2 /*position at depth 2*/); - EXPECT_EQ(tagParcel.valueType, STATS_DIMENSIONS_VALUE_STRING_TYPE); - EXPECT_EQ(tagParcel.stringValue, tag); -} - -// Test conversion of a HashableDimensionKey into a StatsDimensionValueParcel -TEST(AtomMatcherTest, TestSubscriberDimensionWrite) { - int atomId = 10; - // First four fields form an attribution chain - int pos1[] = {1, 1, 1}; - int pos2[] = {1, 1, 2}; - int pos3[] = {1, 2, 1}; - int pos4[] = {1, 2, 2}; - int pos5[] = {2, 1, 1}; - - Field field1(atomId, pos1, /*depth=*/2); - Field field2(atomId, pos2, /*depth=*/2); - Field field3(atomId, pos3, /*depth=*/2); - Field field4(atomId, pos4, /*depth=*/2); - Field field5(atomId, pos5, /*depth=*/0); - - Value value1((int32_t)1); - Value value2("string2"); - Value value3((int32_t)3); - Value value4("string4"); - Value value5((float)5.0); - - HashableDimensionKey dimensionKey; - dimensionKey.addValue(FieldValue(field1, value1)); - dimensionKey.addValue(FieldValue(field2, value2)); - dimensionKey.addValue(FieldValue(field3, value3)); - dimensionKey.addValue(FieldValue(field4, value4)); - dimensionKey.addValue(FieldValue(field5, value5)); - - StatsDimensionsValueParcel rootParcel = dimensionKey.toStatsDimensionsValueParcel(); - EXPECT_EQ(rootParcel.field, atomId); - ASSERT_EQ(rootParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE); - ASSERT_EQ(rootParcel.tupleValue.size(), 2); - - // Check that attribution chain is populated correctly - StatsDimensionsValueParcel attributionChainParcel = rootParcel.tupleValue[0]; - EXPECT_EQ(attributionChainParcel.field, 1 /*position at depth 0*/); - ASSERT_EQ(attributionChainParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE); - ASSERT_EQ(attributionChainParcel.tupleValue.size(), 2); - checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[0], - /*nodeDepthInAttributionChain=*/1, - value1.int_value, value2.str_value); - checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[1], - /*nodeDepthInAttributionChain=*/2, - value3.int_value, value4.str_value); - - // Check that the float is populated correctly - StatsDimensionsValueParcel floatParcel = rootParcel.tupleValue[1]; - EXPECT_EQ(floatParcel.field, 2 /*position at depth 0*/); - EXPECT_EQ(floatParcel.valueType, STATS_DIMENSIONS_VALUE_FLOAT_TYPE); - EXPECT_EQ(floatParcel.floatValue, value5.float_value); -} - -TEST(AtomMatcherTest, TestWriteDimensionToProto) { - HashableDimensionKey dim; - int pos1[] = {1, 1, 1}; - int pos2[] = {1, 1, 2}; - int pos3[] = {1, 1, 3}; - int pos4[] = {2, 0, 0}; - Field field1(10, pos1, 2); - Field field2(10, pos2, 2); - Field field3(10, pos3, 2); - Field field4(10, pos4, 0); - - Value value1((int32_t)10025); - Value value2("tag"); - Value value3((int32_t)987654); - Value value4((int32_t)99999); - - dim.addValue(FieldValue(field1, value1)); - dim.addValue(FieldValue(field2, value2)); - dim.addValue(FieldValue(field3, value3)); - dim.addValue(FieldValue(field4, value4)); - - android::util::ProtoOutputStream protoOut; - writeDimensionToProto(dim, nullptr /* include strings */, &protoOut); - - vector outData; - outData.resize(protoOut.size()); - size_t pos = 0; - sp reader = protoOut.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - DimensionsValue result; - ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); - EXPECT_EQ(10, result.field()); - EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case()); - ASSERT_EQ(2, result.value_tuple().dimensions_value_size()); - - const auto& dim1 = result.value_tuple().dimensions_value(0); - EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, dim1.value_case()); - ASSERT_EQ(3, dim1.value_tuple().dimensions_value_size()); - - const auto& dim11 = dim1.value_tuple().dimensions_value(0); - EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim11.value_case()); - EXPECT_EQ(10025, dim11.value_int()); - - const auto& dim12 = dim1.value_tuple().dimensions_value(1); - EXPECT_EQ(DimensionsValue::ValueCase::kValueStr, dim12.value_case()); - EXPECT_EQ("tag", dim12.value_str()); - - const auto& dim13 = dim1.value_tuple().dimensions_value(2); - EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim13.value_case()); - EXPECT_EQ(987654, dim13.value_int()); - - const auto& dim2 = result.value_tuple().dimensions_value(1); - EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim2.value_case()); - EXPECT_EQ(99999, dim2.value_int()); -} - -TEST(AtomMatcherTest, TestWriteDimensionLeafNodesToProto) { - HashableDimensionKey dim; - int pos1[] = {1, 1, 1}; - int pos2[] = {1, 1, 2}; - int pos3[] = {1, 1, 3}; - int pos4[] = {2, 0, 0}; - Field field1(10, pos1, 2); - Field field2(10, pos2, 2); - Field field3(10, pos3, 2); - Field field4(10, pos4, 0); - - Value value1((int32_t)10025); - Value value2("tag"); - Value value3((int32_t)987654); - Value value4((int64_t)99999); - - dim.addValue(FieldValue(field1, value1)); - dim.addValue(FieldValue(field2, value2)); - dim.addValue(FieldValue(field3, value3)); - dim.addValue(FieldValue(field4, value4)); - - android::util::ProtoOutputStream protoOut; - writeDimensionLeafNodesToProto(dim, 1, nullptr /* include strings */, &protoOut); - - vector outData; - outData.resize(protoOut.size()); - size_t pos = 0; - sp reader = protoOut.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - DimensionsValueTuple result; - ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); - ASSERT_EQ(4, result.dimensions_value_size()); - - const auto& dim1 = result.dimensions_value(0); - EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim1.value_case()); - EXPECT_EQ(10025, dim1.value_int()); - - const auto& dim2 = result.dimensions_value(1); - EXPECT_EQ(DimensionsValue::ValueCase::kValueStr, dim2.value_case()); - EXPECT_EQ("tag", dim2.value_str()); - - const auto& dim3 = result.dimensions_value(2); - EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim3.value_case()); - EXPECT_EQ(987654, dim3.value_int()); - - const auto& dim4 = result.dimensions_value(3); - EXPECT_EQ(DimensionsValue::ValueCase::kValueLong, dim4.value_case()); - EXPECT_EQ(99999, dim4.value_long()); -} - -TEST(AtomMatcherTest, TestWriteAtomToProto) { - std::vector attributionUids = {1111, 2222}; - std::vector attributionTags = {"location1", "location2"}; - - LogEvent event(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event, 4 /*atomId*/, 12345, attributionUids, attributionTags, 999); - - android::util::ProtoOutputStream protoOutput; - writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput); - - vector outData; - outData.resize(protoOutput.size()); - size_t pos = 0; - sp reader = protoOutput.data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - Atom result; - ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); - EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case()); - const auto& atom = result.ble_scan_result_received(); - ASSERT_EQ(2, atom.attribution_node_size()); - EXPECT_EQ(1111, atom.attribution_node(0).uid()); - EXPECT_EQ("location1", atom.attribution_node(0).tag()); - EXPECT_EQ(2222, atom.attribution_node(1).uid()); - EXPECT_EQ("location2", atom.attribution_node(1).tag()); - EXPECT_EQ(999, atom.num_results()); -} - -/* - * Test two Matchers is not a subset of one Matcher. - * Test one Matcher is subset of two Matchers. - */ -TEST(AtomMatcherTest, TestSubsetDimensions1) { - // Initialize first set of matchers - FieldMatcher matcher1; - matcher1.set_field(10); - - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - child->set_position(Position::ALL); - child->add_child()->set_field(1); - child->add_child()->set_field(2); - - vector matchers1; - translateFieldMatcher(matcher1, &matchers1); - ASSERT_EQ(2, matchers1.size()); - - // Initialize second set of matchers - FieldMatcher matcher2; - matcher2.set_field(10); - - child = matcher2.add_child(); - child->set_field(1); - child->set_position(Position::ALL); - child->add_child()->set_field(1); - - vector matchers2; - translateFieldMatcher(matcher2, &matchers2); - ASSERT_EQ(1, matchers2.size()); - - EXPECT_FALSE(subsetDimensions(matchers1, matchers2)); - EXPECT_TRUE(subsetDimensions(matchers2, matchers1)); -} -/* - * Test not a subset with one matching Matcher, one non-matching Matcher. - */ -TEST(AtomMatcherTest, TestSubsetDimensions2) { - // Initialize first set of matchers - FieldMatcher matcher1; - matcher1.set_field(10); - - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - - child = matcher1.add_child(); - child->set_field(2); - - vector matchers1; - translateFieldMatcher(matcher1, &matchers1); - - // Initialize second set of matchers - FieldMatcher matcher2; - matcher2.set_field(10); - - child = matcher2.add_child(); - child->set_field(1); - - child = matcher2.add_child(); - child->set_field(3); - - vector matchers2; - translateFieldMatcher(matcher2, &matchers2); - - EXPECT_FALSE(subsetDimensions(matchers1, matchers2)); -} - -/* - * Test not a subset if parent field is not equal. - */ -TEST(AtomMatcherTest, TestSubsetDimensions3) { - // Initialize first set of matchers - FieldMatcher matcher1; - matcher1.set_field(10); - - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - - vector matchers1; - translateFieldMatcher(matcher1, &matchers1); - - // Initialize second set of matchers - FieldMatcher matcher2; - matcher2.set_field(5); - - child = matcher2.add_child(); - child->set_field(1); - - vector matchers2; - translateFieldMatcher(matcher2, &matchers2); - - EXPECT_FALSE(subsetDimensions(matchers1, matchers2)); -} - -/* - * Test is subset with two matching Matchers. - */ -TEST(AtomMatcherTest, TestSubsetDimensions4) { - // Initialize first set of matchers - FieldMatcher matcher1; - matcher1.set_field(10); - - FieldMatcher* child = matcher1.add_child(); - child->set_field(1); - - child = matcher1.add_child(); - child->set_field(2); - - vector matchers1; - translateFieldMatcher(matcher1, &matchers1); - - // Initialize second set of matchers - FieldMatcher matcher2; - matcher2.set_field(10); - - child = matcher2.add_child(); - child->set_field(1); - - child = matcher2.add_child(); - child->set_field(2); - - child = matcher2.add_child(); - child->set_field(3); - - vector matchers2; - translateFieldMatcher(matcher2, &matchers2); - - EXPECT_TRUE(subsetDimensions(matchers1, matchers2)); - EXPECT_FALSE(subsetDimensions(matchers2, matchers1)); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/HashableDimensionKey_test.cpp b/cmds/statsd/tests/HashableDimensionKey_test.cpp deleted file mode 100644 index 29adcd08a7b8..000000000000 --- a/cmds/statsd/tests/HashableDimensionKey_test.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "src/HashableDimensionKey.h" - -#include - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "statsd_test_util.h" - -#ifdef __ANDROID__ - -using android::util::ProtoReader; - -namespace android { -namespace os { -namespace statsd { - -/** - * Test that #containsLinkedStateValues returns false when the whatKey is - * smaller than the primaryKey. - */ -TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_WhatKeyTooSmall) { - std::vector mMetric2StateLinks; - - int32_t uid1 = 1000; - HashableDimensionKey whatKey = DEFAULT_DIMENSION_KEY; - HashableDimensionKey primaryKey; - getUidProcessKey(uid1, &primaryKey); - - EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, - UID_PROCESS_STATE_ATOM_ID)); -} - -/** - * Test that #containsLinkedStateValues returns false when the linked values - * are not equal. - */ -TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_UnequalLinkedValues) { - int stateAtomId = UID_PROCESS_STATE_ATOM_ID; - - FieldMatcher whatMatcher; - whatMatcher.set_field(util::OVERLAY_STATE_CHANGED); - FieldMatcher* child11 = whatMatcher.add_child(); - child11->set_field(1); - - FieldMatcher stateMatcher; - stateMatcher.set_field(stateAtomId); - FieldMatcher* child21 = stateMatcher.add_child(); - child21->set_field(1); - - std::vector mMetric2StateLinks; - Metric2State ms; - ms.stateAtomId = stateAtomId; - translateFieldMatcher(whatMatcher, &ms.metricFields); - translateFieldMatcher(stateMatcher, &ms.stateFields); - mMetric2StateLinks.push_back(ms); - - int32_t uid1 = 1000; - int32_t uid2 = 1001; - HashableDimensionKey whatKey; - getOverlayKey(uid2, "package", &whatKey); - HashableDimensionKey primaryKey; - getUidProcessKey(uid1, &primaryKey); - - EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId)); -} - -/** - * Test that #containsLinkedStateValues returns false when there is no link - * between the key values. - */ -TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_MissingMetric2StateLinks) { - int stateAtomId = UID_PROCESS_STATE_ATOM_ID; - - std::vector mMetric2StateLinks; - - int32_t uid1 = 1000; - HashableDimensionKey whatKey; - getOverlayKey(uid1, "package", &whatKey); - HashableDimensionKey primaryKey; - getUidProcessKey(uid1, &primaryKey); - - EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId)); -} - -/** - * Test that #containsLinkedStateValues returns true when the key values are - * linked and equal. - */ -TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_AllConditionsMet) { - int stateAtomId = UID_PROCESS_STATE_ATOM_ID; - - FieldMatcher whatMatcher; - whatMatcher.set_field(util::OVERLAY_STATE_CHANGED); - FieldMatcher* child11 = whatMatcher.add_child(); - child11->set_field(1); - - FieldMatcher stateMatcher; - stateMatcher.set_field(stateAtomId); - FieldMatcher* child21 = stateMatcher.add_child(); - child21->set_field(1); - - std::vector mMetric2StateLinks; - Metric2State ms; - ms.stateAtomId = stateAtomId; - translateFieldMatcher(whatMatcher, &ms.metricFields); - translateFieldMatcher(stateMatcher, &ms.stateFields); - mMetric2StateLinks.push_back(ms); - - int32_t uid1 = 1000; - HashableDimensionKey whatKey; - getOverlayKey(uid1, "package", &whatKey); - HashableDimensionKey primaryKey; - getUidProcessKey(uid1, &primaryKey); - - EXPECT_TRUE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId)); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp deleted file mode 100644 index 6264c075426a..000000000000 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ /dev/null @@ -1,809 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include "annotations.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/matcher_util.h" -#include "stats_event.h" -#include "stats_log_util.h" -#include "stats_util.h" -#include "statsd_test_util.h" - -using namespace android::os::statsd; -using std::unordered_map; -using std::vector; - -const int32_t TAG_ID = 123; -const int32_t TAG_ID_2 = 28; // hardcoded tag of atom with uid field -const int FIELD_ID_1 = 1; -const int FIELD_ID_2 = 2; -const int FIELD_ID_3 = 2; - -const int ATTRIBUTION_UID_FIELD_ID = 1; -const int ATTRIBUTION_TAG_FIELD_ID = 2; - - -#ifdef __ANDROID__ - -namespace { - -void makeIntLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const int32_t value) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - AStatsEvent_writeInt32(statsEvent, value); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const float floatValue) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - AStatsEvent_writeFloat(statsEvent, floatValue); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const string& name) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - AStatsEvent_writeString(statsEvent, name.c_str()); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeIntWithBoolAnnotationLogEvent(LogEvent* logEvent, const int32_t atomId, - const int32_t field, const uint8_t annotationId, - const bool annotationValue) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_writeInt32(statsEvent, field); - AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const vector& attributionUids, - const vector& attributionTags, const string& name) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, name.c_str()); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const bool bool1, const bool bool2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - AStatsEvent_writeBool(statsEvent, bool1); - AStatsEvent_writeBool(statsEvent, bool2); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -} // anonymous namespace - -TEST(AtomMatcherTest, TestSimpleMatcher) { - UidMap uidMap; - - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - - LogEvent event(/*uid=*/0, /*pid=*/0); - makeIntLogEvent(&event, TAG_ID, 0, 11); - - // Test - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - // Wrong tag id. - simpleMatcher->set_atom_id(TAG_ID + 1); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestAttributionMatcher) { - UidMap uidMap; - std::vector attributionUids = {1111, 2222, 3333}; - std::vector attributionTags = {"location1", "location2", "location3"}; - - // Set up the log event. - LogEvent event(/*uid=*/0, /*pid=*/0); - makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value"); - - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - - // Match first node. - auto attributionMatcher = simpleMatcher->add_field_value_matcher(); - attributionMatcher->set_field(FIELD_ID_1); - attributionMatcher->set_position(Position::FIRST); - attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( - ATTRIBUTION_TAG_FIELD_ID); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "tag"); - - auto fieldMatcher = simpleMatcher->add_field_value_matcher(); - fieldMatcher->set_field(FIELD_ID_2); - fieldMatcher->set_eq_string("some value"); - - // Tag not matched. - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location3"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - // Match last node. - attributionMatcher->set_position(Position::LAST); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - // Match any node. - attributionMatcher->set_position(Position::ANY); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location2"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location4"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - // Attribution match but primitive field not match. - attributionMatcher->set_position(Position::ANY); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "location2"); - fieldMatcher->set_eq_string("wrong value"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - fieldMatcher->set_eq_string("some value"); - - // Uid match. - attributionMatcher->set_position(Position::ANY); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field( - ATTRIBUTION_UID_FIELD_ID); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - uidMap.updateMap( - 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, - {android::String16("v1"), android::String16("v1"), android::String16("v2"), - android::String16("v1"), android::String16("v2")}, - {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), - android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, - {android::String16(""), android::String16(""), android::String16(""), - android::String16(""), android::String16("")}); - - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::FIRST); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::LAST); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - // Uid + tag. - attributionMatcher->set_position(Position::ANY); - attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( - ATTRIBUTION_TAG_FIELD_ID); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location2"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::FIRST); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location2"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::LAST); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg0"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg1"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location2"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg2"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( - "pkg3"); - attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( - "location1"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestUidFieldMatcher) { - UidMap uidMap; - uidMap.updateMap( - 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, - {android::String16("v1"), android::String16("v1"), android::String16("v2"), - android::String16("v1"), android::String16("v2")}, - {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), - android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, - {android::String16(""), android::String16(""), android::String16(""), - android::String16(""), android::String16("")}); - - // Set up matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - simpleMatcher->add_field_value_matcher()->set_field(1); - simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0"); - - // Make event without is_uid annotation. - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeIntLogEvent(&event1, TAG_ID, 0, 1111); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1)); - - // Make event with is_uid annotation. - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID_2, 1111, ANNOTATION_ID_IS_UID, true); - - // Event has is_uid annotation, so mapping from uid to package name occurs. - simpleMatcher->set_atom_id(TAG_ID_2); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); - - // Event has is_uid annotation, but uid maps to different package name. - simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2)); -} - -TEST(AtomMatcherTest, TestNeqAnyStringMatcher) { - UidMap uidMap; - uidMap.updateMap( - 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, - {android::String16("v1"), android::String16("v1"), android::String16("v2"), - android::String16("v1"), android::String16("v2")}, - {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), - android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, - {android::String16(""), android::String16(""), android::String16(""), - android::String16(""), android::String16("")}); - - std::vector attributionUids = {1111, 2222, 3333, 1066}; - std::vector attributionTags = {"location1", "location2", "location3", "location3"}; - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value"); - - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - - // Match first node. - auto attributionMatcher = simpleMatcher->add_field_value_matcher(); - attributionMatcher->set_field(FIELD_ID_1); - attributionMatcher->set_position(Position::FIRST); - attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( - ATTRIBUTION_UID_FIELD_ID); - auto neqStringList = attributionMatcher->mutable_matches_tuple() - ->mutable_field_value_matcher(0) - ->mutable_neq_any_string(); - neqStringList->add_str_value("pkg2"); - neqStringList->add_str_value("pkg3"); - - auto fieldMatcher = simpleMatcher->add_field_value_matcher(); - fieldMatcher->set_field(FIELD_ID_2); - fieldMatcher->set_eq_string("some value"); - - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - neqStringList->Clear(); - neqStringList->add_str_value("pkg1"); - neqStringList->add_str_value("pkg3"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::ANY); - neqStringList->Clear(); - neqStringList->add_str_value("maps.com"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - neqStringList->Clear(); - neqStringList->add_str_value("PkG3"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::LAST); - neqStringList->Clear(); - neqStringList->add_str_value("AID_STATSD"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestEqAnyStringMatcher) { - UidMap uidMap; - uidMap.updateMap( - 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, - {android::String16("v1"), android::String16("v1"), android::String16("v2"), - android::String16("v1"), android::String16("v2")}, - {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), - android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, - {android::String16(""), android::String16(""), android::String16(""), - android::String16(""), android::String16("")}); - - std::vector attributionUids = {1067, 2222, 3333, 1066}; - std::vector attributionTags = {"location1", "location2", "location3", "location3"}; - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value"); - - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - - // Match first node. - auto attributionMatcher = simpleMatcher->add_field_value_matcher(); - attributionMatcher->set_field(FIELD_ID_1); - attributionMatcher->set_position(Position::FIRST); - attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( - ATTRIBUTION_UID_FIELD_ID); - auto eqStringList = attributionMatcher->mutable_matches_tuple() - ->mutable_field_value_matcher(0) - ->mutable_eq_any_string(); - eqStringList->add_str_value("AID_ROOT"); - eqStringList->add_str_value("AID_INCIDENTD"); - - auto fieldMatcher = simpleMatcher->add_field_value_matcher(); - fieldMatcher->set_field(FIELD_ID_2); - fieldMatcher->set_eq_string("some value"); - - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - attributionMatcher->set_position(Position::ANY); - eqStringList->Clear(); - eqStringList->add_str_value("AID_STATSD"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - eqStringList->Clear(); - eqStringList->add_str_value("pkg1"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - auto normalStringField = fieldMatcher->mutable_eq_any_string(); - normalStringField->add_str_value("some value123"); - normalStringField->add_str_value("some value"); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - normalStringField->Clear(); - normalStringField->add_str_value("AID_STATSD"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - eqStringList->Clear(); - eqStringList->add_str_value("maps.com"); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestBoolMatcher) { - UidMap uidMap; - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - auto keyValue1 = simpleMatcher->add_field_value_matcher(); - keyValue1->set_field(FIELD_ID_1); - auto keyValue2 = simpleMatcher->add_field_value_matcher(); - keyValue2->set_field(FIELD_ID_2); - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - makeBoolLogEvent(&event, TAG_ID, 0, true, false); - - // Test - keyValue1->set_eq_bool(true); - keyValue2->set_eq_bool(false); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - keyValue1->set_eq_bool(false); - keyValue2->set_eq_bool(false); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - keyValue1->set_eq_bool(false); - keyValue2->set_eq_bool(true); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - keyValue1->set_eq_bool(true); - keyValue2->set_eq_bool(true); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestStringMatcher) { - UidMap uidMap; - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - auto keyValue = simpleMatcher->add_field_value_matcher(); - keyValue->set_field(FIELD_ID_1); - keyValue->set_eq_string("some value"); - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - makeStringLogEvent(&event, TAG_ID, 0, "some value"); - - // Test - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestMultiFieldsMatcher) { - UidMap uidMap; - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - auto keyValue1 = simpleMatcher->add_field_value_matcher(); - keyValue1->set_field(FIELD_ID_1); - auto keyValue2 = simpleMatcher->add_field_value_matcher(); - keyValue2->set_field(FIELD_ID_2); - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event, TAG_ID, 0, 2, 3); - - // Test - keyValue1->set_eq_int(2); - keyValue2->set_eq_int(3); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - keyValue1->set_eq_int(2); - keyValue2->set_eq_int(4); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - keyValue1->set_eq_int(4); - keyValue2->set_eq_int(3); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestIntComparisonMatcher) { - UidMap uidMap; - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - - simpleMatcher->set_atom_id(TAG_ID); - auto keyValue = simpleMatcher->add_field_value_matcher(); - keyValue->set_field(FIELD_ID_1); - - // Set up the event - LogEvent event(/*uid=*/0, /*pid=*/0); - makeIntLogEvent(&event, TAG_ID, 0, 11); - - // Test - - // eq_int - keyValue->set_eq_int(10); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_eq_int(11); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_eq_int(12); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - // lt_int - keyValue->set_lt_int(10); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_lt_int(11); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_lt_int(12); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - // lte_int - keyValue->set_lte_int(10); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_lte_int(11); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_lte_int(12); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - - // gt_int - keyValue->set_gt_int(10); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_gt_int(11); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_gt_int(12); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - - // gte_int - keyValue->set_gte_int(10); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_gte_int(11); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue->set_gte_int(12); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -} - -TEST(AtomMatcherTest, TestFloatComparisonMatcher) { - UidMap uidMap; - // Set up the matcher - AtomMatcher matcher; - auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_atom_id(TAG_ID); - - auto keyValue = simpleMatcher->add_field_value_matcher(); - keyValue->set_field(FIELD_ID_1); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeFloatLogEvent(&event1, TAG_ID, 0, 10.1f); - keyValue->set_lt_float(10.0); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1)); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeFloatLogEvent(&event2, TAG_ID, 0, 9.9f); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); - - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeFloatLogEvent(&event3, TAG_ID, 0, 10.1f); - keyValue->set_gt_float(10.0); - EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3)); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - makeFloatLogEvent(&event4, TAG_ID, 0, 9.9f); - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4)); -} - -// Helper for the composite matchers. -void addSimpleMatcher(SimpleAtomMatcher* simpleMatcher, int tag, int key, int val) { - simpleMatcher->set_atom_id(tag); - auto keyValue = simpleMatcher->add_field_value_matcher(); - keyValue->set_field(key); - keyValue->set_eq_int(val); -} - -TEST(AtomMatcherTest, TestAndMatcher) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::AND; - - vector children; - children.push_back(0); - children.push_back(1); - children.push_back(2); - - vector matcherResults; - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kMatched); - - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kMatched); - - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); -} - -TEST(AtomMatcherTest, TestOrMatcher) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::OR; - - vector children; - children.push_back(0); - children.push_back(1); - children.push_back(2); - - vector matcherResults; - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kMatched); - - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kNotMatched); - - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); -} - -TEST(AtomMatcherTest, TestNotMatcher) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NOT; - - vector children; - children.push_back(0); - - vector matcherResults; - matcherResults.push_back(MatchingState::kMatched); - - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kNotMatched); - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); -} - -TEST(AtomMatcherTest, TestNandMatcher) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NAND; - - vector children; - children.push_back(0); - children.push_back(1); - - vector matcherResults; - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kNotMatched); - - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kNotMatched); - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kMatched); - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); -} - -TEST(AtomMatcherTest, TestNorMatcher) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NOR; - - vector children; - children.push_back(0); - children.push_back(1); - - vector matcherResults; - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kNotMatched); - - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kNotMatched); - matcherResults.push_back(MatchingState::kNotMatched); - EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - - matcherResults.clear(); - matcherResults.push_back(MatchingState::kMatched); - matcherResults.push_back(MatchingState::kMatched); - EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); -} -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp deleted file mode 100644 index bde59f497b19..000000000000 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/logd/LogEvent.h" - -#include - -#include "frameworks/proto_logging/stats/atoms.pb.h" -#include "frameworks/proto_logging/stats/enums/stats/launcher/launcher.pb.h" -#include "log/log_event_list.h" -#include "stats_event.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -using std::string; -using std::vector; -using util::ProtoOutputStream; -using util::ProtoReader; - -namespace { - -Field getField(int32_t tag, const vector& pos, int32_t depth, const vector& last) { - Field f(tag, (int32_t*)pos.data(), depth); - - // For loop starts at 1 because the last field at depth 0 is not decorated. - for (int i = 1; i < depth; i++) { - if (last[i]) f.decorateLastPos(i); - } - - return f; -} - -void createIntWithBoolAnnotationLogEvent(LogEvent* logEvent, uint8_t annotationId, - bool annotationValue) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, /*atomId=*/100); - AStatsEvent_writeInt32(statsEvent, 10); - AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - EXPECT_TRUE(logEvent->parseBuffer(buf, size)); - - AStatsEvent_release(statsEvent); -} - -void createIntWithIntAnnotationLogEvent(LogEvent* logEvent, uint8_t annotationId, - int annotationValue) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, /*atomId=*/100); - AStatsEvent_writeInt32(statsEvent, 10); - AStatsEvent_addInt32Annotation(statsEvent, annotationId, annotationValue); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - EXPECT_TRUE(logEvent->parseBuffer(buf, size)); - - AStatsEvent_release(statsEvent); -} - -} // anonymous namespace - -TEST(LogEventTest, TestPrimitiveParsing) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - AStatsEvent_writeInt32(event, 10); - AStatsEvent_writeInt64(event, 0x123456789); - AStatsEvent_writeFloat(event, 2.0); - AStatsEvent_writeBool(event, true); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - - LogEvent logEvent(/*uid=*/1000, /*pid=*/1001); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - - EXPECT_EQ(100, logEvent.GetTagId()); - EXPECT_EQ(1000, logEvent.GetUid()); - EXPECT_EQ(1001, logEvent.GetPid()); - EXPECT_FALSE(logEvent.hasAttributionChain()); - - const vector& values = logEvent.getValues(); - ASSERT_EQ(4, values.size()); - - const FieldValue& int32Item = values[0]; - Field expectedField = getField(100, {1, 1, 1}, 0, {false, false, false}); - EXPECT_EQ(expectedField, int32Item.mField); - EXPECT_EQ(Type::INT, int32Item.mValue.getType()); - EXPECT_EQ(10, int32Item.mValue.int_value); - - const FieldValue& int64Item = values[1]; - expectedField = getField(100, {2, 1, 1}, 0, {false, false, false}); - EXPECT_EQ(expectedField, int64Item.mField); - EXPECT_EQ(Type::LONG, int64Item.mValue.getType()); - EXPECT_EQ(0x123456789, int64Item.mValue.long_value); - - const FieldValue& floatItem = values[2]; - expectedField = getField(100, {3, 1, 1}, 0, {false, false, false}); - EXPECT_EQ(expectedField, floatItem.mField); - EXPECT_EQ(Type::FLOAT, floatItem.mValue.getType()); - EXPECT_EQ(2.0, floatItem.mValue.float_value); - - const FieldValue& boolItem = values[3]; - expectedField = getField(100, {4, 1, 1}, 0, {true, false, false}); - EXPECT_EQ(expectedField, boolItem.mField); - EXPECT_EQ(Type::INT, boolItem.mValue.getType()); // FieldValue does not support boolean type - EXPECT_EQ(1, boolItem.mValue.int_value); - - AStatsEvent_release(event); -} - -TEST(LogEventTest, TestStringAndByteArrayParsing) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - string str = "test"; - AStatsEvent_writeString(event, str.c_str()); - AStatsEvent_writeByteArray(event, (uint8_t*)str.c_str(), str.length()); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - - LogEvent logEvent(/*uid=*/1000, /*pid=*/1001); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - - EXPECT_EQ(100, logEvent.GetTagId()); - EXPECT_EQ(1000, logEvent.GetUid()); - EXPECT_EQ(1001, logEvent.GetPid()); - EXPECT_FALSE(logEvent.hasAttributionChain()); - - const vector& values = logEvent.getValues(); - ASSERT_EQ(2, values.size()); - - const FieldValue& stringItem = values[0]; - Field expectedField = getField(100, {1, 1, 1}, 0, {false, false, false}); - EXPECT_EQ(expectedField, stringItem.mField); - EXPECT_EQ(Type::STRING, stringItem.mValue.getType()); - EXPECT_EQ(str, stringItem.mValue.str_value); - - const FieldValue& storageItem = values[1]; - expectedField = getField(100, {2, 1, 1}, 0, {true, false, false}); - EXPECT_EQ(expectedField, storageItem.mField); - EXPECT_EQ(Type::STORAGE, storageItem.mValue.getType()); - vector expectedValue = {'t', 'e', 's', 't'}; - EXPECT_EQ(expectedValue, storageItem.mValue.storage_value); - - AStatsEvent_release(event); -} - -TEST(LogEventTest, TestEmptyString) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - string empty = ""; - AStatsEvent_writeString(event, empty.c_str()); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - - LogEvent logEvent(/*uid=*/1000, /*pid=*/1001); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - - EXPECT_EQ(100, logEvent.GetTagId()); - EXPECT_EQ(1000, logEvent.GetUid()); - EXPECT_EQ(1001, logEvent.GetPid()); - EXPECT_FALSE(logEvent.hasAttributionChain()); - - const vector& values = logEvent.getValues(); - ASSERT_EQ(1, values.size()); - - const FieldValue& item = values[0]; - Field expectedField = getField(100, {1, 1, 1}, 0, {true, false, false}); - EXPECT_EQ(expectedField, item.mField); - EXPECT_EQ(Type::STRING, item.mValue.getType()); - EXPECT_EQ(empty, item.mValue.str_value); - - AStatsEvent_release(event); -} - -TEST(LogEventTest, TestByteArrayWithNullCharacter) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - uint8_t message[] = {'\t', 'e', '\0', 's', 't'}; - AStatsEvent_writeByteArray(event, message, 5); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - - LogEvent logEvent(/*uid=*/1000, /*pid=*/1001); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - - EXPECT_EQ(100, logEvent.GetTagId()); - EXPECT_EQ(1000, logEvent.GetUid()); - EXPECT_EQ(1001, logEvent.GetPid()); - - const vector& values = logEvent.getValues(); - ASSERT_EQ(1, values.size()); - - const FieldValue& item = values[0]; - Field expectedField = getField(100, {1, 1, 1}, 0, {true, false, false}); - EXPECT_EQ(expectedField, item.mField); - EXPECT_EQ(Type::STORAGE, item.mValue.getType()); - vector expectedValue(message, message + 5); - EXPECT_EQ(expectedValue, item.mValue.storage_value); - - AStatsEvent_release(event); -} - -TEST(LogEventTest, TestAttributionChain) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); - - string tag1 = "tag1"; - string tag2 = "tag2"; - - uint32_t uids[] = {1001, 1002}; - const char* tags[] = {tag1.c_str(), tag2.c_str()}; - - AStatsEvent_writeAttributionChain(event, uids, tags, 2); - AStatsEvent_build(event); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(event, &size); - - LogEvent logEvent(/*uid=*/1000, /*pid=*/1001); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - - EXPECT_EQ(100, logEvent.GetTagId()); - EXPECT_EQ(1000, logEvent.GetUid()); - EXPECT_EQ(1001, logEvent.GetPid()); - - const vector& values = logEvent.getValues(); - ASSERT_EQ(4, values.size()); // 2 per attribution node - - std::pair attrIndexRange; - EXPECT_TRUE(logEvent.hasAttributionChain(&attrIndexRange)); - EXPECT_EQ(0, attrIndexRange.first); - EXPECT_EQ(3, attrIndexRange.second); - - // Check first attribution node - const FieldValue& uid1Item = values[0]; - Field expectedField = getField(100, {1, 1, 1}, 2, {true, false, false}); - EXPECT_EQ(expectedField, uid1Item.mField); - EXPECT_EQ(Type::INT, uid1Item.mValue.getType()); - EXPECT_EQ(1001, uid1Item.mValue.int_value); - - const FieldValue& tag1Item = values[1]; - expectedField = getField(100, {1, 1, 2}, 2, {true, false, true}); - EXPECT_EQ(expectedField, tag1Item.mField); - EXPECT_EQ(Type::STRING, tag1Item.mValue.getType()); - EXPECT_EQ(tag1, tag1Item.mValue.str_value); - - // Check second attribution nodes - const FieldValue& uid2Item = values[2]; - expectedField = getField(100, {1, 2, 1}, 2, {true, true, false}); - EXPECT_EQ(expectedField, uid2Item.mField); - EXPECT_EQ(Type::INT, uid2Item.mValue.getType()); - EXPECT_EQ(1002, uid2Item.mValue.int_value); - - const FieldValue& tag2Item = values[3]; - expectedField = getField(100, {1, 2, 2}, 2, {true, true, true}); - EXPECT_EQ(expectedField, tag2Item.mField); - EXPECT_EQ(Type::STRING, tag2Item.mValue.getType()); - EXPECT_EQ(tag2, tag2Item.mValue.str_value); - - AStatsEvent_release(event); -} - -TEST(LogEventTest, TestAnnotationIdIsUid) { - LogEvent event(/*uid=*/0, /*pid=*/0); - createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_IS_UID, true); - - const vector& values = event.getValues(); - ASSERT_EQ(values.size(), 1); - EXPECT_EQ(event.getUidFieldIndex(), 0); -} - -TEST(LogEventTest, TestAnnotationIdStateNested) { - LogEvent event(/*uid=*/0, /*pid=*/0); - createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_STATE_NESTED, true); - - const vector& values = event.getValues(); - ASSERT_EQ(values.size(), 1); - EXPECT_TRUE(values[0].mAnnotations.isNested()); -} - -TEST(LogEventTest, TestPrimaryFieldAnnotation) { - LogEvent event(/*uid=*/0, /*pid=*/0); - createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_PRIMARY_FIELD, true); - - const vector& values = event.getValues(); - ASSERT_EQ(values.size(), 1); - EXPECT_TRUE(values[0].mAnnotations.isPrimaryField()); -} - -TEST(LogEventTest, TestExclusiveStateAnnotation) { - LogEvent event(/*uid=*/0, /*pid=*/0); - createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_EXCLUSIVE_STATE, true); - - const vector& values = event.getValues(); - ASSERT_EQ(values.size(), 1); - EXPECT_TRUE(values[0].mAnnotations.isExclusiveState()); -} - -TEST(LogEventTest, TestPrimaryFieldFirstUidAnnotation) { - // Event has 10 ints and then an attribution chain - int numInts = 10; - int firstUidInChainIndex = numInts; - string tag1 = "tag1"; - string tag2 = "tag2"; - uint32_t uids[] = {1001, 1002}; - const char* tags[] = {tag1.c_str(), tag2.c_str()}; - - // Construct AStatsEvent - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 100); - for (int i = 0; i < numInts; i++) { - AStatsEvent_writeInt32(statsEvent, 10); - } - AStatsEvent_writeAttributionChain(statsEvent, uids, tags, 2); - AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true); - AStatsEvent_build(statsEvent); - - // Construct LogEvent - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - LogEvent logEvent(/*uid=*/0, /*pid=*/0); - EXPECT_TRUE(logEvent.parseBuffer(buf, size)); - AStatsEvent_release(statsEvent); - - // Check annotation - const vector& values = logEvent.getValues(); - ASSERT_EQ(values.size(), numInts + 4); - EXPECT_TRUE(values[firstUidInChainIndex].mAnnotations.isPrimaryField()); -} - -TEST(LogEventTest, TestResetStateAnnotation) { - int32_t resetState = 10; - LogEvent event(/*uid=*/0, /*pid=*/0); - createIntWithIntAnnotationLogEvent(&event, ANNOTATION_ID_TRIGGER_STATE_RESET, resetState); - - const vector& values = event.getValues(); - ASSERT_EQ(values.size(), 1); - EXPECT_EQ(event.getResetState(), resetState); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/LogReader_test.cpp b/cmds/statsd/tests/LogReader_test.cpp deleted file mode 100644 index 7ce1d6a71c85..000000000000 --- a/cmds/statsd/tests/LogReader_test.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include - -TEST(LogReaderTest, TestNothingAtAll) { - printf("yay!"); -} diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp deleted file mode 100644 index 6259757fe092..000000000000 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ /dev/null @@ -1,799 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include - -#include -#include -#include - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "metrics/metrics_test_helper.h" -#include "src/condition/ConditionTracker.h" -#include "src/matchers/LogMatchingTracker.h" -#include "src/metrics/CountMetricProducer.h" -#include "src/metrics/GaugeMetricProducer.h" -#include "src/metrics/MetricProducer.h" -#include "src/metrics/ValueMetricProducer.h" -#include "src/metrics/metrics_manager_util.h" -#include "src/state/StateManager.h" -#include "statsd_test_util.h" - -using namespace testing; -using android::sp; -using android::os::statsd::Predicate; -using std::map; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -namespace { -const ConfigKey kConfigKey(0, 12345); -const long kAlertId = 3; - -const long timeBaseSec = 1000; - -StatsdConfig buildGoodConfig() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_OFF")); - - simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF")); - - AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(StringToId("SCREEN_IS_ON")); - combination->add_matcher(StringToId("SCREEN_IS_OFF")); - - CountMetric* metric = config.add_count_metric(); - metric->set_id(3); - metric->set_what(StringToId("SCREEN_IS_ON")); - metric->set_bucket(ONE_MINUTE); - metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/); - metric->mutable_dimensions_in_what()->add_child()->set_field(1); - - config.add_no_report_metric(3); - - auto alert = config.add_alert(); - alert->set_id(kAlertId); - alert->set_metric_id(3); - alert->set_num_buckets(10); - alert->set_refractory_period_secs(100); - alert->set_trigger_if_sum_gt(100); - return config; -} - -StatsdConfig buildCircleMatchers() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF")); - - AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(StringToId("SCREEN_IS_ON")); - // Circle dependency - combination->add_matcher(StringToId("SCREEN_ON_OR_OFF")); - - return config; -} - -StatsdConfig buildAlertWithUnknownMetric() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - CountMetric* metric = config.add_count_metric(); - metric->set_id(3); - metric->set_what(StringToId("SCREEN_IS_ON")); - metric->set_bucket(ONE_MINUTE); - metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/); - metric->mutable_dimensions_in_what()->add_child()->set_field(1); - - auto alert = config.add_alert(); - alert->set_id(3); - alert->set_metric_id(2); - alert->set_num_buckets(10); - alert->set_refractory_period_secs(100); - alert->set_trigger_if_sum_gt(100); - return config; -} - -StatsdConfig buildMissingMatchers() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF")); - - AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(StringToId("SCREEN_IS_ON")); - // undefined matcher - combination->add_matcher(StringToId("ABC")); - - return config; -} - -StatsdConfig buildMissingPredicate() { - StatsdConfig config; - config.set_id(12345); - - CountMetric* metric = config.add_count_metric(); - metric->set_id(3); - metric->set_what(StringToId("SCREEN_EVENT")); - metric->set_bucket(ONE_MINUTE); - metric->set_condition(StringToId("SOME_CONDITION")); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_EVENT")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2); - - return config; -} - -StatsdConfig buildDimensionMetricsWithMultiTags() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("BATTERY_VERY_LOW")); - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW")); - simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(3); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("BATTERY_LOW")); - - AtomMatcher_Combination* combination = eventMatcher->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_matcher(StringToId("BATTERY_VERY_LOW")); - combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW")); - - // Count process state changes, slice by uid, while SCREEN_IS_OFF - CountMetric* metric = config.add_count_metric(); - metric->set_id(3); - metric->set_what(StringToId("BATTERY_LOW")); - metric->set_bucket(ONE_MINUTE); - // This case is interesting. We want to dimension across two atoms. - metric->mutable_dimensions_in_what()->add_child()->set_field(1); - - auto alert = config.add_alert(); - alert->set_id(kAlertId); - alert->set_metric_id(3); - alert->set_num_buckets(10); - alert->set_refractory_period_secs(100); - alert->set_trigger_if_sum_gt(100); - return config; -} - -StatsdConfig buildCirclePredicates() { - StatsdConfig config; - config.set_id(12345); - - AtomMatcher* eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_ON")); - - SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - eventMatcher = config.add_atom_matcher(); - eventMatcher->set_id(StringToId("SCREEN_IS_OFF")); - - simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_field_value_matcher()->set_field( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); - - auto condition = config.add_predicate(); - condition->set_id(StringToId("SCREEN_IS_ON")); - SimplePredicate* simplePredicate = condition->mutable_simple_predicate(); - simplePredicate->set_start(StringToId("SCREEN_IS_ON")); - simplePredicate->set_stop(StringToId("SCREEN_IS_OFF")); - - condition = config.add_predicate(); - condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF")); - - Predicate_Combination* combination = condition->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_predicate(StringToId("SCREEN_IS_ON")); - combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF")); - - return config; -} - -StatsdConfig buildConfigWithDifferentPredicates() { - StatsdConfig config; - config.set_id(12345); - - auto pulledAtomMatcher = - CreateSimpleAtomMatcher("SUBSYSTEM_SLEEP", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - auto screenOnAtomMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = screenOnAtomMatcher; - auto screenOffAtomMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = screenOffAtomMatcher; - auto batteryNoneAtomMatcher = CreateBatteryStateNoneMatcher(); - *config.add_atom_matcher() = batteryNoneAtomMatcher; - auto batteryUsbAtomMatcher = CreateBatteryStateUsbMatcher(); - *config.add_atom_matcher() = batteryUsbAtomMatcher; - - // Simple condition with InitialValue set to default (unknown). - auto screenOnUnknownPredicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = screenOnUnknownPredicate; - - // Simple condition with InitialValue set to false. - auto screenOnFalsePredicate = config.add_predicate(); - screenOnFalsePredicate->set_id(StringToId("ScreenIsOnInitialFalse")); - SimplePredicate* simpleScreenOnFalsePredicate = - screenOnFalsePredicate->mutable_simple_predicate(); - simpleScreenOnFalsePredicate->set_start(screenOnAtomMatcher.id()); - simpleScreenOnFalsePredicate->set_stop(screenOffAtomMatcher.id()); - simpleScreenOnFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE); - - // Simple condition with InitialValue set to false. - auto onBatteryFalsePredicate = config.add_predicate(); - onBatteryFalsePredicate->set_id(StringToId("OnBatteryInitialFalse")); - SimplePredicate* simpleOnBatteryFalsePredicate = - onBatteryFalsePredicate->mutable_simple_predicate(); - simpleOnBatteryFalsePredicate->set_start(batteryNoneAtomMatcher.id()); - simpleOnBatteryFalsePredicate->set_stop(batteryUsbAtomMatcher.id()); - simpleOnBatteryFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE); - - // Combination condition with both simple condition InitialValues set to false. - auto screenOnFalseOnBatteryFalsePredicate = config.add_predicate(); - screenOnFalseOnBatteryFalsePredicate->set_id(StringToId("ScreenOnFalseOnBatteryFalse")); - screenOnFalseOnBatteryFalsePredicate->mutable_combination()->set_operation( - LogicalOperation::AND); - addPredicateToPredicateCombination(*screenOnFalsePredicate, - screenOnFalseOnBatteryFalsePredicate); - addPredicateToPredicateCombination(*onBatteryFalsePredicate, - screenOnFalseOnBatteryFalsePredicate); - - // Combination condition with one simple condition InitialValue set to unknown and one set to - // false. - auto screenOnUnknownOnBatteryFalsePredicate = config.add_predicate(); - screenOnUnknownOnBatteryFalsePredicate->set_id(StringToId("ScreenOnUnknowneOnBatteryFalse")); - screenOnUnknownOnBatteryFalsePredicate->mutable_combination()->set_operation( - LogicalOperation::AND); - addPredicateToPredicateCombination(screenOnUnknownPredicate, - screenOnUnknownOnBatteryFalsePredicate); - addPredicateToPredicateCombination(*onBatteryFalsePredicate, - screenOnUnknownOnBatteryFalsePredicate); - - // Simple condition metric with initial value false. - ValueMetric* metric1 = config.add_value_metric(); - metric1->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialFalse")); - metric1->set_what(pulledAtomMatcher.id()); - *metric1->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - metric1->set_bucket(FIVE_MINUTES); - metric1->set_condition(screenOnFalsePredicate->id()); - - // Simple condition metric with initial value unknown. - ValueMetric* metric2 = config.add_value_metric(); - metric2->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialUnknown")); - metric2->set_what(pulledAtomMatcher.id()); - *metric2->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - metric2->set_bucket(FIVE_MINUTES); - metric2->set_condition(screenOnUnknownPredicate.id()); - - // Combination condition metric with initial values false and false. - ValueMetric* metric3 = config.add_value_metric(); - metric3->set_id(StringToId("ValueSubsystemSleepWhileScreenOnFalseDeviceUnpluggedFalse")); - metric3->set_what(pulledAtomMatcher.id()); - *metric3->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - metric3->set_bucket(FIVE_MINUTES); - metric3->set_condition(screenOnFalseOnBatteryFalsePredicate->id()); - - // Combination condition metric with initial values unknown and false. - ValueMetric* metric4 = config.add_value_metric(); - metric4->set_id(StringToId("ValueSubsystemSleepWhileScreenOnUnknownDeviceUnpluggedFalse")); - metric4->set_what(pulledAtomMatcher.id()); - *metric4->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - metric4->set_bucket(FIVE_MINUTES); - metric4->set_condition(screenOnUnknownOnBatteryFalsePredicate->id()); - - return config; -} - -bool isSubset(const set& set1, const set& set2) { - return std::includes(set2.begin(), set2.end(), set1.begin(), set1.end()); -} -} // anonymous namespace - -TEST(MetricsManagerTest, TestInitialConditions) { - UidMap uidMap; - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - StatsdConfig config = buildConfigWithDifferentPredicates(); - set allTagIds; - vector> allAtomMatchers; - vector> allConditionTrackers; - vector> allMetricProducers; - std::vector> allAnomalyTrackers; - std::vector> allAlarmTrackers; - unordered_map> conditionToMetricMap; - unordered_map> trackerToMetricMap; - unordered_map> trackerToConditionMap; - unordered_map> activationAtomTrackerToMetricMap; - unordered_map> deactivationAtomTrackerToMetricMap; - unordered_map alertTrackerMap; - vector metricsWithActivation; - std::set noReportMetricIds; - - EXPECT_TRUE(initStatsdConfig( - kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, - allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, - noReportMetricIds)); - ASSERT_EQ(4u, allMetricProducers.size()); - ASSERT_EQ(5u, allConditionTrackers.size()); - - ConditionKey queryKey; - vector conditionCache(5, ConditionState::kNotEvaluated); - - allConditionTrackers[3]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache); - allConditionTrackers[4]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache); - EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]); - EXPECT_EQ(ConditionState::kFalse, conditionCache[1]); - EXPECT_EQ(ConditionState::kFalse, conditionCache[2]); - EXPECT_EQ(ConditionState::kFalse, conditionCache[3]); - EXPECT_EQ(ConditionState::kUnknown, conditionCache[4]); - - EXPECT_EQ(ConditionState::kFalse, allMetricProducers[0]->mCondition); - EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[1]->mCondition); - EXPECT_EQ(ConditionState::kFalse, allMetricProducers[2]->mCondition); - EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition); -} - -TEST(MetricsManagerTest, TestGoodConfig) { - UidMap uidMap; - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - StatsdConfig config = buildGoodConfig(); - set allTagIds; - vector> allAtomMatchers; - vector> allConditionTrackers; - vector> allMetricProducers; - std::vector> allAnomalyTrackers; - std::vector> allAlarmTrackers; - unordered_map> conditionToMetricMap; - unordered_map> trackerToMetricMap; - unordered_map> trackerToConditionMap; - unordered_map> activationAtomTrackerToMetricMap; - unordered_map> deactivationAtomTrackerToMetricMap; - unordered_map alertTrackerMap; - vector metricsWithActivation; - std::set noReportMetricIds; - - EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, - periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, - allAtomMatchers, allConditionTrackers, allMetricProducers, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - alertTrackerMap, metricsWithActivation, - noReportMetricIds)); - ASSERT_EQ(1u, allMetricProducers.size()); - ASSERT_EQ(1u, allAnomalyTrackers.size()); - ASSERT_EQ(1u, noReportMetricIds.size()); - ASSERT_EQ(1u, alertTrackerMap.size()); - EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end()); - EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0); -} - -TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { - UidMap uidMap; - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - StatsdConfig config = buildDimensionMetricsWithMultiTags(); - set allTagIds; - vector> allAtomMatchers; - vector> allConditionTrackers; - vector> allMetricProducers; - std::vector> allAnomalyTrackers; - std::vector> allAlarmTrackers; - unordered_map> conditionToMetricMap; - unordered_map> trackerToMetricMap; - unordered_map> trackerToConditionMap; - unordered_map> activationAtomTrackerToMetricMap; - unordered_map> deactivationAtomTrackerToMetricMap; - unordered_map alertTrackerMap; - vector metricsWithActivation; - std::set noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, - periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, - allAtomMatchers, allConditionTrackers, allMetricProducers, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - alertTrackerMap, metricsWithActivation, - noReportMetricIds)); -} - -TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { - UidMap uidMap; - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - StatsdConfig config = buildCircleMatchers(); - set allTagIds; - vector> allAtomMatchers; - vector> allConditionTrackers; - vector> allMetricProducers; - std::vector> allAnomalyTrackers; - std::vector> allAlarmTrackers; - unordered_map> conditionToMetricMap; - unordered_map> trackerToMetricMap; - unordered_map> trackerToConditionMap; - unordered_map> activationAtomTrackerToMetricMap; - unordered_map> deactivationAtomTrackerToMetricMap; - unordered_map alertTrackerMap; - vector metricsWithActivation; - std::set noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, - periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, - allAtomMatchers, allConditionTrackers, allMetricProducers, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - alertTrackerMap, metricsWithActivation, - noReportMetricIds)); -} - -TEST(MetricsManagerTest, TestMissingMatchers) { - UidMap uidMap; - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - StatsdConfig config = buildMissingMatchers(); - set allTagIds; - vector> allAtomMatchers; - vector> allConditionTrackers; - vector> allMetricProducers; - std::vector> allAnomalyTrackers; - std::vector> allAlarmTrackers; - unordered_map> conditionToMetricMap; - unordered_map> trackerToMetricMap; - unordered_map> trackerToConditionMap; - unordered_map> activationAtomTrackerToMetricMap; - unordered_map> deactivationAtomTrackerToMetricMap; - unordered_map alertTrackerMap; - vector metricsWithActivation; - std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, - periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, - allAtomMatchers, allConditionTrackers, allMetricProducers, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - alertTrackerMap, metricsWithActivation, - noReportMetricIds)); -} - -TEST(MetricsManagerTest, TestMissingPredicate) { - UidMap uidMap; - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - StatsdConfig config = buildMissingPredicate(); - set allTagIds; - vector> allAtomMatchers; - vector> allConditionTrackers; - vector> allMetricProducers; - std::vector> allAnomalyTrackers; - std::vector> allAlarmTrackers; - unordered_map> conditionToMetricMap; - unordered_map> trackerToMetricMap; - unordered_map> trackerToConditionMap; - unordered_map> activationAtomTrackerToMetricMap; - unordered_map> deactivationAtomTrackerToMetricMap; - unordered_map alertTrackerMap; - vector metricsWithActivation; - std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, - periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, - allAtomMatchers, allConditionTrackers, allMetricProducers, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - alertTrackerMap, metricsWithActivation, noReportMetricIds)); -} - -TEST(MetricsManagerTest, TestCirclePredicateDependency) { - UidMap uidMap; - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - StatsdConfig config = buildCirclePredicates(); - set allTagIds; - vector> allAtomMatchers; - vector> allConditionTrackers; - vector> allMetricProducers; - std::vector> allAnomalyTrackers; - std::vector> allAlarmTrackers; - unordered_map> conditionToMetricMap; - unordered_map> trackerToMetricMap; - unordered_map> trackerToConditionMap; - unordered_map> activationAtomTrackerToMetricMap; - unordered_map> deactivationAtomTrackerToMetricMap; - unordered_map alertTrackerMap; - vector metricsWithActivation; - std::set noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, - periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, - allAtomMatchers, allConditionTrackers, allMetricProducers, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - alertTrackerMap, metricsWithActivation, - noReportMetricIds)); -} - -TEST(MetricsManagerTest, testAlertWithUnknownMetric) { - UidMap uidMap; - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - StatsdConfig config = buildAlertWithUnknownMetric(); - set allTagIds; - vector> allAtomMatchers; - vector> allConditionTrackers; - vector> allMetricProducers; - std::vector> allAnomalyTrackers; - std::vector> allAlarmTrackers; - unordered_map> conditionToMetricMap; - unordered_map> trackerToMetricMap; - unordered_map> trackerToConditionMap; - unordered_map> activationAtomTrackerToMetricMap; - unordered_map> deactivationAtomTrackerToMetricMap; - unordered_map alertTrackerMap; - vector metricsWithActivation; - std::set noReportMetricIds; - - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, - periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, - allAtomMatchers, allConditionTrackers, allMetricProducers, - allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - alertTrackerMap, metricsWithActivation, - noReportMetricIds)); -} - -TEST(MetricsManagerTest, TestLogSources) { - string app1 = "app1"; - set app1Uids = {1111, 11111}; - string app2 = "app2"; - set app2Uids = {2222}; - string app3 = "app3"; - set app3Uids = {3333, 1111}; - - map> pkgToUids; - pkgToUids[app1] = app1Uids; - pkgToUids[app2] = app2Uids; - pkgToUids[app3] = app3Uids; - - int32_t atom1 = 10; - int32_t atom2 = 20; - int32_t atom3 = 30; - sp uidMap = new StrictMock(); - EXPECT_CALL(*uidMap, getAppUid(_)) - .Times(4) - .WillRepeatedly(Invoke([&pkgToUids](const string& pkg) { - const auto& it = pkgToUids.find(pkg); - if (it != pkgToUids.end()) { - return it->second; - } - return set(); - })); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterPullUidProvider(kConfigKey, _)).Times(1); - EXPECT_CALL(*pullerManager, UnregisterPullUidProvider(kConfigKey, _)).Times(1); - - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - - StatsdConfig config = buildGoodConfig(); - config.add_allowed_log_source("AID_SYSTEM"); - config.add_allowed_log_source(app1); - config.add_default_pull_packages("AID_SYSTEM"); - config.add_default_pull_packages("AID_ROOT"); - - const set defaultPullUids = {AID_SYSTEM, AID_ROOT}; - - PullAtomPackages* pullAtomPackages = config.add_pull_atom_packages(); - pullAtomPackages->set_atom_id(atom1); - pullAtomPackages->add_packages(app1); - pullAtomPackages->add_packages(app3); - - pullAtomPackages = config.add_pull_atom_packages(); - pullAtomPackages->set_atom_id(atom2); - pullAtomPackages->add_packages(app2); - pullAtomPackages->add_packages("AID_STATSD"); - - MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap, - pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor); - - EXPECT_TRUE(metricsManager.isConfigValid()); - - ASSERT_EQ(metricsManager.mAllowedUid.size(), 1); - EXPECT_EQ(metricsManager.mAllowedUid[0], AID_SYSTEM); - - ASSERT_EQ(metricsManager.mAllowedPkg.size(), 1); - EXPECT_EQ(metricsManager.mAllowedPkg[0], app1); - - ASSERT_EQ(metricsManager.mAllowedLogSources.size(), 3); - EXPECT_TRUE(isSubset({AID_SYSTEM}, metricsManager.mAllowedLogSources)); - EXPECT_TRUE(isSubset(app1Uids, metricsManager.mAllowedLogSources)); - - ASSERT_EQ(metricsManager.mDefaultPullUids.size(), 2); - EXPECT_TRUE(isSubset(defaultPullUids, metricsManager.mDefaultPullUids)); - ; - - vector atom1Uids = metricsManager.getPullAtomUids(atom1); - ASSERT_EQ(atom1Uids.size(), 5); - set expectedAtom1Uids; - expectedAtom1Uids.insert(defaultPullUids.begin(), defaultPullUids.end()); - expectedAtom1Uids.insert(app1Uids.begin(), app1Uids.end()); - expectedAtom1Uids.insert(app3Uids.begin(), app3Uids.end()); - EXPECT_TRUE(isSubset(expectedAtom1Uids, set(atom1Uids.begin(), atom1Uids.end()))); - - vector atom2Uids = metricsManager.getPullAtomUids(atom2); - ASSERT_EQ(atom2Uids.size(), 4); - set expectedAtom2Uids; - expectedAtom1Uids.insert(defaultPullUids.begin(), defaultPullUids.end()); - expectedAtom1Uids.insert(app2Uids.begin(), app2Uids.end()); - expectedAtom1Uids.insert(AID_STATSD); - EXPECT_TRUE(isSubset(expectedAtom2Uids, set(atom2Uids.begin(), atom2Uids.end()))); - - vector atom3Uids = metricsManager.getPullAtomUids(atom3); - ASSERT_EQ(atom3Uids.size(), 2); - EXPECT_TRUE(isSubset(defaultPullUids, set(atom3Uids.begin(), atom3Uids.end()))); -} - -TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom) { - sp uidMap; - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - - StatsdConfig config = buildGoodConfig(); - config.add_whitelisted_atom_ids(3); - config.add_whitelisted_atom_ids(4); - - MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap, - pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor); - - LogEvent event(0 /* uid */, 0 /* pid */); - CreateNoValuesLogEvent(&event, 10 /* atom id */, 0 /* timestamp */); - EXPECT_FALSE(metricsManager.checkLogCredentials(event)); - - CreateNoValuesLogEvent(&event, 3 /* atom id */, 0 /* timestamp */); - EXPECT_TRUE(metricsManager.checkLogCredentials(event)); - - CreateNoValuesLogEvent(&event, 4 /* atom id */, 0 /* timestamp */); - EXPECT_TRUE(metricsManager.checkLogCredentials(event)); -} - -TEST(MetricsManagerTest, TestWhitelistedAtomStateTracker) { - sp uidMap; - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - - StatsdConfig config = buildGoodConfig(); - config.add_allowed_log_source("AID_SYSTEM"); - config.add_whitelisted_atom_ids(3); - config.add_whitelisted_atom_ids(4); - - State state; - state.set_id(1); - state.set_atom_id(3); - - *config.add_state() = state; - - config.mutable_count_metric(0)->add_slice_by_state(state.id()); - - StateManager::getInstance().clear(); - - MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap, - pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor); - - EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount()); - EXPECT_FALSE(metricsManager.isConfigValid()); -} - -} // namespace statsd -} // namespace os -} // namespace android - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp deleted file mode 100644 index 12cfcc7f4d3f..000000000000 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ /dev/null @@ -1,1886 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "StatsLogProcessor.h" - -#include -#include -#include - -#include "StatsService.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "packages/UidMap.h" -#include "statslog_statsdtest.h" -#include "storage/StorageManager.h" -#include "tests/statsd_test_util.h" - -using namespace android; -using namespace testing; -using ::ndk::SharedRefBase; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoOutputStream; - -#ifdef __ANDROID__ - -/** - * Mock MetricsManager (ByteSize() is called). - */ -class MockMetricsManager : public MetricsManager { -public: - MockMetricsManager() - : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, 1000, new UidMap(), - new StatsPullerManager(), - new AlarmMonitor(10, - [](const shared_ptr&, int64_t) {}, - [](const shared_ptr&) {}), - new AlarmMonitor(10, - [](const shared_ptr&, int64_t) {}, - [](const shared_ptr&) {})) { - } - - MOCK_METHOD0(byteSize, size_t()); - - MOCK_METHOD1(dropData, void(const int64_t dropTimeNs)); -}; - -TEST(StatsLogProcessorTest, TestRateLimitByteSize) { - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp periodicAlarmMonitor; - // Construct the processor with a no-op sendBroadcast function that does nothing. - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }, - [](const int&, const vector&) {return true;}); - - MockMetricsManager mockMetricsManager; - - ConfigKey key(100, 12345); - // Expect only the first flush to trigger a check for byte size since the last two are - // rate-limited. - EXPECT_CALL(mockMetricsManager, byteSize()).Times(1); - p.flushIfNecessaryLocked(key, mockMetricsManager); - p.flushIfNecessaryLocked(key, mockMetricsManager); - p.flushIfNecessaryLocked(key, mockMetricsManager); -} - -TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - int broadcastCount = 0; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - return true; - }, - [](const int&, const vector&) {return true;}); - - MockMetricsManager mockMetricsManager; - - ConfigKey key(100, 12345); - EXPECT_CALL(mockMetricsManager, byteSize()) - .Times(1) - .WillRepeatedly(::testing::Return(int( - StatsdStats::kMaxMetricsBytesPerConfig * .95))); - - // Expect only one broadcast despite always returning a size that should trigger broadcast. - p.flushIfNecessaryLocked(key, mockMetricsManager); - EXPECT_EQ(1, broadcastCount); - - // b/73089712 - // This next call to flush should not trigger a broadcast. - // p.mLastByteSizeTimes.clear(); // Force another check for byte size. - // p.flushIfNecessaryLocked(2, key, mockMetricsManager); - // EXPECT_EQ(1, broadcastCount); -} - -TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - int broadcastCount = 0; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - return true; - }, - [](const int&, const vector&) {return true;}); - - MockMetricsManager mockMetricsManager; - - ConfigKey key(100, 12345); - EXPECT_CALL(mockMetricsManager, byteSize()) - .Times(1) - .WillRepeatedly(::testing::Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); - - EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1); - - // Expect to call the onDumpReport and skip the broadcast. - p.flushIfNecessaryLocked(key, mockMetricsManager); - EXPECT_EQ(0, broadcastCount); -} - -StatsdConfig MakeConfig(bool includeMetric) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - if (includeMetric) { - auto appCrashMatcher = CreateProcessCrashAtomMatcher(); - *config.add_atom_matcher() = appCrashMatcher; - auto countMetric = config.add_count_metric(); - countMetric->set_id(StringToId("AppCrashes")); - countMetric->set_what(appCrashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - } - return config; -} - -TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) { - // Setup simple config key corresponding to empty config. - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")}, - {String16("p1"), String16("p2")}, {String16(""), String16("")}); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - int broadcastCount = 0; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - return true; - }, - [](const int&, const vector&) {return true;}); - ConfigKey key(3, 4); - StatsdConfig config = MakeConfig(true); - p.OnConfigUpdated(0, key, config); - - // Expect to get no metrics, but snapshot specified above in uidmap. - vector bytes; - p.onDumpReport(key, 1, false, true, ADB_DUMP, FAST, &bytes); - - ConfigMetricsReportList output; - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_TRUE(output.reports_size() > 0); - auto uidmap = output.reports(0).uid_map(); - EXPECT_TRUE(uidmap.snapshots_size() > 0); - ASSERT_EQ(2, uidmap.snapshots(0).package_info_size()); -} - -TEST(StatsLogProcessorTest, TestEmptyConfigHasNoUidMap) { - // Setup simple config key corresponding to empty config. - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")}, - {String16("p1"), String16("p2")}, {String16(""), String16("")}); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - int broadcastCount = 0; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - return true; - }, - [](const int&, const vector&) {return true;}); - ConfigKey key(3, 4); - StatsdConfig config = MakeConfig(false); - p.OnConfigUpdated(0, key, config); - - // Expect to get no metrics, but snapshot specified above in uidmap. - vector bytes; - p.onDumpReport(key, 1, false, true, ADB_DUMP, FAST, &bytes); - - ConfigMetricsReportList output; - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_TRUE(output.reports_size() > 0); - EXPECT_FALSE(output.reports(0).has_uid_map()); -} - -TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) { - // Setup simple config key corresponding to empty config. - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - int broadcastCount = 0; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - return true; - }, - [](const int&, const vector&) {return true;}); - ConfigKey key(3, 4); - StatsdConfig config; - auto annotation = config.add_annotation(); - annotation->set_field_int64(1); - annotation->set_field_int32(2); - config.add_allowed_log_source("AID_ROOT"); - p.OnConfigUpdated(1, key, config); - - // Expect to get no metrics, but snapshot specified above in uidmap. - vector bytes; - p.onDumpReport(key, 1, false, true, ADB_DUMP, FAST, &bytes); - - ConfigMetricsReportList output; - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_TRUE(output.reports_size() > 0); - auto report = output.reports(0); - ASSERT_EQ(1, report.annotation_size()); - EXPECT_EQ(1, report.annotation(0).field_int64()); - EXPECT_EQ(2, report.annotation(0).field_int32()); -} - -TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) { - // Setup a simple config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = wakelockAcquireMatcher; - - auto countMetric = config.add_count_metric(); - countMetric->set_id(123456); - countMetric->set_what(wakelockAcquireMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - - ConfigKey cfgKey; - sp processor = CreateStatsLogProcessor(1, 1, config, cfgKey); - - std::vector attributionUids = {111}; - std::vector attributionTags = {"App1"}; - std::unique_ptr event = - CreateAcquireWakelockEvent(2 /*timestamp*/, attributionUids, attributionTags, "wl1"); - processor->OnLogEvent(event.get()); - - vector bytes; - ConfigMetricsReportList output; - - // Dump report WITHOUT erasing data. - processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST, - &bytes); - output.ParseFromArray(bytes.data(), bytes.size()); - ASSERT_EQ(output.reports_size(), 1); - ASSERT_EQ(output.reports(0).metrics_size(), 1); - ASSERT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1); - - // Dump report WITH erasing data. There should be data since we didn't previously erase it. - processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes); - output.ParseFromArray(bytes.data(), bytes.size()); - ASSERT_EQ(output.reports_size(), 1); - ASSERT_EQ(output.reports(0).metrics_size(), 1); - ASSERT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1); - - // Dump report again. There should be no data since we erased it. - processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes); - output.ParseFromArray(bytes.data(), bytes.size()); - // We don't care whether statsd has a report, as long as it has no count metrics in it. - bool noData = output.reports_size() == 0 || output.reports(0).metrics_size() == 0 || - output.reports(0).metrics(0).count_metrics().data_size() == 0; - EXPECT_TRUE(noData); -} - -TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate) { - // Setup simple config key corresponding to empty config. - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - StatsLogProcessor p( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }, - [](const int&, const vector&) { return true; }); - ConfigKey key(3, 4); - StatsdConfig config = MakeConfig(false); - p.OnConfigUpdated(0, key, config); - EXPECT_NE(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end()); - - config.add_default_pull_packages("AID_STATSD"); - p.OnConfigUpdated(5, key, config); - EXPECT_NE(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end()); - - p.OnConfigRemoved(key); - EXPECT_EQ(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end()); -} - -TEST(StatsLogProcessorTest, InvalidConfigRemoved) { - // Setup simple config key corresponding to empty config. - StatsdStats::getInstance().reset(); - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")}, - {String16("p1"), String16("p2")}, {String16(""), String16("")}); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }, - [](const int&, const vector&) {return true;}); - ConfigKey key(3, 4); - StatsdConfig config = MakeConfig(true); - p.OnConfigUpdated(0, key, config); - EXPECT_EQ(1, p.mMetricsManagers.size()); - EXPECT_NE(p.mMetricsManagers.find(key), p.mMetricsManagers.end()); - // Cannot assert the size of mConfigStats since it is static and does not get cleared on reset. - EXPECT_NE(StatsdStats::getInstance().mConfigStats.end(), - StatsdStats::getInstance().mConfigStats.find(key)); - EXPECT_EQ(0, StatsdStats::getInstance().mIceBox.size()); - - StatsdConfig invalidConfig = MakeConfig(true); - invalidConfig.clear_allowed_log_source(); - p.OnConfigUpdated(0, key, invalidConfig); - EXPECT_EQ(0, p.mMetricsManagers.size()); - // The current configs should not contain the invalid config. - EXPECT_EQ(StatsdStats::getInstance().mConfigStats.end(), - StatsdStats::getInstance().mConfigStats.find(key)); - // Both "config" and "invalidConfig" should be in the icebox. - EXPECT_EQ(2, StatsdStats::getInstance().mIceBox.size()); - -} - - -TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { - int uid = 1111; - - // Setup a simple config, no activation - StatsdConfig config1; - int64_t cfgId1 = 12341; - config1.set_id(cfgId1); - config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - *config1.add_atom_matcher() = wakelockAcquireMatcher; - - long metricId1 = 1234561; - long metricId2 = 1234562; - auto countMetric1 = config1.add_count_metric(); - countMetric1->set_id(metricId1); - countMetric1->set_what(wakelockAcquireMatcher.id()); - countMetric1->set_bucket(FIVE_MINUTES); - - auto countMetric2 = config1.add_count_metric(); - countMetric2->set_id(metricId2); - countMetric2->set_what(wakelockAcquireMatcher.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - ConfigKey cfgKey1(uid, cfgId1); - - // Add another config, with two metrics, one with activation - StatsdConfig config2; - int64_t cfgId2 = 12342; - config2.set_id(cfgId2); - config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config2.add_atom_matcher() = wakelockAcquireMatcher; - - long metricId3 = 1234561; - long metricId4 = 1234562; - - auto countMetric3 = config2.add_count_metric(); - countMetric3->set_id(metricId3); - countMetric3->set_what(wakelockAcquireMatcher.id()); - countMetric3->set_bucket(FIVE_MINUTES); - - auto countMetric4 = config2.add_count_metric(); - countMetric4->set_id(metricId4); - countMetric4->set_what(wakelockAcquireMatcher.id()); - countMetric4->set_bucket(FIVE_MINUTES); - - auto metric3Activation = config2.add_metric_activation(); - metric3Activation->set_metric_id(metricId3); - metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY); - auto metric3ActivationTrigger = metric3Activation->add_event_activation(); - metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric3ActivationTrigger->set_ttl_seconds(100); - - ConfigKey cfgKey2(uid, cfgId2); - - // Add another config, with two metrics, both with activations - StatsdConfig config3; - int64_t cfgId3 = 12343; - config3.set_id(cfgId3); - config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config3.add_atom_matcher() = wakelockAcquireMatcher; - - long metricId5 = 1234565; - long metricId6 = 1234566; - auto countMetric5 = config3.add_count_metric(); - countMetric5->set_id(metricId5); - countMetric5->set_what(wakelockAcquireMatcher.id()); - countMetric5->set_bucket(FIVE_MINUTES); - - auto countMetric6 = config3.add_count_metric(); - countMetric6->set_id(metricId6); - countMetric6->set_what(wakelockAcquireMatcher.id()); - countMetric6->set_bucket(FIVE_MINUTES); - - auto metric5Activation = config3.add_metric_activation(); - metric5Activation->set_metric_id(metricId5); - metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY); - auto metric5ActivationTrigger = metric5Activation->add_event_activation(); - metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric5ActivationTrigger->set_ttl_seconds(100); - - auto metric6Activation = config3.add_metric_activation(); - metric6Activation->set_metric_id(metricId6); - metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY); - auto metric6ActivationTrigger = metric6Activation->add_event_activation(); - metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric6ActivationTrigger->set_ttl_seconds(200); - - ConfigKey cfgKey3(uid, cfgId3); - - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - vector activeConfigsBroadcast; - - long timeBase1 = 1; - int broadcastCount = 0; - StatsLogProcessor processor( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, timeBase1, - [](const ConfigKey& key) { return true; }, - [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, - const vector& activeConfigs) { - broadcastCount++; - EXPECT_EQ(broadcastUid, uid); - activeConfigsBroadcast.clear(); - activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), - activeConfigs.end()); - return true; - }); - - processor.OnConfigUpdated(1, cfgKey1, config1); - processor.OnConfigUpdated(2, cfgKey2, config2); - processor.OnConfigUpdated(3, cfgKey3, config3); - - ASSERT_EQ(3, processor.mMetricsManagers.size()); - - // Expect the first config and both metrics in it to be active. - auto it = processor.mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor.mMetricsManagers.end()); - auto& metricsManager1 = it->second; - EXPECT_TRUE(metricsManager1->isActive()); - - auto metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer1 = *metricIt; - EXPECT_TRUE(metricProducer1->isActive()); - - metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer2 = *metricIt; - EXPECT_TRUE(metricProducer2->isActive()); - - // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active. - it = processor.mMetricsManagers.find(cfgKey2); - EXPECT_TRUE(it != processor.mMetricsManagers.end()); - auto& metricsManager2 = it->second; - EXPECT_TRUE(metricsManager2->isActive()); - - metricIt = metricsManager2->mAllMetricProducers.begin(); - for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId3) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end()); - auto& metricProducer3 = *metricIt; - EXPECT_FALSE(metricProducer3->isActive()); - - metricIt = metricsManager2->mAllMetricProducers.begin(); - for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId4) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end()); - auto& metricProducer4 = *metricIt; - EXPECT_TRUE(metricProducer4->isActive()); - - // Expect the third config and both metrics in it to be inactive. - it = processor.mMetricsManagers.find(cfgKey3); - EXPECT_TRUE(it != processor.mMetricsManagers.end()); - auto& metricsManager3 = it->second; - EXPECT_FALSE(metricsManager3->isActive()); - - metricIt = metricsManager3->mAllMetricProducers.begin(); - for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId5) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end()); - auto& metricProducer5 = *metricIt; - EXPECT_FALSE(metricProducer5->isActive()); - - metricIt = metricsManager3->mAllMetricProducers.begin(); - for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId6) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end()); - auto& metricProducer6 = *metricIt; - EXPECT_FALSE(metricProducer6->isActive()); - - // No broadcast for active configs should have happened yet. - EXPECT_EQ(broadcastCount, 0); - - // Activate all 3 metrics that were not active. - std::vector attributionUids = {111}; - std::vector attributionTags = {"App1"}; - std::unique_ptr event = - CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); - processor.OnLogEvent(event.get()); - - // Assert that all 3 configs are active. - EXPECT_TRUE(metricsManager1->isActive()); - EXPECT_TRUE(metricsManager2->isActive()); - EXPECT_TRUE(metricsManager3->isActive()); - - // A broadcast should have happened, and all 3 configs should be active in the broadcast. - EXPECT_EQ(broadcastCount, 1); - ASSERT_EQ(activeConfigsBroadcast.size(), 3); - EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) != - activeConfigsBroadcast.end()); - EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) != - activeConfigsBroadcast.end()); - EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) != - activeConfigsBroadcast.end()); - - // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns. - int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; - processor.SaveActiveConfigsToDisk(shutDownTime); - const int64_t ttl3 = event->GetElapsedTimestampNs() + - metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; - const int64_t ttl5 = event->GetElapsedTimestampNs() + - metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; - const int64_t ttl6 = event->GetElapsedTimestampNs() + - metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; - - // Create a second StatsLogProcessor and push the same 3 configs. - long timeBase2 = 1000; - sp processor2 = - CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); - processor2->OnConfigUpdated(timeBase2, cfgKey2, config2); - processor2->OnConfigUpdated(timeBase2, cfgKey3, config3); - - ASSERT_EQ(3, processor2->mMetricsManagers.size()); - - // First config and both metrics are active. - it = processor2->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1001 = it->second; - EXPECT_TRUE(metricsManager1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1001 = *metricIt; - EXPECT_TRUE(metricProducer1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1002 = *metricIt; - EXPECT_TRUE(metricProducer1002->isActive()); - - // Second config is active. Metric 3 is inactive, metric 4 is active. - it = processor2->mMetricsManagers.find(cfgKey2); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1002 = it->second; - EXPECT_TRUE(metricsManager1002->isActive()); - - metricIt = metricsManager1002->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId3) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end()); - auto& metricProducer1003 = *metricIt; - EXPECT_FALSE(metricProducer1003->isActive()); - - metricIt = metricsManager1002->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId4) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end()); - auto& metricProducer1004 = *metricIt; - EXPECT_TRUE(metricProducer1004->isActive()); - - // Config 3 is inactive. both metrics are inactive. - it = processor2->mMetricsManagers.find(cfgKey3); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1003 = it->second; - EXPECT_FALSE(metricsManager1003->isActive()); - ASSERT_EQ(2, metricsManager1003->mAllMetricProducers.size()); - - metricIt = metricsManager1003->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId5) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end()); - auto& metricProducer1005 = *metricIt; - EXPECT_FALSE(metricProducer1005->isActive()); - - metricIt = metricsManager1003->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId6) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end()); - auto& metricProducer1006 = *metricIt; - EXPECT_FALSE(metricProducer1006->isActive()); - - // Assert that all 3 metrics with activation are inactive and that the ttls were properly set. - EXPECT_FALSE(metricProducer1003->isActive()); - const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second; - EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); - EXPECT_EQ(0, activation1003->start_ns); - EXPECT_FALSE(metricProducer1005->isActive()); - const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second; - EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns); - EXPECT_EQ(0, activation1005->start_ns); - EXPECT_FALSE(metricProducer1006->isActive()); - const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second; - EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns); - EXPECT_EQ(0, activation1006->start_ns); - - processor2->LoadActiveConfigsFromDisk(); - - // After loading activations from disk, assert that all 3 metrics are active. - EXPECT_TRUE(metricProducer1003->isActive()); - EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns); - EXPECT_TRUE(metricProducer1005->isActive()); - EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns); - EXPECT_TRUE(metricProducer1006->isActive()); - EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns); - - // Make sure no more broadcasts have happened. - EXPECT_EQ(broadcastCount, 1); -} - -TEST(StatsLogProcessorTest, TestActivationOnBoot) { - int uid = 1111; - - StatsdConfig config1; - config1.set_id(12341); - config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - *config1.add_atom_matcher() = wakelockAcquireMatcher; - - long metricId1 = 1234561; - long metricId2 = 1234562; - auto countMetric1 = config1.add_count_metric(); - countMetric1->set_id(metricId1); - countMetric1->set_what(wakelockAcquireMatcher.id()); - countMetric1->set_bucket(FIVE_MINUTES); - - auto countMetric2 = config1.add_count_metric(); - countMetric2->set_id(metricId2); - countMetric2->set_what(wakelockAcquireMatcher.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - auto metric1Activation = config1.add_metric_activation(); - metric1Activation->set_metric_id(metricId1); - metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); - auto metric1ActivationTrigger = metric1Activation->add_event_activation(); - metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric1ActivationTrigger->set_ttl_seconds(100); - - ConfigKey cfgKey1(uid, 12341); - long timeBase1 = 1; - sp processor = - CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); - - ASSERT_EQ(1, processor->mMetricsManagers.size()); - auto it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); - auto& metricsManager1 = it->second; - EXPECT_TRUE(metricsManager1->isActive()); - - auto metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer1 = *metricIt; - EXPECT_FALSE(metricProducer1->isActive()); - - metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer2 = *metricIt; - EXPECT_TRUE(metricProducer2->isActive()); - - const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second; - EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kNotActive, activation1->state); - - std::vector attributionUids = {111}; - std::vector attributionTags = {"App1"}; - std::unique_ptr event = - CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); - processor->OnLogEvent(event.get()); - - EXPECT_FALSE(metricProducer1->isActive()); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1->state); - - int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; - processor->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_FALSE(metricProducer1->isActive()); - const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC; - - long timeBase2 = 1000; - sp processor2 = - CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); - - ASSERT_EQ(1, processor2->mMetricsManagers.size()); - it = processor2->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1001 = it->second; - EXPECT_TRUE(metricsManager1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1001 = *metricIt; - EXPECT_FALSE(metricProducer1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1002 = *metricIt; - EXPECT_TRUE(metricProducer1002->isActive()); - - const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second; - EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); - EXPECT_EQ(0, activation1001->start_ns); - EXPECT_EQ(kNotActive, activation1001->state); - - processor2->LoadActiveConfigsFromDisk(); - - EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns); - EXPECT_EQ(kActive, activation1001->state); -} - -TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { - int uid = 1111; - - // Create config with 2 metrics: - // Metric 1: Activate on boot with 2 activations - // Metric 2: Always active - StatsdConfig config1; - config1.set_id(12341); - config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - *config1.add_atom_matcher() = wakelockAcquireMatcher; - *config1.add_atom_matcher() = screenOnMatcher; - - long metricId1 = 1234561; - long metricId2 = 1234562; - - auto countMetric1 = config1.add_count_metric(); - countMetric1->set_id(metricId1); - countMetric1->set_what(wakelockAcquireMatcher.id()); - countMetric1->set_bucket(FIVE_MINUTES); - - auto countMetric2 = config1.add_count_metric(); - countMetric2->set_id(metricId2); - countMetric2->set_what(wakelockAcquireMatcher.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - auto metric1Activation = config1.add_metric_activation(); - metric1Activation->set_metric_id(metricId1); - metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); - auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); - metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric1ActivationTrigger1->set_ttl_seconds(100); - auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); - metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); - metric1ActivationTrigger2->set_ttl_seconds(200); - - ConfigKey cfgKey1(uid, 12341); - long timeBase1 = 1; - sp processor = - CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor->mMetricsManagers.size()); - auto it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); - auto& metricsManager1 = it->second; - EXPECT_TRUE(metricsManager1->isActive()); - - auto metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer1 = *metricIt; - EXPECT_FALSE(metricProducer1->isActive()); - - metricIt = metricsManager1->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); - auto& metricProducer2 = *metricIt; - EXPECT_TRUE(metricProducer2->isActive()); - - int i = 0; - for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { - if (metricsManager1->mAllAtomMatchers[i]->getId() == - metric1ActivationTrigger1->atom_matcher_id()) { - break; - } - } - const auto& activation1 = metricProducer1->mEventActivationMap.at(i); - EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kNotActive, activation1->state); - - i = 0; - for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { - if (metricsManager1->mAllAtomMatchers[i]->getId() == - metric1ActivationTrigger2->atom_matcher_id()) { - break; - } - } - const auto& activation2 = metricProducer1->mEventActivationMap.at(i); - EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); - EXPECT_EQ(0, activation2->start_ns); - EXPECT_EQ(kNotActive, activation2->state); - // }}}------------------------------------------------------------------------------ - - // Trigger Activation 1 for Metric 1 - std::vector attributionUids = {111}; - std::vector attributionTags = {"App1"}; - std::unique_ptr event = - CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); - processor->OnLogEvent(event.get()); - - // Metric 1 is not active; Activation 1 set to kActiveOnBoot - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_FALSE(metricProducer1->isActive()); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1->state); - EXPECT_EQ(0, activation2->start_ns); - EXPECT_EQ(kNotActive, activation2->state); - - EXPECT_TRUE(metricProducer2->isActive()); - // }}}----------------------------------------------------------------------------- - - // Simulate shutdown by saving state to disk - int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; - processor->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_FALSE(metricProducer1->isActive()); - int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC; - - // Simulate device restarted state by creating new instance of StatsLogProcessor with the - // same config. - long timeBase2 = 1000; - sp processor2 = - CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor2->mMetricsManagers.size()); - it = processor2->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1001 = it->second; - EXPECT_TRUE(metricsManager1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1001 = *metricIt; - EXPECT_FALSE(metricProducer1001->isActive()); - - metricIt = metricsManager1001->mAllMetricProducers.begin(); - for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); - auto& metricProducer1002 = *metricIt; - EXPECT_TRUE(metricProducer1002->isActive()); - - i = 0; - for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { - if (metricsManager1001->mAllAtomMatchers[i]->getId() == - metric1ActivationTrigger1->atom_matcher_id()) { - break; - } - } - const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i); - EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns); - EXPECT_EQ(0, activation1001_1->start_ns); - EXPECT_EQ(kNotActive, activation1001_1->state); - - i = 0; - for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { - if (metricsManager1001->mAllAtomMatchers[i]->getId() == - metric1ActivationTrigger2->atom_matcher_id()) { - break; - } - } - - const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i); - EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns); - EXPECT_EQ(0, activation1001_2->start_ns); - EXPECT_EQ(kNotActive, activation1001_2->state); - // }}}----------------------------------------------------------------------------------- - - // Load saved state from disk. - processor2->LoadActiveConfigsFromDisk(); - - // Metric 1 active; Activation 1 is active, Activation 2 is not active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); - EXPECT_EQ(kActive, activation1001_1->state); - EXPECT_EQ(0, activation1001_2->start_ns); - EXPECT_EQ(kNotActive, activation1001_2->state); - - EXPECT_TRUE(metricProducer1002->isActive()); - // }}}-------------------------------------------------------------------------------- - - // Trigger Activation 2 for Metric 1. - auto screenOnEvent = - CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON); - processor2->OnLogEvent(screenOnEvent.get()); - - // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); - EXPECT_EQ(kActive, activation1001_1->state); - EXPECT_EQ(0, activation1001_2->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1001_2->state); - - EXPECT_TRUE(metricProducer1002->isActive()); - // }}}--------------------------------------------------------------------------- - - // Simulate shutdown by saving state to disk - shutDownTime = timeBase2 + 50 * NS_PER_SEC; - processor2->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_TRUE(metricProducer1002->isActive()); - ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime; - int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC; - - // Simulate device restarted state by creating new instance of StatsLogProcessor with the - // same config. - long timeBase3 = timeBase2 + 120 * NS_PER_SEC; - sp processor3 = - CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor3->mMetricsManagers.size()); - it = processor3->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor3->mMetricsManagers.end()); - auto& metricsManagerTimeBase3 = it->second; - EXPECT_TRUE(metricsManagerTimeBase3->isActive()); - - metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); - for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); - auto& metricProducerTimeBase3_1 = *metricIt; - EXPECT_FALSE(metricProducerTimeBase3_1->isActive()); - - metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); - for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); - auto& metricProducerTimeBase3_2 = *metricIt; - EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); - - i = 0; - for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { - if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == - metric1ActivationTrigger1->atom_matcher_id()) { - break; - } - } - const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i); - EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns); - EXPECT_EQ(0, activationTimeBase3_1->start_ns); - EXPECT_EQ(kNotActive, activationTimeBase3_1->state); - - i = 0; - for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { - if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == - metric1ActivationTrigger2->atom_matcher_id()) { - break; - } - } - - const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i); - EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns); - EXPECT_EQ(0, activationTimeBase3_2->start_ns); - EXPECT_EQ(kNotActive, activationTimeBase3_2->state); - - EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); - // }}}---------------------------------------------------------------------------------- - - // Load saved state from disk. - processor3->LoadActiveConfigsFromDisk(); - - // Metric 1 active: Activation 1 is active, Activation 2 is active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); - EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns); - EXPECT_EQ(kActive, activationTimeBase3_1->state); - EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); - EXPECT_EQ(kActive, activationTimeBase3_2->state); - - EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); - // }}}------------------------------------------------------------------------------- - - // Trigger Activation 2 for Metric 1 again. - screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC, - android::view::DISPLAY_STATE_ON); - processor3->OnLogEvent(screenOnEvent.get()); - - // Metric 1 active; Activation 1 is not active, Activation 2 is set to active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); - EXPECT_EQ(kNotActive, activationTimeBase3_1->state); - EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); - EXPECT_EQ(kActive, activationTimeBase3_2->state); - - EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); - // }}}--------------------------------------------------------------------------- - - // Simulate shutdown by saving state to disk. - shutDownTime = timeBase3 + 500 * NS_PER_SEC; - processor3->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_TRUE(metricProducer1002->isActive()); - ttl1 = timeBase3 + ttl1 - shutDownTime; - ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime; - - // Simulate device restarted state by creating new instance of StatsLogProcessor with the - // same config. - long timeBase4 = timeBase3 + 600 * NS_PER_SEC; - sp processor4 = - CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor4->mMetricsManagers.size()); - it = processor4->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor4->mMetricsManagers.end()); - auto& metricsManagerTimeBase4 = it->second; - EXPECT_TRUE(metricsManagerTimeBase4->isActive()); - - metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin(); - for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId1) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end()); - auto& metricProducerTimeBase4_1 = *metricIt; - EXPECT_FALSE(metricProducerTimeBase4_1->isActive()); - - metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin(); - for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) { - if ((*metricIt)->getMetricId() == metricId2) { - break; - } - } - EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end()); - auto& metricProducerTimeBase4_2 = *metricIt; - EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); - - i = 0; - for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) { - if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() == - metric1ActivationTrigger1->atom_matcher_id()) { - break; - } - } - const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i); - EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns); - EXPECT_EQ(0, activationTimeBase4_1->start_ns); - EXPECT_EQ(kNotActive, activationTimeBase4_1->state); - - i = 0; - for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) { - if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() == - metric1ActivationTrigger2->atom_matcher_id()) { - break; - } - } - - const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i); - EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns); - EXPECT_EQ(0, activationTimeBase4_2->start_ns); - EXPECT_EQ(kNotActive, activationTimeBase4_2->state); - - EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); - // }}}---------------------------------------------------------------------------------- - - // Load saved state from disk. - processor4->LoadActiveConfigsFromDisk(); - - // Metric 1 active: Activation 1 is not active, Activation 2 is not active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_FALSE(metricProducerTimeBase4_1->isActive()); - EXPECT_EQ(kNotActive, activationTimeBase4_1->state); - EXPECT_EQ(kNotActive, activationTimeBase4_2->state); - - EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); - // }}}------------------------------------------------------------------------------- -} - -TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) { - int uid = 1111; - - // Create config with 2 metrics: - // Metric 1: Activate on boot with 2 activations - // Metric 2: Always active - StatsdConfig config1; - config1.set_id(12341); - config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - *config1.add_atom_matcher() = wakelockAcquireMatcher; - *config1.add_atom_matcher() = screenOnMatcher; - - long metricId1 = 1234561; - long metricId2 = 1234562; - - auto countMetric1 = config1.add_count_metric(); - countMetric1->set_id(metricId1); - countMetric1->set_what(wakelockAcquireMatcher.id()); - countMetric1->set_bucket(FIVE_MINUTES); - - auto countMetric2 = config1.add_count_metric(); - countMetric2->set_id(metricId2); - countMetric2->set_what(wakelockAcquireMatcher.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - auto metric1Activation = config1.add_metric_activation(); - metric1Activation->set_metric_id(metricId1); - metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); - auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); - metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric1ActivationTrigger1->set_ttl_seconds(100); - auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); - metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); - metric1ActivationTrigger2->set_ttl_seconds(200); - metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); - - ConfigKey cfgKey1(uid, 12341); - long timeBase1 = 1; - sp processor1 = - CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor1->mMetricsManagers.size()); - auto it = processor1->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor1->mMetricsManagers.end()); - auto& metricsManager1 = it->second; - EXPECT_TRUE(metricsManager1->isActive()); - - ASSERT_EQ(metricsManager1->mAllMetricProducers.size(), 2); - // We assume that the index of a MetricProducer within the mAllMetricProducers - // array follows the order in which metrics are added to the config. - auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0]; - EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1); - EXPECT_FALSE(metricProducer1_1->isActive()); // inactive due to associated MetricActivation - - auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1]; - EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2); - EXPECT_TRUE(metricProducer1_2->isActive()); - - ASSERT_EQ(metricProducer1_1->mEventActivationMap.size(), 2); - // The key in mEventActivationMap is the index of the associated atom matcher. We assume - // that matchers are indexed in the order that they are added to the config. - const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0); - EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns); - EXPECT_EQ(0, activation1_1_1->start_ns); - EXPECT_EQ(kNotActive, activation1_1_1->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType); - - const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1); - EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns); - EXPECT_EQ(0, activation1_1_2->start_ns); - EXPECT_EQ(kNotActive, activation1_1_2->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType); - // }}}------------------------------------------------------------------------------ - - // Trigger Activation 1 for Metric 1 - std::vector attributionUids = {111}; - std::vector attributionTags = {"App1"}; - std::unique_ptr event = - CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); - processor1->OnLogEvent(event.get()); - - // Metric 1 is not active; Activation 1 set to kActiveOnBoot - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_FALSE(metricProducer1_1->isActive()); - EXPECT_EQ(0, activation1_1_1->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1_1_1->state); - EXPECT_EQ(0, activation1_1_2->start_ns); - EXPECT_EQ(kNotActive, activation1_1_2->state); - - EXPECT_TRUE(metricProducer1_2->isActive()); - // }}}----------------------------------------------------------------------------- - - // Simulate shutdown by saving state to disk - int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; - processor1->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_FALSE(metricProducer1_1->isActive()); - - // Simulate device restarted state by creating new instance of StatsLogProcessor with the - // same config. - long timeBase2 = 1000; - sp processor2 = - CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor2->mMetricsManagers.size()); - it = processor2->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager2 = it->second; - EXPECT_TRUE(metricsManager2->isActive()); - - ASSERT_EQ(metricsManager2->mAllMetricProducers.size(), 2); - // We assume that the index of a MetricProducer within the mAllMetricProducers - // array follows the order in which metrics are added to the config. - auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0]; - EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1); - EXPECT_FALSE(metricProducer2_1->isActive()); - - auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1]; - EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2); - EXPECT_TRUE(metricProducer2_2->isActive()); - - ASSERT_EQ(metricProducer2_1->mEventActivationMap.size(), 2); - // The key in mEventActivationMap is the index of the associated atom matcher. We assume - // that matchers are indexed in the order that they are added to the config. - const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0); - EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns); - EXPECT_EQ(0, activation2_1_1->start_ns); - EXPECT_EQ(kNotActive, activation2_1_1->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType); - - const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1); - EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns); - EXPECT_EQ(0, activation2_1_2->start_ns); - EXPECT_EQ(kNotActive, activation2_1_2->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType); - // }}}----------------------------------------------------------------------------------- - - // Load saved state from disk. - processor2->LoadActiveConfigsFromDisk(); - - // Metric 1 active; Activation 1 is active, Activation 2 is not active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer2_1->isActive()); - int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC; - EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns); - EXPECT_EQ(kActive, activation2_1_1->state); - EXPECT_EQ(0, activation2_1_2->start_ns); - EXPECT_EQ(kNotActive, activation2_1_2->state); - - EXPECT_TRUE(metricProducer2_2->isActive()); - // }}}-------------------------------------------------------------------------------- - - // Trigger Activation 2 for Metric 1. - auto screenOnEvent = - CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON); - processor2->OnLogEvent(screenOnEvent.get()); - - // Metric 1 active; Activation 1 is active, Activation 2 is active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer2_1->isActive()); - EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns); - EXPECT_EQ(kActive, activation2_1_1->state); - EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns); - EXPECT_EQ(kActive, activation2_1_2->state); - - EXPECT_TRUE(metricProducer2_2->isActive()); - // }}}--------------------------------------------------------------------------- - - // Simulate shutdown by saving state to disk - shutDownTime = timeBase2 + 50 * NS_PER_SEC; - processor2->SaveActiveConfigsToDisk(shutDownTime); - EXPECT_TRUE(metricProducer2_1->isActive()); - EXPECT_TRUE(metricProducer2_2->isActive()); - ttl1 -= shutDownTime - timeBase2; - int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - - (shutDownTime - screenOnEvent->GetElapsedTimestampNs()); - - // Simulate device restarted state by creating new instance of StatsLogProcessor with the - // same config. - long timeBase3 = timeBase2 + 120 * NS_PER_SEC; - sp processor3 = - CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1); - - // Metric 1 is not active. - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor3->mMetricsManagers.size()); - it = processor3->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor3->mMetricsManagers.end()); - auto& metricsManager3 = it->second; - EXPECT_TRUE(metricsManager3->isActive()); - - ASSERT_EQ(metricsManager3->mAllMetricProducers.size(), 2); - // We assume that the index of a MetricProducer within the mAllMetricProducers - // array follows the order in which metrics are added to the config. - auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0]; - EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1); - EXPECT_FALSE(metricProducer3_1->isActive()); - - auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1]; - EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2); - EXPECT_TRUE(metricProducer3_2->isActive()); - - ASSERT_EQ(metricProducer3_1->mEventActivationMap.size(), 2); - // The key in mEventActivationMap is the index of the associated atom matcher. We assume - // that matchers are indexed in the order that they are added to the config. - const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0); - EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns); - EXPECT_EQ(0, activation3_1_1->start_ns); - EXPECT_EQ(kNotActive, activation3_1_1->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType); - - const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1); - EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns); - EXPECT_EQ(0, activation3_1_2->start_ns); - EXPECT_EQ(kNotActive, activation3_1_2->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType); - // }}}---------------------------------------------------------------------------------- - - // Load saved state from disk. - processor3->LoadActiveConfigsFromDisk(); - - // Metric 1 active: Activation 1 is active, Activation 2 is active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer3_1->isActive()); - EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns); - EXPECT_EQ(kActive, activation3_1_1->state); - EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns); - EXPECT_EQ(kActive, activation3_1_2->state); - - EXPECT_TRUE(metricProducer3_2->isActive()); - // }}}------------------------------------------------------------------------------- - - // Trigger Activation 2 for Metric 1 again. - screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC, - android::view::DISPLAY_STATE_ON); - processor3->OnLogEvent(screenOnEvent.get()); - - // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire), - // Activation 2 is set to active - // Metric 2 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_TRUE(metricProducer3_1->isActive()); - EXPECT_EQ(kNotActive, activation3_1_1->state); - EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns); - EXPECT_EQ(kActive, activation3_1_2->state); - - EXPECT_TRUE(metricProducer3_2->isActive()); - // }}}--------------------------------------------------------------------------- -} - -TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { - int uid = 9876; - long configId = 12341; - - // Create config with 3 metrics: - // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate. - // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate. - // Metric 3: Always active - StatsdConfig config1; - config1.set_id(configId); - config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto jobStartMatcher = CreateStartScheduledJobAtomMatcher(); - auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher(); - *config1.add_atom_matcher() = wakelockAcquireMatcher; - *config1.add_atom_matcher() = screenOnMatcher; - *config1.add_atom_matcher() = jobStartMatcher; - *config1.add_atom_matcher() = jobFinishMatcher; - - long metricId1 = 1234561; - long metricId2 = 1234562; - long metricId3 = 1234563; - - auto countMetric1 = config1.add_count_metric(); - countMetric1->set_id(metricId1); - countMetric1->set_what(wakelockAcquireMatcher.id()); - countMetric1->set_bucket(FIVE_MINUTES); - - auto countMetric2 = config1.add_count_metric(); - countMetric2->set_id(metricId2); - countMetric2->set_what(wakelockAcquireMatcher.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - auto countMetric3 = config1.add_count_metric(); - countMetric3->set_id(metricId3); - countMetric3->set_what(wakelockAcquireMatcher.id()); - countMetric3->set_bucket(FIVE_MINUTES); - - // Metric 1 activates on boot for wakelock acquire, immediately for screen on. - auto metric1Activation = config1.add_metric_activation(); - metric1Activation->set_metric_id(metricId1); - auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); - metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); - metric1ActivationTrigger1->set_ttl_seconds(100); - metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); - auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); - metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); - metric1ActivationTrigger2->set_ttl_seconds(200); - metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); - - // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish. - auto metric2Activation = config1.add_metric_activation(); - metric2Activation->set_metric_id(metricId2); - auto metric2ActivationTrigger1 = metric2Activation->add_event_activation(); - metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id()); - metric2ActivationTrigger1->set_ttl_seconds(100); - metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); - auto metric2ActivationTrigger2 = metric2Activation->add_event_activation(); - metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id()); - metric2ActivationTrigger2->set_ttl_seconds(200); - metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); - - // Send the config. - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - string serialized = config1.SerializeAsString(); - service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()}); - - // Make sure the config is stored on disk. Otherwise, we will not reset on system server death. - StatsdConfig tmpConfig; - ConfigKey cfgKey1(uid, configId); - EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig)); - - // Metric 1 is not active. - // Metric 2 is not active. - // Metric 3 is active. - // {{{--------------------------------------------------------------------------- - sp processor = service->mProcessor; - ASSERT_EQ(1, processor->mMetricsManagers.size()); - auto it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); - auto& metricsManager1 = it->second; - EXPECT_TRUE(metricsManager1->isActive()); - ASSERT_EQ(3, metricsManager1->mAllMetricProducers.size()); - - auto& metricProducer1 = metricsManager1->mAllMetricProducers[0]; - EXPECT_EQ(metricId1, metricProducer1->getMetricId()); - EXPECT_FALSE(metricProducer1->isActive()); - - auto& metricProducer2 = metricsManager1->mAllMetricProducers[1]; - EXPECT_EQ(metricId2, metricProducer2->getMetricId()); - EXPECT_FALSE(metricProducer2->isActive()); - - auto& metricProducer3 = metricsManager1->mAllMetricProducers[2]; - EXPECT_EQ(metricId3, metricProducer3->getMetricId()); - EXPECT_TRUE(metricProducer3->isActive()); - - // Check event activations. - ASSERT_EQ(metricsManager1->mAllAtomMatchers.size(), 4); - EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(), - metric1ActivationTrigger1->atom_matcher_id()); - const auto& activation1 = metricProducer1->mEventActivationMap.at(0); - EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kNotActive, activation1->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType); - - EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(), - metric1ActivationTrigger2->atom_matcher_id()); - const auto& activation2 = metricProducer1->mEventActivationMap.at(1); - EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); - EXPECT_EQ(0, activation2->start_ns); - EXPECT_EQ(kNotActive, activation2->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType); - - EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(), - metric2ActivationTrigger1->atom_matcher_id()); - const auto& activation3 = metricProducer2->mEventActivationMap.at(2); - EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns); - EXPECT_EQ(0, activation3->start_ns); - EXPECT_EQ(kNotActive, activation3->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType); - - EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(), - metric2ActivationTrigger2->atom_matcher_id()); - const auto& activation4 = metricProducer2->mEventActivationMap.at(3); - EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns); - EXPECT_EQ(0, activation4->start_ns); - EXPECT_EQ(kNotActive, activation4->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType); - // }}}------------------------------------------------------------------------------ - - // Trigger Activation 1 for Metric 1. Should activate on boot. - // Trigger Activation 4 for Metric 2. Should activate immediately. - int64_t configAddedTimeNs = metricsManager1->mLastReportTimeNs; - std::vector attributionUids = {111}; - std::vector attributionTags = {"App1"}; - std::unique_ptr event1 = CreateAcquireWakelockEvent( - 1 + configAddedTimeNs, attributionUids, attributionTags, "wl1"); - processor->OnLogEvent(event1.get()); - - std::unique_ptr event2 = CreateFinishScheduledJobEvent( - 2 + configAddedTimeNs, attributionUids, attributionTags, "finish1"); - processor->OnLogEvent(event2.get()); - - // Metric 1 is not active; Activation 1 set to kActiveOnBoot - // Metric 2 is active. Activation 4 set to kActive - // Metric 3 is active. - // {{{--------------------------------------------------------------------------- - EXPECT_FALSE(metricProducer1->isActive()); - EXPECT_EQ(0, activation1->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1->state); - EXPECT_EQ(0, activation2->start_ns); - EXPECT_EQ(kNotActive, activation2->state); - - EXPECT_TRUE(metricProducer2->isActive()); - EXPECT_EQ(0, activation3->start_ns); - EXPECT_EQ(kNotActive, activation3->state); - EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns); - EXPECT_EQ(kActive, activation4->state); - - EXPECT_TRUE(metricProducer3->isActive()); - // }}}----------------------------------------------------------------------------- - - // Can't fake time with StatsService. - // Lets get a time close to the system server death time and make sure it's sane. - int64_t approximateSystemServerDeath = getElapsedRealtimeNs(); - EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs); - EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs); - - // System server dies. - service->statsCompanionServiceDiedImpl(); - - // We should have a new metrics manager. Lets get it and ensure activation status is restored. - // {{{--------------------------------------------------------------------------- - ASSERT_EQ(1, processor->mMetricsManagers.size()); - it = processor->mMetricsManagers.find(cfgKey1); - EXPECT_TRUE(it != processor->mMetricsManagers.end()); - auto& metricsManager2 = it->second; - EXPECT_TRUE(metricsManager2->isActive()); - ASSERT_EQ(3, metricsManager2->mAllMetricProducers.size()); - - auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0]; - EXPECT_EQ(metricId1, metricProducer1001->getMetricId()); - EXPECT_FALSE(metricProducer1001->isActive()); - - auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1]; - EXPECT_EQ(metricId2, metricProducer1002->getMetricId()); - EXPECT_TRUE(metricProducer1002->isActive()); - - auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2]; - EXPECT_EQ(metricId3, metricProducer1003->getMetricId()); - EXPECT_TRUE(metricProducer1003->isActive()); - - // Check event activations. - // Activation 1 is kActiveOnBoot. - // Activation 2 and 3 are not active. - // Activation 4 is active. - ASSERT_EQ(metricsManager2->mAllAtomMatchers.size(), 4); - EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(), - metric1ActivationTrigger1->atom_matcher_id()); - const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0); - EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); - EXPECT_EQ(0, activation1001->start_ns); - EXPECT_EQ(kActiveOnBoot, activation1001->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType); - - EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(), - metric1ActivationTrigger2->atom_matcher_id()); - const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1); - EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns); - EXPECT_EQ(0, activation1002->start_ns); - EXPECT_EQ(kNotActive, activation1002->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType); - - EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(), - metric2ActivationTrigger1->atom_matcher_id()); - const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2); - EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); - EXPECT_EQ(0, activation1003->start_ns); - EXPECT_EQ(kNotActive, activation1003->state); - EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType); - - EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(), - metric2ActivationTrigger2->atom_matcher_id()); - const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3); - EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns); - EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns); - EXPECT_EQ(kActive, activation1004->state); - EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType); - // }}}------------------------------------------------------------------------------ - - // Clear the data stored on disk as a result of the system server death. - vector buffer; - processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true, ADB_DUMP, FAST, - &buffer); -} - -TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUid) { - int hostUid = 20; - int isolatedUid = 30; - uint64_t eventTimeNs = 12355; - int atomId = 89; - int field1 = 90; - int field2 = 28; - sp mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); - ConfigKey cfgKey; - StatsdConfig config = MakeConfig(false); - sp processor = - CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); - - shared_ptr logEvent = makeUidLogEvent(atomId, eventTimeNs, hostUid, field1, field2); - - processor->OnLogEvent(logEvent.get()); - - const vector* actualFieldValues = &logEvent->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value); -} - -TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUid) { - int hostUid = 20; - int isolatedUid = 30; - uint64_t eventTimeNs = 12355; - int atomId = 89; - int field1 = 90; - int field2 = 28; - sp mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); - ConfigKey cfgKey; - StatsdConfig config = MakeConfig(false); - sp processor = - CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); - - shared_ptr logEvent = - makeUidLogEvent(atomId, eventTimeNs, isolatedUid, field1, field2); - - processor->OnLogEvent(logEvent.get()); - - const vector* actualFieldValues = &logEvent->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value); -} - -TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUidAttributionChain) { - int hostUid = 20; - int isolatedUid = 30; - uint64_t eventTimeNs = 12355; - int atomId = 89; - int field1 = 90; - int field2 = 28; - sp mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); - ConfigKey cfgKey; - StatsdConfig config = MakeConfig(false); - sp processor = - CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); - - shared_ptr logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {hostUid, 200}, - {"tag1", "tag2"}, field1, field2); - - processor->OnLogEvent(logEvent.get()); - - const vector* actualFieldValues = &logEvent->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value); -} - -TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUidAttributionChain) { - int hostUid = 20; - int isolatedUid = 30; - uint64_t eventTimeNs = 12355; - int atomId = 89; - int field1 = 90; - int field2 = 28; - sp mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); - ConfigKey cfgKey; - StatsdConfig config = MakeConfig(false); - sp processor = - CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); - - shared_ptr logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {isolatedUid, 200}, - {"tag1", "tag2"}, field1, field2); - - processor->OnLogEvent(logEvent.get()); - - const vector* actualFieldValues = &logEvent->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value); -} - -TEST(StatsLogProcessorTest, TestDumpReportWithoutErasingDataDoesNotUpdateTimestamp) { - int hostUid = 20; - int isolatedUid = 30; - sp mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); - ConfigKey key(3, 4); - - // TODO: All tests should not persist state on disk. This removes any reports that were present. - ProtoOutputStream proto; - StorageManager::appendConfigMetricsReport(key, &proto, /*erase data=*/true, /*isAdb=*/false); - - StatsdConfig config = MakeConfig(false); - sp processor = - CreateStatsLogProcessor(1, 1, config, key, nullptr, 0, mockUidMap); - vector bytes; - - int64_t dumpTime1Ns = 1 * NS_PER_SEC; - processor->onDumpReport(key, dumpTime1Ns, false /* include_current_bucket */, - true /* erase_data */, ADB_DUMP, FAST, &bytes); - - ConfigMetricsReportList output; - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_EQ(output.reports_size(), 1); - EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime1Ns); - - int64_t dumpTime2Ns = 5 * NS_PER_SEC; - processor->onDumpReport(key, dumpTime2Ns, false /* include_current_bucket */, - false /* erase_data */, ADB_DUMP, FAST, &bytes); - - // Check that the dump report without clearing data is successful. - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_EQ(output.reports_size(), 1); - EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime2Ns); - EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns); - - int64_t dumpTime3Ns = 10 * NS_PER_SEC; - processor->onDumpReport(key, dumpTime3Ns, false /* include_current_bucket */, - true /* erase_data */, ADB_DUMP, FAST, &bytes); - - // Check that the previous dump report that didn't clear data did not overwrite the first dump's - // timestamps. - output.ParseFromArray(bytes.data(), bytes.size()); - EXPECT_EQ(output.reports_size(), 1); - EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime3Ns); - EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns); - -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp deleted file mode 100644 index cc38c4a4067a..000000000000 --- a/cmds/statsd/tests/StatsService_test.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "StatsService.h" -#include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -#include -#include -#include - -#include - -using namespace android; -using namespace testing; - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoOutputStream; -using ::ndk::SharedRefBase; - -#ifdef __ANDROID__ - -TEST(StatsServiceTest, TestAddConfig_simple) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - StatsdConfig config; - config.set_id(12345); - string serialized = config.SerializeAsString(); - - EXPECT_TRUE( - service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); -} - -TEST(StatsServiceTest, TestAddConfig_empty) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - string serialized = ""; - - EXPECT_TRUE( - service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); -} - -TEST(StatsServiceTest, TestAddConfig_invalid) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - string serialized = "Invalid config!"; - - EXPECT_FALSE( - service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); -} - -TEST(StatsServiceTest, TestGetUidFromArgs) { - Vector args; - args.push(String8("-1")); - args.push(String8("0")); - args.push(String8("1")); - args.push(String8("a1")); - args.push(String8("")); - - int32_t uid; - - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - service->mEngBuild = true; - - // "-1" - EXPECT_FALSE(service->getUidFromArgs(args, 0, uid)); - - // "0" - EXPECT_TRUE(service->getUidFromArgs(args, 1, uid)); - EXPECT_EQ(0, uid); - - // "1" - EXPECT_TRUE(service->getUidFromArgs(args, 2, uid)); - EXPECT_EQ(1, uid); - - // "a1" - EXPECT_FALSE(service->getUidFromArgs(args, 3, uid)); - - // "" - EXPECT_FALSE(service->getUidFromArgs(args, 4, uid)); - - // For a non-userdebug, uid "1" cannot be impersonated. - service->mEngBuild = false; - EXPECT_FALSE(service->getUidFromArgs(args, 2, uid)); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp deleted file mode 100644 index 33bdc64333e0..000000000000 --- a/cmds/statsd/tests/UidMap_test.cpp +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "packages/UidMap.h" -#include "StatsLogProcessor.h" -#include "config/ConfigKey.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "hash.h" -#include "statslog_statsdtest.h" -#include "statsd_test_util.h" - -#include -#include - -#include - -using namespace android; - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoOutputStream; -using android::util::ProtoReader; - -#ifdef __ANDROID__ -const string kApp1 = "app1.sharing.1"; -const string kApp2 = "app2.sharing.1"; - -TEST(UidMapTest, TestIsolatedUID) { - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - // Construct the processor with a no-op sendBroadcast function that does nothing. - StatsLogProcessor p( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }, - [](const int&, const vector&) { return true; }); - - std::unique_ptr addEvent = CreateIsolatedUidChangedEvent( - 1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 1 /*is_create*/); - EXPECT_EQ(101, m->getHostUidOrSelf(101)); - p.OnLogEvent(addEvent.get()); - EXPECT_EQ(100, m->getHostUidOrSelf(101)); - - std::unique_ptr removeEvent = CreateIsolatedUidChangedEvent( - 1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 0 /*is_create*/); - p.OnLogEvent(removeEvent.get()); - EXPECT_EQ(101, m->getHostUidOrSelf(101)); -} - -TEST(UidMapTest, TestMatching) { - UidMap m; - vector uids; - vector versions; - vector apps; - vector versionStrings; - vector installers; - - uids.push_back(1000); - uids.push_back(1000); - versionStrings.push_back(String16("v1")); - versionStrings.push_back(String16("v1")); - installers.push_back(String16("")); - installers.push_back(String16("")); - apps.push_back(String16(kApp1.c_str())); - apps.push_back(String16(kApp2.c_str())); - versions.push_back(4); - versions.push_back(5); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - EXPECT_TRUE(m.hasApp(1000, kApp1)); - EXPECT_TRUE(m.hasApp(1000, kApp2)); - EXPECT_FALSE(m.hasApp(1000, "not.app")); - - std::set name_set = m.getAppNamesFromUid(1000u, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 2u); - EXPECT_TRUE(name_set.find(kApp1) != name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - - name_set = m.getAppNamesFromUid(12345, true /* returnNormalized */); - EXPECT_TRUE(name_set.empty()); -} - -TEST(UidMapTest, TestAddAndRemove) { - UidMap m; - vector uids; - vector versions; - vector apps; - vector versionStrings; - vector installers; - - uids.push_back(1000); - uids.push_back(1000); - versionStrings.push_back(String16("v1")); - versionStrings.push_back(String16("v1")); - installers.push_back(String16("")); - installers.push_back(String16("")); - apps.push_back(String16(kApp1.c_str())); - apps.push_back(String16(kApp2.c_str())); - versions.push_back(4); - versions.push_back(5); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - std::set name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 2u); - EXPECT_TRUE(name_set.find(kApp1) != name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - - // Update the app1 version. - m.updateApp(2, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16("")); - EXPECT_EQ(40, m.getAppVersion(1000, kApp1)); - - name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 2u); - EXPECT_TRUE(name_set.find(kApp1) != name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - - m.removeApp(3, String16(kApp1.c_str()), 1000); - EXPECT_FALSE(m.hasApp(1000, kApp1)); - EXPECT_TRUE(m.hasApp(1000, kApp2)); - name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 1u); - EXPECT_TRUE(name_set.find(kApp1) == name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - - // Remove app2. - m.removeApp(4, String16(kApp2.c_str()), 1000); - EXPECT_FALSE(m.hasApp(1000, kApp1)); - EXPECT_FALSE(m.hasApp(1000, kApp2)); - name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - EXPECT_TRUE(name_set.empty()); -} - -TEST(UidMapTest, TestUpdateApp) { - UidMap m; - m.updateMap(1, {1000, 1000}, {4, 5}, {String16("v4"), String16("v5")}, - {String16(kApp1.c_str()), String16(kApp2.c_str())}, {String16(""), String16("")}); - std::set name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 2u); - EXPECT_TRUE(name_set.find(kApp1) != name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - - // Adds a new name for uid 1000. - m.updateApp(2, String16("NeW_aPP1_NAmE"), 1000, 40, String16("v40"), String16("")); - name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 3u); - EXPECT_TRUE(name_set.find(kApp1) != name_set.end()); - EXPECT_TRUE(name_set.find(kApp2) != name_set.end()); - EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end()); - EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end()); - - // This name is also reused by another uid 2000. - m.updateApp(3, String16("NeW_aPP1_NAmE"), 2000, 1, String16("v1"), String16("")); - name_set = m.getAppNamesFromUid(2000, true /* returnNormalized */); - ASSERT_EQ(name_set.size(), 1u); - EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end()); - EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end()); -} - -static void protoOutputStreamToUidMapping(ProtoOutputStream* proto, UidMapping* results) { - vector bytes; - bytes.resize(proto->size()); - size_t pos = 0; - sp reader = proto->data(); - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((bytes)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - results->ParseFromArray(bytes.data(), bytes.size()); -} - -// Test that uid map returns at least one snapshot even if we already obtained -// this snapshot from a previous call to getData. -TEST(UidMapTest, TestOutputIncludesAtLeastOneSnapshot) { - UidMap m; - // Initialize single config key. - ConfigKey config1(1, StringToId("config1")); - m.OnConfigUpdated(config1); - vector uids; - vector versions; - vector apps; - vector versionStrings; - vector installers; - uids.push_back(1000); - apps.push_back(String16(kApp2.c_str())); - versionStrings.push_back(String16("v1")); - installers.push_back(String16("")); - versions.push_back(5); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - // Set the last timestamp for this config key to be newer. - m.mLastUpdatePerConfigKey[config1] = 2; - - ProtoOutputStream proto; - m.appendUidMap(3, config1, nullptr, true, true, &proto); - - // Check there's still a uidmap attached this one. - UidMapping results; - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - EXPECT_EQ("v1", results.snapshots(0).package_info(0).version_string()); -} - -TEST(UidMapTest, TestRemovedAppRetained) { - UidMap m; - // Initialize single config key. - ConfigKey config1(1, StringToId("config1")); - m.OnConfigUpdated(config1); - vector uids; - vector versions; - vector versionStrings; - vector installers; - vector apps; - uids.push_back(1000); - apps.push_back(String16(kApp2.c_str())); - versions.push_back(5); - versionStrings.push_back(String16("v5")); - installers.push_back(String16("")); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - m.removeApp(2, String16(kApp2.c_str()), 1000); - - ProtoOutputStream proto; - m.appendUidMap(3, config1, nullptr, true, true, &proto); - - // Snapshot should still contain this item as deleted. - UidMapping results; - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots(0).package_info_size()); - EXPECT_EQ(true, results.snapshots(0).package_info(0).deleted()); -} - -TEST(UidMapTest, TestRemovedAppOverGuardrail) { - UidMap m; - // Initialize single config key. - ConfigKey config1(1, StringToId("config1")); - m.OnConfigUpdated(config1); - vector uids; - vector versions; - vector versionStrings; - vector installers; - vector apps; - const int maxDeletedApps = StatsdStats::kMaxDeletedAppsInUidMap; - for (int j = 0; j < maxDeletedApps + 10; j++) { - uids.push_back(j); - apps.push_back(String16(kApp1.c_str())); - versions.push_back(j); - versionStrings.push_back(String16("v")); - installers.push_back(String16("")); - } - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - // First, verify that we have the expected number of items. - UidMapping results; - ProtoOutputStream proto; - m.appendUidMap(3, config1, nullptr, true, true, &proto); - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(maxDeletedApps + 10, results.snapshots(0).package_info_size()); - - // Now remove all the apps. - m.updateMap(1, uids, versions, versionStrings, apps, installers); - for (int j = 0; j < maxDeletedApps + 10; j++) { - m.removeApp(4, String16(kApp1.c_str()), j); - } - - proto.clear(); - m.appendUidMap(5, config1, nullptr, true, true, &proto); - // Snapshot drops the first nine items. - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(maxDeletedApps, results.snapshots(0).package_info_size()); -} - -TEST(UidMapTest, TestClearingOutput) { - UidMap m; - - ConfigKey config1(1, StringToId("config1")); - ConfigKey config2(1, StringToId("config2")); - - m.OnConfigUpdated(config1); - - vector uids; - vector versions; - vector versionStrings; - vector installers; - vector apps; - uids.push_back(1000); - uids.push_back(1000); - apps.push_back(String16(kApp1.c_str())); - apps.push_back(String16(kApp2.c_str())); - versions.push_back(4); - versions.push_back(5); - versionStrings.push_back(String16("v4")); - versionStrings.push_back(String16("v5")); - installers.push_back(String16("")); - installers.push_back(String16("")); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - ProtoOutputStream proto; - m.appendUidMap(2, config1, nullptr, true, true, &proto); - UidMapping results; - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - - // We have to keep at least one snapshot in memory at all times. - proto.clear(); - m.appendUidMap(2, config1, nullptr, true, true, &proto); - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - - // Now add another configuration. - m.OnConfigUpdated(config2); - m.updateApp(5, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16("")); - ASSERT_EQ(1U, m.mChanges.size()); - proto.clear(); - m.appendUidMap(6, config1, nullptr, true, true, &proto); - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - ASSERT_EQ(1, results.changes_size()); - ASSERT_EQ(1U, m.mChanges.size()); - - // Add another delta update. - m.updateApp(7, String16(kApp2.c_str()), 1001, 41, String16("v41"), String16("")); - ASSERT_EQ(2U, m.mChanges.size()); - - // We still can't remove anything. - proto.clear(); - m.appendUidMap(8, config1, nullptr, true, true, &proto); - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - ASSERT_EQ(1, results.changes_size()); - ASSERT_EQ(2U, m.mChanges.size()); - - proto.clear(); - m.appendUidMap(9, config2, nullptr, true, true, &proto); - protoOutputStreamToUidMapping(&proto, &results); - ASSERT_EQ(1, results.snapshots_size()); - ASSERT_EQ(2, results.changes_size()); - // At this point both should be cleared. - ASSERT_EQ(0U, m.mChanges.size()); -} - -TEST(UidMapTest, TestMemoryComputed) { - UidMap m; - - ConfigKey config1(1, StringToId("config1")); - m.OnConfigUpdated(config1); - - size_t startBytes = m.mBytesUsed; - vector uids; - vector versions; - vector apps; - vector versionStrings; - vector installers; - uids.push_back(1000); - apps.push_back(String16(kApp1.c_str())); - versions.push_back(1); - versionStrings.push_back(String16("v1")); - installers.push_back(String16("")); - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - m.updateApp(3, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16("")); - - ProtoOutputStream proto; - vector bytes; - m.appendUidMap(2, config1, nullptr, true, true, &proto); - size_t prevBytes = m.mBytesUsed; - - m.appendUidMap(4, config1, nullptr, true, true, &proto); - EXPECT_TRUE(m.mBytesUsed < prevBytes); -} - -TEST(UidMapTest, TestMemoryGuardrail) { - UidMap m; - string buf; - - ConfigKey config1(1, StringToId("config1")); - m.OnConfigUpdated(config1); - - size_t startBytes = m.mBytesUsed; - vector uids; - vector versions; - vector versionStrings; - vector installers; - vector apps; - for (int i = 0; i < 100; i++) { - uids.push_back(1); - buf = "EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY." + to_string(i); - apps.push_back(String16(buf.c_str())); - versions.push_back(1); - versionStrings.push_back(String16("v1")); - installers.push_back(String16("")); - } - m.updateMap(1, uids, versions, versionStrings, apps, installers); - - m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2, - String16("v2"), String16("")); - ASSERT_EQ(1U, m.mChanges.size()); - - // Now force deletion by limiting the memory to hold one delta change. - m.maxBytesOverride = 120; // Since the app string alone requires >45 characters. - m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4, - String16("v4"), String16("")); - ASSERT_EQ(1U, m.mChanges.size()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp deleted file mode 100644 index 64ea219c8465..000000000000 --- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/anomaly/AlarmTracker.h" - -#include -#include -#include -#include - -using namespace testing; -using android::sp; -using std::set; -using std::shared_ptr; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -const ConfigKey kConfigKey(0, 12345); - -TEST(AlarmTrackerTest, TestTriggerTimestamp) { - sp subscriberAlarmMonitor = - new AlarmMonitor(100, - [](const shared_ptr&, int64_t){}, - [](const shared_ptr&){}); - Alarm alarm; - alarm.set_offset_millis(15 * MS_PER_SEC); - alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr - int64_t startMillis = 100000000 * MS_PER_SEC; - int64_t nextAlarmTime = startMillis / MS_PER_SEC + 15; - AlarmTracker tracker(startMillis, startMillis, alarm, kConfigKey, subscriberAlarmMonitor); - - EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); - - uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10; - std::unordered_set, SpHash> firedAlarmSet = - subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); - EXPECT_TRUE(firedAlarmSet.empty()); - tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); - EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); - EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); - - currentTimeSec = startMillis / MS_PER_SEC + 7000; - nextAlarmTime = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60; - firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); - ASSERT_EQ(firedAlarmSet.size(), 1u); - tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); - EXPECT_TRUE(firedAlarmSet.empty()); - EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); - EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); - - // Alarm fires exactly on time. - currentTimeSec = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60; - nextAlarmTime = startMillis / MS_PER_SEC + 15 + 3 * 60 * 60; - firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); - ASSERT_EQ(firedAlarmSet.size(), 1u); - tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); - EXPECT_TRUE(firedAlarmSet.empty()); - EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); - EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); - - // Alarm fires exactly 1 period late. - currentTimeSec = startMillis / MS_PER_SEC + 15 + 4 * 60 * 60; - nextAlarmTime = startMillis / MS_PER_SEC + 15 + 5 * 60 * 60; - firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); - ASSERT_EQ(firedAlarmSet.size(), 1u); - tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); - EXPECT_TRUE(firedAlarmSet.empty()); - EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime); - EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp deleted file mode 100644 index 0cc8af16c782..000000000000 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/anomaly/AnomalyTracker.h" - -#include -#include -#include - -#include - -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -const ConfigKey kConfigKey(0, 12345); - -MetricDimensionKey getMockMetricDimensionKey(int key, string value) { - int pos[] = {key, 0, 0}; - HashableDimensionKey dim; - dim.addValue(FieldValue(Field(1, pos, 0), Value(value))); - return MetricDimensionKey(dim, DEFAULT_DIMENSION_KEY); -} - -void AddValueToBucket(const std::vector>& key_value_pair_list, - std::shared_ptr bucket) { - for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) { - (*bucket)[itr->first] += itr->second; - } -} - -std::shared_ptr MockBucket( - const std::vector>& key_value_pair_list) { - std::shared_ptr bucket = std::make_shared(); - AddValueToBucket(key_value_pair_list, bucket); - return bucket; -} - -// Returns the value, for the given key, in that bucket, or 0 if not present. -int64_t getBucketValue(const std::shared_ptr& bucket, - const MetricDimensionKey& key) { - const auto& itr = bucket->find(key); - if (itr != bucket->end()) { - return itr->second; - } - return 0; -} - -// Returns true if keys in trueList are detected as anomalies and keys in falseList are not. -bool detectAnomaliesPass(AnomalyTracker& tracker, - const int64_t& bucketNum, - const std::shared_ptr& currentBucket, - const std::set& trueList, - const std::set& falseList) { - for (const MetricDimensionKey& key : trueList) { - if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) { - return false; - } - } - for (const MetricDimensionKey& key : falseList) { - if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) { - return false; - } - } - return true; -} - -// Calls tracker.detectAndDeclareAnomaly on each key in the bucket. -void detectAndDeclareAnomalies(AnomalyTracker& tracker, - const int64_t& bucketNum, - const std::shared_ptr& bucket, - const int64_t& eventTimestamp) { - for (const auto& kv : *bucket) { - tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first, - kv.second); - } -} - -// Asserts that the refractory time for each key in timestamps is the corresponding -// timestamp (in ns) + refractoryPeriodSec. -// If a timestamp value is negative, instead asserts that the refractory period is inapplicable -// (either non-existant or already past). -void checkRefractoryTimes(AnomalyTracker& tracker, - const int64_t& currTimestampNs, - const int32_t& refractoryPeriodSec, - const std::unordered_map& timestamps) { - for (const auto& kv : timestamps) { - if (kv.second < 0) { - // Make sure that, if there is a refractory period, it is already past. - EXPECT_LT(tracker.getRefractoryPeriodEndsSec(kv.first) * NS_PER_SEC, - (uint64_t)currTimestampNs) - << "Failure was at currTimestampNs " << currTimestampNs; - } else { - EXPECT_EQ(tracker.getRefractoryPeriodEndsSec(kv.first), - std::ceil(1.0 * kv.second / NS_PER_SEC) + refractoryPeriodSec) - << "Failure was at currTimestampNs " << currTimestampNs; - } - } -} - -TEST(AnomalyTrackerTest, TestConsecutiveBuckets) { - const int64_t bucketSizeNs = 30 * NS_PER_SEC; - const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC; - Alert alert; - alert.set_num_buckets(3); - alert.set_refractory_period_secs(refractoryPeriodSec); - alert.set_trigger_if_sum_gt(2); - - AnomalyTracker anomalyTracker(alert, kConfigKey); - MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a"); - MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b"); - MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c"); - - int64_t eventTimestamp0 = 10 * NS_PER_SEC; - int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC; - int64_t eventTimestamp2 = 2 * bucketSizeNs + 12 * NS_PER_SEC; - int64_t eventTimestamp3 = 3 * bucketSizeNs + 13 * NS_PER_SEC; - int64_t eventTimestamp4 = 4 * bucketSizeNs + 14 * NS_PER_SEC; - int64_t eventTimestamp5 = 5 * bucketSizeNs + 5 * NS_PER_SEC; - int64_t eventTimestamp6 = 6 * bucketSizeNs + 16 * NS_PER_SEC; - - std::shared_ptr bucket0 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}}); - std::shared_ptr bucket1 = MockBucket({{keyA, 1}}); - std::shared_ptr bucket2 = MockBucket({{keyB, 1}}); - std::shared_ptr bucket3 = MockBucket({{keyA, 2}}); - std::shared_ptr bucket4 = MockBucket({{keyB, 5}}); - std::shared_ptr bucket5 = MockBucket({{keyA, 2}}); - std::shared_ptr bucket6 = MockBucket({{keyA, 2}}); - - // Start time with no events. - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL); - - // Event from bucket #0 occurs. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 0, bucket0, {}, {keyA, keyB, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 0, bucket0, eventTimestamp1); - checkRefractoryTimes(anomalyTracker, eventTimestamp0, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}}); - - // Adds past bucket #0 - anomalyTracker.addPastBucket(bucket0, 0); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL); - - // Event from bucket #1 occurs. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1); - checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}}); - - // Adds past bucket #0 again. The sum does not change. - anomalyTracker.addPastBucket(bucket0, 0); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1 + 1); - checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}}); - - // Adds past bucket #1. - anomalyTracker.addPastBucket(bucket1, 1); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - - // Event from bucket #2 occurs. New anomaly on keyB. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2); - checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}}); - - // Adds past bucket #1 again. Nothing changes. - anomalyTracker.addPastBucket(bucket1, 1); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - // Event from bucket #2 occurs (again). - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2 + 1); - checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}}); - - // Adds past bucket #2. - anomalyTracker.addPastBucket(bucket2, 2); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - - // Event from bucket #3 occurs. New anomaly on keyA. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 3, bucket3, {keyA}, {keyB, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 3, bucket3, eventTimestamp3); - checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec, - {{keyA, eventTimestamp3}, {keyB, eventTimestamp2}, {keyC, -1}}); - - // Adds bucket #3. - anomalyTracker.addPastBucket(bucket3, 3L); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - - // Event from bucket #4 occurs. New anomaly on keyB. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 4, bucket4, {keyB}, {keyA, keyC})); - detectAndDeclareAnomalies(anomalyTracker, 4, bucket4, eventTimestamp4); - checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec, - {{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}}); - - // Adds bucket #4. - anomalyTracker.addPastBucket(bucket4, 4); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL); - - // Event from bucket #5 occurs. New anomaly on keyA, which is still in refractory. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 5, bucket5, {keyA, keyB}, {keyC})); - detectAndDeclareAnomalies(anomalyTracker, 5, bucket5, eventTimestamp5); - checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec, - {{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}}); - - // Adds bucket #5. - anomalyTracker.addPastBucket(bucket5, 5); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL); - - // Event from bucket #6 occurs. New anomaly on keyA, which is now out of refractory. - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 6, bucket6, {keyA, keyB}, {keyC})); - detectAndDeclareAnomalies(anomalyTracker, 6, bucket6, eventTimestamp6); - checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec, - {{keyA, eventTimestamp6}, {keyB, eventTimestamp4}, {keyC, -1}}); -} - -TEST(AnomalyTrackerTest, TestSparseBuckets) { - const int64_t bucketSizeNs = 30 * NS_PER_SEC; - const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC; - Alert alert; - alert.set_num_buckets(3); - alert.set_refractory_period_secs(refractoryPeriodSec); - alert.set_trigger_if_sum_gt(2); - - AnomalyTracker anomalyTracker(alert, kConfigKey); - MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a"); - MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b"); - MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c"); - MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d"); - MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e"); - - std::shared_ptr bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}}); - std::shared_ptr bucket16 = MockBucket({{keyB, 4}}); - std::shared_ptr bucket18 = MockBucket({{keyB, 1}, {keyC, 1}}); - std::shared_ptr bucket20 = MockBucket({{keyB, 3}, {keyC, 1}}); - std::shared_ptr bucket25 = MockBucket({{keyD, 1}}); - std::shared_ptr bucket28 = MockBucket({{keyE, 2}}); - - int64_t eventTimestamp1 = bucketSizeNs * 8 + 1; - int64_t eventTimestamp2 = bucketSizeNs * 15 + 11; - int64_t eventTimestamp3 = bucketSizeNs * 17 + 1; - int64_t eventTimestamp4 = bucketSizeNs * 19 + 2; - int64_t eventTimestamp5 = bucketSizeNs * 24 + 3; - int64_t eventTimestamp6 = bucketSizeNs * 27 + 3; - - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 9, bucket9, {}, {keyA, keyB, keyC, keyD})); - detectAndDeclareAnomalies(anomalyTracker, 9, bucket9, eventTimestamp1); - checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Add past bucket #9 - anomalyTracker.addPastBucket(bucket9, 9); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 16, bucket16, {keyB}, {keyA, keyC, keyD})); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L); - detectAndDeclareAnomalies(anomalyTracker, 16, bucket16, eventTimestamp2); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L); - checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Add past bucket #16 - anomalyTracker.addPastBucket(bucket16, 16); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 18, bucket18, {keyB}, {keyA, keyC, keyD})); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL); - // Within refractory period. - detectAndDeclareAnomalies(anomalyTracker, 18, bucket18, eventTimestamp3); - checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL); - - // Add past bucket #18 - anomalyTracker.addPastBucket(bucket18, 18); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD})); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4); - checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Add bucket #18 again. Nothing changes. - anomalyTracker.addPastBucket(bucket18, 18); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD})); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4 + 1); - // Within refractory period. - checkRefractoryTimes(anomalyTracker, eventTimestamp4 + 1, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Add past bucket #20 - anomalyTracker.addPastBucket(bucket20, 20); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 3LL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 25, bucket25, {}, {keyA, keyB, keyC, keyD})); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - detectAndDeclareAnomalies(anomalyTracker, 25, bucket25, eventTimestamp5); - checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec, - {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Add past bucket #25 - anomalyTracker.addPastBucket(bucket25, 25); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL); - EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyD), 1LL); - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {}, - {keyA, keyB, keyC, keyD, keyE})); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}}); - - // Updates current bucket #28. - (*bucket28)[keyE] = 5; - EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {keyE}, - {keyA, keyB, keyC, keyD})); - EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6 + 7); - ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL); - checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec, - {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, eventTimestamp6 + 7}}); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp deleted file mode 100644 index 1d501fd5a87c..000000000000 --- a/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "condition/condition_util.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -#include - -#include -#include - -using namespace android::os::statsd; -using std::vector; - -#ifdef __ANDROID__ - -TEST(ConditionTrackerTest, TestUnknownCondition) { - LogicalOperation operation = LogicalOperation::AND; - - vector children; - children.push_back(0); - children.push_back(1); - children.push_back(2); - - vector conditionResults; - conditionResults.push_back(ConditionState::kUnknown); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kTrue); - - EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults), - ConditionState::kUnknown); -} - -TEST(ConditionTrackerTest, TestAndCondition) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::AND; - - vector children; - children.push_back(0); - children.push_back(1); - children.push_back(2); - - vector conditionResults; - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kTrue); - - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kTrue); - - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); -} - -TEST(ConditionTrackerTest, TestOrCondition) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::OR; - - vector children; - children.push_back(0); - children.push_back(1); - children.push_back(2); - - vector conditionResults; - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kTrue); - - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kFalse); - - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); -} - -TEST(ConditionTrackerTest, TestNotCondition) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NOT; - - vector children; - children.push_back(0); - - vector conditionResults; - conditionResults.push_back(ConditionState::kTrue); - - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kFalse); - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); - - children.clear(); - conditionResults.clear(); - EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults), - ConditionState::kUnknown); -} - -TEST(ConditionTrackerTest, TestNandCondition) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NAND; - - vector children; - children.push_back(0); - children.push_back(1); - - vector conditionResults; - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kFalse); - - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kFalse); - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kTrue); - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); -} - -TEST(ConditionTrackerTest, TestNorCondition) { - // Set up the matcher - LogicalOperation operation = LogicalOperation::NOR; - - vector children; - children.push_back(0); - children.push_back(1); - - vector conditionResults; - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kFalse); - - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kFalse); - conditionResults.push_back(ConditionState::kFalse); - EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); - - conditionResults.clear(); - conditionResults.push_back(ConditionState::kTrue); - conditionResults.push_back(ConditionState::kTrue); - EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/condition/ConditionTimer_test.cpp b/cmds/statsd/tests/condition/ConditionTimer_test.cpp deleted file mode 100644 index ea02cd3a5ee1..000000000000 --- a/cmds/statsd/tests/condition/ConditionTimer_test.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/condition/ConditionTimer.h" - -#include -#include - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -static int64_t time_base = 10; -static int64_t ct_start_time = 200; - -TEST(ConditionTimerTest, TestTimer_Inital_False) { - ConditionTimer timer(false, time_base); - EXPECT_EQ(false, timer.mCondition); - EXPECT_EQ(0, timer.mTimerNs); - - EXPECT_EQ(0, timer.newBucketStart(ct_start_time)); - EXPECT_EQ(0, timer.mTimerNs); - - timer.onConditionChanged(true, ct_start_time + 5); - EXPECT_EQ(ct_start_time + 5, timer.mLastConditionTrueTimestampNs); - EXPECT_EQ(true, timer.mCondition); - - EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100)); - EXPECT_EQ(ct_start_time + 100, timer.mLastConditionTrueTimestampNs); - EXPECT_EQ(true, timer.mCondition); -} - -TEST(ConditionTimerTest, TestTimer_Inital_True) { - ConditionTimer timer(true, time_base); - EXPECT_EQ(true, timer.mCondition); - EXPECT_EQ(0, timer.mTimerNs); - - EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time)); - EXPECT_EQ(true, timer.mCondition); - EXPECT_EQ(0, timer.mTimerNs); - EXPECT_EQ(ct_start_time, timer.mLastConditionTrueTimestampNs); - - timer.onConditionChanged(false, ct_start_time + 5); - EXPECT_EQ(5, timer.mTimerNs); - - EXPECT_EQ(5, timer.newBucketStart(ct_start_time + 100)); - EXPECT_EQ(0, timer.mTimerNs); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp deleted file mode 100644 index 07b5311b1207..000000000000 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ /dev/null @@ -1,739 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/condition/SimpleConditionTracker.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -#include -#include -#include -#include -#include - -using std::map; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -namespace { - -const ConfigKey kConfigKey(0, 12345); - -const int ATTRIBUTION_NODE_FIELD_ID = 1; -const int ATTRIBUTION_UID_FIELD_ID = 1; -const int TAG_ID = 1; - -SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse, - bool outputSlicedUid, Position position) { - SimplePredicate simplePredicate; - simplePredicate.set_start(StringToId("WAKE_LOCK_ACQUIRE")); - simplePredicate.set_stop(StringToId("WAKE_LOCK_RELEASE")); - simplePredicate.set_stop_all(StringToId("RELEASE_ALL")); - if (outputSlicedUid) { - simplePredicate.mutable_dimensions()->set_field(TAG_ID); - simplePredicate.mutable_dimensions()->add_child()->set_field(ATTRIBUTION_NODE_FIELD_ID); - simplePredicate.mutable_dimensions()->mutable_child(0)->set_position(position); - simplePredicate.mutable_dimensions()->mutable_child(0)->add_child()->set_field( - ATTRIBUTION_UID_FIELD_ID); - } - - simplePredicate.set_count_nesting(countNesting); - simplePredicate.set_initial_value(defaultFalse ? SimplePredicate_InitialValue_FALSE - : SimplePredicate_InitialValue_UNKNOWN); - return simplePredicate; -} - -void makeWakeLockEvent(LogEvent* logEvent, uint32_t atomId, uint64_t timestamp, - const vector& uids, const string& wl, int acquire) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - vector tags(uids.size()); // vector of empty strings - writeAttribution(statsEvent, uids, tags); - - AStatsEvent_writeString(statsEvent, wl.c_str()); - AStatsEvent_writeInt32(statsEvent, acquire); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -} // anonymous namespace - - -std::map getWakeLockQueryKey( - const Position position, - const std::vector &uids, const string& conditionName) { - std::map outputKeyMap; - std::vector uid_indexes; - int pos[] = {1, 1, 1}; - int depth = 2; - Field field(1, pos, depth); - switch(position) { - case Position::FIRST: - uid_indexes.push_back(0); - break; - case Position::LAST: - uid_indexes.push_back(uids.size() - 1); - field.setField(0x02018001); - break; - case Position::ANY: - uid_indexes.resize(uids.size()); - std::iota(uid_indexes.begin(), uid_indexes.end(), 0); - field.setField(0x02010001); - break; - default: - break; - } - - for (const int idx : uid_indexes) { - Value value((int32_t)uids[idx]); - HashableDimensionKey dim; - dim.addValue(FieldValue(field, value)); - outputKeyMap[StringToId(conditionName)] = dim; - } - return outputKeyMap; -} - -TEST(SimpleConditionTrackerTest, TestNonSlicedInitialValueFalse) { - SimplePredicate simplePredicate; - simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); - simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); - simplePredicate.set_count_nesting(false); - simplePredicate.set_initial_value(SimplePredicate_InitialValue_FALSE); - - unordered_map trackerNameIndexMap; - trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; - trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), - 0 /*tracker index*/, simplePredicate, - trackerNameIndexMap); - - ConditionKey queryKey; - vector> allPredicates; - vector conditionCache(1, ConditionState::kNotEvaluated); - - // Check that initial condition is false. - conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - - vector matcherState; - vector changedCache(1, false); - - // Matched stop event. - // Check that condition is still false. - unique_ptr screenOffEvent = - CreateScreenStateChangedEvent(/*timestamp=*/50, android::view::DISPLAY_STATE_OFF); - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); // On matcher not matched - matcherState.push_back(MatchingState::kMatched); // Off matcher matched - conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.evaluateCondition(*screenOffEvent, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); - - // Matched start event. - // Check that condition has changed to true. - unique_ptr screenOnEvent = - CreateScreenStateChangedEvent(/*timestamp=*/100, android::view::DISPLAY_STATE_ON); - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); // On matcher matched - matcherState.push_back(MatchingState::kNotMatched); // Off matcher not matched - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(*screenOnEvent, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); -} - -TEST(SimpleConditionTrackerTest, TestNonSlicedInitialValueUnknown) { - SimplePredicate simplePredicate; - simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); - simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); - simplePredicate.set_count_nesting(false); - simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN); - - unordered_map trackerNameIndexMap; - trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; - trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), - 0 /*tracker index*/, simplePredicate, - trackerNameIndexMap); - - ConditionKey queryKey; - vector> allPredicates; - vector conditionCache(1, ConditionState::kNotEvaluated); - - // Check that initial condition is unknown. - conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); - EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]); - - vector matcherState; - vector changedCache(1, false); - - // Matched stop event. - // Check that condition is changed to false. - unique_ptr screenOffEvent = - CreateScreenStateChangedEvent(/*timestamp=*/50, android::view::DISPLAY_STATE_OFF); - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); // On matcher not matched - matcherState.push_back(MatchingState::kMatched); // Off matcher matched - conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.evaluateCondition(*screenOffEvent, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); - - // Matched start event. - // Check that condition has changed to true. - unique_ptr screenOnEvent = - CreateScreenStateChangedEvent(/*timestamp=*/100, android::view::DISPLAY_STATE_ON); - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); // On matcher matched - matcherState.push_back(MatchingState::kNotMatched); // Off matcher not matched - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(*screenOnEvent, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); -} - -TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { - SimplePredicate simplePredicate; - simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); - simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); - simplePredicate.set_count_nesting(false); - simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN); - - unordered_map trackerNameIndexMap; - trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; - trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*tracker index*/, - simplePredicate, trackerNameIndexMap); - EXPECT_FALSE(conditionTracker.isSliced()); - - // This event is not accessed in this test besides dimensions which is why this is okay. - // This is technically an invalid LogEvent because we do not call parseBuffer. - LogEvent event(/*uid=*/0, /*pid=*/0); - - vector matcherState; - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - - vector> allPredicates; - vector conditionCache(1, ConditionState::kNotEvaluated); - vector changedCache(1, false); - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - // not matched start or stop. condition doesn't change - EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); - - // prepare a case for match start. - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - // now condition should change to true. - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); - - // match nothing. - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); - - // the case for match stop. - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - - // condition changes to false. - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); - - // match stop again. - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - // condition should still be false. not changed. - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); -} - -TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { - std::vector> allConditions; - SimplePredicate simplePredicate; - simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); - simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); - simplePredicate.set_count_nesting(true); - - unordered_map trackerNameIndexMap; - trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0; - trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), - 0 /*condition tracker index*/, simplePredicate, - trackerNameIndexMap); - EXPECT_FALSE(conditionTracker.isSliced()); - - // This event is not accessed in this test besides dimensions which is why this is okay. - // This is technically an invalid LogEvent because we do not call parseBuffer. - LogEvent event(/*uid=*/0, /*pid=*/0); - - // one matched start - vector matcherState; - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - vector> allPredicates; - vector conditionCache(1, ConditionState::kNotEvaluated); - vector changedCache(1, false); - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); - - // prepare for another matched start. - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); - - // ONE MATCHED STOP - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - // result should still be true - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - EXPECT_FALSE(changedCache[0]); - - // ANOTHER MATCHED STOP - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - EXPECT_TRUE(changedCache[0]); -} - -TEST(SimpleConditionTrackerTest, TestSlicedCondition) { - std::vector> allConditions; - for (Position position : {Position::FIRST, Position::LAST}) { - SimplePredicate simplePredicate = getWakeLockHeldCondition( - true /*nesting*/, true /*default to false*/, true /*output slice by uid*/, - position); - string conditionName = "WL_HELD_BY_UID2"; - - unordered_map trackerNameIndexMap; - trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; - trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; - trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), - 0 /*condition tracker index*/, simplePredicate, - trackerNameIndexMap); - - std::vector uids = {111, 222, 333}; - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/1); - - // one matched start - vector matcherState; - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - vector> allPredicates; - vector conditionCache(1, ConditionState::kNotEvaluated); - vector changedCache(1, false); - - conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache, - changedCache); - - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - } else { - ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); - } - EXPECT_TRUE(changedCache[0]); - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u); - EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - } else { - EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), - uids.size()); - } - - // Now test query - const auto queryKey = getWakeLockQueryKey(position, uids, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; - - conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - - // another wake lock acquired by this uid - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/1); - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_FALSE(changedCache[0]); - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - } else { - ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); - } - EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); - EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - - - // wake lock 1 release - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/0); - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, - changedCache); - // nothing changes, because wake lock 2 is still held for this uid - EXPECT_FALSE(changedCache[0]); - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - } else { - ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); - } - EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); - EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/0); - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache, - changedCache); - ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); - EXPECT_TRUE(changedCache[0]); - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u); - EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); - } else { - EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), - uids.size()); - } - - // query again - conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - } - -} - -TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { - std::vector> allConditions; - - SimplePredicate simplePredicate = - getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/, - false /*slice output by uid*/, Position::ANY /* position */); - string conditionName = "WL_HELD"; - - unordered_map trackerNameIndexMap; - trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; - trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; - trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), - 0 /*condition tracker index*/, simplePredicate, - trackerNameIndexMap); - - EXPECT_FALSE(conditionTracker.isSliced()); - - std::vector uids1 = {111, 1111, 11111}; - string uid1_wl1 = "wl1_1"; - std::vector uids2 = {222, 2222, 22222}; - string uid2_wl1 = "wl2_1"; - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1, /*acquire=*/1); - - // one matched start for uid1 - vector matcherState; - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - vector> allPredicates; - vector conditionCache(1, ConditionState::kNotEvaluated); - vector changedCache(1, false); - - conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache, - changedCache); - - ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - EXPECT_TRUE(changedCache[0]); - - // Now test query - ConditionKey queryKey; - conditionCache[0] = ConditionState::kNotEvaluated; - - conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - - // another wake lock acquired by this uid - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1, /*acquire=*/1); - - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_FALSE(changedCache[0]); - - // uid1 wake lock 1 release - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1, - /*release=*/0); // now release it. - - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, - changedCache); - // nothing changes, because uid2 is still holding wl. - EXPECT_FALSE(changedCache[0]); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1, - /*acquire=*/0); // now release it. - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache, - changedCache); - ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); - EXPECT_TRUE(changedCache[0]); - - // query again - conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); -} - -TEST(SimpleConditionTrackerTest, TestStopAll) { - std::vector> allConditions; - for (Position position : {Position::FIRST, Position::LAST}) { - SimplePredicate simplePredicate = - getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/, - true /*output slice by uid*/, position); - string conditionName = "WL_HELD_BY_UID3"; - - unordered_map trackerNameIndexMap; - trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; - trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; - trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; - - SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), - 0 /*condition tracker index*/, simplePredicate, - trackerNameIndexMap); - - std::vector uids1 = {111, 1111, 11111}; - std::vector uids2 = {222, 2222, 22222}; - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, "wl1", /*acquire=*/1); - - // one matched start - vector matcherState; - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - vector> allPredicates; - vector conditionCache(1, ConditionState::kNotEvaluated); - vector changedCache(1, false); - - conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache, - changedCache); - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - } else { - ASSERT_EQ(uids1.size(), conditionTracker.mSlicedConditionState.size()); - } - EXPECT_TRUE(changedCache[0]); - { - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); - EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - } else { - EXPECT_EQ(uids1.size(), - conditionTracker.getChangedToTrueDimensions(allConditions)->size()); - EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - } - } - - // Now test query - const auto queryKey = getWakeLockQueryKey(position, uids1, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; - - conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - - // another wake lock acquired by uid2 - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1); - - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, - changedCache); - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(2UL, conditionTracker.mSlicedConditionState.size()); - } else { - ASSERT_EQ(uids1.size() + uids2.size(), conditionTracker.mSlicedConditionState.size()); - } - EXPECT_TRUE(changedCache[0]); - { - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); - EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - } else { - EXPECT_EQ(uids2.size(), - conditionTracker.getChangedToTrueDimensions(allConditions)->size()); - EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - } - } - - // TEST QUERY - const auto queryKey2 = getWakeLockQueryKey(position, uids2, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); - - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - - // stop all event - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1); - - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_TRUE(changedCache[0]); - ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); - { - if (position == Position::FIRST || position == Position::LAST) { - ASSERT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size()); - EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); - } else { - EXPECT_EQ(uids1.size() + uids2.size(), - conditionTracker.getChangedToFalseDimensions(allConditions)->size()); - EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); - } - } - - // TEST QUERY - const auto queryKey3 = getWakeLockQueryKey(position, uids1, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - - // TEST QUERY - const auto queryKey4 = getWakeLockQueryKey(position, uids2, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - } -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp b/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp deleted file mode 100644 index 93b278388a1b..000000000000 --- a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto alarm = config.add_alarm(); - alarm->set_id(123456); - alarm->set_offset_millis(TimeUnitToBucketSizeInMillis(TEN_MINUTES)); - alarm->set_period_millis(TimeUnitToBucketSizeInMillis(ONE_HOUR)); - - alarm = config.add_alarm(); - alarm->set_id(654321); - alarm->set_offset_millis(TimeUnitToBucketSizeInMillis(FIVE_MINUTES)); - alarm->set_period_millis(TimeUnitToBucketSizeInMillis(THIRTY_MINUTES)); - return config; -} - -} // namespace - -TEST(AlarmE2eTest, TestMultipleAlarms) { - auto config = CreateStatsdConfig(); - int64_t bucketStartTimeNs = 10000000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(2u, processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers.size()); - - auto alarmTracker1 = processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers[0]; - auto alarmTracker2 = processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers[1]; - - int64_t alarmTimestampSec0 = bucketStartTimeNs / NS_PER_SEC + 10 * 60; - int64_t alarmTimestampSec1 = bucketStartTimeNs / NS_PER_SEC + 5 * 60; - EXPECT_EQ(alarmTimestampSec0, alarmTracker1->getAlarmTimestampSec()); - EXPECT_EQ(alarmTimestampSec1, alarmTracker2->getAlarmTimestampSec()); - - // Alarm fired. - const int64_t alarmFiredTimestampSec0 = alarmTimestampSec1 + 5; - auto alarmSet = processor->getPeriodicAlarmMonitor()->popSoonerThan( - static_cast(alarmFiredTimestampSec0)); - ASSERT_EQ(1u, alarmSet.size()); - processor->onPeriodicAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet); - EXPECT_EQ(alarmTimestampSec0, alarmTracker1->getAlarmTimestampSec()); - EXPECT_EQ(alarmTimestampSec1 + 30 * 60, alarmTracker2->getAlarmTimestampSec()); - - // Alarms fired very late. - const int64_t alarmFiredTimestampSec1 = alarmTimestampSec0 + 2 * 60 * 60 + 125; - alarmSet = processor->getPeriodicAlarmMonitor()->popSoonerThan( - static_cast(alarmFiredTimestampSec1)); - ASSERT_EQ(2u, alarmSet.size()); - processor->onPeriodicAlarmFired(alarmFiredTimestampSec1 * NS_PER_SEC, alarmSet); - EXPECT_EQ(alarmTimestampSec0 + 60 * 60 * 3, alarmTracker1->getAlarmTimestampSec()); - EXPECT_EQ(alarmTimestampSec1 + 30 * 60 * 5, alarmTracker2->getAlarmTimestampSec()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp deleted file mode 100644 index af9436b98ec8..000000000000 --- a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig(int num_buckets, int threshold, int refractory_period_sec) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - - *config.add_atom_matcher() = wakelockAcquireMatcher; - - auto countMetric = config.add_count_metric(); - countMetric->set_id(123456); - countMetric->set_what(wakelockAcquireMatcher.id()); - *countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - countMetric->set_bucket(FIVE_MINUTES); - - auto alert = config.add_alert(); - alert->set_id(StringToId("alert")); - alert->set_metric_id(123456); - alert->set_num_buckets(num_buckets); - alert->set_refractory_period_secs(refractory_period_sec); - alert->set_trigger_if_sum_gt(threshold); - return config; -} - -} // namespace - -TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) { - const int num_buckets = 1; - const int threshold = 3; - const int refractory_period_sec = 10; - auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); - const uint64_t alert_id = config.alert(0).id(); - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - sp anomalyTracker = - processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; - - std::vector attributionUids1 = {111}; - std::vector attributionTags1 = {"App1"}; - std::vector attributionUids2 = {111, 222}; - std::vector attributionTags2 = {"App1", "GMSCoreModule1"}; - std::vector attributionUids3 = {111, 333}; - std::vector attributionTags3 = {"App1", "App3"}; - std::vector attributionUids4 = {222, 333}; - std::vector attributionTags4 = {"GMSCoreModule1", "App3"}; - std::vector attributionUids5 = {222}; - std::vector attributionTags5 = {"GMSCoreModule1"}; - - FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), - Value((int32_t)111)); - HashableDimensionKey whatKey1({fieldValue1}); - MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); - - FieldValue fieldValue2(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), - Value((int32_t)222)); - HashableDimensionKey whatKey2({fieldValue2}); - MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); - - auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids4, attributionTags4, - "wl2"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2, - "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids5, attributionTags5, - "wl2"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids3, attributionTags3, - "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids5, attributionTags5, - "wl2"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); - - // Fired alarm and refractory period end timestamp updated. - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 5, attributionUids1, attributionTags1, - "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 100, attributionUids1, attributionTags1, - "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids4, - attributionTags4, "wl2"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids5, - attributionTags5, "wl2"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 3, attributionUids5, - attributionTags5, "wl2"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 4, attributionUids5, - attributionTags5, "wl2"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -} - -TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) { - const int num_buckets = 3; - const int threshold = 3; - const int refractory_period_sec = 10; - auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); - const uint64_t alert_id = config.alert(0).id(); - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - sp anomalyTracker = - processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; - - std::vector attributionUids1 = {111}; - std::vector attributionTags1 = {"App1"}; - std::vector attributionUids2 = {111, 222}; - std::vector attributionTags2 = {"App1", "GMSCoreModule1"}; - - FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), - Value((int32_t)111)); - HashableDimensionKey whatKey1({fieldValue1}); - MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); - - auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2, - "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Fired alarm and refractory period end timestamp updated. - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids1, attributionTags1, - "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 2, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -} - -TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written) { - const int num_buckets = 1; - const int threshold = 0; - const int refractory_period_sec = 86400 * 365; // 1 year - auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); - const int64_t alert_id = config.alert(0).id(); - - int64_t bucketStartTimeNs = 10000000000; - - int configUid = 2000; - int64_t configId = 1000; - ConfigKey cfgKey(configUid, configId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - metadata::StatsMetadataList result; - int64_t mockWallClockNs = 1584991200 * NS_PER_SEC; - int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC; - processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result); - - ASSERT_EQ(result.stats_metadata_size(), 0); -} - -TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk) { - const int num_buckets = 1; - const int threshold = 0; - const int refractory_period_sec = 86400 * 365; // 1 year - auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); - const int64_t alert_id = config.alert(0).id(); - - int64_t bucketStartTimeNs = 10000000000; - - int configUid = 2000; - int64_t configId = 1000; - ConfigKey cfgKey(configUid, configId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - sp anomalyTracker = - processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; - - std::vector attributionUids1 = {111}; - std::vector attributionTags1 = {"App1"}; - std::vector attributionUids2 = {111, 222}; - std::vector attributionTags2 = {"App1", "GMSCoreModule1"}; - - FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), - Value((int32_t)111)); - HashableDimensionKey whatKey1({fieldValue1}); - MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); - - auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - metadata::StatsMetadataList result; - int64_t mockWallClockNs = 1584991200 * NS_PER_SEC; - int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC; - processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result); - - metadata::StatsMetadata statsMetadata = result.stats_metadata(0); - ASSERT_EQ(result.stats_metadata_size(), 1); - EXPECT_EQ(statsMetadata.config_key().config_id(), configId); - EXPECT_EQ(statsMetadata.config_key().uid(), configUid); - - metadata::AlertMetadata alertMetadata = statsMetadata.alert_metadata(0); - ASSERT_EQ(statsMetadata.alert_metadata_size(), 1); - EXPECT_EQ(alertMetadata.alert_id(), alert_id); - metadata::AlertDimensionKeyedData keyedData = alertMetadata.alert_dim_keyed_data(0); - ASSERT_EQ(alertMetadata.alert_dim_keyed_data_size(), 1); - EXPECT_EQ(keyedData.last_refractory_ends_sec(), - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) - - mockElapsedTimeNs / NS_PER_SEC + - mockWallClockNs / NS_PER_SEC); - - metadata::MetricDimensionKey metadataDimKey = keyedData.dimension_key(); - metadata::FieldValue dimKeyInWhat = metadataDimKey.dimension_key_in_what(0); - EXPECT_EQ(dimKeyInWhat.field().tag(), fieldValue1.mField.getTag()); - EXPECT_EQ(dimKeyInWhat.field().field(), fieldValue1.mField.getField()); - EXPECT_EQ(dimKeyInWhat.value_int(), fieldValue1.mValue.int_value); -} - -TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk) { - const int num_buckets = 1; - const int threshold = 0; - const int refractory_period_sec = 86400 * 365; // 1 year - auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); - const int64_t alert_id = config.alert(0).id(); - - int64_t bucketStartTimeNs = 10000000000; - - int configUid = 2000; - int64_t configId = 1000; - ConfigKey cfgKey(configUid, configId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - sp anomalyTracker = - processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; - - std::vector attributionUids1 = {111}; - std::vector attributionTags1 = {"App1"}; - std::vector attributionUids2 = {111, 222}; - std::vector attributionTags2 = {"App1", "GMSCoreModule1"}; - - FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), - Value((int32_t)111)); - HashableDimensionKey whatKey1({fieldValue1}); - MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); - - auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(event.get()); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - int64_t mockWallClockNs = 1584991200 * NS_PER_SEC; - int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC; - processor->SaveMetadataToDisk(mockWallClockNs, mockElapsedTimeNs); - - auto processor2 = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - int64_t mockElapsedTimeSinceBoot = 10 * NS_PER_SEC; - processor2->LoadMetadataFromDisk(mockWallClockNs, mockElapsedTimeSinceBoot); - - sp anomalyTracker2 = - processor2->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; - EXPECT_EQ(anomalyTracker2->getRefractoryPeriodEndsSec(dimensionKey1) - - mockElapsedTimeSinceBoot / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) - - mockElapsedTimeNs / NS_PER_SEC); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp deleted file mode 100644 index 70e7365ec238..000000000000 --- a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp +++ /dev/null @@ -1,527 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include - -#include - -#include "src/StatsLogProcessor.h" -#include "src/StatsService.h" -#include "src/anomaly/DurationAnomalyTracker.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -using ::ndk::SharedRefBase; - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -const int kConfigKey = 789130124; -const int kCallingUid = 0; - -StatsdConfig CreateStatsdConfig(int num_buckets, - uint64_t threshold_ns, - DurationMetric::AggregationType aggregationType, - bool nesting) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = screenIsOffPredicate; - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - FieldMatcher dimensions = CreateAttributionUidDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock. - *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; - holdingWakelockPredicate.mutable_simple_predicate()->set_count_nesting(nesting); - *config.add_predicate() = holdingWakelockPredicate; - - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("WakelockDuration")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->set_condition(screenIsOffPredicate.id()); - durationMetric->set_aggregation_type(aggregationType); - *durationMetric->mutable_dimensions_in_what() = - CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - durationMetric->set_bucket(FIVE_MINUTES); - - auto alert = config.add_alert(); - alert->set_id(StringToId("alert")); - alert->set_metric_id(StringToId("WakelockDuration")); - alert->set_num_buckets(num_buckets); - alert->set_refractory_period_secs(2); - alert->set_trigger_if_sum_gt(threshold_ns); - return config; -} - -std::vector attributionUids1 = {111, 222}; -std::vector attributionTags1 = {"App1", "GMSCoreModule1"}; - -std::vector attributionUids2 = {111, 222}; -std::vector attributionTags2 = {"App2", "GMSCoreModule1"}; - -std::vector attributionUids3 = {222}; -std::vector attributionTags3 = {"GMSCoreModule1"}; - -MetricDimensionKey dimensionKey1( - HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED, - (int32_t)0x02010101), - Value((int32_t)111))}), - DEFAULT_DIMENSION_KEY); - -MetricDimensionKey dimensionKey2( - HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED, - (int32_t)0x02010101), Value((int32_t)222))}), - DEFAULT_DIMENSION_KEY); - -void sendConfig(shared_ptr& service, const StatsdConfig& config) { - string str; - config.SerializeToString(&str); - std::vector configAsVec(str.begin(), str.end()); - service->addConfiguration(kConfigKey, configAsVec, kCallingUid); -} - -} // namespace - -TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) { - const int num_buckets = 1; - const uint64_t threshold_ns = NS_PER_SEC; - auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); - const uint64_t alert_id = config.alert(0).id(); - const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); - - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - sendConfig(service, config); - - auto processor = service->mProcessor; - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - int64_t bucketStartTimeNs = processor->mTimeBaseNs; - int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6; - - sp anomalyTracker = - processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; - - auto screen_on_event = CreateScreenStateChangedEvent( - bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - auto screen_off_event = CreateScreenStateChangedEvent( - bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - processor->OnLogEvent(screen_on_event.get()); - processor->OnLogEvent(screen_off_event.get()); - - // Acquire wakelock wl1. - auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 11, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event. - auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 101, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get()); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Acquire wakelock wl1 within bucket #0. - acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 110, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Release wakelock wl1. One anomaly detected. - release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 109, - attributionUids2, attributionTags2, "wl1"); - processor->OnLogEvent(release_event.get()); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Acquire wakelock wl1. - acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 112, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the - // end of the refractory period. - const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, - (uint32_t)alarmFiredTimestampSec0); - EXPECT_EQ(alarmFiredTimestampSec0, - processor->getAnomalyAlarmMonitor()->getRegisteredAlarmTimeSec()); - - // Anomaly alarm fired. - auto alarmTriggerEvent = CreateBatterySaverOnEvent(alarmFiredTimestampSec0 * NS_PER_SEC); - processor->OnLogEvent(alarmTriggerEvent.get(), alarmFiredTimestampSec0 * NS_PER_SEC); - - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Release wakelock wl1. - release_event = - CreateReleaseWakelockEvent(alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get()); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - // Within refractory period. No more anomaly detected. - EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Acquire wakelock wl1. - acquire_event = CreateAcquireWakelockEvent( - roundedBucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(acquire_event.get()); - const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1); - EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC, - (uint64_t)alarmFiredTimestampSec1); - - // Release wakelock wl1. - int64_t release_event_time = roundedBucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( - static_cast(alarmFiredTimestampSec1)); - ASSERT_EQ(0u, alarmSet.size()); - - // Acquire wakelock wl1 near the end of bucket #0. - acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - 2, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - - // Release the event at early bucket #1. - release_event_time = roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - // Anomaly detected when stopping the alarm. The refractory period does not change. - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Condition changes to false. - screen_on_event = - CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 20, - android::view::DisplayStateEnum::DISPLAY_STATE_ON); - processor->OnLogEvent(screen_on_event.get()); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - - acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 30, - attributionUids2, attributionTags2, "wl1"); - processor->OnLogEvent(acquire_event.get()); - // The condition is false. Do not start the alarm. - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Condition turns true. - screen_off_event = - CreateScreenStateChangedEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - processor->OnLogEvent(screen_off_event.get()); - EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - - // Condition turns to false. - int64_t condition_false_time = bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1; - screen_on_event = CreateScreenStateChangedEvent( - condition_false_time, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - processor->OnLogEvent(screen_on_event.get(), condition_false_time); - // Condition turns to false. Cancelled the alarm. - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - // Detected one anomaly. - EXPECT_EQ(refractory_period_sec + - (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Condition turns to true again. - screen_off_event = - CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - processor->OnLogEvent(screen_off_event.get()); - EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - - release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(release_event.get()); - EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); -} - -TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) { - const int num_buckets = 3; - const uint64_t threshold_ns = NS_PER_SEC; - auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); - const uint64_t alert_id = config.alert(0).id(); - const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); - - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - sendConfig(service, config); - - auto processor = service->mProcessor; - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - int64_t bucketStartTimeNs = processor->mTimeBaseNs; - int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6; - - sp anomalyTracker = - processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; - - auto screen_off_event = CreateScreenStateChangedEvent( - bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - processor->OnLogEvent(screen_off_event.get()); - - // Acquire wakelock "wc1" in bucket #0. - auto acquire_event = - CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Release wakelock "wc1" in bucket #0. - int64_t release_event_time = roundedBucketStartTimeNs + bucketSizeNs - 1; - auto release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Acquire wakelock "wc1" in bucket #1. - acquire_event = - CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC + 1, - attributionUids2, attributionTags2, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - release_event_time = roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC + 100; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Acquire wakelock "wc2" in bucket #2. - acquire_event = - CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + 1, - attributionUids3, attributionTags3, "wl2"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3, - anomalyTracker->getAlarmTimestampSec(dimensionKey2)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); - - // Release wakelock "wc2" in bucket #2. - release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids3, - attributionTags3, "wl2"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); - EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); - - // Acquire wakelock "wc1" in bucket #2. - acquire_event = - CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, - attributionUids2, attributionTags2, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3 + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Release wakelock "wc1" in bucket #2. - release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 3.5 * NS_PER_SEC; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2, - attributionTags2, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC + 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 6 * bucketSizeNs + 4, - attributionUids3, attributionTags3, "wl2"); - processor->OnLogEvent(acquire_event.get()); - acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 6 * bucketSizeNs + 5, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 2, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ((roundedBucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 2, - anomalyTracker->getAlarmTimestampSec(dimensionKey2)); - - release_event_time = roundedBucketStartTimeNs + 6 * bucketSizeNs + NS_PER_SEC + 2; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids3, - attributionTags3, "wl2"); - processor->OnLogEvent(release_event.get(), release_event_time); - release_event = CreateReleaseWakelockEvent(release_event_time + 4, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time + 4); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); - // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered. - EXPECT_EQ(refractory_period_sec + - (int64_t)(roundedBucketStartTimeNs + 6 * bucketSizeNs + NS_PER_SEC) / - NS_PER_SEC + - 1, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -} - -TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) { - const int num_buckets = 2; - const uint64_t threshold_ns = 3 * NS_PER_SEC; - auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false); - const uint64_t alert_id = config.alert(0).id(); - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6; - const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC; - config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec); - - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - sendConfig(service, config); - - auto processor = service->mProcessor; - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); - - int64_t bucketStartTimeNs = processor->mTimeBaseNs; - int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC; - - sp anomalyTracker = - processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; - - auto screen_off_event = CreateScreenStateChangedEvent( - bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - processor->OnLogEvent(screen_off_event.get()); - - // Acquire wakelock "wc1" in bucket #0. - auto acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - 100, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Acquire the wakelock "wc1" again. - acquire_event = - CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - // The alarm does not change. - EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // Anomaly alarm fired late. - const int64_t firedAlarmTimestampNs = roundedBucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC; - auto alarmTriggerEvent = CreateBatterySaverOnEvent(firedAlarmTimestampNs); - processor->OnLogEvent(alarmTriggerEvent.get(), firedAlarmTimestampNs); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs - 100, - attributionUids1, attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - int64_t release_event_time = bucketStartTimeNs + 2 * bucketSizeNs + 1; - auto release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - // Within the refractory period. No anomaly. - EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - // A new wakelock, but still within refractory period. - acquire_event = CreateAcquireWakelockEvent( - roundedBucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - - release_event = - CreateReleaseWakelockEvent(roundedBucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC, - attributionUids1, attributionTags1, "wl1"); - // Still in the refractory period. No anomaly. - processor->OnLogEvent(release_event.get()); - EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); - - acquire_event = CreateAcquireWakelockEvent( - roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 5, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - - release_event_time = roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 4; - release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(release_event.get(), release_event_time); - EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); - - acquire_event = CreateAcquireWakelockEvent( - roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 3, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(acquire_event.get()); - EXPECT_EQ((roundedBucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC + 1, - anomalyTracker->getAlarmTimestampSec(dimensionKey1)); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp deleted file mode 100644 index 4c2caa904f6a..000000000000 --- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include -#include - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig(const Position position) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - auto attributionNodeMatcher = - wakelockAcquireMatcher.mutable_simple_atom_matcher()->add_field_value_matcher(); - attributionNodeMatcher->set_field(1); - attributionNodeMatcher->set_position(Position::ANY); - auto uidMatcher = attributionNodeMatcher->mutable_matches_tuple()->add_field_value_matcher(); - uidMatcher->set_field(1); // uid field. - uidMatcher->set_eq_string("com.android.gmscore"); - - *config.add_atom_matcher() = wakelockAcquireMatcher; - - auto countMetric = config.add_count_metric(); - countMetric->set_id(123456); - countMetric->set_what(wakelockAcquireMatcher.id()); - *countMetric->mutable_dimensions_in_what() = - CreateAttributionUidAndTagDimensions( - util::WAKELOCK_STATE_CHANGED, {position}); - countMetric->set_bucket(FIVE_MINUTES); - return config; -} - -// GMS core node is in the middle. -std::vector attributionUids1 = {111, 222, 333}; -std::vector attributionTags1 = {"App1", "GMSCoreModule1", "App3"}; - -// GMS core node is the last one. -std::vector attributionUids2 = {111, 333, 222}; -std::vector attributionTags2 = {"App1", "App3", "GMSCoreModule1"}; - -// GMS core node is the first one. -std::vector attributionUids3 = {222, 333}; -std::vector attributionTags3 = {"GMSCoreModule1", "App3"}; - -// Single GMS core node. -std::vector attributionUids4 = {222}; -std::vector attributionTags4 = {"GMSCoreModule1"}; - -// GMS core has another uid. -std::vector attributionUids5 = {111, 444, 333}; -std::vector attributionTags5 = {"App1", "GMSCoreModule2", "App3"}; - -// Multiple GMS core nodes. -std::vector attributionUids6 = {444, 222}; -std::vector attributionTags6 = {"GMSCoreModule2", "GMSCoreModule1"}; - -// No GMS core nodes -std::vector attributionUids7 = {111, 333}; -std::vector attributionTags7 = {"App1", "App3"}; - -std::vector attributionUids8 = {111}; -std::vector attributionTags8 = {"App1"}; - -// GMS core node with isolated uid. -const int isolatedUid = 666; -std::vector attributionUids9 = {isolatedUid}; -std::vector attributionTags9 = {"GMSCoreModule3"}; - -std::vector attributionUids10 = {isolatedUid}; -std::vector attributionTags10 = {"GMSCoreModule1"}; - -} // namespace - -TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) { - auto config = CreateStatsdConfig(Position::FIRST); - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - - // Here it assumes that GMS core has two uids. - processor->getUidMap()->updateMap( - 1, {222, 444, 111, 333}, {1, 1, 2, 2}, - {String16("v1"), String16("v1"), String16("v2"), String16("v2")}, - {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), - String16("APP3")}, - {String16(""), String16(""), String16(""), String16("")}); - - std::vector> events; - // Events 1~4 are in the 1st bucket. - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, - attributionTags1, "wl1")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2, - attributionTags2, "wl1")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, - attributionUids3, attributionTags3, "wl1")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4, - attributionTags4, "wl1")); - - // Events 5~8 are in the 3rd bucket. - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, - attributionUids5, attributionTags5, "wl2")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, - attributionUids6, attributionTags6, "wl2")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2, - attributionUids7, attributionTags7, "wl2")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs, - attributionUids8, attributionTags8, "wl2")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, - attributionUids9, attributionTags9, "wl2")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100, - attributionUids9, attributionTags9, "wl2")); - events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222, - isolatedUid, true /*is_create*/)); - events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222, - isolatedUid, false /*is_create*/)); - - sortLogEventsByTimestamp(&events); - - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(countMetrics.data_size(), 4); - - auto data = countMetrics.data(0); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 111, "App1"); - ASSERT_EQ(data.bucket_info_size(), 2); - EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).count(), 1); - EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), - bucketStartTimeNs + 2 * bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); - - data = countMetrics.data(1); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ASSERT_EQ(data.bucket_info_size(), 2); - EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).count(), 1); - EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); - - data = countMetrics.data(2); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule3"); - ASSERT_EQ(data.bucket_info_size(), 1); - EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), - bucketStartTimeNs + 3 * bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs); - - data = countMetrics.data(3); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 444, - "GMSCoreModule2"); - ASSERT_EQ(data.bucket_info_size(), 1); - EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), - bucketStartTimeNs + 2 * bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); -} - -TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) { - auto config = CreateStatsdConfig(Position::ALL); - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - - // Here it assumes that GMS core has two uids. - processor->getUidMap()->updateMap( - 1, {222, 444, 111, 333}, {1, 1, 2, 2}, - {String16("v1"), String16("v1"), String16("v2"), String16("v2")}, - {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), - String16("APP3")}, - {String16(""), String16(""), String16(""), String16("")}); - - std::vector> events; - // Events 1~4 are in the 1st bucket. - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, - attributionTags1, "wl1")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2, - attributionTags2, "wl1")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, - attributionUids3, attributionTags3, "wl1")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4, - attributionTags4, "wl1")); - - // Events 5~8 are in the 3rd bucket. - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, - attributionUids5, attributionTags5, "wl2")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, - attributionUids6, attributionTags6, "wl2")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2, - attributionUids7, attributionTags7, "wl2")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs, - attributionUids8, attributionTags8, "wl2")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, - attributionUids10, attributionTags10, "wl2")); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100, - attributionUids10, attributionTags10, "wl2")); - events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222, - isolatedUid, true /*is_create*/)); - events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222, - isolatedUid, false /*is_create*/)); - - sortLogEventsByTimestamp(&events); - - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(countMetrics.data_size(), 6); - - auto data = countMetrics.data(0); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(1, data.bucket_info(1).count()); - EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, - data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = countMetrics.data(1); - ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 222); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 333); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, - util::WAKELOCK_STATE_CHANGED, 333, "App3"); - ASSERT_EQ(data.bucket_info_size(), 1); - EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - - data = countMetrics.data(2); - ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 444); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, - util::WAKELOCK_STATE_CHANGED, 444, - "GMSCoreModule2"); - ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 222); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ASSERT_EQ(data.bucket_info_size(), 1); - EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(3); - ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 111); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, - util::WAKELOCK_STATE_CHANGED, 111, "App1"); - ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 222); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 333); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, - util::WAKELOCK_STATE_CHANGED, 333, "App3"); - ASSERT_EQ(data.bucket_info_size(), 1); - EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(4); - ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 111); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, - util::WAKELOCK_STATE_CHANGED, 111, "App1"); - ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 333); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, - util::WAKELOCK_STATE_CHANGED, 333, "App3"); - ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 222); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, - util::WAKELOCK_STATE_CHANGED, 222, - "GMSCoreModule1"); - ASSERT_EQ(data.bucket_info_size(), 1); - EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(5); - ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 111); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, - util::WAKELOCK_STATE_CHANGED, 111, "App1"); - ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 444); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, - util::WAKELOCK_STATE_CHANGED, 444, - "GMSCoreModule2"); - ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 333); - ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, - util::WAKELOCK_STATE_CHANGED, 333, "App3"); - ASSERT_EQ(data.bucket_info_size(), 1); - EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp deleted file mode 100644 index 0bce0baa049b..000000000000 --- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); - - *config.add_atom_matcher() = wakelockAcquireMatcher; - - auto countMetric = config.add_count_metric(); - countMetric->set_id(123456); - countMetric->set_what(wakelockAcquireMatcher.id()); - *countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - countMetric->set_bucket(FIVE_MINUTES); - - auto alert = config.add_alert(); - alert->set_id(StringToId("alert")); - alert->set_metric_id(123456); - alert->set_num_buckets(num_buckets); - alert->set_refractory_period_secs(10); - alert->set_trigger_if_sum_gt(threshold); - - // Two hours - config.set_ttl_in_seconds(2 * 3600); - return config; -} - -} // namespace - -TEST(ConfigTtlE2eTest, TestCountMetric) { - const int num_buckets = 1; - const int threshold = 3; - auto config = CreateStatsdConfig(num_buckets, threshold); - const uint64_t alert_id = config.alert(0).id(); - const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - - std::vector attributionUids1 = {111}; - std::vector attributionTags1 = {"App1"}; - - FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), - Value((int32_t)111)); - HashableDimensionKey whatKey1({fieldValue1}); - MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); - - FieldValue fieldValue2(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), - Value((int32_t)222)); - HashableDimensionKey whatKey2({fieldValue2}); - MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); - - auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(event.get()); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids1, - attributionTags1, "wl2"); - processor->OnLogEvent(event.get()); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * bucketSizeNs + 2, attributionUids1, - attributionTags1, "wl1"); - processor->OnLogEvent(event.get()); - - EXPECT_EQ((int64_t)(bucketStartTimeNs + 25 * bucketSizeNs + 2 + 2 * 3600 * NS_PER_SEC), - processor->mMetricsManagers.begin()->second->getTtlEndNs()); - - // Clear the data stored on disk as a result of the ttl. - vector buffer; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 25 * bucketSizeNs + 3, false, true, - ADB_DUMP, FAST, &buffer); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp deleted file mode 100644 index 04eb40080631..000000000000 --- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp +++ /dev/null @@ -1,901 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "src/StatsLogProcessor.h" -#include "src/state/StateManager.h" -#include "src/state/StateTracker.h" -#include "tests/statsd_test_util.h" - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -/** - * Tests the initial condition and condition after the first log events for - * count metrics with either a combination condition or simple condition. - * - * Metrics should be initialized with condition kUnknown (given that the - * predicate is using the default InitialValue of UNKNOWN). The condition should - * be updated to either kFalse or kTrue if a condition event is logged for all - * children conditions. - */ -TEST(CountMetricE2eTest, TestInitialConditionChanges) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - - auto syncStartMatcher = CreateSyncStartAtomMatcher(); - *config.add_atom_matcher() = syncStartMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = CreateBatteryStateNoneMatcher(); - *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); - - auto screenOnPredicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = screenOnPredicate; - - auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate(); - *config.add_predicate() = deviceUnpluggedPredicate; - - auto screenOnOnBatteryPredicate = config.add_predicate(); - screenOnOnBatteryPredicate->set_id(StringToId("screenOnOnBatteryPredicate")); - screenOnOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenOnPredicate, screenOnOnBatteryPredicate); - addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOnOnBatteryPredicate); - - // CountSyncStartWhileScreenOnOnBattery (CombinationCondition) - CountMetric* countMetric1 = config.add_count_metric(); - countMetric1->set_id(StringToId("CountSyncStartWhileScreenOnOnBattery")); - countMetric1->set_what(syncStartMatcher.id()); - countMetric1->set_condition(screenOnOnBatteryPredicate->id()); - countMetric1->set_bucket(FIVE_MINUTES); - - // CountSyncStartWhileOnBattery (SimpleCondition) - CountMetric* countMetric2 = config.add_count_metric(); - countMetric2->set_id(StringToId("CountSyncStartWhileOnBatterySliceScreen")); - countMetric2->set_what(syncStartMatcher.id()); - countMetric2->set_condition(deviceUnpluggedPredicate.id()); - countMetric2->set_bucket(FIVE_MINUTES); - - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - const uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - EXPECT_EQ(2, metricsManager->mAllMetricProducers.size()); - - sp metricProducer1 = metricsManager->mAllMetricProducers[0]; - sp metricProducer2 = metricsManager->mAllMetricProducers[1]; - - EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); - - auto screenOnEvent = - CreateScreenStateChangedEvent(bucketStartTimeNs + 30, android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); - - auto pluggedUsbEvent = CreateBatteryStateChangedEvent( - bucketStartTimeNs + 50, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); - processor->OnLogEvent(pluggedUsbEvent.get()); - EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kFalse, metricProducer2->mCondition); - - auto pluggedNoneEvent = CreateBatteryStateChangedEvent( - bucketStartTimeNs + 70, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE); - processor->OnLogEvent(pluggedNoneEvent.get()); - EXPECT_EQ(ConditionState::kTrue, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition); -} - -/** -* Test a count metric that has one slice_by_state with no primary fields. -* -* Once the CountMetricProducer is initialized, it has one atom id in -* mSlicedStateAtoms and no entries in mStateGroupMap. - -* One StateTracker tracks the state atom, and it has one listener which is the -* CountMetricProducer that was initialized. -*/ -TEST(CountMetricE2eTest, TestSlicedState) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto syncStartMatcher = CreateSyncStartAtomMatcher(); - *config.add_atom_matcher() = syncStartMatcher; - - auto state = CreateScreenState(); - *config.add_state() = state; - - // Create count metric that slices by screen state. - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(syncStartMatcher.id()); - countMetric->set_bucket(TimeUnit::FIVE_MINUTES); - countMetric->add_slice_by_state(state.id()); - - // Initialize StatsLogProcessor. - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - const uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - // Check that CountMetricProducer was initialized correctly. - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0); - - // Check that StateTrackers were initialized correctly. - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - - /* - bucket #1 bucket #2 - | 1 2 3 4 5 6 7 8 9 10 (minutes) - |-----------------------------|-----------------------------|-- - x x x x x x (syncStartEvents) - | | (ScreenIsOnEvent) - | | (ScreenIsOffEvent) - | (ScreenDozeEvent) - */ - // Initialize log events - first bucket. - std::vector attributionUids1 = {123}; - std::vector attributionTags1 = {"App1"}; - - std::vector> events; - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 50 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 1:00 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 75 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 1:25 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 150 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 2:40 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 200 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 3:30 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 4:20 - - // Initialize log events - second bucket. - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 350 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 6:00 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 6:50 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 450 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 7:40 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 475 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 8:05 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 500 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 8:30 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 520 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 8:50 - - // Send log events to StatsLogProcessor. - for (auto& event : events) { - processor->OnLogEvent(event.get()); - } - - // Check dump report. - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, - FAST, &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(3, countMetrics.data_size()); - - // For each CountMetricData, check StateValue info is correct and buckets - // have correct counts. - auto data = countMetrics.data(0); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(1); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(2, data.bucket_info(1).count()); - - data = countMetrics.data(2); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(1, data.bucket_info(1).count()); -} - -/** - * Test a count metric that has one slice_by_state with a mapping and no - * primary fields. - * - * Once the CountMetricProducer is initialized, it has one atom id in - * mSlicedStateAtoms and has one entry per state value in mStateGroupMap. - * - * One StateTracker tracks the state atom, and it has one listener which is the - * CountMetricProducer that was initialized. - */ -TEST(CountMetricE2eTest, TestSlicedStateWithMap) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto syncStartMatcher = CreateSyncStartAtomMatcher(); - *config.add_atom_matcher() = syncStartMatcher; - - int64_t screenOnId = 4444; - int64_t screenOffId = 9876; - auto state = CreateScreenStateWithOnOffMap(screenOnId, screenOffId); - *config.add_state() = state; - - // Create count metric that slices by screen state with on/off map. - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(syncStartMatcher.id()); - countMetric->set_bucket(TimeUnit::FIVE_MINUTES); - countMetric->add_slice_by_state(state.id()); - - // Initialize StatsLogProcessor. - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - const uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - // Check that StateTrackers were initialized correctly. - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - - // Check that CountMetricProducer was initialized correctly. - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1); - - StateMap map = state.map(); - for (auto group : map.group()) { - for (auto value : group.value()) { - EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value), - group.group_id()); - } - } - - /* - bucket #1 bucket #2 - | 1 2 3 4 5 6 7 8 9 10 (minutes) - |-----------------------------|-----------------------------|-- - x x x x x x x x x (syncStartEvents) - -----------------------------------------------------------SCREEN_OFF events - | | (ScreenStateOffEvent = 1) - | | (ScreenStateDozeEvent = 3) - | (ScreenStateDozeSuspendEvent = - 4) - -----------------------------------------------------------SCREEN_ON events - | | (ScreenStateOnEvent = 2) - | (ScreenStateVrEvent = 5) - | (ScreenStateOnSuspendEvent = 6) - */ - // Initialize log events - first bucket. - std::vector attributionUids1 = {123}; - std::vector attributionTags1 = {"App1"}; - - std::vector> events; - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 20 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 0:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 30 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 0:40 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 1:10 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 90 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:40 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 120 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 2:10 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 150 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:40 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 180 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_VR)); // 3:10 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 3:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 210 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:40 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 4:20 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 280 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:50 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 285 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 4:55 - - // Initialize log events - second bucket. - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 360 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 6:10 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 390 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 430 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND)); // 7:20 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 440 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 7:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 540 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 9:10 - events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 570 * NS_PER_SEC, attributionUids1, - attributionTags1, "sync_name")); // 9:40 - - // Send log events to StatsLogProcessor. - for (auto& event : events) { - processor->OnLogEvent(event.get()); - } - - // Check dump report. - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, - FAST, &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(3, countMetrics.data_size()); - - // For each CountMetricData, check StateValue info is correct and buckets - // have correct counts. - auto data = countMetrics.data(0); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(1); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(1, data.bucket_info(1).count()); - - data = countMetrics.data(2); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(4, data.bucket_info(0).count()); - EXPECT_EQ(2, data.bucket_info(1).count()); -} - -/** -* Test a count metric that has one slice_by_state with a primary field. - -* Once the CountMetricProducer is initialized, it should have one -* MetricStateLink stored. State querying using a non-empty primary key -* should also work as intended. -*/ -TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto appCrashMatcher = - CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED); - *config.add_atom_matcher() = appCrashMatcher; - - auto state = CreateUidProcessState(); - *config.add_state() = state; - - // Create count metric that slices by uid process state. - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(appCrashMatcher.id()); - countMetric->set_bucket(TimeUnit::FIVE_MINUTES); - countMetric->add_slice_by_state(state.id()); - MetricStateLink* stateLink = countMetric->add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/}); - - // Initialize StatsLogProcessor. - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - const uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - // Check that StateTrackers were initialized correctly. - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); - - // Check that CountMetricProducer was initialized correctly. - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0); - ASSERT_EQ(metricProducer->mMetric2StateLinks.size(), 1); - - /* - NOTE: "1" or "2" represents the uid associated with the state/app crash event - bucket #1 bucket #2 - | 1 2 3 4 5 6 7 8 9 10 - |------------------------|-------------------------|-- - 1 1 1 1 1 2 1 1 2 (AppCrashEvents) - -----------------------------------------------------PROCESS STATE events - 1 2 (TopEvent = 1002) - 1 1 (ForegroundServiceEvent = 1003) - 2 (ImportantBackgroundEvent = 1006) - 1 1 1 (ImportantForegroundEvent = 1005) - - Based on the diagram above, an AppCrashEvent querying for process state value would return: - - StateTracker::kStateUnknown - - Important foreground - - Top - - Important foreground - - Foreground service - - Top (both the app crash and state still have matching uid = 2) - - - Foreground service - - Foreground service - - Important background - */ - // Initialize log events - first bucket. - std::vector> events; - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:40 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 1:40 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/)); // 2:10 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 2:40 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/)); // 3:30 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 210 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 3:40 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/)); // 4:20 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 4:50 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/)); // 4:55 - - // Initialize log events - second bucket. - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/)); // 6:10 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 390 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 6:40 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 430 * NS_PER_SEC, 2 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 7:20 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 440 * NS_PER_SEC, 1 /*uid*/)); // 7:30 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 9:10 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/)); // 9:40 - - // Send log events to StatsLogProcessor. - for (auto& event : events) { - processor->OnLogEvent(event.get()); - } - - // Check dump report. - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, - FAST, &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(5, countMetrics.data_size()); - - // For each CountMetricData, check StateValue info is correct and buckets - // have correct counts. - auto data = countMetrics.data(0); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(1); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); - - data = countMetrics.data(2); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(2, data.bucket_info(1).count()); - - data = countMetrics.data(3); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); - - data = countMetrics.data(4); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); -} - -TEST(CountMetricE2eTest, TestMultipleSlicedStates) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto appCrashMatcher = - CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED); - *config.add_atom_matcher() = appCrashMatcher; - - int64_t screenOnId = 4444; - int64_t screenOffId = 9876; - auto state1 = CreateScreenStateWithOnOffMap(screenOnId, screenOffId); - *config.add_state() = state1; - auto state2 = CreateUidProcessState(); - *config.add_state() = state2; - - // Create count metric that slices by screen state with on/off map and - // slices by uid process state. - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(appCrashMatcher.id()); - countMetric->set_bucket(TimeUnit::FIVE_MINUTES); - countMetric->add_slice_by_state(state1.id()); - countMetric->add_slice_by_state(state2.id()); - MetricStateLink* stateLink = countMetric->add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/}); - - // Initialize StatsLogProcessor. - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - const uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - // Check that StateTrackers were properly initialized. - EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); - - // Check that CountMetricProducer was initialized correctly. - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 2); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), UID_PROCESS_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1); - ASSERT_EQ(metricProducer->mMetric2StateLinks.size(), 1); - - StateMap map = state1.map(); - for (auto group : map.group()) { - for (auto value : group.value()) { - EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value), - group.group_id()); - } - } - - /* - bucket #1 bucket #2 - | 1 2 3 4 5 6 7 8 9 10 (minutes) - |------------------------|------------------------|-- - 1 1 1 1 1 2 1 1 2 (AppCrashEvents) - ---------------------------------------------------SCREEN_OFF events - | | (ScreenOffEvent = 1) - | | (ScreenDozeEvent = 3) - ---------------------------------------------------SCREEN_ON events - | | (ScreenOnEvent = 2) - | (ScreenOnSuspendEvent = 6) - ---------------------------------------------------PROCESS STATE events - 1 2 (TopEvent = 1002) - 1 (ForegroundServiceEvent = 1003) - 2 (ImportantBackgroundEvent = 1006) - 1 1 1 (ImportantForegroundEvent = 1005) - - Based on the diagram above, Screen State / Process State pairs for each - AppCrashEvent are: - - StateTracker::kStateUnknown / important foreground - - off / important foreground - - off / Top - - on / important foreground - - off / important foreground - - off / top - - - off / important foreground - - off / foreground service - - on / important background - - */ - // Initialize log events - first bucket. - std::vector> events; - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 5 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:15 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 30 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 0:40 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 1:40 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 90 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:40 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/)); // 2:10 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 2:40 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 160 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:50 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/)); // 3:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 210 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:40 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/)); // 4:20 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 4:50 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/)); // 4:55 - - // Initialize log events - second bucket. - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/)); // 6:10 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 380 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 6:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 390 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 420 * NS_PER_SEC, 2 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 7:10 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 440 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 7:30 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 450 * NS_PER_SEC, 1 /*uid*/)); // 7:40 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 520 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 8:50 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 9:10 - events.push_back( - CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/)); // 9:40 - - // Send log events to StatsLogProcessor. - for (auto& event : events) { - processor->OnLogEvent(event.get()); - } - - // Check dump report. - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, - FAST, &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(6, countMetrics.data_size()); - - // For each CountMetricData, check StateValue info is correct and buckets - // have correct counts. - auto data = countMetrics.data(0); - ASSERT_EQ(2, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(-1, data.slice_by_state(0).value()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); - EXPECT_TRUE(data.slice_by_state(1).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(1); - ASSERT_EQ(2, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); - EXPECT_TRUE(data.slice_by_state(1).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(2); - ASSERT_EQ(2, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); - EXPECT_TRUE(data.slice_by_state(1).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(3); - ASSERT_EQ(2, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); - EXPECT_TRUE(data.slice_by_state(1).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); - - data = countMetrics.data(4); - ASSERT_EQ(2, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); - EXPECT_TRUE(data.slice_by_state(1).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - - data = countMetrics.data(5); - ASSERT_EQ(2, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); - EXPECT_TRUE(data.slice_by_state(1).has_value()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).count()); - EXPECT_EQ(1, data.bucket_info(1).count()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp deleted file mode 100644 index 4efb038e538d..000000000000 --- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp +++ /dev/null @@ -1,1473 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include - -#include "src/StatsLogProcessor.h" -#include "src/state/StateTracker.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -TEST(DurationMetricE2eTest, TestOneBucket) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = screenOffMatcher; - - auto durationPredicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = durationPredicate; - - int64_t metricId = 123456; - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(metricId); - durationMetric->set_what(durationPredicate.id()); - durationMetric->set_bucket(FIVE_MINUTES); - durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); - - const int64_t baseTimeNs = 0; // 0:00 - const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 - const int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; - - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - - auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - - std::unique_ptr event; - - // Screen is off at start of bucket. - event = CreateScreenStateChangedEvent(configAddedTimeNs, - android::view::DISPLAY_STATE_OFF); // 0:01 - processor->OnLogEvent(event.get()); - - // Turn screen on. - const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11 - event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(event.get()); - - // Turn off screen 30 seconds after turning on. - const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41 - event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(event.get()); - - event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64); // 0:42 - processor->OnLogEvent(event.get()); - - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true, - ADB_DUMP, FAST, &buffer); // 5:01 - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -TEST(DurationMetricE2eTest, TestTwoBuckets) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = screenOffMatcher; - - auto durationPredicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = durationPredicate; - - int64_t metricId = 123456; - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(metricId); - durationMetric->set_what(durationPredicate.id()); - durationMetric->set_bucket(FIVE_MINUTES); - durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); - - const int64_t baseTimeNs = 0; // 0:00 - const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 - const int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; - - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - - auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - - std::unique_ptr event; - - // Screen is off at start of bucket. - event = CreateScreenStateChangedEvent(configAddedTimeNs, - android::view::DISPLAY_STATE_OFF); // 0:01 - processor->OnLogEvent(event.get()); - - // Turn screen on. - const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11 - event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(event.get()); - - // Turn off screen 30 seconds after turning on. - const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41 - event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(event.get()); - - event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64); // 0:42 - processor->OnLogEvent(event.get()); - - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false, - true, ADB_DUMP, FAST, &buffer); // 10:01 - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_EQ(1, data.bucket_info_size()); - - auto bucketInfo = data.bucket_info(0); - EXPECT_EQ(0, bucketInfo.bucket_num()); - EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos()); - EXPECT_EQ(configAddedTimeNs, bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -} - -TEST(DurationMetricE2eTest, TestWithActivation) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = screenOffMatcher; - *config.add_atom_matcher() = crashMatcher; - - auto durationPredicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = durationPredicate; - - int64_t metricId = 123456; - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(metricId); - durationMetric->set_what(durationPredicate.id()); - durationMetric->set_bucket(FIVE_MINUTES); - durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); - - auto metric_activation1 = config.add_metric_activation(); - metric_activation1->set_metric_id(metricId); - auto event_activation1 = metric_activation1->add_event_activation(); - event_activation1->set_atom_matcher_id(crashMatcher.id()); - event_activation1->set_ttl_seconds(30); // 30 secs. - - const int64_t bucketStartTimeNs = 10000000000; - const int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; - - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - vector activeConfigsBroadcast; - - int broadcastCount = 0; - StatsLogProcessor processor( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, - [](const ConfigKey& key) { return true; }, - [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, - const vector& activeConfigs) { - broadcastCount++; - EXPECT_EQ(broadcastUid, uid); - activeConfigsBroadcast.clear(); - activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), - activeConfigs.end()); - return true; - }); - - processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); // 0:00 - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - ASSERT_EQ(eventActivationMap.size(), 1u); - EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); - - std::unique_ptr event; - - // Turn screen off. - event = CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * NS_PER_SEC, - android::view::DISPLAY_STATE_OFF); // 0:02 - processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC); - - // Turn screen on. - const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05 - event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), durationStartNs); - - // Activate metric. - const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10 - const int64_t activationEndNs = - activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40 - event = CreateAppCrashEvent(activationStartNs, 111); - processor.OnLogEvent(event.get(), activationStartNs); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 1); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); - - // Expire activation. - const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC; - event = CreateScreenBrightnessChangedEvent(expirationNs, 64); // 0:47 - processor.OnLogEvent(event.get(), expirationNs); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 2); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - ASSERT_EQ(eventActivationMap.size(), 1u); - EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); - - // Turn off screen 10 seconds after activation expiration. - const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50 - event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF); - processor.OnLogEvent(event.get(), durationEndNs); - - // Turn screen on. - const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55 - event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), duration2StartNs); - - // Turn off screen. - const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05 - event = CreateScreenStateChangedEvent(duration2EndNs, android::view::DISPLAY_STATE_OFF); - processor.OnLogEvent(event.get(), duration2EndNs); - - // Activate metric. - const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10 - const int64_t activation2EndNs = - activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40 - event = CreateAppCrashEvent(activation2StartNs, 211); - processor.OnLogEvent(event.get(), activation2StartNs); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 3); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, activation2StartNs); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); - - ConfigMetricsReportList reports; - vector buffer; - processor.onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true, - ADB_DUMP, FAST, &buffer); // 5:01 - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_EQ(1, data.bucket_info_size()); - - auto bucketInfo = data.bucket_info(0); - EXPECT_EQ(0, bucketInfo.bucket_num()); - EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(expirationNs, bucketInfo.end_bucket_elapsed_nanos()); - EXPECT_EQ(expirationNs - durationStartNs, bucketInfo.duration_nanos()); -} - -TEST(DurationMetricE2eTest, TestWithCondition) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); - *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - *config.add_predicate() = holdingWakelockPredicate; - - auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); - *config.add_predicate() = isInBackgroundPredicate; - - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("WakelockDuration")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->set_condition(isInBackgroundPredicate.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - ConfigKey cfgKey; - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_TRUE(eventActivationMap.empty()); - - int appUid = 123; - vector attributionUids1 = {appUid}; - vector attributionTags1 = {"App1"}; - - auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1, - attributionTags1, - "wl1"); // 0:10 - processor->OnLogEvent(event.get()); - - event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22 - processor->OnLogEvent(event.get()); - - event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, - appUid); // 3:15 - processor->OnLogEvent(event.get()); - - event = CreateReleaseWakelockEvent(bucketStartTimeNs + 4 * 60 * NS_PER_SEC, attributionUids1, - attributionTags1, - "wl1"); // 4:00 - processor->OnLogEvent(event.get()); - - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, - FAST, &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - - // Validate bucket info. - ASSERT_EQ(1, data.bucket_info_size()); - - auto bucketInfo = data.bucket_info(0); - EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); - EXPECT_EQ((2 * 60 + 53) * NS_PER_SEC, bucketInfo.duration_nanos()); -} - -TEST(DurationMetricE2eTest, TestWithSlicedCondition) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); - *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - // The predicate is dimensioning by first attribution node by uid. - FieldMatcher dimensions = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, - {Position::FIRST}); - *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; - *config.add_predicate() = holdingWakelockPredicate; - - auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); - *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); - *config.add_predicate() = isInBackgroundPredicate; - - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("WakelockDuration")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->set_condition(isInBackgroundPredicate.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - // The metric is dimensioning by first attribution node and only by uid. - *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - durationMetric->set_bucket(FIVE_MINUTES); - - // Links between wakelock state atom and condition of app is in background. - auto links = durationMetric->add_links(); - links->set_condition(isInBackgroundPredicate.id()); - auto dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED); - dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); - - ConfigKey cfgKey; - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_TRUE(eventActivationMap.empty()); - - int appUid = 123; - std::vector attributionUids1 = {appUid}; - std::vector attributionTags1 = {"App1"}; - - auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1, - attributionTags1, "wl1"); // 0:10 - processor->OnLogEvent(event.get()); - - event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22 - processor->OnLogEvent(event.get()); - - event = CreateReleaseWakelockEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1, - attributionTags1, "wl1"); // 1:00 - processor->OnLogEvent(event.get()); - - event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, - appUid); // 3:15 - processor->OnLogEvent(event.get()); - - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, - FAST, &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - // Validate dimension value. - ValidateAttributionUidDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, appUid); - // Validate bucket info. - ASSERT_EQ(1, data.bucket_info_size()); - - auto bucketInfo = data.bucket_info(0); - EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); - EXPECT_EQ(38 * NS_PER_SEC, bucketInfo.duration_nanos()); -} - -TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); - *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); - *config.add_atom_matcher() = screenOnMatcher; - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - // The predicate is dimensioning by first attribution node by uid. - FieldMatcher dimensions = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, - {Position::FIRST}); - *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; - *config.add_predicate() = holdingWakelockPredicate; - - auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); - *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); - *config.add_predicate() = isInBackgroundPredicate; - - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("WakelockDuration")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->set_condition(isInBackgroundPredicate.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - // The metric is dimensioning by first attribution node and only by uid. - *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - durationMetric->set_bucket(FIVE_MINUTES); - - // Links between wakelock state atom and condition of app is in background. - auto links = durationMetric->add_links(); - links->set_condition(isInBackgroundPredicate.id()); - auto dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED); - dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); - - auto metric_activation1 = config.add_metric_activation(); - metric_activation1->set_metric_id(durationMetric->id()); - auto event_activation1 = metric_activation1->add_event_activation(); - event_activation1->set_atom_matcher_id(screenOnMatcher.id()); - event_activation1->set_ttl_seconds(60 * 2); // 2 minutes. - - ConfigKey cfgKey; - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - ASSERT_EQ(eventActivationMap.size(), 1u); - EXPECT_TRUE(eventActivationMap.find(4) != eventActivationMap.end()); - EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[4]->start_ns, 0); - EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); - - int appUid = 123; - std::vector attributionUids1 = {appUid}; - std::vector attributionTags1 = {"App1"}; - - auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1, - attributionTags1, "wl1"); // 0:10 - processor->OnLogEvent(event.get()); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[4]->start_ns, 0); - EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); - - event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22 - processor->OnLogEvent(event.get()); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[4]->start_ns, 0); - EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); - - const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC; // 0:30 - event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(event.get()); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs); - EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); - - const int64_t durationEndNs = - durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC; // 3:00 - event = CreateAppCrashEvent(durationEndNs, 333); - processor->OnLogEvent(event.get()); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs); - EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); - - event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, - appUid); // 3:15 - processor->OnLogEvent(event.get()); - - event = CreateReleaseWakelockEvent(bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC, - attributionUids1, attributionTags1, "wl1"); // 4:17 - processor->OnLogEvent(event.get()); - - event = CreateMoveToBackgroundEvent(bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC, - appUid); // 4:20 - processor->OnLogEvent(event.get()); - - event = CreateAcquireWakelockEvent(bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC, - attributionUids1, attributionTags1, "wl1"); // 4:25 - processor->OnLogEvent(event.get()); - - const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC; // 4:30 - event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(event.get()); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[4]->start_ns, duration2StartNs); - EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); - - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, - FAST, &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(1, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - // Validate dimension value. - ValidateAttributionUidDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, appUid); - // Validate bucket info. - ASSERT_EQ(2, data.bucket_info_size()); - - auto bucketInfo = data.bucket_info(0); - EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(durationEndNs, bucketInfo.end_bucket_elapsed_nanos()); - EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos()); - - bucketInfo = data.bucket_info(1); - EXPECT_EQ(durationEndNs, bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos()); -} - -TEST(DurationMetricE2eTest, TestWithSlicedState) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher(); - *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher(); - - auto batterySaverModePredicate = CreateBatterySaverModePredicate(); - *config.add_predicate() = batterySaverModePredicate; - - auto screenState = CreateScreenState(); - *config.add_state() = screenState; - - // Create duration metric that slices by screen state. - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("DurationBatterySaverModeSliceScreen")); - durationMetric->set_what(batterySaverModePredicate.id()); - durationMetric->add_slice_by_state(screenState.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - // Initialize StatsLogProcessor. - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - uint64_t bucketStartTimeNs = 10000000000; // 0:10 - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - EXPECT_TRUE(metricsManager->isActive()); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - EXPECT_TRUE(metricProducer->mIsActive); - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0); - - // Check that StateTrackers were initialized correctly. - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - - /* - bucket #1 bucket #2 - | 1 2 3 4 5 6 7 8 9 10 (minutes) - |-----------------------------|-----------------------------|-- - ON OFF ON (BatterySaverMode) - | | | (ScreenIsOnEvent) - | | (ScreenIsOffEvent) - | (ScreenDozeEvent) - */ - // Initialize log events. - std::vector> events; - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 10 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:20 - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 50 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:00 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 80 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 1:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 120 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:10 - events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 250 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:20 - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 - - // Bucket boundary. - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 310 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 5:20 - - // Send log events to StatsLogProcessor. - for (auto& event : events) { - processor->OnLogEvent(event.get()); - } - - // Check dump report. - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 360 * NS_PER_SEC, - true /* include current partial bucket */, true, ADB_DUMP, FAST, - &buffer); // 6:10 - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(3, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(370 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(1); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(110 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(370 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(2); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher(); - *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher(); - *config.add_atom_matcher() = CreateBatteryStateNoneMatcher(); - *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); - - auto batterySaverModePredicate = CreateBatterySaverModePredicate(); - *config.add_predicate() = batterySaverModePredicate; - - auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate(); - *config.add_predicate() = deviceUnpluggedPredicate; - - auto screenState = CreateScreenState(); - *config.add_state() = screenState; - - // Create duration metric that has a condition and slices by screen state. - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("DurationBatterySaverModeOnBatterySliceScreen")); - durationMetric->set_what(batterySaverModePredicate.id()); - durationMetric->set_condition(deviceUnpluggedPredicate.id()); - durationMetric->add_slice_by_state(screenState.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - // Initialize StatsLogProcessor. - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - uint64_t bucketStartTimeNs = 10000000000; // 0:10 - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - EXPECT_TRUE(metricsManager->isActive()); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - EXPECT_TRUE(metricProducer->mIsActive); - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0); - - // Check that StateTrackers were initialized correctly. - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - - /* - bucket #1 bucket #2 - | 1 2 3 4 5 6 7 8 (minutes) - |---------------------------------------|------------------ - ON OFF ON (BatterySaverMode) - T F T (DeviceUnpluggedPredicate) - | | | (ScreenIsOnEvent) - | | | (ScreenIsOffEvent) - | (ScreenDozeEvent) - */ - // Initialize log events. - std::vector> events; - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 20 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:30 - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 80 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:30 - events.push_back( - CreateBatteryStateChangedEvent(bucketStartTimeNs + 110 * NS_PER_SEC, - BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // 2:00 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 145 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:35 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 170 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 3:00 - events.push_back( - CreateBatteryStateChangedEvent(bucketStartTimeNs + 180 * NS_PER_SEC, - BatteryPluggedStateEnum::BATTERY_PLUGGED_USB)); // 3:10 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 200 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:30 - events.push_back( - CreateBatteryStateChangedEvent(bucketStartTimeNs + 230 * NS_PER_SEC, - BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // 4:00 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 260 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 4:30 - events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 - - // Bucket boundary. - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 320 * NS_PER_SEC)); // 5:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 380 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 6:30 - - // Send log events to StatsLogProcessor. - for (auto& event : events) { - processor->OnLogEvent(event.get()); - } - - // Check dump report. - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 410 * NS_PER_SEC, - true /* include current partial bucket */, true, ADB_DUMP, FAST, - &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(3, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(45 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(420 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(1); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(45 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(60 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(420 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(2); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -TEST(DurationMetricE2eTest, TestWithSlicedStateMapped) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher(); - *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher(); - - auto batterySaverModePredicate = CreateBatterySaverModePredicate(); - *config.add_predicate() = batterySaverModePredicate; - - int64_t screenOnId = 4444; - int64_t screenOffId = 9876; - auto screenStateWithMap = CreateScreenStateWithOnOffMap(screenOnId, screenOffId); - *config.add_state() = screenStateWithMap; - - // Create duration metric that slices by mapped screen state. - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("DurationBatterySaverModeSliceScreenMapped")); - durationMetric->set_what(batterySaverModePredicate.id()); - durationMetric->add_slice_by_state(screenStateWithMap.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - // Initialize StatsLogProcessor. - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - uint64_t bucketStartTimeNs = 10000000000; // 0:10 - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - EXPECT_TRUE(metricsManager->isActive()); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - EXPECT_TRUE(metricProducer->mIsActive); - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1); - - // Check that StateTrackers were initialized correctly. - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - - /* - bucket #1 bucket #2 - | 1 2 3 4 5 6 7 8 9 10 (minutes) - |-----------------------------|-----------------------------|-- - ON OFF ON (BatterySaverMode) - ---------------------------------------------------------SCREEN_OFF events - | | (ScreenStateOffEvent = 1) - | (ScreenStateDozeEvent = 3) - | (ScreenStateDozeSuspendEvent = 4) - ---------------------------------------------------------SCREEN_ON events - | | | (ScreenStateOnEvent = 2) - | (ScreenStateVrEvent = 5) - | (ScreenStateOnSuspendEvent = 6) - */ - // Initialize log events. - std::vector> events; - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 10 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:20 - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 70 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:20 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 100 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 1:50 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 120 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:10 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 170 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_VR)); // 3:00 - events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 250 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:20 - events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 - - // Bucket boundary 5:10. - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 320 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 5:30 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 390 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40 - events.push_back(CreateScreenStateChangedEvent( - bucketStartTimeNs + 430 * NS_PER_SEC, - android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND)); // 7:20 - // Send log events to StatsLogProcessor. - for (auto& event : events) { - processor->OnLogEvent(event.get()); - } - - // Check dump report. - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 490 * NS_PER_SEC, - true /* include current partial bucket */, true, ADB_DUMP, FAST, - &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(2, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(130 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(110 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(500 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(1); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(70 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(80 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(500 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); -} - -TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - *config.add_predicate() = holdingWakelockPredicate; - - auto uidProcessState = CreateUidProcessState(); - *config.add_state() = uidProcessState; - - // Create duration metric that slices by uid process state. - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("DurationHoldingWakelockSliceUidProcessState")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->add_slice_by_state(uidProcessState.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - // The state has only one primary field (uid). - auto stateLink = durationMetric->add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - - // Initialize StatsLogProcessor. - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - uint64_t bucketStartTimeNs = 10000000000; // 0:10 - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - // This config is rejected because the dimension in what fields are not a superset of the sliced - // state primary fields. - ASSERT_EQ(processor->mMetricsManagers.size(), 0); -} - -TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset) { - // Initialize config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - *config.add_predicate() = holdingWakelockPredicate; - - auto uidProcessState = CreateUidProcessState(); - *config.add_state() = uidProcessState; - - // Create duration metric that slices by uid process state. - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("DurationPartialWakelockPerTagUidSliceProcessState")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->add_slice_by_state(uidProcessState.id()); - durationMetric->set_aggregation_type(DurationMetric::SUM); - durationMetric->set_bucket(FIVE_MINUTES); - - // The metric is dimensioning by first uid of attribution node and tag. - *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidAndOtherDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}, {3 /* tag */}); - // The state has only one primary field (uid). - auto stateLink = durationMetric->add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - - // Initialize StatsLogProcessor. - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - uint64_t bucketStartTimeNs = 10000000000; // 0:10 - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - EXPECT_TRUE(metricsManager->isActive()); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - EXPECT_TRUE(metricProducer->mIsActive); - ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); - EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID); - ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0); - - // Check that StateTrackers were initialized correctly. - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); - - // Initialize log events. - int appUid1 = 1001; - int appUid2 = 1002; - std::vector attributionUids1 = {appUid1}; - std::vector attributionTags1 = {"App1"}; - - std::vector attributionUids2 = {appUid2}; - std::vector attributionTags2 = {"App2"}; - - std::vector> events; - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 10 * NS_PER_SEC, appUid1, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:20 - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 20 * NS_PER_SEC, - attributionUids1, attributionTags1, - "wakelock1")); // 0:30 - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * NS_PER_SEC, - attributionUids1, attributionTags1, - "wakelock2")); // 0:35 - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 30 * NS_PER_SEC, - attributionUids2, attributionTags2, - "wakelock1")); // 0:40 - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC, - attributionUids2, attributionTags2, - "wakelock2")); // 0:45 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 50 * NS_PER_SEC, appUid2, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 1:00 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 60 * NS_PER_SEC, appUid1, - android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 1:10 - events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 100 * NS_PER_SEC, - attributionUids2, attributionTags2, - "wakelock1")); // 1:50 - events.push_back(CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 120 * NS_PER_SEC, appUid2, - android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 2:10 - events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 200 * NS_PER_SEC, - attributionUids1, attributionTags1, - "wakelock2")); // 3:30 - - // Send log events to StatsLogProcessor. - for (auto& event : events) { - processor->OnLogEvent(event.get()); - } - - // Check dump report. - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 320 * NS_PER_SEC, - true /* include current partial bucket */, true, ADB_DUMP, FAST, - &buffer); - ASSERT_GT(buffer.size(), 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); - StatsLogReport::DurationMetricDataWrapper durationMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), - &durationMetrics); - ASSERT_EQ(9, durationMetrics.data_size()); - - DurationMetricData data = durationMetrics.data(0); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1, - "wakelock1"); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(1); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1, - "wakelock1"); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(240 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(330 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(2); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1, - "wakelock2"); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(3); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1, - "wakelock2"); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(140 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(4); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2, - "wakelock1"); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(-1 /* StateTracker:: kStateUnknown */, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(5); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2, - "wakelock1"); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(6); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2, - "wakelock2"); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(-1 /* StateTracker:: kStateUnknown */, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(15 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(7); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2, - "wakelock2"); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, - data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(180 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).duration_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(330 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos()); - - data = durationMetrics.data(8); - ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2, - "wakelock2"); - ASSERT_EQ(1, data.slice_by_state_size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(70 * NS_PER_SEC, data.bucket_info(0).duration_nanos()); - EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp deleted file mode 100644 index 1be261297e0a..000000000000 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp +++ /dev/null @@ -1,624 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -using ::ndk::SharedRefBase; - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -const int64_t metricId = 123456; -const int32_t ATOM_TAG = util::SUBSYSTEM_SLEEP_STATE; - -StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type, - bool useCondition = true) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", ATOM_TAG); - *config.add_atom_matcher() = atomMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = screenIsOffPredicate; - - auto gaugeMetric = config.add_gauge_metric(); - gaugeMetric->set_id(metricId); - gaugeMetric->set_what(atomMatcher.id()); - if (useCondition) { - gaugeMetric->set_condition(screenIsOffPredicate.id()); - } - gaugeMetric->set_sampling_type(sampling_type); - gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true); - *gaugeMetric->mutable_dimensions_in_what() = - CreateDimensions(ATOM_TAG, {1 /* subsystem name */}); - gaugeMetric->set_bucket(FIVE_MINUTES); - gaugeMetric->set_max_pull_delay_sec(INT_MAX); - config.set_hash_strings_in_metric_report(false); - - return config; -} - -} // namespaces - -TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { - auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); - int64_t baseTimeNs = getElapsedRealtimeNs(); - int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = - CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, - SharedRefBase::make(), ATOM_TAG); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - processor->mPullerManager->ForceClearPullerCache(); - - int startBucketNum = processor->mMetricsManagers.begin() - ->second->mAllMetricProducers[0] - ->getCurrentBucketNum(); - EXPECT_GT(startBucketNum, (int64_t)0); - - // When creating the config, the gauge metric producer should register the alarm at the - // end of the current bucket. - ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); - EXPECT_EQ(bucketSizeNs, - processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); - int64_t& nextPullTimeNs = - processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); - - auto screenOffEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - // Pulling alarm arrives on time and reset the sequential pulling alarm. - processor->informPullAlarmFired(nextPullTimeNs + 1); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); - - auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10, - android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - - screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100, - android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - processor->informPullAlarmFired(nextPullTimeNs + 1); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); - - processor->informPullAlarmFired(nextPullTimeNs + 1); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs); - - screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2, - android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - - processor->informPullAlarmFired(nextPullTimeNs + 3); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); - - screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1, - android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - processor->informPullAlarmFired(nextPullTimeNs + 2); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs); - - processor->informPullAlarmFired(nextPullTimeNs + 2); - - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); - ASSERT_GT((int)gaugeMetrics.data_size(), 1); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* subsystem name field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); - ASSERT_EQ(6, data.bucket_info_size()); - - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); - ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(1, data.bucket_info(1).atom_size()); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(1, data.bucket_info(2).atom_size()); - ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(1, data.bucket_info(3).atom_size()); - ASSERT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1, data.bucket_info(3).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(3).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(3).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(1, data.bucket_info(4).atom_size()); - ASSERT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(4).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(4).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(4).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(1, data.bucket_info(5).atom_size()); - ASSERT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2, data.bucket_info(5).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(5).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(5).atom(0).subsystem_sleep_state().time_millis(), 0); -} - -TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) { - auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE); - int64_t baseTimeNs = getElapsedRealtimeNs(); - int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = - CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, - SharedRefBase::make(), ATOM_TAG); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - processor->mPullerManager->ForceClearPullerCache(); - - int startBucketNum = processor->mMetricsManagers.begin() - ->second->mAllMetricProducers[0] - ->getCurrentBucketNum(); - EXPECT_GT(startBucketNum, (int64_t)0); - - auto screenOffEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10, - android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - - screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100, - android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2, - android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - - screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1, - android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 3, - android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 10, - android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); - ASSERT_GT((int)gaugeMetrics.data_size(), 1); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* subsystem name field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); - ASSERT_EQ(3, data.bucket_info_size()); - - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); - ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(1, data.bucket_info(1).atom_size()); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100, data.bucket_info(1).elapsed_timestamp_nanos(0)); - EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(2, data.bucket_info(2).atom_size()); - ASSERT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10, data.bucket_info(2).elapsed_timestamp_nanos(1)); - EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); - EXPECT_TRUE(data.bucket_info(2).atom(1).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0); -} - -TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) { - auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); - int64_t baseTimeNs = getElapsedRealtimeNs(); - int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = - CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, - SharedRefBase::make(), ATOM_TAG); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - processor->mPullerManager->ForceClearPullerCache(); - - int startBucketNum = processor->mMetricsManagers.begin() - ->second->mAllMetricProducers[0] - ->getCurrentBucketNum(); - EXPECT_GT(startBucketNum, (int64_t)0); - - // When creating the config, the gauge metric producer should register the alarm at the - // end of the current bucket. - ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); - EXPECT_EQ(bucketSizeNs, - processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); - int64_t& nextPullTimeNs = - processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); - - auto screenOffEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10, - android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - - // Pulling alarm arrives one bucket size late. - processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); - - screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 11, - android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - // Pulling alarm arrives more than one bucket size late. - processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs + 12); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); - - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); - ASSERT_GT((int)gaugeMetrics.data_size(), 1); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* subsystem name field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); - ASSERT_EQ(3, data.bucket_info_size()); - - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(1, data.bucket_info(1).atom_size()); - EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11, - data.bucket_info(1).elapsed_timestamp_nanos(0)); - EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(1, data.bucket_info(2).atom_size()); - ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12, data.bucket_info(2).elapsed_timestamp_nanos(0)); - EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); -} - -TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) { - auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false); - - int64_t baseTimeNs = getElapsedRealtimeNs(); - int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; - - auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher(); - *config.add_atom_matcher() = batterySaverStartMatcher; - const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. - auto metric_activation = config.add_metric_activation(); - metric_activation->set_metric_id(metricId); - metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); - auto event_activation = metric_activation->add_event_activation(); - event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); - event_activation->set_ttl_seconds(ttlNs / 1000000000); - - ConfigKey cfgKey; - auto processor = - CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, - SharedRefBase::make(), ATOM_TAG); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - processor->mPullerManager->ForceClearPullerCache(); - - int startBucketNum = processor->mMetricsManagers.begin() - ->second->mAllMetricProducers[0] - ->getCurrentBucketNum(); - EXPECT_GT(startBucketNum, (int64_t)0); - EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); - - // When creating the config, the gauge metric producer should register the alarm at the - // end of the current bucket. - ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); - EXPECT_EQ(bucketSizeNs, - processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); - int64_t& nextPullTimeNs = - processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); - - // Pulling alarm arrives on time and reset the sequential pulling alarm. - // Event should not be kept. - processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns. - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); - EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); - - // Activate the metric. A pull occurs upon activation. - const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis. - auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs); - processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms. - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); - - // This event should be kept. 2 total. - processor->informPullAlarmFired(nextPullTimeNs + 1); // 20 mins + 1 ns. - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); - - // This event should be kept. 3 total. - processor->informPullAlarmFired(nextPullTimeNs + 2); // 25 mins + 2 ns. - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs); - - // Create random event to deactivate metric. - auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50); - processor->OnLogEvent(deactivationEvent.get()); - EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); - - // Event should not be kept. 3 total. - processor->informPullAlarmFired(nextPullTimeNs + 3); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); - - processor->informPullAlarmFired(nextPullTimeNs + 2); - - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); - ASSERT_GT((int)gaugeMetrics.data_size(), 0); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* subsystem name field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); - ASSERT_EQ(3, data.bucket_info_size()); - - auto bucketInfo = data.bucket_info(0); - ASSERT_EQ(1, bucketInfo.atom_size()); - ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); - EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0)); - ASSERT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); - EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); - - bucketInfo = data.bucket_info(1); - ASSERT_EQ(1, bucketInfo.atom_size()); - ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0)); - ASSERT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); - EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); - - bucketInfo = data.bucket_info(2); - ASSERT_EQ(1, bucketInfo.atom_size()); - ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0)); - ASSERT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); - EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)), - bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)), - bucketInfo.end_bucket_elapsed_nanos()); - EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); -} - -TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition) { - auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false); - - int64_t baseTimeNs = getElapsedRealtimeNs(); - int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, - SharedRefBase::make(), - ATOM_TAG); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - processor->mPullerManager->ForceClearPullerCache(); - - int startBucketNum = processor->mMetricsManagers.begin()->second-> - mAllMetricProducers[0]->getCurrentBucketNum(); - EXPECT_GT(startBucketNum, (int64_t)0); - - // When creating the config, the gauge metric producer should register the alarm at the - // end of the current bucket. - ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); - EXPECT_EQ(bucketSizeNs, - processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); - int64_t& nextPullTimeNs = - processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); - - // Pulling alarm arrives on time and reset the sequential pulling alarm. - processor->informPullAlarmFired(nextPullTimeNs + 1); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); - - processor->informPullAlarmFired(nextPullTimeNs + 4); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, - nextPullTimeNs); - - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue( - reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); - ASSERT_GT((int)gaugeMetrics.data_size(), 0); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* subsystem name field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); - ASSERT_EQ(3, data.bucket_info_size()); - - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).elapsed_timestamp_nanos(0)); - ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(1, data.bucket_info(1).atom_size()); - ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0)); - ASSERT_EQ(0, data.bucket_info(1).wall_clock_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); - - ASSERT_EQ(1, data.bucket_info(2).atom_size()); - ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 4, data.bucket_info(2).elapsed_timestamp_nanos(0)); - ASSERT_EQ(0, data.bucket_info(2).wall_clock_timestamp_nanos_size()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); - EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); - EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp deleted file mode 100644 index a40a9484a841..000000000000 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sampling_type) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); - *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); - - auto atomMatcher = CreateSimpleAtomMatcher("", util::APP_START_OCCURRED); - *config.add_atom_matcher() = atomMatcher; - - auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); - *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); - *config.add_predicate() = isInBackgroundPredicate; - - auto gaugeMetric = config.add_gauge_metric(); - gaugeMetric->set_id(123456); - gaugeMetric->set_what(atomMatcher.id()); - gaugeMetric->set_condition(isInBackgroundPredicate.id()); - gaugeMetric->mutable_gauge_fields_filter()->set_include_all(false); - gaugeMetric->set_sampling_type(sampling_type); - auto fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields(); - fieldMatcher->set_field(util::APP_START_OCCURRED); - fieldMatcher->add_child()->set_field(3); // type (enum) - fieldMatcher->add_child()->set_field(4); // activity_name(str) - fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64) - *gaugeMetric->mutable_dimensions_in_what() = - CreateDimensions(util::APP_START_OCCURRED, {1 /* uid field */ }); - gaugeMetric->set_bucket(FIVE_MINUTES); - - auto links = gaugeMetric->add_links(); - links->set_condition(isInBackgroundPredicate.id()); - auto dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(util::APP_START_OCCURRED); - dimensionWhat->add_child()->set_field(1); // uid field. - auto dimensionCondition = links->mutable_fields_in_condition(); - dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED); - dimensionCondition->add_child()->set_field(1); // uid field. - return config; -} - -std::unique_ptr CreateAppStartOccurredEvent( - uint64_t timestampNs, const int uid, const string& pkg_name, - AppStartOccurred::TransitionType type, const string& activity_name, - const string& calling_pkg_name, const bool is_instant_app, int64_t activity_start_msec) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::APP_START_OCCURRED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeString(statsEvent, pkg_name.c_str()); - AStatsEvent_writeInt32(statsEvent, type); - AStatsEvent_writeString(statsEvent, activity_name.c_str()); - AStatsEvent_writeString(statsEvent, calling_pkg_name.c_str()); - AStatsEvent_writeInt32(statsEvent, is_instant_app); - AStatsEvent_writeInt32(statsEvent, activity_start_msec); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -} // namespace - -TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { - for (const auto& sampling_type : - {GaugeMetric::FIRST_N_SAMPLES, GaugeMetric::RANDOM_ONE_SAMPLE}) { - auto config = CreateStatsdConfigForPushedEvent(sampling_type); - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = - CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - - int appUid1 = 123; - int appUid2 = 456; - std::vector> events; - events.push_back(CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid1)); - events.push_back( - CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid1)); - events.push_back( - CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid1)); - events.push_back( - CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, appUid1)); - - events.push_back(CreateAppStartOccurredEvent( - bucketStartTimeNs + 10, appUid1, "app1", AppStartOccurred::WARM, "activity_name1", - "calling_pkg_name1", true /*is_instant_app*/, 101 /*activity_start_msec*/)); - events.push_back(CreateAppStartOccurredEvent( - bucketStartTimeNs + 20, appUid1, "app1", AppStartOccurred::HOT, "activity_name2", - "calling_pkg_name2", true /*is_instant_app*/, 102 /*activity_start_msec*/)); - events.push_back(CreateAppStartOccurredEvent( - bucketStartTimeNs + 30, appUid1, "app1", AppStartOccurred::COLD, "activity_name3", - "calling_pkg_name3", true /*is_instant_app*/, 103 /*activity_start_msec*/)); - events.push_back(CreateAppStartOccurredEvent( - bucketStartTimeNs + bucketSizeNs + 30, appUid1, "app1", AppStartOccurred::WARM, - "activity_name4", "calling_pkg_name4", true /*is_instant_app*/, - 104 /*activity_start_msec*/)); - events.push_back(CreateAppStartOccurredEvent( - bucketStartTimeNs + 2 * bucketSizeNs, appUid1, "app1", AppStartOccurred::COLD, - "activity_name5", "calling_pkg_name5", true /*is_instant_app*/, - 105 /*activity_start_msec*/)); - events.push_back(CreateAppStartOccurredEvent( - bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid1, "app1", AppStartOccurred::HOT, - "activity_name6", "calling_pkg_name6", false /*is_instant_app*/, - 106 /*activity_start_msec*/)); - - events.push_back( - CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 10, appUid2)); - events.push_back(CreateAppStartOccurredEvent( - bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid2, "app2", AppStartOccurred::COLD, - "activity_name7", "calling_pkg_name7", true /*is_instant_app*/, - 201 /*activity_start_msec*/)); - - sortLogEventsByTimestamp(&events); - - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), - &gaugeMetrics); - ASSERT_EQ(2, gaugeMetrics.data_size()); - - auto data = gaugeMetrics.data(0); - EXPECT_EQ(util::APP_START_OCCURRED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(appUid1, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(3, data.bucket_info_size()); - if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) { - ASSERT_EQ(2, data.bucket_info(0).atom_size()); - ASSERT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size()); - ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, - data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(AppStartOccurred::HOT, - data.bucket_info(0).atom(0).app_start_occurred().type()); - EXPECT_EQ("activity_name2", - data.bucket_info(0).atom(0).app_start_occurred().activity_name()); - EXPECT_EQ(102L, - data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); - EXPECT_EQ(AppStartOccurred::COLD, - data.bucket_info(0).atom(1).app_start_occurred().type()); - EXPECT_EQ("activity_name3", - data.bucket_info(0).atom(1).app_start_occurred().activity_name()); - EXPECT_EQ(103L, - data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis()); - - ASSERT_EQ(1, data.bucket_info(1).atom_size()); - ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, - data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(1).end_bucket_elapsed_nanos()); - EXPECT_EQ(AppStartOccurred::WARM, - data.bucket_info(1).atom(0).app_start_occurred().type()); - EXPECT_EQ("activity_name4", - data.bucket_info(1).atom(0).app_start_occurred().activity_name()); - EXPECT_EQ(104L, - data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); - - ASSERT_EQ(2, data.bucket_info(2).atom_size()); - ASSERT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(2).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, - data.bucket_info(2).end_bucket_elapsed_nanos()); - EXPECT_EQ(AppStartOccurred::COLD, - data.bucket_info(2).atom(0).app_start_occurred().type()); - EXPECT_EQ("activity_name5", - data.bucket_info(2).atom(0).app_start_occurred().activity_name()); - EXPECT_EQ(105L, - data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); - EXPECT_EQ(AppStartOccurred::HOT, - data.bucket_info(2).atom(1).app_start_occurred().type()); - EXPECT_EQ("activity_name6", - data.bucket_info(2).atom(1).app_start_occurred().activity_name()); - EXPECT_EQ(106L, - data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis()); - } else { - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, - data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(AppStartOccurred::HOT, - data.bucket_info(0).atom(0).app_start_occurred().type()); - EXPECT_EQ("activity_name2", - data.bucket_info(0).atom(0).app_start_occurred().activity_name()); - EXPECT_EQ(102L, - data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); - - ASSERT_EQ(1, data.bucket_info(1).atom_size()); - ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, - data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(1).end_bucket_elapsed_nanos()); - EXPECT_EQ(AppStartOccurred::WARM, - data.bucket_info(1).atom(0).app_start_occurred().type()); - EXPECT_EQ("activity_name4", - data.bucket_info(1).atom(0).app_start_occurred().activity_name()); - EXPECT_EQ(104L, - data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); - - ASSERT_EQ(1, data.bucket_info(2).atom_size()); - ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(2).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, - data.bucket_info(2).end_bucket_elapsed_nanos()); - EXPECT_EQ(AppStartOccurred::COLD, - data.bucket_info(2).atom(0).app_start_occurred().type()); - EXPECT_EQ("activity_name5", - data.bucket_info(2).atom(0).app_start_occurred().activity_name()); - EXPECT_EQ(105L, - data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); - } - - data = gaugeMetrics.data(1); - - EXPECT_EQ(data.dimensions_in_what().field(), util::APP_START_OCCURRED); - ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(appUid2, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - ASSERT_EQ(1, data.bucket_info(0).atom_size()); - ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, - data.bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type()); - EXPECT_EQ("activity_name7", - data.bucket_info(0).atom(0).app_start_occurred().activity_name()); - EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); - } -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp deleted file mode 100644 index e320419a9d44..000000000000 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ /dev/null @@ -1,1833 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - - *config.add_atom_matcher() = saverModeMatcher; - *config.add_atom_matcher() = crashMatcher; - *config.add_atom_matcher() = screenOnMatcher; - - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(crashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - auto metric_activation1 = config.add_metric_activation(); - metric_activation1->set_metric_id(metricId); - auto event_activation1 = metric_activation1->add_event_activation(); - event_activation1->set_atom_matcher_id(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - auto event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - - return config; -} - -StatsdConfig CreateStatsdConfigWithOneDeactivation() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher(); - - *config.add_atom_matcher() = saverModeMatcher; - *config.add_atom_matcher() = crashMatcher; - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher; - - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(crashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - auto metric_activation1 = config.add_metric_activation(); - metric_activation1->set_metric_id(metricId); - auto event_activation1 = metric_activation1->add_event_activation(); - event_activation1->set_atom_matcher_id(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - auto event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - - return config; -} - -StatsdConfig CreateStatsdConfigWithTwoDeactivations() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher(); - auto brightnessChangedMatcher2 = CreateScreenBrightnessChangedAtomMatcher(); - brightnessChangedMatcher2.set_id(StringToId("ScreenBrightnessChanged2")); - - *config.add_atom_matcher() = saverModeMatcher; - *config.add_atom_matcher() = crashMatcher; - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher2; - - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(crashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - auto metric_activation1 = config.add_metric_activation(); - metric_activation1->set_metric_id(metricId); - auto event_activation1 = metric_activation1->add_event_activation(); - event_activation1->set_atom_matcher_id(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - auto event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id()); - - return config; -} - -StatsdConfig CreateStatsdConfigWithSameDeactivations() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher(); - - *config.add_atom_matcher() = saverModeMatcher; - *config.add_atom_matcher() = crashMatcher; - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher; - - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(crashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - auto metric_activation1 = config.add_metric_activation(); - metric_activation1->set_metric_id(metricId); - auto event_activation1 = metric_activation1->add_event_activation(); - event_activation1->set_atom_matcher_id(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - auto event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - - return config; -} - -StatsdConfig CreateStatsdConfigWithTwoMetricsTwoDeactivations() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); - auto crashMatcher = CreateProcessCrashAtomMatcher(); - auto foregroundMatcher = CreateMoveToForegroundAtomMatcher(); - auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); - auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher(); - auto brightnessChangedMatcher2 = CreateScreenBrightnessChangedAtomMatcher(); - brightnessChangedMatcher2.set_id(StringToId("ScreenBrightnessChanged2")); - - *config.add_atom_matcher() = saverModeMatcher; - *config.add_atom_matcher() = crashMatcher; - *config.add_atom_matcher() = screenOnMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher; - *config.add_atom_matcher() = brightnessChangedMatcher2; - *config.add_atom_matcher() = foregroundMatcher; - - int64_t metricId = 123456; - auto countMetric = config.add_count_metric(); - countMetric->set_id(metricId); - countMetric->set_what(crashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - int64_t metricId2 = 234567; - countMetric = config.add_count_metric(); - countMetric->set_id(metricId2); - countMetric->set_what(foregroundMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - countMetric->mutable_dimensions_in_what()->set_field( - util::ACTIVITY_FOREGROUND_STATE_CHANGED); - countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field - - auto metric_activation1 = config.add_metric_activation(); - metric_activation1->set_metric_id(metricId); - auto event_activation1 = metric_activation1->add_event_activation(); - event_activation1->set_atom_matcher_id(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - auto event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id()); - - metric_activation1 = config.add_metric_activation(); - metric_activation1->set_metric_id(metricId2); - event_activation1 = metric_activation1->add_event_activation(); - event_activation1->set_atom_matcher_id(saverModeMatcher.id()); - event_activation1->set_ttl_seconds(60 * 6); // 6 minutes - event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id()); - event_activation2 = metric_activation1->add_event_activation(); - event_activation2->set_atom_matcher_id(screenOnMatcher.id()); - event_activation2->set_ttl_seconds(60 * 2); // 2 minutes - event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id()); - - return config; -} - -} // namespace - -TEST(MetricActivationE2eTest, TestCountMetric) { - auto config = CreateStatsdConfig(); - - int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; - - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - vector activeConfigsBroadcast; - - long timeBase1 = 1; - int broadcastCount = 0; - StatsLogProcessor processor( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, - [](const ConfigKey& key) { return true; }, - [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, - const vector& activeConfigs) { - broadcastCount++; - EXPECT_EQ(broadcastUid, uid); - activeConfigsBroadcast.clear(); - activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), - activeConfigs.end()); - return true; - }); - - processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - // Two activations: one is triggered by battery saver mode (tracker index 0), the other is - // triggered by screen on event (tracker index 2). - ASSERT_EQ(eventActivationMap.size(), 2u); - EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); - EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, 0); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - - std::unique_ptr event; - - event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 0); - - // Activated by battery save mode. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 1); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - - // First processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); - - // Activated by screen on event. - event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - - // 2nd processed event. - // The activation by screen_on event expires, but the one by battery save mode is still active. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - // No new broadcast since the config should still be active. - EXPECT_EQ(broadcastCount, 1); - - // 3rd processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - - // All activations expired. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - // New broadcast since the config is no longer active. - EXPECT_EQ(broadcastCount, 2); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - - // Re-activate metric via screen on. - event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, - android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 3); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - - // 4th processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - - ConfigMetricsReportList reports; - vector buffer; - processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(4, countMetrics.data_size()); - - auto data = countMetrics.data(0); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(1); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(2); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - // Partial bucket as metric is deactivated. - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, - data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(3); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { - auto config = CreateStatsdConfigWithOneDeactivation(); - - int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; - - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - vector activeConfigsBroadcast; - - long timeBase1 = 1; - int broadcastCount = 0; - StatsLogProcessor processor( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, - [](const ConfigKey& key) { return true; }, - [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, - const vector& activeConfigs) { - broadcastCount++; - EXPECT_EQ(broadcastUid, uid); - activeConfigsBroadcast.clear(); - activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), - activeConfigs.end()); - return true; - }); - - processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; - - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - // Two activations: one is triggered by battery saver mode (tracker index 0), the other is - // triggered by screen on event (tracker index 2). - ASSERT_EQ(eventActivationMap.size(), 2u); - EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); - EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, 0); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - ASSERT_EQ(eventDeactivationMap.size(), 1u); - EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); - ASSERT_EQ(eventDeactivationMap[3].size(), 1u); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - - std::unique_ptr event; - - event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 0); - - // Activated by battery save mode. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 1); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - - // First processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); - - // Activated by screen on event. - event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - - // 2nd processed event. - // The activation by screen_on event expires, but the one by battery save mode is still active. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - // No new broadcast since the config should still be active. - EXPECT_EQ(broadcastCount, 1); - - // 3rd processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - - // All activations expired. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - // New broadcast since the config is no longer active. - EXPECT_EQ(broadcastCount, 2); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - - // Re-activate metric via screen on. - event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, - android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 3); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - - // 4th processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - - // Re-enable battery saver mode activation. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 3); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - - // 5th processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - - // Cancel battery saver mode activation. - event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 3); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - - // Screen-on activation expired. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - // New broadcast since the config is no longer active. - EXPECT_EQ(broadcastCount, 4); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - - // Re-enable battery saver mode activation. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 5); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - - // Cancel battery saver mode activation. - event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 6); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - - ConfigMetricsReportList reports; - vector buffer; - processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(5, countMetrics.data_size()); - - auto data = countMetrics.data(0); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(1); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(2); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - // Partial bucket as metric is deactivated. - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, - data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(3); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13, - data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(4); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13, - data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { - auto config = CreateStatsdConfigWithTwoDeactivations(); - - int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; - - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - vector activeConfigsBroadcast; - - long timeBase1 = 1; - int broadcastCount = 0; - StatsLogProcessor processor( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, - [](const ConfigKey& key) { return true; }, - [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, - const vector& activeConfigs) { - broadcastCount++; - EXPECT_EQ(broadcastUid, uid); - activeConfigsBroadcast.clear(); - activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), - activeConfigs.end()); - return true; - }); - - processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; - - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - // Two activations: one is triggered by battery saver mode (tracker index 0), the other is - // triggered by screen on event (tracker index 2). - ASSERT_EQ(eventActivationMap.size(), 2u); - EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); - EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, 0); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - ASSERT_EQ(eventDeactivationMap.size(), 2u); - EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); - EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); - ASSERT_EQ(eventDeactivationMap[3].size(), 1u); - ASSERT_EQ(eventDeactivationMap[4].size(), 1u); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - std::unique_ptr event; - - event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 0); - - // Activated by battery save mode. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 1); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - // First processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); - - // Activated by screen on event. - event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - // 2nd processed event. - // The activation by screen_on event expires, but the one by battery save mode is still active. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - // No new broadcast since the config should still be active. - EXPECT_EQ(broadcastCount, 1); - - // 3rd processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - - // All activations expired. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - // New broadcast since the config is no longer active. - EXPECT_EQ(broadcastCount, 2); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - // Re-activate metric via screen on. - event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, - android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 3); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - // 4th processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - - // Re-enable battery saver mode activation. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 3); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - // 5th processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - - // Cancel battery saver mode and screen on activation. - event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - // New broadcast since the config is no longer active. - EXPECT_EQ(broadcastCount, 4); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - // Screen-on activation expired. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 4); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - - // Re-enable battery saver mode activation. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 5); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - // Cancel battery saver mode and screen on activation. - event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 6); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - ConfigMetricsReportList reports; - vector buffer; - processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(5, countMetrics.data_size()); - - auto data = countMetrics.data(0); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(1); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(2); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - // Partial bucket as metric is deactivated. - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, - data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(3); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, - data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(4); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, - data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { - auto config = CreateStatsdConfigWithSameDeactivations(); - - int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; - - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - vector activeConfigsBroadcast; - - long timeBase1 = 1; - int broadcastCount = 0; - StatsLogProcessor processor( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, - [](const ConfigKey& key) { return true; }, - [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, - const vector& activeConfigs) { - broadcastCount++; - EXPECT_EQ(broadcastUid, uid); - activeConfigsBroadcast.clear(); - activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), - activeConfigs.end()); - return true; - }); - - processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; - - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - // Two activations: one is triggered by battery saver mode (tracker index 0), the other is - // triggered by screen on event (tracker index 2). - ASSERT_EQ(eventActivationMap.size(), 2u); - EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); - EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, 0); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - ASSERT_EQ(eventDeactivationMap.size(), 1u); - EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); - ASSERT_EQ(eventDeactivationMap[3].size(), 2u); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[3][1], eventActivationMap[2]); - EXPECT_EQ(broadcastCount, 0); - - std::unique_ptr event; - - // Event that should be ignored. - event = CreateAppCrashEvent(bucketStartTimeNs + 1, 111); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 1); - - // Activate metric via screen on for 2 minutes. - event = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 1); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10); - - // 1st processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); - - // Enable battery saver mode activation for 5 minutes. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 1); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 + 10); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10); - - // 2nd processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 40, 333); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40); - - // Cancel battery saver mode and screen on activation. - int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61; - event = CreateScreenBrightnessChangedEvent(firstDeactivation, 64); - processor.OnLogEvent(event.get(), firstDeactivation); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - // New broadcast since the config is no longer active. - EXPECT_EQ(broadcastCount, 2); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - - // Should be ignored - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 61 + 80, 444); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80); - - // Re-enable battery saver mode activation. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 3); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - - // 3rd processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80, 555); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80); - - // Cancel battery saver mode activation. - int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13; - event = CreateScreenBrightnessChangedEvent(secondDeactivation, 140); - processor.OnLogEvent(event.get(), secondDeactivation); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(broadcastCount, 4); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - - // Should be ignored. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80, 666); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); - - ConfigMetricsReportList reports; - vector buffer; - processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - - StatsLogReport::CountMetricDataWrapper countMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(3, countMetrics.data_size()); - - auto data = countMetrics.data(0); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(1); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(2); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(555, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - // Partial bucket as metric is deactivated. - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(secondDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { - auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations(); - - int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs - int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; - - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - - sp m = new UidMap(); - sp pullerManager = new StatsPullerManager(); - sp anomalyAlarmMonitor; - sp subscriberAlarmMonitor; - vector activeConfigsBroadcast; - - long timeBase1 = 1; - int broadcastCount = 0; - StatsLogProcessor processor( - m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, - [](const ConfigKey& key) { return true; }, - [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, - const vector& activeConfigs) { - broadcastCount++; - EXPECT_EQ(broadcastUid, uid); - activeConfigsBroadcast.clear(); - activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), - activeConfigs.end()); - return true; - }); - - processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); - - ASSERT_EQ(processor.mMetricsManagers.size(), 1u); - sp metricsManager = processor.mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 2); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - auto& eventActivationMap = metricProducer->mEventActivationMap; - auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; - sp metricProducer2 = metricsManager->mAllMetricProducers[1]; - auto& eventActivationMap2 = metricProducer2->mEventActivationMap; - auto& eventDeactivationMap2 = metricProducer2->mEventDeactivationMap; - - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_FALSE(metricProducer2->mIsActive); - // Two activations: one is triggered by battery saver mode (tracker index 0), the other is - // triggered by screen on event (tracker index 2). - ASSERT_EQ(eventActivationMap.size(), 2u); - EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); - EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, 0); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - ASSERT_EQ(eventDeactivationMap.size(), 2u); - EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); - EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); - ASSERT_EQ(eventDeactivationMap[3].size(), 1u); - ASSERT_EQ(eventDeactivationMap[4].size(), 1u); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - - ASSERT_EQ(eventActivationMap2.size(), 2u); - EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end()); - EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end()); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, 0); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - ASSERT_EQ(eventDeactivationMap2.size(), 2u); - EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end()); - EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end()); - ASSERT_EQ(eventDeactivationMap[3].size(), 1u); - ASSERT_EQ(eventDeactivationMap[4].size(), 1u); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - - std::unique_ptr event; - - event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); - event = CreateMoveToForegroundEvent(bucketStartTimeNs + 5, 1111); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_FALSE(metricProducer2->mIsActive); - EXPECT_EQ(broadcastCount, 0); - - // Activated by battery save mode. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_EQ(broadcastCount, 1); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - EXPECT_TRUE(metricProducer2->mIsActive); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - - // First processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); - event = CreateMoveToForegroundEvent(bucketStartTimeNs + 15, 2222); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); - - // Activated by screen on event. - event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - EXPECT_TRUE(metricProducer2->mIsActive); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - - // 2nd processed event. - // The activation by screen_on event expires, but the one by battery save mode is still active. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 3333); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - EXPECT_TRUE(metricProducer2->mIsActive); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - // No new broadcast since the config should still be active. - EXPECT_EQ(broadcastCount, 1); - - // 3rd processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 4444); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - - // All activations expired. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); - event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 5555); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); - EXPECT_FALSE(metricsManager->isActive()); - // New broadcast since the config is no longer active. - EXPECT_EQ(broadcastCount, 2); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - EXPECT_FALSE(metricProducer2->mIsActive); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - - // Re-activate metric via screen on. - event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, - android::view::DISPLAY_STATE_ON); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_EQ(broadcastCount, 3); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - EXPECT_TRUE(metricProducer2->mIsActive); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - - // 4th processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 6666); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - - // Re-enable battery saver mode activation. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_EQ(broadcastCount, 3); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - EXPECT_TRUE(metricProducer2->mIsActive); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - - // 5th processed event. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 7777); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - - // Cancel battery saver mode and screen on activation. - event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); - EXPECT_FALSE(metricsManager->isActive()); - // New broadcast since the config is no longer active. - EXPECT_EQ(broadcastCount, 4); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - EXPECT_FALSE(metricProducer2->mIsActive); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - - // Screen-on activation expired. - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); - event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 8888); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_EQ(broadcastCount, 4); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - EXPECT_FALSE(metricProducer2->mIsActive); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - - event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 9999); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - - // Re-enable battery saver mode activation. - event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_TRUE(metricsManager->isActive()); - EXPECT_EQ(broadcastCount, 5); - ASSERT_EQ(activeConfigsBroadcast.size(), 1); - EXPECT_EQ(activeConfigsBroadcast[0], cfgId); - EXPECT_TRUE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - EXPECT_TRUE(metricProducer2->mIsActive); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - - // Cancel battery saver mode and screen on activation. - event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140); - processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); - EXPECT_FALSE(metricsManager->isActive()); - EXPECT_EQ(broadcastCount, 6); - ASSERT_EQ(activeConfigsBroadcast.size(), 0); - EXPECT_FALSE(metricProducer->mIsActive); - EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); - EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); - EXPECT_FALSE(metricProducer2->mIsActive); - EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); - EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); - EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); - EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); - EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); - - ConfigMetricsReportList reports; - vector buffer; - processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(2, reports.reports(0).metrics_size()); - - StatsLogReport::CountMetricDataWrapper countMetrics; - - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); - ASSERT_EQ(5, countMetrics.data_size()); - - auto data = countMetrics.data(0); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(1); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(2); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - // Partial bucket as metric is deactivated. - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, - data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(3); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, - data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(4); - EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, - data.bucket_info(0).end_bucket_elapsed_nanos()); - - countMetrics.clear_data(); - sortMetricDataByDimensionsValue(reports.reports(0).metrics(1).count_metrics(), &countMetrics); - ASSERT_EQ(5, countMetrics.data_size()); - - data = countMetrics.data(0); - EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(2222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(1); - EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(3333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(2); - EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(4444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - // Partial bucket as metric is deactivated. - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, - data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(3); - EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(6666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, - data.bucket_info(0).end_bucket_elapsed_nanos()); - - data = countMetrics.data(4); - EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* uid field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_EQ(7777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(1, data.bucket_info(0).count()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, - data.bucket_info(0).end_bucket_elapsed_nanos()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp deleted file mode 100644 index 5e77ee0f0b0a..000000000000 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ -namespace { - -StatsdConfig CreateStatsdConfig() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); - *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); - - *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); - *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); - - auto appCrashMatcher = CreateProcessCrashAtomMatcher(); - *config.add_atom_matcher() = appCrashMatcher; - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - - auto isSyncingPredicate = CreateIsSyncingPredicate(); - auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidDimensions( - util::SYNC_STATE_CHANGED, {Position::FIRST}); - syncDimension->add_child()->set_field(2 /* name field*/); - - auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); - *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); - - *config.add_predicate() = screenIsOffPredicate; - *config.add_predicate() = isSyncingPredicate; - *config.add_predicate() = isInBackgroundPredicate; - - auto combinationPredicate = config.add_predicate(); - combinationPredicate->set_id(StringToId("combinationPredicate")); - combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); - addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); - addPredicateToPredicateCombination(isInBackgroundPredicate, combinationPredicate); - - auto countMetric = config.add_count_metric(); - countMetric->set_id(StringToId("AppCrashes")); - countMetric->set_what(appCrashMatcher.id()); - countMetric->set_condition(combinationPredicate->id()); - // The metric is dimensioning by uid only. - *countMetric->mutable_dimensions_in_what() = - CreateDimensions(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1}); - countMetric->set_bucket(FIVE_MINUTES); - - // Links between crash atom and condition of app is in syncing. - auto links = countMetric->add_links(); - links->set_condition(isSyncingPredicate.id()); - auto dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - util::SYNC_STATE_CHANGED, {Position::FIRST}); - - // Links between crash atom and condition of app is in background. - links = countMetric->add_links(); - links->set_condition(isInBackgroundPredicate.id()); - dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - dimensionWhat->add_child()->set_field(1); // uid field. - auto dimensionCondition = links->mutable_fields_in_condition(); - dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED); - dimensionCondition->add_child()->set_field(1); // uid field. - return config; -} -} // namespace - -// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests, -// we should use the real API which will clear the data after dump data is called. -TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) { - auto config = CreateStatsdConfig(); - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - - int appUid = 123; - auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid); - auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid); - auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid); - - auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid); - auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid); - auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid); - - auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid); - auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid); - - auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid); - auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid); - - auto screenTurnedOnEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - auto screenTurnedOffEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - auto screenTurnedOnEvent2 = - CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100, - android::view::DisplayStateEnum::DISPLAY_STATE_ON); - - std::vector attributionUids = {appUid, appUid + 1}; - std::vector attributionTags = {"App1", "GMSCoreModule1"}; - - auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids, - attributionTags, "ReadEmail"); - auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids, - attributionTags, "ReadEmail"); - auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000, - attributionUids, attributionTags, "ReadDoc"); - - auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid); - auto moveToForegroundEvent1 = - CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid); - - auto moveToBackgroundEvent2 = - CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid); - auto moveToForegroundEvent2 = - CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid); - - /* - bucket #1 bucket #2 - - - | | | | | | | | | | (crashEvents) - |-------------------------------------|-----------------------------------|--------- - - | | (MoveToBkground) - - | | (MoveToForeground) - - | | (SyncIsOn) - | (SyncIsOff) - | | (ScreenIsOn) - | (ScreenIsOff) - */ - std::vector> events; - events.push_back(std::move(crashEvent1)); - events.push_back(std::move(crashEvent2)); - events.push_back(std::move(crashEvent3)); - events.push_back(std::move(crashEvent4)); - events.push_back(std::move(crashEvent5)); - events.push_back(std::move(crashEvent6)); - events.push_back(std::move(crashEvent7)); - events.push_back(std::move(crashEvent8)); - events.push_back(std::move(crashEvent9)); - events.push_back(std::move(crashEvent10)); - events.push_back(std::move(screenTurnedOnEvent)); - events.push_back(std::move(screenTurnedOffEvent)); - events.push_back(std::move(screenTurnedOnEvent2)); - events.push_back(std::move(syncOnEvent1)); - events.push_back(std::move(syncOffEvent1)); - events.push_back(std::move(syncOnEvent2)); - events.push_back(std::move(moveToBackgroundEvent1)); - events.push_back(std::move(moveToForegroundEvent1)); - events.push_back(std::move(moveToBackgroundEvent2)); - events.push_back(std::move(moveToForegroundEvent2)); - - sortLogEventsByTimestamp(&events); - - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1); - EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); - auto data = reports.reports(0).metrics(0).count_metrics().data(0); - // Validate dimension value. - EXPECT_EQ(data.dimensions_in_what().field(), util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); - // Uid field. - EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); -} - -TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) { - auto config = CreateStatsdConfig(); - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - - int appUid = 123; - auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid); - auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid); - auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid); - - auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid); - auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid); - auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid); - - auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid); - auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid); - - auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid); - auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid); - - auto screenTurnedOnEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - auto screenTurnedOffEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - auto screenTurnedOnEvent2 = - CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100, - android::view::DisplayStateEnum::DISPLAY_STATE_ON); - - std::vector attributionUids = {appUid, appUid + 1}; - std::vector attributionTags = {"App1", "GMSCoreModule1"}; - - auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids, - attributionTags, "ReadEmail"); - auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids, - attributionTags, "ReadEmail"); - auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000, - attributionUids, attributionTags, "ReadDoc"); - - auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid); - auto moveToForegroundEvent1 = - CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid); - - auto moveToBackgroundEvent2 = - CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid); - auto moveToForegroundEvent2 = - CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid); - - /* - bucket #1 bucket #2 - - - | | | | | | | | | | (crashEvents) - |-------------------------------------|-----------------------------------|--------- - - | | (MoveToBkground) - - | | (MoveToForeground) - - | | (SyncIsOn) - | (SyncIsOff) - | | (ScreenIsOn) - | (ScreenIsOff) - */ - std::vector> events; - events.push_back(std::move(crashEvent1)); - events.push_back(std::move(crashEvent2)); - events.push_back(std::move(crashEvent3)); - events.push_back(std::move(crashEvent4)); - events.push_back(std::move(crashEvent5)); - events.push_back(std::move(crashEvent6)); - events.push_back(std::move(crashEvent7)); - events.push_back(std::move(crashEvent8)); - events.push_back(std::move(crashEvent9)); - events.push_back(std::move(crashEvent10)); - events.push_back(std::move(screenTurnedOnEvent)); - events.push_back(std::move(screenTurnedOffEvent)); - events.push_back(std::move(screenTurnedOnEvent2)); - events.push_back(std::move(syncOnEvent1)); - events.push_back(std::move(syncOffEvent1)); - events.push_back(std::move(syncOnEvent2)); - events.push_back(std::move(moveToBackgroundEvent1)); - events.push_back(std::move(moveToForegroundEvent1)); - events.push_back(std::move(moveToBackgroundEvent2)); - events.push_back(std::move(moveToForegroundEvent2)); - - sortLogEventsByTimestamp(&events); - - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - ConfigMetricsReportList reports; - vector buffer; - - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2); - EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); - EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3); - auto data = reports.reports(0).metrics(0).count_metrics().data(0); - // Validate dimension value. - EXPECT_EQ(data.dimensions_in_what().field(), util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); - // Uid field. - EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp deleted file mode 100644 index c03b9254f70d..000000000000 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include - -#include - -#include "src/StatsLogProcessor.h" -#include "src/StatsService.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -using::ndk::SharedRefBase; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ -namespace { -const string kApp1 = "app1.sharing.1"; -const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs. -const int kCallingUid = 0; // Randomly chosen - -void SendConfig(shared_ptr& service, const StatsdConfig& config) { - string str; - config.SerializeToString(&str); - std::vector configAsVec(str.begin(), str.end()); - service->addConfiguration(kConfigKey, configAsVec, kCallingUid); -} - -ConfigMetricsReport GetReports(sp processor, int64_t timestamp, - bool include_current = false) { - vector output; - ConfigKey configKey(AIBinder_getCallingUid(), kConfigKey); - processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/, - true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output); - ConfigMetricsReportList reports; - reports.ParseFromArray(output.data(), output.size()); - EXPECT_EQ(1, reports.reports_size()); - return reports.reports(kCallingUid); -} - -StatsdConfig MakeConfig() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto appCrashMatcher = CreateProcessCrashAtomMatcher(); - *config.add_atom_matcher() = appCrashMatcher; - auto countMetric = config.add_count_metric(); - countMetric->set_id(StringToId("AppCrashes")); - countMetric->set_what(appCrashMatcher.id()); - countMetric->set_bucket(FIVE_MINUTES); - return config; -} - -StatsdConfig MakeValueMetricConfig(int64_t minTime) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - - auto pulledAtomMatcher = - CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto valueMetric = config.add_value_metric(); - valueMetric->set_id(123456); - valueMetric->set_what(pulledAtomMatcher.id()); - *valueMetric->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - *valueMetric->mutable_dimensions_in_what() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); - valueMetric->set_bucket(FIVE_MINUTES); - valueMetric->set_min_bucket_size_nanos(minTime); - valueMetric->set_use_absolute_value_on_reset(true); - valueMetric->set_skip_zero_diff_output(false); - return config; -} - -StatsdConfig MakeGaugeMetricConfig(int64_t minTime) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - - auto pulledAtomMatcher = - CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto gaugeMetric = config.add_gauge_metric(); - gaugeMetric->set_id(123456); - gaugeMetric->set_what(pulledAtomMatcher.id()); - gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true); - *gaugeMetric->mutable_dimensions_in_what() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); - gaugeMetric->set_bucket(FIVE_MINUTES); - gaugeMetric->set_min_bucket_size_nanos(minTime); - return config; -} -} // anonymous namespace - -TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - SendConfig(service, MakeConfig()); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 2, 100).get()); - - ConfigMetricsReport report = GetReports(service->mProcessor, start + 3); - // Expect no metrics since the bucket has not finished yet. - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(0, report.metrics(0).count_metrics().data_size()); -} - -TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - SendConfig(service, MakeConfig()); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - // Force the uidmap to update at timestamp 2. - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); - // This is a new installation, so there shouldn't be a split (should be same as the without - // split case). - service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), - String16("")); - // Goes into the second bucket. - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); - - ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(0, report.metrics(0).count_metrics().data_size()); -} - -TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - SendConfig(service, MakeConfig()); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, - {String16("")}); - - // Force the uidmap to update at timestamp 2. - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); - service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), - String16("")); - // Goes into the second bucket. - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); - - ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); - backfillStartEndTimestamp(&report); - - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); - ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); - EXPECT_TRUE(report.metrics(0) - .count_metrics() - .data(0) - .bucket_info(0) - .has_start_bucket_elapsed_nanos()); - EXPECT_TRUE(report.metrics(0) - .count_metrics() - .data(0) - .bucket_info(0) - .has_end_bucket_elapsed_nanos()); - EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); -} - -TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - SendConfig(service, MakeConfig()); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, - {String16("")}); - - // Force the uidmap to update at timestamp 2. - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); - service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1); - // Goes into the second bucket. - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); - - ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); - backfillStartEndTimestamp(&report); - - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); - ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); - EXPECT_TRUE(report.metrics(0) - .count_metrics() - .data(0) - .bucket_info(0) - .has_start_bucket_elapsed_nanos()); - EXPECT_TRUE(report.metrics(0) - .count_metrics() - .data(0) - .bucket_info(0) - .has_end_bucket_elapsed_nanos()); - EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); -} - -TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - SendConfig(service, MakeConfig()); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - // Goes into the first bucket - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + NS_PER_SEC, 100).get()); - int64_t bootCompleteTimeNs = start + 2 * NS_PER_SEC; - service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs); - // Goes into the second bucket. - service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3 * NS_PER_SEC, 100).get()); - - ConfigMetricsReport report = GetReports(service->mProcessor, start + 4 * NS_PER_SEC); - backfillStartEndTimestamp(&report); - - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); - ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); - EXPECT_TRUE(report.metrics(0) - .count_metrics() - .data(0) - .bucket_info(0) - .has_start_bucket_elapsed_nanos()); - EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)), - report.metrics(0).count_metrics().data(0).bucket_info(0).end_bucket_elapsed_nanos()); - EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); -} - -TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make()); - // Partial buckets don't occur when app is first installed. - service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); - SendConfig(service, MakeValueMetricConfig(0)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - int64_t appUpgradeTimeNs = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC; - service->mUidMap->updateApp(appUpgradeTimeNs, String16(kApp1.c_str()), 1, 2, String16("v2"), - String16("")); - - ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); - backfillStartEndTimestamp(&report); - - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(0, report.metrics(0).value_metrics().skipped_size()); - - // The fake subsystem state sleep puller returns two atoms. - ASSERT_EQ(2, report.metrics(0).value_metrics().data_size()); - ASSERT_EQ(2, report.metrics(0).value_metrics().data(0).bucket_info_size()); - EXPECT_EQ(MillisToNano(NanoToMillis(appUpgradeTimeNs)), - report.metrics(0).value_metrics().data(0).bucket_info(1).end_bucket_elapsed_nanos()); -} - -TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make()); - // Partial buckets don't occur when app is first installed. - service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); - SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC; - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"), - String16("")); - - ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); - backfillStartEndTimestamp(&report); - - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size()); - EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos()); - // Can't test the start time since it will be based on the actual time when the pulling occurs. - EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)), - report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos()); - - ASSERT_EQ(2, report.metrics(0).value_metrics().data_size()); - ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size()); -} - -TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - // Initial pull will fail since puller is not registered. - SendConfig(service, MakeValueMetricConfig(0)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make()); - - int64_t bootCompleteTimeNs = start + NS_PER_SEC; - service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs); - - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - - ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); - backfillStartEndTimestamp(&report); - - // First bucket is dropped due to the initial pull failing - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size()); - EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)), - report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos()); - - // The fake subsystem state sleep puller returns two atoms. - ASSERT_EQ(2, report.metrics(0).value_metrics().data_size()); - ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size()); - EXPECT_EQ( - MillisToNano(NanoToMillis(bootCompleteTimeNs)), - report.metrics(0).value_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos()); -} - -TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make()); - // Partial buckets don't occur when app is first installed. - service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); - SendConfig(service, MakeGaugeMetricConfig(0)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2, - String16("v2"), String16("")); - - ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); - backfillStartEndTimestamp(&report); - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(0, report.metrics(0).gauge_metrics().skipped_size()); - // The fake subsystem state sleep puller returns two atoms. - ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size()); - ASSERT_EQ(2, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); -} - -TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - // Partial buckets don't occur when app is first installed. - service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make()); - SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2; - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"), - String16("")); - - ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); - backfillStartEndTimestamp(&report); - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size()); - // Can't test the start time since it will be based on the actual time when the pulling occurs. - EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos()); - EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)), - report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos()); - ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size()); - ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); -} - -TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket) { - shared_ptr service = SharedRefBase::make(nullptr, nullptr); - // Initial pull will fail since puller hasn't been registered. - SendConfig(service, MakeGaugeMetricConfig(0)); - int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. - - service->mPullerManager->RegisterPullAtomCallback( - /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, - SharedRefBase::make()); - - int64_t bootCompleteTimeNs = start + NS_PER_SEC; - service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs); - - service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - - ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); - backfillStartEndTimestamp(&report); - - ASSERT_EQ(1, report.metrics_size()); - ASSERT_EQ(0, report.metrics(0).gauge_metrics().skipped_size()); - // The fake subsystem state sleep puller returns two atoms. - ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size()); - // No data in the first bucket, so nothing is reported - ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); - EXPECT_EQ( - MillisToNano(NanoToMillis(bootCompleteTimeNs)), - report.metrics(0).gauge_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp deleted file mode 100644 index 4d3928277527..000000000000 --- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp +++ /dev/null @@ -1,679 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include - -using ::ndk::SharedRefBase; - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -const int64_t metricId = 123456; - -StatsdConfig CreateStatsdConfig(bool useCondition = true) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - auto pulledAtomMatcher = - CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = screenIsOffPredicate; - - auto valueMetric = config.add_value_metric(); - valueMetric->set_id(metricId); - valueMetric->set_what(pulledAtomMatcher.id()); - if (useCondition) { - valueMetric->set_condition(screenIsOffPredicate.id()); - } - *valueMetric->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - *valueMetric->mutable_dimensions_in_what() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); - valueMetric->set_bucket(FIVE_MINUTES); - valueMetric->set_use_absolute_value_on_reset(true); - valueMetric->set_skip_zero_diff_output(false); - valueMetric->set_max_pull_delay_sec(INT_MAX); - return config; -} - -StatsdConfig CreateStatsdConfigWithStates() { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. - - auto pulledAtomMatcher = CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = CreateBatteryStateNoneMatcher(); - *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); - - auto screenOnPredicate = CreateScreenIsOnPredicate(); - *config.add_predicate() = screenOnPredicate; - - auto screenOffPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = screenOffPredicate; - - auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate(); - *config.add_predicate() = deviceUnpluggedPredicate; - - auto screenOnOnBatteryPredicate = config.add_predicate(); - screenOnOnBatteryPredicate->set_id(StringToId("screenOnOnBatteryPredicate")); - screenOnOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenOnPredicate, screenOnOnBatteryPredicate); - addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOnOnBatteryPredicate); - - auto screenOffOnBatteryPredicate = config.add_predicate(); - screenOffOnBatteryPredicate->set_id(StringToId("ScreenOffOnBattery")); - screenOffOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND); - addPredicateToPredicateCombination(screenOffPredicate, screenOffOnBatteryPredicate); - addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOffOnBatteryPredicate); - - const State screenState = - CreateScreenStateWithSimpleOnOffMap(/*screen on id=*/321, /*screen off id=*/123); - *config.add_state() = screenState; - - // ValueMetricSubsystemSleepWhileScreenOnOnBattery - auto valueMetric1 = config.add_value_metric(); - valueMetric1->set_id(metricId); - valueMetric1->set_what(pulledAtomMatcher.id()); - valueMetric1->set_condition(screenOnOnBatteryPredicate->id()); - *valueMetric1->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - valueMetric1->set_bucket(FIVE_MINUTES); - valueMetric1->set_use_absolute_value_on_reset(true); - valueMetric1->set_skip_zero_diff_output(false); - valueMetric1->set_max_pull_delay_sec(INT_MAX); - - // ValueMetricSubsystemSleepWhileScreenOffOnBattery - ValueMetric* valueMetric2 = config.add_value_metric(); - valueMetric2->set_id(StringToId("ValueMetricSubsystemSleepWhileScreenOffOnBattery")); - valueMetric2->set_what(pulledAtomMatcher.id()); - valueMetric2->set_condition(screenOffOnBatteryPredicate->id()); - *valueMetric2->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - valueMetric2->set_bucket(FIVE_MINUTES); - valueMetric2->set_use_absolute_value_on_reset(true); - valueMetric2->set_skip_zero_diff_output(false); - valueMetric2->set_max_pull_delay_sec(INT_MAX); - - // ValueMetricSubsystemSleepWhileOnBatterySliceScreen - ValueMetric* valueMetric3 = config.add_value_metric(); - valueMetric3->set_id(StringToId("ValueMetricSubsystemSleepWhileOnBatterySliceScreen")); - valueMetric3->set_what(pulledAtomMatcher.id()); - valueMetric3->set_condition(deviceUnpluggedPredicate.id()); - *valueMetric3->mutable_value_field() = - CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */}); - valueMetric3->add_slice_by_state(screenState.id()); - valueMetric3->set_bucket(FIVE_MINUTES); - valueMetric3->set_use_absolute_value_on_reset(true); - valueMetric3->set_skip_zero_diff_output(false); - valueMetric3->set_max_pull_delay_sec(INT_MAX); - return config; -} - -} // namespace - -/** - * Tests the initial condition and condition after the first log events for - * value metrics with either a combination condition or simple condition. - * - * Metrics should be initialized with condition kUnknown (given that the - * predicate is using the default InitialValue of UNKNOWN). The condition should - * be updated to either kFalse or kTrue if a condition event is logged for all - * children conditions. - */ -TEST(ValueMetricE2eTest, TestInitialConditionChanges) { - StatsdConfig config = CreateStatsdConfigWithStates(); - int64_t baseTimeNs = getElapsedRealtimeNs(); - int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - int32_t tagId = util::SUBSYSTEM_SLEEP_STATE; - auto processor = - CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, - SharedRefBase::make(), tagId); - - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - EXPECT_EQ(3, metricsManager->mAllMetricProducers.size()); - - // Combination condition metric - screen on and device unplugged - sp metricProducer1 = metricsManager->mAllMetricProducers[0]; - // Simple condition metric - device unplugged - sp metricProducer2 = metricsManager->mAllMetricProducers[2]; - - EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); - - auto screenOnEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 30, android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); - - auto screenOffEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 40, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition); - - auto pluggedUsbEvent = CreateBatteryStateChangedEvent( - configAddedTimeNs + 50, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); - processor->OnLogEvent(pluggedUsbEvent.get()); - EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kFalse, metricProducer2->mCondition); - - auto pluggedNoneEvent = CreateBatteryStateChangedEvent( - configAddedTimeNs + 70, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE); - processor->OnLogEvent(pluggedNoneEvent.get()); - EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition); - EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition); -} - -TEST(ValueMetricE2eTest, TestPulledEvents) { - auto config = CreateStatsdConfig(); - int64_t baseTimeNs = getElapsedRealtimeNs(); - int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, - SharedRefBase::make(), - util::SUBSYSTEM_SLEEP_STATE); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - processor->mPullerManager->ForceClearPullerCache(); - - int startBucketNum = processor->mMetricsManagers.begin() - ->second->mAllMetricProducers[0] - ->getCurrentBucketNum(); - EXPECT_GT(startBucketNum, (int64_t)0); - - // When creating the config, the value metric producer should register the alarm at the - // end of the current bucket. - ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); - EXPECT_EQ(bucketSizeNs, - processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); - int64_t& expectedPullTimeNs = - processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); - - auto screenOffEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - auto screenOnEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - - screenOffEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - // Pulling alarm arrives on time and reset the sequential pulling alarm. - processor->informPullAlarmFired(expectedPullTimeNs + 1); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs); - - processor->informPullAlarmFired(expectedPullTimeNs + 1); - - screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 2 * bucketSizeNs + 15, - android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - - processor->informPullAlarmFired(expectedPullTimeNs + 1); - - processor->informPullAlarmFired(expectedPullTimeNs + 1); - - screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 11, - android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - processor->informPullAlarmFired(expectedPullTimeNs + 1); - - processor->informPullAlarmFired(expectedPullTimeNs + 1); - - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::ValueMetricDataWrapper valueMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); - ASSERT_GT((int)valueMetrics.data_size(), 1); - - auto data = valueMetrics.data(0); - EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* subsystem name field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); - // We have 4 buckets, the first one was incomplete since the condition was unknown. - ASSERT_EQ(4, data.bucket_info_size()); - - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - ASSERT_EQ(1, data.bucket_info(0).values_size()); - - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); - ASSERT_EQ(1, data.bucket_info(1).values_size()); - - EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); - ASSERT_EQ(1, data.bucket_info(2).values_size()); - - EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); - ASSERT_EQ(1, data.bucket_info(3).values_size()); -} - -TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) { - auto config = CreateStatsdConfig(); - int64_t baseTimeNs = getElapsedRealtimeNs(); - // 10 mins == 2 bucket durations. - int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, - SharedRefBase::make(), - util::SUBSYSTEM_SLEEP_STATE); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - processor->mPullerManager->ForceClearPullerCache(); - - int startBucketNum = processor->mMetricsManagers.begin() - ->second->mAllMetricProducers[0] - ->getCurrentBucketNum(); - EXPECT_GT(startBucketNum, (int64_t)0); - - // When creating the config, the value metric producer should register the alarm at the - // end of the current bucket. - ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); - EXPECT_EQ(bucketSizeNs, - processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); - int64_t& expectedPullTimeNs = - processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); - - // Screen off/on/off events. - auto screenOffEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - auto screenOnEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - - screenOffEvent = - CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - // Pulling alarm arrives late by 2 buckets and 1 ns. 2 buckets late is too far away in the - // future, data will be skipped. - processor->informPullAlarmFired(expectedPullTimeNs + 2 * bucketSizeNs + 1); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs); - - // This screen state change will start a new bucket. - screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 65, - android::view::DISPLAY_STATE_ON); - processor->OnLogEvent(screenOnEvent.get()); - - // The alarm is delayed but we already created a bucket thanks to the screen state condition. - // This bucket does not have to be skipped since the alarm arrives in time for the next bucket. - processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs); - - screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 6 * bucketSizeNs + 31, - android::view::DISPLAY_STATE_OFF); - processor->OnLogEvent(screenOffEvent.get()); - - processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 8 * bucketSizeNs, expectedPullTimeNs); - - processor->informPullAlarmFired(expectedPullTimeNs + 1); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 9 * bucketSizeNs, expectedPullTimeNs); - - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::ValueMetricDataWrapper valueMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); - ASSERT_GT((int)valueMetrics.data_size(), 1); - - auto data = valueMetrics.data(0); - EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* subsystem name field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); - ASSERT_EQ(3, data.bucket_info_size()); - - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); - ASSERT_EQ(1, data.bucket_info(0).values_size()); - - EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); - ASSERT_EQ(1, data.bucket_info(1).values_size()); - - EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); - ASSERT_EQ(1, data.bucket_info(2).values_size()); -} - -TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { - auto config = CreateStatsdConfig(false); - int64_t baseTimeNs = getElapsedRealtimeNs(); - int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; - - auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher(); - *config.add_atom_matcher() = batterySaverStartMatcher; - const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. - auto metric_activation = config.add_metric_activation(); - metric_activation->set_metric_id(metricId); - metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); - auto event_activation = metric_activation->add_event_activation(); - event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); - event_activation->set_ttl_seconds(ttlNs / 1000000000); - - ConfigKey cfgKey; - auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, - SharedRefBase::make(), - util::SUBSYSTEM_SLEEP_STATE); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - processor->mPullerManager->ForceClearPullerCache(); - - int startBucketNum = processor->mMetricsManagers.begin() - ->second->mAllMetricProducers[0] - ->getCurrentBucketNum(); - EXPECT_GT(startBucketNum, (int64_t)0); - EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); - - // When creating the config, the value metric producer should register the alarm at the - // end of the current bucket. - ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); - EXPECT_EQ(bucketSizeNs, - processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); - int64_t& expectedPullTimeNs = - processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); - - // Pulling alarm arrives on time and reset the sequential pulling alarm. - processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns. - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs); - EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); - - // Activate the metric. A pull occurs here - const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis. - auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs); - processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms. - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); - - processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns. - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs); - - processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns. - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs); - - // Create random event to deactivate metric. - auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50); - processor->OnLogEvent(deactivationEvent.get()); - EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); - - processor->informPullAlarmFired(expectedPullTimeNs + 3); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, expectedPullTimeNs); - - processor->informPullAlarmFired(expectedPullTimeNs + 4); - EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs); - - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, - ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(1, reports.reports_size()); - ASSERT_EQ(1, reports.reports(0).metrics_size()); - StatsLogReport::ValueMetricDataWrapper valueMetrics; - sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); - ASSERT_GT((int)valueMetrics.data_size(), 0); - - auto data = valueMetrics.data(0); - EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); - ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); - EXPECT_EQ(1 /* subsystem name field */, - data.dimensions_in_what().value_tuple().dimensions_value(0).field()); - EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); - // We have 2 full buckets, the two surrounding the activation are dropped. - ASSERT_EQ(2, data.bucket_info_size()); - - auto bucketInfo = data.bucket_info(0); - EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); - ASSERT_EQ(1, bucketInfo.values_size()); - - bucketInfo = data.bucket_info(1); - EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); - EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); - ASSERT_EQ(1, bucketInfo.values_size()); -} - -/** - * Test initialization of a simple value metric that is sliced by a state. - * - * ValueCpuUserTimePerScreenState - */ -TEST(ValueMetricE2eTest, TestInitWithSlicedState) { - // Create config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto pulledAtomMatcher = - CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE); - *config.add_atom_matcher() = pulledAtomMatcher; - - auto screenState = CreateScreenState(); - *config.add_state() = screenState; - - // Create value metric that slices by screen state without a map. - int64_t metricId = 123456; - auto valueMetric = config.add_value_metric(); - valueMetric->set_id(metricId); - valueMetric->set_bucket(TimeUnit::FIVE_MINUTES); - valueMetric->set_what(pulledAtomMatcher.id()); - *valueMetric->mutable_value_field() = - CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */}); - valueMetric->add_slice_by_state(screenState.id()); - valueMetric->set_max_pull_delay_sec(INT_MAX); - - // Initialize StatsLogProcessor. - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - const uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000LL; - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - // Check that StateTrackers were initialized correctly. - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - - // Check that ValueMetricProducer was initialized correctly. - ASSERT_EQ(1U, processor->mMetricsManagers.size()); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(1, metricsManager->mAllMetricProducers.size()); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(1, metricProducer->mSlicedStateAtoms.size()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0)); - ASSERT_EQ(0, metricProducer->mStateGroupMap.size()); -} - -/** - * Test initialization of a value metric that is sliced by state and has - * dimensions_in_what. - * - * ValueCpuUserTimePerUidPerUidProcessState - */ -TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions) { - // Create config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto cpuTimePerUidMatcher = - CreateSimpleAtomMatcher("CpuTimePerUidMatcher", util::CPU_TIME_PER_UID); - *config.add_atom_matcher() = cpuTimePerUidMatcher; - - auto uidProcessState = CreateUidProcessState(); - *config.add_state() = uidProcessState; - - // Create value metric that slices by screen state with a complete map. - int64_t metricId = 123456; - auto valueMetric = config.add_value_metric(); - valueMetric->set_id(metricId); - valueMetric->set_bucket(TimeUnit::FIVE_MINUTES); - valueMetric->set_what(cpuTimePerUidMatcher.id()); - *valueMetric->mutable_value_field() = - CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */}); - *valueMetric->mutable_dimensions_in_what() = - CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */}); - valueMetric->add_slice_by_state(uidProcessState.id()); - MetricStateLink* stateLink = valueMetric->add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - valueMetric->set_max_pull_delay_sec(INT_MAX); - - // Initialize StatsLogProcessor. - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - // Check that StateTrackers were initialized correctly. - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); - - // Check that ValueMetricProducer was initialized correctly. - ASSERT_EQ(1U, processor->mMetricsManagers.size()); - sp metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - ASSERT_EQ(1, metricsManager->mAllMetricProducers.size()); - sp metricProducer = metricsManager->mAllMetricProducers[0]; - ASSERT_EQ(1, metricProducer->mSlicedStateAtoms.size()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0)); - ASSERT_EQ(0, metricProducer->mStateGroupMap.size()); -} - -/** - * Test initialization of a value metric that is sliced by state and has - * dimensions_in_what. - * - * ValueCpuUserTimePerUidPerUidProcessState - */ -TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions) { - // Create config. - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - - auto cpuTimePerUidMatcher = - CreateSimpleAtomMatcher("CpuTimePerUidMatcher", util::CPU_TIME_PER_UID); - *config.add_atom_matcher() = cpuTimePerUidMatcher; - - auto uidProcessState = CreateUidProcessState(); - *config.add_state() = uidProcessState; - - // Create value metric that slices by screen state with a complete map. - int64_t metricId = 123456; - auto valueMetric = config.add_value_metric(); - valueMetric->set_id(metricId); - valueMetric->set_bucket(TimeUnit::FIVE_MINUTES); - valueMetric->set_what(cpuTimePerUidMatcher.id()); - *valueMetric->mutable_value_field() = - CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */}); - valueMetric->add_slice_by_state(uidProcessState.id()); - MetricStateLink* stateLink = valueMetric->add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - valueMetric->set_max_pull_delay_sec(INT_MAX); - - // Initialize StatsLogProcessor. - const uint64_t bucketStartTimeNs = 10000000000; // 0:10 - int uid = 12345; - int64_t cfgId = 98765; - ConfigKey cfgKey(uid, cfgId); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - - // No StateTrackers are initialized. - EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount()); - - // Config initialization fails. - ASSERT_EQ(0, processor->mMetricsManagers.size()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp deleted file mode 100644 index 52bc222e5fe2..000000000000 --- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "src/StatsLogProcessor.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -#include - -namespace android { -namespace os { -namespace statsd { - -#ifdef __ANDROID__ - -namespace { - -StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) { - StatsdConfig config; - config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. - *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); - *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); - *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); - *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - - auto screenIsOffPredicate = CreateScreenIsOffPredicate(); - *config.add_predicate() = screenIsOffPredicate; - - auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); - // The predicate is dimensioning by any attribution node and both by uid and tag. - FieldMatcher dimensions = CreateAttributionUidAndTagDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST, Position::LAST}); - // Also slice by the wakelock tag - dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock. - *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; - *config.add_predicate() = holdingWakelockPredicate; - - auto durationMetric = config.add_duration_metric(); - durationMetric->set_id(StringToId("WakelockDuration")); - durationMetric->set_what(holdingWakelockPredicate.id()); - durationMetric->set_condition(screenIsOffPredicate.id()); - durationMetric->set_aggregation_type(aggregationType); - // The metric is dimensioning by first attribution node and only by uid. - *durationMetric->mutable_dimensions_in_what() = - CreateAttributionUidDimensions( - util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - durationMetric->set_bucket(FIVE_MINUTES); - return config; -} - -std::vector attributionUids1 = {111, 222, 222}; -std::vector attributionTags1 = {"App1", "GMSCoreModule1", "GMSCoreModule2"}; - -std::vector attributionUids2 = {111, 222, 222}; -std::vector attributionTags2 = {"App2", "GMSCoreModule1", "GMSCoreModule2"}; - -/* -Events: -Screen off is met from (200ns,1 min+500ns]. -Acquire event for wl1 from 2ns to 1min+2ns -Acquire event for wl2 from 1min-10ns to 2min-15ns -*/ -void FeedEvents(StatsdConfig config, sp processor) { - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - - auto screenTurnedOnEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - auto screenTurnedOffEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - auto screenTurnedOnEvent2 = - CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 500, - android::view::DisplayStateEnum::DISPLAY_STATE_ON); - - auto acquireEvent1 = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, - attributionTags1, "wl1"); - auto releaseEvent1 = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, - attributionUids1, attributionTags1, "wl1"); - auto acquireEvent2 = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 10, - attributionUids2, attributionTags2, "wl2"); - auto releaseEvent2 = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 15, - attributionUids2, attributionTags2, "wl2"); - - std::vector> events; - - events.push_back(std::move(screenTurnedOnEvent)); - events.push_back(std::move(screenTurnedOffEvent)); - events.push_back(std::move(screenTurnedOnEvent2)); - events.push_back(std::move(acquireEvent1)); - events.push_back(std::move(acquireEvent2)); - events.push_back(std::move(releaseEvent1)); - events.push_back(std::move(releaseEvent2)); - - sortLogEventsByTimestamp(&events); - - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } -} - -} // namespace - -TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) { - ConfigKey cfgKey; - auto config = CreateStatsdConfig(DurationMetric::SUM); - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - FeedEvents(config, processor); - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - // Only 1 dimension output. The tag dimension in the predicate has been aggregated. - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - - auto data = reports.reports(0).metrics(0).duration_metrics().data(0); - // Validate dimension value. - ValidateAttributionUidDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 111); - // Validate bucket info. - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); - data = reports.reports(0).metrics(0).duration_metrics().data(0); - // The wakelock holding interval starts from the screen off event and to the end of the 1st - // bucket. - EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200); -} - -TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) { - ConfigKey cfgKey; - auto config = CreateStatsdConfig(DurationMetric::SUM); - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - FeedEvents(config, processor); - vector buffer; - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - // Dump the report after the end of 2nd bucket. - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); - auto data = reports.reports(0).metrics(0).duration_metrics().data(0); - // Validate dimension value. - ValidateAttributionUidDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 111); - // Two output buckets. - // The wakelock holding interval in the 1st bucket starts from the screen off event and to - // the end of the 1st bucket. - EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), - bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200)); - // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and - // ends at the second screen on event. - EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL); -} - -TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) { - ConfigKey cfgKey; - auto config = CreateStatsdConfig(DurationMetric::SUM); - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - FeedEvents(config, processor); - vector buffer; - ConfigMetricsReportList reports; - - std::vector> events; - events.push_back( - CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, - attributionUids1, attributionTags1, "wl3")); - events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100, - attributionUids1, attributionTags1, "wl3")); - sortLogEventsByTimestamp(&events); - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - - processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6); - auto data = reports.reports(0).metrics(0).duration_metrics().data(0); - ValidateAttributionUidDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 111); - // The last wakelock holding spans 4 buckets. - EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100); - EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs); - EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs); - EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL); -} - -TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) { - ConfigKey cfgKey; - auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - FeedEvents(config, processor); - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - - ASSERT_EQ(reports.reports_size(), 1); - - // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as - // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by - // itself. - ASSERT_EQ(1, reports.reports(0).metrics_size()); - ASSERT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size()); -} - -TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) { - ConfigKey cfgKey; - auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - FeedEvents(config, processor); - ConfigMetricsReportList reports; - vector buffer; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - // Dump the report after the end of 2nd bucket. One dimension with one bucket. - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); - auto data = reports.reports(0).metrics(0).duration_metrics().data(0); - // Validate dimension value. - ValidateAttributionUidDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 111); - // The max is acquire event for wl1 to screen off start. - EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200); -} - -TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) { - ConfigKey cfgKey; - auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); - ASSERT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - FeedEvents(config, processor); - ConfigMetricsReportList reports; - vector buffer; - - std::vector> events; - events.push_back( - CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); - events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, - attributionUids1, attributionTags1, "wl3")); - events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100, - attributionUids1, attributionTags1, "wl3")); - sortLogEventsByTimestamp(&events); - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - - processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP, - FAST, &buffer); - EXPECT_TRUE(buffer.size() > 0); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - backfillDimensionPath(&reports); - backfillStringInReport(&reports); - backfillStartEndTimestamp(&reports); - ASSERT_EQ(reports.reports_size(), 1); - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); - auto data = reports.reports(0).metrics(0).duration_metrics().data(0); - ValidateAttributionUidDimension(data.dimensions_in_what(), - util::WAKELOCK_STATE_CHANGED, 111); - // The last wakelock holding spans 4 buckets. - EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs); - EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(), - bucketStartTimeNs + 5 * bucketSizeNs); - EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_elapsed_nanos(), - bucketStartTimeNs + 6 * bucketSizeNs); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp deleted file mode 100644 index 85a60886349e..000000000000 --- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/external/StatsCallbackPuller.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../metrics/metrics_test_helper.h" -#include "src/stats_log_util.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -using namespace testing; -using Status = ::ndk::ScopedAStatus; -using aidl::android::os::BnPullAtomCallback; -using aidl::android::os::IPullAtomResultReceiver; -using aidl::android::util::StatsEventParcel; -using ::ndk::SharedRefBase; -using std::make_shared; -using std::shared_ptr; -using std::vector; -using std::this_thread::sleep_for; -using testing::Contains; - -namespace { -int pullTagId = -12; -bool pullSuccess; -vector values; -int64_t pullDelayNs; -int64_t pullTimeoutNs; -int64_t pullCoolDownNs; -std::thread pullThread; - -AStatsEvent* createSimpleEvent(int64_t value) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, pullTagId); - AStatsEvent_writeInt64(event, value); - AStatsEvent_build(event); - return event; -} - -void executePull(const shared_ptr& resultReceiver) { - // Convert stats_events into StatsEventParcels. - vector parcels; - for (int i = 0; i < values.size(); i++) { - AStatsEvent* event = createSimpleEvent(values[i]); - size_t size; - uint8_t* buffer = AStatsEvent_getBuffer(event, &size); - - StatsEventParcel p; - // vector.assign() creates a copy, but this is inevitable unless - // stats_event.h/c uses a vector as opposed to a buffer. - p.buffer.assign(buffer, buffer + size); - parcels.push_back(std::move(p)); - AStatsEvent_release(event); - } - - sleep_for(std::chrono::nanoseconds(pullDelayNs)); - resultReceiver->pullFinished(pullTagId, pullSuccess, parcels); -} - -class FakePullAtomCallback : public BnPullAtomCallback { -public: - Status onPullAtom(int atomTag, - const shared_ptr& resultReceiver) override { - // Force pull to happen in separate thread to simulate binder. - pullThread = std::thread(executePull, resultReceiver); - return Status::ok(); - } -}; - -class StatsCallbackPullerTest : public ::testing::Test { -public: - StatsCallbackPullerTest() { - } - - void SetUp() override { - pullSuccess = false; - pullDelayNs = 0; - values.clear(); - pullTimeoutNs = 10000000000LL; // 10 seconds. - pullCoolDownNs = 1000000000; // 1 second. - } - - void TearDown() override { - if (pullThread.joinable()) { - pullThread.join(); - } - values.clear(); - } -}; -} // Anonymous namespace. - -TEST_F(StatsCallbackPullerTest, PullSuccess) { - shared_ptr cb = SharedRefBase::make(); - int64_t value = 43; - pullSuccess = true; - values.push_back(value); - - StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {}); - - vector> dataHolder; - int64_t startTimeNs = getElapsedRealtimeNs(); - EXPECT_TRUE(puller.PullInternal(&dataHolder)); - int64_t endTimeNs = getElapsedRealtimeNs(); - - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_LT(startTimeNs, dataHolder[0]->GetElapsedTimestampNs()); - EXPECT_GT(endTimeNs, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(value, dataHolder[0]->getValues()[0].mValue.int_value); -} - -TEST_F(StatsCallbackPullerTest, PullFail) { - shared_ptr cb = SharedRefBase::make(); - pullSuccess = false; - int64_t value = 1234; - values.push_back(value); - - StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {}); - - vector> dataHolder; - EXPECT_FALSE(puller.PullInternal(&dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -TEST_F(StatsCallbackPullerTest, PullTimeout) { - shared_ptr cb = SharedRefBase::make(); - pullSuccess = true; - pullDelayNs = MillisToNano(5); // 5ms. - pullTimeoutNs = 10000; // 10 microseconds. - int64_t value = 4321; - values.push_back(value); - - StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {}); - - vector> dataHolder; - int64_t startTimeNs = getElapsedRealtimeNs(); - // Returns true to let StatsPuller code evaluate the timeout. - EXPECT_TRUE(puller.PullInternal(&dataHolder)); - int64_t endTimeNs = getElapsedRealtimeNs(); - int64_t actualPullDurationNs = endTimeNs - startTimeNs; - - // Pull should take at least the timeout amount of time, but should stop early because the delay - // is bigger. - EXPECT_LT(pullTimeoutNs, actualPullDurationNs); - EXPECT_GT(pullDelayNs, actualPullDurationNs); - ASSERT_EQ(0, dataHolder.size()); - - // Let the pull return and make sure that the dataHolder is not modified. - pullThread.join(); - ASSERT_EQ(0, dataHolder.size()); -} - -// Register a puller and ensure that the timeout logic works. -TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) { - shared_ptr cb = SharedRefBase::make(); - pullSuccess = true; - pullDelayNs = MillisToNano(5); // 5 ms. - pullTimeoutNs = 10000; // 10 microsseconds. - int64_t value = 4321; - int32_t uid = 123; - values.push_back(value); - - sp pullerManager = new StatsPullerManager(); - pullerManager->RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs, - vector(), cb); - vector> dataHolder; - int64_t startTimeNs = getElapsedRealtimeNs(); - // Returns false, since StatsPuller code will evaluate the timeout. - EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, startTimeNs, &dataHolder)); - int64_t endTimeNs = getElapsedRealtimeNs(); - int64_t actualPullDurationNs = endTimeNs - startTimeNs; - - // Pull should take at least the timeout amount of time, but should stop early because the delay - // is bigger. Make sure that the time is closer to the timeout, than to the intended delay. - EXPECT_LT(pullTimeoutNs, actualPullDurationNs); - EXPECT_GT(pullDelayNs / 5, actualPullDurationNs); - ASSERT_EQ(0, dataHolder.size()); - - // Let the pull return and make sure that the dataHolder is not modified. - pullThread.join(); - ASSERT_EQ(0, dataHolder.size()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/external/StatsPullerManager_test.cpp b/cmds/statsd/tests/external/StatsPullerManager_test.cpp deleted file mode 100644 index c76e85ec75e6..000000000000 --- a/cmds/statsd/tests/external/StatsPullerManager_test.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/external/StatsPullerManager.h" - -#include -#include -#include -#include - -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -using aidl::android::util::StatsEventParcel; -using ::ndk::SharedRefBase; -using std::make_shared; -using std::shared_ptr; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -namespace { - -int pullTagId1 = 10101; -int pullTagId2 = 10102; -int uid1 = 9999; -int uid2 = 8888; -ConfigKey configKey(50, 12345); -ConfigKey badConfigKey(60, 54321); -int unregisteredUid = 98765; -int64_t coolDownNs = NS_PER_SEC; -int64_t timeoutNs = NS_PER_SEC / 2; - -AStatsEvent* createSimpleEvent(int32_t atomId, int32_t value) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, atomId); - AStatsEvent_writeInt32(event, value); - AStatsEvent_build(event); - return event; -} - -class FakePullAtomCallback : public BnPullAtomCallback { -public: - FakePullAtomCallback(int32_t uid) : mUid(uid){}; - Status onPullAtom(int atomTag, - const shared_ptr& resultReceiver) override { - vector parcels; - AStatsEvent* event = createSimpleEvent(atomTag, mUid); - size_t size; - uint8_t* buffer = AStatsEvent_getBuffer(event, &size); - - StatsEventParcel p; - // vector.assign() creates a copy, but this is inevitable unless - // stats_event.h/c uses a vector as opposed to a buffer. - p.buffer.assign(buffer, buffer + size); - parcels.push_back(std::move(p)); - AStatsEvent_release(event); - resultReceiver->pullFinished(atomTag, /*success*/ true, parcels); - return Status::ok(); - } - int32_t mUid; -}; - -class FakePullUidProvider : public PullUidProvider { -public: - vector getPullAtomUids(int atomId) override { - if (atomId == pullTagId1) { - return {uid2, uid1}; - } else if (atomId == pullTagId2) { - return {uid2}; - } - return {}; - } -}; - -sp createPullerManagerAndRegister() { - sp pullerManager = new StatsPullerManager(); - shared_ptr cb1 = SharedRefBase::make(uid1); - pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1, true); - shared_ptr cb2 = SharedRefBase::make(uid2); - pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2, true); - pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1, true); - return pullerManager; -} -} // anonymous namespace - -TEST(StatsPullerManagerTest, TestPullInvalidUid) { - sp pullerManager = createPullerManagerAndRegister(); - - vector> data; - EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data, true)); -} - -TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) { - sp pullerManager = createPullerManagerAndRegister(); - - vector> data; - EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data, true)); - ASSERT_EQ(data.size(), 1); - EXPECT_EQ(data[0]->GetTagId(), pullTagId1); - ASSERT_EQ(data[0]->getValues().size(), 1); - EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid1); -} - -TEST(StatsPullerManagerTest, TestPullInvalidConfigKey) { - sp pullerManager = createPullerManagerAndRegister(); - sp uidProvider = new FakePullUidProvider(); - pullerManager->RegisterPullUidProvider(configKey, uidProvider); - - vector> data; - EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data, true)); -} - -TEST(StatsPullerManagerTest, TestPullConfigKeyGood) { - sp pullerManager = createPullerManagerAndRegister(); - sp uidProvider = new FakePullUidProvider(); - pullerManager->RegisterPullUidProvider(configKey, uidProvider); - - vector> data; - EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data, true)); - EXPECT_EQ(data[0]->GetTagId(), pullTagId1); - ASSERT_EQ(data[0]->getValues().size(), 1); - EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2); -} - -TEST(StatsPullerManagerTest, TestPullConfigKeyNoPullerWithUid) { - sp pullerManager = createPullerManagerAndRegister(); - sp uidProvider = new FakePullUidProvider(); - pullerManager->RegisterPullUidProvider(configKey, uidProvider); - - vector> data; - EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data, true)); -} - -} // namespace statsd -} // namespace os -} // namespace android \ No newline at end of file diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp deleted file mode 100644 index 55a90365e151..000000000000 --- a/cmds/statsd/tests/external/StatsPuller_test.cpp +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include - -#include -#include -#include - -#include "../metrics/metrics_test_helper.h" -#include "src/stats_log_util.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -using namespace testing; -using std::make_shared; -using std::shared_ptr; -using std::vector; -using std::this_thread::sleep_for; -using testing::Contains; - -namespace { -int pullTagId = 10014; - -bool pullSuccess; -vector> pullData; -long pullDelayNs; - -class FakePuller : public StatsPuller { -public: - FakePuller() - : StatsPuller(pullTagId, /*coolDownNs=*/MillisToNano(10), /*timeoutNs=*/MillisToNano(5)){}; - -private: - bool PullInternal(vector>* data) override { - (*data) = pullData; - sleep_for(std::chrono::nanoseconds(pullDelayNs)); - return pullSuccess; - } -}; - -FakePuller puller; - -std::unique_ptr createSimpleEvent(int64_t eventTimeNs, int64_t value) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, pullTagId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - AStatsEvent_writeInt64(statsEvent, value); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -class StatsPullerTest : public ::testing::Test { -public: - StatsPullerTest() { - } - - void SetUp() override { - puller.ForceClearCache(); - pullSuccess = false; - pullDelayNs = 0; - pullData.clear(); - } -}; - -} // Anonymous namespace. - -TEST_F(StatsPullerTest, PullSuccess) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = true; - - vector> dataHolder; - EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); - - sleep_for(std::chrono::milliseconds(11)); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - pullSuccess = true; - - EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value); -} - -TEST_F(StatsPullerTest, PullFailAfterSuccess) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = true; - - vector> dataHolder; - EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); - - sleep_for(std::chrono::milliseconds(11)); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - pullSuccess = false; - dataHolder.clear(); - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); - - // Fails due to hitting the cool down. - pullSuccess = true; - dataHolder.clear(); - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown -TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) { - pullData.push_back(createSimpleEvent(1111L, 33)); - pullSuccess = true; - // timeout is 5ms - pullDelayNs = MillisToNano(6); - - vector> dataHolder; - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - pullDelayNs = 0; - - pullSuccess = true; - dataHolder.clear(); - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -TEST_F(StatsPullerTest, PullFail) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = false; - - vector> dataHolder; - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -TEST_F(StatsPullerTest, PullTakeTooLong) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = true; - pullDelayNs = MillisToNano(6); - - vector> dataHolder; - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -TEST_F(StatsPullerTest, PullTooFast) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = true; - - vector> dataHolder; - EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - pullSuccess = true; - - dataHolder.clear(); - EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -} - -TEST_F(StatsPullerTest, PullFailsAndTooFast) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = false; - - vector> dataHolder; - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - pullSuccess = true; - - EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -TEST_F(StatsPullerTest, PullSameEventTime) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = true; - int64_t eventTimeNs = getElapsedRealtimeNs(); - - vector> dataHolder; - EXPECT_TRUE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - // Sleep to ensure the cool down expires. - sleep_for(std::chrono::milliseconds(11)); - pullSuccess = true; - - dataHolder.clear(); - EXPECT_TRUE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(1, dataHolder.size()); - EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); - EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); - ASSERT_EQ(1, dataHolder[0]->size()); - EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -} - -// Test pull takes longer than timeout, 2nd pull happens at same event time -TEST_F(StatsPullerTest, PullTakeTooLongAndPullSameEventTime) { - pullData.push_back(createSimpleEvent(1111L, 33)); - pullSuccess = true; - int64_t eventTimeNs = getElapsedRealtimeNs(); - // timeout is 5ms - pullDelayNs = MillisToNano(6); - - vector> dataHolder; - EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); - - // Sleep to ensure the cool down expires. 6ms is taken by the delay, so only 5 is needed here. - sleep_for(std::chrono::milliseconds(5)); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - pullDelayNs = 0; - - pullSuccess = true; - dataHolder.clear(); - EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -TEST_F(StatsPullerTest, PullFailsAndPullSameEventTime) { - pullData.push_back(createSimpleEvent(1111L, 33)); - - pullSuccess = false; - int64_t eventTimeNs = getElapsedRealtimeNs(); - - vector> dataHolder; - EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); - - // Sleep to ensure the cool down expires. - sleep_for(std::chrono::milliseconds(11)); - - pullData.clear(); - pullData.push_back(createSimpleEvent(2222L, 44)); - - pullSuccess = true; - - EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder)); - ASSERT_EQ(0, dataHolder.size()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp deleted file mode 100644 index a21dc8717776..000000000000 --- a/cmds/statsd/tests/external/puller_util_test.cpp +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "external/puller_util.h" - -#include -#include -#include - -#include - -#include "../metrics/metrics_test_helper.h" -#include "FieldValue.h" -#include "annotations.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -using namespace testing; -using std::shared_ptr; -using std::vector; -/* - * Test merge isolated and host uid - */ -namespace { -const int uidAtomTagId = 100; -const vector additiveFields = {3}; -const int nonUidAtomTagId = 200; -const int timestamp = 1234; -const int isolatedUid1 = 30; -const int isolatedUid2 = 40; -const int isolatedNonAdditiveData = 32; -const int isolatedAdditiveData = 31; -const int hostUid = 20; -const int hostNonAdditiveData = 22; -const int hostAdditiveData = 21; -const int attributionAtomTagId = 300; - -sp makeMockUidMap() { - return makeMockUidMapForOneHost(hostUid, {isolatedUid1, isolatedUid2}); -} - -} // anonymous namespace - -TEST(PullerUtilTest, MergeNoDimension) { - vector> data = { - // 30->22->31 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData, - isolatedAdditiveData), - - // 20->22->21 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData, - hostAdditiveData), - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - - ASSERT_EQ(1, (int)data.size()); - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(2).mValue.int_value); -} - -TEST(PullerUtilTest, MergeWithDimension) { - vector> data = { - // 30->32->31 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 20->32->21 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData, - hostAdditiveData), - - // 20->22->21 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData, - hostAdditiveData), - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value); - - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value); -} - -TEST(PullerUtilTest, NoMergeHostUidOnly) { - vector> data = { - // 20->32->31 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 20->22->21 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData, - hostAdditiveData), - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value); - - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value); -} - -TEST(PullerUtilTest, IsolatedUidOnly) { - vector> data = { - // 30->32->31 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 30->22->21 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData, - hostAdditiveData), - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - // 20->32->31 - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value); - - // 20->22->21 - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value); -} - -TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUid) { - vector> data = { - // 30->32->31 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 31->32->21 - makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid2, isolatedNonAdditiveData, - hostAdditiveData), - - // 20->32->21 - makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData, - hostAdditiveData), - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - - ASSERT_EQ(1, (int)data.size()); - - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(3, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData, - actualFieldValues->at(2).mValue.int_value); -} - -TEST(PullerUtilTest, NoNeedToMerge) { - vector> data = { - // 32->31 - CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 22->21 - CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, hostNonAdditiveData, - hostAdditiveData), - - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, nonUidAtomTagId, {} /*no additive fields*/); - - ASSERT_EQ(2, (int)data.size()); - - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(2, actualFieldValues->size()); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(1).mValue.int_value); - - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(2, actualFieldValues->size()); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(1).mValue.int_value); -} - -TEST(PullerUtilTest, MergeNoDimensionAttributionChain) { - vector> data = { - // 30->tag1->400->tag2->22->31 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, - {"tag1", "tag2"}, hostNonAdditiveData, isolatedAdditiveData), - - // 20->tag1->400->tag2->22->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, - {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); - - ASSERT_EQ(1, (int)data.size()); - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(5).mValue.int_value); -} - -TEST(PullerUtilTest, MergeWithDimensionAttributionChain) { - vector> data = { - // 200->tag1->30->tag2->32->31 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, isolatedUid1}, - {"tag1", "tag2"}, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 200->tag1->20->tag2->32->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid}, - {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData), - - // 200->tag1->20->tag2->22->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid}, - {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value); - - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value); -} - -TEST(PullerUtilTest, NoMergeHostUidOnlyAttributionChain) { - vector> data = { - // 20->tag1->400->tag2->32->31 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, - {"tag1", "tag2"}, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 20->tag1->400->tag2->22->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, - {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value); - - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value); -} - -TEST(PullerUtilTest, IsolatedUidOnlyAttributionChain) { - vector> data = { - // 30->tag1->400->tag2->32->31 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, - {"tag1", "tag2"}, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 30->tag1->400->tag2->22->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, - {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); - - ASSERT_EQ(2, (int)data.size()); - - // 20->tag1->400->tag2->32->31 - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value); - - // 20->tag1->400->tag2->22->21 - actualFieldValues = &data[1]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value); -} - -TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUidAttributionChain) { - vector> data = { - // 30->tag1->400->tag2->32->31 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, - {"tag1", "tag2"}, isolatedNonAdditiveData, - isolatedAdditiveData), - - // 31->tag1->400->tag2->32->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid2, 400}, - {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData), - - // 20->tag1->400->tag2->32->21 - makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, - {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData), - }; - - sp uidMap = makeMockUidMap(); - mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); - - ASSERT_EQ(1, (int)data.size()); - - const vector* actualFieldValues = &data[0]->getValues(); - ASSERT_EQ(6, actualFieldValues->size()); - EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); - EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); - EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); - EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); - EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); - EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData, - actualFieldValues->at(5).mValue.int_value); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp deleted file mode 100644 index 428c46f8a0d2..000000000000 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ /dev/null @@ -1,544 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/guardrail/StatsdStats.h" -#include "statslog_statsdtest.h" -#include "tests/statsd_test_util.h" - -#include -#include - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -using std::vector; - -TEST(StatsdStatsTest, TestValidConfigAdd) { - StatsdStats stats; - ConfigKey key(0, 12345); - const int metricsCount = 10; - const int conditionsCount = 20; - const int matchersCount = 30; - const int alertsCount = 10; - stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, - true /*valid config*/); - vector output; - stats.dumpStats(&output, false /*reset stats*/); - - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport = report.config_stats(0); - EXPECT_EQ(0, configReport.uid()); - EXPECT_EQ(12345, configReport.id()); - EXPECT_EQ(metricsCount, configReport.metric_count()); - EXPECT_EQ(conditionsCount, configReport.condition_count()); - EXPECT_EQ(matchersCount, configReport.matcher_count()); - EXPECT_EQ(alertsCount, configReport.alert_count()); - EXPECT_EQ(true, configReport.is_valid()); - EXPECT_FALSE(configReport.has_deletion_time_sec()); -} - -TEST(StatsdStatsTest, TestInvalidConfigAdd) { - StatsdStats stats; - ConfigKey key(0, 12345); - const int metricsCount = 10; - const int conditionsCount = 20; - const int matchersCount = 30; - const int alertsCount = 10; - stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, - false /*bad config*/); - vector output; - stats.dumpStats(&output, false); - - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport = report.config_stats(0); - // The invalid config should be put into icebox with a deletion time. - EXPECT_TRUE(configReport.has_deletion_time_sec()); -} - -TEST(StatsdStatsTest, TestConfigRemove) { - StatsdStats stats; - ConfigKey key(0, 12345); - const int metricsCount = 10; - const int conditionsCount = 20; - const int matchersCount = 30; - const int alertsCount = 10; - stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {}, - true); - vector output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport = report.config_stats(0); - EXPECT_FALSE(configReport.has_deletion_time_sec()); - - stats.noteConfigRemoved(key); - stats.dumpStats(&output, false); - good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport2 = report.config_stats(0); - EXPECT_TRUE(configReport2.has_deletion_time_sec()); -} - -TEST(StatsdStatsTest, TestSubStats) { - StatsdStats stats; - ConfigKey key(0, 12345); - stats.noteConfigReceived(key, 2, 3, 4, 5, {std::make_pair(123, 456)}, true); - - stats.noteMatcherMatched(key, StringToId("matcher1")); - stats.noteMatcherMatched(key, StringToId("matcher1")); - stats.noteMatcherMatched(key, StringToId("matcher2")); - - stats.noteConditionDimensionSize(key, StringToId("condition1"), 250); - stats.noteConditionDimensionSize(key, StringToId("condition1"), 240); - - stats.noteMetricDimensionSize(key, StringToId("metric1"), 201); - stats.noteMetricDimensionSize(key, StringToId("metric1"), 202); - - stats.noteAnomalyDeclared(key, StringToId("alert1")); - stats.noteAnomalyDeclared(key, StringToId("alert1")); - stats.noteAnomalyDeclared(key, StringToId("alert2")); - - // broadcast-> 2 - stats.noteBroadcastSent(key); - stats.noteBroadcastSent(key); - - // data drop -> 1 - stats.noteDataDropped(key, 123); - - // dump report -> 3 - stats.noteMetricsReportSent(key, 0); - stats.noteMetricsReportSent(key, 0); - stats.noteMetricsReportSent(key, 0); - - // activation_time_sec -> 2 - stats.noteActiveStatusChanged(key, true); - stats.noteActiveStatusChanged(key, true); - - // deactivation_time_sec -> 1 - stats.noteActiveStatusChanged(key, false); - - vector output; - stats.dumpStats(&output, true); // Dump and reset stats - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport = report.config_stats(0); - ASSERT_EQ(2, configReport.broadcast_sent_time_sec_size()); - ASSERT_EQ(1, configReport.data_drop_time_sec_size()); - ASSERT_EQ(1, configReport.data_drop_bytes_size()); - EXPECT_EQ(123, configReport.data_drop_bytes(0)); - ASSERT_EQ(3, configReport.dump_report_time_sec_size()); - ASSERT_EQ(3, configReport.dump_report_data_size_size()); - ASSERT_EQ(2, configReport.activation_time_sec_size()); - ASSERT_EQ(1, configReport.deactivation_time_sec_size()); - ASSERT_EQ(1, configReport.annotation_size()); - EXPECT_EQ(123, configReport.annotation(0).field_int64()); - EXPECT_EQ(456, configReport.annotation(0).field_int32()); - - ASSERT_EQ(2, configReport.matcher_stats_size()); - // matcher1 is the first in the list - if (configReport.matcher_stats(0).id() == StringToId("matcher1")) { - EXPECT_EQ(2, configReport.matcher_stats(0).matched_times()); - EXPECT_EQ(1, configReport.matcher_stats(1).matched_times()); - EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(1).id()); - } else { - // matcher1 is the second in the list. - EXPECT_EQ(1, configReport.matcher_stats(0).matched_times()); - EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(0).id()); - - EXPECT_EQ(2, configReport.matcher_stats(1).matched_times()); - EXPECT_EQ(StringToId("matcher1"), configReport.matcher_stats(1).id()); - } - - ASSERT_EQ(2, configReport.alert_stats_size()); - bool alert1first = configReport.alert_stats(0).id() == StringToId("alert1"); - EXPECT_EQ(StringToId("alert1"), configReport.alert_stats(alert1first ? 0 : 1).id()); - EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).alerted_times()); - EXPECT_EQ(StringToId("alert2"), configReport.alert_stats(alert1first ? 1 : 0).id()); - EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).alerted_times()); - - ASSERT_EQ(1, configReport.condition_stats_size()); - EXPECT_EQ(StringToId("condition1"), configReport.condition_stats(0).id()); - EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts()); - - ASSERT_EQ(1, configReport.metric_stats_size()); - EXPECT_EQ(StringToId("metric1"), configReport.metric_stats(0).id()); - EXPECT_EQ(202, configReport.metric_stats(0).max_tuple_counts()); - - // after resetting the stats, some new events come - stats.noteMatcherMatched(key, StringToId("matcher99")); - stats.noteConditionDimensionSize(key, StringToId("condition99"), 300); - stats.noteMetricDimensionSize(key, StringToId("metric99tion99"), 270); - stats.noteAnomalyDeclared(key, StringToId("alert99")); - - // now the config stats should only contain the stats about the new event. - stats.dumpStats(&output, false); - good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - ASSERT_EQ(1, report.config_stats_size()); - const auto& configReport2 = report.config_stats(0); - ASSERT_EQ(1, configReport2.matcher_stats_size()); - EXPECT_EQ(StringToId("matcher99"), configReport2.matcher_stats(0).id()); - EXPECT_EQ(1, configReport2.matcher_stats(0).matched_times()); - - ASSERT_EQ(1, configReport2.condition_stats_size()); - EXPECT_EQ(StringToId("condition99"), configReport2.condition_stats(0).id()); - EXPECT_EQ(300, configReport2.condition_stats(0).max_tuple_counts()); - - ASSERT_EQ(1, configReport2.metric_stats_size()); - EXPECT_EQ(StringToId("metric99tion99"), configReport2.metric_stats(0).id()); - EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts()); - - ASSERT_EQ(1, configReport2.alert_stats_size()); - EXPECT_EQ(StringToId("alert99"), configReport2.alert_stats(0).id()); - EXPECT_EQ(1, configReport2.alert_stats(0).alerted_times()); -} - -TEST(StatsdStatsTest, TestAtomLog) { - StatsdStats stats; - time_t now = time(nullptr); - // old event, we get it from the stats buffer. should be ignored. - stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, 1000); - - stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, now + 1); - stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, now + 2); - stats.noteAtomLogged(util::APP_CRASH_OCCURRED, now + 3); - - vector output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - - ASSERT_EQ(2, report.atom_stats_size()); - bool sensorAtomGood = false; - bool dropboxAtomGood = false; - - for (const auto& atomStats : report.atom_stats()) { - if (atomStats.tag() == util::SENSOR_STATE_CHANGED && atomStats.count() == 3) { - sensorAtomGood = true; - } - if (atomStats.tag() == util::APP_CRASH_OCCURRED && atomStats.count() == 1) { - dropboxAtomGood = true; - } - } - - EXPECT_TRUE(dropboxAtomGood); - EXPECT_TRUE(sensorAtomGood); -} - -TEST(StatsdStatsTest, TestNonPlatformAtomLog) { - StatsdStats stats; - time_t now = time(nullptr); - int newAtom1 = StatsdStats::kMaxPushedAtomId + 1; - int newAtom2 = StatsdStats::kMaxPushedAtomId + 2; - - stats.noteAtomLogged(newAtom1, now + 1); - stats.noteAtomLogged(newAtom1, now + 2); - stats.noteAtomLogged(newAtom2, now + 3); - - vector output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - - ASSERT_EQ(2, report.atom_stats_size()); - bool newAtom1Good = false; - bool newAtom2Good = false; - - for (const auto& atomStats : report.atom_stats()) { - if (atomStats.tag() == newAtom1 && atomStats.count() == 2) { - newAtom1Good = true; - } - if (atomStats.tag() == newAtom2 && atomStats.count() == 1) { - newAtom2Good = true; - } - } - - EXPECT_TRUE(newAtom1Good); - EXPECT_TRUE(newAtom2Good); -} - -TEST(StatsdStatsTest, TestPullAtomStats) { - StatsdStats stats; - - stats.updateMinPullIntervalSec(util::DISK_SPACE, 3333L); - stats.updateMinPullIntervalSec(util::DISK_SPACE, 2222L); - stats.updateMinPullIntervalSec(util::DISK_SPACE, 4444L); - - stats.notePull(util::DISK_SPACE); - stats.notePullTime(util::DISK_SPACE, 1111L); - stats.notePullDelay(util::DISK_SPACE, 1111L); - stats.notePull(util::DISK_SPACE); - stats.notePullTime(util::DISK_SPACE, 3333L); - stats.notePullDelay(util::DISK_SPACE, 3335L); - stats.notePull(util::DISK_SPACE); - stats.notePullFromCache(util::DISK_SPACE); - stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true); - stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, false); - stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true); - stats.notePullBinderCallFailed(util::DISK_SPACE); - stats.notePullUidProviderNotFound(util::DISK_SPACE); - stats.notePullerNotFound(util::DISK_SPACE); - stats.notePullerNotFound(util::DISK_SPACE); - stats.notePullTimeout(util::DISK_SPACE, 3000L, 6000L); - stats.notePullTimeout(util::DISK_SPACE, 4000L, 7000L); - - vector output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - - ASSERT_EQ(1, report.pulled_atom_stats_size()); - - EXPECT_EQ(util::DISK_SPACE, report.pulled_atom_stats(0).atom_id()); - EXPECT_EQ(3, report.pulled_atom_stats(0).total_pull()); - EXPECT_EQ(1, report.pulled_atom_stats(0).total_pull_from_cache()); - EXPECT_EQ(2222L, report.pulled_atom_stats(0).min_pull_interval_sec()); - EXPECT_EQ(2222L, report.pulled_atom_stats(0).average_pull_time_nanos()); - EXPECT_EQ(3333L, report.pulled_atom_stats(0).max_pull_time_nanos()); - EXPECT_EQ(2223L, report.pulled_atom_stats(0).average_pull_delay_nanos()); - EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos()); - EXPECT_EQ(2L, report.pulled_atom_stats(0).registered_count()); - EXPECT_EQ(1L, report.pulled_atom_stats(0).unregistered_count()); - EXPECT_EQ(1L, report.pulled_atom_stats(0).binder_call_failed()); - EXPECT_EQ(1L, report.pulled_atom_stats(0).failed_uid_provider_not_found()); - EXPECT_EQ(2L, report.pulled_atom_stats(0).puller_not_found()); - ASSERT_EQ(2, report.pulled_atom_stats(0).pull_atom_metadata_size()); - EXPECT_EQ(3000L, report.pulled_atom_stats(0).pull_atom_metadata(0).pull_timeout_uptime_millis()); - EXPECT_EQ(4000L, report.pulled_atom_stats(0).pull_atom_metadata(1).pull_timeout_uptime_millis()); - EXPECT_EQ(6000L, report.pulled_atom_stats(0).pull_atom_metadata(0) - .pull_timeout_elapsed_millis()); - EXPECT_EQ(7000L, report.pulled_atom_stats(0).pull_atom_metadata(1) - .pull_timeout_elapsed_millis()); -} - -TEST(StatsdStatsTest, TestAtomMetricsStats) { - StatsdStats stats; - time_t now = time(nullptr); - // old event, we get it from the stats buffer. should be ignored. - stats.noteBucketDropped(1000L); - - stats.noteBucketBoundaryDelayNs(1000L, -1L); - stats.noteBucketBoundaryDelayNs(1000L, -10L); - stats.noteBucketBoundaryDelayNs(1000L, 2L); - - stats.noteBucketBoundaryDelayNs(1001L, 1L); - - vector output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - - ASSERT_EQ(2, report.atom_metric_stats().size()); - - auto atomStats = report.atom_metric_stats(0); - EXPECT_EQ(1000L, atomStats.metric_id()); - EXPECT_EQ(1L, atomStats.bucket_dropped()); - EXPECT_EQ(-10L, atomStats.min_bucket_boundary_delay_ns()); - EXPECT_EQ(2L, atomStats.max_bucket_boundary_delay_ns()); - - auto atomStats2 = report.atom_metric_stats(1); - EXPECT_EQ(1001L, atomStats2.metric_id()); - EXPECT_EQ(0L, atomStats2.bucket_dropped()); - EXPECT_EQ(0L, atomStats2.min_bucket_boundary_delay_ns()); - EXPECT_EQ(1L, atomStats2.max_bucket_boundary_delay_ns()); -} - -TEST(StatsdStatsTest, TestAnomalyMonitor) { - StatsdStats stats; - stats.noteRegisteredAnomalyAlarmChanged(); - stats.noteRegisteredAnomalyAlarmChanged(); - - vector output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - bool good = report.ParseFromArray(&output[0], output.size()); - EXPECT_TRUE(good); - - EXPECT_EQ(2, report.anomaly_alarm_stats().alarms_registered()); -} - -TEST(StatsdStatsTest, TestTimestampThreshold) { - StatsdStats stats; - vector timestamps; - for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) { - timestamps.push_back(i); - } - ConfigKey key(0, 12345); - stats.noteConfigReceived(key, 2, 3, 4, 5, {}, true); - - for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) { - stats.noteDataDropped(key, timestamps[i]); - stats.noteBroadcastSent(key, timestamps[i]); - stats.noteMetricsReportSent(key, 0, timestamps[i]); - stats.noteActiveStatusChanged(key, true, timestamps[i]); - stats.noteActiveStatusChanged(key, false, timestamps[i]); - } - - int32_t newTimestamp = 10000; - - // now it should trigger removing oldest timestamp - stats.noteDataDropped(key, 123, 10000); - stats.noteBroadcastSent(key, 10000); - stats.noteMetricsReportSent(key, 0, 10000); - stats.noteActiveStatusChanged(key, true, 10000); - stats.noteActiveStatusChanged(key, false, 10000); - - EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end()); - const auto& configStats = stats.mConfigStats[key]; - - size_t maxCount = StatsdStats::kMaxTimestampCount; - ASSERT_EQ(maxCount, configStats->broadcast_sent_time_sec.size()); - ASSERT_EQ(maxCount, configStats->data_drop_time_sec.size()); - ASSERT_EQ(maxCount, configStats->dump_report_stats.size()); - ASSERT_EQ(maxCount, configStats->activation_time_sec.size()); - ASSERT_EQ(maxCount, configStats->deactivation_time_sec.size()); - - // the oldest timestamp is the second timestamp in history - EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); - EXPECT_EQ(1, configStats->data_drop_bytes.front()); - EXPECT_EQ(1, configStats->dump_report_stats.front().first); - EXPECT_EQ(1, configStats->activation_time_sec.front()); - EXPECT_EQ(1, configStats->deactivation_time_sec.front()); - - // the last timestamp is the newest timestamp. - EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back()); - EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back()); - EXPECT_EQ(123, configStats->data_drop_bytes.back()); - EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first); - EXPECT_EQ(newTimestamp, configStats->activation_time_sec.back()); - EXPECT_EQ(newTimestamp, configStats->deactivation_time_sec.back()); -} - -TEST(StatsdStatsTest, TestSystemServerCrash) { - StatsdStats stats; - vector timestamps; - for (int i = 0; i < StatsdStats::kMaxSystemServerRestarts; i++) { - timestamps.push_back(i); - stats.noteSystemServerRestart(timestamps[i]); - } - vector output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - EXPECT_TRUE(report.ParseFromArray(&output[0], output.size())); - const int maxCount = StatsdStats::kMaxSystemServerRestarts; - ASSERT_EQ(maxCount, (int)report.system_restart_sec_size()); - - stats.noteSystemServerRestart(StatsdStats::kMaxSystemServerRestarts + 1); - output.clear(); - stats.dumpStats(&output, false); - EXPECT_TRUE(report.ParseFromArray(&output[0], output.size())); - ASSERT_EQ(maxCount, (int)report.system_restart_sec_size()); - EXPECT_EQ(StatsdStats::kMaxSystemServerRestarts + 1, report.system_restart_sec(maxCount - 1)); -} - -TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit) { - StatsdStats stats; - int uid1 = 1; - int uid2 = 2; - stats.noteActivationBroadcastGuardrailHit(uid1, 10); - stats.noteActivationBroadcastGuardrailHit(uid1, 20); - - // Test that we only keep 20 timestamps. - for (int i = 0; i < 100; i++) { - stats.noteActivationBroadcastGuardrailHit(uid2, i); - } - - vector output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - EXPECT_TRUE(report.ParseFromArray(&output[0], output.size())); - - ASSERT_EQ(2, report.activation_guardrail_stats_size()); - bool uid1Good = false; - bool uid2Good = false; - for (const auto& guardrailTimes : report.activation_guardrail_stats()) { - if (uid1 == guardrailTimes.uid()) { - uid1Good = true; - ASSERT_EQ(2, guardrailTimes.guardrail_met_sec_size()); - EXPECT_EQ(10, guardrailTimes.guardrail_met_sec(0)); - EXPECT_EQ(20, guardrailTimes.guardrail_met_sec(1)); - } else if (uid2 == guardrailTimes.uid()) { - int maxCount = StatsdStats::kMaxTimestampCount; - uid2Good = true; - ASSERT_EQ(maxCount, guardrailTimes.guardrail_met_sec_size()); - for (int i = 0; i < maxCount; i++) { - EXPECT_EQ(100 - maxCount + i, guardrailTimes.guardrail_met_sec(i)); - } - } else { - FAIL() << "Unexpected uid."; - } - } - EXPECT_TRUE(uid1Good); - EXPECT_TRUE(uid2Good); -} - -TEST(StatsdStatsTest, TestAtomErrorStats) { - StatsdStats stats; - - int pushAtomTag = 100; - int pullAtomTag = 1000; - int numErrors = 10; - - for (int i = 0; i < numErrors; i++) { - // We must call noteAtomLogged as well because only those pushed atoms - // that have been logged will have stats printed about them in the - // proto. - stats.noteAtomLogged(pushAtomTag, /*timeSec=*/0); - stats.noteAtomError(pushAtomTag, /*pull=*/false); - - stats.noteAtomError(pullAtomTag, /*pull=*/true); - } - - vector output; - stats.dumpStats(&output, false); - StatsdStatsReport report; - EXPECT_TRUE(report.ParseFromArray(&output[0], output.size())); - - // Check error count = numErrors for push atom - ASSERT_EQ(1, report.atom_stats_size()); - const auto& pushedAtomStats = report.atom_stats(0); - EXPECT_EQ(pushAtomTag, pushedAtomStats.tag()); - EXPECT_EQ(numErrors, pushedAtomStats.error_count()); - - // Check error count = numErrors for pull atom - ASSERT_EQ(1, report.pulled_atom_stats_size()); - const auto& pulledAtomStats = report.pulled_atom_stats(0); - EXPECT_EQ(pullAtomTag, pulledAtomStats.atom_id()); - EXPECT_EQ(numErrors, pulledAtomStats.atom_error_count()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp deleted file mode 100644 index 3a654565b4aa..000000000000 --- a/cmds/statsd/tests/indexed_priority_queue_test.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/anomaly/indexed_priority_queue.h" - -#include - -using namespace android::os::statsd; - -/** struct for template in indexed_priority_queue */ -struct AATest : public RefBase { - AATest(uint32_t val, std::string a, std::string b) : val(val), a(a), b(b) { - } - - const int val; - const std::string a; - const std::string b; - - struct Smaller { - bool operator()(const sp a, const sp b) const { - return (a->val < b->val); - } - }; -}; - -#ifdef __ANDROID__ -TEST(indexed_priority_queue, empty_and_size) { - std::string emptyMetricId; - std::string emptyDimensionId; - indexed_priority_queue ipq; - sp aa4 = new AATest{4, emptyMetricId, emptyDimensionId}; - sp aa8 = new AATest{8, emptyMetricId, emptyDimensionId}; - - ASSERT_EQ(0u, ipq.size()); - EXPECT_TRUE(ipq.empty()); - - ipq.push(aa4); - ASSERT_EQ(1u, ipq.size()); - EXPECT_FALSE(ipq.empty()); - - ipq.push(aa8); - ASSERT_EQ(2u, ipq.size()); - EXPECT_FALSE(ipq.empty()); - - ipq.remove(aa4); - ASSERT_EQ(1u, ipq.size()); - EXPECT_FALSE(ipq.empty()); - - ipq.remove(aa8); - ASSERT_EQ(0u, ipq.size()); - EXPECT_TRUE(ipq.empty()); -} - -TEST(indexed_priority_queue, top) { - std::string emptyMetricId; - std::string emptyDimensionId; - indexed_priority_queue ipq; - sp aa2 = new AATest{2, emptyMetricId, emptyDimensionId}; - sp aa4 = new AATest{4, emptyMetricId, emptyDimensionId}; - sp aa8 = new AATest{8, emptyMetricId, emptyDimensionId}; - sp aa12 = new AATest{12, emptyMetricId, emptyDimensionId}; - sp aa16 = new AATest{16, emptyMetricId, emptyDimensionId}; - sp aa20 = new AATest{20, emptyMetricId, emptyDimensionId}; - - EXPECT_EQ(ipq.top(), nullptr); - - // add 8, 4, 12 - ipq.push(aa8); - EXPECT_EQ(ipq.top(), aa8); - - ipq.push(aa12); - EXPECT_EQ(ipq.top(), aa8); - - ipq.push(aa4); - EXPECT_EQ(ipq.top(), aa4); - - // remove 12, 4 - ipq.remove(aa12); - EXPECT_EQ(ipq.top(), aa4); - - ipq.remove(aa4); - EXPECT_EQ(ipq.top(), aa8); - - // add 16, 2, 20 - ipq.push(aa16); - EXPECT_EQ(ipq.top(), aa8); - - ipq.push(aa2); - EXPECT_EQ(ipq.top(), aa2); - - ipq.push(aa20); - EXPECT_EQ(ipq.top(), aa2); - - // remove 2, 20, 16, 8 - ipq.remove(aa2); - EXPECT_EQ(ipq.top(), aa8); - - ipq.remove(aa20); - EXPECT_EQ(ipq.top(), aa8); - - ipq.remove(aa16); - EXPECT_EQ(ipq.top(), aa8); - - ipq.remove(aa8); - EXPECT_EQ(ipq.top(), nullptr); -} - -TEST(indexed_priority_queue, push_same_aa) { - std::string emptyMetricId; - std::string emptyDimensionId; - indexed_priority_queue ipq; - sp aa4_a = new AATest{4, emptyMetricId, emptyDimensionId}; - sp aa4_b = new AATest{4, emptyMetricId, emptyDimensionId}; - - ipq.push(aa4_a); - ASSERT_EQ(1u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4_a)); - EXPECT_FALSE(ipq.contains(aa4_b)); - - ipq.push(aa4_a); - ASSERT_EQ(1u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4_a)); - EXPECT_FALSE(ipq.contains(aa4_b)); - - ipq.push(aa4_b); - ASSERT_EQ(2u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4_a)); - EXPECT_TRUE(ipq.contains(aa4_b)); -} - -TEST(indexed_priority_queue, remove_nonexistant) { - std::string emptyMetricId; - std::string emptyDimensionId; - indexed_priority_queue ipq; - sp aa4 = new AATest{4, emptyMetricId, emptyDimensionId}; - sp aa5 = new AATest{5, emptyMetricId, emptyDimensionId}; - - ipq.push(aa4); - ipq.remove(aa5); - ASSERT_EQ(1u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4)); - EXPECT_FALSE(ipq.contains(aa5)); -} - -TEST(indexed_priority_queue, remove_same_aa) { - indexed_priority_queue ipq; - std::string emptyMetricId; - std::string emptyDimensionId; - sp aa4_a = new AATest{4, emptyMetricId, emptyDimensionId}; - sp aa4_b = new AATest{4, emptyMetricId, emptyDimensionId}; - - ipq.push(aa4_a); - ipq.push(aa4_b); - ASSERT_EQ(2u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4_a)); - EXPECT_TRUE(ipq.contains(aa4_b)); - - ipq.remove(aa4_b); - ASSERT_EQ(1u, ipq.size()); - EXPECT_TRUE(ipq.contains(aa4_a)); - EXPECT_FALSE(ipq.contains(aa4_b)); - - ipq.remove(aa4_a); - ASSERT_EQ(0u, ipq.size()); - EXPECT_FALSE(ipq.contains(aa4_a)); - EXPECT_FALSE(ipq.contains(aa4_b)); -} - -TEST(indexed_priority_queue, nulls) { - indexed_priority_queue ipq; - - EXPECT_TRUE(ipq.empty()); - EXPECT_FALSE(ipq.contains(nullptr)); - - ipq.push(nullptr); - EXPECT_TRUE(ipq.empty()); - EXPECT_FALSE(ipq.contains(nullptr)); - - ipq.remove(nullptr); - EXPECT_TRUE(ipq.empty()); - EXPECT_FALSE(ipq.contains(nullptr)); -} - -TEST(indexed_priority_queue, pop) { - indexed_priority_queue ipq; - std::string emptyMetricId; - std::string emptyDimensionId; - sp a = new AATest{1, emptyMetricId, emptyDimensionId}; - sp b = new AATest{2, emptyMetricId, emptyDimensionId}; - sp c = new AATest{3, emptyMetricId, emptyDimensionId}; - - ipq.push(c); - ipq.push(b); - ipq.push(a); - ASSERT_EQ(3u, ipq.size()); - - ipq.pop(); - ASSERT_EQ(2u, ipq.size()); - EXPECT_FALSE(ipq.contains(a)); - EXPECT_TRUE(ipq.contains(b)); - EXPECT_TRUE(ipq.contains(c)); - - ipq.pop(); - ASSERT_EQ(1u, ipq.size()); - EXPECT_FALSE(ipq.contains(a)); - EXPECT_FALSE(ipq.contains(b)); - EXPECT_TRUE(ipq.contains(c)); - - ipq.pop(); - ASSERT_EQ(0u, ipq.size()); - EXPECT_FALSE(ipq.contains(a)); - EXPECT_FALSE(ipq.contains(b)); - EXPECT_FALSE(ipq.contains(c)); - EXPECT_TRUE(ipq.empty()); - - ipq.pop(); // pop an empty queue - EXPECT_TRUE(ipq.empty()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp deleted file mode 100644 index a15f95bef358..000000000000 --- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "logd/LogEventQueue.h" - -#include -#include -#include - -#include - -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -namespace android { -namespace os { -namespace statsd { - -using namespace android; -using namespace testing; - -using std::unique_ptr; - -namespace { - -std::unique_ptr makeLogEvent(uint64_t timestampNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 10); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -} // anonymous namespace - -#ifdef __ANDROID__ -TEST(LogEventQueue_test, TestGoodConsumer) { - LogEventQueue queue(50); - int64_t timeBaseNs = 100; - std::thread writer([&queue, timeBaseNs] { - for (int i = 0; i < 100; i++) { - int64_t oldestEventNs; - bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs); - EXPECT_TRUE(success); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - }); - - std::thread reader([&queue, timeBaseNs] { - for (int i = 0; i < 100; i++) { - auto event = queue.waitPop(); - EXPECT_TRUE(event != nullptr); - // All events are in right order. - EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs()); - } - }); - - reader.join(); - writer.join(); -} - -TEST(LogEventQueue_test, TestSlowConsumer) { - LogEventQueue queue(50); - int64_t timeBaseNs = 100; - std::thread writer([&queue, timeBaseNs] { - int failure_count = 0; - int64_t oldestEventNs; - for (int i = 0; i < 100; i++) { - bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs); - if (!success) failure_count++; - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - - // There is some remote chance that reader thread not get chance to run before writer thread - // ends. That's why the following comparison is not "==". - // There will be at least 45 events lost due to overflow. - EXPECT_TRUE(failure_count >= 45); - // The oldest event must be at least the 6th event. - EXPECT_TRUE(oldestEventNs <= (100 + 5 * 1000)); - }); - - std::thread reader([&queue, timeBaseNs] { - // The consumer quickly processed 5 events, then it got stuck (not reading anymore). - for (int i = 0; i < 5; i++) { - auto event = queue.waitPop(); - EXPECT_TRUE(event != nullptr); - // All events are in right order. - EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs()); - } - }); - - reader.join(); - writer.join(); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/metadata_util_test.cpp b/cmds/statsd/tests/metadata_util_test.cpp deleted file mode 100644 index 7707890cbd0c..000000000000 --- a/cmds/statsd/tests/metadata_util_test.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include "metadata_util.h" -#include "tests/statsd_test_util.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -TEST(MetadataUtilTest, TestWriteAndReadMetricDimensionKey) { - HashableDimensionKey dim; - HashableDimensionKey dim2; - int pos1[] = {1, 1, 1}; - int pos2[] = {1, 1, 2}; - int pos3[] = {1, 1, 3}; - int pos4[] = {2, 0, 0}; - Field field1(10, pos1, 2); - Field field2(10, pos2, 2); - Field field3(10, pos3, 2); - Field field4(10, pos4, 0); - - Value value1((int32_t)10025); - Value value2("tag"); - Value value3((int32_t)987654); - Value value4((int32_t)99999); - - dim.addValue(FieldValue(field1, value1)); - dim.addValue(FieldValue(field2, value2)); - dim.addValue(FieldValue(field3, value3)); - dim.addValue(FieldValue(field4, value4)); - - dim2.addValue(FieldValue(field1, value1)); - dim2.addValue(FieldValue(field2, value2)); - - MetricDimensionKey dimKey(dim, dim2); - - metadata::MetricDimensionKey metadataDimKey; - writeMetricDimensionKeyToMetadataDimensionKey(dimKey, &metadataDimKey); - - MetricDimensionKey loadedDimKey = loadMetricDimensionKeyFromProto(metadataDimKey); - - ASSERT_EQ(loadedDimKey, dimKey); - ASSERT_EQ(std::hash{}(loadedDimKey), - std::hash{}(dimKey)); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp deleted file mode 100644 index bb8e7bfd90f4..000000000000 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ /dev/null @@ -1,476 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/metrics/CountMetricProducer.h" - -#include -#include -#include -#include - -#include - -#include "metrics_test_helper.h" -#include "src/stats_log_util.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - - -namespace { -const ConfigKey kConfigKey(0, 12345); - -void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeString(statsEvent, uid.c_str()); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -} // namespace - -// Setup for parameterized tests. -class CountMetricProducerTest_PartialBucket : public TestWithParam {}; - -INSTANTIATE_TEST_SUITE_P(CountMetricProducerTest_PartialBucket, - CountMetricProducerTest_PartialBucket, - testing::Values(APP_UPGRADE, BOOT_COMPLETE)); - -TEST(CountMetricProducerTest, TestFirstBucket) { - CountMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - sp wizard = new NaggyMock(); - - CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2); - EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(10, countProducer.mCurrentBucketNum); - EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs()); -} - -TEST(CountMetricProducerTest, TestNonDimensionalEvents) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; - int tagId = 1; - - CountMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - - sp wizard = new NaggyMock(); - - CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, bucketStartTimeNs, bucketStartTimeNs); - - // 2 events in bucket 1. - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); - - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // Flushes at event #2. - countProducer.flushIfNeededLocked(bucketStartTimeNs + 2); - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - - // Flushes. - countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); - ASSERT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != - countProducer.mPastBuckets.end()); - const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(1UL, buckets.size()); - EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); - EXPECT_EQ(2LL, buckets[0].mCount); - - // 1 matched event happens in bucket 2. - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 2, tagId); - - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - - countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); - ASSERT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != - countProducer.mPastBuckets.end()); - ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1]; - EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs); - EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs); - EXPECT_EQ(1LL, bucketInfo2.mCount); - - // nothing happens in bucket 3. we should not record anything for bucket 3. - countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); - ASSERT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != - countProducer.mPastBuckets.end()); - const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(2UL, buckets3.size()); -} - -TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - - CountMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_condition(StringToId("SCREEN_ON")); - - sp wizard = new NaggyMock(); - - CountMetricProducer countProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, - bucketStartTimeNs, bucketStartTimeNs); - - countProducer.onConditionChanged(true, bucketStartTimeNs); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1); - countProducer.onMatchedLogEvent(1 /*matcher index*/, event1); - - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - - countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2); - - // Upon this match event, the matched event1 is flushed. - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, bucketStartTimeNs + 10, /*atomId=*/1); - countProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - - countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); - ASSERT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != - countProducer.mPastBuckets.end()); - - const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(1UL, buckets.size()); - const auto& bucketInfo = buckets[0]; - EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); - EXPECT_EQ(1LL, bucketInfo.mCount); -} - -TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - - int tagId = 1; - int conditionTagId = 2; - - CountMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); - MetricConditionLink* link = metric.add_links(); - link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); - buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what()); - buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition()); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, bucketStartTimeNs + 10, tagId, /*uid=*/"222"); - - ConditionKey key1; - key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = { - getMockedDimensionKey(conditionTagId, 2, "111")}; - - ConditionKey key2; - key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = { - getMockedDimensionKey(conditionTagId, 2, "222")}; - - sp wizard = new NaggyMock(); - - EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); - - EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); - - CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/, - {ConditionState::kUnknown}, wizard, bucketStartTimeNs, - bucketStartTimeNs); - - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); - ASSERT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != - countProducer.mPastBuckets.end()); - const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(1UL, buckets.size()); - const auto& bucketInfo = buckets[0]; - EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); - EXPECT_EQ(1LL, bucketInfo.mCount); -} - -TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) { - sp alarmMonitor; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - - int tagId = 1; - int conditionTagId = 2; - - CountMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - Alert alert; - alert.set_num_buckets(3); - alert.set_trigger_if_sum_gt(2); - - sp wizard = new NaggyMock(); - - CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard, - bucketStartTimeNs, bucketStartTimeNs); - - sp anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); - EXPECT_TRUE(anomalyTracker != nullptr); - - // Bucket is not flushed yet. - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - - // App upgrade or boot complete forces bucket flush. - // Check that there's a past bucket and the bucket end is not adjusted. - switch (GetParam()) { - case APP_UPGRADE: - countProducer.notifyAppUpgrade(eventTimeNs); - break; - case BOOT_COMPLETE: - countProducer.onStatsdInitCompleted(eventTimeNs); - break; - } - ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(eventTimeNs, - countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ(0, countProducer.getCurrentBucketNum()); - EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs); - // Anomaly tracker only contains full buckets. - EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - - int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs(); - // Next event occurs in same bucket as partial bucket created. - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222"); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(0, countProducer.getCurrentBucketNum()); - EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - - // Third event in following bucket. - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333"); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, countProducer.getCurrentBucketNum()); - EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -} - -TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; - - int tagId = 1; - int conditionTagId = 2; - - CountMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - - sp wizard = new NaggyMock(); - - CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard, - bucketStartTimeNs, bucketStartTimeNs); - - // Bucket is flushed yet. - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - ASSERT_EQ(0UL, countProducer.mPastBuckets.size()); - - // App upgrade or boot complete forces bucket flush. - // Check that there's a past bucket and the bucket end is not adjusted since the upgrade - // occurred after the bucket end time. - switch (GetParam()) { - case APP_UPGRADE: - countProducer.notifyAppUpgrade(eventTimeNs); - break; - case BOOT_COMPLETE: - countProducer.onStatsdInitCompleted(eventTimeNs); - break; - } - ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, - countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs); - - // Next event occurs in same bucket as partial bucket created. - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, bucketStartTimeNs + 70 * NS_PER_SEC + 10, tagId, /*uid=*/"222"); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - - // Third event in following bucket. - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333"); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ((int64_t)eventTimeNs, - countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, - countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs); -} - -TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { - sp alarmMonitor; - Alert alert; - alert.set_id(11); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(2); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 1; - alert.set_refractory_period_secs(refPeriodSec); - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; - - CountMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - - sp wizard = new NaggyMock(); - - CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, bucketStartTimeNs, bucketStartTimeNs); - - sp anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); - - int tagId = 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event3, bucketStartTimeNs + 2 * bucketSizeNs + 1, tagId); - LogEvent event4(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event4, bucketStartTimeNs + 3 * bucketSizeNs + 1, tagId); - LogEvent event5(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event5, bucketStartTimeNs + 3 * bucketSizeNs + 2, tagId); - LogEvent event6(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event6, bucketStartTimeNs + 3 * bucketSizeNs + 3, tagId); - LogEvent event7(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event7, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, tagId); - - // Two events in bucket #0. - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); - EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); - - // One event in bucket #2. No alarm as bucket #0 is trashed out. - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); - EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); - - // Two events in bucket #3. - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5); - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6); - ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); - EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second); - // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6 - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); - - countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7); - ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); - EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); -} - -TEST(CountMetricProducerTest, TestOneWeekTimeUnit) { - CountMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_WEEK); - - sp wizard = new NaggyMock(); - - int64_t oneDayNs = 24 * 60 * 60 * 1e9; - int64_t fiveWeeksNs = 5 * 7 * oneDayNs; - - CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard, - oneDayNs, fiveWeeksNs); - - int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs; - - EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(4, countProducer.mCurrentBucketNum); - EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp deleted file mode 100644 index 05cfa37b0ee1..000000000000 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/metrics/DurationMetricProducer.h" - -#include -#include -#include - -#include -#include -#include - -#include "metrics_test_helper.h" -#include "src/condition/ConditionWizard.h" -#include "src/stats_log_util.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -using namespace android::os::statsd; -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - - -namespace { - -const ConfigKey kConfigKey(0, 12345); -void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -} // namespace - -// Setup for parameterized tests. -class DurationMetricProducerTest_PartialBucket : public TestWithParam {}; - -INSTANTIATE_TEST_SUITE_P(DurationMetricProducerTest_PartialBucket, - DurationMetricProducerTest_PartialBucket, - testing::Values(APP_UPGRADE, BOOT_COMPLETE)); - -TEST(DurationMetricTrackerTest, TestFirstBucket) { - sp wizard = new NaggyMock(); - DurationMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_SUM); - - FieldMatcher dimensions; - - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2); - - EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(10, durationProducer.mCurrentBucketNum); - EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs()); -} - -TEST(DurationMetricTrackerTest, TestNoCondition) { - sp wizard = new NaggyMock(); - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - - DurationMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_SUM); - - int tagId = 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, bucketStartTimeNs + bucketSizeNs + 2, tagId); - - FieldMatcher dimensions; - - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); - - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); - ASSERT_EQ(1UL, durationProducer.mPastBuckets.size()); - EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != - durationProducer.mPastBuckets.end()); - const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(2UL, buckets.size()); - EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); - EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs); - EXPECT_EQ(2LL, buckets[1].mDuration); -} - -TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { - sp wizard = new NaggyMock(); - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - - DurationMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_SUM); - - int tagId = 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId); - LogEvent event4(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId); - - FieldMatcher dimensions; - - DurationMetricProducer durationProducer( - kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, - 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); - durationProducer.mCondition = ConditionState::kFalse; - - EXPECT_FALSE(durationProducer.mCondition); - EXPECT_FALSE(durationProducer.isConditionSliced()); - - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); - ASSERT_EQ(0UL, durationProducer.mPastBuckets.size()); - - durationProducer.onMatchedLogEvent(1 /* start index*/, event3); - durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); - durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); - ASSERT_EQ(1UL, durationProducer.mPastBuckets.size()); - EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != - durationProducer.mPastBuckets.end()); - const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(1UL, buckets2.size()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); - EXPECT_EQ(1LL, buckets2[0].mDuration); -} - -TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { - sp wizard = new NaggyMock(); - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - - DurationMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_SUM); - - int tagId = 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); - LogEvent event3(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId); - LogEvent event4(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId); - - FieldMatcher dimensions; - - DurationMetricProducer durationProducer( - kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, - 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); - - EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); - EXPECT_FALSE(durationProducer.isConditionSliced()); - - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); - ASSERT_EQ(0UL, durationProducer.mPastBuckets.size()); - - durationProducer.onMatchedLogEvent(1 /* start index*/, event3); - durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); - durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); - ASSERT_EQ(1UL, durationProducer.mPastBuckets.size()); - const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(1UL, buckets2.size()); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); - EXPECT_EQ(1LL, buckets2[0].mDuration); -} - -TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) { - /** - * The duration starts from the first bucket, through the two partial buckets (10-70sec), - * another bucket, and ends at the beginning of the next full bucket. - * Expected buckets: - * - [10,25]: 14 secs - * - [25,70]: All 45 secs - * - [70,130]: All 60 secs - * - [130, 210]: Only 5 secs (event ended at 135sec) - */ - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int tagId = 1; - - DurationMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_SUM); - sp wizard = new NaggyMock(); - FieldMatcher dimensions; - - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); - - int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - ASSERT_EQ(0UL, durationProducer.mPastBuckets.size()); - EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - switch (GetParam()) { - case APP_UPGRADE: - durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - ASSERT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - std::vector buckets = - durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); - EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketEndNs); - EXPECT_EQ(partialBucketSplitTimeNs - startTimeNs, buckets[0].mDuration); - EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(0, durationProducer.getCurrentBucketNum()); - - // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(3UL, buckets.size()); - EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - partialBucketSplitTimeNs, buckets[1].mDuration); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); - EXPECT_EQ(bucketSizeNs, buckets[2].mDuration); -} - -TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollowingBucket) { - /** - * Expected buckets (start at 11s, upgrade at 75s, end at 135s): - * - [10,70]: 59 secs - * - [70,75]: 5 sec - * - [75,130]: 55 secs - */ - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int tagId = 1; - - DurationMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_SUM); - sp wizard = new NaggyMock(); - FieldMatcher dimensions; - - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); - - int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - ASSERT_EQ(0UL, durationProducer.mPastBuckets.size()); - EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; - switch (GetParam()) { - case APP_UPGRADE: - durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - ASSERT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - std::vector buckets = - durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); - EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketEndNs); - EXPECT_EQ(partialBucketSplitTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); - EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, durationProducer.getCurrentBucketNum()); - - // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(3UL, buckets.size()); - EXPECT_EQ(partialBucketSplitTimeNs, buckets[2].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - partialBucketSplitTimeNs, - buckets[2].mDuration); -} - -TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) { - sp alarmMonitor; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int tagId = 1; - - // Setup metric with alert. - DurationMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_SUM); - Alert alert; - alert.set_num_buckets(3); - alert.set_trigger_if_sum_gt(2); - - sp wizard = new NaggyMock(); - FieldMatcher dimensions; - - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); - - sp anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); - EXPECT_TRUE(anomalyTracker != nullptr); - - int64_t startTimeNs = bucketStartTimeNs + 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - switch (GetParam()) { - case APP_UPGRADE: - durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - - // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, - anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -} - -TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int tagId = 1; - - DurationMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); - - sp wizard = new NaggyMock(); - FieldMatcher dimensions; - - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); - - int64_t startTimeNs = bucketStartTimeNs + 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - ASSERT_EQ(0UL, durationProducer.mPastBuckets.size()); - EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - switch (GetParam()) { - case APP_UPGRADE: - durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(0, durationProducer.getCurrentBucketNum()); - - // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - - durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); - std::vector buckets = - durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(1UL, buckets.size()); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs); - EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); -} - -TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int tagId = 1; - - DurationMetric metric; - metric.set_id(1); - metric.set_bucket(ONE_MINUTE); - metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); - - sp wizard = new NaggyMock(); - FieldMatcher dimensions; - - DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {}, - 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, - dimensions, bucketStartTimeNs, bucketStartTimeNs); - - int64_t startTimeNs = bucketStartTimeNs + 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - ASSERT_EQ(0UL, durationProducer.mPastBuckets.size()); - EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; - switch (GetParam()) { - case APP_UPGRADE: - durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, durationProducer.getCurrentBucketNum()); - - // Stop occurs in the same partial bucket as created for the app upgrade. - int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); - durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); - ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); - - durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); - std::vector buckets = - durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; - ASSERT_EQ(1UL, buckets.size()); - EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); - EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp deleted file mode 100644 index dfbb9da568b0..000000000000 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/metrics/EventMetricProducer.h" - -#include -#include -#include - -#include - -#include "metrics_test_helper.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -const ConfigKey kConfigKey(0, 12345); - -namespace { -void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeString(statsEvent, str.c_str()); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} -} // anonymous namespace - -TEST(EventMetricProducerTest, TestNoCondition) { - int64_t bucketStartTimeNs = 10000000000; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - - EventMetric metric; - metric.set_id(1); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 2); - - sp wizard = new NaggyMock(); - - EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, bucketStartTimeNs); - - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - - // Check dump report content. - ProtoOutputStream output; - std::set strSet; - eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, - true /*erase data*/, FAST, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_event_metrics()); - ASSERT_EQ(2, report.event_metrics().data_size()); - EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos()); - EXPECT_EQ(bucketStartTimeNs + 2, report.event_metrics().data(1).elapsed_timestamp_nanos()); -} - -TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { - int64_t bucketStartTimeNs = 10000000000; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - - EventMetric metric; - metric.set_id(1); - metric.set_condition(StringToId("SCREEN_ON")); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10); - - sp wizard = new NaggyMock(); - - EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, bucketStartTimeNs); - - eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs); - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); - - eventProducer.onConditionChanged(false /*condition*/, bucketStartTimeNs + 2); - - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - - // Check dump report content. - ProtoOutputStream output; - std::set strSet; - eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, - true /*erase data*/, FAST, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_event_metrics()); - ASSERT_EQ(1, report.event_metrics().data_size()); - EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos()); -} - -TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - - int tagId = 1; - int conditionTagId = 2; - - EventMetric metric; - metric.set_id(1); - metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); - MetricConditionLink* link = metric.add_links(); - link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); - buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what()); - buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition()); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1, "111"); - ConditionKey key1; - key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = { - getMockedDimensionKey(conditionTagId, 2, "111")}; - - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10, "222"); - ConditionKey key2; - key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = { - getMockedDimensionKey(conditionTagId, 2, "222")}; - - sp wizard = new NaggyMock(); - // Condition is false for first event. - EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); - // Condition is true for second event. - EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); - - EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, bucketStartTimeNs); - - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); - eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - - // Check dump report content. - ProtoOutputStream output; - std::set strSet; - eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, - true /*erase data*/, FAST, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_event_metrics()); - ASSERT_EQ(1, report.event_metrics().data_size()); - EXPECT_EQ(bucketStartTimeNs + 10, report.event_metrics().data(0).elapsed_timestamp_nanos()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp deleted file mode 100644 index 5997bedcdf2d..000000000000 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ /dev/null @@ -1,865 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/metrics/GaugeMetricProducer.h" - -#include -#include -#include -#include - -#include - -#include "logd/LogEvent.h" -#include "metrics_test_helper.h" -#include "src/matchers/SimpleLogMatchingTracker.h" -#include "src/metrics/MetricProducer.h" -#include "src/stats_log_util.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; -using std::make_shared; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -namespace { - -const ConfigKey kConfigKey(0, 12345); -const int tagId = 1; -const int64_t metricId = 123; -const int64_t atomMatcherId = 678; -const int logEventMatcherIndex = 0; -const int64_t bucketStartTimeNs = 10 * NS_PER_SEC; -const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; -const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; -const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; -const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - -shared_ptr makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1, - int32_t value2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, value1); - AStatsEvent_writeString(statsEvent, str1.c_str()); - AStatsEvent_writeInt32(statsEvent, value2); - - shared_ptr logEvent = std::make_shared(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} -} // anonymous namespace - -// Setup for parameterized tests. -class GaugeMetricProducerTest_PartialBucket : public TestWithParam {}; - -INSTANTIATE_TEST_SUITE_P(GaugeMetricProducerTest_PartialBucket, - GaugeMetricProducerTest_PartialBucket, - testing::Values(APP_UPGRADE, BOOT_COMPLETE)); - -/* - * Tests that the first bucket works correctly - */ -TEST(GaugeMetricProducerTest, TestFirstBucket) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.mutable_gauge_fields_filter()->set_include_all(false); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(1); - gaugeFieldMatcher->add_child()->set_field(3); - - sp wizard = new NaggyMock(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = new EventMatcherWizard({ - new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - - sp pullerManager = new StrictMock(); - - // statsd started long ago. - // The metric starts in the middle of the bucket - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, -1, -1, - tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); - gaugeProducer.prepareFirstBucket(); - - EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum); - EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs()); -} - -TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.mutable_gauge_fields_filter()->set_include_all(false); - metric.set_max_pull_delay_sec(INT_MAX); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(1); - gaugeFieldMatcher->add_child()->set_field(3); - - sp wizard = new NaggyMock(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11)); - return true; - })); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - - vector> allData; - allData.clear(); - allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11)); - - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(10, it->mValue.int_value); - it++; - EXPECT_EQ(11, it->mValue.int_value); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin() - ->second.back() - .mGaugeAtoms.front() - .mFields->begin() - ->mValue.int_value); - - allData.clear(); - allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(24, it->mValue.int_value); - it++; - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(25, it->mValue.int_value); - // One dimension. - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); - it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(10L, it->mValue.int_value); - it++; - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(11L, it->mValue.int_value); - - gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs); - ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); - // One dimension. - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - ASSERT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size()); - it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(24L, it->mValue.int_value); - it++; - EXPECT_EQ(INT, it->mValue.getType()); - EXPECT_EQ(25L, it->mValue.int_value); -} - -TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) { - sp alarmMonitor; - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.mutable_gauge_fields_filter()->set_include_all(true); - - Alert alert; - alert.set_id(101); - alert.set_metric_id(metricId); - alert.set_trigger_if_sum_gt(25); - alert.set_num_buckets(100); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, - -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - - sp anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); - EXPECT_TRUE(anomalyTracker != nullptr); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); - - switch (GetParam()) { - case APP_UPGRADE: - gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(partialBucketSplitTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); - EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); - // Partial buckets are not sent to anomaly tracker. - EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - - // Create an event in the same partial bucket. - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(partialBucketSplitTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ((int64_t)partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); - // Partial buckets are not sent to anomaly tracker. - EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - - // Next event should trigger creation of new bucket and send previous full bucket to anomaly - // tracker. - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum); - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - - // Next event should trigger creation of new bucket. - LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 125 * NS_PER_SEC, 1, 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum); - ASSERT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -} - -TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_max_pull_delay_sec(INT_MAX); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(2); - - sp wizard = new NaggyMock(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - .WillOnce(Return(false)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2)); - return true; - })); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - - switch (GetParam()) { - case APP_UPGRADE: - gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(partialBucketSplitTimeNs, - gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); - EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); -} - -TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_max_pull_delay_sec(INT_MAX); - metric.set_split_bucket_for_app_upgrade(false); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(2); - - sp wizard = new NaggyMock(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Return(false)); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - - gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - ASSERT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); - EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); -} - -TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_max_pull_delay_sec(INT_MAX); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(2); - metric.set_condition(StringToId("SCREEN_ON")); - - sp wizard = new NaggyMock(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - - int64_t conditionChangeNs = bucketStartTimeNs + 8; - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100)); - return true; - })); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - - gaugeProducer.onConditionChanged(true, conditionChangeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size()); - - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin() - ->second.back() - .mGaugeAtoms.front() - .mFields->begin() - ->mValue.int_value); - - gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10); - gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin() - ->second.back() - .mGaugeAtoms.front() - .mFields->begin() - ->mValue.int_value); -} - -TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { - const int conditionTag = 65; - GaugeMetric metric; - metric.set_id(1111111); - metric.set_bucket(ONE_MINUTE); - metric.mutable_gauge_fields_filter()->set_include_all(true); - metric.set_condition(StringToId("APP_DIED")); - metric.set_max_pull_delay_sec(INT_MAX); - auto dim = metric.mutable_dimensions_in_what(); - dim->set_field(tagId); - dim->add_child()->set_field(1); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - - sp wizard = new NaggyMock(); - EXPECT_CALL(*wizard, query(_, _, _)) - .WillRepeatedly( - Invoke([](const int conditionIndex, const ConditionKey& conditionParameters, - const bool isPartialLink) { - int pos[] = {1, 0, 0}; - Field f(conditionTag, pos, 0); - HashableDimensionKey key; - key.mutableValues()->emplace_back(f, Value((int32_t)1000000)); - - return ConditionState::kTrue; - })); - - int64_t sliceConditionChangeNs = bucketStartTimeNs + 8; - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100)); - return true; - })); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/, - {ConditionState::kUnknown}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - - gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs); - - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first; - ASSERT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - - ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size()); - - vector> allData; - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -} - -TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { - sp alarmMonitor; - sp wizard = new NaggyMock(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Return(false)); - - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_max_pull_delay_sec(INT_MAX); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(2); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, - tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); - - Alert alert; - alert.set_id(101); - alert.set_metric_id(metricId); - alert.set_trigger_if_sum_gt(25); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 60; - alert.set_refractory_period_secs(refPeriodSec); - sp anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); - - int tagId = 1; - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); - - std::shared_ptr event2 = - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 20, 15); - - allData.clear(); - allData.push_back(event2); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec); - - allData.clear(); - allData.push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() - ->second.front() - .mFields->begin() - ->mValue.int_value); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); - - // This event does not have the gauge field. Thus the current bucket value is 0. - allData.clear(); - allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10)); - gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty()); -} - -TEST(GaugeMetricProducerTest, TestPullOnTrigger) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); - metric.mutable_gauge_fields_filter()->set_include_all(false); - metric.set_max_pull_delay_sec(INT_MAX); - auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); - gaugeFieldMatcher->set_field(tagId); - gaugeFieldMatcher->add_child()->set_field(1); - - sp wizard = new NaggyMock(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5)); - return true; - })) - .WillOnce(Return(true)); - - int triggerId = 5; - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, - triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); - - LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); - triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); - triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - - ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size()); - EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin() - ->second.back() - .mGaugeAtoms[0] - .mFields->begin() - ->mValue.int_value); - EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin() - ->second.back() - .mGaugeAtoms[1] - .mFields->begin() - ->mValue.int_value); -} - -TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); - metric.mutable_gauge_fields_filter()->set_include_all(true); - metric.set_max_pull_delay_sec(INT_MAX); - auto dimensionMatcher = metric.mutable_dimensions_in_what(); - // use field 1 as dimension. - dimensionMatcher->set_field(tagId); - dimensionMatcher->add_child()->set_field(1); - - sp wizard = new NaggyMock(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6)); - return true; - })) - .WillOnce(Return(true)); - - int triggerId = 5; - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, - triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 10); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size()); - ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); - triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); - triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - - ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.size()); - auto bucketIt = gaugeProducer.mPastBuckets.begin(); - ASSERT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size()); - EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); - EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); - bucketIt++; - ASSERT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size()); - EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); - EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); - EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value); -} - -/* - * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size - * is smaller than the "min_bucket_size_nanos" specified in the metric config. - */ -TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { - GaugeMetric metric; - metric.set_id(metricId); - metric.set_bucket(FIVE_MINUTES); - metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); - metric.set_min_bucket_size_nanos(10000000000); // 10 seconds - - sp wizard = new NaggyMock(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _, _)) - // Bucket start. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10)); - return true; - })); - - int triggerId = 5; - GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, - triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - gaugeProducer.prepareFirstBucket(); - - LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3); - gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, true, - FAST /* dump_latency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_gauge_metrics()); - ASSERT_EQ(0, report.gauge_metrics().data_size()); - ASSERT_EQ(1, report.gauge_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.gauge_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), - report.gauge_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp deleted file mode 100644 index fda3daaa56aa..000000000000 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/metrics/duration_helper/MaxDurationTracker.h" -#include "src/condition/ConditionWizard.h" -#include "metrics_test_helper.h" -#include "tests/statsd_test_util.h" - -#include -#include -#include -#include -#include -#include - -using namespace android::os::statsd; -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -const ConfigKey kConfigKey(0, 12345); - -const int TagId = 1; - -const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1"); -const HashableDimensionKey conditionKey = getMockedDimensionKey(TagId, 4, "1"); -const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); -const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); -const int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - -TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); - const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - - - sp wizard = new NaggyMock(); - - unordered_map> buckets; - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - - int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey()); - // Event starts again. This would not change anything as it already starts. - tracker.noteStart(key1, true, bucketStartTimeNs + 3, ConditionKey()); - // Stopped. - tracker.noteStop(key1, bucketStartTimeNs + 10, false); - - // Another event starts in this bucket. - tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey()); - tracker.noteStop(key2, bucketStartTimeNs + 40, false /*stop all*/); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(20LL, buckets[eventKey][0].mDuration); -} - -TEST(MaxDurationTrackerTest, TestStopAll) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); - const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - - sp wizard = new NaggyMock(); - - unordered_map> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - - int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey()); - - // Another event starts in this bucket. - tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey()); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets); - tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40); - EXPECT_TRUE(tracker.mInfos.empty()); - EXPECT_TRUE(buckets.find(eventKey) == buckets.end()); - - tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(bucketSizeNs + 40 - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[eventKey][0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs); -} - -TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); - const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - sp wizard = new NaggyMock(); - - unordered_map> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - - int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - // The event starts. - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); - - // Starts again. Does not DEFAULT_DIMENSION_KEY anything. - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1, - ConditionKey()); - - // The event stops at early 4th bucket. - // Notestop is called from DurationMetricProducer's onMatchedLogEvent, which calls - // flushIfneeded. - tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets); - tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (3 * bucketSizeNs) + 20, - false /*stop all*/); - EXPECT_TRUE(buckets.find(eventKey) == buckets.end()); - - tracker.flushIfNeeded(bucketStartTimeNs + 4 * bucketSizeNs, &buckets); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ((3 * bucketSizeNs) + 20 - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[eventKey][0].mBucketStartNs); - EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs); -} - -TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); - const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - sp wizard = new NaggyMock(); - - unordered_map> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - - int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - // 2 starts - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 10, ConditionKey()); - // one stop - tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 20, false /*stop all*/); - - tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1, &buckets); - // Because of nesting, still not stopped. - EXPECT_TRUE(buckets.find(eventKey) == buckets.end()); - - // real stop now. - tracker.noteStop(DEFAULT_DIMENSION_KEY, - bucketStartTimeNs + (2 * bucketSizeNs) + 5, false); - tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets); - - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(2 * bucketSizeNs + 5 - 1, buckets[eventKey][0].mDuration); -} - -TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { - const HashableDimensionKey conditionDimKey = key1; - - sp wizard = new NaggyMock(); - - ConditionKey conditionKey1; - MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 1, "1"); - conditionKey1[StringToId("APP_BACKGROUND")] = conditionDimKey; - - /** - Start in first bucket, stop in second bucket. Condition turns on and off in the first bucket - and again turns on and off in the second bucket. - */ - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t eventStartTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; - int64_t conditionStarts1 = bucketStartTimeNs + 11 * NS_PER_SEC; - int64_t conditionStops1 = bucketStartTimeNs + 14 * NS_PER_SEC; - int64_t conditionStarts2 = bucketStartTimeNs + bucketSizeNs + 5 * NS_PER_SEC; - int64_t conditionStops2 = conditionStarts2 + 10 * NS_PER_SEC; - int64_t eventStopTimeNs = conditionStops2 + 8 * NS_PER_SEC; - - int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, - 0, bucketStartTimeNs, bucketSizeNs, true, false, {}); - EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); - - tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1); - tracker.noteConditionChanged(key1, true, conditionStarts1); - tracker.noteConditionChanged(key1, false, conditionStops1); - unordered_map> buckets; - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - ASSERT_EQ(0U, buckets.size()); - - tracker.noteConditionChanged(key1, true, conditionStarts2); - tracker.noteConditionChanged(key1, false, conditionStops2); - tracker.noteStop(key1, eventStopTimeNs, false); - tracker.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1, &buckets); - ASSERT_EQ(1U, buckets.size()); - vector item = buckets.begin()->second; - ASSERT_EQ(1UL, item.size()); - EXPECT_EQ((int64_t)(13LL * NS_PER_SEC), item[0].mDuration); -} - -TEST(MaxDurationTrackerTest, TestAnomalyDetection) { - sp wizard = new NaggyMock(); - - ConditionKey conditionKey1; - MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); - conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; - - unordered_map> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = 13000000000; - int64_t durationTimeNs = 2 * 1000; - - int64_t metricId = 1; - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 45; - alert.set_refractory_period_secs(refPeriodSec); - sp alarmMonitor; - sp anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, - {anomalyTracker}); - - tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1); - sp alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(53ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); - - // Remove the anomaly alarm when the duration is no longer fully met. - tracker.noteConditionChanged(key1, false, eventStartTimeNs + 15 * NS_PER_SEC); - ASSERT_EQ(0U, anomalyTracker->mAlarms.size()); - - // Since the condition was off for 10 seconds, the anomaly should trigger 10 sec later. - tracker.noteConditionChanged(key1, true, eventStartTimeNs + 25 * NS_PER_SEC); - ASSERT_EQ(1U, anomalyTracker->mAlarms.size()); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(63ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); -} - -// This tests that we correctly compute the predicted time of an anomaly assuming that the current -// state continues forward as-is. -TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) { - sp wizard = new NaggyMock(); - - ConditionKey conditionKey1; - MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); - conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; - ConditionKey conditionKey2; - conditionKey2[StringToId("APP_BACKGROUND")] = getMockedDimensionKey(TagId, 4, "2"); - - unordered_map> buckets; - - /** - * Suppose we have two sub-dimensions that we're taking the MAX over. In the first of these - * nested dimensions, we enter the pause state after 3 seconds. When we resume, the second - * dimension has already been running for 4 seconds. Thus, we have 40-4=36 seconds remaining - * before we trigger the anomaly. - */ - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC; // Condition is off at start. - int64_t conditionStarts1 = bucketStartTimeNs + 11 * NS_PER_SEC; - int64_t conditionStops1 = bucketStartTimeNs + 14 * NS_PER_SEC; - int64_t conditionStarts2 = bucketStartTimeNs + 20 * NS_PER_SEC; - int64_t eventStartTimeNs2 = conditionStarts2 - 4 * NS_PER_SEC; - - int64_t metricId = 1; - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 45; - alert.set_refractory_period_secs(refPeriodSec); - sp alarmMonitor; - sp anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, - {anomalyTracker}); - - tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1); - tracker.noteConditionChanged(key1, true, conditionStarts1); - tracker.noteConditionChanged(key1, false, conditionStops1); - tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2); // Condition is on already. - tracker.noteConditionChanged(key1, true, conditionStarts2); - ASSERT_EQ(1U, anomalyTracker->mAlarms.size()); - auto alarm = anomalyTracker->mAlarms.begin()->second; - int64_t anomalyFireTimeSec = alarm->timestampSec; - EXPECT_EQ(conditionStarts2 + 36 * NS_PER_SEC, - (long long)anomalyFireTimeSec * NS_PER_SEC); - - // Now we test the calculation now that there's a refractory period. - // At the correct time, declare the anomaly. This will set a refractory period. Make sure it - // gets correctly taken into account in future predictAnomalyTimestampNs calculations. - std::unordered_set, SpHash> firedAlarms({alarm}); - anomalyTracker->informAlarmsFired(anomalyFireTimeSec * NS_PER_SEC, firedAlarms); - ASSERT_EQ(0u, anomalyTracker->mAlarms.size()); - int64_t refractoryPeriodEndsSec = anomalyFireTimeSec + refPeriodSec; - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), refractoryPeriodEndsSec); - - // Now stop and start again. Make sure the new predictAnomalyTimestampNs takes into account - // the refractory period correctly. - int64_t eventStopTimeNs = anomalyFireTimeSec * NS_PER_SEC + 10; - tracker.noteStop(key1, eventStopTimeNs, false); - tracker.noteStop(key2, eventStopTimeNs, false); - tracker.noteStart(key1, true, eventStopTimeNs + 1000000, conditionKey1); - // Anomaly is ongoing, but we're still in the refractory period. - ASSERT_EQ(1U, anomalyTracker->mAlarms.size()); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ(refractoryPeriodEndsSec, (long long)(alarm->timestampSec)); - - // Makes sure it is correct after the refractory period is over. - tracker.noteStop(key1, eventStopTimeNs + 2000000, false); - int64_t justBeforeRefPeriodNs = (refractoryPeriodEndsSec - 2) * NS_PER_SEC; - tracker.noteStart(key1, true, justBeforeRefPeriodNs, conditionKey1); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ(justBeforeRefPeriodNs + 40 * NS_PER_SEC, - (unsigned long long)(alarm->timestampSec * NS_PER_SEC)); -} - -// Suppose that within one tracker there are two dimensions A and B. -// Suppose A starts, then B starts, and then A stops. We still need to set an anomaly based on the -// elapsed duration of B. -TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) { - sp wizard = new NaggyMock(); - - ConditionKey conditionKey1; - MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); - conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; - ConditionKey conditionKey2; - conditionKey2[StringToId("APP_BACKGROUND")] = getMockedDimensionKey(TagId, 4, "2"); - - unordered_map> buckets; - - /** - * Suppose we have two sub-dimensions that we're taking the MAX over. In the first of these - * nested dimensions, are started for 8 seconds. When we stop, the other nested dimension has - * been started for 5 seconds. So we can only allow 35 more seconds from now. - */ - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucketNum = 0; - int64_t eventStartTimeNs1 = bucketStartTimeNs + 5 * NS_PER_SEC; // Condition is off at start. - int64_t eventStopTimeNs1 = bucketStartTimeNs + 13 * NS_PER_SEC; - int64_t eventStartTimeNs2 = bucketStartTimeNs + 8 * NS_PER_SEC; - - int64_t metricId = 1; - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 45; - alert.set_refractory_period_secs(refPeriodSec); - sp alarmMonitor; - sp anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, - {anomalyTracker}); - - tracker.noteStart(key1, true, eventStartTimeNs1, conditionKey1); - tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2); - tracker.noteStop(key1, eventStopTimeNs1, false); - ASSERT_EQ(1U, anomalyTracker->mAlarms.size()); - auto alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ(eventStopTimeNs1 + 35 * NS_PER_SEC, - (unsigned long long)(alarm->timestampSec * NS_PER_SEC)); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp deleted file mode 100644 index 1d6f7de5e7e3..000000000000 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ /dev/null @@ -1,576 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/metrics/duration_helper/OringDurationTracker.h" -#include "src/condition/ConditionWizard.h" -#include "metrics_test_helper.h" -#include "tests/statsd_test_util.h" - -#include -#include -#include -#include -#include -#include -#include - -using namespace testing; -using android::sp; -using std::set; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ -namespace android { -namespace os { -namespace statsd { - -const ConfigKey kConfigKey(0, 12345); -const int TagId = 1; -const int64_t metricId = 123; -const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event"); - -const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(TagId, 1, "maps"); -const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); -const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); -const int64_t bucketSizeNs = 30 * NS_PER_SEC; - -TEST(OringDurationTrackerTest, TestDurationOverlap) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp wizard = new NaggyMock(); - - unordered_map> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t durationTimeNs = 2 * 1000; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - false, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); - EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl - EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); - - tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false); - tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(durationTimeNs, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestDurationNested) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp wizard = new NaggyMock(); - - unordered_map> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl - - tracker.noteStop(kEventKey1, eventStartTimeNs + 2000, false); - tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(2003LL, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestStopAll) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const std::vector kConditionKey1 = - {getMockedDimensionKey(TagId, 1, "maps")}; - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp wizard = new NaggyMock(); - - unordered_map> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); - tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl - - tracker.noteStopAll(eventStartTimeNs + 2003); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(2003LL, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp wizard = new NaggyMock(); - - unordered_map> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t durationTimeNs = 2 * 1000; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); - EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, ConditionKey()); - EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime); - - ASSERT_EQ(2u, buckets[eventKey].size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); - - tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 10, false); - tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 12, false); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(2u, buckets[eventKey].size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); -} - -TEST(OringDurationTrackerTest, TestDurationConditionChange) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp wizard = new NaggyMock(); - - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - - EXPECT_CALL(*wizard, query(_, key1, _)) // #4 - .WillOnce(Return(ConditionState::kFalse)); - - unordered_map> buckets; - - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t durationTimeNs = 2 * 1000; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - true, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); - - tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 5); - - tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(5LL, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestDurationConditionChange2) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp wizard = new NaggyMock(); - - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - - EXPECT_CALL(*wizard, query(_, key1, _)) - .Times(2) - .WillOnce(Return(ConditionState::kFalse)) - .WillOnce(Return(ConditionState::kTrue)); - - unordered_map> buckets; - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - int64_t durationTimeNs = 2 * 1000; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - true, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); - // condition to false; record duration 5n - tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 5); - // condition to true. - tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 1000); - // 2nd duration: 1000ns - tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(1005LL, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - sp wizard = new NaggyMock(); - - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - - EXPECT_CALL(*wizard, query(_, key1, _)) // #4 - .WillOnce(Return(ConditionState::kFalse)); - - unordered_map> buckets; - - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + 1; - - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1); - - tracker.noteStop(kEventKey1, eventStartTimeNs + 3, false); - - tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 15); - - tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false); - - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(15LL, buckets[eventKey][0].mDuration); -} - -TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - alert.set_refractory_period_secs(1); - - unordered_map> buckets; - sp wizard = new NaggyMock(); - - int64_t bucketStartTimeNs = 10 * NS_PER_SEC; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - - sp alarmMonitor; - sp anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, - {anomalyTracker}); - - // Nothing in the past bucket. - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); - EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs), - tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs)); - - tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 3, false); - ASSERT_EQ(0u, buckets[eventKey].size()); - - int64_t event1StartTimeNs = eventStartTimeNs + 10; - tracker.noteStart(kEventKey1, true, event1StartTimeNs, ConditionKey()); - // No past buckets. The anomaly will happen in bucket #0. - EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3), - tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs)); - - int64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10; - tracker.flushIfNeeded(event1StopTimeNs, &buckets); - tracker.noteStop(kEventKey1, event1StopTimeNs, false); - - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - ASSERT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(3LL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10, - buckets[eventKey][0].mDuration); - - const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10; - const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs; - - // One past buckets. The anomaly will happen in bucket #1. - int64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15; - tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey()); - EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration - - bucket1Duration), - tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs)); - tracker.noteStop(kEventKey1, event2StartTimeNs + 1, false); - - // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in - // bucket #2. - int64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC; - tracker.noteStart(kEventKey1, true, event3StartTimeNs, ConditionKey()); - EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL), - tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs)); -} - -TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp2) { - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(5 * NS_PER_SEC); - alert.set_num_buckets(1); - alert.set_refractory_period_secs(20); - - int64_t bucketStartTimeNs = 10 * NS_PER_SEC; - int64_t bucketNum = 0; - - sp wizard = new NaggyMock(); - sp alarmMonitor; - sp anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard, 1, - - true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, true, false, {anomalyTracker}); - - int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC; - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); - // Anomaly happens in the bucket #1. - EXPECT_EQ((long long)(bucketStartTimeNs + 14 * NS_PER_SEC), - tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs)); - - tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 14 * NS_PER_SEC, false); - - EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY)); - - int64_t event2StartTimeNs = bucketStartTimeNs + 22 * NS_PER_SEC; - EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC, - anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY)); - EXPECT_EQ((long long)(bucketStartTimeNs + 35 * NS_PER_SEC), - tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs)); -} - -TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp3) { - // Test the cases where the refractory period is smaller than the bucket size, longer than - // the bucket size, and longer than 2x of the anomaly detection window. - for (int j = 0; j < 3; j++) { - int64_t thresholdNs = j * bucketSizeNs + 5 * NS_PER_SEC; - for (int i = 0; i <= 7; ++i) { - - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(thresholdNs); - alert.set_num_buckets(3); - alert.set_refractory_period_secs( - bucketSizeNs / NS_PER_SEC / 2 + i * bucketSizeNs / NS_PER_SEC); - - int64_t bucketStartTimeNs = 10 * NS_PER_SEC; - int64_t bucketNum = 101; - - sp wizard = new NaggyMock(); - sp alarmMonitor; - sp anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard, - 1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, true, false, {anomalyTracker}); - - int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC; - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); - EXPECT_EQ((long long)(eventStartTimeNs + thresholdNs), - tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs)); - int64_t eventStopTimeNs = eventStartTimeNs + thresholdNs + NS_PER_SEC; - tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStopTimeNs, false); - - int64_t refractoryPeriodEndSec = - anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY); - EXPECT_EQ(eventStopTimeNs / (int64_t)NS_PER_SEC + alert.refractory_period_secs(), - refractoryPeriodEndSec); - - // Acquire and release a wakelock in the next bucket. - int64_t event2StartTimeNs = eventStopTimeNs + bucketSizeNs; - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, event2StartTimeNs, ConditionKey()); - int64_t event2StopTimeNs = event2StartTimeNs + 4 * NS_PER_SEC; - tracker.noteStop(DEFAULT_DIMENSION_KEY, event2StopTimeNs, false); - - // Test the alarm prediction works well when seeing another wakelock start event. - for (int k = 0; k <= 2; ++k) { - int64_t event3StartTimeNs = event2StopTimeNs + NS_PER_SEC + k * bucketSizeNs; - int64_t alarmTimestampNs = - tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs); - EXPECT_GT(alarmTimestampNs, 0u); - EXPECT_GE(alarmTimestampNs, event3StartTimeNs); - EXPECT_GE(alarmTimestampNs, refractoryPeriodEndSec *(int64_t) NS_PER_SEC); - } - } - } -} - -TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 45; - alert.set_refractory_period_secs(refPeriodSec); - - unordered_map> buckets; - sp wizard = new NaggyMock(); - - int64_t bucketStartTimeNs = 10 * NS_PER_SEC; - int64_t bucketNum = 0; - int64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - - sp alarmMonitor; - sp anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/, - bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - false, false, {anomalyTracker}); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); - tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - EXPECT_TRUE(tracker.mStarted.empty()); - EXPECT_EQ(10LL, tracker.mStateKeyDurationMap[DEFAULT_DIMENSION_KEY].mDuration); // 10ns - - ASSERT_EQ(0u, tracker.mStarted.size()); - - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey()); - ASSERT_EQ(1u, anomalyTracker->mAlarms.size()); - EXPECT_EQ((long long)(52ULL * NS_PER_SEC), // (10s + 1s + 1ns + 20ns) - 10ns + 40s, rounded up - (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC)); - // The alarm is set to fire at 52s, and when it does, an anomaly would be declared. However, - // because this is a unit test, the alarm won't actually fire at all. Since the alarm fails - // to fire in time, the anomaly is instead caught when noteStop is called, at around 71s. - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets); - tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 25, false); - EXPECT_EQ(anomalyTracker->getSumOverPastBuckets(eventKey), (long long)(bucketSizeNs)); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), - std::ceil((eventStartTimeNs + 2 * bucketSizeNs + 25.0) / NS_PER_SEC + refPeriodSec)); -} - -TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); - - const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); - const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - Alert alert; - alert.set_id(101); - alert.set_metric_id(1); - alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 45; - alert.set_refractory_period_secs(refPeriodSec); - - unordered_map> buckets; - sp wizard = new NaggyMock(); - ConditionKey conkey; - conkey[StringToId("APP_BACKGROUND")] = kConditionKey1; - int64_t bucketStartTimeNs = 10 * NS_PER_SEC; - int64_t bucketSizeNs = 30 * NS_PER_SEC; - - sp alarmMonitor; - sp anomalyTracker = - new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/, - bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, false, - false, {anomalyTracker}); - - tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1 - ASSERT_EQ(1u, anomalyTracker->mAlarms.size()); - sp alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - - tracker.noteStop(kEventKey1, 17 * NS_PER_SEC, false); // stop key1 (2 seconds later) - ASSERT_EQ(0u, anomalyTracker->mAlarms.size()); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - - tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again - ASSERT_EQ(1u, anomalyTracker->mAlarms.size()); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - - tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2 - ASSERT_EQ(1u, anomalyTracker->mAlarms.size()); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - - tracker.noteStop(kEventKey1, 47 * NS_PER_SEC, false); // stop key1 - ASSERT_EQ(1u, anomalyTracker->mAlarms.size()); - alarm = anomalyTracker->mAlarms.begin()->second; - EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - - // Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time. - std::unordered_set, SpHash> firedAlarms({alarm}); - anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms); - ASSERT_EQ(0u, anomalyTracker->mAlarms.size()); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec); - - tracker.noteStop(kEventKey2, 69 * NS_PER_SEC, false); // stop key2 - ASSERT_EQ(0u, anomalyTracker->mAlarms.size()); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp deleted file mode 100644 index 5666501d7d51..000000000000 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ /dev/null @@ -1,5061 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/metrics/ValueMetricProducer.h" - -#include -#include -#include -#include - -#include - -#include "metrics_test_helper.h" -#include "src/matchers/SimpleLogMatchingTracker.h" -#include "src/metrics/MetricProducer.h" -#include "src/stats_log_util.h" -#include "tests/statsd_test_util.h" - -using namespace testing; -using android::sp; -using std::make_shared; -using std::set; -using std::shared_ptr; -using std::unordered_map; -using std::vector; - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -namespace { - -const ConfigKey kConfigKey(0, 12345); -const int tagId = 1; -const int64_t metricId = 123; -const int64_t atomMatcherId = 678; -const int logEventMatcherIndex = 0; -const int64_t bucketStartTimeNs = 10000000000; -const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; -const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; -const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; -const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs; -const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs; -double epsilon = 0.001; - -static void assertPastBucketValuesSingleKey( - const std::unordered_map>& mPastBuckets, - const std::initializer_list& expectedValuesList, - const std::initializer_list& expectedDurationNsList, - const std::initializer_list& expectedStartTimeNsList, - const std::initializer_list& expectedEndTimeNsList) { - vector expectedValues(expectedValuesList); - vector expectedDurationNs(expectedDurationNsList); - vector expectedStartTimeNs(expectedStartTimeNsList); - vector expectedEndTimeNs(expectedEndTimeNsList); - - ASSERT_EQ(expectedValues.size(), expectedDurationNs.size()); - ASSERT_EQ(expectedValues.size(), expectedStartTimeNs.size()); - ASSERT_EQ(expectedValues.size(), expectedEndTimeNs.size()); - - if (expectedValues.size() == 0) { - ASSERT_EQ(0, mPastBuckets.size()); - return; - } - - ASSERT_EQ(1, mPastBuckets.size()); - ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size()); - - const vector& buckets = mPastBuckets.begin()->second; - for (int i = 0; i < expectedValues.size(); i++) { - EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value) - << "Values differ at index " << i; - EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs) - << "Condition duration value differ at index " << i; - EXPECT_EQ(expectedStartTimeNs[i], buckets[i].mBucketStartNs) - << "Start time differs at index " << i; - EXPECT_EQ(expectedEndTimeNs[i], buckets[i].mBucketEndNs) - << "End time differs at index " << i; - } -} - -} // anonymous namespace - -class ValueMetricProducerTestHelper { -public: - static sp createValueProducerNoConditions( - sp& pullerManager, ValueMetric& metric) { - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)) - .WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)) - .WillRepeatedly(Return()); - - sp valueProducer = - new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); - return valueProducer; - } - - static sp createValueProducerWithCondition( - sp& pullerManager, ValueMetric& metric, - ConditionState conditionAfterFirstBucketPrepared) { - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)) - .WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)) - .WillRepeatedly(Return()); - - sp valueProducer = new ValueMetricProducer( - kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); - valueProducer->mCondition = conditionAfterFirstBucketPrepared; - return valueProducer; - } - - static sp createValueProducerWithState( - sp& pullerManager, ValueMetric& metric, - vector slicedStateAtoms, - unordered_map> stateGroupMap) { - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)) - .WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)) - .WillRepeatedly(Return()); - - sp valueProducer = new ValueMetricProducer( - kConfigKey, metric, -1 /* no condition */, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, - {}, slicedStateAtoms, stateGroupMap); - valueProducer->prepareFirstBucket(); - return valueProducer; - } - - static sp createValueProducerWithConditionAndState( - sp& pullerManager, ValueMetric& metric, - vector slicedStateAtoms, - unordered_map> stateGroupMap, - ConditionState conditionAfterFirstBucketPrepared) { - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)) - .WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)) - .WillRepeatedly(Return()); - - sp valueProducer = new ValueMetricProducer( - kConfigKey, metric, 0 /* condition tracker index */, {ConditionState::kUnknown}, - wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap); - valueProducer->prepareFirstBucket(); - valueProducer->mCondition = conditionAfterFirstBucketPrepared; - return valueProducer; - } - - static ValueMetric createMetric() { - ValueMetric metric; - metric.set_id(metricId); - metric.set_bucket(ONE_MINUTE); - metric.mutable_value_field()->set_field(tagId); - metric.mutable_value_field()->add_child()->set_field(2); - metric.set_max_pull_delay_sec(INT_MAX); - return metric; - } - - static ValueMetric createMetricWithCondition() { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_condition(StringToId("SCREEN_ON")); - return metric; - } - - static ValueMetric createMetricWithState(string state) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.add_slice_by_state(StringToId(state)); - return metric; - } - - static ValueMetric createMetricWithConditionAndState(string state) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_condition(StringToId("SCREEN_ON")); - metric.add_slice_by_state(StringToId(state)); - return metric; - } -}; - -// Setup for parameterized tests. -class ValueMetricProducerTest_PartialBucket : public TestWithParam {}; - -INSTANTIATE_TEST_SUITE_P(ValueMetricProducerTest_PartialBucket, - ValueMetricProducerTest_PartialBucket, - testing::Values(APP_UPGRADE, BOOT_COMPLETE)); - -/* - * Tests that the first bucket works correctly - */ -TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - int64_t startTimeBase = 11; - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - // statsd started long ago. - // The metric starts in the middle of the bucket - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, -1, - startTimeBase, 22, pullerManager); - valueProducer.prepareFirstBucket(); - - EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10)); - EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10)); - EXPECT_EQ(60 * NS_PER_SEC + startTimeBase, - valueProducer.calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC)); - EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase, - valueProducer.calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC)); -} - -/* - * Tests that the first bucket works correctly - */ -TEST(ValueMetricProducerTest, TestFirstBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - // statsd started long ago. - // The metric starts in the middle of the bucket - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, -1, 5, - 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); - valueProducer.prepareFirstBucket(); - - EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(10, valueProducer.mCurrentBucketNum); - EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs()); -} - -/* - * Tests pulled atoms with no conditions - */ -TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(8, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(23, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(12, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(36, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(13, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs); - EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - sp pullerManager = new StrictMock(); - int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2; - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Initialize bucket. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); - return true; - })) - // Partial bucket. - .WillOnce(Invoke([partialBucketSplitTimeNs]( - int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs + 8, 5)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - // First bucket ends. - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 2)); - valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs); - - // Partial buckets created in 2nd bucket. - switch (GetParam()) { - case APP_UPGRADE: - valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs); - EXPECT_EQ(1, valueProducer->getCurrentBucketNum()); - - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1, 3}, - {bucketSizeNs, partialBucketSplitTimeNs - bucket2StartTimeNs}, - {bucketStartTimeNs, bucket2StartTimeNs}, - {bucket2StartTimeNs, partialBucketSplitTimeNs}); -} - -/* - * Tests pulled atoms with filtering - */ -TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - auto keyValue = atomMatcher.add_field_value_matcher(); - keyValue->set_field(1); - keyValue->set_eq_int(3); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3)); - return true; - })); - - sp valueProducer = new ValueMetricProducer( - kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); - - vector> allData; - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 3, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(8, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket3StartTimeNs + 1, 4, 23)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // No new data seen, so data has been cleared. - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(8, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - - // the base was reset - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(36, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size()); - EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs); -} - -/* - * Tests pulled atoms with no conditions and take absolute value after reset - */ -TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_use_absolute_value_on_reset(true); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Return(true)); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(10, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(10, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(36, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(26, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size()); - EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); - EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs); -} - -/* - * Tests pulled atoms with no conditions and take zero value after reset - */ -TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Return(false)); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(10, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(36, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(26, curInterval.value.long_value); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); -} - -/* - * Test pulled event with non sliced condition. - */ -TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 1); // Third condition change. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - // startUpdated:false sum:0 start:100 - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(110, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(10, curInterval.value.long_value); - - valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(20, curInterval.value.long_value); - EXPECT_EQ(false, curBaseInfo.hasBase); - - valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1}, - {bucketStartTimeNs, bucket2StartTimeNs}, - {bucket2StartTimeNs, bucket3StartTimeNs}); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150; - switch (GetParam()) { - case APP_UPGRADE: - valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, - {partialBucketSplitTimeNs - bucketStartTimeNs}, - {bucketStartTimeNs}, {partialBucketSplitTimeNs}); - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(0, valueProducer.getCurrentBucketNum()); - - // Event arrives after the bucket split. - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 20); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, - {partialBucketSplitTimeNs - bucketStartTimeNs}, - {bucketStartTimeNs}, {partialBucketSplitTimeNs}); - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(0, valueProducer.getCurrentBucketNum()); - - // Next value should create a new bucket. - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 5 * NS_PER_SEC, 10); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10, 20}, - {partialBucketSplitTimeNs - bucketStartTimeNs, - bucket2StartTimeNs - partialBucketSplitTimeNs}, - {bucketStartTimeNs, partialBucketSplitTimeNs}, - {partialBucketSplitTimeNs, bucket2StartTimeNs}); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, valueProducer.getCurrentBucketNum()); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150; - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - .WillOnce(Return(true)) - .WillOnce(Invoke([partialBucketSplitTimeNs]( - int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120)); - return true; - })); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100)); - - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - - switch (GetParam()) { - case APP_UPGRADE: - valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1, valueProducer.getCurrentBucketNum()); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150}, {bucket2StartTimeNs}, - {partialBucketSplitTimeNs}); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150)); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(2, valueProducer.getCurrentBucketNum()); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30}, {150, bucketSizeNs - 150}, - {bucket2StartTimeNs, partialBucketSplitTimeNs}, - {partialBucketSplitTimeNs, bucket3StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_split_bucket_for_app_upgrade(false); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Return(true)); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100)); - - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - - valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150); - ASSERT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, - bucket2StartTimeNs - 100); // Condition change to false time. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 120)); - return true; - })); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); - - valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100); - EXPECT_FALSE(valueProducer->mCondition); - - int64_t partialBucketSplitTimeNs = bucket2StartTimeNs - 50; - switch (GetParam()) { - case APP_UPGRADE: - valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - // Expect one full buckets already done and starting a partial bucket. - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs); - EXPECT_EQ(0, valueProducer->getCurrentBucketNum()); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, - {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)}, - {bucketStartTimeNs}, {partialBucketSplitTimeNs}); - EXPECT_FALSE(valueProducer->mCondition); -} - -TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(30, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, - logEventMatcherIndex, eventMatcherWizard, -1, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - valueProducer.mCondition = ConditionState::kFalse; - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has 1 slice - ASSERT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); - - valueProducer.onConditionChangedLocked(true, bucketStartTimeNs + 15); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(20, curInterval.value.long_value); - - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 30); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(50, curInterval.value.long_value); - - valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 40); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(50, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20}, {bucketStartTimeNs}, - {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestAnomalyDetection) { - sp alarmMonitor; - Alert alert; - alert.set_id(101); - alert.set_metric_id(metricId); - alert.set_trigger_if_sum_gt(130); - alert.set_num_buckets(2); - const int32_t refPeriodSec = 3; - alert.set_refractory_period_secs(refPeriodSec); - - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, - wizard, logEventMatcherIndex, eventMatcherWizard, - -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - valueProducer.prepareFirstBucket(); - - sp anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 1 * NS_PER_SEC, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 2 + NS_PER_SEC, 20); - - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event3, tagId, - bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, 130); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event4, tagId, - bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC, 1); - - LogEvent event5(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event5, tagId, - bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, 150); - - LogEvent event6(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event6, tagId, - bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC, 160); - - // Two events in bucket #0. - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - // Value sum == 30 <= 130. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); - - // One event in bucket #2. No alarm as bucket #0 is trashed out. - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - // Value sum == 130 <= 130. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); - - // Three events in bucket #3. - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - // Anomaly at event 4 since Value sum == 131 > 130! - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event5); - // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event6); - // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - std::ceil(1.0 * event6.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); -} - -// Test value metric no condition, the pull on bucket boundary come in time and too late -TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Return(true)); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector> allData; - // pull 1 - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - - // startUpdated:true sum:0 start:11 - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(11, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - // pull 2 at correct time - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - // tartUpdated:false sum:12 - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(23, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}, - {bucket2StartTimeNs}, {bucket3StartTimeNs}); - - // pull 3 come late. - // The previous bucket gets closed with error. (Has start value 23, no ending) - // Another bucket gets closed with error. (No start, but ending with 36) - // The new bucket is back to normal. - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - // startUpdated:false sum:12 - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(36, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}, - {bucket2StartTimeNs}, {bucket3StartTimeNs}); - // The 1st bucket is dropped because of no data - // The 3rd bucket is dropped due to multiple buckets being skipped. - ASSERT_EQ(2, valueProducer->mSkippedBuckets.size()); - - EXPECT_EQ(bucketStartTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs); - EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs); - ASSERT_EQ(1, valueProducer->mSkippedBuckets[0].dropEvents.size()); - EXPECT_EQ(NO_DATA, valueProducer->mSkippedBuckets[0].dropEvents[0].reason); - EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].dropEvents[0].dropTimeNs); - - EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[1].bucketStartTimeNs); - EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].bucketEndTimeNs); - ASSERT_EQ(1, valueProducer->mSkippedBuckets[1].dropEvents.size()); - EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[1].dropEvents[0].reason); - EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].dropEvents[0].dropTimeNs); -} - -/* - * Test pulled event with non sliced condition. The pull on boundary come late because the alarm - * was delivered late. - */ -TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // condition becomes true - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })) - // condition becomes false - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120)); - return true; - })); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - // pull on bucket boundary come late, condition change happens before it - valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - EXPECT_EQ(false, curBaseInfo.hasBase); - - // Now the alarm is delivered. - // since the condition turned to off before this pull finish, it has no effect - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); -} - -/* - * Test pulled event with non sliced condition. The pull on boundary come late, after the condition - * change to false, and then true again. This is due to alarm delivered late. - */ -TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // condition becomes true - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })) - // condition becomes false - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120)); - return true; - })) - // condition becomes true again - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 25); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - // startUpdated:false sum:0 start:100 - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - // pull on bucket boundary come late, condition change happens before it - valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - - // condition changed to true again, before the pull alarm is delivered - valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(130, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - - // Now the alarm is delivered, but it is considered late, the data will be used - // for the new bucket since it was just pulled. - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50); - - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(140, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(10, curInterval.value.long_value); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - assertPastBucketValuesSingleKey( - valueProducer->mPastBuckets, {20, 30}, {bucketSizeNs - 8, bucketSizeNs - 24}, - {bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPushedAggregateMin) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_aggregation_type(ValueMetric::MIN); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(10, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPushedAggregateMax) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_aggregation_type(ValueMetric::MAX); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(20, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_aggregation_type(ValueMetric::AVG); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval; - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(1, curInterval.sampleSize); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(25, curInterval.value.long_value); - EXPECT_EQ(2, curInterval.sampleSize); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer.mPastBuckets.size()); - ASSERT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - - EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value - - 12.5) < epsilon); -} - -TEST(ValueMetricProducerTest, TestPushedAggregateSum) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_aggregation_type(ValueMetric::SUM); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(25, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_aggregation_type(ValueMetric::MIN); - metric.set_use_diff(true); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(10, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(5, curInterval.value.long_value); - - // no change in data. - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(15, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(0, curInterval.value.long_value); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15); - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(15, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(0, curInterval.value.long_value); - - valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_value_field()->add_child()->set_field(3); - metric.set_aggregation_type(ValueMetric::MIN); - metric.set_use_diff(true); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, - pullerManager); - valueProducer.prepareFirstBucket(); - - LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateThreeValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10, 20); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateThreeValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 1, 15, 22); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(10, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(20, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - - // has one slice - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(5, curInterval.value.long_value); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(2, curInterval.value.long_value); - - // no change in first value field - LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateThreeValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 1, 15, 25); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; - - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(15, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(25, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateThreeValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15, 29); - - valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); - ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(15, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1]; - curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(29, curBaseInfo.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - - valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - - ASSERT_EQ(1UL, valueProducer.mPastBuckets.size()); - ASSERT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); - ASSERT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size()); - ASSERT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size()); - - EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[0].mConditionTrueNs); - EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].valueIndex[0]); - EXPECT_EQ(2, valueProducer.mPastBuckets.begin()->second[0].values[1].long_value); - EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].valueIndex[1]); - - EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[1].mConditionTrueNs); - EXPECT_EQ(3, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value); - EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[1].valueIndex[0]); -} - -/* - * Tests zero default base. - */ -TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - metric.set_use_zero_default_base(true); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto iter = valueProducer->mCurrentSlicedBucket.begin(); - auto& interval1 = iter->second[0]; - auto iterBase = valueProducer->mCurrentBaseInfo.begin(); - auto& baseInfo1 = iterBase->second[0]; - EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(3, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - vector> allData; - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(11, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(8, interval1.value.long_value); - - auto it = valueProducer->mCurrentSlicedBucket.begin(); - for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { - if (it != iter) { - break; - } - } - auto itBase = valueProducer->mCurrentBaseInfo.begin(); - for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) { - if (itBase != iterBase) { - break; - } - } - EXPECT_TRUE(it != iter); - EXPECT_TRUE(itBase != iterBase); - auto& interval2 = it->second[0]; - auto& baseInfo2 = itBase->second[0]; - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(4, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_EQ(4, interval2.value.long_value); - - ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); - auto iterator = valueProducer->mPastBuckets.begin(); - EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); - EXPECT_EQ(8, iterator->second[0].values[0].long_value); - iterator++; - EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); - EXPECT_EQ(4, iterator->second[0].values[0].long_value); -} - -/* - * Tests using zero default base with failed pull. - */ -TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - metric.set_use_zero_default_base(true); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - const auto& it = valueProducer->mCurrentSlicedBucket.begin(); - ValueMetricProducer::Interval& interval1 = it->second[0]; - ValueMetricProducer::BaseInfo& baseInfo1 = - valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0]; - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(3, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - vector> allData; - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); - - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(11, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(8, interval1.value.long_value); - - auto it2 = valueProducer->mCurrentSlicedBucket.begin(); - for (; it2 != valueProducer->mCurrentSlicedBucket.end(); it2++) { - if (it2 != it) { - break; - } - } - EXPECT_TRUE(it2 != it); - ValueMetricProducer::Interval& interval2 = it2->second[0]; - ValueMetricProducer::BaseInfo& baseInfo2 = - valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0]; - EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(4, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_EQ(4, interval2.value.long_value); - ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); - - // next pull somehow did not happen, skip to end of bucket 3 - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(5, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 13)); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 1, 5)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); - - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Get new references now that entries have been deleted from the map - const auto& it3 = valueProducer->mCurrentSlicedBucket.begin(); - const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin()); - ASSERT_EQ(it3->second.size(), 1); - ASSERT_EQ(it4->second.size(), 1); - ValueMetricProducer::Interval& interval3 = it3->second[0]; - ValueMetricProducer::Interval& interval4 = it4->second[0]; - ValueMetricProducer::BaseInfo& baseInfo3 = - valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0]; - ValueMetricProducer::BaseInfo& baseInfo4 = - valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())->second[0]; - - EXPECT_EQ(true, baseInfo3.hasBase); - EXPECT_EQ(5, baseInfo3.base.long_value); - EXPECT_EQ(false, interval3.hasValue); - EXPECT_EQ(5, interval3.value.long_value); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - EXPECT_EQ(true, baseInfo4.hasBase); - EXPECT_EQ(13, baseInfo4.base.long_value); - EXPECT_EQ(false, interval4.hasValue); - EXPECT_EQ(8, interval4.value.long_value); - - ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); -} - -/* - * Tests trim unused dimension key if no new data is seen in an entire bucket. - */ -TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto iter = valueProducer->mCurrentSlicedBucket.begin(); - auto& interval1 = iter->second[0]; - auto iterBase = valueProducer->mCurrentBaseInfo.begin(); - auto& baseInfo1 = iterBase->second[0]; - EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(3, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - vector> allData; - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(11, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(8, interval1.value.long_value); - EXPECT_FALSE(interval1.seenNewData); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - - auto it = valueProducer->mCurrentSlicedBucket.begin(); - for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { - if (it != iter) { - break; - } - } - auto itBase = valueProducer->mCurrentBaseInfo.begin(); - for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) { - if (itBase != iterBase) { - break; - } - } - EXPECT_TRUE(it != iter); - EXPECT_TRUE(itBase != iterBase); - auto interval2 = it->second[0]; - auto baseInfo2 = itBase->second[0]; - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(4, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_FALSE(interval2.seenNewData); - - // next pull somehow did not happen, skip to end of bucket 3 - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - // Only one interval left. One was trimmed. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(5, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_FALSE(interval2.seenNewData); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); - - interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(14, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_FALSE(interval2.seenNewData); - ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); - auto iterator = valueProducer->mPastBuckets.begin(); - EXPECT_EQ(bucket4StartTimeNs, iterator->second[0].mBucketStartNs); - EXPECT_EQ(bucket5StartTimeNs, iterator->second[0].mBucketEndNs); - EXPECT_EQ(9, iterator->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); - iterator++; - EXPECT_EQ(bucketStartTimeNs, iterator->second[0].mBucketStartNs); - EXPECT_EQ(bucket2StartTimeNs, iterator->second[0].mBucketEndNs); - EXPECT_EQ(8, iterator->second[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); -} - -TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - // Used by onConditionChanged. - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - - vector> allData; - valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer->mHasGlobalBase); -} - -TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // Condition change to true. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })) - .WillOnce(Return(false)); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - - valueProducer->onConditionChanged(false, bucketStartTimeNs + 20); - - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, valueProducer->mHasGlobalBase); -} - -TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50)); - return false; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to false. - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Don't directly set mCondition; the real code never does that. Go through regular code path - // to avoid unexpected behaviors. - // valueProducer->mCondition = ConditionState::kTrue; - valueProducer->onConditionChanged(true, bucketStartTimeNs); - - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - - valueProducer->onConditionChanged(false, bucketStartTimeNs + 1); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer->mHasGlobalBase); -} - -TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(0); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 120)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Max delay is set to 0 so pull will exceed max delay. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); -} - -TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return()); - - ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucket2StartTimeNs, bucket2StartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - valueProducer.mCondition = ConditionState::kFalse; - - // Event should be skipped since it is from previous bucket. - // Pull should not be called. - valueProducer.onConditionChanged(true, bucketStartTimeNs); - ASSERT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); -} - -TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - valueProducer->mHasGlobalBase = false; - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); - valueProducer->mHasGlobalBase = true; - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(100, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); -} - -/* - * Tests that a bucket is marked invalid when a condition change pull fails. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // First onConditionChanged - .WillOnce(Return(false)) - // Second onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kTrue); - - // Bucket start. - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - - // This will fail and should invalidate the whole bucket since we do not have all the data - // needed to compute the metric value when the screen was on. - valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); - - // Bucket end. - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); - - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - // Contains base from last pull which was successful. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(140, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 10, false /* include partial bucket */, true, - FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis()); -} - -/* - * Tests that a bucket is marked invalid when the guardrail is hit. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - metric.set_condition(StringToId("SCREEN_ON")); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - for (int i = 0; i < 2000; i++) { - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i)); - } - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 2); - EXPECT_EQ(true, valueProducer->mCurrentBucketIsSkipped); - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(0UL, valueProducer->mSkippedBuckets.size()); - - // Bucket 2 start. - vector> allData; - allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // First bucket added to mSkippedBuckets after flush. - ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size()); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */, - true, FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::DIMENSION_GUARDRAIL_REACHED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis()); -} - -/* - * Tests that a bucket is marked invalid when the bucket's initial pull fails. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120)); - return true; - })) - // Second onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kTrue); - - // Bucket start. - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); - - valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); - - // Bucket end. - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); - - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - // Contains base from last pull which was successful. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(140, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */, - true, FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis()); -} - -/* - * Tests that a bucket is marked invalid when the bucket's final pull fails - * (i.e. failed pull on bucket boundary). - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120)); - return true; - })) - // Second onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kTrue); - - // Bucket start. - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); - - valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); - - // Bucket end. - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); - - valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); - - ASSERT_EQ(0UL, valueProducer->mPastBuckets.size()); - // Last pull failed so base has been reset. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer->mHasGlobalBase); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */, - true, FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis()); -} - -TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - // Start bucket. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - // Bucket 2 start. - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - - // Bucket 3 empty. - allData.clear(); - allData.push_back(CreateNoValuesLogEvent(tagId, bucket3StartTimeNs + 1)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - // Data has been trimmed. - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); -} - -TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - // Empty pull. - valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer->mHasGlobalBase); -} - -TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 11); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 12); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 11); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 12); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval& curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - // End of bucket - vector> allData; - allData.clear(); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - // Data is empty, base should be reset. - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(5, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer->mHasGlobalBase); - - ASSERT_EQ(1UL, valueProducer->mPastBuckets.size()); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - metric.set_condition(StringToId("SCREEN_ON")); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _)) - // First onConditionChanged - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - - // End of bucket - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 2)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // Key 1 should be reset since in not present in the most pull. - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - auto iterator = valueProducer->mCurrentSlicedBucket.begin(); - auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin(); - EXPECT_EQ(true, baseInfoIter->second[0].hasBase); - EXPECT_EQ(2, baseInfoIter->second[0].base.long_value); - EXPECT_EQ(false, iterator->second[0].hasValue); - iterator++; - baseInfoIter++; - EXPECT_EQ(false, baseInfoIter->second[0].hasBase); - EXPECT_EQ(1, baseInfoIter->second[0].base.long_value); - EXPECT_EQ(false, iterator->second[0].hasValue); - - EXPECT_EQ(true, valueProducer->mHasGlobalBase); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp pullerManager = new StrictMock(); - int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2; - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Initialization. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })) - // notifyAppUpgrade. - .WillOnce(Invoke([partialBucketSplitTimeNs]( - int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10)); - return true; - })); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size()); - - switch (GetParam()) { - case APP_UPGRADE: - valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs); - EXPECT_EQ(0, valueProducer->getCurrentBucketNum()); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, - {partialBucketSplitTimeNs - bucketStartTimeNs}, - {bucketStartTimeNs}, {partialBucketSplitTimeNs}); - ASSERT_EQ(1UL, valueProducer->mCurrentFullBucket.size()); - - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4)); - // Pull fails and arrives late. - valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, - {partialBucketSplitTimeNs - bucketStartTimeNs}, - {bucketStartTimeNs}, {partialBucketSplitTimeNs}); - ASSERT_EQ(1, valueProducer->mSkippedBuckets.size()); - ASSERT_EQ(2, valueProducer->mSkippedBuckets[0].dropEvents.size()); - EXPECT_EQ(PULL_FAILED, valueProducer->mSkippedBuckets[0].dropEvents[0].reason); - EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[0].dropEvents[1].reason); - EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs); - EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs); - ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size()); -} - -TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Second onConditionChanged. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5)); - return true; - })) - // Third onConditionChanged. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kUnknown); - - valueProducer->onConditionChanged(false, bucketStartTimeNs); - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - - // End of first bucket - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 4)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1); - ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - - valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curBaseInfo.hasBase); - EXPECT_EQ(5, curBaseInfo.base.long_value); - EXPECT_EQ(false, curInterval.hasValue); - - valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10); - - // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10}, - {bucket2StartTimeNs}, {bucket3StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_use_diff(false); - - sp pullerManager = new StrictMock(); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - // Initialization. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST_P(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2; - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Initialization. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })) - // notifyAppUpgrade. - .WillOnce(Invoke([partialBucketSplitTimeNs]( - int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - switch (GetParam()) { - case APP_UPGRADE: - valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); - break; - case BOOT_COMPLETE: - valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); - break; - } - - // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // First on condition changed. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })) - // Second on condition changed. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 12); - - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(2, curInterval.value.long_value); - - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1); - - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}, {bucketStartTimeNs}, - {bucket2StartTimeNs}); -} - -// TODO: b/145705635 fix or delete this test -TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // First condition change. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1)); - return true; - })) - // 2nd condition change. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 8); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1)); - return true; - })) - // 3rd condition change. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); - - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 3, 10)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20)); - valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); - - valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8); - valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // There was not global base available so all buckets are invalid. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}); -} - -TEST(ValueMetricProducerTest, TestPullNeededFastDump) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return()); - - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - // Initial pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1)); - return true; - })); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - ProtoOutputStream output; - std::set strSet; - valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true, - FAST, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - // Bucket is invalid since we did not pull when dump report was called. - ASSERT_EQ(0, report.value_metrics().data_size()); -} - -TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return()); - - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _)) - // Initial pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1)); - return true; - })); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - vector> allData; - allData.clear(); - allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, tagId, 2, 2)); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - ProtoOutputStream output; - std::set strSet; - valueProducer.onDumpReport(bucket4StartTimeNs, false /* include recent buckets */, true, FAST, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - // Previous bucket is part of the report. - ASSERT_EQ(1, report.value_metrics().data_size()); - EXPECT_EQ(0, report.value_metrics().data(0).bucket_info(0).bucket_num()); -} - -TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp wizard = new NaggyMock(); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return()); - - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Initial pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1)); - return true; - })) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back( - CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10, tagId, 3, 3)); - return true; - })); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); - - ProtoOutputStream output; - std::set strSet; - valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - ASSERT_EQ(1, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size()); - EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); -} - -TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); - metric.set_use_diff(false); - - sp pullerManager = new StrictMock(); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 10)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30); - - // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); -} - -TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - metric.set_use_diff(false); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // condition becomes true - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - return true; - })) - // condition becomes false - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20)); - return true; - })); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 50); - // has one slice - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(20, curInterval.value.long_value); - - // Now the alarm is delivered. Condition is off though. - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); -} - -TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - metric.set_use_diff(false); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _)) - // condition becomes true - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - return true; - })); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - - // Now the alarm is delivered. Condition is off though. - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}, - {bucketStartTimeNs}, {bucket2StartTimeNs}); - ValueMetricProducer::Interval curInterval = - valueProducer->mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(false, curBaseInfo.hasBase); - EXPECT_EQ(false, curInterval.hasValue); -} - -TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - metric.set_use_diff(false); - - sp pullerManager = new StrictMock(); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Now the alarm is delivered. Condition is off though. - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // Condition was always false. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}); -} - -TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - metric.set_use_diff(false); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // condition becomes true - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10)); - return true; - })) - .WillOnce(Return(false)); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 50); - - // Now the alarm is delivered. Condition is off though. - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // No buckets, we had a failure. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}); -} - -/* - * Test that DUMP_REPORT_REQUESTED dump reason is logged. - * - * For the bucket to be marked invalid during a dump report requested, - * three things must be true: - * - we want to include the current partial bucket - * - we need a pull (metric is pulled and condition is true) - * - the dump latency must be FAST - */ - -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 20); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 40, true /* include recent buckets */, true, - FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40), dropEvent.drop_time_millis()); -} - -/* - * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late condition - * change event (i.e. the condition change occurs in the wrong bucket). - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); - - // Bucket boundary pull. - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); - - // Late condition change event. - valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(1, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(1); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), dropEvent.drop_time_millis()); -} - -/* - * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late accumulate - * event (i.e. the accumulate events call occurs in the wrong bucket). - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 100); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); - - // Bucket boundary pull. - vector> allData; - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); - - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 20)); - - // Late accumulateEvents event. - valueProducer->accumulateEvents(allData, bucket2StartTimeNs - 100, bucket2StartTimeNs - 100); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(1, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis()); -} - -/* - * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition - * when a metric is initialized. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10000); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kUnknown); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 10000; - valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that PULL_FAILED dump reason is logged due to a pull failure in - * #pullAndMatchEventsLocked. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10)); - return true; - })) - // Dump report requested, pull fails. - .WillOnce(Return(false)); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 10000; - valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that MULTIPLE_BUCKETS_SKIPPED dump reason is logged when a log event - * skips over more than one bucket. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket4StartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1000, 15)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - - // Condition change event that skips forward by three buckets. - valueProducer->onConditionChanged(false, bucket4StartTimeNs + 10); - - int64_t dumpTimeNs = bucket4StartTimeNs + 1000; - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(dumpTimeNs, true /* include current buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(2, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket4StartTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::MULTIPLE_BUCKETS_SKIPPED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket4StartTimeNs + 10), dropEvent.drop_time_millis()); - - // This bucket is skipped because a dumpReport with include current buckets is called. - // This creates a new bucket from bucket4StartTimeNs to dumpTimeNs in which we have no data - // since the condition is false for the entire bucket interval. - EXPECT_EQ(NanoToMillis(bucket4StartTimeNs), - report.value_metrics().skipped(1).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpTimeNs), - report.value_metrics().skipped(1).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size()); - - dropEvent = report.value_metrics().skipped(1).drop_event(0); - EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size - * is smaller than the "min_bucket_size_nanos" specified in the metric config. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - metric.set_min_bucket_size_nanos(10000000000); // 10 seconds - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 9000000); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 9000000, 15)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 9000000; - valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that NO_DATA dump reason is logged when a flushed bucket contains no data. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kFalse); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds - valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that all buckets are dropped due to condition unknown until the first onConditionChanged. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuckets) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 10)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucket2StartTimeNs + 15 * NS_PER_SEC, 15)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kUnknown); - - // Bucket should be dropped because of condition unknown. - int64_t appUpgradeTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC; - valueProducer->notifyAppUpgrade(appUpgradeTimeNs); - - // Bucket also dropped due to condition unknown - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 3)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - // This bucket is also dropped due to condition unknown. - int64_t conditionChangeTimeNs = bucket2StartTimeNs + 10 * NS_PER_SEC; - valueProducer->onConditionChanged(true, conditionChangeTimeNs); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - int64_t dumpReportTimeNs = bucket2StartTimeNs + 15 * NS_PER_SEC; // 15 seconds - valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(3, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), dropEvent.drop_time_millis()); - - EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), - report.value_metrics().skipped(1).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(1).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size()); - - dropEvent = report.value_metrics().skipped(1).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis()); - - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(2).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(2).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(2).drop_event_size()); - - dropEvent = report.value_metrics().skipped(2).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(conditionChangeTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that a skipped bucket is logged when a forced bucket split occurs when the previous bucket - * was not flushed in time. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenForceBucketSplitBeforeBucketFlush) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10)); - return true; - })) - // App Update. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1000); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1000, 15)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric, - ConditionState::kFalse); - - // Condition changed event - int64_t conditionChangeTimeNs = bucketStartTimeNs + 10; - valueProducer->onConditionChanged(true, conditionChangeTimeNs); - - // App update event. - int64_t appUpdateTimeNs = bucket2StartTimeNs + 1000; - valueProducer->notifyAppUpgrade(appUpdateTimeNs); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000; // 10 seconds - valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(1, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size()); - auto data = report.value_metrics().data(0); - ASSERT_EQ(0, data.bucket_info(0).bucket_num()); - EXPECT_EQ(5, data.bucket_info(0).values(0).value_long()); - - EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(appUpdateTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test multiple bucket drop events in the same bucket. - */ -TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _)) - // Condition change to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kUnknown); - - // Condition change event. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 1000; - valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true, - FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 10), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(1); - EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis()); -} - -/* - * Test that the number of logged bucket drop events is capped at the maximum. - * The maximum is currently 10 and is set in MetricProducer::maxDropEventsReached(). - */ -TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // First condition change event. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - for (int i = 0; i < 2000; i++) { - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i)); - } - return true; - })) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Return(false)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 220); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 220, 10)); - return true; - })); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, ConditionState::kUnknown); - - // First condition change event causes guardrail to be reached. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); - - // 2-10 condition change events result in failed pulls. - valueProducer->onConditionChanged(false, bucketStartTimeNs + 30); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 70); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 90); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 100); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 150); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 170); - valueProducer->onConditionChanged(true, bucketStartTimeNs + 190); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 200); - - // Condition change event 11 - valueProducer->onConditionChanged(true, bucketStartTimeNs + 220); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 1000; - // Because we already have 10 dump events in the current bucket, - // this case should not be added to the list of dump events. - valueProducer->onDumpReport(bucketStartTimeNs + 1000, true /* include recent buckets */, true, - FAST /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(dumpReportTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(10, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 10), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(1); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 30), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(2); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 50), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(3); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 70), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(4); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 90), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(5); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 100), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(6); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 150), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(7); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 170), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(8); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 190), dropEvent.drop_time_millis()); - - dropEvent = report.value_metrics().skipped(0).drop_event(9); - EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 200), dropEvent.drop_time_millis()); -} - -/* - * Test metric with a simple sliced state - * - Increasing values - * - Using diff - * - Second field is value field - */ -TEST(ValueMetricProducerTest, TestSlicedState) { - // Set up ValueMetricProducer. - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE"); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // ValueMetricProducer initialized. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })) - // Screen state change to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5)); - return true; - })) - // Screen state change to OFF. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9)); - return true; - })) - // Screen state change to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30)); - return true; - })); - - StateManager::getInstance().clear(); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithState( - pullerManager, metric, {util::SCREEN_STATE_CHANGED}, {}); - EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - - // Bucket status after metric initialized. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(3, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Bucket status after screen state change kStateUnknown->ON. - auto screenEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(5, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); - - // Bucket status after screen state change ON->OFF. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(9, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, ON} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(4, it->second[0].value.long_value); - // Value for dimension, state key {{}, kStateUnknown} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); - - // Bucket status after screen state change OFF->ON. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15, - android::view::DisplayStateEnum::DISPLAY_STATE_ON); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(21, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, OFF} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(12, it->second[0].value.long_value); - // Value for dimension, state key {{}, ON} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(4, it->second[0].value.long_value); - // Value for dimension, state key {{}, kStateUnknown} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); - - // Start dump report and check output. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(3, report.value_metrics().data_size()); - - auto data = report.value_metrics().data(0); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); - - data = report.value_metrics().data(1); - ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); - EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value()); - - data = report.value_metrics().data(2); - ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size()); - EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value()); -} - -/* - * Test metric with sliced state with map - * - Increasing values - * - Using diff - * - Second field is value field - */ -TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { - // Set up ValueMetricProducer. - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF"); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // ValueMetricProducer initialized. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); - return true; - })) - // Screen state change to ON. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5)); - return true; - })) - // Screen state change to VR has no pull because it is in the same - // state group as ON. - - // Screen state change to ON has no pull because it is in the same - // state group as VR. - - // Screen state change to OFF. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21)); - return true; - })) - // Dump report requested. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30)); - return true; - })); - - const StateMap& stateMap = - CreateScreenStateOnOffMap(/*screen on id=*/321, /*screen off id=*/123); - const StateMap_StateGroup screenOnGroup = stateMap.group(0); - const StateMap_StateGroup screenOffGroup = stateMap.group(1); - - unordered_map> stateGroupMap; - for (auto group : stateMap.group()) { - for (auto value : group.value()) { - stateGroupMap[SCREEN_STATE_ATOM_ID][value] = group.group_id(); - } - } - - StateManager::getInstance().clear(); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithState( - pullerManager, metric, {util::SCREEN_STATE_CHANGED}, stateGroupMap); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); - - // Bucket status after metric initialized. - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(3, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, {kStateUnknown}} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Bucket status after screen state change kStateUnknown->ON. - auto screenEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(5, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - itBase->second[0].currentState.getValues()[0].mValue.long_value); - // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); - - // Bucket status after screen state change ON->VR. - // Both ON and VR are in the same state group, so the base should not change. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, - android::view::DisplayStateEnum::DISPLAY_STATE_VR); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(5, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); - - // Bucket status after screen state change VR->ON. - // Both ON and VR are in the same state group, so the base should not change. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12, - android::view::DisplayStateEnum::DISPLAY_STATE_ON); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(5, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, kStateUnknown} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); - - // Bucket status after screen state change VR->OFF. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15, - android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(21, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(screenOffGroup.group_id(), - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{}, ON GROUP} - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(screenOnGroup.group_id(), - it->first.getStateValuesKey().getValues()[0].mValue.long_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(16, it->second[0].value.long_value); - // Value for dimension, state key {{}, kStateUnknown} - it++; - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); - - // Start dump report and check output. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(3, report.value_metrics().data_size()); - - auto data = report.value_metrics().data(0); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value()); - - data = report.value_metrics().data(1); - ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); - EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id()); - - data = report.value_metrics().data(2); - ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size()); - EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); - EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_group_id()); - EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id()); -} - -/* - * Test metric that slices by state with a primary field and has dimensions - * - Increasing values - * - Using diff - * - Second field is value field - */ -TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { - // Set up ValueMetricProducer. - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE"); - metric.mutable_dimensions_in_what()->set_field(tagId); - metric.mutable_dimensions_in_what()->add_child()->set_field(1); - - MetricStateLink* stateLink = metric.add_state_link(); - stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); - auto fieldsInWhat = stateLink->mutable_fields_in_what(); - *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */}); - auto fieldsInState = stateLink->mutable_fields_in_state(); - *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); - - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // ValueMetricProducer initialized. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs); - data->clear(); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 7)); - data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1 /*uid*/, 3)); - return true; - })) - // Uid 1 process state change from kStateUnknown -> Foreground - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20); - data->clear(); - data->push_back( - CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 1 /*uid*/, 6)); - - // This event should be skipped. - data->push_back( - CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 2 /*uid*/, 8)); - return true; - })) - // Uid 2 process state change from kStateUnknown -> Background - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40); - data->clear(); - data->push_back( - CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 2 /*uid*/, 9)); - - // This event should be skipped. - data->push_back( - CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 1 /*uid*/, 12)); - return true; - })) - // Uid 1 process state change from Foreground -> Background - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20); - data->clear(); - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 1 /*uid*/, 13)); - - // This event should be skipped. - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 2 /*uid*/, 11)); - return true; - })) - // Uid 1 process state change from Background -> Foreground - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40); - data->clear(); - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 1 /*uid*/, 17)); - - // This event should be skipped. - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 2 /*uid */, 15)); - return true; - })) - // Dump report pull. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50); - data->clear(); - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 2 /*uid*/, 20)); - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 1 /*uid*/, 21)); - return true; - })); - - StateManager::getInstance().clear(); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithState( - pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); - - // Bucket status after metric initialized. - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {uid 1}. - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(3, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{uid 1}, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - // Base for dimension key {uid 2} - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(7, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for dimension, state key {{uid 2}, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Bucket status after uid 1 process state change kStateUnknown -> Foreground. - auto uidProcessEvent = CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {uid 1}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(6, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {uid 1, kStateUnknown}. - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(3, it->second[0].value.long_value); - - // Base for dimension key {uid 2} - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(7, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Bucket status after uid 2 process state change kStateUnknown -> Background. - uidProcessEvent = CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 40, 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {uid 1}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(6, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {uid 1, kStateUnknown}. - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(3, it->second[0].value.long_value); - - // Base for dimension key {uid 2} - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(9, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); - - // Pull at end of first bucket. - vector> allData; - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 10)); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 15)); - valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); - - // Buckets flushed after end of first bucket. - // None of the buckets should have a value. - ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(4UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); - // Base for dimension key {uid 2}. - it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(15, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, BACKGROUND}. - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Base for dimension key {uid 1} - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(10, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {uid 1, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* kStateTracker::kUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Value for key {uid 1, FOREGROUND} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Value for key {uid 2, kStateUnknown} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* kStateTracker::kUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Bucket status after uid 1 process state change from Foreground -> Background. - uidProcessEvent = CreateUidProcessStateChangedEvent( - bucket2StartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - - ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(4UL, valueProducer->mPastBuckets.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); - // Base for dimension key {uid 2}. - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(15, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, BACKGROUND}. - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - // Base for dimension key {uid 1} - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(13, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {uid 1, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - // Value for key {uid 1, FOREGROUND} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(3, it->second[0].value.long_value); - // Value for key {uid 2, kStateUnknown} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Bucket status after uid 1 process state change Background->Foreground. - uidProcessEvent = CreateUidProcessStateChangedEvent( - bucket2StartTimeNs + 40, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); - StateManager::getInstance().onLogEvent(*uidProcessEvent); - - ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size()); - ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); - // Base for dimension key {uid 2} - it = valueProducer->mCurrentSlicedBucket.begin(); - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(15, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, BACKGROUND} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Base for dimension key {uid 1} - it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(17, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {uid 1, kStateUnknown} - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Value for key {uid 1, BACKGROUND} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(4, it->second[0].value.long_value); - - // Value for key {uid 1, FOREGROUND} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(3, it->second[0].value.long_value); - - // Value for key {uid 2, kStateUnknown} - it++; - ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - - // Start dump report and check output. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 50, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(5, report.value_metrics().data_size()); - - auto data = report.value_metrics().data(0); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - - data = report.value_metrics().data(1); - ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); - EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value()); - - data = report.value_metrics().data(2); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(2, report.value_metrics().data(2).bucket_info_size()); - EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); - EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long()); - - data = report.value_metrics().data(3); - ASSERT_EQ(1, report.value_metrics().data(3).bucket_info_size()); - EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long()); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value()); - - data = report.value_metrics().data(4); - EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, - data.slice_by_state(0).value()); - ASSERT_EQ(2, report.value_metrics().data(4).bucket_info_size()); - EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long()); - EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long()); -} - -TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { - // Set up ValueMetricProducer. - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState( - "BATTERY_SAVER_MODE_STATE"); - sp pullerManager = new StrictMock(); - EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _)) - // Condition changed to true. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 3)); - return true; - })) - // Battery saver mode state changed to OFF. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC); - data->clear(); - data->push_back( - CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5)); - return true; - })) - // Condition changed to false. - .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, - vector>* data, bool) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC); - data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 15)); - return true; - })); - - StateManager::getInstance().clear(); - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithConditionAndState( - pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}, - ConditionState::kFalse); - EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); - - // Set up StateManager and check that StateTrackers are initialized. - StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, - valueProducer); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getListenersCount( - util::BATTERY_SAVER_MODE_STATE_CHANGED)); - - // Bucket status after battery saver mode ON event. - // Condition is false so we do nothing. - unique_ptr batterySaverOnEvent = - CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOnEvent); - EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(0UL, valueProducer->mCurrentBaseInfo.size()); - - // Bucket status after condition change to true. - valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - std::unordered_map>::iterator - itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(3, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {{}, -1} - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - std::unordered_map>::iterator - it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1 /*StateTracker::kUnknown*/, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_FALSE(it->second[0].hasValue); - - // Bucket status after battery saver mode OFF event. - unique_ptr batterySaverOffEvent = - CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC); - StateManager::getInstance().onLogEvent(*batterySaverOffEvent); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(5, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {{}, ON} - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(2, it->second[0].value.long_value); - - // Pull at end of first bucket. - vector> allData; - allData.clear(); - allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11)); - valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - - EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_TRUE(itBase->second[0].hasBase); - EXPECT_EQ(11, itBase->second[0].base.long_value); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - - // Bucket 2 status after condition change to false. - valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC); - // Base for dimension key {} - ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); - itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY); - EXPECT_FALSE(itBase->second[0].hasBase); - EXPECT_TRUE(itBase->second[0].hasCurrentState); - ASSERT_EQ(1, itBase->second[0].currentState.getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - itBase->second[0].currentState.getValues()[0].mValue.int_value); - // Value for key {{}, OFF} - ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); - ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, - it->first.getStateValuesKey().getValues()[0].mValue.int_value); - EXPECT_TRUE(it->second[0].hasValue); - EXPECT_EQ(4, it->second[0].value.long_value); - - // Start dump report and check output. - ProtoOutputStream output; - std::set strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, - true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, - &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(2, report.value_metrics().data_size()); - - ValueMetricData data = report.value_metrics().data(0); - EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value()); - ASSERT_EQ(1, data.bucket_info_size()); - EXPECT_EQ(2, data.bucket_info(0).values(0).value_long()); - - data = report.value_metrics().data(1); - EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); - EXPECT_TRUE(data.slice_by_state(0).has_value()); - EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value()); - ASSERT_EQ(2, data.bucket_info_size()); - EXPECT_EQ(6, data.bucket_info(0).values(0).value_long()); - EXPECT_EQ(4, data.bucket_info(1).values(0).value_long()); -} - -/* - * Test bucket splits when condition is unknown. - */ -TEST(ValueMetricProducerTest, TestForcedBucketSplitWhenConditionUnknownSkipsBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - - sp pullerManager = new StrictMock(); - - sp valueProducer = - ValueMetricProducerTestHelper::createValueProducerWithCondition( - pullerManager, metric, - ConditionState::kUnknown); - - // App update event. - int64_t appUpdateTimeNs = bucketStartTimeNs + 1000; - valueProducer->notifyAppUpgrade(appUpdateTimeNs); - - // Check dump report. - ProtoOutputStream output; - std::set strSet; - int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds - valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true, - NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); - - StatsLogReport report = outputStreamToProto(&output); - EXPECT_TRUE(report.has_value_metrics()); - ASSERT_EQ(0, report.value_metrics().data_size()); - ASSERT_EQ(1, report.value_metrics().skipped_size()); - - EXPECT_EQ(NanoToMillis(bucketStartTimeNs), - report.value_metrics().skipped(0).start_bucket_elapsed_millis()); - EXPECT_EQ(NanoToMillis(appUpdateTimeNs), - report.value_metrics().skipped(0).end_bucket_elapsed_millis()); - ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size()); - - auto dropEvent = report.value_metrics().skipped(0).drop_event(0); - EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason()); - EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis()); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp deleted file mode 100644 index 108df04b45cb..000000000000 --- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "metrics_test_helper.h" - -namespace android { -namespace os { -namespace statsd { - -HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) { - HashableDimensionKey dimension; - int pos[] = {key, 0, 0}; - dimension.addValue(FieldValue(Field(tagId, pos, 0), Value(value))); - - return dimension; -} - -HashableDimensionKey getMockedDimensionKeyLongValue(int tagId, int key, int64_t value) { - HashableDimensionKey dimension; - int pos[] = {key, 0, 0}; - dimension.addValue(FieldValue(Field(tagId, pos, 0), Value(value))); - - return dimension; -} - -MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) { - return MetricDimensionKey(getMockedDimensionKey(tagId, key, value), DEFAULT_DIMENSION_KEY); -} - -MetricDimensionKey getMockedStateDimensionKey(int tagId, int key, int64_t value) { - return MetricDimensionKey(DEFAULT_DIMENSION_KEY, - getMockedDimensionKeyLongValue(tagId, key, value)); -} - -void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) { - matcher->set_field(tagId); -} - -void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatcher* matcher) { - matcher->set_field(tagId); - matcher->add_child()->set_field(fieldNum); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h deleted file mode 100644 index eeb38a4644fd..000000000000 --- a/cmds/statsd/tests/metrics/metrics_test_helper.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#pragma once - -#include "src/condition/ConditionWizard.h" -#include "src/external/StatsPullerManager.h" -#include "src/packages/UidMap.h" - -#include -#include - -namespace android { -namespace os { -namespace statsd { - -class MockConditionWizard : public ConditionWizard { -public: - MOCK_METHOD3(query, - ConditionState(const int conditionIndex, const ConditionKey& conditionParameters, - const bool isPartialLink)); -}; - -class MockStatsPullerManager : public StatsPullerManager { -public: - MOCK_METHOD5(RegisterReceiver, - void(int tagId, const ConfigKey& key, wp receiver, - int64_t nextPulltimeNs, int64_t intervalNs)); - MOCK_METHOD3(UnRegisterReceiver, - void(int tagId, const ConfigKey& key, wp receiver)); - MOCK_METHOD5(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs, - vector>* data, bool useUids)); - MOCK_METHOD5(Pull, - bool(const int pullCode, const vector& uids, const int64_t eventTimeNs, - vector>* data, bool useUids)); - MOCK_METHOD2(RegisterPullUidProvider, - void(const ConfigKey& configKey, wp provider)); - MOCK_METHOD2(UnregisterPullUidProvider, - void(const ConfigKey& configKey, wp provider)); -}; - -HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value); -MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value); - -HashableDimensionKey getMockedDimensionKeyLongValue(int tagId, int key, int64_t value); -MetricDimensionKey getMockedStateDimensionKey(int tagId, int key, int64_t value); - -// Utils to build FieldMatcher proto for simple one-depth atoms. -void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher); -void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp deleted file mode 100644 index 1f0e2c82cb32..000000000000 --- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "src/shell/ShellSubscriber.h" - -#include -#include -#include - -#include - -#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h" -#include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h" -#include "frameworks/proto_logging/stats/atoms.pb.h" -#include "stats_event.h" -#include "tests/metrics/metrics_test_helper.h" -#include "tests/statsd_test_util.h" - -using namespace android::os::statsd; -using android::sp; -using std::vector; -using testing::_; -using testing::Invoke; -using testing::NaggyMock; -using testing::StrictMock; - -#ifdef __ANDROID__ - -void runShellTest(ShellSubscription config, sp uidMap, - sp pullerManager, - const vector>& pushedEvents, - const ShellData& expectedData) { - // set up 2 pipes for read/write config and data - int fds_config[2]; - ASSERT_EQ(0, pipe(fds_config)); - - int fds_data[2]; - ASSERT_EQ(0, pipe(fds_data)); - - size_t bufferSize = config.ByteSize(); - // write the config to pipe, first write size of the config - write(fds_config[1], &bufferSize, sizeof(bufferSize)); - // then write config itself - vector buffer(bufferSize); - config.SerializeToArray(&buffer[0], bufferSize); - write(fds_config[1], buffer.data(), bufferSize); - close(fds_config[1]); - - sp shellClient = new ShellSubscriber(uidMap, pullerManager); - - // mimic a binder thread that a shell subscriber runs on. it would block. - std::thread reader([&shellClient, &fds_config, &fds_data] { - shellClient->startNewSubscription(fds_config[0], fds_data[1], /*timeoutSec=*/-1); - }); - reader.detach(); - - // let the shell subscriber to receive the config from pipe. - std::this_thread::sleep_for(100ms); - - if (pushedEvents.size() > 0) { - // send a log event that matches the config. - std::thread log_reader([&shellClient, &pushedEvents] { - for (const auto& event : pushedEvents) { - shellClient->onLogEvent(*event); - } - }); - - log_reader.detach(); - - if (log_reader.joinable()) { - log_reader.join(); - } - } - - // wait for the data to be written. - std::this_thread::sleep_for(100ms); - - // Because we might receive heartbeats from statsd, consisting of data sizes - // of 0, encapsulate reads within a while loop. - bool readAtom = false; - while (!readAtom) { - // Read the atom size. - size_t dataSize = 0; - read(fds_data[0], &dataSize, sizeof(dataSize)); - if (dataSize == 0) continue; - EXPECT_EQ(expectedData.ByteSize(), int(dataSize)); - - // Read that much data in proto binary format. - vector dataBuffer(dataSize); - EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize)); - - // Make sure the received bytes can be parsed to an atom. - ShellData receivedAtom; - EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0); - - // Serialize the expected atom to byte array and compare to make sure - // they are the same. - vector expectedAtomBuffer(expectedData.ByteSize()); - expectedData.SerializeToArray(expectedAtomBuffer.data(), expectedData.ByteSize()); - EXPECT_EQ(expectedAtomBuffer, dataBuffer); - - readAtom = true; - } - - close(fds_data[0]); - if (reader.joinable()) { - reader.join(); - } -} - -TEST(ShellSubscriberTest, testPushedSubscription) { - sp uidMap = new NaggyMock(); - - sp pullerManager = new StrictMock(); - vector> pushedList; - - // Create the LogEvent from an AStatsEvent - std::unique_ptr logEvent = CreateScreenStateChangedEvent( - 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON); - pushedList.push_back(std::move(logEvent)); - - // create a simple config to get screen events - ShellSubscription config; - config.add_pushed()->set_atom_id(29); - - // this is the expected screen event atom. - ShellData shellData; - shellData.add_atom()->mutable_screen_state_changed()->set_state( - ::android::view::DisplayStateEnum::DISPLAY_STATE_ON); - - runShellTest(config, uidMap, pullerManager, pushedList, shellData); -} - -namespace { - -int kUid1 = 1000; -int kUid2 = 2000; - -int kCpuTime1 = 100; -int kCpuTime2 = 200; - -ShellData getExpectedShellData() { - ShellData shellData; - auto* atom1 = shellData.add_atom()->mutable_cpu_active_time(); - atom1->set_uid(kUid1); - atom1->set_time_millis(kCpuTime1); - - auto* atom2 = shellData.add_atom()->mutable_cpu_active_time(); - atom2->set_uid(kUid2); - atom2->set_time_millis(kCpuTime2); - - return shellData; -} - -ShellSubscription getPulledConfig() { - ShellSubscription config; - auto* pull_config = config.add_pulled(); - pull_config->mutable_matcher()->set_atom_id(10016); - pull_config->set_freq_millis(2000); - return config; -} - -shared_ptr makeCpuActiveTimeAtom(int32_t uid, int64_t timeMillis) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 10016); - AStatsEvent_overwriteTimestamp(statsEvent, 1111L); - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeInt64(statsEvent, timeMillis); - - std::shared_ptr logEvent = std::make_shared(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -} // namespace - -TEST(ShellSubscriberTest, testPulledSubscription) { - sp uidMap = new NaggyMock(); - - sp pullerManager = new StrictMock(); - const vector uids = {AID_SYSTEM}; - EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _, _)) - .WillRepeatedly(Invoke([](int tagId, const vector&, const int64_t, - vector>* data, bool) { - data->clear(); - data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1)); - data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2)); - return true; - })); - runShellTest(getPulledConfig(), uidMap, pullerManager, vector>(), - getExpectedShellData()); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp deleted file mode 100644 index 6516c1529514..000000000000 --- a/cmds/statsd/tests/state/StateTracker_test.cpp +++ /dev/null @@ -1,571 +0,0 @@ -/* - * Copyright (C) 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "state/StateTracker.h" - -#include -#include - -#include "state/StateListener.h" -#include "state/StateManager.h" -#include "state/StateTracker.h" -#include "stats_event.h" -#include "tests/statsd_test_util.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -const int32_t timestampNs = 1000; - -/** - * Mock StateListener class for testing. - * Stores primary key and state pairs. - */ -class TestStateListener : public virtual StateListener { -public: - TestStateListener(){}; - - virtual ~TestStateListener(){}; - - struct Update { - Update(const HashableDimensionKey& key, int state) : mKey(key), mState(state){}; - HashableDimensionKey mKey; - int mState; - }; - - std::vector updates; - - void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, - const HashableDimensionKey& primaryKey, const FieldValue& oldState, - const FieldValue& newState) { - updates.emplace_back(primaryKey, newState.mValue.int_value); - } -}; - -int getStateInt(StateManager& mgr, int atomId, const HashableDimensionKey& queryKey) { - FieldValue output; - mgr.getStateValue(atomId, queryKey, &output); - return output.mValue.int_value; -} - -// START: build event functions. -// Incorrect event - missing fields -std::unique_ptr buildIncorrectOverlayEvent(int uid, const std::string& packageName, - int state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, 1000); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeString(statsEvent, packageName.c_str()); - // Missing field 3 - using_alert_window. - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -// Incorrect event - exclusive state has wrong type -std::unique_ptr buildOverlayEventBadStateType(int uid, const std::string& packageName) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, 1000); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeString(statsEvent, packageName.c_str()); - AStatsEvent_writeInt32(statsEvent, true); // using_alert_window - AStatsEvent_writeString(statsEvent, "string"); // exclusive state: string instead of int - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} -// END: build event functions. - -TEST(StateListenerTest, TestStateListenerWeakPointer) { - sp listener = new TestStateListener(); - wp wListener = listener; - listener = nullptr; // let go of listener - EXPECT_TRUE(wListener.promote() == nullptr); -} - -TEST(StateManagerTest, TestStateManagerGetInstance) { - sp listener1 = new TestStateListener(); - StateManager& mgr = StateManager::getInstance(); - mgr.clear(); - - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); -} - -TEST(StateManagerTest, TestOnLogEvent) { - sp uidMap = makeMockUidMapForPackage("com.android.systemui", {10111}); - sp listener1 = new TestStateListener(); - StateManager mgr; - mgr.updateLogSources(uidMap); - // Add StateTracker by registering a listener. - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - - // log event using AID_ROOT - std::unique_ptr event = CreateScreenStateChangedEvent( - timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - mgr.onLogEvent(*event); - - // check StateTracker was updated by querying for state - HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); - - // log event using mocked uid - event = CreateScreenStateChangedEvent( - timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_OFF, 10111); - mgr.onLogEvent(*event); - - // check StateTracker was updated by querying for state - queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); - - // log event using non-whitelisted uid - event = CreateScreenStateChangedEvent(timestampNs, - android::view::DisplayStateEnum::DISPLAY_STATE_ON, 10112); - mgr.onLogEvent(*event); - - // check StateTracker was NOT updated by querying for state - queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); - - // log event using AID_SYSTEM - event = CreateScreenStateChangedEvent( - timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON, AID_SYSTEM); - mgr.onLogEvent(*event); - - // check StateTracker was updated by querying for state - queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); -} - -/** - * Test registering listeners to StateTrackers - * - * - StateManager will create a new StateTracker if it doesn't already exist - * and then register the listener to the StateTracker. - * - If a listener is already registered to a StateTracker, it is not added again. - * - StateTrackers are only created for atoms that are state atoms. - */ -TEST(StateTrackerTest, TestRegisterListener) { - sp listener1 = new TestStateListener(); - sp listener2 = new TestStateListener(); - StateManager mgr; - - // Register listener to non-existing StateTracker - EXPECT_EQ(0, mgr.getStateTrackersCount()); - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Register listener to existing StateTracker - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Register already registered listener to existing StateTracker - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Register listener to non-state atom - mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2); - EXPECT_EQ(2, mgr.getStateTrackersCount()); -} - -/** - * Test unregistering listeners from StateTrackers - * - * - StateManager will unregister listeners from a StateTracker only if the - * StateTracker exists and the listener is registered to the StateTracker. - * - Once all listeners are removed from a StateTracker, the StateTracker - * is also removed. - */ -TEST(StateTrackerTest, TestUnregisterListener) { - sp listener1 = new TestStateListener(); - sp listener2 = new TestStateListener(); - StateManager mgr; - - // Unregister listener from non-existing StateTracker - EXPECT_EQ(0, mgr.getStateTrackersCount()); - mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener1); - EXPECT_EQ(0, mgr.getStateTrackersCount()); - EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Unregister non-registered listener from existing StateTracker - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener2); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Unregister second-to-last listener from StateTracker - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2); - mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener1); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); - - // Unregister last listener from StateTracker - mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener2); - EXPECT_EQ(0, mgr.getStateTrackersCount()); - EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); -} - -/** - * Test a binary state atom with nested counting. - * - * To go from an "ON" state to an "OFF" state with nested counting, we must see - * an equal number of "OFF" events as "ON" events. - * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state. - * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state. - */ -TEST(StateTrackerTest, TestStateChangeNested) { - sp listener = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener); - - std::vector attributionUids1 = {1000}; - std::vector attributionTags1 = {"tag"}; - - std::unique_ptr event1 = CreateAcquireWakelockEvent(timestampNs, attributionUids1, - attributionTags1, "wakelockName"); - mgr.onLogEvent(*event1); - ASSERT_EQ(1, listener->updates.size()); - EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(1, listener->updates[0].mState); - listener->updates.clear(); - - std::unique_ptr event2 = CreateAcquireWakelockEvent( - timestampNs + 1000, attributionUids1, attributionTags1, "wakelockName"); - mgr.onLogEvent(*event2); - ASSERT_EQ(0, listener->updates.size()); - - std::unique_ptr event3 = CreateReleaseWakelockEvent( - timestampNs + 2000, attributionUids1, attributionTags1, "wakelockName"); - mgr.onLogEvent(*event3); - ASSERT_EQ(0, listener->updates.size()); - - std::unique_ptr event4 = CreateReleaseWakelockEvent( - timestampNs + 3000, attributionUids1, attributionTags1, "wakelockName"); - mgr.onLogEvent(*event4); - ASSERT_EQ(1, listener->updates.size()); - EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(0, listener->updates[0].mState); -} - -/** - * Test a state atom with a reset state. - * - * If the reset state value is seen, every state in the map is set to the default - * state and every listener is notified. - */ -TEST(StateTrackerTest, TestStateChangeReset) { - sp listener = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::BLE_SCAN_STATE_CHANGED, listener); - - std::vector attributionUids1 = {1000}; - std::vector attributionTags1 = {"tag1"}; - std::vector attributionUids2 = {2000}; - - std::unique_ptr event1 = - CreateBleScanStateChangedEvent(timestampNs, attributionUids1, attributionTags1, - BleScanStateChanged::ON, false, false, false); - mgr.onLogEvent(*event1); - ASSERT_EQ(1, listener->updates.size()); - EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState); - FieldValue stateFieldValue; - mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue); - EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value); - listener->updates.clear(); - - std::unique_ptr event2 = - CreateBleScanStateChangedEvent(timestampNs + 1000, attributionUids2, attributionTags1, - BleScanStateChanged::ON, false, false, false); - mgr.onLogEvent(*event2); - ASSERT_EQ(1, listener->updates.size()); - EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState); - mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue); - EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value); - listener->updates.clear(); - - std::unique_ptr event3 = - CreateBleScanStateChangedEvent(timestampNs + 2000, attributionUids2, attributionTags1, - BleScanStateChanged::RESET, false, false, false); - mgr.onLogEvent(*event3); - ASSERT_EQ(2, listener->updates.size()); - for (const TestStateListener::Update& update : listener->updates) { - EXPECT_EQ(BleScanStateChanged::OFF, update.mState); - - mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, update.mKey, &stateFieldValue); - EXPECT_EQ(BleScanStateChanged::OFF, stateFieldValue.mValue.int_value); - } -} - -/** - * Test StateManager's onLogEvent and StateListener's onStateChanged correctly - * updates listener for states without primary keys. - */ -TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) { - sp listener1 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - - // log event - std::unique_ptr event = CreateScreenStateChangedEvent( - timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - mgr.onLogEvent(*event); - - // check listener was updated - ASSERT_EQ(1, listener1->updates.size()); - EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey); - EXPECT_EQ(2, listener1->updates[0].mState); - - // check StateTracker was updated by querying for state - HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); -} - -/** - * Test StateManager's onLogEvent and StateListener's onStateChanged correctly - * updates listener for states with one primary key. - */ -TEST(StateTrackerTest, TestStateChangeOnePrimaryField) { - sp listener1 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener1); - - // log event - std::unique_ptr event = CreateUidProcessStateChangedEvent( - timestampNs, 1000 /*uid*/, android::app::ProcessStateEnum::PROCESS_STATE_TOP); - mgr.onLogEvent(*event); - - // check listener was updated - ASSERT_EQ(1, listener1->updates.size()); - EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(1002, listener1->updates[0].mState); - - // check StateTracker was updated by querying for state - HashableDimensionKey queryKey; - getUidProcessKey(1000 /* uid */, &queryKey); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP, - getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey)); -} - -TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) { - sp listener1 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener1); - - // Log event. - std::vector attributionUids = {1001}; - std::vector attributionTags = {"tag1"}; - - std::unique_ptr event = CreateAcquireWakelockEvent(timestampNs, attributionUids, - attributionTags, "wakelockName"); - mgr.onLogEvent(*event); - EXPECT_EQ(1, mgr.getStateTrackersCount()); - EXPECT_EQ(1, mgr.getListenersCount(util::WAKELOCK_STATE_CHANGED)); - - // Check listener was updated. - ASSERT_EQ(1, listener1->updates.size()); - ASSERT_EQ(3, listener1->updates[0].mKey.getValues().size()); - EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value); - EXPECT_EQ("wakelockName", listener1->updates[0].mKey.getValues()[2].mValue.str_value); - EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState); - - // Check StateTracker was updated by querying for state. - HashableDimensionKey queryKey; - getPartialWakelockKey(1001 /* uid */, "wakelockName", &queryKey); - EXPECT_EQ(WakelockStateChanged::ACQUIRE, - getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey)); - - // No state stored for this query key. - HashableDimensionKey queryKey2; - getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2); - EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, - getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2)); - - // Partial query fails. - HashableDimensionKey queryKey3; - getPartialWakelockKey(1001 /* uid */, &queryKey3); - EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, - getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3)); -} - -/** - * Test StateManager's onLogEvent and StateListener's onStateChanged correctly - * updates listener for states with multiple primary keys. - */ -TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) { - sp listener1 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1); - - // log event - std::unique_ptr event = CreateOverlayStateChangedEvent( - timestampNs, 1000 /* uid */, "package1", true /*using_alert_window*/, - OverlayStateChanged::ENTERED); - mgr.onLogEvent(*event); - - // check listener was updated - ASSERT_EQ(1, listener1->updates.size()); - EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); - EXPECT_EQ(1, listener1->updates[0].mState); - - // check StateTracker was updated by querying for state - HashableDimensionKey queryKey; - getOverlayKey(1000 /* uid */, "package1", &queryKey); - EXPECT_EQ(OverlayStateChanged::ENTERED, - getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey)); -} - -/** - * Test StateManager's onLogEvent and StateListener's onStateChanged - * when there is an error extracting state from log event. Listener is not - * updated of state change. - */ -TEST(StateTrackerTest, TestStateChangeEventError) { - sp listener1 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1); - - // log event - std::shared_ptr event1 = - buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */); - std::shared_ptr event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2"); - - // check listener was updated - mgr.onLogEvent(*event1); - ASSERT_EQ(0, listener1->updates.size()); - mgr.onLogEvent(*event2); - ASSERT_EQ(0, listener1->updates.size()); -} - -TEST(StateTrackerTest, TestStateQuery) { - sp listener1 = new TestStateListener(); - sp listener2 = new TestStateListener(); - sp listener3 = new TestStateListener(); - sp listener4 = new TestStateListener(); - StateManager mgr; - mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); - mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener2); - mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener3); - mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener4); - - std::unique_ptr event1 = CreateUidProcessStateChangedEvent( - timestampNs, 1000 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 - std::unique_ptr event2 = CreateUidProcessStateChangedEvent( - timestampNs + 1000, 1001 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: - // 1003 - std::unique_ptr event3 = CreateUidProcessStateChangedEvent( - timestampNs + 2000, 1002 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000 - std::unique_ptr event4 = CreateUidProcessStateChangedEvent( - timestampNs + 3000, 1001 /*uid*/, - android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 - std::unique_ptr event5 = CreateScreenStateChangedEvent( - timestampNs + 4000, android::view::DisplayStateEnum::DISPLAY_STATE_ON); - std::unique_ptr event6 = CreateOverlayStateChangedEvent( - timestampNs + 5000, 1000 /*uid*/, "package1", true /*using_alert_window*/, - OverlayStateChanged::ENTERED); - std::unique_ptr event7 = CreateOverlayStateChangedEvent( - timestampNs + 6000, 1000 /*uid*/, "package2", true /*using_alert_window*/, - OverlayStateChanged::EXITED); - - std::vector attributionUids = {1005}; - std::vector attributionTags = {"tag"}; - - std::unique_ptr event8 = CreateAcquireWakelockEvent( - timestampNs + 7000, attributionUids, attributionTags, "wakelock1"); - std::unique_ptr event9 = CreateReleaseWakelockEvent( - timestampNs + 8000, attributionUids, attributionTags, "wakelock2"); - - mgr.onLogEvent(*event1); - mgr.onLogEvent(*event2); - mgr.onLogEvent(*event3); - mgr.onLogEvent(*event5); - mgr.onLogEvent(*event5); - mgr.onLogEvent(*event6); - mgr.onLogEvent(*event7); - mgr.onLogEvent(*event8); - mgr.onLogEvent(*event9); - - // Query for UidProcessState of uid 1001 - HashableDimensionKey queryKey1; - getUidProcessKey(1001, &queryKey1); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, - getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1)); - - // Query for UidProcessState of uid 1004 - not in state map - HashableDimensionKey queryKey2; - getUidProcessKey(1004, &queryKey2); - EXPECT_EQ(-1, getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, - queryKey2)); // default state - - // Query for UidProcessState of uid 1001 - after change in state - mgr.onLogEvent(*event4); - EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP, - getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1)); - - // Query for ScreenState - EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - getStateInt(mgr, util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY)); - - // Query for OverlayState of uid 1000, package name "package2" - HashableDimensionKey queryKey3; - getOverlayKey(1000, "package2", &queryKey3); - EXPECT_EQ(OverlayStateChanged::EXITED, - getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey3)); - - // Query for WakelockState of uid 1005, tag 2 - HashableDimensionKey queryKey4; - getPartialWakelockKey(1005, "wakelock2", &queryKey4); - EXPECT_EQ(WakelockStateChanged::RELEASE, - getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey4)); - - // Query for WakelockState of uid 1005, tag 1 - HashableDimensionKey queryKey5; - getPartialWakelockKey(1005, "wakelock1", &queryKey5); - EXPECT_EQ(WakelockStateChanged::ACQUIRE, - getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5)); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp deleted file mode 100644 index cee83725d075..000000000000 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ /dev/null @@ -1,1393 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "statsd_test_util.h" - -#include -#include "stats_event.h" - -using aidl::android::util::StatsEventParcel; -using std::shared_ptr; - -namespace android { -namespace os { -namespace statsd { - -StatsLogReport outputStreamToProto(ProtoOutputStream* proto) { - vector bytes; - bytes.resize(proto->size()); - size_t pos = 0; - sp reader = proto->data(); - - while (reader->readBuffer() != NULL) { - size_t toRead = reader->currentToRead(); - std::memcpy(&((bytes)[pos]), reader->readBuffer(), toRead); - pos += toRead; - reader->move(toRead); - } - - StatsLogReport report; - report.ParseFromArray(bytes.data(), bytes.size()); - return report; -} - -AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(atomId); - return atom_matcher; -} - -AtomMatcher CreateTemperatureAtomMatcher() { - return CreateSimpleAtomMatcher("TemperatureMatcher", util::TEMPERATURE); -} - -AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name, - ScheduledJobStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::SCHEDULED_JOB_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(3); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateStartScheduledJobAtomMatcher() { - return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart", - ScheduledJobStateChanged::STARTED); -} - -AtomMatcher CreateFinishScheduledJobAtomMatcher() { - return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish", - ScheduledJobStateChanged::FINISHED); -} - -AtomMatcher CreateScreenBrightnessChangedAtomMatcher() { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId("ScreenBrightnessChanged")); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::SCREEN_BRIGHTNESS_CHANGED); - return atom_matcher; -} - -AtomMatcher CreateUidProcessStateChangedAtomMatcher() { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId("UidProcessStateChanged")); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::UID_PROCESS_STATE_CHANGED); - return atom_matcher; -} - -AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name, - WakelockStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::WAKELOCK_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(4); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateAcquireWakelockAtomMatcher() { - return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE); -} - -AtomMatcher CreateReleaseWakelockAtomMatcher() { - return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE); -} - -AtomMatcher CreateBatterySaverModeStateChangedAtomMatcher( - const string& name, BatterySaverModeStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(1); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateBatterySaverModeStartAtomMatcher() { - return CreateBatterySaverModeStateChangedAtomMatcher( - "BatterySaverModeStart", BatterySaverModeStateChanged::ON); -} - - -AtomMatcher CreateBatterySaverModeStopAtomMatcher() { - return CreateBatterySaverModeStateChangedAtomMatcher( - "BatterySaverModeStop", BatterySaverModeStateChanged::OFF); -} - -AtomMatcher CreateBatteryStateChangedAtomMatcher(const string& name, - BatteryPluggedStateEnum state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::PLUGGED_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(1); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateBatteryStateNoneMatcher() { - return CreateBatteryStateChangedAtomMatcher("BatteryPluggedNone", - BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE); -} - -AtomMatcher CreateBatteryStateUsbMatcher() { - return CreateBatteryStateChangedAtomMatcher("BatteryPluggedUsb", - BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); -} - -AtomMatcher CreateScreenStateChangedAtomMatcher( - const string& name, android::view::DisplayStateEnum state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::SCREEN_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(1); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateScreenTurnedOnAtomMatcher() { - return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", - android::view::DisplayStateEnum::DISPLAY_STATE_ON); -} - -AtomMatcher CreateScreenTurnedOffAtomMatcher() { - return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff", - ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF); -} - -AtomMatcher CreateSyncStateChangedAtomMatcher( - const string& name, SyncStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::SYNC_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(3); // State field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateSyncStartAtomMatcher() { - return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON); -} - -AtomMatcher CreateSyncEndAtomMatcher() { - return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF); -} - -AtomMatcher CreateActivityForegroundStateChangedAtomMatcher( - const string& name, ActivityForegroundStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::ACTIVITY_FOREGROUND_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(4); // Activity field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateMoveToBackgroundAtomMatcher() { - return CreateActivityForegroundStateChangedAtomMatcher( - "Background", ActivityForegroundStateChanged::BACKGROUND); -} - -AtomMatcher CreateMoveToForegroundAtomMatcher() { - return CreateActivityForegroundStateChangedAtomMatcher( - "Foreground", ActivityForegroundStateChanged::FOREGROUND); -} - -AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher( - const string& name, ProcessLifeCycleStateChanged::State state) { - AtomMatcher atom_matcher; - atom_matcher.set_id(StringToId(name)); - auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); - simple_atom_matcher->set_atom_id(util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); - field_value_matcher->set_field(3); // Process state field. - field_value_matcher->set_eq_int(state); - return atom_matcher; -} - -AtomMatcher CreateProcessCrashAtomMatcher() { - return CreateProcessLifeCycleStateChangedAtomMatcher( - "Crashed", ProcessLifeCycleStateChanged::CRASHED); -} - -Predicate CreateScheduledJobPredicate() { - Predicate predicate; - predicate.set_id(StringToId("ScheduledJobRunningPredicate")); - predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish")); - return predicate; -} - -Predicate CreateBatterySaverModePredicate() { - Predicate predicate; - predicate.set_id(StringToId("BatterySaverIsOn")); - predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop")); - return predicate; -} - -Predicate CreateDeviceUnpluggedPredicate() { - Predicate predicate; - predicate.set_id(StringToId("DeviceUnplugged")); - predicate.mutable_simple_predicate()->set_start(StringToId("BatteryPluggedNone")); - predicate.mutable_simple_predicate()->set_stop(StringToId("BatteryPluggedUsb")); - return predicate; -} - -Predicate CreateScreenIsOnPredicate() { - Predicate predicate; - predicate.set_id(StringToId("ScreenIsOn")); - predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff")); - return predicate; -} - -Predicate CreateScreenIsOffPredicate() { - Predicate predicate; - predicate.set_id(1111123); - predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn")); - return predicate; -} - -Predicate CreateHoldingWakelockPredicate() { - Predicate predicate; - predicate.set_id(StringToId("HoldingWakelock")); - predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock")); - predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock")); - return predicate; -} - -Predicate CreateIsSyncingPredicate() { - Predicate predicate; - predicate.set_id(33333333333333); - predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart")); - predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd")); - return predicate; -} - -Predicate CreateIsInBackgroundPredicate() { - Predicate predicate; - predicate.set_id(StringToId("IsInBackground")); - predicate.mutable_simple_predicate()->set_start(StringToId("Background")); - predicate.mutable_simple_predicate()->set_stop(StringToId("Foreground")); - return predicate; -} - -State CreateScreenState() { - State state; - state.set_id(StringToId("ScreenState")); - state.set_atom_id(util::SCREEN_STATE_CHANGED); - return state; -} - -State CreateUidProcessState() { - State state; - state.set_id(StringToId("UidProcessState")); - state.set_atom_id(util::UID_PROCESS_STATE_CHANGED); - return state; -} - -State CreateOverlayState() { - State state; - state.set_id(StringToId("OverlayState")); - state.set_atom_id(util::OVERLAY_STATE_CHANGED); - return state; -} - -State CreateScreenStateWithOnOffMap(int64_t screenOnId, int64_t screenOffId) { - State state; - state.set_id(StringToId("ScreenStateOnOff")); - state.set_atom_id(util::SCREEN_STATE_CHANGED); - - auto map = CreateScreenStateOnOffMap(screenOnId, screenOffId); - *state.mutable_map() = map; - - return state; -} - -State CreateScreenStateWithSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId) { - State state; - state.set_id(StringToId("ScreenStateSimpleOnOff")); - state.set_atom_id(util::SCREEN_STATE_CHANGED); - - auto map = CreateScreenStateSimpleOnOffMap(screenOnId, screenOffId); - *state.mutable_map() = map; - - return state; -} - -StateMap_StateGroup CreateScreenStateOnGroup(int64_t screenOnId) { - StateMap_StateGroup group; - group.set_group_id(screenOnId); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_VR); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND); - return group; -} - -StateMap_StateGroup CreateScreenStateOffGroup(int64_t screenOffId) { - StateMap_StateGroup group; - group.set_group_id(screenOffId); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND); - return group; -} - -StateMap_StateGroup CreateScreenStateSimpleOnGroup(int64_t screenOnId) { - StateMap_StateGroup group; - group.set_group_id(screenOnId); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON); - return group; -} - -StateMap_StateGroup CreateScreenStateSimpleOffGroup(int64_t screenOffId) { - StateMap_StateGroup group; - group.set_group_id(screenOffId); - group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_OFF); - return group; -} - -StateMap CreateScreenStateOnOffMap(int64_t screenOnId, int64_t screenOffId) { - StateMap map; - *map.add_group() = CreateScreenStateOnGroup(screenOnId); - *map.add_group() = CreateScreenStateOffGroup(screenOffId); - return map; -} - -StateMap CreateScreenStateSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId) { - StateMap map; - *map.add_group() = CreateScreenStateSimpleOnGroup(screenOnId); - *map.add_group() = CreateScreenStateSimpleOffGroup(screenOffId); - return map; -} - -void addPredicateToPredicateCombination(const Predicate& predicate, - Predicate* combinationPredicate) { - combinationPredicate->mutable_combination()->add_predicate(predicate.id()); -} - -FieldMatcher CreateAttributionUidDimensions(const int atomId, - const std::vector& positions) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const auto position : positions) { - auto child = dimensions.add_child(); - child->set_field(1); - child->set_position(position); - child->add_child()->set_field(1); - } - return dimensions; -} - -FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, - const std::vector& positions) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const auto position : positions) { - auto child = dimensions.add_child(); - child->set_field(1); - child->set_position(position); - child->add_child()->set_field(1); - child->add_child()->set_field(2); - } - return dimensions; -} - -FieldMatcher CreateDimensions(const int atomId, const std::vector& fields) { - FieldMatcher dimensions; - dimensions.set_field(atomId); - for (const int field : fields) { - dimensions.add_child()->set_field(field); - } - return dimensions; -} - -FieldMatcher CreateAttributionUidAndOtherDimensions(const int atomId, - const std::vector& positions, - const std::vector& fields) { - FieldMatcher dimensions = CreateAttributionUidDimensions(atomId, positions); - - for (const int field : fields) { - dimensions.add_child()->set_field(field); - } - return dimensions; -} - -// START: get primary key functions -void getUidProcessKey(int uid, HashableDimensionKey* key) { - int pos1[] = {1, 0, 0}; - Field field1(27 /* atom id */, pos1, 0 /* depth */); - Value value1((int32_t)uid); - - key->addValue(FieldValue(field1, value1)); -} - -void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) { - int pos1[] = {1, 0, 0}; - int pos2[] = {2, 0, 0}; - - Field field1(59 /* atom id */, pos1, 0 /* depth */); - Field field2(59 /* atom id */, pos2, 0 /* depth */); - - Value value1((int32_t)uid); - Value value2(packageName); - - key->addValue(FieldValue(field1, value1)); - key->addValue(FieldValue(field2, value2)); -} - -void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key) { - int pos1[] = {1, 1, 1}; - int pos3[] = {2, 0, 0}; - int pos4[] = {3, 0, 0}; - - Field field1(10 /* atom id */, pos1, 2 /* depth */); - - Field field3(10 /* atom id */, pos3, 0 /* depth */); - Field field4(10 /* atom id */, pos4, 0 /* depth */); - - Value value1((int32_t)uid); - Value value3((int32_t)1 /*partial*/); - Value value4(tag); - - key->addValue(FieldValue(field1, value1)); - key->addValue(FieldValue(field3, value3)); - key->addValue(FieldValue(field4, value4)); -} - -void getPartialWakelockKey(int uid, HashableDimensionKey* key) { - int pos1[] = {1, 1, 1}; - int pos3[] = {2, 0, 0}; - - Field field1(10 /* atom id */, pos1, 2 /* depth */); - Field field3(10 /* atom id */, pos3, 0 /* depth */); - - Value value1((int32_t)uid); - Value value3((int32_t)1 /*partial*/); - - key->addValue(FieldValue(field1, value1)); - key->addValue(FieldValue(field3, value3)); -} -// END: get primary key functions - -void writeAttribution(AStatsEvent* statsEvent, const vector& attributionUids, - const vector& attributionTags) { - vector cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast(attributionUids.data()), - cTags.data(), attributionUids.size()); -} - -void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) { - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - - AStatsEvent_release(statsEvent); -} - -void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - AStatsEvent_writeInt32(statsEvent, value1); - AStatsEvent_writeInt32(statsEvent, value2); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -shared_ptr CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2) { - shared_ptr logEvent = std::make_shared(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2); - return logEvent; -} - -void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2, int32_t value3) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - AStatsEvent_writeInt32(statsEvent, value1); - AStatsEvent_writeInt32(statsEvent, value2); - AStatsEvent_writeInt32(statsEvent, value3); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -shared_ptr CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2, int32_t value3) { - shared_ptr logEvent = std::make_shared(/*uid=*/0, /*pid=*/0); - CreateThreeValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2, value3); - return logEvent; -} - -void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, - int32_t value) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - AStatsEvent_writeInt32(statsEvent, value); - AStatsEvent_writeInt32(statsEvent, value); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -shared_ptr CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) { - shared_ptr logEvent = std::make_shared(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(logEvent.get(), atomId, eventTimeNs, value); - return logEvent; -} - -void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - parseStatsEventToLogEvent(statsEvent, logEvent); -} - -shared_ptr CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) { - shared_ptr logEvent = std::make_shared(/*uid=*/0, /*pid=*/0); - CreateNoValuesLogEvent(logEvent.get(), atomId, eventTimeNs); - return logEvent; -} - -shared_ptr makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1, - int data2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true); - AStatsEvent_writeInt32(statsEvent, data1); - AStatsEvent_writeInt32(statsEvent, data2); - - shared_ptr logEvent = std::make_shared(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -shared_ptr makeAttributionLogEvent(int atomId, int64_t eventTimeNs, - const vector& uids, const vector& tags, - int data1, int data2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - writeAttribution(statsEvent, uids, tags); - AStatsEvent_writeInt32(statsEvent, data1); - AStatsEvent_writeInt32(statsEvent, data2); - - shared_ptr logEvent = std::make_shared(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -sp makeMockUidMapForOneHost(int hostUid, const vector& isolatedUids) { - sp uidMap = new NaggyMock(); - EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(ReturnArg<0>()); - for (const int isolatedUid : isolatedUids) { - EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); - } - - return uidMap; -} - -sp makeMockUidMapForPackage(const string& pkg, const set& uids) { - sp uidMap = new StrictMock(); - EXPECT_CALL(*uidMap, getAppUid(_)).Times(AnyNumber()); - EXPECT_CALL(*uidMap, getAppUid(pkg)).WillRepeatedly(Return(uids)); - - return uidMap; -} - -std::unique_ptr CreateScreenStateChangedEvent(uint64_t timestampNs, - const android::view::DisplayStateEnum state, - int loggerUid) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - - std::unique_ptr logEvent = std::make_unique(loggerUid, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateBatterySaverOnEvent(uint64_t timestampNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateBatterySaverOffEvent(uint64_t timestampNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::PLUGGED_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SCREEN_BRIGHTNESS_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, level); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateScheduledJobStateChangedEvent( - const vector& attributionUids, const vector& attributionTags, - const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, jobName.c_str()); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateStartScheduledJobEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& jobName) { - return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, - ScheduledJobStateChanged::STARTED, timestampNs); -} - -// Create log event when scheduled job finishes. -std::unique_ptr CreateFinishScheduledJobEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& jobName) { - return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, - ScheduledJobStateChanged::FINISHED, timestampNs); -} - -std::unique_ptr CreateWakelockStateChangedEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& wakelockName, - const WakelockStateChanged::State state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::WAKELOCK_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true); - AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeString(statsEvent, wakelockName.c_str()); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateAcquireWakelockEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& wakelockName) { - return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags, - wakelockName, WakelockStateChanged::ACQUIRE); -} - -std::unique_ptr CreateReleaseWakelockEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& wakelockName) { - return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags, - wakelockName, WakelockStateChanged::RELEASE); -} - -std::unique_ptr CreateActivityForegroundStateChangedEvent( - uint64_t timestampNs, const int uid, const ActivityForegroundStateChanged::State state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::ACTIVITY_FOREGROUND_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeString(statsEvent, "pkg_name"); - AStatsEvent_writeString(statsEvent, "class_name"); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid) { - return CreateActivityForegroundStateChangedEvent(timestampNs, uid, - ActivityForegroundStateChanged::BACKGROUND); -} - -std::unique_ptr CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid) { - return CreateActivityForegroundStateChangedEvent(timestampNs, uid, - ActivityForegroundStateChanged::FOREGROUND); -} - -std::unique_ptr CreateSyncStateChangedEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& name, - const SyncStateChanged::State state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_writeString(statsEvent, name.c_str()); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateSyncStartEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& name) { - return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, - SyncStateChanged::ON); -} - -std::unique_ptr CreateSyncEndEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& name) { - return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, - SyncStateChanged::OFF); -} - -std::unique_ptr CreateProcessLifeCycleStateChangedEvent( - uint64_t timestampNs, const int uid, const ProcessLifeCycleStateChanged::State state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeString(statsEvent, ""); - AStatsEvent_writeInt32(statsEvent, state); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateAppCrashEvent(uint64_t timestampNs, const int uid) { - return CreateProcessLifeCycleStateChangedEvent(timestampNs, uid, - ProcessLifeCycleStateChanged::CRASHED); -} - -std::unique_ptr CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::APP_CRASH_OCCURRED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_writeString(statsEvent, "eventType"); - AStatsEvent_writeString(statsEvent, "processName"); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid, - int isolatedUid, bool is_create) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::ISOLATED_UID_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, hostUid); - AStatsEvent_writeInt32(statsEvent, isolatedUid); - AStatsEvent_writeInt32(statsEvent, is_create); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateUidProcessStateChangedEvent( - uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::UID_PROCESS_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateBleScanStateChangedEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const BleScanStateChanged::State state, - const bool filtered, const bool firstMatch, - const bool opportunistic) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - writeAttribution(statsEvent, attributionUids, attributionTags); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true); - if (state == util::BLE_SCAN_STATE_CHANGED__STATE__RESET) { - AStatsEvent_addInt32Annotation(statsEvent, ANNOTATION_ID_TRIGGER_STATE_RESET, - util::BLE_SCAN_STATE_CHANGED__STATE__OFF); - } - AStatsEvent_writeBool(statsEvent, filtered); // filtered - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeBool(statsEvent, firstMatch); // first match - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeBool(statsEvent, opportunistic); // opportunistic - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -std::unique_ptr CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid, - const string& packageName, - const bool usingAlertWindow, - const OverlayStateChanged::State state) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeString(statsEvent, packageName.c_str()); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true); - AStatsEvent_writeBool(statsEvent, usingAlertWindow); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true); - AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false); - - std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; -} - -sp CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs, - const StatsdConfig& config, const ConfigKey& key, - const shared_ptr& puller, - const int32_t atomTag, const sp uidMap) { - sp pullerManager = new StatsPullerManager(); - if (puller != nullptr) { - pullerManager->RegisterPullAtomCallback(/*uid=*/0, atomTag, NS_PER_SEC, NS_PER_SEC * 10, {}, - puller); - } - sp anomalyAlarmMonitor = - new AlarmMonitor(1, - [](const shared_ptr&, int64_t){}, - [](const shared_ptr&){}); - sp periodicAlarmMonitor = - new AlarmMonitor(1, - [](const shared_ptr&, int64_t){}, - [](const shared_ptr&){}); - sp processor = - new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, [](const ConfigKey&) { return true; }, - [](const int&, const vector&) {return true;}); - processor->OnConfigUpdated(currentTimeNs, key, config); - return processor; -} - -void sortLogEventsByTimestamp(std::vector> *events) { - std::sort(events->begin(), events->end(), - [](const std::unique_ptr& a, const std::unique_ptr& b) { - return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs(); - }); -} - -int64_t StringToId(const string& str) { - return static_cast(std::hash()(str)); -} - -void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId, - const int uid, const string& tag) { - EXPECT_EQ(value.field(), atomId); - ASSERT_EQ(value.value_tuple().dimensions_value_size(), 2); - // Attribution field. - EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1); - // Uid field. - ASSERT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), - uid); - // Tag field. - EXPECT_EQ(value.value_tuple().dimensions_value(1).field(), 3); - EXPECT_EQ(value.value_tuple().dimensions_value(1).value_str(), tag); -} - -void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) { - EXPECT_EQ(value.field(), atomId); - ASSERT_EQ(value.value_tuple().dimensions_value_size(), 1); - // Attribution field. - EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1); - // Uid only. - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(0).value_int(), uid); -} - -void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid) { - EXPECT_EQ(value.field(), atomId); - ASSERT_GT(value.value_tuple().dimensions_value_size(), node_idx); - // Attribution field. - EXPECT_EQ(value.value_tuple().dimensions_value(node_idx).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(0).value_int(), uid); -} - -void ValidateAttributionUidAndTagDimension( - const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag) { - EXPECT_EQ(value.field(), atomId); - ASSERT_GT(value.value_tuple().dimensions_value_size(), node_idx); - // Attribution field. - EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx).field()); - // Uid only. - EXPECT_EQ(2, value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value_size()); - EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(0).field()); - EXPECT_EQ(uid, value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(0).value_int()); - EXPECT_EQ(2, value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(1).field()); - EXPECT_EQ(tag, value.value_tuple().dimensions_value(node_idx) - .value_tuple().dimensions_value(1).value_str()); -} - -void ValidateAttributionUidAndTagDimension( - const DimensionsValue& value, int atomId, int uid, const std::string& tag) { - EXPECT_EQ(value.field(), atomId); - ASSERT_EQ(1, value.value_tuple().dimensions_value_size()); - // Attribution field. - EXPECT_EQ(1, value.value_tuple().dimensions_value(0).field()); - // Uid only. - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value_size(), 2); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(0).value_int(), uid); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(1).field(), 2); - EXPECT_EQ(value.value_tuple().dimensions_value(0) - .value_tuple().dimensions_value(1).value_str(), tag); -} - -bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) { - if (s1.field() != s2.field()) { - return false; - } - if (s1.value_case() != s2.value_case()) { - return false; - } - switch (s1.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return (s1.value_str() == s2.value_str()); - case DimensionsValue::ValueCase::kValueInt: - return s1.value_int() == s2.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return s1.value_long() == s2.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return s1.value_bool() == s2.value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return s1.value_float() == s2.value_float(); - case DimensionsValue::ValueCase::kValueTuple: { - if (s1.value_tuple().dimensions_value_size() != - s2.value_tuple().dimensions_value_size()) { - return false; - } - bool allMatched = true; - for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) { - allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i)); - } - return allMatched; - } - case DimensionsValue::ValueCase::VALUE_NOT_SET: - default: - return true; - } -} - -bool LessThan(const google::protobuf::RepeatedPtrField& s1, - const google::protobuf::RepeatedPtrField& s2) { - if (s1.size() != s2.size()) { - return s1.size() < s2.size(); - } - for (int i = 0; i < s1.size(); i++) { - const StateValue& state1 = s1[i]; - const StateValue& state2 = s2[i]; - if (state1.atom_id() != state2.atom_id()) { - return state1.atom_id() < state2.atom_id(); - } - if (state1.value() != state2.value()) { - return state1.value() < state2.value(); - } - if (state1.group_id() != state2.group_id()) { - return state1.group_id() < state2.group_id(); - } - } - return false; -} - -bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) { - if (s1.field() != s2.field()) { - return s1.field() < s2.field(); - } - if (s1.value_case() != s2.value_case()) { - return s1.value_case() < s2.value_case(); - } - switch (s1.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return s1.value_str() < s2.value_str(); - case DimensionsValue::ValueCase::kValueInt: - return s1.value_int() < s2.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return s1.value_long() < s2.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return (int)s1.value_bool() < (int)s2.value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return s1.value_float() < s2.value_float(); - case DimensionsValue::ValueCase::kValueTuple: { - if (s1.value_tuple().dimensions_value_size() != - s2.value_tuple().dimensions_value_size()) { - return s1.value_tuple().dimensions_value_size() < - s2.value_tuple().dimensions_value_size(); - } - for (int i = 0; i < s1.value_tuple().dimensions_value_size(); ++i) { - if (EqualsTo(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i))) { - continue; - } else { - return LessThan(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i)); - } - } - return false; - } - case DimensionsValue::ValueCase::VALUE_NOT_SET: - default: - return false; - } -} - -bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2) { - if (LessThan(s1.dimInWhat, s2.dimInWhat)) { - return true; - } else if (LessThan(s2.dimInWhat, s1.dimInWhat)) { - return false; - } - - return LessThan(s1.stateValues, s2.stateValues); -} - -void backfillStringInDimension(const std::map& str_map, - DimensionsValue* dimension) { - if (dimension->has_value_str_hash()) { - auto it = str_map.find((uint64_t)(dimension->value_str_hash())); - if (it != str_map.end()) { - dimension->clear_value_str_hash(); - dimension->set_value_str(it->second); - } else { - ALOGE("Can not find the string hash: %llu", - (unsigned long long)dimension->value_str_hash()); - } - } else if (dimension->has_value_tuple()) { - auto value_tuple = dimension->mutable_value_tuple(); - for (int i = 0; i < value_tuple->dimensions_value_size(); ++i) { - backfillStringInDimension(str_map, value_tuple->mutable_dimensions_value(i)); - } - } -} - -void backfillStringInReport(ConfigMetricsReport *config_report) { - std::map str_map; - for (const auto& str : config_report->strings()) { - uint64_t hash = Hash64(str); - if (str_map.find(hash) != str_map.end()) { - ALOGE("String hash conflicts: %s %s", str.c_str(), str_map[hash].c_str()); - } - str_map[hash] = str; - } - for (int i = 0; i < config_report->metrics_size(); ++i) { - auto metric_report = config_report->mutable_metrics(i); - if (metric_report->has_count_metrics()) { - backfillStringInDimension(str_map, metric_report->mutable_count_metrics()); - } else if (metric_report->has_duration_metrics()) { - backfillStringInDimension(str_map, metric_report->mutable_duration_metrics()); - } else if (metric_report->has_gauge_metrics()) { - backfillStringInDimension(str_map, metric_report->mutable_gauge_metrics()); - } else if (metric_report->has_value_metrics()) { - backfillStringInDimension(str_map, metric_report->mutable_value_metrics()); - } - } - // Backfill the package names. - for (int i = 0 ; i < config_report->uid_map().snapshots_size(); ++i) { - auto snapshot = config_report->mutable_uid_map()->mutable_snapshots(i); - for (int j = 0 ; j < snapshot->package_info_size(); ++j) { - auto package_info = snapshot->mutable_package_info(j); - if (package_info->has_name_hash()) { - auto it = str_map.find((uint64_t)(package_info->name_hash())); - if (it != str_map.end()) { - package_info->clear_name_hash(); - package_info->set_name(it->second); - } else { - ALOGE("Can not find the string package name hash: %llu", - (unsigned long long)package_info->name_hash()); - } - - } - } - } - // Backfill the app name in app changes. - for (int i = 0 ; i < config_report->uid_map().changes_size(); ++i) { - auto change = config_report->mutable_uid_map()->mutable_changes(i); - if (change->has_app_hash()) { - auto it = str_map.find((uint64_t)(change->app_hash())); - if (it != str_map.end()) { - change->clear_app_hash(); - change->set_app(it->second); - } else { - ALOGE("Can not find the string change app name hash: %llu", - (unsigned long long)change->app_hash()); - } - } - } -} - -void backfillStringInReport(ConfigMetricsReportList *config_report_list) { - for (int i = 0; i < config_report_list->reports_size(); ++i) { - backfillStringInReport(config_report_list->mutable_reports(i)); - } -} - -bool backfillDimensionPath(const DimensionsValue& path, - const google::protobuf::RepeatedPtrField& leafValues, - int* leafIndex, - DimensionsValue* dimension) { - dimension->set_field(path.field()); - if (path.has_value_tuple()) { - for (int i = 0; i < path.value_tuple().dimensions_value_size(); ++i) { - if (!backfillDimensionPath( - path.value_tuple().dimensions_value(i), leafValues, leafIndex, - dimension->mutable_value_tuple()->add_dimensions_value())) { - return false; - } - } - } else { - if (*leafIndex < 0 || *leafIndex >= leafValues.size()) { - return false; - } - dimension->MergeFrom(leafValues.Get(*leafIndex)); - (*leafIndex)++; - } - return true; -} - -bool backfillDimensionPath(const DimensionsValue& path, - const google::protobuf::RepeatedPtrField& leafValues, - DimensionsValue* dimension) { - int leafIndex = 0; - return backfillDimensionPath(path, leafValues, &leafIndex, dimension); -} - -void backfillDimensionPath(ConfigMetricsReportList *config_report_list) { - for (int i = 0; i < config_report_list->reports_size(); ++i) { - auto report = config_report_list->mutable_reports(i); - for (int j = 0; j < report->metrics_size(); ++j) { - auto metric_report = report->mutable_metrics(j); - if (metric_report->has_dimensions_path_in_what() || - metric_report->has_dimensions_path_in_condition()) { - auto whatPath = metric_report->dimensions_path_in_what(); - auto conditionPath = metric_report->dimensions_path_in_condition(); - if (metric_report->has_count_metrics()) { - backfillDimensionPath(whatPath, conditionPath, - metric_report->mutable_count_metrics()); - } else if (metric_report->has_duration_metrics()) { - backfillDimensionPath(whatPath, conditionPath, - metric_report->mutable_duration_metrics()); - } else if (metric_report->has_gauge_metrics()) { - backfillDimensionPath(whatPath, conditionPath, - metric_report->mutable_gauge_metrics()); - } else if (metric_report->has_value_metrics()) { - backfillDimensionPath(whatPath, conditionPath, - metric_report->mutable_value_metrics()); - } - metric_report->clear_dimensions_path_in_what(); - metric_report->clear_dimensions_path_in_condition(); - } - } - } -} - -void backfillStartEndTimestamp(StatsLogReport *report) { - const int64_t timeBaseNs = report->time_base_elapsed_nano_seconds(); - const int64_t bucketSizeNs = report->bucket_size_nano_seconds(); - if (report->has_count_metrics()) { - backfillStartEndTimestampForMetrics( - timeBaseNs, bucketSizeNs, report->mutable_count_metrics()); - } else if (report->has_duration_metrics()) { - backfillStartEndTimestampForMetrics( - timeBaseNs, bucketSizeNs, report->mutable_duration_metrics()); - } else if (report->has_gauge_metrics()) { - backfillStartEndTimestampForMetrics( - timeBaseNs, bucketSizeNs, report->mutable_gauge_metrics()); - if (report->gauge_metrics().skipped_size() > 0) { - backfillStartEndTimestampForSkippedBuckets( - timeBaseNs, report->mutable_gauge_metrics()); - } - } else if (report->has_value_metrics()) { - backfillStartEndTimestampForMetrics( - timeBaseNs, bucketSizeNs, report->mutable_value_metrics()); - if (report->value_metrics().skipped_size() > 0) { - backfillStartEndTimestampForSkippedBuckets( - timeBaseNs, report->mutable_value_metrics()); - } - } -} - -void backfillStartEndTimestamp(ConfigMetricsReport *config_report) { - for (int j = 0; j < config_report->metrics_size(); ++j) { - backfillStartEndTimestamp(config_report->mutable_metrics(j)); - } -} - -void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list) { - for (int i = 0; i < config_report_list->reports_size(); ++i) { - backfillStartEndTimestamp(config_report_list->mutable_reports(i)); - } -} - -Status FakeSubsystemSleepCallback::onPullAtom(int atomTag, - const shared_ptr& resultReceiver) { - // Convert stats_events into StatsEventParcels. - std::vector parcels; - for (int i = 1; i < 3; i++) { - AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, atomTag); - std::string subsystemName = "subsystem_name_"; - subsystemName = subsystemName + std::to_string(i); - AStatsEvent_writeString(event, subsystemName.c_str()); - AStatsEvent_writeString(event, "subsystem_subname foo"); - AStatsEvent_writeInt64(event, /*count= */ i); - AStatsEvent_writeInt64(event, /*time_millis= */ i * 100); - AStatsEvent_build(event); - size_t size; - uint8_t* buffer = AStatsEvent_getBuffer(event, &size); - - StatsEventParcel p; - // vector.assign() creates a copy, but this is inevitable unless - // stats_event.h/c uses a vector as opposed to a buffer. - p.buffer.assign(buffer, buffer + size); - parcels.push_back(std::move(p)); - AStatsEvent_release(event); - } - resultReceiver->pullFinished(atomTag, /*success=*/true, parcels); - return Status::ok(); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h deleted file mode 100644 index 3dcf4ecce054..000000000000 --- a/cmds/statsd/tests/statsd_test_util.h +++ /dev/null @@ -1,479 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "src/StatsLogProcessor.h" -#include "src/hash.h" -#include "src/logd/LogEvent.h" -#include "src/packages/UidMap.h" -#include "src/stats_log_util.h" -#include "stats_event.h" -#include "statslog_statsdtest.h" - -namespace android { -namespace os { -namespace statsd { - -using namespace testing; -using ::aidl::android::os::BnPullAtomCallback; -using ::aidl::android::os::IPullAtomCallback; -using ::aidl::android::os::IPullAtomResultReceiver; -using android::util::ProtoReader; -using google::protobuf::RepeatedPtrField; -using Status = ::ndk::ScopedAStatus; - -const int SCREEN_STATE_ATOM_ID = util::SCREEN_STATE_CHANGED; -const int UID_PROCESS_STATE_ATOM_ID = util::UID_PROCESS_STATE_CHANGED; - -enum BucketSplitEvent { APP_UPGRADE, BOOT_COMPLETE }; - -class MockUidMap : public UidMap { -public: - MOCK_METHOD(int, getHostUidOrSelf, (int uid), (const)); - MOCK_METHOD(std::set, getAppUid, (const string& package), (const)); -}; - -// Converts a ProtoOutputStream to a StatsLogReport proto. -StatsLogReport outputStreamToProto(ProtoOutputStream* proto); - -// Create AtomMatcher proto to simply match a specific atom type. -AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId); - -// Create AtomMatcher proto for temperature atom. -AtomMatcher CreateTemperatureAtomMatcher(); - -// Create AtomMatcher proto for scheduled job state changed. -AtomMatcher CreateScheduledJobStateChangedAtomMatcher(); - -// Create AtomMatcher proto for starting a scheduled job. -AtomMatcher CreateStartScheduledJobAtomMatcher(); - -// Create AtomMatcher proto for a scheduled job is done. -AtomMatcher CreateFinishScheduledJobAtomMatcher(); - -// Create AtomMatcher proto for screen brightness state changed. -AtomMatcher CreateScreenBrightnessChangedAtomMatcher(); - -// Create AtomMatcher proto for starting battery save mode. -AtomMatcher CreateBatterySaverModeStartAtomMatcher(); - -// Create AtomMatcher proto for stopping battery save mode. -AtomMatcher CreateBatterySaverModeStopAtomMatcher(); - -// Create AtomMatcher proto for battery state none mode. -AtomMatcher CreateBatteryStateNoneMatcher(); - -// Create AtomMatcher proto for battery state usb mode. -AtomMatcher CreateBatteryStateUsbMatcher(); - -// Create AtomMatcher proto for process state changed. -AtomMatcher CreateUidProcessStateChangedAtomMatcher(); - -// Create AtomMatcher proto for acquiring wakelock. -AtomMatcher CreateAcquireWakelockAtomMatcher(); - -// Create AtomMatcher proto for releasing wakelock. -AtomMatcher CreateReleaseWakelockAtomMatcher() ; - -// Create AtomMatcher proto for screen turned on. -AtomMatcher CreateScreenTurnedOnAtomMatcher(); - -// Create AtomMatcher proto for screen turned off. -AtomMatcher CreateScreenTurnedOffAtomMatcher(); - -// Create AtomMatcher proto for app sync turned on. -AtomMatcher CreateSyncStartAtomMatcher(); - -// Create AtomMatcher proto for app sync turned off. -AtomMatcher CreateSyncEndAtomMatcher(); - -// Create AtomMatcher proto for app sync moves to background. -AtomMatcher CreateMoveToBackgroundAtomMatcher(); - -// Create AtomMatcher proto for app sync moves to foreground. -AtomMatcher CreateMoveToForegroundAtomMatcher(); - -// Create AtomMatcher proto for process crashes -AtomMatcher CreateProcessCrashAtomMatcher() ; - -// Create Predicate proto for screen is on. -Predicate CreateScreenIsOnPredicate(); - -// Create Predicate proto for screen is off. -Predicate CreateScreenIsOffPredicate(); - -// Create Predicate proto for a running scheduled job. -Predicate CreateScheduledJobPredicate(); - -// Create Predicate proto for battery saver mode. -Predicate CreateBatterySaverModePredicate(); - -// Create Predicate proto for device unplogged mode. -Predicate CreateDeviceUnpluggedPredicate(); - -// Create Predicate proto for holding wakelock. -Predicate CreateHoldingWakelockPredicate(); - -// Create a Predicate proto for app syncing. -Predicate CreateIsSyncingPredicate(); - -// Create a Predicate proto for app is in background. -Predicate CreateIsInBackgroundPredicate(); - -// Create State proto for screen state atom. -State CreateScreenState(); - -// Create State proto for uid process state atom. -State CreateUidProcessState(); - -// Create State proto for overlay state atom. -State CreateOverlayState(); - -// Create State proto for screen state atom with on/off map. -State CreateScreenStateWithOnOffMap(int64_t screenOnId, int64_t screenOffId); - -// Create State proto for screen state atom with simple on/off map. -State CreateScreenStateWithSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId); - -// Create StateGroup proto for ScreenState ON group -StateMap_StateGroup CreateScreenStateOnGroup(int64_t screenOnId); - -// Create StateGroup proto for ScreenState OFF group -StateMap_StateGroup CreateScreenStateOffGroup(int64_t screenOffId); - -// Create StateGroup proto for simple ScreenState ON group -StateMap_StateGroup CreateScreenStateSimpleOnGroup(int64_t screenOnId); - -// Create StateGroup proto for simple ScreenState OFF group -StateMap_StateGroup CreateScreenStateSimpleOffGroup(int64_t screenOffId); - -// Create StateMap proto for ScreenState ON/OFF map -StateMap CreateScreenStateOnOffMap(int64_t screenOnId, int64_t screenOffId); - -// Create StateMap proto for simple ScreenState ON/OFF map -StateMap CreateScreenStateSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId); - -// Add a predicate to the predicate combination. -void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination); - -// Create dimensions from primitive fields. -FieldMatcher CreateDimensions(const int atomId, const std::vector& fields); - -// Create dimensions by attribution uid and tag. -FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, - const std::vector& positions); - -// Create dimensions by attribution uid only. -FieldMatcher CreateAttributionUidDimensions(const int atomId, - const std::vector& positions); - -FieldMatcher CreateAttributionUidAndOtherDimensions(const int atomId, - const std::vector& positions, - const std::vector& fields); - -// START: get primary key functions -// These functions take in atom field information and create FieldValues which are stored in the -// given HashableDimensionKey. -void getUidProcessKey(int uid, HashableDimensionKey* key); - -void getOverlayKey(int uid, string packageName, HashableDimensionKey* key); - -void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key); - -void getPartialWakelockKey(int uid, HashableDimensionKey* key); -// END: get primary key functions - -void writeAttribution(AStatsEvent* statsEvent, const vector& attributionUids, - const vector& attributionTags); - -// Builds statsEvent to get buffer that is parsed into logEvent then releases statsEvent. -void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent); - -shared_ptr CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2); - -void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2); - -shared_ptr CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2, int32_t value3); - -void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2, int32_t value3); - -// The repeated value log event helpers create a log event with two int fields, both -// set to the same value. This is useful for testing metrics that are only interested -// in the value of the second field but still need the first field to be populated. -std::shared_ptr CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, - int32_t value); - -void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, - int32_t value); - -std::shared_ptr CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs); - -void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs); - -std::shared_ptr makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1, - int data2); - -std::shared_ptr makeAttributionLogEvent(int atomId, int64_t eventTimeNs, - const vector& uids, - const vector& tags, int data1, int data2); - -sp makeMockUidMapForOneHost(int hostUid, const vector& isolatedUids); - -sp makeMockUidMapForPackage(const string& pkg, const set& uids); - -// Create log event for screen state changed. -std::unique_ptr CreateScreenStateChangedEvent(uint64_t timestampNs, - const android::view::DisplayStateEnum state, - int loggerUid = 0); - -// Create log event for screen brightness state changed. -std::unique_ptr CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level); - -// Create log event when scheduled job starts. -std::unique_ptr CreateStartScheduledJobEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& jobName); - -// Create log event when scheduled job finishes. -std::unique_ptr CreateFinishScheduledJobEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const string& jobName); - -// Create log event when battery saver starts. -std::unique_ptr CreateBatterySaverOnEvent(uint64_t timestampNs); -// Create log event when battery saver stops. -std::unique_ptr CreateBatterySaverOffEvent(uint64_t timestampNs); - -// Create log event when battery state changes. -std::unique_ptr CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state); - -// Create log event for app moving to background. -std::unique_ptr CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid); - -// Create log event for app moving to foreground. -std::unique_ptr CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid); - -// Create log event when the app sync starts. -std::unique_ptr CreateSyncStartEvent(uint64_t timestampNs, const vector& uids, - const vector& tags, const string& name); - -// Create log event when the app sync ends. -std::unique_ptr CreateSyncEndEvent(uint64_t timestampNs, const vector& uids, - const vector& tags, const string& name); - -// Create log event when the app sync ends. -std::unique_ptr CreateAppCrashEvent(uint64_t timestampNs, const int uid); - -// Create log event for an app crash. -std::unique_ptr CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid); - -// Create log event for acquiring wakelock. -std::unique_ptr CreateAcquireWakelockEvent(uint64_t timestampNs, const vector& uids, - const vector& tags, - const string& wakelockName); - -// Create log event for releasing wakelock. -std::unique_ptr CreateReleaseWakelockEvent(uint64_t timestampNs, const vector& uids, - const vector& tags, - const string& wakelockName); - -// Create log event for releasing wakelock. -std::unique_ptr CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid, - int isolatedUid, bool is_create); - -// Create log event for uid process state change. -std::unique_ptr CreateUidProcessStateChangedEvent( - uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state); - -std::unique_ptr CreateBleScanStateChangedEvent(uint64_t timestampNs, - const vector& attributionUids, - const vector& attributionTags, - const BleScanStateChanged::State state, - const bool filtered, const bool firstMatch, - const bool opportunistic); - -std::unique_ptr CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid, - const string& packageName, - const bool usingAlertWindow, - const OverlayStateChanged::State state); - -// Create a statsd log event processor upon the start time in seconds, config and key. -sp CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs, - const StatsdConfig& config, const ConfigKey& key, - const shared_ptr& puller = nullptr, - const int32_t atomTag = 0 /*for puller only*/, - const sp = new UidMap()); - -// Util function to sort the log events by timestamp. -void sortLogEventsByTimestamp(std::vector> *events); - -int64_t StringToId(const string& str); - -void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId, - const int uid, const string& tag); -void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid); -void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid); -void ValidateAttributionUidAndTagDimension( - const DimensionsValue& value, int atomId, int uid, const std::string& tag); -void ValidateAttributionUidAndTagDimension( - const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag); - -struct DimensionsPair { - DimensionsPair(DimensionsValue m1, google::protobuf::RepeatedPtrField m2) - : dimInWhat(m1), stateValues(m2){}; - - DimensionsValue dimInWhat; - google::protobuf::RepeatedPtrField stateValues; -}; - -bool LessThan(const StateValue& s1, const StateValue& s2); -bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2); -bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2); - - -void backfillStartEndTimestamp(ConfigMetricsReport *config_report); -void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list); - -void backfillStringInReport(ConfigMetricsReportList *config_report_list); -void backfillStringInDimension(const std::map& str_map, - DimensionsValue* dimension); - -template -void backfillStringInDimension(const std::map& str_map, - T* metrics) { - for (int i = 0; i < metrics->data_size(); ++i) { - auto data = metrics->mutable_data(i); - if (data->has_dimensions_in_what()) { - backfillStringInDimension(str_map, data->mutable_dimensions_in_what()); - } - if (data->has_dimensions_in_condition()) { - backfillStringInDimension(str_map, data->mutable_dimensions_in_condition()); - } - } -} - -void backfillDimensionPath(ConfigMetricsReportList* config_report_list); - -bool backfillDimensionPath(const DimensionsValue& path, - const google::protobuf::RepeatedPtrField& leafValues, - DimensionsValue* dimension); - -class FakeSubsystemSleepCallback : public BnPullAtomCallback { -public: - Status onPullAtom(int atomTag, - const shared_ptr& resultReceiver) override; -}; - -template -void backfillDimensionPath(const DimensionsValue& whatPath, - const DimensionsValue& conditionPath, - T* metricData) { - for (int i = 0; i < metricData->data_size(); ++i) { - auto data = metricData->mutable_data(i); - if (data->dimension_leaf_values_in_what_size() > 0) { - backfillDimensionPath(whatPath, data->dimension_leaf_values_in_what(), - data->mutable_dimensions_in_what()); - data->clear_dimension_leaf_values_in_what(); - } - if (data->dimension_leaf_values_in_condition_size() > 0) { - backfillDimensionPath(conditionPath, data->dimension_leaf_values_in_condition(), - data->mutable_dimensions_in_condition()); - data->clear_dimension_leaf_values_in_condition(); - } - } -} - -struct DimensionCompare { - bool operator()(const DimensionsPair& s1, const DimensionsPair& s2) const { - return LessThan(s1, s2); - } -}; - -template -void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) { - std::map dimensionIndexMap; - for (int i = 0; i < metricData.data_size(); ++i) { - dimensionIndexMap.insert( - std::make_pair(DimensionsPair(metricData.data(i).dimensions_in_what(), - metricData.data(i).slice_by_state()), - i)); - } - for (const auto& itr : dimensionIndexMap) { - *sortedMetricData->add_data() = metricData.data(itr.second); - } -} - -template -void backfillStartEndTimestampForFullBucket( - const int64_t timeBaseNs, const int64_t bucketSizeNs, T* bucket) { - bucket->set_start_bucket_elapsed_nanos(timeBaseNs + bucketSizeNs * bucket->bucket_num()); - bucket->set_end_bucket_elapsed_nanos( - timeBaseNs + bucketSizeNs * bucket->bucket_num() + bucketSizeNs); - bucket->clear_bucket_num(); -} - -template -void backfillStartEndTimestampForPartialBucket(const int64_t timeBaseNs, T* bucket) { - if (bucket->has_start_bucket_elapsed_millis()) { - bucket->set_start_bucket_elapsed_nanos( - MillisToNano(bucket->start_bucket_elapsed_millis())); - bucket->clear_start_bucket_elapsed_millis(); - } - if (bucket->has_end_bucket_elapsed_millis()) { - bucket->set_end_bucket_elapsed_nanos( - MillisToNano(bucket->end_bucket_elapsed_millis())); - bucket->clear_end_bucket_elapsed_millis(); - } -} - -template -void backfillStartEndTimestampForMetrics(const int64_t timeBaseNs, const int64_t bucketSizeNs, - T* metrics) { - for (int i = 0; i < metrics->data_size(); ++i) { - auto data = metrics->mutable_data(i); - for (int j = 0; j < data->bucket_info_size(); ++j) { - auto bucket = data->mutable_bucket_info(j); - if (bucket->has_bucket_num()) { - backfillStartEndTimestampForFullBucket(timeBaseNs, bucketSizeNs, bucket); - } else { - backfillStartEndTimestampForPartialBucket(timeBaseNs, bucket); - } - } - } -} - -template -void backfillStartEndTimestampForSkippedBuckets(const int64_t timeBaseNs, T* metrics) { - for (int i = 0; i < metrics->skipped_size(); ++i) { - backfillStartEndTimestampForPartialBucket(timeBaseNs, metrics->mutable_skipped(i)); - } -} -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp deleted file mode 100644 index 74eafbf56d66..000000000000 --- a/cmds/statsd/tests/storage/StorageManager_test.cpp +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include "src/storage/StorageManager.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -using namespace testing; -using std::make_shared; -using std::shared_ptr; -using std::vector; -using testing::Contains; - -TEST(StorageManagerTest, TrainInfoReadWriteTest) { - InstallTrainInfo trainInfo; - trainInfo.trainVersionCode = 12345; - trainInfo.trainName = "This is a train name #)$(&&$"; - trainInfo.status = 1; - const char* expIds = "test_ids"; - trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds)); - - bool result; - - result = StorageManager::writeTrainInfo(trainInfo); - - EXPECT_TRUE(result); - - InstallTrainInfo trainInfoResult; - result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult); - EXPECT_TRUE(result); - - EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode); - ASSERT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size()); - EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName); - EXPECT_EQ(trainInfo.status, trainInfoResult.status); - ASSERT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size()); - EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds); -} - -TEST(StorageManagerTest, TrainInfoReadWriteTrainNameSizeOneTest) { - InstallTrainInfo trainInfo; - trainInfo.trainVersionCode = 12345; - trainInfo.trainName = "{"; - trainInfo.status = 1; - const char* expIds = "test_ids"; - trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds)); - - bool result; - - result = StorageManager::writeTrainInfo(trainInfo); - - EXPECT_TRUE(result); - - InstallTrainInfo trainInfoResult; - result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult); - EXPECT_TRUE(result); - - EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode); - ASSERT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size()); - EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName); - EXPECT_EQ(trainInfo.status, trainInfoResult.status); - ASSERT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size()); - EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds); -} - -TEST(StorageManagerTest, SortFileTest) { - vector list; - // assume now sec is 500 - list.emplace_back("200_5000_123454", false, 20, 300); - list.emplace_back("300_2000_123454_history", true, 30, 200); - list.emplace_back("400_100009_123454_history", true, 40, 100); - list.emplace_back("100_2000_123454", false, 50, 400); - - StorageManager::sortFiles(&list); - EXPECT_EQ("200_5000_123454", list[0].mFileName); - EXPECT_EQ("100_2000_123454", list[1].mFileName); - EXPECT_EQ("400_100009_123454_history", list[2].mFileName); - EXPECT_EQ("300_2000_123454_history", list[3].mFileName); -} - -const string testDir = "/data/misc/stats-data/"; -const string file1 = testDir + "2557169347_1066_1"; -const string file2 = testDir + "2557169349_1066_1"; -const string file1_history = file1 + "_history"; -const string file2_history = file2 + "_history"; - -bool prepareLocalHistoryTestFiles() { - android::base::unique_fd fd(TEMP_FAILURE_RETRY( - open(file1.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR))); - if (fd != -1) { - dprintf(fd, "content"); - } else { - return false; - } - - android::base::unique_fd fd2(TEMP_FAILURE_RETRY( - open(file2.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR))); - if (fd2 != -1) { - dprintf(fd2, "content"); - } else { - return false; - } - return true; -} - -void clearLocalHistoryTestFiles() { - TEMP_FAILURE_RETRY(remove(file1.c_str())); - TEMP_FAILURE_RETRY(remove(file2.c_str())); - TEMP_FAILURE_RETRY(remove(file1_history.c_str())); - TEMP_FAILURE_RETRY(remove(file2_history.c_str())); -} - -bool fileExist(string name) { - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY | O_CLOEXEC))); - return fd != -1; -} - -/* The following AppendConfigReportTests test the 4 combinations of [whether erase data] [whether - * the caller is adb] */ -TEST(StorageManagerTest, AppendConfigReportTest1) { - EXPECT_TRUE(prepareLocalHistoryTestFiles()); - - ProtoOutputStream out; - StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, false /*erase?*/, - false /*isAdb?*/); - - EXPECT_FALSE(fileExist(file1)); - EXPECT_FALSE(fileExist(file2)); - - EXPECT_TRUE(fileExist(file1_history)); - EXPECT_TRUE(fileExist(file2_history)); - clearLocalHistoryTestFiles(); -} - -TEST(StorageManagerTest, AppendConfigReportTest2) { - EXPECT_TRUE(prepareLocalHistoryTestFiles()); - - ProtoOutputStream out; - StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, true /*erase?*/, - false /*isAdb?*/); - - EXPECT_FALSE(fileExist(file1)); - EXPECT_FALSE(fileExist(file2)); - EXPECT_FALSE(fileExist(file1_history)); - EXPECT_FALSE(fileExist(file2_history)); - - clearLocalHistoryTestFiles(); -} - -TEST(StorageManagerTest, AppendConfigReportTest3) { - EXPECT_TRUE(prepareLocalHistoryTestFiles()); - - ProtoOutputStream out; - StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, false /*erase?*/, - true /*isAdb?*/); - - EXPECT_TRUE(fileExist(file1)); - EXPECT_TRUE(fileExist(file2)); - EXPECT_FALSE(fileExist(file1_history)); - EXPECT_FALSE(fileExist(file2_history)); - - clearLocalHistoryTestFiles(); -} - -TEST(StorageManagerTest, AppendConfigReportTest4) { - EXPECT_TRUE(prepareLocalHistoryTestFiles()); - - ProtoOutputStream out; - StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, true /*erase?*/, - true /*isAdb?*/); - - EXPECT_FALSE(fileExist(file1)); - EXPECT_FALSE(fileExist(file2)); - EXPECT_FALSE(fileExist(file1_history)); - EXPECT_FALSE(fileExist(file2_history)); - - clearLocalHistoryTestFiles(); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp deleted file mode 100644 index 32cecd3b9dbc..000000000000 --- a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "utils/MultiConditionTrigger.h" - -#include - -#include -#include -#include -#include - -#ifdef __ANDROID__ - -using namespace std; -using std::this_thread::sleep_for; - -namespace android { -namespace os { -namespace statsd { - -TEST(MultiConditionTrigger, TestMultipleConditions) { - int numConditions = 5; - string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5"; - set conditionNames = {t1, t2, t3, t4, t5}; - - mutex lock; - condition_variable cv; - bool triggerCalled = false; - - // Mark done as true and notify in the done. - MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] { - { - lock_guard lg(lock); - triggerCalled = true; - } - cv.notify_all(); - }); - - vector threads; - vector done(numConditions, 0); - - int i = 0; - for (const string& conditionName : conditionNames) { - threads.emplace_back([&done, &conditionName, &trigger, i] { - sleep_for(chrono::milliseconds(3)); - done[i] = 1; - trigger.markComplete(conditionName); - }); - i++; - } - - unique_lock unique_lk(lock); - cv.wait(unique_lk, [&triggerCalled] { - return triggerCalled; - }); - - for (i = 0; i < numConditions; i++) { - EXPECT_EQ(done[i], 1); - } - - for (i = 0; i < numConditions; i++) { - threads[i].join(); - } -} - -TEST(MultiConditionTrigger, TestNoConditions) { - mutex lock; - condition_variable cv; - bool triggerCalled = false; - - MultiConditionTrigger trigger({}, [&lock, &cv, &triggerCalled] { - { - lock_guard lg(lock); - triggerCalled = true; - } - cv.notify_all(); - }); - - unique_lock unique_lk(lock); - cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; }); - EXPECT_TRUE(triggerCalled); - // Ensure that trigger occurs immediately if no events need to be completed. -} - -TEST(MultiConditionTrigger, TestMarkCompleteCalledBySameCondition) { - string t1 = "t1", t2 = "t2"; - set conditionNames = {t1, t2}; - - mutex lock; - condition_variable cv; - bool triggerCalled = false; - - MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] { - { - lock_guard lg(lock); - triggerCalled = true; - } - cv.notify_all(); - }); - - trigger.markComplete(t1); - trigger.markComplete(t1); - - // Ensure that the trigger still hasn't fired. - { - lock_guard lg(lock); - EXPECT_FALSE(triggerCalled); - } - - trigger.markComplete(t2); - unique_lock unique_lk(lock); - cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; }); - EXPECT_TRUE(triggerCalled); -} - -TEST(MultiConditionTrigger, TestTriggerOnlyCalledOnce) { - string t1 = "t1"; - set conditionNames = {t1}; - - mutex lock; - condition_variable cv; - bool triggerCalled = false; - int triggerCount = 0; - - MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled, &triggerCount] { - { - lock_guard lg(lock); - triggerCount++; - triggerCalled = true; - } - cv.notify_all(); - }); - - trigger.markComplete(t1); - - // Ensure that the trigger fired. - { - unique_lock unique_lk(lock); - cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; }); - EXPECT_TRUE(triggerCalled); - EXPECT_EQ(triggerCount, 1); - triggerCalled = false; - } - - trigger.markComplete(t1); - - // Ensure that the trigger does not fire again. - { - unique_lock unique_lk(lock); - cv.wait_for(unique_lk, chrono::milliseconds(5), [&triggerCalled] { return triggerCalled; }); - EXPECT_FALSE(triggerCalled); - EXPECT_EQ(triggerCount, 1); - } -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tools/localtools/Android.bp b/cmds/statsd/tools/localtools/Android.bp deleted file mode 100644 index 69a43a8f4712..000000000000 --- a/cmds/statsd/tools/localtools/Android.bp +++ /dev/null @@ -1,46 +0,0 @@ -java_binary_host { - name: "statsd_localdrive", - manifest: "localdrive_manifest.txt", - srcs: [ - "src/com/android/statsd/shelltools/localdrive/*.java", - "src/com/android/statsd/shelltools/Utils.java", - ], - static_libs: [ - "platformprotos", - "guava", - ], -} - -java_library_host { - name: "statsd_testdrive_lib", - srcs: [ - "src/com/android/statsd/shelltools/testdrive/*.java", - "src/com/android/statsd/shelltools/Utils.java", - ], - static_libs: [ - "platformprotos", - "guava", - ], -} - - -java_binary_host { - name: "statsd_testdrive", - manifest: "testdrive_manifest.txt", - static_libs: [ - "statsd_testdrive_lib", - ], -} - -java_test_host { - name: "statsd_testdrive_test", - test_suites: ["general-tests"], - srcs: ["test/com/android/statsd/shelltools/testdrive/*.java"], - static_libs: [ - "statsd_testdrive_lib", - "junit", - "platformprotos", - "guava", - ], -} - diff --git a/cmds/statsd/tools/localtools/TEST_MAPPING b/cmds/statsd/tools/localtools/TEST_MAPPING deleted file mode 100644 index 7c8a3db5c610..000000000000 --- a/cmds/statsd/tools/localtools/TEST_MAPPING +++ /dev/null @@ -1,8 +0,0 @@ -{ - "presubmit": [ - { - "name": "statsd_testdrive_test", - "host": true - } - ] -} diff --git a/cmds/statsd/tools/localtools/localdrive_manifest.txt b/cmds/statsd/tools/localtools/localdrive_manifest.txt deleted file mode 100644 index 035cea1134bc..000000000000 --- a/cmds/statsd/tools/localtools/localdrive_manifest.txt +++ /dev/null @@ -1 +0,0 @@ -Main-class: com.android.statsd.shelltools.localdrive.LocalDrive diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java deleted file mode 100644 index 6a74480b505e..000000000000 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.shelltools; - -import com.android.os.StatsLog.ConfigMetricsReportList; - -import com.google.common.io.Files; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.ConsoleHandler; -import java.util.logging.Formatter; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Utilities for local use of statsd. - */ -public class Utils { - - public static final String CMD_DUMP_REPORT = "cmd stats dump-report"; - public static final String CMD_LOG_APP_BREADCRUMB = "cmd stats log-app-breadcrumb"; - public static final String CMD_REMOVE_CONFIG = "cmd stats config remove"; - public static final String CMD_UPDATE_CONFIG = "cmd stats config update"; - - public static final String SHELL_UID = "2000"; // Use shell, even if rooted. - - /** - * Runs adb shell command with output directed to outputFile if non-null. - */ - public static void runCommand(File outputFile, Logger logger, String... commands) - throws IOException, InterruptedException { - ProcessBuilder pb = new ProcessBuilder(commands); - if (outputFile != null && outputFile.exists() && outputFile.canWrite()) { - pb.redirectOutput(outputFile); - } - Process process = pb.start(); - - // Capture any errors - StringBuilder err = new StringBuilder(); - BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream())); - for (String line = br.readLine(); line != null; line = br.readLine()) { - err.append(line).append('\n'); - } - logger.severe(err.toString()); - - // Check result - if (process.waitFor() == 0) { - logger.fine("Adb command successful."); - } else { - logger.severe("Abnormal adb shell termination for: " + String.join(",", commands)); - throw new RuntimeException("Error running adb command: " + err.toString()); - } - } - - /** - * Dumps the report from the device and converts it to a ConfigMetricsReportList. - * Erases the data if clearData is true. - * @param configId id of the config - * @param clearData whether to erase the report data from statsd after getting the report. - * @param useShellUid Pulls data for the {@link SHELL_UID} instead of the caller's uid. - * @param logger Logger to log error messages - * @return - * @throws IOException - * @throws InterruptedException - */ - public static ConfigMetricsReportList getReportList(long configId, boolean clearData, - boolean useShellUid, Logger logger, String deviceSerial) - throws IOException, InterruptedException { - try { - File outputFile = File.createTempFile("statsdret", ".bin"); - outputFile.deleteOnExit(); - runCommand( - outputFile, - logger, - "adb", - "-s", - deviceSerial, - "shell", - CMD_DUMP_REPORT, - useShellUid ? SHELL_UID : "", - String.valueOf(configId), - clearData ? "" : "--keep_data", - "--include_current_bucket", - "--proto"); - ConfigMetricsReportList reportList = - ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile)); - return reportList; - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - logger.severe("Failed to fetch and parse the statsd output report. " - + "Perhaps there is not a valid statsd config for the requested " - + (useShellUid ? ("uid=" + SHELL_UID + ", ") : "") - + "configId=" + configId - + "."); - throw (e); - } - } - - /** - * Logs an AppBreadcrumbReported atom. - * @param label which label to log for the app breadcrumb atom. - * @param state which state to log for the app breadcrumb atom. - * @param logger Logger to log error messages - * - * @throws IOException - * @throws InterruptedException - */ - public static void logAppBreadcrumb(int label, int state, Logger logger, String deviceSerial) - throws IOException, InterruptedException { - runCommand( - null, - logger, - "adb", - "-s", - deviceSerial, - "shell", - CMD_LOG_APP_BREADCRUMB, - String.valueOf(label), - String.valueOf(state)); - } - public static void setUpLogger(Logger logger, boolean debug) { - ConsoleHandler handler = new ConsoleHandler(); - handler.setFormatter(new LocalToolsFormatter()); - logger.setUseParentHandlers(false); - if (debug) { - handler.setLevel(Level.ALL); - logger.setLevel(Level.ALL); - } - logger.addHandler(handler); - } - - /** - * Attempt to determine whether tool will work with this statsd, i.e. whether statsd is - * minCodename or higher. - * Algorithm: true if (sdk >= minSdk) || (sdk == minSdk-1 && codeName.startsWith(minCodeName)) - * If all else fails, assume it will work (letting future commands deal with any errors). - */ - public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename, - String deviceSerial) { - BufferedReader in = null; - try { - File outFileSdk = File.createTempFile("shelltools_sdk", "tmp"); - outFileSdk.deleteOnExit(); - runCommand(outFileSdk, logger, - "adb", "-s", deviceSerial, "shell", "getprop", "ro.build.version.sdk"); - in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileSdk))); - // If NullPointerException/NumberFormatException/etc., just catch and return true. - int sdk = Integer.parseInt(in.readLine().trim()); - if (sdk >= minSdk) { - return true; - } else if (sdk == minSdk - 1) { // Could be minSdk-1, or could be minSdk development. - in.close(); - File outFileCode = File.createTempFile("shelltools_codename", "tmp"); - outFileCode.deleteOnExit(); - runCommand(outFileCode, logger, - "adb", "-s", deviceSerial, "shell", "getprop", "ro.build.version.codename"); - in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileCode))); - return in.readLine().startsWith(minCodename); - } else { - return false; - } - } catch (Exception e) { - logger.fine("Could not determine whether statsd version is compatibile " - + "with tool: " + e.toString()); - } finally { - try { - if (in != null) { - in.close(); - } - } catch (IOException e) { - logger.fine("Could not close temporary file: " + e.toString()); - } - } - // Could not determine whether statsd is acceptable version. - // Just assume it is; if it isn't, we'll just get future errors via adb and deal with them. - return true; - } - - public static class LocalToolsFormatter extends Formatter { - public String format(LogRecord record) { - return record.getMessage() + "\n"; - } - } - - /** - * Parse the result of "adb devices" to return the list of connected devices. - * @param logger Logger to log error messages - * @return List of the serial numbers of the connected devices. - */ - public static List getDeviceSerials(Logger logger) { - try { - ArrayList devices = new ArrayList<>(); - File outFile = File.createTempFile("device_serial", "tmp"); - outFile.deleteOnExit(); - Utils.runCommand(outFile, logger, "adb", "devices"); - List outputLines = Files.readLines(outFile, Charset.defaultCharset()); - Pattern regex = Pattern.compile("^(.*)\tdevice$"); - for (String line : outputLines) { - Matcher m = regex.matcher(line); - if (m.find()) { - devices.add(m.group(1)); - } - } - return devices; - } catch (Exception ex) { - logger.log(Level.SEVERE, "Failed to list connected devices: " + ex.getMessage()); - } - return null; - } - - /** - * Returns ANDROID_SERIAL environment variable, or null if that is undefined or unavailable. - * @param logger Destination of error messages. - * @return String value of ANDROID_SERIAL environment variable, or null. - */ - public static String getDefaultDevice(Logger logger) { - try { - return System.getenv("ANDROID_SERIAL"); - } catch (Exception ex) { - logger.log(Level.SEVERE, "Failed to check ANDROID_SERIAL environment variable.", - ex); - } - return null; - } - - /** - * Returns the device to use if one can be deduced, or null. - * @param device Command-line specified device, or null. - * @param connectedDevices List of all connected devices. - * @param defaultDevice Environment-variable specified device, or null. - * @param logger Destination of error messages. - * @return Device to use, or null. - */ - public static String chooseDevice(String device, List connectedDevices, - String defaultDevice, Logger logger) { - if (connectedDevices == null || connectedDevices.isEmpty()) { - logger.severe("No connected device."); - return null; - } - if (device != null) { - if (connectedDevices.contains(device)) { - return device; - } - logger.severe("Device not connected: " + device); - return null; - } - if (connectedDevices.size() == 1) { - return connectedDevices.get(0); - } - if (defaultDevice != null) { - if (connectedDevices.contains(defaultDevice)) { - return defaultDevice; - } else { - logger.severe("ANDROID_SERIAL device is not connected: " + defaultDevice); - return null; - } - } - logger.severe("More than one device is connected. Choose one" - + " with -s DEVICE_SERIAL or environment variable ANDROID_SERIAL."); - return null; - } -} diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java deleted file mode 100644 index ec3c7df7bfba..000000000000 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.shelltools.localdrive; - -import com.android.internal.os.StatsdConfigProto.StatsdConfig; -import com.android.os.StatsLog.ConfigMetricsReport; -import com.android.os.StatsLog.ConfigMetricsReportList; -import com.android.statsd.shelltools.Utils; - -import com.google.common.io.Files; -import com.google.protobuf.TextFormat; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.List; -import java.util.logging.Logger; - -/** - * Tool for using statsd locally. Can upload a config and get the data. Handles - * both binary and human-readable protos. - * To make: make statsd_localdrive - * To run: statsd_localdrive (i.e. ./out/host/linux-x86/bin/statsd_localdrive) - */ -public class LocalDrive { - private static final boolean DEBUG = false; - - public static final int MIN_SDK = 29; - public static final String MIN_CODENAME = "Q"; - - public static final long DEFAULT_CONFIG_ID = 56789; - - public static final String BINARY_FLAG = "--binary"; - public static final String CLEAR_DATA = "--clear"; - public static final String NO_UID_MAP_FLAG = "--no-uid-map"; - - public static final String HELP_STRING = - "Usage:\n\n" + - - "statsd_localdrive [-s DEVICE_SERIAL] upload CONFIG_FILE [CONFIG_ID] [--binary]\n" + - " Uploads the given statsd config file (in binary or human-readable-text format).\n" + - " If a config with this id already exists, removes it first.\n" + - " CONFIG_FILE Location of config file on host.\n" + - " CONFIG_ID Long ID to associate with this config. If absent, uses " - + DEFAULT_CONFIG_ID + ".\n" + - " --binary Config is in binary format; otherwise, assumed human-readable text.\n" + - // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID - "\n" + - - "statsd_localdrive [-s DEVICE_SERIAL] update CONFIG_FILE [CONFIG_ID] [--binary]\n" + - " Same as upload, but does not remove the old config first (if it already exists).\n" + - // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID - "\n" + - - "statsd_localdrive [-s DEVICE_SERIAL] get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" + - " Prints the output statslog data (in binary or human-readable-text format).\n" + - " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" + - " --binary Output should be in binary, instead of default human-readable text.\n" + - " Binary output can be redirected as usual (e.g. > FILENAME).\n" + - " --no-uid-map Do not include the uid-map (the very lengthy uid<-->pkgName map).\n" + - " --clear Erase the data from statsd afterwards. Does not remove the config.\n" + - // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID [--keep_data] - // --include_current_bucket --proto - "\n" + - - "statsd_localdrive [-s DEVICE_SERIAL] remove [CONFIG_ID]\n" + - " Removes the config.\n" + - " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" + - // Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID - "\n" + - - "statsd_localdrive [-s DEVICE_SERIAL] clear [CONFIG_ID]\n" + - " Clears the data associated with the config.\n" + - " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" + - // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID - // --include_current_bucket --proto - ""; - - - private static final Logger sLogger = Logger.getLogger(LocalDrive.class.getName()); - - /** Usage: make statsd_localdrive && statsd_localdrive */ - public static void main(String[] args) { - Utils.setUpLogger(sLogger, DEBUG); - if (args.length == 0) { - printHelp(); - return; - } - - int remainingArgsLength = args.length; - String deviceSerial = null; - if (args[0].equals("-s")) { - if (args.length == 1) { - printHelp(); - } - deviceSerial = args[1]; - remainingArgsLength -= 2; - } - - List connectedDevices = Utils.getDeviceSerials(sLogger); - deviceSerial = Utils.chooseDevice(deviceSerial, connectedDevices, - Utils.getDefaultDevice(sLogger), sLogger); - if (deviceSerial == null) { - return; - } - - if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME, deviceSerial)) { - sLogger.severe("LocalDrive only works with statsd versions for Android " - + MIN_CODENAME + " or higher."); - return; - } - - int idx = args.length - remainingArgsLength; - if (remainingArgsLength > 0) { - switch (args[idx]) { - case "clear": - cmdClear(args, idx, deviceSerial); - return; - case "get-data": - cmdGetData(args, idx, deviceSerial); - return; - case "remove": - cmdRemove(args, idx); - return; - case "update": - cmdUpdate(args, idx, deviceSerial); - return; - case "upload": - cmdUpload(args, idx, deviceSerial); - return; - } - } - printHelp(); - } - - private static void printHelp() { - sLogger.info(HELP_STRING); - } - - // upload CONFIG_FILE [CONFIG_ID] [--binary] - private static boolean cmdUpload(String[] args, int idx, String deviceSerial) { - return updateConfig(args, idx, true, deviceSerial); - } - - // update CONFIG_FILE [CONFIG_ID] [--binary] - private static boolean cmdUpdate(String[] args, int idx, String deviceSerial) { - return updateConfig(args, idx, false, deviceSerial); - } - - private static boolean updateConfig(String[] args, int idx, boolean removeOldConfig, - String deviceSerial) { - int argCount = args.length - 1 - idx; // Used up one for upload/update. - - // Get CONFIG_FILE - if (argCount < 1) { - sLogger.severe("No config file provided."); - printHelp(); - return false; - } - final String origConfigLocation = args[idx + 1]; - if (!new File(origConfigLocation).exists()) { - sLogger.severe("Error - Cannot find the provided config file: " + origConfigLocation); - return false; - } - argCount--; - - // Get --binary - boolean binary = contains(args, idx + 2, BINARY_FLAG); - if (binary) argCount --; - - // Get CONFIG_ID - long configId; - try { - configId = getConfigId(argCount < 1, args, idx + 2); - } catch (NumberFormatException e) { - sLogger.severe("Invalid config id provided."); - printHelp(); - return false; - } - sLogger.fine(String.format("updateConfig with %s %d %b %b", - origConfigLocation, configId, binary, removeOldConfig)); - - // Remove the old config. - if (removeOldConfig) { - try { - Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG, - Utils.SHELL_UID, String.valueOf(configId)); - Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger, - deviceSerial); - } catch (InterruptedException | IOException e) { - sLogger.severe("Failed to remove config: " + e.getMessage()); - return false; - } - } - - // Upload the config. - String configLocation; - if (binary) { - configLocation = origConfigLocation; - } else { - StatsdConfig.Builder builder = StatsdConfig.newBuilder(); - try { - TextFormat.merge(new FileReader(origConfigLocation), builder); - } catch (IOException e) { - sLogger.severe("Failed to read config file " + origConfigLocation + ": " - + e.getMessage()); - return false; - } - - try { - File tempConfigFile = File.createTempFile("statsdconfig", ".config"); - tempConfigFile.deleteOnExit(); - Files.write(builder.build().toByteArray(), tempConfigFile); - configLocation = tempConfigFile.getAbsolutePath(); - } catch (IOException e) { - sLogger.severe("Failed to write temp config file: " + e.getMessage()); - return false; - } - } - String remotePath = "/data/local/tmp/statsdconfig.config"; - try { - Utils.runCommand(null, sLogger, "adb", "push", configLocation, remotePath); - Utils.runCommand(null, sLogger, "adb", "shell", "cat", remotePath, "|", - Utils.CMD_UPDATE_CONFIG, Utils.SHELL_UID, String.valueOf(configId)); - } catch (InterruptedException | IOException e) { - sLogger.severe("Failed to update config: " + e.getMessage()); - return false; - } - return true; - } - - // get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map] - private static boolean cmdGetData(String[] args, int idx, String deviceSerial) { - boolean binary = contains(args, idx + 1, BINARY_FLAG); - boolean noUidMap = contains(args, idx + 1, NO_UID_MAP_FLAG); - boolean clearData = contains(args, idx + 1, CLEAR_DATA); - - // Get CONFIG_ID - int argCount = args.length - 1 - idx; // Used up one for get-data. - if (binary) argCount--; - if (noUidMap) argCount--; - if (clearData) argCount--; - long configId; - try { - configId = getConfigId(argCount < 1, args, idx + 1); - } catch (NumberFormatException e) { - sLogger.severe("Invalid config id provided."); - printHelp(); - return false; - } - sLogger.fine(String.format("cmdGetData with %d %b %b %b", - configId, clearData, binary, noUidMap)); - - // Get the StatsLog - // Even if the args request no modifications, we still parse it to make sure it's valid. - ConfigMetricsReportList reportList; - try { - reportList = Utils.getReportList(configId, clearData, true /* SHELL_UID */, sLogger, - deviceSerial); - } catch (IOException | InterruptedException e) { - sLogger.severe("Failed to get report list: " + e.getMessage()); - return false; - } - if (noUidMap) { - ConfigMetricsReportList.Builder builder - = ConfigMetricsReportList.newBuilder(reportList); - // Clear the reports, then add them back without their UidMap. - builder.clearReports(); - for (ConfigMetricsReport report : reportList.getReportsList()) { - builder.addReports(ConfigMetricsReport.newBuilder(report).clearUidMap()); - } - reportList = builder.build(); - } - - if (!binary) { - sLogger.info(reportList.toString()); - } else { - try { - System.out.write(reportList.toByteArray()); - } catch (IOException e) { - sLogger.severe("Failed to output binary statslog proto: " - + e.getMessage()); - return false; - } - } - return true; - } - - // clear [CONFIG_ID] - private static boolean cmdClear(String[] args, int idx, String deviceSerial) { - // Get CONFIG_ID - long configId; - try { - configId = getConfigId(false, args, idx + 1); - } catch (NumberFormatException e) { - sLogger.severe("Invalid config id provided."); - printHelp(); - return false; - } - sLogger.fine(String.format("cmdClear with %d", configId)); - - try { - Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger, - deviceSerial); - } catch (IOException | InterruptedException e) { - sLogger.severe("Failed to get report list: " + e.getMessage()); - return false; - } - return true; - } - - // remove [CONFIG_ID] - private static boolean cmdRemove(String[] args, int idx) { - // Get CONFIG_ID - long configId; - try { - configId = getConfigId(false, args, idx + 1); - } catch (NumberFormatException e) { - sLogger.severe("Invalid config id provided."); - printHelp(); - return false; - } - sLogger.fine(String.format("cmdRemove with %d", configId)); - - try { - Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG, - Utils.SHELL_UID, String.valueOf(configId)); - } catch (InterruptedException | IOException e) { - sLogger.severe("Failed to remove config: " + e.getMessage()); - return false; - } - return true; - } - - /** - * Searches through the array to see if it contains (precisely) the given value, starting - * at the given firstIdx. - */ - private static boolean contains(String[] array, int firstIdx, String value) { - if (value == null) return false; - if (firstIdx < 0) return false; - for (int i = firstIdx; i < array.length; i++) { - if (value.equals(array[i])) { - return true; - } - } - return false; - } - - /** - * Gets the config id from args[idx], or returns DEFAULT_CONFIG_ID if args[idx] does not exist. - * If justUseDefault, overrides and just uses DEFAULT_CONFIG_ID instead. - */ - private static long getConfigId(boolean justUseDefault, String[] args, int idx) - throws NumberFormatException { - if (justUseDefault || args.length <= idx || idx < 0) { - return DEFAULT_CONFIG_ID; - } - try { - return Long.valueOf(args[idx]); - } catch (NumberFormatException e) { - sLogger.severe("Bad config id provided: " + args[idx]); - throw e; - } - } -} diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java deleted file mode 100644 index 51bcad115cc5..000000000000 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.shelltools.testdrive; - -import com.android.internal.os.StatsdConfigProto; -import com.android.internal.os.StatsdConfigProto.AtomMatcher; -import com.android.internal.os.StatsdConfigProto.EventMetric; -import com.android.internal.os.StatsdConfigProto.FieldFilter; -import com.android.internal.os.StatsdConfigProto.GaugeMetric; -import com.android.internal.os.StatsdConfigProto.PullAtomPackages; -import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher; -import com.android.internal.os.StatsdConfigProto.StatsdConfig; -import com.android.internal.os.StatsdConfigProto.TimeUnit; -import com.android.os.AtomsProto.Atom; -import com.android.os.StatsLog; -import com.android.os.StatsLog.ConfigMetricsReport; -import com.android.os.StatsLog.ConfigMetricsReportList; -import com.android.os.StatsLog.StatsLogReport; -import com.android.statsd.shelltools.Utils; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.io.Files; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class TestDrive { - - private static final int METRIC_ID_BASE = 1111; - private static final long ATOM_MATCHER_ID_BASE = 1234567; - private static final long APP_BREADCRUMB_MATCHER_ID = 1111111; - private static final int PULL_ATOM_START = 10000; - private static final int MAX_PLATFORM_ATOM_TAG = 100000; - private static final int VENDOR_PULLED_ATOM_START_TAG = 150000; - private static final long CONFIG_ID = 54321; - private static final String[] ALLOWED_LOG_SOURCES = { - "AID_GRAPHICS", - "AID_INCIDENTD", - "AID_STATSD", - "AID_RADIO", - "com.android.systemui", - "com.android.vending", - "AID_SYSTEM", - "AID_ROOT", - "AID_BLUETOOTH", - "AID_LMKD", - "com.android.managedprovisioning", - "AID_MEDIA", - "AID_NETWORK_STACK", - "com.google.android.providers.media.module", - }; - private static final String[] DEFAULT_PULL_SOURCES = { - "AID_SYSTEM", - "AID_RADIO" - }; - private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName()); - - @VisibleForTesting - String mDeviceSerial = null; - @VisibleForTesting - Dumper mDumper = new BasicDumper(); - - public static void main(String[] args) { - final Configuration configuration = new Configuration(); - final TestDrive testDrive = new TestDrive(); - Utils.setUpLogger(LOGGER, false); - - if (!testDrive.processArgs(configuration, args, - Utils.getDeviceSerials(LOGGER), Utils.getDefaultDevice(LOGGER))) { - return; - } - - final ConfigMetricsReportList reports = testDrive.testDriveAndGetReports( - configuration.createConfig(), configuration.hasPulledAtoms(), - configuration.hasPushedAtoms()); - if (reports != null) { - configuration.dumpMetrics(reports, testDrive.mDumper); - } - } - - boolean processArgs(Configuration configuration, String[] args, List connectedDevices, - String defaultDevice) { - if (args.length < 1) { - LOGGER.severe("Usage: ./test_drive [-one] " - + "[-p additional_allowed_package] " - + "[-s DEVICE_SERIAL_NUMBER] " - + " ... "); - return false; - } - - int first_arg = 0; - // Consume all flags, which must precede all atoms - for (; first_arg < args.length; ++first_arg) { - String arg = args[first_arg]; - int remaining_args = args.length - first_arg; - if (remaining_args >= 2 && arg.equals("-one")) { - LOGGER.info("Creating one event metric to catch all pushed atoms."); - configuration.mOnePushedAtomEvent = true; - } else if (remaining_args >= 2 && arg.equals("-terse")) { - LOGGER.info("Terse output format."); - mDumper = new TerseDumper(); - } else if (remaining_args >= 3 && arg.equals("-p")) { - configuration.mAdditionalAllowedPackage = args[++first_arg]; - } else if (remaining_args >= 3 && arg.equals("-s")) { - mDeviceSerial = args[++first_arg]; - } else { - break; // Found the atom list - } - } - - mDeviceSerial = Utils.chooseDevice(mDeviceSerial, connectedDevices, defaultDevice, LOGGER); - if (mDeviceSerial == null) { - return false; - } - - for ( ; first_arg < args.length; ++first_arg) { - String atom = args[first_arg]; - try { - configuration.addAtom(Integer.valueOf(atom)); - } catch (NumberFormatException e) { - LOGGER.severe("Bad atom id provided: " + atom); - } - } - - return configuration.hasPulledAtoms() || configuration.hasPushedAtoms(); - } - - private ConfigMetricsReportList testDriveAndGetReports(StatsdConfig config, - boolean hasPulledAtoms, boolean hasPushedAtoms) { - if (config == null) { - LOGGER.severe("Failed to create valid config."); - return null; - } - - String remoteConfigPath = null; - try { - remoteConfigPath = pushConfig(config, mDeviceSerial); - LOGGER.info("Pushed the following config to statsd on device '" + mDeviceSerial - + "':"); - LOGGER.info(config.toString()); - if (hasPushedAtoms) { - LOGGER.info("Now please play with the device to trigger the event."); - } - if (!hasPulledAtoms) { - LOGGER.info( - "All events should be dumped after 1 min ..."); - Thread.sleep(60_000); - } else { - LOGGER.info("All events should be dumped after 1.5 minutes ..."); - Thread.sleep(15_000); - Utils.logAppBreadcrumb(0, 0, LOGGER, mDeviceSerial); - Thread.sleep(75_000); - } - return Utils.getReportList(CONFIG_ID, true, false, LOGGER, - mDeviceSerial); - } catch (Exception e) { - LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e); - } finally { - removeConfig(mDeviceSerial); - if (remoteConfigPath != null) { - try { - Utils.runCommand(null, LOGGER, - "adb", "-s", mDeviceSerial, "shell", "rm", - remoteConfigPath); - } catch (Exception e) { - LOGGER.log(Level.WARNING, - "Unable to remove remote config file: " + remoteConfigPath, e); - } - } - } - return null; - } - - static class Configuration { - boolean mOnePushedAtomEvent = false; - @VisibleForTesting - Set mPushedAtoms = new TreeSet<>(); - @VisibleForTesting - Set mPulledAtoms = new TreeSet<>(); - @VisibleForTesting - String mAdditionalAllowedPackage = null; - private final Set mTrackedMetrics = new HashSet<>(); - - private void dumpMetrics(ConfigMetricsReportList reportList, Dumper dumper) { - // We may get multiple reports. Take the last one. - ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1); - for (StatsLogReport statsLog : report.getMetricsList()) { - if (isTrackedMetric(statsLog.getMetricId())) { - dumper.dump(statsLog); - } - } - } - - boolean isTrackedMetric(long metricId) { - return mTrackedMetrics.contains(metricId); - } - - static boolean isPulledAtom(int atomId) { - return atomId >= PULL_ATOM_START && atomId <= MAX_PLATFORM_ATOM_TAG - || atomId >= VENDOR_PULLED_ATOM_START_TAG; - } - - void addAtom(Integer atom) { - if (Atom.getDescriptor().findFieldByNumber(atom) == null) { - LOGGER.severe("No such atom found: " + atom); - return; - } - if (isPulledAtom(atom)) { - mPulledAtoms.add(atom); - } else { - mPushedAtoms.add(atom); - } - } - - private boolean hasPulledAtoms() { - return !mPulledAtoms.isEmpty(); - } - - private boolean hasPushedAtoms() { - return !mPushedAtoms.isEmpty(); - } - - StatsdConfig createConfig() { - long metricId = METRIC_ID_BASE; - long atomMatcherId = ATOM_MATCHER_ID_BASE; - - StatsdConfig.Builder builder = baseBuilder(); - - if (hasPulledAtoms()) { - builder.addAtomMatcher( - createAtomMatcher( - Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, - APP_BREADCRUMB_MATCHER_ID)); - } - - for (int atomId : mPulledAtoms) { - builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId)); - GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder(); - gaugeMetricBuilder - .setId(metricId) - .setWhat(atomMatcherId) - .setTriggerEvent(APP_BREADCRUMB_MATCHER_ID) - .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build()) - .setBucket(TimeUnit.ONE_MINUTE) - .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES) - .setMaxNumGaugeAtomsPerBucket(100); - builder.addGaugeMetric(gaugeMetricBuilder.build()); - atomMatcherId++; - mTrackedMetrics.add(metricId++); - } - - // A simple atom matcher for each pushed atom. - List simpleAtomMatchers = new ArrayList<>(); - for (int atomId : mPushedAtoms) { - final AtomMatcher atomMatcher = createAtomMatcher(atomId, atomMatcherId++); - simpleAtomMatchers.add(atomMatcher); - builder.addAtomMatcher(atomMatcher); - } - - if (mOnePushedAtomEvent) { - // Create a union event metric, using an matcher that matches all pulled atoms. - AtomMatcher unionAtomMatcher = createUnionMatcher(simpleAtomMatchers, - atomMatcherId); - builder.addAtomMatcher(unionAtomMatcher); - EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder(); - eventMetricBuilder.setId(metricId).setWhat(unionAtomMatcher.getId()); - builder.addEventMetric(eventMetricBuilder.build()); - mTrackedMetrics.add(metricId++); - } else { - // Create multiple event metrics, one per pulled atom. - for (AtomMatcher atomMatcher : simpleAtomMatchers) { - EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder(); - eventMetricBuilder - .setId(metricId) - .setWhat(atomMatcher.getId()); - builder.addEventMetric(eventMetricBuilder.build()); - mTrackedMetrics.add(metricId++); - } - } - - return builder.build(); - } - - private static AtomMatcher createAtomMatcher(int atomId, long matcherId) { - AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder(); - atomMatcherBuilder - .setId(matcherId) - .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId)); - return atomMatcherBuilder.build(); - } - - private AtomMatcher createUnionMatcher(List simpleAtomMatchers, - long atomMatcherId) { - AtomMatcher.Combination.Builder combinationBuilder = - AtomMatcher.Combination.newBuilder(); - combinationBuilder.setOperation(StatsdConfigProto.LogicalOperation.OR); - for (AtomMatcher matcher : simpleAtomMatchers) { - combinationBuilder.addMatcher(matcher.getId()); - } - AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder(); - atomMatcherBuilder.setId(atomMatcherId).setCombination(combinationBuilder.build()); - return atomMatcherBuilder.build(); - } - - private StatsdConfig.Builder baseBuilder() { - ArrayList allowedSources = new ArrayList<>(); - Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES); - if (mAdditionalAllowedPackage != null) { - allowedSources.add(mAdditionalAllowedPackage); - } - return StatsdConfig.newBuilder() - .addAllAllowedLogSource(allowedSources) - .addAllDefaultPullPackages(Arrays.asList(DEFAULT_PULL_SOURCES)) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.GPU_STATS_GLOBAL_INFO_FIELD_NUMBER) - .addPackages("AID_GPU_SERVICE")) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.GPU_STATS_APP_INFO_FIELD_NUMBER) - .addPackages("AID_GPU_SERVICE")) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER) - .addPackages("AID_STATSD")) - .addPullAtomPackages(PullAtomPackages.newBuilder() - .setAtomId(Atom.GENERAL_EXTERNAL_STORAGE_ACCESS_STATS_FIELD_NUMBER) - .addPackages("com.google.android.providers.media.module")) - .setHashStringsInMetricReport(false); - } - } - - interface Dumper { - void dump(StatsLogReport report); - } - - static class BasicDumper implements Dumper { - @Override - public void dump(StatsLogReport report) { - System.out.println(report.toString()); - } - } - - static class TerseDumper extends BasicDumper { - @Override - public void dump(StatsLogReport report) { - if (report.hasGaugeMetrics()) { - dumpGaugeMetrics(report); - } - if (report.hasEventMetrics()) { - dumpEventMetrics(report); - } - } - void dumpEventMetrics(StatsLogReport report) { - final List data = report.getEventMetrics().getDataList(); - if (data.isEmpty()) { - return; - } - long firstTimestampNanos = data.get(0).getElapsedTimestampNanos(); - for (StatsLog.EventMetricData event : data) { - final double deltaSec = (event.getElapsedTimestampNanos() - firstTimestampNanos) - / 1e9; - System.out.println( - String.format("+%.3fs: %s", deltaSec, event.getAtom().toString())); - } - } - void dumpGaugeMetrics(StatsLogReport report) { - final List data = report.getGaugeMetrics().getDataList(); - if (data.isEmpty()) { - return; - } - for (StatsLog.GaugeMetricData gauge : data) { - System.out.println(gauge.toString()); - } - } - } - - private static String pushConfig(StatsdConfig config, String deviceSerial) - throws IOException, InterruptedException { - File configFile = File.createTempFile("statsdconfig", ".config"); - configFile.deleteOnExit(); - Files.write(config.toByteArray(), configFile); - String remotePath = "/data/local/tmp/" + configFile.getName(); - Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial, - "push", configFile.getAbsolutePath(), remotePath); - Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial, - "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG, - String.valueOf(CONFIG_ID)); - return remotePath; - } - - private static void removeConfig(String deviceSerial) { - try { - Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial, - "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID)); - } catch (Exception e) { - LOGGER.severe("Failed to remove config: " + e.getMessage()); - } - } -} diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java deleted file mode 100644 index b1cc60f74993..000000000000 --- a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.statsd.shelltools.testdrive; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import com.android.internal.os.StatsdConfigProto; -import com.android.internal.os.StatsdConfigProto.StatsdConfig; -import com.android.os.AtomsProto; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Tests for {@link TestDrive} - */ -public class ConfigurationTest { - - private StatsdConfigProto.AtomMatcher findAndRemoveAtomMatcherById( - List atomMatchers, long id) { - int numMatches = 0; - StatsdConfigProto.AtomMatcher match = null; - for (StatsdConfigProto.AtomMatcher atomMatcher : atomMatchers) { - if (id == atomMatcher.getId()) { - ++numMatches; - match = atomMatcher; - } - } - if (numMatches == 1) { - atomMatchers.remove(match); - return match; - } - return null; // Too many, or not found - } - - private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration(); - - @Test - public void testOnePushed() { - final int atom = 90; - assertFalse(TestDrive.Configuration.isPulledAtom(atom)); - mConfiguration.addAtom(atom); - StatsdConfig config = mConfiguration.createConfig(); - - //event_metric { - // id: 1111 - // what: 1234567 - //} - //atom_matcher { - // id: 1234567 - // simple_atom_matcher { - // atom_id: 90 - // } - //} - - assertEquals(1, config.getEventMetricCount()); - assertEquals(0, config.getGaugeMetricCount()); - - assertTrue(mConfiguration.isTrackedMetric(config.getEventMetric(0).getId())); - - final List atomMatchers = - new ArrayList<>(config.getAtomMatcherList()); - assertEquals(atom, - findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat()) - .getSimpleAtomMatcher().getAtomId()); - assertEquals(0, atomMatchers.size()); - } - - @Test - public void testOnePulled() { - final int atom = 10022; - assertTrue(TestDrive.Configuration.isPulledAtom(atom)); - mConfiguration.addAtom(atom); - StatsdConfig config = mConfiguration.createConfig(); - - //gauge_metric { - // id: 1111 - // what: 1234567 - // gauge_fields_filter { - // include_all: true - // } - // bucket: ONE_MINUTE - // sampling_type: FIRST_N_SAMPLES - // max_num_gauge_atoms_per_bucket: 100 - // trigger_event: 1111111 - //} - //atom_matcher { - // id: 1111111 - // simple_atom_matcher { - // atom_id: 47 - // } - //} - //atom_matcher { - // id: 1234567 - // simple_atom_matcher { - // atom_id: 10022 - // } - //} - - assertEquals(0, config.getEventMetricCount()); - assertEquals(1, config.getGaugeMetricCount()); - - assertTrue(mConfiguration.isTrackedMetric(config.getGaugeMetric(0).getId())); - - final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0); - assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll()); - - final List atomMatchers = - new ArrayList<>(config.getAtomMatcherList()); - assertEquals(atom, - findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat()) - .getSimpleAtomMatcher().getAtomId()); - assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, - findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent()) - .getSimpleAtomMatcher().getAtomId()); - assertEquals(0, atomMatchers.size()); - } - - @Test - public void testOnePulledTwoPushed() { - final int pulledAtom = 10022; - assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom)); - mConfiguration.addAtom(pulledAtom); - - Integer[] pushedAtoms = new Integer[]{244, 245}; - for (int atom : pushedAtoms) { - assertFalse(TestDrive.Configuration.isPulledAtom(atom)); - mConfiguration.addAtom(atom); - } - StatsdConfig config = mConfiguration.createConfig(); - - // event_metric { - // id: 1111 - // what: 1234567 - // } - // event_metric { - // id: 1112 - // what: 1234568 - // } - // gauge_metric { - // id: 1114 - // what: 1234570 - // gauge_fields_filter { - // include_all: true - // } - // bucket: ONE_MINUTE - // sampling_type: FIRST_N_SAMPLES - // max_num_gauge_atoms_per_bucket: 100 - // trigger_event: 1111111 - // } - // atom_matcher { - // id: 1111111 - // simple_atom_matcher { - // atom_id: 47 - // } - // } - // atom_matcher { - // id: 1234567 - // simple_atom_matcher { - // atom_id: 244 - // } - // } - // atom_matcher { - // id: 1234568 - // simple_atom_matcher { - // atom_id: 245 - // } - // } - // atom_matcher { - // id: 1234570 - // simple_atom_matcher { - // atom_id: 10022 - // } - // } - - assertEquals(2, config.getEventMetricCount()); - assertEquals(1, config.getGaugeMetricCount()); - - final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0); - assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId())); - assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll()); - for (StatsdConfigProto.EventMetric eventMetric : config.getEventMetricList()) { - assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId())); - } - - final List atomMatchers = - new ArrayList<>(config.getAtomMatcherList()); - - assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat()) - .getSimpleAtomMatcher().getAtomId()); - assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, - findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent()) - .getSimpleAtomMatcher().getAtomId()); - - Integer[] actualAtoms = new Integer[]{ - findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat()) - .getSimpleAtomMatcher().getAtomId(), - findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(1).getWhat()) - .getSimpleAtomMatcher().getAtomId()}; - Arrays.sort(actualAtoms); - assertArrayEquals(pushedAtoms, actualAtoms); - - assertEquals(0, atomMatchers.size()); - } - - @Test - public void testOnePulledTwoPushedTogether() { - mConfiguration.mOnePushedAtomEvent = true; // Use one event grabbing all pushed atoms - - final int pulledAtom = 10022; - assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom)); - mConfiguration.addAtom(pulledAtom); - - Integer[] pushedAtoms = new Integer[]{244, 245}; - for (int atom : pushedAtoms) { - assertFalse(TestDrive.Configuration.isPulledAtom(atom)); - mConfiguration.addAtom(atom); - } - StatsdConfig config = mConfiguration.createConfig(); - - // event_metric { - // id: 1112 - // what: 1234570 - // } - // gauge_metric { - // id: 1111 - // what: 1234567 - // gauge_fields_filter { - // include_all: true - // } - // bucket: ONE_MINUTE - // sampling_type: FIRST_N_SAMPLES - // max_num_gauge_atoms_per_bucket: 100 - // trigger_event: 1111111 - // } - // atom_matcher { - // id: 1111111 - // simple_atom_matcher { - // atom_id: 47 - // } - // } - // atom_matcher { - // id: 1234567 - // simple_atom_matcher { - // atom_id: 10022 - // } - // } - // atom_matcher { - // id: 1234568 - // simple_atom_matcher { - // atom_id: 244 - // } - // } - // atom_matcher { - // id: 1234569 - // simple_atom_matcher { - // atom_id: 245 - // } - // } - // atom_matcher { - // id: 1234570 - // combination { - // operation: OR - // matcher: 1234568 - // matcher: 1234569 - // } - // } - - assertEquals(1, config.getEventMetricCount()); - assertEquals(1, config.getGaugeMetricCount()); - - final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0); - assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId())); - assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll()); - - StatsdConfigProto.EventMetric eventMetric = config.getEventMetric(0); - assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId())); - - final List atomMatchers = - new ArrayList<>(config.getAtomMatcherList()); - - assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat()) - .getSimpleAtomMatcher().getAtomId()); - assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, - findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent()) - .getSimpleAtomMatcher().getAtomId()); - - StatsdConfigProto.AtomMatcher unionMatcher = findAndRemoveAtomMatcherById(atomMatchers, - eventMetric.getWhat()); - assertNotNull(unionMatcher.getCombination()); - assertEquals(2, unionMatcher.getCombination().getMatcherCount()); - - Integer[] actualAtoms = new Integer[]{ - findAndRemoveAtomMatcherById(atomMatchers, - unionMatcher.getCombination().getMatcher(0)) - .getSimpleAtomMatcher().getAtomId(), - findAndRemoveAtomMatcherById(atomMatchers, - unionMatcher.getCombination().getMatcher(1)) - .getSimpleAtomMatcher().getAtomId()}; - Arrays.sort(actualAtoms); - assertArrayEquals(pushedAtoms, actualAtoms); - - assertEquals(0, atomMatchers.size()); - } -} diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java deleted file mode 100644 index 363fac0c78ba..000000000000 --- a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.statsd.shelltools.testdrive; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Tests for {@link TestDrive} - */ -@RunWith(Parameterized.class) -public class TestDriveTest { - /** - * Expected results of a single iteration of the paramerized test. - */ - static class Expect { - public boolean success; - public Integer[] atoms; - public boolean onePushedAtomEvent = false; - public String extraPackage = null; - public String target; - public boolean terse = false; - - static Expect success(Integer... atoms) { - return new Expect(true, atoms, - TARGET); - } - Expect(boolean success, Integer[] atoms, String target) { - this.success = success; - this.atoms = atoms; - this.target = target; - } - static final Expect FAILURE = new Expect(false, null, null); - Expect onePushedAtomEvent() { - this.onePushedAtomEvent = true; - return this; - } - Expect extraPackage() { - this.extraPackage = TestDriveTest.PACKAGE; - return this; - } - Expect terse() { - this.terse = true; - return this; - } - } - - @Parameterized.Parameter(0) - public String[] mArgs; - - @Parameterized.Parameter(1) - public List mConnectedDevices; - - @Parameterized.Parameter(2) - public String mDefaultDevice; - - @Parameterized.Parameter(3) - public Expect mExpect; - - private static final String TARGET = "target"; - private static final List TARGET_AND_OTHER = Arrays.asList("otherDevice", - TARGET); - private static final List TWO_OTHER_DEVICES = Arrays.asList( - "other1", "other2"); - private static final List TARGET_ONLY = Collections.singletonList(TARGET); - private static final List NOT_TARGET = Collections.singletonList("other"); - private static final List NO_DEVICES = Collections.emptyList(); - private static final String PACKAGE = "extraPackage"; - - @Parameterized.Parameters - public static Collection data() { - return Arrays.asList( - new Object[]{new String[]{}, null, null, - Expect.FAILURE}, // Usage explanation - new Object[]{new String[]{"244", "245"}, null, null, - Expect.FAILURE}, // Failure looking up connected devices - new Object[]{new String[]{"244", "245"}, NO_DEVICES, null, - Expect.FAILURE}, // No connected devices - new Object[]{new String[]{"-s", TARGET, "244", "245"}, NOT_TARGET, null, - Expect.FAILURE}, // Wrong device connected - new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, null, - Expect.FAILURE}, // Wrong devices connected - new Object[]{new String[]{"244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245)}, // If only one device connected, guess that one - new Object[]{new String[]{"244", "not_an_atom"}, TARGET_ONLY, null, - Expect.success(244)}, // Ignore non-atoms - new Object[]{new String[]{"not_an_atom"}, TARGET_ONLY, null, - Expect.FAILURE}, // Require at least one atom - new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, TARGET, - Expect.FAILURE}, // ANDROID_SERIAL specifies non-connected target - new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, TARGET, - Expect.success(244, 245)}, // ANDROID_SERIAL specifies a valid target - new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, null, - Expect.FAILURE}, // Two connected devices, no indication of which to use - new Object[]{new String[]{"-one", "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).onePushedAtomEvent()}, - new Object[]{new String[]{"-terse", "-one", "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).onePushedAtomEvent().terse()}, - new Object[]{new String[]{"-one", "-terse", "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).onePushedAtomEvent().terse()}, - new Object[]{new String[]{"-p", PACKAGE, "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).extraPackage()}, - new Object[]{new String[]{"-p", PACKAGE, "-one", "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, - new Object[]{new String[]{"-one", "-p", PACKAGE, "244", "245"}, TARGET_ONLY, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, - new Object[]{new String[]{"-s", TARGET, "-one", "-p", PACKAGE, "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, - new Object[]{new String[]{"-one", "-s", TARGET, "-p", PACKAGE, "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, - new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, - new Object[]{new String[]{"-terse", "-one", "-p", PACKAGE, "-s", TARGET, - "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}, - new Object[]{new String[]{"-one", "-terse", "-p", PACKAGE, "-s", TARGET, - "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}, - new Object[]{new String[]{"-one", "-p", PACKAGE, "-terse", "-s", TARGET, - "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}, - new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "-terse", - "244", "245"}, - TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()} - ); - } - - private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration(); - private final TestDrive mTestDrive = new TestDrive(); - - private static Integer[] collectAtoms(TestDrive.Configuration configuration) { - Integer[] result = new Integer[configuration.mPulledAtoms.size() - + configuration.mPushedAtoms.size()]; - int result_index = 0; - for (Integer atom : configuration.mPushedAtoms) { - result[result_index++] = atom; - } - for (Integer atom : configuration.mPulledAtoms) { - result[result_index++] = atom; - } - Arrays.sort(result); - return result; - } - - @Test - public void testProcessArgs() { - boolean result = mTestDrive.processArgs(mConfiguration, mArgs, mConnectedDevices, - mDefaultDevice); - if (mExpect.success) { - assertTrue(result); - assertArrayEquals(mExpect.atoms, collectAtoms(mConfiguration)); - assertEquals(mExpect.onePushedAtomEvent, mConfiguration.mOnePushedAtomEvent); - assertEquals(mExpect.target, mTestDrive.mDeviceSerial); - if (mExpect.terse) { - assertEquals(TestDrive.TerseDumper.class, mTestDrive.mDumper.getClass()); - } else { - assertEquals(TestDrive.BasicDumper.class, mTestDrive.mDumper.getClass()); - } - } else { - assertFalse(result); - } - } -} diff --git a/cmds/statsd/tools/localtools/testdrive_manifest.txt b/cmds/statsd/tools/localtools/testdrive_manifest.txt deleted file mode 100644 index 625ebfa4312a..000000000000 --- a/cmds/statsd/tools/localtools/testdrive_manifest.txt +++ /dev/null @@ -1 +0,0 @@ -Main-class: com.android.statsd.shelltools.testdrive.TestDrive -- GitLab