// Copyright (C) 2017 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include <gtest/gtest.h> #include "src/StatsLogProcessor.h" #include "src/stats_log_util.h" #include "tests/statsd_test_util.h" #include <vector> namespace android { namespace os { namespace statsd { #ifdef __ANDROID__ namespace { StatsdConfig CreateStatsdConfig() { StatsdConfig config; config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); auto appCrashMatcher = CreateProcessCrashAtomMatcher(); *config.add_atom_matcher() = appCrashMatcher; auto screenIsOffPredicate = CreateScreenIsOffPredicate(); auto isSyncingPredicate = CreateIsSyncingPredicate(); auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); *syncDimension = CreateAttributionUidDimensions( android::util::SYNC_STATE_CHANGED, {Position::FIRST}); syncDimension->add_child()->set_field(2 /* name field*/); auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); *config.add_predicate() = screenIsOffPredicate; *config.add_predicate() = isSyncingPredicate; *config.add_predicate() = isInBackgroundPredicate; auto combinationPredicate = config.add_predicate(); combinationPredicate->set_id(StringToId("combinationPredicate")); combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); addPredicateToPredicateCombination(isInBackgroundPredicate, combinationPredicate); auto countMetric = config.add_count_metric(); countMetric->set_id(StringToId("AppCrashes")); countMetric->set_what(appCrashMatcher.id()); countMetric->set_condition(combinationPredicate->id()); // The metric is dimensioning by uid only. *countMetric->mutable_dimensions_in_what() = CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1}); countMetric->set_bucket(FIVE_MINUTES); // Links between crash atom and condition of app is in syncing. auto links = countMetric->add_links(); links->set_condition(isSyncingPredicate.id()); auto dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( android::util::SYNC_STATE_CHANGED, {Position::FIRST}); // Links between crash atom and condition of app is in background. links = countMetric->add_links(); links->set_condition(isInBackgroundPredicate.id()); dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. auto dimensionCondition = links->mutable_fields_in_condition(); dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); dimensionCondition->add_child()->set_field(1); // uid field. return config; } } // namespace // If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests, // we should use the real API which will clear the data after dump data is called. // TODO: better refactor the code so that the tests are not so verbose. TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) { auto config = CreateStatsdConfig(); uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; ConfigKey cfgKey; auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); int appUid = 123; auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1); auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201); auto crashEvent3= CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101); auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51); auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299); auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001); auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16); auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249); auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351); auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2); auto screenTurnedOnEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2); auto screenTurnedOffEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200); auto screenTurnedOnEvent2 = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2 * bucketSizeNs - 100); std::vector<AttributionNodeInternal> attributions = { CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")}; auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50); auto syncOffEvent1 = CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); auto syncOnEvent2 = CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15); auto moveToForegroundEvent1 = CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250); auto moveToBackgroundEvent2 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350); auto moveToForegroundEvent2 = CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1); /* bucket #1 bucket #2 | | | | | | | | | | (crashEvents) |-------------------------------------|-----------------------------------|--------- | | (MoveToBkground) | | (MoveToForeground) | | (SyncIsOn) | (SyncIsOff) | | (ScreenIsOn) | (ScreenIsOff) */ std::vector<std::unique_ptr<LogEvent>> events; events.push_back(std::move(crashEvent1)); events.push_back(std::move(crashEvent2)); events.push_back(std::move(crashEvent3)); events.push_back(std::move(crashEvent4)); events.push_back(std::move(crashEvent5)); events.push_back(std::move(crashEvent6)); events.push_back(std::move(crashEvent7)); events.push_back(std::move(crashEvent8)); events.push_back(std::move(crashEvent9)); events.push_back(std::move(crashEvent10)); events.push_back(std::move(screenTurnedOnEvent)); events.push_back(std::move(screenTurnedOffEvent)); events.push_back(std::move(screenTurnedOnEvent2)); events.push_back(std::move(syncOnEvent1)); events.push_back(std::move(syncOffEvent1)); events.push_back(std::move(syncOnEvent2)); events.push_back(std::move(moveToBackgroundEvent1)); events.push_back(std::move(moveToForegroundEvent1)); events.push_back(std::move(moveToBackgroundEvent2)); events.push_back(std::move(moveToForegroundEvent2)); sortLogEventsByTimestamp(&events); for (const auto& event : events) { processor->OnLogEvent(event.get()); } ConfigMetricsReportList reports; vector<uint8_t> buffer; processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP, &buffer); EXPECT_TRUE(buffer.size() > 0); EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); backfillDimensionPath(&reports); backfillStringInReport(&reports); backfillStartEndTimestamp(&reports); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); auto data = reports.reports(0).metrics(0).count_metrics().data(0); // Validate dimension value. EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); // Uid field. EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); } TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) { auto config = CreateStatsdConfig(); uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; ConfigKey cfgKey; auto processor = CreateStatsLogProcessor( bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); int appUid = 123; auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1); auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201); auto crashEvent3 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101); auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51); auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299); auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001); auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16); auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249); auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351); auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2); auto screenTurnedOnEvent = CreateScreenStateChangedEvent( android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2); auto screenTurnedOffEvent = CreateScreenStateChangedEvent( android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200); auto screenTurnedOnEvent2 = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2 * bucketSizeNs - 100); std::vector<AttributionNodeInternal> attributions = { CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")}; auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50); auto syncOffEvent1 = CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); auto syncOnEvent2 = CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15); auto moveToForegroundEvent1 = CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250); auto moveToBackgroundEvent2 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350); auto moveToForegroundEvent2 = CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1); /* bucket #1 bucket #2 | | | | | | | | | | (crashEvents) |-------------------------------------|-----------------------------------|--------- | | (MoveToBkground) | | (MoveToForeground) | | (SyncIsOn) | (SyncIsOff) | | (ScreenIsOn) | (ScreenIsOff) */ std::vector<std::unique_ptr<LogEvent>> events; events.push_back(std::move(crashEvent1)); events.push_back(std::move(crashEvent2)); events.push_back(std::move(crashEvent3)); events.push_back(std::move(crashEvent4)); events.push_back(std::move(crashEvent5)); events.push_back(std::move(crashEvent6)); events.push_back(std::move(crashEvent7)); events.push_back(std::move(crashEvent8)); events.push_back(std::move(crashEvent9)); events.push_back(std::move(crashEvent10)); events.push_back(std::move(screenTurnedOnEvent)); events.push_back(std::move(screenTurnedOffEvent)); events.push_back(std::move(screenTurnedOnEvent2)); events.push_back(std::move(syncOnEvent1)); events.push_back(std::move(syncOffEvent1)); events.push_back(std::move(syncOnEvent2)); events.push_back(std::move(moveToBackgroundEvent1)); events.push_back(std::move(moveToForegroundEvent1)); events.push_back(std::move(moveToBackgroundEvent2)); events.push_back(std::move(moveToForegroundEvent2)); sortLogEventsByTimestamp(&events); for (const auto& event : events) { processor->OnLogEvent(event.get()); } ConfigMetricsReportList reports; vector<uint8_t> buffer; processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP, &buffer); EXPECT_TRUE(buffer.size() > 0); EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); backfillDimensionPath(&reports); backfillStringInReport(&reports); backfillStartEndTimestamp(&reports); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3); auto data = reports.reports(0).metrics(0).count_metrics().data(0); // Validate dimension value. EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); // Uid field. EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); } #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif } // namespace statsd } // namespace os } // namespace android