* Creates an incident section for statsd data. * Allows dump to output statsd data, in proto format. * Hooks up two statsd outputs to bugreports: -statsd report data in proto format -statsd metadata (statsdstats) in text format The incident section does not import stats_log.proto because that turns out to be extremely difficult: stats_log.proto imports atoms.proto, which imports more things and is enormous and causes all sorts of problems. atoms.proto was purposefully never compiled in AOSP, so to retain that feature, the incident section uses 'bytes' instead of an actual message. Since this isn't ever read in AOSP (other than testing), this should be fine. Bug: 115678461 Test: take a bug report and confirm valid proto Test: cts-tradefed run cts-dev -m CtsStatsdHostTestCases -t android.cts.statsd.atom.HostAtomTests#testDumpsysStats Change-Id: I1c370af7678d1dc7440ce299ea5ea4da6d33832b
351 lines
17 KiB
C++
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, &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, &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
|