| // 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 <aidl/android/os/StatsDimensionsValueParcel.h> |
| #include <android-base/properties.h> |
| #include <android-base/stringprintf.h> |
| #include <android/binder_interface_utils.h> |
| #include <gtest/gtest.h> |
| |
| #include <thread> |
| |
| #include "flags/flags.h" |
| #include "src/StatsLogProcessor.h" |
| #include "src/StatsService.h" |
| #include "src/storage/StorageManager.h" |
| #include "src/subscriber/SubscriberReporter.h" |
| #include "tests/statsd_test_util.h" |
| |
| namespace android { |
| namespace os { |
| namespace statsd { |
| |
| #ifdef __ANDROID__ |
| |
| using aidl::android::os::StatsDimensionsValueParcel; |
| using android::base::SetProperty; |
| using android::base::StringPrintf; |
| using ::ndk::SharedRefBase; |
| using namespace std; |
| |
| // Tests that only run with the partial config update feature turned on. |
| namespace { |
| // Setup for test fixture. |
| class ConfigUpdateE2eTest : public ::testing::Test { |
| }; |
| |
| void ValidateSubsystemSleepDimension(const DimensionsValue& value, string name) { |
| EXPECT_EQ(value.field(), util::SUBSYSTEM_SLEEP_STATE); |
| ASSERT_EQ(value.value_tuple().dimensions_value_size(), 1); |
| EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1 /* subsystem name field */); |
| EXPECT_EQ(value.value_tuple().dimensions_value(0).value_str(), name); |
| } |
| |
| } // Anonymous namespace. |
| |
| TEST_F(ConfigUpdateE2eTest, TestEventMetric) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| |
| AtomMatcher syncStartMatcher = CreateSyncStartAtomMatcher(); |
| *config.add_atom_matcher() = syncStartMatcher; |
| AtomMatcher wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); |
| *config.add_atom_matcher() = wakelockAcquireMatcher; |
| AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); |
| *config.add_atom_matcher() = screenOnMatcher; |
| AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); |
| *config.add_atom_matcher() = screenOffMatcher; |
| AtomMatcher batteryPluggedUsbMatcher = CreateBatteryStateUsbMatcher(); |
| *config.add_atom_matcher() = batteryPluggedUsbMatcher; |
| AtomMatcher unpluggedMatcher = CreateBatteryStateNoneMatcher(); |
| *config.add_atom_matcher() = unpluggedMatcher; |
| |
| AtomMatcher* combinationMatcher = config.add_atom_matcher(); |
| combinationMatcher->set_id(StringToId("SyncOrWakelockMatcher")); |
| combinationMatcher->mutable_combination()->set_operation(LogicalOperation::OR); |
| addMatcherToMatcherCombination(syncStartMatcher, combinationMatcher); |
| addMatcherToMatcherCombination(wakelockAcquireMatcher, combinationMatcher); |
| |
| Predicate screenOnPredicate = CreateScreenIsOnPredicate(); |
| *config.add_predicate() = screenOnPredicate; |
| Predicate unpluggedPredicate = CreateDeviceUnpluggedPredicate(); |
| *config.add_predicate() = unpluggedPredicate; |
| |
| Predicate* combinationPredicate = config.add_predicate(); |
| combinationPredicate->set_id(StringToId("ScreenOnOrUnpluggedPred)")); |
| combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR); |
| addPredicateToPredicateCombination(screenOnPredicate, combinationPredicate); |
| addPredicateToPredicateCombination(unpluggedPredicate, combinationPredicate); |
| |
| EventMetric eventPersist = |
| createEventMetric("SyncOrWlWhileScreenOnOrUnplugged", combinationMatcher->id(), |
| combinationPredicate->id()); |
| EventMetric eventChange = createEventMetric( |
| "WakelockWhileScreenOn", wakelockAcquireMatcher.id(), screenOnPredicate.id()); |
| EventMetric eventRemove = createEventMetric("Syncs", syncStartMatcher.id(), nullopt); |
| |
| *config.add_event_metric() = eventRemove; |
| *config.add_event_metric() = eventPersist; |
| *config.add_event_metric() = eventChange; |
| |
| ConfigKey key(123, 987); |
| uint64_t bucketStartTimeNs = 10000000000; // 0:10 |
| sp<StatsLogProcessor> processor = |
| CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); |
| |
| int app1Uid = 123; |
| vector<int> attributionUids1 = {app1Uid}; |
| vector<string> attributionTags1 = {"App1"}; |
| |
| // Initialize log events before update. |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "wl1")); // Not kept. |
| events.push_back(CreateScreenStateChangedEvent( |
| bucketStartTimeNs + 10 * NS_PER_SEC, |
| android::view::DISPLAY_STATE_ON)); // Condition true for change. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 15 * NS_PER_SEC, attributionUids1, |
| attributionTags1, |
| "sync1")); // Kept for persist & remove. |
| events.push_back(CreateBatteryStateChangedEvent( |
| bucketStartTimeNs + 20 * NS_PER_SEC, |
| BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // Condition true for preserve. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "wl2")); // Kept by persist and change. |
| events.push_back(CreateScreenStateChangedEvent( |
| bucketStartTimeNs + 30 * NS_PER_SEC, |
| android::view::DISPLAY_STATE_OFF)); // Condition false for change. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 35 * NS_PER_SEC, attributionUids1, |
| attributionTags1, |
| "sync2")); // Kept for persist & remove. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 40 * NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "wl3")); // Kept by persist. |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| // Do update. Add matchers/conditions in different order to force indices to change. |
| StatsdConfig newConfig; |
| newConfig.add_allowed_log_source("AID_ROOT"); |
| |
| *newConfig.add_atom_matcher() = screenOnMatcher; |
| *newConfig.add_atom_matcher() = batteryPluggedUsbMatcher; |
| *newConfig.add_atom_matcher() = syncStartMatcher; |
| *newConfig.add_atom_matcher() = *combinationMatcher; |
| *newConfig.add_atom_matcher() = wakelockAcquireMatcher; |
| *newConfig.add_atom_matcher() = screenOffMatcher; |
| *newConfig.add_atom_matcher() = unpluggedMatcher; |
| *newConfig.add_predicate() = *combinationPredicate; |
| *newConfig.add_predicate() = unpluggedPredicate; |
| *newConfig.add_predicate() = screenOnPredicate; |
| |
| // Add metrics. Note that the condition of eventChange will go from false to true. |
| eventChange.set_condition(unpluggedPredicate.id()); |
| *newConfig.add_event_metric() = eventChange; |
| EventMetric eventNew = createEventMetric("ScreenOn", screenOnMatcher.id(), nullopt); |
| *newConfig.add_event_metric() = eventNew; |
| *newConfig.add_event_metric() = eventPersist; |
| |
| int64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; |
| processor->OnConfigUpdated(updateTimeNs, key, newConfig); |
| |
| // Send events after the update. |
| events.clear(); |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 65 * NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "wl4")); // Kept by preserve & change. |
| events.push_back(CreateBatteryStateChangedEvent( |
| bucketStartTimeNs + 70 * NS_PER_SEC, |
| BatteryPluggedStateEnum::BATTERY_PLUGGED_USB)); // All conditions are false. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 75 * NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "wl5")); // Not kept. |
| events.push_back(CreateScreenStateChangedEvent( |
| bucketStartTimeNs + 80 * NS_PER_SEC, |
| android::view::DISPLAY_STATE_ON)); // Condition true for preserve, event kept by new. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 85 * NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "wl6")); // Kept by preserve. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 90 * NS_PER_SEC, attributionUids1, |
| attributionTags1, "sync3")); // Kept by preserve. |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| uint64_t dumpTimeNs = bucketStartTimeNs + 100 * NS_PER_SEC; |
| ConfigMetricsReportList reports; |
| vector<uint8_t> buffer; |
| processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); |
| EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); |
| backfillDimensionPath(&reports); |
| backfillStringInReport(&reports); |
| backfillStartEndTimestamp(&reports); |
| ASSERT_EQ(reports.reports_size(), 2); |
| |
| // Report from before update. |
| ConfigMetricsReport report = reports.reports(0); |
| ASSERT_EQ(report.metrics_size(), 3); |
| // Event remove. Captured sync events. There were 2 syncs before the update. |
| StatsLogReport eventRemoveBefore = report.metrics(0); |
| EXPECT_EQ(eventRemoveBefore.metric_id(), eventRemove.id()); |
| EXPECT_TRUE(eventRemoveBefore.has_event_metrics()); |
| ASSERT_EQ(eventRemoveBefore.event_metrics().data_size(), 2); |
| auto data = eventRemoveBefore.event_metrics().data(0); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 15 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().sync_state_changed().sync_name(), "sync1"); |
| data = eventRemoveBefore.event_metrics().data(1); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 35 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().sync_state_changed().sync_name(), "sync2"); |
| |
| // Captured wakelocks & syncs while screen on or unplugged. There were 2 wakelocks and 2 syncs. |
| StatsLogReport eventPersistBefore = report.metrics(1); |
| EXPECT_EQ(eventPersistBefore.metric_id(), eventPersist.id()); |
| EXPECT_TRUE(eventPersistBefore.has_event_metrics()); |
| ASSERT_EQ(eventPersistBefore.event_metrics().data_size(), 3); |
| data = eventPersistBefore.event_metrics().data(0); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 25 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().wakelock_state_changed().tag(), "wl2"); |
| data = eventPersistBefore.event_metrics().data(1); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 35 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().sync_state_changed().sync_name(), "sync2"); |
| data = eventPersistBefore.event_metrics().data(2); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 40 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().wakelock_state_changed().tag(), "wl3"); |
| |
| // Captured wakelock events while screen on. There was 1 before the update. |
| StatsLogReport eventChangeBefore = report.metrics(2); |
| EXPECT_EQ(eventChangeBefore.metric_id(), eventChange.id()); |
| EXPECT_TRUE(eventChangeBefore.has_event_metrics()); |
| ASSERT_EQ(eventChangeBefore.event_metrics().data_size(), 1); |
| data = eventChangeBefore.event_metrics().data(0); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 25 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().wakelock_state_changed().tag(), "wl2"); |
| |
| // Report from after update. |
| report = reports.reports(1); |
| ASSERT_EQ(report.metrics_size(), 3); |
| // Captured wakelocks while unplugged. There was 1 after the update. |
| StatsLogReport eventChangeAfter = report.metrics(0); |
| EXPECT_EQ(eventChangeAfter.metric_id(), eventChange.id()); |
| EXPECT_TRUE(eventChangeAfter.has_event_metrics()); |
| ASSERT_EQ(eventChangeAfter.event_metrics().data_size(), 1); |
| data = eventChangeAfter.event_metrics().data(0); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 65 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().wakelock_state_changed().tag(), "wl4"); |
| |
| // Captured screen on events. There was 1 after the update. |
| StatsLogReport eventNewAfter = report.metrics(1); |
| EXPECT_EQ(eventNewAfter.metric_id(), eventNew.id()); |
| EXPECT_TRUE(eventNewAfter.has_event_metrics()); |
| ASSERT_EQ(eventNewAfter.event_metrics().data_size(), 1); |
| data = eventNewAfter.event_metrics().data(0); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 80 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().screen_state_changed().state(), android::view::DISPLAY_STATE_ON); |
| |
| // There were 2 wakelocks and 1 sync after the update while the condition was true. |
| StatsLogReport eventPersistAfter = report.metrics(2); |
| EXPECT_EQ(eventPersistAfter.metric_id(), eventPersist.id()); |
| EXPECT_TRUE(eventPersistAfter.has_event_metrics()); |
| ASSERT_EQ(eventPersistAfter.event_metrics().data_size(), 3); |
| data = eventPersistAfter.event_metrics().data(0); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 65 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().wakelock_state_changed().tag(), "wl4"); |
| data = eventPersistAfter.event_metrics().data(1); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 85 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().wakelock_state_changed().tag(), "wl6"); |
| data = eventPersistAfter.event_metrics().data(2); |
| EXPECT_EQ(data.elapsed_timestamp_nanos(), bucketStartTimeNs + 90 * NS_PER_SEC); |
| EXPECT_EQ(data.atom().sync_state_changed().sync_name(), "sync3"); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestCountMetric) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| |
| AtomMatcher syncStartMatcher = CreateSyncStartAtomMatcher(); |
| *config.add_atom_matcher() = syncStartMatcher; |
| AtomMatcher wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); |
| *config.add_atom_matcher() = wakelockAcquireMatcher; |
| AtomMatcher wakelockReleaseMatcher = CreateReleaseWakelockAtomMatcher(); |
| *config.add_atom_matcher() = wakelockReleaseMatcher; |
| AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); |
| *config.add_atom_matcher() = screenOnMatcher; |
| AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); |
| *config.add_atom_matcher() = screenOffMatcher; |
| |
| Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); |
| // The predicate is dimensioning by first attribution node by uid. |
| *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| *config.add_predicate() = holdingWakelockPredicate; |
| |
| Predicate screenOnPredicate = CreateScreenIsOnPredicate(); |
| *config.add_predicate() = screenOnPredicate; |
| |
| Predicate* combination = config.add_predicate(); |
| combination->set_id(StringToId("ScreenOnAndHoldingWL)")); |
| combination->mutable_combination()->set_operation(LogicalOperation::AND); |
| addPredicateToPredicateCombination(screenOnPredicate, combination); |
| addPredicateToPredicateCombination(holdingWakelockPredicate, combination); |
| |
| State uidProcessState = CreateUidProcessState(); |
| *config.add_state() = uidProcessState; |
| |
| CountMetric countPersist = |
| createCountMetric("CountSyncPerUidWhileScreenOnHoldingWLSliceProcessState", |
| syncStartMatcher.id(), combination->id(), {uidProcessState.id()}); |
| *countPersist.mutable_dimensions_in_what() = |
| CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST}); |
| // Links between sync state atom and condition of uid is holding wakelock. |
| MetricConditionLink* links = countPersist.add_links(); |
| links->set_condition(holdingWakelockPredicate.id()); |
| *links->mutable_fields_in_what() = |
| CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST}); |
| *links->mutable_fields_in_condition() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| MetricStateLink* stateLink = countPersist.add_state_link(); |
| stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED); |
| *stateLink->mutable_fields_in_what() = |
| CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST}); |
| *stateLink->mutable_fields_in_state() = |
| CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/}); |
| |
| CountMetric countChange = createCountMetric("Count*WhileScreenOn", syncStartMatcher.id(), |
| screenOnPredicate.id(), {}); |
| CountMetric countRemove = createCountMetric("CountSync", syncStartMatcher.id(), nullopt, {}); |
| *config.add_count_metric() = countRemove; |
| *config.add_count_metric() = countPersist; |
| *config.add_count_metric() = countChange; |
| |
| ConfigKey key(123, 987); |
| uint64_t bucketStartTimeNs = 10000000000; // 0:10 |
| uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL; |
| sp<StatsLogProcessor> processor = |
| CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); |
| |
| int app1Uid = 123, app2Uid = 456; |
| vector<int> attributionUids1 = {app1Uid}; |
| vector<string> attributionTags1 = {"App1"}; |
| vector<int> attributionUids2 = {app2Uid}; |
| vector<string> attributionTags2 = {"App2"}; |
| |
| // Initialize log events before update. Counts are for countPersist since others are simpler. |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateUidProcessStateChangedEvent( |
| bucketStartTimeNs + 2 * NS_PER_SEC, app1Uid, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); |
| events.push_back(CreateUidProcessStateChangedEvent( |
| bucketStartTimeNs + 3 * NS_PER_SEC, app2Uid, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 5 * NS_PER_SEC, attributionUids1, |
| attributionTags1, "sync_name")); // Not counted. |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_ON)); |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 15 * NS_PER_SEC, |
| attributionUids1, attributionTags1, "wl1")); |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 20 * NS_PER_SEC, attributionUids1, |
| attributionTags1, "sync_name")); // Counted. uid1 = 1. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 21 * NS_PER_SEC, attributionUids2, |
| attributionTags2, "sync_name")); // Not counted. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * NS_PER_SEC, |
| attributionUids2, attributionTags2, "wl2")); |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 30 * NS_PER_SEC, attributionUids1, |
| attributionTags1, "sync_name")); // Counted. uid1 = 2. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 31 * NS_PER_SEC, attributionUids2, |
| attributionTags2, "sync_name")); // Counted. uid2 = 1 |
| events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC, |
| attributionUids1, attributionTags1, "wl1")); |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| // Do update. Add matchers/conditions in different order to force indices to change. |
| StatsdConfig newConfig; |
| newConfig.add_allowed_log_source("AID_ROOT"); |
| |
| *newConfig.add_atom_matcher() = screenOnMatcher; |
| *newConfig.add_atom_matcher() = screenOffMatcher; |
| *newConfig.add_atom_matcher() = syncStartMatcher; |
| *newConfig.add_atom_matcher() = wakelockAcquireMatcher; |
| *newConfig.add_atom_matcher() = wakelockReleaseMatcher; |
| *newConfig.add_predicate() = *combination; |
| *newConfig.add_predicate() = holdingWakelockPredicate; |
| *newConfig.add_predicate() = screenOnPredicate; |
| *newConfig.add_state() = uidProcessState; |
| |
| countChange.set_what(screenOnMatcher.id()); |
| *newConfig.add_count_metric() = countChange; |
| CountMetric countNew = createCountMetric("CountWlWhileScreenOn", wakelockAcquireMatcher.id(), |
| screenOnPredicate.id(), {}); |
| *newConfig.add_count_metric() = countNew; |
| *newConfig.add_count_metric() = countPersist; |
| |
| int64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; |
| processor->OnConfigUpdated(updateTimeNs, key, newConfig); |
| |
| // Send events after the update. Counts reset to 0 since this is a new bucket. |
| events.clear(); |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 65 * NS_PER_SEC, attributionUids1, |
| attributionTags1, "sync_name")); // Not counted. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 66 * NS_PER_SEC, attributionUids2, |
| attributionTags2, "sync_name")); // Counted. uid2 = 1. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 70 * NS_PER_SEC, |
| attributionUids1, attributionTags1, "wl1")); |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 75 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 80 * NS_PER_SEC, attributionUids2, |
| attributionTags2, "sync_name")); // Not counted. |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 85 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_ON)); |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 90 * NS_PER_SEC, attributionUids1, |
| attributionTags1, "sync_name")); // Counted. uid1 = 1. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 11 * NS_PER_SEC, attributionUids2, |
| attributionTags2, "sync_name")); // Counted. uid2 = 2. |
| // Flushes bucket. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + NS_PER_SEC, |
| attributionUids1, attributionTags1, "wl2")); |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| uint64_t dumpTimeNs = bucketStartTimeNs + bucketSizeNs + 10 * NS_PER_SEC; |
| ConfigMetricsReportList reports; |
| vector<uint8_t> buffer; |
| processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); |
| EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); |
| backfillDimensionPath(&reports); |
| backfillStringInReport(&reports); |
| backfillStartEndTimestamp(&reports); |
| ASSERT_EQ(reports.reports_size(), 2); |
| |
| // Report from before update. |
| ConfigMetricsReport report = reports.reports(0); |
| ASSERT_EQ(report.metrics_size(), 3); |
| // Count syncs. There were 5 syncs before the update. |
| StatsLogReport countRemoveBefore = report.metrics(0); |
| EXPECT_EQ(countRemoveBefore.metric_id(), countRemove.id()); |
| EXPECT_TRUE(countRemoveBefore.has_count_metrics()); |
| ASSERT_EQ(countRemoveBefore.count_metrics().data_size(), 1); |
| auto data = countRemoveBefore.count_metrics().data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 5); |
| |
| // Uid 1 had 2 syncs, uid 2 had 1 sync. |
| StatsLogReport countPersistBefore = report.metrics(1); |
| EXPECT_EQ(countPersistBefore.metric_id(), countPersist.id()); |
| EXPECT_TRUE(countPersistBefore.has_count_metrics()); |
| StatsLogReport::CountMetricDataWrapper countMetrics; |
| sortMetricDataByDimensionsValue(countPersistBefore.count_metrics(), &countMetrics); |
| ASSERT_EQ(countMetrics.data_size(), 2); |
| data = countMetrics.data(0); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app1Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 2); |
| |
| data = countMetrics.data(1); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app2Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 1); |
| |
| // Counts syncs while screen on. There were 4 before the update. |
| StatsLogReport countChangeBefore = report.metrics(2); |
| EXPECT_EQ(countChangeBefore.metric_id(), countChange.id()); |
| EXPECT_TRUE(countChangeBefore.has_count_metrics()); |
| ASSERT_EQ(countChangeBefore.count_metrics().data_size(), 1); |
| data = countChangeBefore.count_metrics().data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 4); |
| |
| // Report from after update. |
| report = reports.reports(1); |
| ASSERT_EQ(report.metrics_size(), 3); |
| // Count screen on while screen is on. There was 1 after the update. |
| StatsLogReport countChangeAfter = report.metrics(0); |
| EXPECT_EQ(countChangeAfter.metric_id(), countChange.id()); |
| EXPECT_TRUE(countChangeAfter.has_count_metrics()); |
| ASSERT_EQ(countChangeAfter.count_metrics().data_size(), 1); |
| data = countChangeAfter.count_metrics().data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), updateTimeNs, bucketStartTimeNs + bucketSizeNs, 1); |
| |
| // Count wl acquires while screen on. There were 2, one in each bucket. |
| StatsLogReport countNewAfter = report.metrics(1); |
| EXPECT_EQ(countNewAfter.metric_id(), countNew.id()); |
| EXPECT_TRUE(countNewAfter.has_count_metrics()); |
| ASSERT_EQ(countNewAfter.count_metrics().data_size(), 1); |
| data = countNewAfter.count_metrics().data(0); |
| ASSERT_EQ(data.bucket_info_size(), 2); |
| ValidateCountBucket(data.bucket_info(0), updateTimeNs, bucketStartTimeNs + bucketSizeNs, 1); |
| ValidateCountBucket(data.bucket_info(1), bucketStartTimeNs + bucketSizeNs, dumpTimeNs, 1); |
| |
| // Uid 1 had 1 sync, uid 2 had 2 syncs. |
| StatsLogReport countPersistAfter = report.metrics(2); |
| EXPECT_EQ(countPersistAfter.metric_id(), countPersist.id()); |
| EXPECT_TRUE(countPersistAfter.has_count_metrics()); |
| countMetrics.Clear(); |
| sortMetricDataByDimensionsValue(countPersistAfter.count_metrics(), &countMetrics); |
| ASSERT_EQ(countMetrics.data_size(), 2); |
| data = countMetrics.data(0); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app1Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), updateTimeNs, bucketStartTimeNs + bucketSizeNs, 1); |
| |
| data = countMetrics.data(1); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app2Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), updateTimeNs, bucketStartTimeNs + bucketSizeNs, 2); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestDurationMetric) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| |
| AtomMatcher syncStartMatcher = CreateSyncStartAtomMatcher(); |
| *config.add_atom_matcher() = syncStartMatcher; |
| AtomMatcher syncStopMatcher = CreateSyncEndAtomMatcher(); |
| *config.add_atom_matcher() = syncStopMatcher; |
| AtomMatcher wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); |
| *config.add_atom_matcher() = wakelockAcquireMatcher; |
| AtomMatcher wakelockReleaseMatcher = CreateReleaseWakelockAtomMatcher(); |
| *config.add_atom_matcher() = wakelockReleaseMatcher; |
| AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); |
| *config.add_atom_matcher() = screenOnMatcher; |
| AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); |
| *config.add_atom_matcher() = screenOffMatcher; |
| |
| Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); |
| // The predicate is dimensioning by first attribution node by uid. |
| *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| *config.add_predicate() = holdingWakelockPredicate; |
| |
| Predicate screenOnPredicate = CreateScreenIsOnPredicate(); |
| *config.add_predicate() = screenOnPredicate; |
| |
| Predicate syncPredicate = CreateIsSyncingPredicate(); |
| // The predicate is dimensioning by first attribution node by uid. |
| *syncPredicate.mutable_simple_predicate()->mutable_dimensions() = |
| CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST}); |
| *config.add_predicate() = syncPredicate; |
| |
| State uidProcessState = CreateUidProcessState(); |
| *config.add_state() = uidProcessState; |
| |
| DurationMetric durationSumPersist = |
| createDurationMetric("DurSyncPerUidWhileHoldingWLSliceProcessState", syncPredicate.id(), |
| holdingWakelockPredicate.id(), {uidProcessState.id()}); |
| *durationSumPersist.mutable_dimensions_in_what() = |
| CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST}); |
| // Links between sync state atom and condition of uid is holding wakelock. |
| MetricConditionLink* links = durationSumPersist.add_links(); |
| links->set_condition(holdingWakelockPredicate.id()); |
| *links->mutable_fields_in_what() = |
| CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST}); |
| *links->mutable_fields_in_condition() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| MetricStateLink* stateLink = durationSumPersist.add_state_link(); |
| stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED); |
| *stateLink->mutable_fields_in_what() = |
| CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST}); |
| *stateLink->mutable_fields_in_state() = |
| CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/}); |
| |
| DurationMetric durationMaxPersist = |
| createDurationMetric("DurMaxSyncPerUidWhileHoldingWL", syncPredicate.id(), |
| holdingWakelockPredicate.id(), {}); |
| durationMaxPersist.set_aggregation_type(DurationMetric::MAX_SPARSE); |
| *durationMaxPersist.mutable_dimensions_in_what() = |
| CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST}); |
| // Links between sync state atom and condition of uid is holding wakelock. |
| links = durationMaxPersist.add_links(); |
| links->set_condition(holdingWakelockPredicate.id()); |
| *links->mutable_fields_in_what() = |
| CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST}); |
| *links->mutable_fields_in_condition() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| |
| DurationMetric durationChange = createDurationMetric("Dur*WhileScreenOn", syncPredicate.id(), |
| screenOnPredicate.id(), {}); |
| DurationMetric durationRemove = |
| createDurationMetric("DurScreenOn", screenOnPredicate.id(), nullopt, {}); |
| *config.add_duration_metric() = durationMaxPersist; |
| *config.add_duration_metric() = durationRemove; |
| *config.add_duration_metric() = durationSumPersist; |
| *config.add_duration_metric() = durationChange; |
| |
| ConfigKey key(123, 987); |
| uint64_t bucketStartTimeNs = 10000000000; // 0:10 |
| uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL; |
| sp<StatsLogProcessor> processor = |
| CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); |
| |
| int app1Uid = 123, app2Uid = 456, app3Uid = 789; |
| vector<int> attributionUids1 = {app1Uid}; |
| vector<string> attributionTags1 = {"App1"}; |
| vector<int> attributionUids2 = {app2Uid}; |
| vector<string> attributionTags2 = {"App2"}; |
| vector<int> attributionUids3 = {app3Uid}; |
| vector<string> attributionTags3 = {"App3"}; |
| |
| // Initialize log events before update. Comments provided for durations of persisted metrics. |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateUidProcessStateChangedEvent( |
| bucketStartTimeNs + 2 * NS_PER_SEC, app1Uid, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); |
| events.push_back(CreateUidProcessStateChangedEvent( |
| bucketStartTimeNs + 3 * NS_PER_SEC, app2Uid, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); |
| events.push_back(CreateScreenStateChangedEvent( |
| bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON)); |
| events.push_back(CreateUidProcessStateChangedEvent( |
| bucketStartTimeNs + 6 * NS_PER_SEC, app3Uid, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1, |
| attributionTags1, "sync_name")); // uid1 paused. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 15 * NS_PER_SEC, |
| attributionUids2, attributionTags2, |
| "wl2")); // uid2 cond true. |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 25 * NS_PER_SEC, attributionUids2, |
| attributionTags2, "sync_name")); // Uid 2 start t=25. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 27 * NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "wl1")); // Uid 1 start t=27. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 35 * NS_PER_SEC, attributionUids3, |
| attributionTags3, "sync_name")); // Uid 3 paused. |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_ON)); |
| events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 45 * NS_PER_SEC, attributionUids2, |
| attributionTags2, |
| "sync_name")); // Uid 2 stop. sum/max = 20. |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| // Do update. Add matchers/conditions in different order to force indices to change. |
| StatsdConfig newConfig; |
| newConfig.add_allowed_log_source("AID_ROOT"); |
| |
| *newConfig.add_atom_matcher() = wakelockReleaseMatcher; |
| *newConfig.add_atom_matcher() = syncStopMatcher; |
| *newConfig.add_atom_matcher() = screenOnMatcher; |
| *newConfig.add_atom_matcher() = screenOffMatcher; |
| *newConfig.add_atom_matcher() = syncStartMatcher; |
| *newConfig.add_atom_matcher() = wakelockAcquireMatcher; |
| *newConfig.add_predicate() = syncPredicate; |
| *newConfig.add_predicate() = screenOnPredicate; |
| *newConfig.add_predicate() = holdingWakelockPredicate; |
| *newConfig.add_state() = uidProcessState; |
| |
| durationChange.set_what(holdingWakelockPredicate.id()); |
| *newConfig.add_duration_metric() = durationChange; |
| DurationMetric durationNew = |
| createDurationMetric("DurationSync", syncPredicate.id(), nullopt, {}); |
| *newConfig.add_duration_metric() = durationNew; |
| *newConfig.add_duration_metric() = durationMaxPersist; |
| *newConfig.add_duration_metric() = durationSumPersist; |
| |
| // At update, only uid 1 is syncing & holding a wakelock, duration=33. Max is paused for uid3. |
| // Before the update, only uid2 will report a duration for max, since others are started/paused. |
| int64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; |
| processor->OnConfigUpdated(updateTimeNs, key, newConfig); |
| |
| // Send events after the update. |
| events.clear(); |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 65 * NS_PER_SEC, |
| attributionUids3, attributionTags3, |
| "wl3")); // Uid3 start t=65. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 70 * NS_PER_SEC, attributionUids2, |
| attributionTags2, "sync_name")); // Uid2 start t=70. |
| events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 75 * NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "wl1")); // Uid1 Pause. Dur = 15. |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 81 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); |
| events.push_back(CreateUidProcessStateChangedEvent( |
| bucketStartTimeNs + 85 * NS_PER_SEC, app3Uid, |
| android::app::ProcessStateEnum:: |
| PROCESS_STATE_IMPORTANT_FOREGROUND)); // Uid3 Sum in BG: 20. FG starts. Max is |
| // unchanged. |
| events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 90 * NS_PER_SEC, attributionUids3, |
| attributionTags3, |
| "sync_name")); // Uid3 stop. Sum in FG: 5. MAX: 25. |
| |
| // Flushes bucket. Sum: uid1=15, uid2=bucketSize - 70, uid3 = 20 in FG, 5 in BG. Max: uid3=25, |
| // others don't report. |
| events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "sync_name")); // Uid1 stop. Max=15+33=48, Sum is 0. |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; |
| uint64_t dumpTimeNs = bucketStartTimeNs + bucketSizeNs + 10 * NS_PER_SEC; |
| ConfigMetricsReportList reports; |
| vector<uint8_t> buffer; |
| // In the partial bucket, Sum for uid2 = 10, Max for Uid1 = 48. Rest are unreported. |
| processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); |
| EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); |
| backfillDimensionPath(&reports); |
| backfillStringInReport(&reports); |
| backfillStartEndTimestamp(&reports); |
| ASSERT_EQ(reports.reports_size(), 2); |
| |
| // Report from before update. |
| ConfigMetricsReport report = reports.reports(0); |
| ASSERT_EQ(report.metrics_size(), 4); |
| // Max duration of syncs per uid while uid holding WL. Only uid2 should have data. |
| StatsLogReport durationMaxPersistBefore = report.metrics(0); |
| EXPECT_EQ(durationMaxPersistBefore.metric_id(), durationMaxPersist.id()); |
| EXPECT_TRUE(durationMaxPersistBefore.has_duration_metrics()); |
| StatsLogReport::DurationMetricDataWrapper durationMetrics; |
| sortMetricDataByDimensionsValue(durationMaxPersistBefore.duration_metrics(), &durationMetrics); |
| ASSERT_EQ(durationMetrics.data_size(), 1); |
| auto data = durationMetrics.data(0); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app2Uid); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 20 * NS_PER_SEC); |
| |
| // Screen on time. ON: 5, OFF: 20, ON: 40. Update: 60. Result: 35 |
| StatsLogReport durationRemoveBefore = report.metrics(1); |
| EXPECT_EQ(durationRemoveBefore.metric_id(), durationRemove.id()); |
| EXPECT_TRUE(durationRemoveBefore.has_duration_metrics()); |
| durationMetrics.Clear(); |
| sortMetricDataByDimensionsValue(durationRemoveBefore.duration_metrics(), &durationMetrics); |
| ASSERT_EQ(durationMetrics.data_size(), 1); |
| data = durationMetrics.data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 35 * NS_PER_SEC); |
| |
| // Duration of syncs per uid while uid holding WL, slice screen. Uid1=33, uid2=20. |
| StatsLogReport durationSumPersistBefore = report.metrics(2); |
| EXPECT_EQ(durationSumPersistBefore.metric_id(), durationSumPersist.id()); |
| EXPECT_TRUE(durationSumPersistBefore.has_duration_metrics()); |
| durationMetrics.Clear(); |
| sortMetricDataByDimensionsValue(durationSumPersistBefore.duration_metrics(), &durationMetrics); |
| ASSERT_EQ(durationMetrics.data_size(), 2); |
| data = durationMetrics.data(0); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app1Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 33 * NS_PER_SEC); |
| |
| data = durationMetrics.data(1); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app2Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 20 * NS_PER_SEC); |
| |
| // Duration of syncs while screen on. Start: 10, pause: 20, start: 40 Update: 60. Total: 30. |
| StatsLogReport durationChangeBefore = report.metrics(3); |
| EXPECT_EQ(durationChangeBefore.metric_id(), durationChange.id()); |
| EXPECT_TRUE(durationChangeBefore.has_duration_metrics()); |
| durationMetrics.Clear(); |
| sortMetricDataByDimensionsValue(durationChangeBefore.duration_metrics(), &durationMetrics); |
| ASSERT_EQ(durationMetrics.data_size(), 1); |
| data = durationMetrics.data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 30 * NS_PER_SEC); |
| |
| // Report from after update. |
| report = reports.reports(1); |
| ASSERT_EQ(report.metrics_size(), 4); |
| // Duration of WL while screen on. Update: 60, pause: 81. Total: 21. |
| StatsLogReport durationChangeAfter = report.metrics(0); |
| EXPECT_EQ(durationChangeAfter.metric_id(), durationChange.id()); |
| EXPECT_TRUE(durationChangeAfter.has_duration_metrics()); |
| durationMetrics.Clear(); |
| sortMetricDataByDimensionsValue(durationChangeAfter.duration_metrics(), &durationMetrics); |
| ASSERT_EQ(durationMetrics.data_size(), 1); |
| data = durationMetrics.data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), updateTimeNs, bucketEndTimeNs, 21 * NS_PER_SEC); |
| |
| // Duration of syncs. Always true since at least 1 uid is always syncing. |
| StatsLogReport durationNewAfter = report.metrics(1); |
| EXPECT_EQ(durationNewAfter.metric_id(), durationNew.id()); |
| EXPECT_TRUE(durationNewAfter.has_duration_metrics()); |
| durationMetrics.Clear(); |
| sortMetricDataByDimensionsValue(durationNewAfter.duration_metrics(), &durationMetrics); |
| ASSERT_EQ(durationMetrics.data_size(), 1); |
| data = durationMetrics.data(0); |
| ASSERT_EQ(data.bucket_info_size(), 2); |
| ValidateDurationBucket(data.bucket_info(0), updateTimeNs, bucketEndTimeNs, |
| bucketEndTimeNs - updateTimeNs); |
| ValidateDurationBucket(data.bucket_info(1), bucketEndTimeNs, dumpTimeNs, |
| dumpTimeNs - bucketEndTimeNs); |
| |
| // Max duration of syncs per uid while uid holding WL. |
| StatsLogReport durationMaxPersistAfter = report.metrics(2); |
| EXPECT_EQ(durationMaxPersistAfter.metric_id(), durationMaxPersist.id()); |
| EXPECT_TRUE(durationMaxPersistAfter.has_duration_metrics()); |
| durationMetrics.Clear(); |
| sortMetricDataByDimensionsValue(durationMaxPersistAfter.duration_metrics(), &durationMetrics); |
| ASSERT_EQ(durationMetrics.data_size(), 2); |
| |
| // Uid 1. Duration = 48 in the later bucket. |
| data = durationMetrics.data(0); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app1Uid); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), bucketEndTimeNs, dumpTimeNs, 48 * NS_PER_SEC); |
| |
| // Uid 3. Duration = 25. |
| data = durationMetrics.data(1); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app3Uid); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), updateTimeNs, bucketEndTimeNs, 25 * NS_PER_SEC); |
| |
| // Duration of syncs per uid while uid holding WL, slice screen. |
| StatsLogReport durationSumPersistAfter = report.metrics(3); |
| EXPECT_EQ(durationSumPersistAfter.metric_id(), durationSumPersist.id()); |
| EXPECT_TRUE(durationSumPersistAfter.has_duration_metrics()); |
| durationMetrics.Clear(); |
| sortMetricDataByDimensionsValue(durationSumPersistAfter.duration_metrics(), &durationMetrics); |
| ASSERT_EQ(durationMetrics.data_size(), 4); |
| |
| // Uid 1 in BG. Duration = 15. |
| data = durationMetrics.data(0); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app1Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), updateTimeNs, bucketEndTimeNs, 15 * NS_PER_SEC); |
| |
| // Uid 2 in FG. Duration = bucketSize - 70 in first bucket, 10 in second bucket. |
| data = durationMetrics.data(1); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app2Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 2); |
| ValidateDurationBucket(data.bucket_info(0), updateTimeNs, bucketEndTimeNs, |
| bucketSizeNs - 70 * NS_PER_SEC); |
| ValidateDurationBucket(data.bucket_info(1), bucketEndTimeNs, dumpTimeNs, 10 * NS_PER_SEC); |
| |
| // Uid 3 in FG. Duration = 5. |
| data = durationMetrics.data(2); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app3Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), updateTimeNs, bucketEndTimeNs, 5 * NS_PER_SEC); |
| |
| // Uid 3 in BG. Duration = 20. |
| data = durationMetrics.data(3); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::SYNC_STATE_CHANGED, app3Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateDurationBucket(data.bucket_info(0), updateTimeNs, bucketEndTimeNs, 20 * NS_PER_SEC); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestGaugeMetric) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. |
| |
| AtomMatcher appStartMatcher = CreateSimpleAtomMatcher("AppStart", util::APP_START_OCCURRED); |
| *config.add_atom_matcher() = appStartMatcher; |
| AtomMatcher backgroundMatcher = CreateMoveToBackgroundAtomMatcher(); |
| *config.add_atom_matcher() = backgroundMatcher; |
| AtomMatcher foregroundMatcher = CreateMoveToForegroundAtomMatcher(); |
| *config.add_atom_matcher() = foregroundMatcher; |
| AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); |
| *config.add_atom_matcher() = screenOnMatcher; |
| AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); |
| *config.add_atom_matcher() = screenOffMatcher; |
| AtomMatcher subsystemSleepMatcher = |
| CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE); |
| *config.add_atom_matcher() = subsystemSleepMatcher; |
| |
| Predicate isInBackgroundPredicate = CreateIsInBackgroundPredicate(); |
| *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = |
| CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid field*/}); |
| *config.add_predicate() = isInBackgroundPredicate; |
| |
| Predicate screenOnPredicate = CreateScreenIsOnPredicate(); |
| *config.add_predicate() = screenOnPredicate; |
| |
| GaugeMetric gaugePullPersist = |
| createGaugeMetric("SubsystemSleepWhileScreenOn", subsystemSleepMatcher.id(), |
| GaugeMetric::RANDOM_ONE_SAMPLE, screenOnPredicate.id(), {}); |
| *gaugePullPersist.mutable_dimensions_in_what() = |
| CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); |
| |
| GaugeMetric gaugePushPersist = |
| createGaugeMetric("AppStartWhileInBg", appStartMatcher.id(), |
| GaugeMetric::FIRST_N_SAMPLES, isInBackgroundPredicate.id(), nullopt); |
| *gaugePushPersist.mutable_dimensions_in_what() = |
| CreateDimensions(util::APP_START_OCCURRED, {1 /*uid field*/}); |
| // Links between sync state atom and condition of uid is holding wakelock. |
| MetricConditionLink* links = gaugePushPersist.add_links(); |
| links->set_condition(isInBackgroundPredicate.id()); |
| *links->mutable_fields_in_what() = |
| CreateDimensions(util::APP_START_OCCURRED, {1 /*uid field*/}); |
| *links->mutable_fields_in_condition() = |
| CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid field*/}); |
| |
| GaugeMetric gaugeChange = createGaugeMetric("GaugeScrOn", screenOnMatcher.id(), |
| GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt); |
| GaugeMetric gaugeRemove = |
| createGaugeMetric("GaugeSubsysTriggerScr", subsystemSleepMatcher.id(), |
| GaugeMetric::FIRST_N_SAMPLES, nullopt, screenOnMatcher.id()); |
| *gaugeRemove.mutable_dimensions_in_what() = |
| CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); |
| *config.add_gauge_metric() = gaugeRemove; |
| *config.add_gauge_metric() = gaugePullPersist; |
| *config.add_gauge_metric() = gaugeChange; |
| *config.add_gauge_metric() = gaugePushPersist; |
| |
| ConfigKey key(123, 987); |
| uint64_t bucketStartTimeNs = getElapsedRealtimeNs(); // 0:10 |
| uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL; |
| sp<StatsLogProcessor> processor = CreateStatsLogProcessor( |
| bucketStartTimeNs, bucketStartTimeNs, config, key, |
| SharedRefBase::make<FakeSubsystemSleepCallback>(), util::SUBSYSTEM_SLEEP_STATE); |
| |
| int app1Uid = 123, app2Uid = 456; |
| |
| // Initialize log events before update. |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateMoveToBackgroundEvent(bucketStartTimeNs + 5 * NS_PER_SEC, app1Uid)); |
| events.push_back(CreateAppStartOccurredEvent(bucketStartTimeNs + 10 * NS_PER_SEC, app1Uid, |
| "app1", AppStartOccurred::WARM, "", "", true, |
| /*start_msec*/ 101)); // Kept by gaugePushPersist. |
| events.push_back( |
| CreateAppStartOccurredEvent(bucketStartTimeNs + 15 * NS_PER_SEC, app2Uid, "app2", |
| AppStartOccurred::WARM, "", "", true, |
| /*start_msec*/ 201)); // Not kept by gaugePushPersist. |
| events.push_back(CreateScreenStateChangedEvent( |
| bucketStartTimeNs + 20 * NS_PER_SEC, |
| android::view::DISPLAY_STATE_ON)); // Pulls gaugePullPersist and gaugeRemove. |
| events.push_back(CreateMoveToBackgroundEvent(bucketStartTimeNs + 25 * NS_PER_SEC, app2Uid)); |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 30 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); |
| events.push_back(CreateAppStartOccurredEvent(bucketStartTimeNs + 35 * NS_PER_SEC, app1Uid, |
| "app1", AppStartOccurred::WARM, "", "", true, |
| /*start_msec*/ 102)); // Kept by gaugePushPersist. |
| events.push_back(CreateAppStartOccurredEvent(bucketStartTimeNs + 40 * NS_PER_SEC, app2Uid, |
| "app2", AppStartOccurred::WARM, "", "", true, |
| /*start_msec*/ 202)); // Kept by gaugePushPersist. |
| events.push_back(CreateScreenStateChangedEvent( |
| bucketStartTimeNs + 45 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // Pulls gaugeRemove only. |
| events.push_back(CreateMoveToForegroundEvent(bucketStartTimeNs + 50 * NS_PER_SEC, app1Uid)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->mPullerManager->ForceClearPullerCache(); |
| processor->OnLogEvent(event.get()); |
| } |
| processor->mPullerManager->ForceClearPullerCache(); |
| |
| // Do the update. Add matchers/conditions in different order to force indices to change. |
| StatsdConfig newConfig; |
| newConfig.add_allowed_log_source("AID_ROOT"); |
| newConfig.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. |
| |
| *newConfig.add_atom_matcher() = screenOffMatcher; |
| *newConfig.add_atom_matcher() = foregroundMatcher; |
| *newConfig.add_atom_matcher() = appStartMatcher; |
| *newConfig.add_atom_matcher() = subsystemSleepMatcher; |
| *newConfig.add_atom_matcher() = backgroundMatcher; |
| *newConfig.add_atom_matcher() = screenOnMatcher; |
| |
| *newConfig.add_predicate() = isInBackgroundPredicate; |
| *newConfig.add_predicate() = screenOnPredicate; |
| |
| gaugeChange.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); |
| *newConfig.add_gauge_metric() = gaugeChange; |
| GaugeMetric gaugeNew = createGaugeMetric("GaugeSubsys", subsystemSleepMatcher.id(), |
| GaugeMetric::RANDOM_ONE_SAMPLE, {}, {}); |
| *gaugeNew.mutable_dimensions_in_what() = |
| CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); |
| *newConfig.add_gauge_metric() = gaugeNew; |
| *newConfig.add_gauge_metric() = gaugePushPersist; |
| *newConfig.add_gauge_metric() = gaugePullPersist; |
| |
| int64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; |
| // Update pulls gaugePullPersist and gaugeNew. |
| processor->OnConfigUpdated(updateTimeNs, key, newConfig); |
| |
| // Verify puller manager is properly set. |
| sp<StatsPullerManager> pullerManager = processor->mPullerManager; |
| EXPECT_EQ(pullerManager->mNextPullTimeNs, bucketStartTimeNs + bucketSizeNs); |
| ASSERT_EQ(pullerManager->mReceivers.size(), 1); |
| ASSERT_EQ(pullerManager->mReceivers.begin()->second.size(), 2); |
| |
| // Send events after the update. Counts reset to 0 since this is a new bucket. |
| events.clear(); |
| events.push_back( |
| CreateAppStartOccurredEvent(bucketStartTimeNs + 65 * NS_PER_SEC, app1Uid, "app1", |
| AppStartOccurred::WARM, "", "", true, |
| /*start_msec*/ 103)); // Not kept by gaugePushPersist. |
| events.push_back(CreateAppStartOccurredEvent(bucketStartTimeNs + 70 * NS_PER_SEC, app2Uid, |
| "app2", AppStartOccurred::WARM, "", "", true, |
| /*start_msec*/ 203)); // Kept by gaugePushPersist. |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 75 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 80 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_ON)); |
| events.push_back(CreateMoveToBackgroundEvent(bucketStartTimeNs + 85 * NS_PER_SEC, app1Uid)); |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 90 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); |
| events.push_back(CreateAppStartOccurredEvent(bucketStartTimeNs + 95 * NS_PER_SEC, app1Uid, |
| "app1", AppStartOccurred::WARM, "", "", true, |
| /*start_msec*/ 104)); // Kept by gaugePushPersist. |
| events.push_back(CreateAppStartOccurredEvent(bucketStartTimeNs + 100 * NS_PER_SEC, app2Uid, |
| "app2", AppStartOccurred::WARM, "", "", true, |
| /*start_msec*/ 204)); // Kept by gaugePushPersist. |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 105 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_ON)); |
| events.push_back( |
| CreateScreenStateChangedEvent(bucketStartTimeNs + 110 * NS_PER_SEC, |
| android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->mPullerManager->ForceClearPullerCache(); |
| processor->OnLogEvent(event.get()); |
| } |
| processor->mPullerManager->ForceClearPullerCache(); |
| // Pulling alarm arrive, triggering a bucket split. Only gaugeNew keeps the data since the |
| // condition is false for gaugeNew. |
| processor->informPullAlarmFired(bucketStartTimeNs + bucketSizeNs); |
| |
| uint64_t dumpTimeNs = bucketStartTimeNs + bucketSizeNs + 10 * NS_PER_SEC; |
| ConfigMetricsReportList reports; |
| vector<uint8_t> buffer; |
| processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); |
| EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); |
| backfillDimensionPath(&reports); |
| backfillStringInReport(&reports); |
| backfillStartEndTimestamp(&reports); |
| ASSERT_EQ(reports.reports_size(), 2); |
| |
| int64_t roundedBucketStartNs = MillisToNano(NanoToMillis(bucketStartTimeNs)); |
| int64_t roundedUpdateTimeNs = MillisToNano(NanoToMillis(updateTimeNs)); |
| int64_t roundedBucketEndNs = MillisToNano(NanoToMillis(bucketStartTimeNs + bucketSizeNs)); |
| int64_t roundedDumpTimeNs = MillisToNano(NanoToMillis(dumpTimeNs)); |
| |
| // Report from before update. |
| ConfigMetricsReport report = reports.reports(0); |
| ASSERT_EQ(report.metrics_size(), 4); |
| // Gauge subsystem sleep state trigger screen on. 2 pulls occurred. |
| StatsLogReport gaugeRemoveBefore = report.metrics(0); |
| EXPECT_EQ(gaugeRemoveBefore.metric_id(), gaugeRemove.id()); |
| EXPECT_TRUE(gaugeRemoveBefore.has_gauge_metrics()); |
| StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; |
| sortMetricDataByDimensionsValue(gaugeRemoveBefore.gauge_metrics(), &gaugeMetrics); |
| ASSERT_EQ(gaugeMetrics.data_size(), 2); |
| auto data = gaugeMetrics.data(0); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_1"); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, |
| {(int64_t)(bucketStartTimeNs + 20 * NS_PER_SEC), |
| (int64_t)(bucketStartTimeNs + 45 * NS_PER_SEC)}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 2); |
| EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 101); |
| EXPECT_EQ(data.bucket_info(0).atom(1).subsystem_sleep_state().time_millis(), 401); |
| |
| data = gaugeMetrics.data(1); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_2"); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, |
| {(int64_t)(bucketStartTimeNs + 20 * NS_PER_SEC), |
| (int64_t)(bucketStartTimeNs + 45 * NS_PER_SEC)}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 2); |
| EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 102); |
| EXPECT_EQ(data.bucket_info(0).atom(1).subsystem_sleep_state().time_millis(), 402); |
| |
| // Gauge subsystem sleep state when screen is on. One pull when the screen turned on |
| StatsLogReport gaugePullPersistBefore = report.metrics(1); |
| EXPECT_EQ(gaugePullPersistBefore.metric_id(), gaugePullPersist.id()); |
| EXPECT_TRUE(gaugePullPersistBefore.has_gauge_metrics()); |
| gaugeMetrics.Clear(); |
| sortMetricDataByDimensionsValue(gaugePullPersistBefore.gauge_metrics(), &gaugeMetrics); |
| ASSERT_EQ(gaugeMetrics.data_size(), 2); |
| data = gaugeMetrics.data(0); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_1"); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, |
| {(int64_t)(bucketStartTimeNs + 20 * NS_PER_SEC)}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 101); |
| |
| data = gaugeMetrics.data(1); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_2"); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, |
| {(int64_t)(bucketStartTimeNs + 20 * NS_PER_SEC)}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 102); |
| |
| // Gauge screen on events, one per bucket. |
| StatsLogReport gaugeChangeBefore = report.metrics(2); |
| EXPECT_EQ(gaugeChangeBefore.metric_id(), gaugeChange.id()); |
| EXPECT_TRUE(gaugeChangeBefore.has_gauge_metrics()); |
| gaugeMetrics.Clear(); |
| sortMetricDataByDimensionsValue(gaugeChangeBefore.gauge_metrics(), &gaugeMetrics); |
| ASSERT_EQ(gaugeMetrics.data_size(), 1); |
| data = gaugeMetrics.data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, |
| {(int64_t)(bucketStartTimeNs + 20 * NS_PER_SEC)}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(0).atom(0).screen_state_changed().state(), |
| android::view::DISPLAY_STATE_ON); |
| |
| // Gauge app start while app is in the background. App 1 started twice, app 2 started once. |
| StatsLogReport gaugePushPersistBefore = report.metrics(3); |
| EXPECT_EQ(gaugePushPersistBefore.metric_id(), gaugePushPersist.id()); |
| EXPECT_TRUE(gaugePushPersistBefore.has_gauge_metrics()); |
| gaugeMetrics.Clear(); |
| sortMetricDataByDimensionsValue(gaugePushPersistBefore.gauge_metrics(), &gaugeMetrics); |
| ASSERT_EQ(gaugeMetrics.data_size(), 2); |
| data = gaugeMetrics.data(0); |
| ValidateUidDimension(data.dimensions_in_what(), util::APP_START_OCCURRED, app1Uid); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, |
| {(int64_t)(bucketStartTimeNs + 10 * NS_PER_SEC), |
| (int64_t)(bucketStartTimeNs + 35 * NS_PER_SEC)}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 2); |
| EXPECT_EQ(data.bucket_info(0).atom(0).app_start_occurred().pkg_name(), "app1"); |
| EXPECT_EQ(data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis(), 101); |
| EXPECT_EQ(data.bucket_info(0).atom(1).app_start_occurred().pkg_name(), "app1"); |
| EXPECT_EQ(data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis(), 102); |
| |
| data = gaugeMetrics.data(1); |
| ValidateUidDimension(data.dimensions_in_what(), util::APP_START_OCCURRED, app2Uid); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, |
| {(int64_t)(bucketStartTimeNs + 40 * NS_PER_SEC)}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(0).atom(0).app_start_occurred().pkg_name(), "app2"); |
| EXPECT_EQ(data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis(), 202); |
| |
| // Report from after update. |
| report = reports.reports(1); |
| ASSERT_EQ(report.metrics_size(), 4); |
| // Gauge screen on events FIRST_N_SAMPLES. There were 2. |
| StatsLogReport gaugeChangeAfter = report.metrics(0); |
| EXPECT_EQ(gaugeChangeAfter.metric_id(), gaugeChange.id()); |
| EXPECT_TRUE(gaugeChangeAfter.has_gauge_metrics()); |
| gaugeMetrics.Clear(); |
| sortMetricDataByDimensionsValue(gaugeChangeAfter.gauge_metrics(), &gaugeMetrics); |
| ASSERT_EQ(gaugeMetrics.data_size(), 1); |
| data = gaugeMetrics.data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, |
| {(int64_t)(bucketStartTimeNs + 80 * NS_PER_SEC), |
| (int64_t)(bucketStartTimeNs + 105 * NS_PER_SEC)}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 2); |
| EXPECT_EQ(data.bucket_info(0).atom(0).screen_state_changed().state(), |
| android::view::DISPLAY_STATE_ON); |
| EXPECT_EQ(data.bucket_info(0).atom(1).screen_state_changed().state(), |
| android::view::DISPLAY_STATE_ON); |
| |
| // Gauge subsystem sleep state, random one sample, no condition. |
| // Pulled at update time and after the normal bucket end. |
| StatsLogReport gaugeNewAfter = report.metrics(1); |
| EXPECT_EQ(gaugeNewAfter.metric_id(), gaugeNew.id()); |
| EXPECT_TRUE(gaugeNewAfter.has_gauge_metrics()); |
| gaugeMetrics.Clear(); |
| sortMetricDataByDimensionsValue(gaugeNewAfter.gauge_metrics(), &gaugeMetrics); |
| ASSERT_EQ(gaugeMetrics.data_size(), 2); |
| data = gaugeMetrics.data(0); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_1"); |
| ASSERT_EQ(data.bucket_info_size(), 2); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, |
| {updateTimeNs}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 901); |
| ValidateGaugeBucketTimes(data.bucket_info(1), roundedBucketEndNs, roundedDumpTimeNs, |
| {(int64_t)(bucketStartTimeNs + bucketSizeNs)}); |
| ASSERT_EQ(data.bucket_info(1).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 1601); |
| |
| data = gaugeMetrics.data(1); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_2"); |
| ASSERT_EQ(data.bucket_info_size(), 2); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, |
| {updateTimeNs}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 902); |
| ValidateGaugeBucketTimes(data.bucket_info(1), roundedBucketEndNs, roundedDumpTimeNs, |
| {(int64_t)(bucketStartTimeNs + bucketSizeNs)}); |
| ASSERT_EQ(data.bucket_info(1).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 1602); |
| |
| // Gauge app start while app is in the background. App 1 started once, app 2 started twice. |
| StatsLogReport gaugePushPersistAfter = report.metrics(2); |
| EXPECT_EQ(gaugePushPersistAfter.metric_id(), gaugePushPersist.id()); |
| EXPECT_TRUE(gaugePushPersistAfter.has_gauge_metrics()); |
| gaugeMetrics.Clear(); |
| sortMetricDataByDimensionsValue(gaugePushPersistAfter.gauge_metrics(), &gaugeMetrics); |
| ASSERT_EQ(gaugeMetrics.data_size(), 2); |
| data = gaugeMetrics.data(0); |
| ValidateUidDimension(data.dimensions_in_what(), util::APP_START_OCCURRED, app1Uid); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, |
| {(int64_t)(bucketStartTimeNs + 95 * NS_PER_SEC)}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(0).atom(0).app_start_occurred().pkg_name(), "app1"); |
| EXPECT_EQ(data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis(), 104); |
| |
| data = gaugeMetrics.data(1); |
| ValidateUidDimension(data.dimensions_in_what(), util::APP_START_OCCURRED, app2Uid); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, |
| {(int64_t)(bucketStartTimeNs + 70 * NS_PER_SEC), |
| (int64_t)(bucketStartTimeNs + 100 * NS_PER_SEC)}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 2); |
| EXPECT_EQ(data.bucket_info(0).atom(0).app_start_occurred().pkg_name(), "app2"); |
| EXPECT_EQ(data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis(), 203); |
| EXPECT_EQ(data.bucket_info(0).atom(1).app_start_occurred().pkg_name(), "app2"); |
| EXPECT_EQ(data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis(), 204); |
| |
| // Gauge subsystem sleep state when screen is on. One pull at update since screen is on then. |
| StatsLogReport gaugePullPersistAfter = report.metrics(3); |
| EXPECT_EQ(gaugePullPersistAfter.metric_id(), gaugePullPersist.id()); |
| EXPECT_TRUE(gaugePullPersistAfter.has_gauge_metrics()); |
| gaugeMetrics.Clear(); |
| sortMetricDataByDimensionsValue(gaugePullPersistAfter.gauge_metrics(), &gaugeMetrics); |
| ASSERT_EQ(gaugeMetrics.data_size(), 2); |
| data = gaugeMetrics.data(0); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_1"); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, |
| {updateTimeNs}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 901); |
| |
| data = gaugeMetrics.data(1); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_2"); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateGaugeBucketTimes(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, |
| {updateTimeNs}); |
| ASSERT_EQ(data.bucket_info(0).atom_size(), 1); |
| EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 902); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestValueMetric) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. |
| |
| AtomMatcher brightnessMatcher = CreateScreenBrightnessChangedAtomMatcher(); |
| *config.add_atom_matcher() = brightnessMatcher; |
| AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); |
| *config.add_atom_matcher() = screenOnMatcher; |
| AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); |
| *config.add_atom_matcher() = screenOffMatcher; |
| AtomMatcher batteryPluggedUsbMatcher = CreateBatteryStateUsbMatcher(); |
| *config.add_atom_matcher() = batteryPluggedUsbMatcher; |
| AtomMatcher unpluggedMatcher = CreateBatteryStateNoneMatcher(); |
| *config.add_atom_matcher() = unpluggedMatcher; |
| AtomMatcher subsystemSleepMatcher = |
| CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE); |
| *config.add_atom_matcher() = subsystemSleepMatcher; |
| |
| Predicate screenOnPredicate = CreateScreenIsOnPredicate(); |
| *config.add_predicate() = screenOnPredicate; |
| Predicate unpluggedPredicate = CreateDeviceUnpluggedPredicate(); |
| *config.add_predicate() = unpluggedPredicate; |
| |
| State screenState = CreateScreenState(); |
| *config.add_state() = screenState; |
| |
| ValueMetric valuePullPersist = |
| createValueMetric("SubsystemSleepWhileUnpluggedSliceScreen", subsystemSleepMatcher, 4, |
| unpluggedPredicate.id(), {screenState.id()}); |
| *valuePullPersist.mutable_dimensions_in_what() = |
| CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); |
| |
| ValueMetric valuePushPersist = createValueMetric( |
| "MinScreenBrightnessWhileScreenOn", brightnessMatcher, 1, screenOnPredicate.id(), {}); |
| valuePushPersist.set_aggregation_type(ValueMetric::MIN); |
| |
| ValueMetric valueChange = |
| createValueMetric("SubsystemSleep", subsystemSleepMatcher, 4, nullopt, {}); |
| *valueChange.mutable_dimensions_in_what() = |
| CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */}); |
| |
| ValueMetric valueRemove = |
| createValueMetric("AvgScreenBrightness", brightnessMatcher, 1, nullopt, {}); |
| valueRemove.set_aggregation_type(ValueMetric::AVG); |
| |
| *config.add_value_metric() = valuePullPersist; |
| *config.add_value_metric() = valueRemove; |
| *config.add_value_metric() = valuePushPersist; |
| *config.add_value_metric() = valueChange; |
| |
| ConfigKey key(123, 987); |
| uint64_t bucketStartTimeNs = getElapsedRealtimeNs(); |
| uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL; |
| // Config creation triggers pull #1. |
| sp<StatsLogProcessor> processor = CreateStatsLogProcessor( |
| bucketStartTimeNs, bucketStartTimeNs, config, key, |
| SharedRefBase::make<FakeSubsystemSleepCallback>(), util::SUBSYSTEM_SLEEP_STATE); |
| |
| // Initialize log events before update. |
| // ValuePushPersist and ValuePullPersist will skip the bucket due to condition unknown. |
| std::vector<std::unique_ptr<LogEvent>> events; |
| events.push_back(CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 5 * NS_PER_SEC, 5)); |
| events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, |
| android::view::DISPLAY_STATE_ON)); |
| events.push_back(CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC, 15)); |
| events.push_back(CreateBatteryStateChangedEvent( |
| bucketStartTimeNs + 20 * NS_PER_SEC, |
| BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // Pull #2. |
| events.push_back(CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 40)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->mPullerManager->ForceClearPullerCache(); |
| processor->OnLogEvent(event.get()); |
| } |
| processor->mPullerManager->ForceClearPullerCache(); |
| |
| // Do the update. Add matchers/conditions in different order to force indices to change. |
| StatsdConfig newConfig; |
| newConfig.add_allowed_log_source("AID_ROOT"); |
| newConfig.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root. |
| |
| *newConfig.add_atom_matcher() = screenOffMatcher; |
| *newConfig.add_atom_matcher() = unpluggedMatcher; |
| *newConfig.add_atom_matcher() = batteryPluggedUsbMatcher; |
| *newConfig.add_atom_matcher() = subsystemSleepMatcher; |
| *newConfig.add_atom_matcher() = brightnessMatcher; |
| *newConfig.add_atom_matcher() = screenOnMatcher; |
| |
| *newConfig.add_predicate() = unpluggedPredicate; |
| *newConfig.add_predicate() = screenOnPredicate; |
| |
| *config.add_state() = screenState; |
| |
| valueChange.set_condition(screenOnPredicate.id()); |
| *newConfig.add_value_metric() = valueChange; |
| ValueMetric valueNew = createValueMetric("MaxScrBrightness", brightnessMatcher, 1, nullopt, {}); |
| valueNew.set_aggregation_type(ValueMetric::MAX); |
| *newConfig.add_value_metric() = valueNew; |
| *newConfig.add_value_metric() = valuePushPersist; |
| *newConfig.add_value_metric() = valuePullPersist; |
| |
| int64_t updateTimeNs = bucketStartTimeNs + 30 * NS_PER_SEC; |
| // Update pulls valuePullPersist and valueNew. Pull #3. |
| processor->OnConfigUpdated(updateTimeNs, key, newConfig); |
| |
| // Verify puller manager is properly set. |
| sp<StatsPullerManager> pullerManager = processor->mPullerManager; |
| EXPECT_EQ(pullerManager->mNextPullTimeNs, bucketStartTimeNs + bucketSizeNs); |
| ASSERT_EQ(pullerManager->mReceivers.size(), 1); |
| ASSERT_EQ(pullerManager->mReceivers.begin()->second.size(), 2); |
| |
| // Send events after the update. Values reset since this is a new bucket. |
| events.clear(); |
| events.push_back(CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 35 * NS_PER_SEC, 30)); |
| events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, |
| android::view::DISPLAY_STATE_OFF)); // Pull #4. |
| events.push_back(CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 45 * NS_PER_SEC, 20)); |
| events.push_back(CreateBatteryStateChangedEvent( |
| bucketStartTimeNs + 50 * NS_PER_SEC, |
| BatteryPluggedStateEnum::BATTERY_PLUGGED_USB)); // Pull #5. |
| events.push_back(CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 55 * NS_PER_SEC, 25)); |
| events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 60 * NS_PER_SEC, |
| android::view::DISPLAY_STATE_ON)); // Pull #6. |
| events.push_back(CreateBatteryStateChangedEvent( |
| bucketStartTimeNs + 65 * NS_PER_SEC, |
| BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // Pull #7. |
| events.push_back(CreateScreenBrightnessChangedEvent(bucketStartTimeNs + 70 * NS_PER_SEC, 40)); |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->mPullerManager->ForceClearPullerCache(); |
| processor->OnLogEvent(event.get()); |
| } |
| processor->mPullerManager->ForceClearPullerCache(); |
| |
| // Pulling alarm arrive, triggering a bucket split. |
| // Both valuePullPersist and valueChange use the value since both conditions are true. Pull #8. |
| processor->informPullAlarmFired(bucketStartTimeNs + bucketSizeNs); |
| processor->OnLogEvent(CreateScreenBrightnessChangedEvent( |
| bucketStartTimeNs + bucketSizeNs + 5 * NS_PER_SEC, 50) |
| .get()); |
| |
| uint64_t dumpTimeNs = bucketStartTimeNs + bucketSizeNs + 10 * NS_PER_SEC; |
| ConfigMetricsReportList reports; |
| vector<uint8_t> buffer; |
| processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); |
| EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); |
| backfillDimensionPath(&reports); |
| backfillStringInReport(&reports); |
| backfillStartEndTimestamp(&reports); |
| ASSERT_EQ(reports.reports_size(), 2); |
| |
| int64_t roundedBucketStartNs = MillisToNano(NanoToMillis(bucketStartTimeNs)); |
| int64_t roundedUpdateTimeNs = MillisToNano(NanoToMillis(updateTimeNs)); |
| int64_t roundedBucketEndNs = MillisToNano(NanoToMillis(bucketStartTimeNs + bucketSizeNs)); |
| int64_t roundedDumpTimeNs = MillisToNano(NanoToMillis(dumpTimeNs)); |
| |
| // Report from before update. |
| ConfigMetricsReport report = reports.reports(0); |
| ASSERT_EQ(report.metrics_size(), 4); |
| // Pull subsystem sleep while unplugged slice screen. Bucket skipped due to condition unknown. |
| StatsLogReport valuePullPersistBefore = report.metrics(0); |
| EXPECT_EQ(valuePullPersistBefore.metric_id(), valuePullPersist.id()); |
| EXPECT_TRUE(valuePullPersistBefore.has_value_metrics()); |
| ASSERT_EQ(valuePullPersistBefore.value_metrics().data_size(), 0); |
| ASSERT_EQ(valuePullPersistBefore.value_metrics().skipped_size(), 1); |
| StatsLogReport::SkippedBuckets skipBucket = valuePullPersistBefore.value_metrics().skipped(0); |
| EXPECT_EQ(skipBucket.start_bucket_elapsed_nanos(), roundedBucketStartNs); |
| EXPECT_EQ(skipBucket.end_bucket_elapsed_nanos(), roundedUpdateTimeNs); |
| ASSERT_EQ(skipBucket.drop_event_size(), 1); |
| EXPECT_EQ(skipBucket.drop_event(0).drop_reason(), BucketDropReason::CONDITION_UNKNOWN); |
| |
| // Average screen brightness. Values were 5, 15, 40. Avg: 20. |
| StatsLogReport valueRemoveBefore = report.metrics(1); |
| EXPECT_EQ(valueRemoveBefore.metric_id(), valueRemove.id()); |
| EXPECT_TRUE(valueRemoveBefore.has_value_metrics()); |
| StatsLogReport::ValueMetricDataWrapper valueMetrics; |
| sortMetricDataByDimensionsValue(valueRemoveBefore.value_metrics(), &valueMetrics); |
| ASSERT_EQ(valueMetrics.data_size(), 1); |
| ValueMetricData data = valueMetrics.data(0); |
| EXPECT_FALSE(data.has_dimensions_in_what()); |
| EXPECT_EQ(data.slice_by_state_size(), 0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateValueBucket(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, {20}, 0); |
| |
| // Min screen brightness while screen on. Bucket skipped due to condition unknown. |
| StatsLogReport valuePushPersistBefore = report.metrics(2); |
| EXPECT_EQ(valuePushPersistBefore.metric_id(), valuePushPersist.id()); |
| EXPECT_TRUE(valuePushPersistBefore.has_value_metrics()); |
| ASSERT_EQ(valuePushPersistBefore.value_metrics().data_size(), 0); |
| ASSERT_EQ(valuePushPersistBefore.value_metrics().skipped_size(), 1); |
| skipBucket = valuePushPersistBefore.value_metrics().skipped(0); |
| EXPECT_EQ(skipBucket.start_bucket_elapsed_nanos(), roundedBucketStartNs); |
| EXPECT_EQ(skipBucket.end_bucket_elapsed_nanos(), roundedUpdateTimeNs); |
| ASSERT_EQ(skipBucket.drop_event_size(), 1); |
| EXPECT_EQ(skipBucket.drop_event(0).drop_reason(), BucketDropReason::CONDITION_UNKNOWN); |
| |
| // Pull Subsystem sleep state. Value is Pull #3 (900) - Pull#1 (100). |
| StatsLogReport valueChangeBefore = report.metrics(3); |
| EXPECT_EQ(valueChangeBefore.metric_id(), valueChange.id()); |
| EXPECT_TRUE(valueChangeBefore.has_value_metrics()); |
| valueMetrics.Clear(); |
| sortMetricDataByDimensionsValue(valueChangeBefore.value_metrics(), &valueMetrics); |
| ASSERT_EQ(valueMetrics.data_size(), 2); |
| data = valueMetrics.data(0); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_1"); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateValueBucket(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, {800}, 0); |
| data = valueMetrics.data(1); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_2"); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateValueBucket(data.bucket_info(0), roundedBucketStartNs, roundedUpdateTimeNs, {800}, 0); |
| |
| // Report from after update. |
| report = reports.reports(1); |
| ASSERT_EQ(report.metrics_size(), 4); |
| // Pull subsystem sleep while screen on. |
| // Pull#4 (1600) - pull#3 (900) + pull#8 (6400) - pull#6 (3600) |
| StatsLogReport valueChangeAfter = report.metrics(0); |
| EXPECT_EQ(valueChangeAfter.metric_id(), valueChange.id()); |
| EXPECT_TRUE(valueChangeAfter.has_value_metrics()); |
| valueMetrics.Clear(); |
| sortMetricDataByDimensionsValue(valueChangeAfter.value_metrics(), &valueMetrics); |
| int64_t conditionTrueNs = bucketSizeNs - 60 * NS_PER_SEC + 10 * NS_PER_SEC; |
| ASSERT_EQ(valueMetrics.data_size(), 2); |
| data = valueMetrics.data(0); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_1"); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateValueBucket(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, {3500}, |
| conditionTrueNs); |
| ASSERT_EQ(valueMetrics.data_size(), 2); |
| data = valueMetrics.data(1); |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_2"); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateValueBucket(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, {3500}, |
| conditionTrueNs); |
| |
| ASSERT_EQ(valueChangeAfter.value_metrics().skipped_size(), 1); |
| skipBucket = valueChangeAfter.value_metrics().skipped(0); |
| EXPECT_EQ(skipBucket.start_bucket_elapsed_nanos(), roundedBucketEndNs); |
| EXPECT_EQ(skipBucket.end_bucket_elapsed_nanos(), roundedDumpTimeNs); |
| ASSERT_EQ(skipBucket.drop_event_size(), 1); |
| EXPECT_EQ(skipBucket.drop_event(0).drop_reason(), BucketDropReason::DUMP_REPORT_REQUESTED); |
| |
| // Max screen brightness, no condition. Val is 40 in first bucket, 50 in second. |
| StatsLogReport valueNewAfter = report.metrics(1); |
| EXPECT_EQ(valueNewAfter.metric_id(), valueNew.id()); |
| EXPECT_TRUE(valueNewAfter.has_value_metrics()); |
| valueMetrics.Clear(); |
| sortMetricDataByDimensionsValue(valueNewAfter.value_metrics(), &valueMetrics); |
| ASSERT_EQ(valueMetrics.data_size(), 1); |
| data = valueMetrics.data(0); |
| ASSERT_EQ(data.bucket_info_size(), 2); |
| ValidateValueBucket(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, {40}, 0); |
| ValidateValueBucket(data.bucket_info(1), roundedBucketEndNs, roundedDumpTimeNs, {50}, 0); |
| |
| // Min screen brightness when screen on. Val is 30 in first bucket, 50 in second. |
| StatsLogReport valuePushPersistAfter = report.metrics(2); |
| EXPECT_EQ(valuePushPersistAfter.metric_id(), valuePushPersist.id()); |
| EXPECT_TRUE(valuePushPersistAfter.has_value_metrics()); |
| valueMetrics.Clear(); |
| sortMetricDataByDimensionsValue(valuePushPersistAfter.value_metrics(), &valueMetrics); |
| ASSERT_EQ(valueMetrics.data_size(), 1); |
| data = valueMetrics.data(0); |
| ASSERT_EQ(data.bucket_info_size(), 2); |
| conditionTrueNs = bucketSizeNs - 60 * NS_PER_SEC + 10 * NS_PER_SEC; |
| ValidateValueBucket(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, {30}, |
| conditionTrueNs); |
| ValidateValueBucket(data.bucket_info(1), roundedBucketEndNs, roundedDumpTimeNs, {50}, |
| 10 * NS_PER_SEC); |
| |
| // TODO(b/179725160): fix assertions. |
| // Subsystem sleep state while unplugged slice screen. |
| StatsLogReport valuePullPersistAfter = report.metrics(3); |
| EXPECT_EQ(valuePullPersistAfter.metric_id(), valuePullPersist.id()); |
| EXPECT_TRUE(valuePullPersistAfter.has_value_metrics()); |
| valueMetrics.Clear(); |
| sortMetricDataByDimensionsValue(valuePullPersistAfter.value_metrics(), &valueMetrics); |
| ASSERT_EQ(valueMetrics.data_size(), 4); |
| // Name 1, screen OFF. Pull#5 (2500) - pull#4 (1600). |
| data = valueMetrics.data(0); |
| conditionTrueNs = 10 * NS_PER_SEC; |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_1"); |
| ValidateStateValue(data.slice_by_state(), util::SCREEN_STATE_CHANGED, |
| android::view::DisplayStateEnum::DISPLAY_STATE_OFF); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateValueBucket(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, {900}, -1); |
| // Name 1, screen ON. Pull#4 (1600) - pull#3 (900) + pull#8 (6400) - pull#7 (4900). |
| data = valueMetrics.data(1); |
| conditionTrueNs = 10 * NS_PER_SEC + bucketSizeNs - 65 * NS_PER_SEC; |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_1"); |
| ValidateStateValue(data.slice_by_state(), util::SCREEN_STATE_CHANGED, |
| android::view::DisplayStateEnum::DISPLAY_STATE_ON); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateValueBucket(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, {2200}, -1); |
| // Name 2, screen OFF. Pull#5 (2500) - pull#4 (1600). |
| data = valueMetrics.data(2); |
| conditionTrueNs = 10 * NS_PER_SEC; |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_2"); |
| ValidateStateValue(data.slice_by_state(), util::SCREEN_STATE_CHANGED, |
| android::view::DisplayStateEnum::DISPLAY_STATE_OFF); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateValueBucket(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, {900}, -1); |
| // Name 2, screen ON. Pull#4 (1600) - pull#3 (900) + pull#8 (6400) - pull#7 (4900). |
| data = valueMetrics.data(3); |
| conditionTrueNs = 10 * NS_PER_SEC + bucketSizeNs - 65 * NS_PER_SEC; |
| ValidateSubsystemSleepDimension(data.dimensions_in_what(), "subsystem_name_2"); |
| ValidateStateValue(data.slice_by_state(), util::SCREEN_STATE_CHANGED, |
| android::view::DisplayStateEnum::DISPLAY_STATE_ON); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateValueBucket(data.bucket_info(0), roundedUpdateTimeNs, roundedBucketEndNs, {2200}, -1); |
| |
| ASSERT_EQ(valuePullPersistAfter.value_metrics().skipped_size(), 1); |
| skipBucket = valuePullPersistAfter.value_metrics().skipped(0); |
| EXPECT_EQ(skipBucket.start_bucket_elapsed_nanos(), roundedBucketEndNs); |
| EXPECT_EQ(skipBucket.end_bucket_elapsed_nanos(), roundedDumpTimeNs); |
| ASSERT_EQ(skipBucket.drop_event_size(), 1); |
| EXPECT_EQ(skipBucket.drop_event(0).drop_reason(), BucketDropReason::DUMP_REPORT_REQUESTED); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestMetricActivation) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| |
| string immediateTag = "immediate", bootTag = "boot", childTag = "child"; |
| |
| AtomMatcher syncStartMatcher = CreateSyncStartAtomMatcher(); |
| *config.add_atom_matcher() = syncStartMatcher; |
| |
| AtomMatcher immediateMatcher = |
| CreateSimpleAtomMatcher("immediateMatcher", util::WAKELOCK_STATE_CHANGED); |
| FieldValueMatcher* fvm = |
| immediateMatcher.mutable_simple_atom_matcher()->add_field_value_matcher(); |
| fvm->set_field(3); // Tag. |
| fvm->set_eq_string(immediateTag); |
| *config.add_atom_matcher() = immediateMatcher; |
| |
| AtomMatcher bootMatcher = CreateSimpleAtomMatcher("bootMatcher", util::WAKELOCK_STATE_CHANGED); |
| fvm = bootMatcher.mutable_simple_atom_matcher()->add_field_value_matcher(); |
| fvm->set_field(3); // Tag. |
| fvm->set_eq_string(bootTag); |
| *config.add_atom_matcher() = bootMatcher; |
| |
| AtomMatcher childMatcher = |
| CreateSimpleAtomMatcher("childMatcher", util::WAKELOCK_STATE_CHANGED); |
| fvm = childMatcher.mutable_simple_atom_matcher()->add_field_value_matcher(); |
| fvm->set_field(3); // Tag. |
| fvm->set_eq_string(childTag); |
| *config.add_atom_matcher() = childMatcher; |
| |
| AtomMatcher acquireMatcher = CreateAcquireWakelockAtomMatcher(); |
| *config.add_atom_matcher() = acquireMatcher; |
| |
| AtomMatcher combinationMatcher; |
| combinationMatcher.set_id(StringToId("combination")); |
| AtomMatcher_Combination* combination = combinationMatcher.mutable_combination(); |
| combination->set_operation(LogicalOperation::OR); |
| combination->add_matcher(acquireMatcher.id()); |
| combination->add_matcher(childMatcher.id()); |
| *config.add_atom_matcher() = combinationMatcher; |
| |
| CountMetric immediateMetric = |
| createCountMetric("ImmediateMetric", syncStartMatcher.id(), nullopt, {}); |
| CountMetric bootMetric = createCountMetric("BootMetric", syncStartMatcher.id(), nullopt, {}); |
| CountMetric combinationMetric = |
| createCountMetric("CombinationMetric", syncStartMatcher.id(), nullopt, {}); |
| *config.add_count_metric() = immediateMetric; |
| *config.add_count_metric() = bootMetric; |
| *config.add_count_metric() = combinationMetric; |
| |
| MetricActivation immediateMetricActivation; |
| immediateMetricActivation.set_metric_id(immediateMetric.id()); |
| auto eventActivation = immediateMetricActivation.add_event_activation(); |
| eventActivation->set_activation_type(ActivationType::ACTIVATE_IMMEDIATELY); |
| eventActivation->set_atom_matcher_id(immediateMatcher.id()); |
| eventActivation->set_ttl_seconds(60); // One minute. |
| *config.add_metric_activation() = immediateMetricActivation; |
| |
| MetricActivation bootMetricActivation; |
| bootMetricActivation.set_metric_id(bootMetric.id()); |
| eventActivation = bootMetricActivation.add_event_activation(); |
| eventActivation->set_activation_type(ActivationType::ACTIVATE_ON_BOOT); |
| eventActivation->set_atom_matcher_id(bootMatcher.id()); |
| eventActivation->set_ttl_seconds(60); // One minute. |
| *config.add_metric_activation() = bootMetricActivation; |
| |
| MetricActivation combinationMetricActivation; |
| combinationMetricActivation.set_metric_id(combinationMetric.id()); |
| eventActivation = combinationMetricActivation.add_event_activation(); |
| eventActivation->set_activation_type(ActivationType::ACTIVATE_IMMEDIATELY); |
| eventActivation->set_atom_matcher_id(combinationMatcher.id()); |
| eventActivation->set_ttl_seconds(60); // One minute. |
| *config.add_metric_activation() = combinationMetricActivation; |
| |
| ConfigKey key(123, 987); |
| uint64_t bucketStartTimeNs = 10000000000; // 0:10 |
| uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL; |
| sp<StatsLogProcessor> processor = |
| CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); |
| int uid1 = 55555; |
| |
| // Initialize log events before update. |
| // Counts provided in order of immediate, boot, and combination metric. |
| std::vector<std::unique_ptr<LogEvent>> events; |
| |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 5 * NS_PER_SEC, {uid1}, {""}, |
| "")); // Count: 0, 0, 0. |
| events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, {uid1}, {""}, |
| immediateTag)); // Activate immediate metric. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 15 * NS_PER_SEC, {uid1}, {""}, |
| "")); // Count: 1, 0, 0. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 20 * NS_PER_SEC, {uid1}, {""}, |
| "foo")); // Activate combination metric. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 25 * NS_PER_SEC, {uid1}, {""}, |
| "")); // Count: 2, 0, 1. |
| events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 30 * NS_PER_SEC, {uid1}, {""}, |
| bootTag)); // Boot metric pending activation. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 35 * NS_PER_SEC, {uid1}, {""}, |
| "")); // Count: 3, 0, 2. |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| |
| // Do update. Add matchers/conditions in different order to force indices to change. |
| StatsdConfig newConfig; |
| newConfig.add_allowed_log_source("AID_ROOT"); |
| newConfig.set_hash_strings_in_metric_report(false); // Modify metadata for fun. |
| |
| // Change combination matcher, will mean combination metric isn't active after update. |
| combinationMatcher.mutable_combination()->set_operation(LogicalOperation::AND); |
| *newConfig.add_atom_matcher() = acquireMatcher; |
| *newConfig.add_atom_matcher() = bootMatcher; |
| *newConfig.add_atom_matcher() = combinationMatcher; |
| *newConfig.add_atom_matcher() = childMatcher; |
| *newConfig.add_atom_matcher() = syncStartMatcher; |
| *newConfig.add_atom_matcher() = immediateMatcher; |
| |
| *newConfig.add_count_metric() = bootMetric; |
| *newConfig.add_count_metric() = combinationMetric; |
| *newConfig.add_count_metric() = immediateMetric; |
| |
| *newConfig.add_metric_activation() = bootMetricActivation; |
| *newConfig.add_metric_activation() = combinationMetricActivation; |
| *newConfig.add_metric_activation() = immediateMetricActivation; |
| |
| int64_t updateTimeNs = bucketStartTimeNs + 40 * NS_PER_SEC; |
| processor->OnConfigUpdated(updateTimeNs, key, newConfig); |
| |
| // The reboot will write to disk again, so sleep for 1 second to avoid this. |
| // TODO(b/178887128): clean this up. |
| std::this_thread::sleep_for(1000ms); |
| // Send event after the update. Counts reset to 0 since this is a new bucket. |
| processor->OnLogEvent( |
| CreateSyncStartEvent(bucketStartTimeNs + 45 * NS_PER_SEC, {uid1}, {""}, "") |
| .get()); // Count: 1, 0, 0. |
| |
| // Fake a reboot. Code is from StatsService::informDeviceShutdown. |
| int64_t shutDownTimeNs = bucketStartTimeNs + 50 * NS_PER_SEC; |
| processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST, shutDownTimeNs); |
| processor->SaveActiveConfigsToDisk(shutDownTimeNs); |
| processor->SaveMetadataToDisk(getWallClockNs(), shutDownTimeNs); |
| |
| // On boot, use StartUp. However, skip config manager for simplicity. |
| int64_t bootTimeNs = bucketStartTimeNs + 55 * NS_PER_SEC; |
| processor = CreateStatsLogProcessor(bootTimeNs, bootTimeNs, newConfig, key); |
| processor->LoadActiveConfigsFromDisk(); |
| processor->LoadMetadataFromDisk(getWallClockNs(), bootTimeNs); |
| |
| // Send events after boot. Counts reset to 0 since this is a new bucket. Boot metric now active. |
| events.clear(); |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 60 * NS_PER_SEC, {uid1}, {""}, |
| "")); // Count: 1, 1, 0. |
| int64_t deactivationTimeNs = bucketStartTimeNs + 76 * NS_PER_SEC; |
| events.push_back(CreateScreenStateChangedEvent( |
| deactivationTimeNs, |
| android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // TTLs immediate metric. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 80 * NS_PER_SEC, {uid1}, {""}, |
| "")); // Count: 1, 2, 0. |
| events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 85 * NS_PER_SEC, {uid1}, {""}, |
| childTag)); // Activate combination metric. |
| events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 90 * NS_PER_SEC, {uid1}, {""}, |
| "")); // Count: 1, 3, 1. |
| |
| // Send log events to StatsLogProcessor. |
| for (auto& event : events) { |
| processor->OnLogEvent(event.get()); |
| } |
| uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; |
| ConfigMetricsReportList reports; |
| vector<uint8_t> buffer; |
| processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); |
| EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); |
| backfillDimensionPath(&reports); |
| backfillStringInReport(&reports); |
| backfillStartEndTimestamp(&reports); |
| ASSERT_EQ(reports.reports_size(), 3); |
| |
| // Report from before update. |
| ConfigMetricsReport report = reports.reports(0); |
| EXPECT_EQ(report.last_report_elapsed_nanos(), bucketStartTimeNs); |
| EXPECT_EQ(report.current_report_elapsed_nanos(), updateTimeNs); |
| ASSERT_EQ(report.metrics_size(), 3); |
| // Immediate metric. Count = 3. |
| StatsLogReport metricReport = report.metrics(0); |
| EXPECT_EQ(metricReport.metric_id(), immediateMetric.id()); |
| EXPECT_TRUE(metricReport.is_active()); |
| EXPECT_TRUE(metricReport.has_count_metrics()); |
| ASSERT_EQ(metricReport.count_metrics().data_size(), 1); |
| auto data = metricReport.count_metrics().data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 3); |
| |
| // Boot metric. Count = 0. |
| metricReport = report.metrics(1); |
| EXPECT_EQ(metricReport.metric_id(), bootMetric.id()); |
| EXPECT_FALSE(metricReport.is_active()); |
| EXPECT_FALSE(metricReport.has_count_metrics()); |
| |
| // Combination metric. Count = 2. |
| metricReport = report.metrics(2); |
| EXPECT_EQ(metricReport.metric_id(), combinationMetric.id()); |
| EXPECT_TRUE(metricReport.is_active()); |
| EXPECT_TRUE(metricReport.has_count_metrics()); |
| ASSERT_EQ(metricReport.count_metrics().data_size(), 1); |
| data = metricReport.count_metrics().data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), bucketStartTimeNs, updateTimeNs, 2); |
| |
| // Report from after update, before boot. |
| report = reports.reports(1); |
| EXPECT_EQ(report.last_report_elapsed_nanos(), updateTimeNs); |
| EXPECT_EQ(report.current_report_elapsed_nanos(), shutDownTimeNs); |
| ASSERT_EQ(report.metrics_size(), 3); |
| // Boot metric. Count = 0. |
| metricReport = report.metrics(0); |
| EXPECT_EQ(metricReport.metric_id(), bootMetric.id()); |
| EXPECT_FALSE(metricReport.is_active()); |
| EXPECT_FALSE(metricReport.has_count_metrics()); |
| |
| // Combination metric. Count = 0. |
| metricReport = report.metrics(1); |
| EXPECT_EQ(metricReport.metric_id(), combinationMetric.id()); |
| EXPECT_FALSE(metricReport.is_active()); |
| EXPECT_FALSE(metricReport.has_count_metrics()); |
| |
| // Immediate metric. Count = 1. |
| metricReport = report.metrics(2); |
| EXPECT_EQ(metricReport.metric_id(), immediateMetric.id()); |
| EXPECT_TRUE(metricReport.is_active()); |
| EXPECT_TRUE(metricReport.has_count_metrics()); |
| ASSERT_EQ(metricReport.count_metrics().data_size(), 1); |
| data = metricReport.count_metrics().data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), updateTimeNs, shutDownTimeNs, 1); |
| |
| // Report from after reboot. |
| report = reports.reports(2); |
| EXPECT_EQ(report.last_report_elapsed_nanos(), bootTimeNs); |
| EXPECT_EQ(report.current_report_elapsed_nanos(), dumpTimeNs); |
| ASSERT_EQ(report.metrics_size(), 3); |
| // Boot metric. Count = 3. |
| metricReport = report.metrics(0); |
| EXPECT_EQ(metricReport.metric_id(), bootMetric.id()); |
| EXPECT_TRUE(metricReport.is_active()); |
| EXPECT_TRUE(metricReport.has_count_metrics()); |
| ASSERT_EQ(metricReport.count_metrics().data_size(), 1); |
| data = metricReport.count_metrics().data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), bootTimeNs, dumpTimeNs, 3); |
| |
| // Combination metric. Count = 1. |
| metricReport = report.metrics(1); |
| EXPECT_EQ(metricReport.metric_id(), combinationMetric.id()); |
| EXPECT_TRUE(metricReport.is_active()); |
| EXPECT_TRUE(metricReport.has_count_metrics()); |
| ASSERT_EQ(metricReport.count_metrics().data_size(), 1); |
| data = metricReport.count_metrics().data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), bootTimeNs, dumpTimeNs, 1); |
| |
| // Immediate metric. Count = 1. |
| metricReport = report.metrics(2); |
| EXPECT_EQ(metricReport.metric_id(), immediateMetric.id()); |
| EXPECT_FALSE(metricReport.is_active()); |
| EXPECT_TRUE(metricReport.has_count_metrics()); |
| ASSERT_EQ(metricReport.count_metrics().data_size(), 1); |
| data = metricReport.count_metrics().data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| ValidateCountBucket(data.bucket_info(0), bootTimeNs, deactivationTimeNs, 1); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestAnomalyCountMetric) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| |
| AtomMatcher syncStartMatcher = CreateSyncStartAtomMatcher(); |
| *config.add_atom_matcher() = syncStartMatcher; |
| AtomMatcher wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); |
| *config.add_atom_matcher() = wakelockAcquireMatcher; |
| |
| CountMetric countWakelock = |
| createCountMetric("CountWakelock", wakelockAcquireMatcher.id(), nullopt, {}); |
| *countWakelock.mutable_dimensions_in_what() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| |
| CountMetric countSync = createCountMetric("CountSync", syncStartMatcher.id(), nullopt, {}); |
| *countSync.mutable_dimensions_in_what() = |
| CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST}); |
| |
| *config.add_count_metric() = countWakelock; |
| *config.add_count_metric() = countSync; |
| |
| Alert alertPreserve = |
| createAlert("AlertPreserve", countWakelock.id(), /*buckets=*/2, /*triggerSumGt=*/1); |
| alertPreserve.set_refractory_period_secs(20); |
| Alert alertReplace = createAlert("AlertReplace", countSync.id(), 1, 1); |
| alertReplace.set_refractory_period_secs(1); |
| Alert alertRemove = createAlert("AlertRemove", countWakelock.id(), 1, 0); |
| alertRemove.set_refractory_period_secs(1); |
| *config.add_alert() = alertReplace; |
| *config.add_alert() = alertPreserve; |
| *config.add_alert() = alertRemove; |
| |
| int preserveSubId = 1, replaceSubId = 2, removeSubId = 3; |
| Subscription preserveSub = createSubscription("S1", Subscription::ALERT, alertPreserve.id()); |
| preserveSub.mutable_broadcast_subscriber_details()->set_subscriber_id(preserveSubId); |
| Subscription replaceSub = createSubscription("S2", Subscription::ALERT, alertReplace.id()); |
| replaceSub.mutable_broadcast_subscriber_details()->set_subscriber_id(replaceSubId); |
| Subscription removeSub = createSubscription("S3", Subscription::ALERT, alertRemove.id()); |
| removeSub.mutable_broadcast_subscriber_details()->set_subscriber_id(removeSubId); |
| *config.add_subscription() = preserveSub; |
| *config.add_subscription() = removeSub; |
| *config.add_subscription() = replaceSub; |
| |
| int app1Uid = 123, app2Uid = 456; |
| vector<int> attributionUids1 = {app1Uid}; |
| vector<string> attributionTags1 = {"App1"}; |
| vector<int> attributionUids2 = {app2Uid}; |
| vector<string> attributionTags2 = {"App2"}; |
| int64_t configUid = 123, configId = 987; |
| ConfigKey key(configUid, configId); |
| |
| int alertPreserveCount = 0, alertRemoveCount = 0; |
| StatsDimensionsValueParcel alertPreserveDims; |
| StatsDimensionsValueParcel alertRemoveDims; |
| |
| // The binder calls here will happen synchronously because they are in-process. |
| shared_ptr<MockPendingIntentRef> preserveBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*preserveBroadcast, sendSubscriberBroadcast(configUid, configId, preserveSub.id(), |
| alertPreserve.id(), _, _)) |
| .Times(2) |
| .WillRepeatedly( |
| Invoke([&alertPreserveCount, &alertPreserveDims]( |
| int64_t, int64_t, int64_t, int64_t, const vector<string>&, |
| const StatsDimensionsValueParcel& dimensionsValueParcel) { |
| alertPreserveCount++; |
| alertPreserveDims = dimensionsValueParcel; |
| return Status::ok(); |
| })); |
| |
| shared_ptr<MockPendingIntentRef> replaceBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*replaceBroadcast, sendSubscriberBroadcast(configUid, configId, replaceSub.id(), |
| alertReplace.id(), _, _)) |
| .Times(0); |
| |
| shared_ptr<MockPendingIntentRef> removeBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*removeBroadcast, sendSubscriberBroadcast(configUid, configId, removeSub.id(), |
| alertRemove.id(), _, _)) |
| .Times(3) |
| .WillRepeatedly( |
| Invoke([&alertRemoveCount, &alertRemoveDims]( |
| int64_t, int64_t, int64_t, int64_t, const vector<string>&, |
| const StatsDimensionsValueParcel& dimensionsValueParcel) { |
| alertRemoveCount++; |
| alertRemoveDims = dimensionsValueParcel; |
| return Status::ok(); |
| })); |
| |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, preserveSubId, preserveBroadcast); |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, replaceSubId, replaceBroadcast); |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, removeSubId, removeBroadcast); |
| |
| uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL; |
| uint64_t bucketStartTimeNs = 10000000000; // 0:10 |
| uint64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; |
| sp<StatsLogProcessor> processor = |
| CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); |
| |
| StatsDimensionsValueParcel wlUid1 = |
| CreateAttributionUidDimensionsValueParcel(util::WAKELOCK_STATE_CHANGED, app1Uid); |
| StatsDimensionsValueParcel wlUid2 = |
| CreateAttributionUidDimensionsValueParcel(util::WAKELOCK_STATE_CHANGED, app2Uid); |
| |
| processor->OnLogEvent(CreateAcquireWakelockEvent(bucketStartTimeNs + 15 * NS_PER_SEC, |
| attributionUids1, attributionTags1, "wl1") |
| .get()); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 1); |
| EXPECT_EQ(alertRemoveDims, wlUid1); |
| |
| processor->OnLogEvent(CreateAcquireWakelockEvent(bucketStartTimeNs + 20 * NS_PER_SEC, |
| attributionUids2, attributionTags2, "wl2") |
| .get()); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 2); |
| EXPECT_EQ(alertRemoveDims, wlUid2); |
| |
| processor->OnLogEvent(CreateSyncStartEvent(bucket2StartTimeNs + 5 * NS_PER_SEC, |
| attributionUids1, attributionTags1, "sync1") |
| .get()); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 2); |
| |
| // AlertPreserve enters 30 sec refractory period for uid2. |
| processor->OnLogEvent(CreateAcquireWakelockEvent(bucket2StartTimeNs + 10 * NS_PER_SEC, |
| attributionUids2, attributionTags2, "wl2") |
| .get()); |
| EXPECT_EQ(alertPreserveCount, 1); |
| EXPECT_EQ(alertPreserveDims, wlUid2); |
| EXPECT_EQ(alertRemoveCount, 3); |
| EXPECT_EQ(alertRemoveDims, wlUid2); |
| |
| // Do config update. |
| StatsdConfig newConfig; |
| newConfig.add_allowed_log_source("AID_ROOT"); |
| *newConfig.add_atom_matcher() = wakelockAcquireMatcher; |
| *newConfig.add_atom_matcher() = syncStartMatcher; |
| |
| // Clear dims of sync metric, will result in alertReplace getting replaced. |
| countSync.clear_dimensions_in_what(); |
| *newConfig.add_count_metric() = countSync; |
| *newConfig.add_count_metric() = countWakelock; |
| |
| // New alert on existing metric. Should get current full bucket, but not history of 1st bucket. |
| Alert alertNew = createAlert("AlertNew", countWakelock.id(), /*buckets=*/1, /*triggerSumGt=*/1); |
| *newConfig.add_alert() = alertPreserve; |
| *newConfig.add_alert() = alertNew; |
| *newConfig.add_alert() = alertReplace; |
| |
| int newSubId = 4; |
| Subscription newSub = createSubscription("S4", Subscription::ALERT, alertNew.id()); |
| newSub.mutable_broadcast_subscriber_details()->set_subscriber_id(newSubId); |
| *newConfig.add_subscription() = newSub; |
| *newConfig.add_subscription() = replaceSub; |
| *newConfig.add_subscription() = preserveSub; |
| |
| int alertNewCount = 0; |
| StatsDimensionsValueParcel alertNewDims; |
| shared_ptr<MockPendingIntentRef> newBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*newBroadcast, |
| sendSubscriberBroadcast(configUid, configId, newSub.id(), alertNew.id(), _, _)) |
| .Times(1) |
| .WillRepeatedly( |
| Invoke([&alertNewCount, &alertNewDims]( |
| int64_t, int64_t, int64_t, int64_t, const vector<string>&, |
| const StatsDimensionsValueParcel& dimensionsValueParcel) { |
| alertNewCount++; |
| alertNewDims = dimensionsValueParcel; |
| return Status::ok(); |
| })); |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, newSubId, newBroadcast); |
| |
| int64_t updateTimeNs = bucket2StartTimeNs + 15 * NS_PER_SEC; |
| processor->OnConfigUpdated(updateTimeNs, key, newConfig); |
| |
| // Within refractory of AlertPreserve, but AlertNew should fire since the full bucket has 2. |
| processor->OnLogEvent(CreateAcquireWakelockEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, |
| attributionUids2, attributionTags2, "wl2") |
| .get()); |
| EXPECT_EQ(alertPreserveCount, 1); |
| EXPECT_EQ(alertNewCount, 1); |
| EXPECT_EQ(alertNewDims, wlUid2); |
| |
| // Wakelock for uid1 fired in first bucket, alert preserve should keep the history and fire. |
| processor->OnLogEvent(CreateAcquireWakelockEvent(bucket2StartTimeNs + 25 * NS_PER_SEC, |
| attributionUids1, attributionTags1, "wl1") |
| .get()); |
| EXPECT_EQ(alertPreserveCount, 2); |
| EXPECT_EQ(alertPreserveDims, wlUid1); |
| EXPECT_EQ(alertNewCount, 1); |
| |
| processor->OnLogEvent(CreateSyncStartEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, |
| attributionUids1, attributionTags1, "sync1") |
| .get()); |
| EXPECT_EQ(alertPreserveCount, 2); |
| EXPECT_EQ(alertNewCount, 1); |
| EXPECT_EQ(alertRemoveCount, 3); |
| |
| // Clear data so it doesn't stay on disk. |
| vector<uint8_t> buffer; |
| processor->onDumpReport(key, bucket2StartTimeNs + 100 * NS_PER_SEC, true, true, ADB_DUMP, FAST, |
| &buffer); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, preserveSubId); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, replaceSubId); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, removeSubId); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, newSubId); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestAnomalyDurationMetric) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| |
| AtomMatcher wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); |
| *config.add_atom_matcher() = wakelockAcquireMatcher; |
| AtomMatcher wakelockReleaseMatcher = CreateReleaseWakelockAtomMatcher(); |
| *config.add_atom_matcher() = wakelockReleaseMatcher; |
| AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); |
| *config.add_atom_matcher() = screenOnMatcher; |
| AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); |
| *config.add_atom_matcher() = screenOffMatcher; |
| |
| Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); |
| *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| *config.add_predicate() = holdingWakelockPredicate; |
| Predicate screenOnPredicate = CreateScreenIsOnPredicate(); |
| *config.add_predicate() = screenOnPredicate; |
| |
| DurationMetric durationWakelock = |
| createDurationMetric("DurWakelock", holdingWakelockPredicate.id(), nullopt, {}); |
| *durationWakelock.mutable_dimensions_in_what() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| DurationMetric durationScreen = |
| createDurationMetric("DurScreen", screenOnPredicate.id(), nullopt, {}); |
| *config.add_duration_metric() = durationScreen; |
| *config.add_duration_metric() = durationWakelock; |
| |
| Alert alertPreserve = createAlert("AlertPreserve", durationWakelock.id(), /*buckets=*/2, |
| /*triggerSumGt=*/30 * NS_PER_SEC); |
| alertPreserve.set_refractory_period_secs(300); |
| Alert alertReplace = createAlert("AlertReplace", durationScreen.id(), 2, 30 * NS_PER_SEC); |
| alertReplace.set_refractory_period_secs(1); |
| Alert alertRemove = createAlert("AlertRemove", durationWakelock.id(), 5, 10 * NS_PER_SEC); |
| alertRemove.set_refractory_period_secs(1); |
| *config.add_alert() = alertReplace; |
| *config.add_alert() = alertPreserve; |
| *config.add_alert() = alertRemove; |
| |
| int preserveSubId = 1, replaceSubId = 2, removeSubId = 3; |
| Subscription preserveSub = createSubscription("S1", Subscription::ALERT, alertPreserve.id()); |
| preserveSub.mutable_broadcast_subscriber_details()->set_subscriber_id(preserveSubId); |
| Subscription replaceSub = createSubscription("S2", Subscription::ALERT, alertReplace.id()); |
| replaceSub.mutable_broadcast_subscriber_details()->set_subscriber_id(replaceSubId); |
| Subscription removeSub = createSubscription("S3", Subscription::ALERT, alertRemove.id()); |
| removeSub.mutable_broadcast_subscriber_details()->set_subscriber_id(removeSubId); |
| *config.add_subscription() = preserveSub; |
| *config.add_subscription() = removeSub; |
| *config.add_subscription() = replaceSub; |
| |
| int app1Uid = 123, app2Uid = 456, app3Uid = 789, app4Uid = 111; |
| vector<int> attributionUids1 = {app1Uid}, attributionUids2 = {app2Uid}, |
| attributionUids3 = {app3Uid}, attributionUids4 = {app4Uid}; |
| vector<string> attributionTags1 = {"App1"}, attributionTags2 = {"App2"}, |
| attributionTags3 = {"App3"}, attributionTags4 = {"App4"}; |
| |
| int64_t configUid = 123, configId = 987; |
| ConfigKey key(configUid, configId); |
| |
| int alertPreserveCount = 0, alertRemoveCount = 0; |
| StatsDimensionsValueParcel alertPreserveDims; |
| StatsDimensionsValueParcel alertRemoveDims; |
| |
| // The binder calls here will happen synchronously because they are in-process. |
| shared_ptr<MockPendingIntentRef> preserveBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*preserveBroadcast, sendSubscriberBroadcast(configUid, configId, preserveSub.id(), |
| alertPreserve.id(), _, _)) |
| .Times(4) |
| .WillRepeatedly( |
| Invoke([&alertPreserveCount, &alertPreserveDims]( |
| int64_t, int64_t, int64_t, int64_t, const vector<string>&, |
| const StatsDimensionsValueParcel& dimensionsValueParcel) { |
| alertPreserveCount++; |
| alertPreserveDims = dimensionsValueParcel; |
| return Status::ok(); |
| })); |
| |
| shared_ptr<MockPendingIntentRef> replaceBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*replaceBroadcast, sendSubscriberBroadcast(configUid, configId, replaceSub.id(), |
| alertReplace.id(), _, _)) |
| .Times(0); |
| |
| shared_ptr<MockPendingIntentRef> removeBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*removeBroadcast, sendSubscriberBroadcast(configUid, configId, removeSub.id(), |
| alertRemove.id(), _, _)) |
| .Times(6) |
| .WillRepeatedly( |
| Invoke([&alertRemoveCount, &alertRemoveDims]( |
| int64_t, int64_t, int64_t, int64_t, const vector<string>&, |
| const StatsDimensionsValueParcel& dimensionsValueParcel) { |
| alertRemoveCount++; |
| alertRemoveDims = dimensionsValueParcel; |
| return Status::ok(); |
| })); |
| |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, preserveSubId, preserveBroadcast); |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, replaceSubId, replaceBroadcast); |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, removeSubId, removeBroadcast); |
| |
| shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); |
| sp<StatsLogProcessor> processor = service->mProcessor; |
| uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL; |
| int64_t bucketStartTimeNs = processor->mTimeBaseNs; |
| int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC; |
| uint64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; |
| processor->OnConfigUpdated(bucketStartTimeNs, key, config); |
| |
| StatsDimensionsValueParcel wlUid1 = |
| CreateAttributionUidDimensionsValueParcel(util::WAKELOCK_STATE_CHANGED, app1Uid); |
| StatsDimensionsValueParcel wlUid2 = |
| CreateAttributionUidDimensionsValueParcel(util::WAKELOCK_STATE_CHANGED, app2Uid); |
| StatsDimensionsValueParcel wlUid3 = |
| CreateAttributionUidDimensionsValueParcel(util::WAKELOCK_STATE_CHANGED, app3Uid); |
| StatsDimensionsValueParcel wlUid4 = |
| CreateAttributionUidDimensionsValueParcel(util::WAKELOCK_STATE_CHANGED, app4Uid); |
| |
| int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; |
| processor->OnLogEvent( |
| CreateAcquireWakelockEvent(eventTimeNs, attributionUids1, attributionTags1, "wl1") |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 0); |
| |
| eventTimeNs = bucketStartTimeNs + 20 * NS_PER_SEC; |
| processor->OnLogEvent(CreateScreenStateChangedEvent( |
| eventTimeNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON) |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 0); |
| |
| // Uid 1 accumulates 15 seconds in bucket #1. |
| eventTimeNs = bucketStartTimeNs + 30 * NS_PER_SEC; |
| processor->OnLogEvent( |
| CreateReleaseWakelockEvent(eventTimeNs, attributionUids1, attributionTags1, "wl1") |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 1); |
| EXPECT_EQ(alertRemoveDims, wlUid1); |
| |
| // 20 seconds accumulated in bucket #1. |
| eventTimeNs = bucketStartTimeNs + 40 * NS_PER_SEC; |
| processor->OnLogEvent(CreateScreenStateChangedEvent( |
| eventTimeNs, android::view::DisplayStateEnum::DISPLAY_STATE_OFF) |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 1); |
| |
| eventTimeNs = bucket2StartTimeNs + 2 * NS_PER_SEC; |
| processor->OnLogEvent( |
| CreateAcquireWakelockEvent(eventTimeNs, attributionUids4, attributionTags4, "wl4") |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 1); |
| |
| eventTimeNs = bucket2StartTimeNs + 5 * NS_PER_SEC; |
| processor->OnLogEvent( |
| CreateAcquireWakelockEvent(eventTimeNs, attributionUids2, attributionTags2, "wl2") |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 1); |
| |
| // Alarm for alert remove for uid 4. |
| eventTimeNs = bucket2StartTimeNs + 13 * NS_PER_SEC; |
| processor->OnLogEvent(CreateBatteryStateChangedEvent( |
| eventTimeNs, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB) |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 2); |
| EXPECT_EQ(alertRemoveDims, wlUid4); |
| |
| // Uid3 will be pending at the update. |
| // Also acts as the alarm for alert remove for uid 2. |
| eventTimeNs = bucket2StartTimeNs + 30 * NS_PER_SEC; |
| processor->OnLogEvent( |
| CreateAcquireWakelockEvent(eventTimeNs, attributionUids3, attributionTags3, "wl3") |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 0); |
| EXPECT_EQ(alertRemoveCount, 3); |
| EXPECT_EQ(alertRemoveDims, wlUid2); |
| |
| // Alarm for alert preserve for uid 4, enters 5 min refractory period. |
| eventTimeNs = bucket2StartTimeNs + 33 * NS_PER_SEC; |
| processor->OnLogEvent(CreateBatteryStateChangedEvent( |
| eventTimeNs, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB) |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 1); |
| EXPECT_EQ(alertPreserveDims, wlUid4); |
| EXPECT_EQ(alertRemoveCount, 3); |
| |
| // Uid 2 accumulates 32 seconds in partial bucket before the update. Alert preserve fires. |
| // Preserve enters 5 min refractory for uid 2. |
| // Alert remove fires again for uid 2 since the refractory has expired. |
| eventTimeNs = bucket2StartTimeNs + 37 * NS_PER_SEC; |
| processor->OnLogEvent( |
| CreateReleaseWakelockEvent(eventTimeNs, attributionUids2, attributionTags2, "wl2") |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 2); |
| EXPECT_EQ(alertPreserveDims, wlUid2); |
| EXPECT_EQ(alertRemoveCount, 4); |
| EXPECT_EQ(alertRemoveDims, wlUid2); |
| |
| // Alarm for alert remove for uid 3. |
| eventTimeNs = bucket2StartTimeNs + 41 * NS_PER_SEC; |
| processor->OnLogEvent(CreateBatteryStateChangedEvent( |
| eventTimeNs, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB) |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 2); |
| EXPECT_EQ(alertRemoveCount, 5); |
| EXPECT_EQ(alertRemoveDims, wlUid3); |
| |
| // Release wl for uid 4, has accumulated 41 seconds in partial bucket before update. |
| // Acts as alarm for uid3 of alert remove. |
| eventTimeNs = bucket2StartTimeNs + 43 * NS_PER_SEC; |
| processor->OnLogEvent( |
| CreateReleaseWakelockEvent(eventTimeNs, attributionUids4, attributionTags4, "wl4") |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 2); |
| EXPECT_EQ(alertRemoveCount, 6); |
| EXPECT_EQ(alertRemoveDims, wlUid4); |
| |
| // Starts the timer for screen on. |
| eventTimeNs = bucket2StartTimeNs + 46 * NS_PER_SEC; |
| processor->OnLogEvent(CreateScreenStateChangedEvent( |
| eventTimeNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON) |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 2); |
| EXPECT_EQ(alertRemoveCount, 6); |
| |
| // Do config update. |
| StatsdConfig newConfig; |
| newConfig.add_allowed_log_source("AID_ROOT"); |
| *newConfig.add_atom_matcher() = wakelockAcquireMatcher; |
| *newConfig.add_atom_matcher() = screenOffMatcher; |
| *newConfig.add_atom_matcher() = wakelockReleaseMatcher; |
| *newConfig.add_atom_matcher() = screenOnMatcher; |
| *newConfig.add_predicate() = screenOnPredicate; |
| *newConfig.add_predicate() = holdingWakelockPredicate; |
| *newConfig.add_duration_metric() = durationWakelock; |
| *newConfig.add_duration_metric() = durationScreen; |
| |
| alertReplace.set_refractory_period_secs(2); // Causes replacement. |
| // New alert on existing metric. Should get current full bucket, but not history of 1st bucket. |
| Alert alertNew = createAlert("AlertNew", durationWakelock.id(), /*buckets=*/2, |
| /*triggerSumGt=*/40 * NS_PER_SEC); |
| *newConfig.add_alert() = alertPreserve; |
| *newConfig.add_alert() = alertNew; |
| *newConfig.add_alert() = alertReplace; |
| |
| int newSubId = 4; |
| Subscription newSub = createSubscription("S4", Subscription::ALERT, alertNew.id()); |
| newSub.mutable_broadcast_subscriber_details()->set_subscriber_id(newSubId); |
| *newConfig.add_subscription() = newSub; |
| *newConfig.add_subscription() = replaceSub; |
| *newConfig.add_subscription() = preserveSub; |
| |
| int alertNewCount = 0; |
| StatsDimensionsValueParcel alertNewDims; |
| shared_ptr<MockPendingIntentRef> newBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*newBroadcast, |
| sendSubscriberBroadcast(configUid, configId, newSub.id(), alertNew.id(), _, _)) |
| .Times(3) |
| .WillRepeatedly( |
| Invoke([&alertNewCount, &alertNewDims]( |
| int64_t, int64_t, int64_t, int64_t, const vector<string>&, |
| const StatsDimensionsValueParcel& dimensionsValueParcel) { |
| alertNewCount++; |
| alertNewDims = dimensionsValueParcel; |
| return Status::ok(); |
| })); |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, newSubId, newBroadcast); |
| |
| int64_t updateTimeNs = bucket2StartTimeNs + 50 * NS_PER_SEC; |
| processor->OnConfigUpdated(updateTimeNs, key, newConfig); |
| |
| // Alert preserve will set alarm after the refractory period, but alert new will set it for |
| // 8 seconds after this event. |
| // Alert new should fire for uid 4, since it has already accumulated 41s and should fire on the |
| // first event after the update. |
| eventTimeNs = bucket2StartTimeNs + 55 * NS_PER_SEC; |
| processor->OnLogEvent( |
| CreateAcquireWakelockEvent(eventTimeNs, attributionUids2, attributionTags2, "wl2") |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 2); |
| EXPECT_EQ(alertNewCount, 1); |
| EXPECT_EQ(alertNewDims, wlUid4); |
| |
| eventTimeNs = bucket2StartTimeNs + 60 * NS_PER_SEC; |
| // Alert replace doesn't fire because it has lost history. |
| processor->OnLogEvent(CreateScreenStateChangedEvent( |
| eventTimeNs, android::view::DisplayStateEnum::DISPLAY_STATE_OFF) |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 2); |
| EXPECT_EQ(alertNewCount, 1); |
| |
| // Alert preserve has 15 seconds from 1st bucket, so alert should fire at bucket2Start + 80. |
| // Serves as alarm for alert new for uid2. |
| // Also serves as alarm for alert preserve for uid 3, which began at bucket2Start + 30. |
| eventTimeNs = bucket2StartTimeNs + 65 * NS_PER_SEC; |
| processor->OnLogEvent( |
| CreateAcquireWakelockEvent(eventTimeNs, attributionUids1, attributionTags1, "wl1") |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 3); |
| EXPECT_EQ(alertPreserveDims, wlUid3); |
| EXPECT_EQ(alertNewCount, 2); |
| EXPECT_EQ(alertNewDims, wlUid2); |
| |
| // Release wakelock for uid1, causing alert preserve to fire for uid1. |
| // Also serves as alarm for alert new for uid3. |
| eventTimeNs = bucket2StartTimeNs + 81 * NS_PER_SEC; |
| processor->OnLogEvent( |
| CreateReleaseWakelockEvent(eventTimeNs, attributionUids1, attributionTags1, "wl1") |
| .get(), |
| eventTimeNs); |
| EXPECT_EQ(alertPreserveCount, 4); |
| EXPECT_EQ(alertPreserveDims, wlUid1); |
| EXPECT_EQ(alertNewCount, 3); |
| EXPECT_EQ(alertNewDims, wlUid3); |
| |
| // Clear data so it doesn't stay on disk. |
| vector<uint8_t> buffer; |
| processor->onDumpReport(key, bucket2StartTimeNs + 100 * NS_PER_SEC, true, true, ADB_DUMP, FAST, |
| &buffer); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, preserveSubId); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, replaceSubId); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, removeSubId); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, newSubId); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestAlarms) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| Alarm alarmPreserve = createAlarm("AlarmPreserve", /*offset*/ 5 * MS_PER_SEC, |
| /*period*/ TimeUnitToBucketSizeInMillis(ONE_MINUTE)); |
| Alarm alarmReplace = createAlarm("AlarmReplace", /*offset*/ 1, |
| /*period*/ TimeUnitToBucketSizeInMillis(FIVE_MINUTES)); |
| Alarm alarmRemove = createAlarm("AlarmRemove", /*offset*/ 1, |
| /*period*/ TimeUnitToBucketSizeInMillis(ONE_MINUTE)); |
| *config.add_alarm() = alarmReplace; |
| *config.add_alarm() = alarmPreserve; |
| *config.add_alarm() = alarmRemove; |
| |
| int preserveSubId = 1, replaceSubId = 2, removeSubId = 3; |
| Subscription preserveSub = createSubscription("S1", Subscription::ALARM, alarmPreserve.id()); |
| preserveSub.mutable_broadcast_subscriber_details()->set_subscriber_id(preserveSubId); |
| Subscription replaceSub = createSubscription("S2", Subscription::ALARM, alarmReplace.id()); |
| replaceSub.mutable_broadcast_subscriber_details()->set_subscriber_id(replaceSubId); |
| Subscription removeSub = createSubscription("S3", Subscription::ALARM, alarmRemove.id()); |
| removeSub.mutable_broadcast_subscriber_details()->set_subscriber_id(removeSubId); |
| *config.add_subscription() = preserveSub; |
| *config.add_subscription() = removeSub; |
| *config.add_subscription() = replaceSub; |
| |
| int64_t configUid = 123, configId = 987; |
| ConfigKey key(configUid, configId); |
| |
| int alarmPreserveCount = 0, alarmReplaceCount = 0, alarmRemoveCount = 0; |
| |
| // The binder calls here will happen synchronously because they are in-process. |
| shared_ptr<MockPendingIntentRef> preserveBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*preserveBroadcast, sendSubscriberBroadcast(configUid, configId, preserveSub.id(), |
| alarmPreserve.id(), _, _)) |
| .Times(4) |
| .WillRepeatedly([&alarmPreserveCount](int64_t, int64_t, int64_t, int64_t, |
| const vector<string>&, |
| const StatsDimensionsValueParcel&) { |
| alarmPreserveCount++; |
| return Status::ok(); |
| }); |
| |
| shared_ptr<MockPendingIntentRef> replaceBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*replaceBroadcast, sendSubscriberBroadcast(configUid, configId, replaceSub.id(), |
| alarmReplace.id(), _, _)) |
| .Times(2) |
| .WillRepeatedly([&alarmReplaceCount](int64_t, int64_t, int64_t, int64_t, |
| const vector<string>&, |
| const StatsDimensionsValueParcel&) { |
| alarmReplaceCount++; |
| return Status::ok(); |
| }); |
| |
| shared_ptr<MockPendingIntentRef> removeBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*removeBroadcast, sendSubscriberBroadcast(configUid, configId, removeSub.id(), |
| alarmRemove.id(), _, _)) |
| .Times(1) |
| .WillRepeatedly([&alarmRemoveCount](int64_t, int64_t, int64_t, int64_t, |
| const vector<string>&, |
| const StatsDimensionsValueParcel&) { |
| alarmRemoveCount++; |
| return Status::ok(); |
| }); |
| |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, preserveSubId, preserveBroadcast); |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, replaceSubId, replaceBroadcast); |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, removeSubId, removeBroadcast); |
| |
| int64_t startTimeSec = 10; |
| sp<StatsLogProcessor> processor = CreateStatsLogProcessor( |
| startTimeSec * NS_PER_SEC, startTimeSec * NS_PER_SEC, config, key); |
| |
| sp<AlarmMonitor> alarmMonitor = processor->getPeriodicAlarmMonitor(); |
| // First alarm is for alarm preserve's offset of 5 seconds. |
| EXPECT_EQ(alarmMonitor->getRegisteredAlarmTimeSec(), startTimeSec + 5); |
| |
| // Alarm fired at 5. AlarmPreserve should fire. |
| int32_t alarmFiredTimestampSec = startTimeSec + 5; |
| auto alarmSet = alarmMonitor->popSoonerThan(static_cast<uint32_t>(alarmFiredTimestampSec)); |
| processor->onPeriodicAlarmFired(alarmFiredTimestampSec * NS_PER_SEC, alarmSet); |
| EXPECT_EQ(alarmPreserveCount, 1); |
| EXPECT_EQ(alarmReplaceCount, 0); |
| EXPECT_EQ(alarmRemoveCount, 0); |
| EXPECT_EQ(alarmMonitor->getRegisteredAlarmTimeSec(), startTimeSec + 60); |
| |
| // Alarm fired at 75. AlarmPreserve and AlarmRemove should fire. |
| alarmFiredTimestampSec = startTimeSec + 75; |
| alarmSet = alarmMonitor->popSoonerThan(static_cast<uint32_t>(alarmFiredTimestampSec)); |
| processor->onPeriodicAlarmFired(alarmFiredTimestampSec * NS_PER_SEC, alarmSet); |
| EXPECT_EQ(alarmPreserveCount, 2); |
| EXPECT_EQ(alarmReplaceCount, 0); |
| EXPECT_EQ(alarmRemoveCount, 1); |
| EXPECT_EQ(alarmMonitor->getRegisteredAlarmTimeSec(), startTimeSec + 120); |
| |
| // Do config update. |
| StatsdConfig newConfig; |
| newConfig.add_allowed_log_source("AID_ROOT"); |
| |
| // Change alarm replace's definition. |
| alarmReplace.set_period_millis(TimeUnitToBucketSizeInMillis(ONE_MINUTE)); |
| Alarm alarmNew = createAlarm("AlarmNew", /*offset*/ 1, |
| /*period*/ TimeUnitToBucketSizeInMillis(FIVE_MINUTES)); |
| *newConfig.add_alarm() = alarmNew; |
| *newConfig.add_alarm() = alarmPreserve; |
| *newConfig.add_alarm() = alarmReplace; |
| |
| int newSubId = 4; |
| Subscription newSub = createSubscription("S4", Subscription::ALARM, alarmNew.id()); |
| newSub.mutable_broadcast_subscriber_details()->set_subscriber_id(newSubId); |
| *newConfig.add_subscription() = newSub; |
| *newConfig.add_subscription() = replaceSub; |
| *newConfig.add_subscription() = preserveSub; |
| |
| int alarmNewCount = 0; |
| shared_ptr<MockPendingIntentRef> newBroadcast = |
| SharedRefBase::make<StrictMock<MockPendingIntentRef>>(); |
| EXPECT_CALL(*newBroadcast, |
| sendSubscriberBroadcast(configUid, configId, newSub.id(), alarmNew.id(), _, _)) |
| .Times(1) |
| .WillRepeatedly([&alarmNewCount](int64_t, int64_t, int64_t, int64_t, |
| const vector<string>&, |
| const StatsDimensionsValueParcel&) { |
| alarmNewCount++; |
| return Status::ok(); |
| }); |
| SubscriberReporter::getInstance().setBroadcastSubscriber(key, newSubId, newBroadcast); |
| |
| processor->OnConfigUpdated((startTimeSec + 90) * NS_PER_SEC, key, newConfig); |
| // After the update, the alarm time should remain unchanged since alarm replace now fires every |
| // minute with no offset. |
| EXPECT_EQ(alarmMonitor->getRegisteredAlarmTimeSec(), startTimeSec + 120); |
| |
| // Alarm fired at 120. AlermReplace should fire. |
| alarmFiredTimestampSec = startTimeSec + 120; |
| alarmSet = alarmMonitor->popSoonerThan(static_cast<uint32_t>(alarmFiredTimestampSec)); |
| processor->onPeriodicAlarmFired(alarmFiredTimestampSec * NS_PER_SEC, alarmSet); |
| EXPECT_EQ(alarmPreserveCount, 2); |
| EXPECT_EQ(alarmReplaceCount, 1); |
| EXPECT_EQ(alarmNewCount, 0); |
| EXPECT_EQ(alarmMonitor->getRegisteredAlarmTimeSec(), startTimeSec + 125); |
| |
| // Alarm fired at 130. AlarmPreserve should fire. |
| alarmFiredTimestampSec = startTimeSec + 130; |
| alarmSet = alarmMonitor->popSoonerThan(static_cast<uint32_t>(alarmFiredTimestampSec)); |
| processor->onPeriodicAlarmFired(alarmFiredTimestampSec * NS_PER_SEC, alarmSet); |
| EXPECT_EQ(alarmPreserveCount, 3); |
| EXPECT_EQ(alarmReplaceCount, 1); |
| EXPECT_EQ(alarmNewCount, 0); |
| EXPECT_EQ(alarmMonitor->getRegisteredAlarmTimeSec(), startTimeSec + 180); |
| |
| // Alarm fired late at 310. All alerms should fire. |
| alarmFiredTimestampSec = startTimeSec + 310; |
| alarmSet = alarmMonitor->popSoonerThan(static_cast<uint32_t>(alarmFiredTimestampSec)); |
| processor->onPeriodicAlarmFired(alarmFiredTimestampSec * NS_PER_SEC, alarmSet); |
| EXPECT_EQ(alarmPreserveCount, 4); |
| EXPECT_EQ(alarmReplaceCount, 2); |
| EXPECT_EQ(alarmNewCount, 1); |
| EXPECT_EQ(alarmMonitor->getRegisteredAlarmTimeSec(), startTimeSec + 360); |
| |
| // Clear subscribers |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, preserveSubId); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, replaceSubId); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, removeSubId); |
| SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, newSubId); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhat) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); |
| *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); |
| |
| Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); |
| *config.add_predicate() = holdingWakelockPredicate; |
| |
| ConfigKey key(123, 987); |
| uint64_t bucketStartTimeNs = 10000000000; // 0:10 |
| uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; |
| sp<StatsLogProcessor> processor = |
| CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); |
| |
| int app1Uid = 123; |
| vector<int> attributionUids1 = {app1Uid}; |
| vector<string> attributionTags1 = {"App1"}; |
| // Create a wakelock acquire, causing the condition to be true. |
| unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "wl1"); // 0:10 |
| processor->OnLogEvent(event.get()); |
| |
| // Add metric. |
| DurationMetric* durationMetric = config.add_duration_metric(); |
| durationMetric->set_id(StringToId("WakelockDuration")); |
| durationMetric->set_what(holdingWakelockPredicate.id()); |
| durationMetric->set_aggregation_type(DurationMetric::SUM); |
| durationMetric->set_bucket(FIVE_MINUTES); |
| |
| uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 |
| processor->OnConfigUpdated(updateTimeNs, key, config); |
| |
| event = CreateReleaseWakelockEvent(bucketStartTimeNs + 80 * NS_PER_SEC, attributionUids1, |
| attributionTags1, |
| "wl1"); // 1:20 |
| processor->OnLogEvent(event.get()); |
| uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 |
| ConfigMetricsReportList reports; |
| vector<uint8_t> buffer; |
| processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); |
| 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); |
| EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); |
| |
| StatsLogReport::DurationMetricDataWrapper metricData; |
| sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); |
| ASSERT_EQ(metricData.data_size(), 1); |
| DurationMetricData data = metricData.data(0); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| |
| DurationBucketInfo bucketInfo = data.bucket_info(0); |
| EXPECT_EQ(bucketInfo.start_bucket_elapsed_nanos(), updateTimeNs); |
| EXPECT_EQ(bucketInfo.end_bucket_elapsed_nanos(), dumpTimeNs); |
| EXPECT_EQ(bucketInfo.duration_nanos(), 20 * NS_PER_SEC); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedCondition) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); |
| *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); |
| *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); |
| *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); |
| |
| Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); |
| // The predicate is dimensioning by first attribution node by uid. |
| *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| *config.add_predicate() = holdingWakelockPredicate; |
| |
| Predicate isInBackgroundPredicate = CreateIsInBackgroundPredicate(); |
| *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = |
| CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/}); |
| *config.add_predicate() = isInBackgroundPredicate; |
| |
| ConfigKey key(123, 987); |
| uint64_t bucketStartTimeNs = 10000000000; // 0:10 |
| uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; |
| sp<StatsLogProcessor> processor = |
| CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); |
| |
| int app1Uid = 123, app2Uid = 456; |
| vector<int> attributionUids1 = {app1Uid}; |
| vector<string> attributionTags1 = {"App1"}; |
| vector<int> attributionUids2 = {app2Uid}; |
| vector<string> attributionTags2 = {"App2"}; |
| unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, |
| attributionUids1, attributionTags1, |
| "wl1"); // 0:10 |
| processor->OnLogEvent(event.get()); |
| event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, app1Uid); // 0:22 |
| processor->OnLogEvent(event.get()); |
| event = CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC, attributionUids2, |
| attributionTags2, |
| "wl1"); // 0:35 |
| processor->OnLogEvent(event.get()); |
| |
| // Add metric. |
| DurationMetric* 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()); |
| *links->mutable_fields_in_what() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| *links->mutable_fields_in_condition() = |
| CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/}); |
| |
| uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 |
| processor->OnConfigUpdated(updateTimeNs, key, config); |
| |
| event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 73 * NS_PER_SEC, app2Uid); // 1:13 |
| processor->OnLogEvent(event.get()); |
| event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1, |
| attributionTags1, "wl1"); // 1:24 |
| processor->OnLogEvent(event.get()); |
| |
| uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 |
| ConfigMetricsReportList reports; |
| vector<uint8_t> buffer; |
| processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); |
| 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); |
| EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); |
| |
| StatsLogReport::DurationMetricDataWrapper metricData; |
| sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); |
| ASSERT_EQ(metricData.data_size(), 2); |
| |
| DurationMetricData data = metricData.data(0); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, |
| app1Uid); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| DurationBucketInfo bucketInfo = data.bucket_info(0); |
| EXPECT_EQ(bucketInfo.duration_nanos(), 24 * NS_PER_SEC); |
| |
| data = metricData.data(1); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, |
| app2Uid); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| bucketInfo = data.bucket_info(0); |
| EXPECT_EQ(bucketInfo.duration_nanos(), 17 * NS_PER_SEC); |
| } |
| |
| TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedState) { |
| StatsdConfig config; |
| config.add_allowed_log_source("AID_ROOT"); |
| *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); |
| *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); |
| |
| Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); |
| // The predicate is dimensioning by first attribution node by uid. |
| *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| *config.add_predicate() = holdingWakelockPredicate; |
| |
| auto uidProcessState = CreateUidProcessState(); |
| *config.add_state() = uidProcessState; |
| |
| // Count metric. We don't care about this one. Only use it so the StateTracker gets persisted. |
| CountMetric* countMetric = config.add_count_metric(); |
| countMetric->set_id(StringToId("Tmp")); |
| countMetric->set_what(config.atom_matcher(0).id()); |
| countMetric->add_slice_by_state(uidProcessState.id()); |
| // The metric is dimensioning by first attribution node and only by uid. |
| *countMetric->mutable_dimensions_in_what() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| countMetric->set_bucket(FIVE_MINUTES); |
| auto stateLink = countMetric->add_state_link(); |
| stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED); |
| *stateLink->mutable_fields_in_what() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| *stateLink->mutable_fields_in_state() = |
| CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/}); |
| config.add_no_report_metric(countMetric->id()); |
| |
| ConfigKey key(123, 987); |
| uint64_t bucketStartTimeNs = 10000000000; // 0:10 |
| uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; |
| sp<StatsLogProcessor> processor = |
| CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); |
| |
| int app1Uid = 123, app2Uid = 456; |
| vector<int> attributionUids1 = {app1Uid}; |
| vector<string> attributionTags1 = {"App1"}; |
| vector<int> attributionUids2 = {app2Uid}; |
| vector<string> attributionTags2 = {"App2"}; |
| unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent( |
| bucketStartTimeNs + 10 * NS_PER_SEC, app1Uid, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:10 |
| processor->OnLogEvent(event.get()); |
| event = CreateAcquireWakelockEvent(bucketStartTimeNs + 22 * NS_PER_SEC, attributionUids1, |
| attributionTags1, |
| "wl1"); // 0:22 |
| processor->OnLogEvent(event.get()); |
| event = CreateUidProcessStateChangedEvent( |
| bucketStartTimeNs + 30 * NS_PER_SEC, app2Uid, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:30 |
| processor->OnLogEvent(event.get()); |
| |
| // Add metric. |
| DurationMetric* durationMetric = config.add_duration_metric(); |
| durationMetric->set_id(StringToId("WakelockDuration")); |
| durationMetric->set_what(holdingWakelockPredicate.id()); |
| durationMetric->add_slice_by_state(uidProcessState.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. |
| stateLink = durationMetric->add_state_link(); |
| stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED); |
| *stateLink->mutable_fields_in_what() = |
| CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
| *stateLink->mutable_fields_in_state() = |
| CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/}); |
| |
| uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 |
| processor->OnConfigUpdated(updateTimeNs, key, config); |
| |
| event = CreateAcquireWakelockEvent(bucketStartTimeNs + 72 * NS_PER_SEC, attributionUids2, |
| attributionTags2, |
| "wl1"); // 1:13 |
| processor->OnLogEvent(event.get()); |
| event = CreateUidProcessStateChangedEvent( |
| bucketStartTimeNs + 75 * NS_PER_SEC, app1Uid, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); // 1:15 |
| processor->OnLogEvent(event.get()); |
| event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1, |
| attributionTags1, "wl1"); // 1:24 |
| processor->OnLogEvent(event.get()); |
| |
| uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 |
| ConfigMetricsReportList reports; |
| vector<uint8_t> buffer; |
| processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); |
| 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); |
| EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); |
| |
| StatsLogReport::DurationMetricDataWrapper metricData; |
| sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); |
| ASSERT_EQ(metricData.data_size(), 3); |
| |
| DurationMetricData data = metricData.data(0); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, |
| app1Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| DurationBucketInfo bucketInfo = data.bucket_info(0); |
| EXPECT_EQ(bucketInfo.duration_nanos(), 15 * NS_PER_SEC); |
| |
| data = metricData.data(1); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, |
| app1Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| bucketInfo = data.bucket_info(0); |
| EXPECT_EQ(bucketInfo.duration_nanos(), 9 * NS_PER_SEC); |
| |
| data = metricData.data(2); |
| ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, |
| app2Uid); |
| ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, |
| android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); |
| ASSERT_EQ(data.bucket_info_size(), 1); |
| bucketInfo = data.bucket_info(0); |
| EXPECT_EQ(bucketInfo.duration_nanos(), 18 * NS_PER_SEC); |
| } |
| |
| #else |
| GTEST_LOG_(INFO) << "This test does nothing.\n"; |
| #endif |
| |
| } // namespace statsd |
| } // namespace os |
| } // namespace android |