/* * 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()); } TEST(CountMetricE2eTest, TestUploadThreshold) { // 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 thresholdCount = 2; UploadThreshold threshold; threshold.set_gt_int(thresholdCount); int64_t metricId = 123456; CountMetric countMetric = createCountMetric("COUNT", appCrashMatcher.id(), nullopt, {}); *countMetric.mutable_dimensions_in_what() = CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/}); *countMetric.mutable_threshold() = threshold; *config.add_count_metric() = countMetric; // 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); int appUid1 = 1; int appUid2 = 2; std::vector> events; events.push_back( CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, appUid1)); // 0:30 events.push_back( CreateAppCrashOccurredEvent(bucketStartTimeNs + 40 * NS_PER_SEC, appUid2)); // 0:50 events.push_back( CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, appUid1)); // 1:10 events.push_back( CreateAppCrashOccurredEvent(bucketStartTimeNs + 65 * NS_PER_SEC, appUid1)); // 1:15 // 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 + 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(1, countMetrics.data_size()); CountMetricData data = countMetrics.data(0); // Uid 1 reports a count greater than the threshold. // Uid 2 is dropped because the count was less than the threshold. ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, appUid1); ValidateCountBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + bucketSizeNs, 3); } } // namespace statsd } // namespace os } // namespace android #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif