Files
frameworks_base/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
Olivier Gaillard 6c75ecdcef Introduces an option to set a dump latency requirement.
We are currently dumping invalid data for pulled metrics. Pulled metrics
require a new pull when flushing a bucket. We should either do another
pull or invalidate the previous bucket.

There are cases where we cannot afford to do another pull, e.g. statsd
being killed. If we do not have enough time, we'll just invalidte the
bucket to make sure we have correct data.

Bug: 123866830
Test: atest statsd_test
Change-Id: I090127cace3b7265032ebb2c9bddae976c883771
2019-02-20 17:59:50 +00:00

351 lines
17 KiB
C++

// 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.
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, true,
ADB_DUMP, FAST, &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, true,
ADB_DUMP, FAST, &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