Merge "Statsd cpu optimizaton" into pi-dev

This commit is contained in:
TreeHugger Robot
2018-03-08 04:03:19 +00:00
committed by Android (Google) Code Review
46 changed files with 3829 additions and 314 deletions

View File

@@ -199,7 +199,9 @@ LOCAL_SRC_FILES := \
tests/e2e/MetricConditionLink_e2e_test.cpp \
tests/e2e/Attribution_e2e_test.cpp \
tests/e2e/GaugeMetric_e2e_test.cpp \
tests/e2e/DimensionInCondition_e2e_test.cpp
tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp \
tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp \
tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
@@ -247,11 +249,32 @@ include $(CLEAR_VARS)
LOCAL_MODULE := statsd_benchmark
LOCAL_SRC_FILES := $(statsd_common_src) \
src/atom_field_options.proto \
src/atoms.proto \
src/stats_log.proto \
benchmark/main.cpp \
benchmark/hello_world_benchmark.cpp \
benchmark/log_event_benchmark.cpp \
benchmark/stats_write_benchmark.cpp \
benchmark/filter_value_benchmark.cpp
benchmark/filter_value_benchmark.cpp \
benchmark/get_dimensions_for_condition_benchmark.cpp \
benchmark/metric_util.cpp \
benchmark/duration_metric_benchmark.cpp
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries)
LOCAL_PROTOC_OPTIMIZE_TYPE := full
LOCAL_PROTOC_FLAGS := \
-Iexternal/protobuf/src
LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
libprotobuf-cpp-full
LOCAL_STATIC_JAVA_LIBRARIES := \
platformprotoslite
LOCAL_C_INCLUDES := $(statsd_common_c_includes)
@@ -285,4 +308,4 @@ statsd_common_static_libraries:=
statsd_common_shared_libraries:=
include $(call all-makefiles-under,$(LOCAL_PATH))
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@@ -0,0 +1,320 @@
/*
* Copyright (C) 2018 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 <vector>
#include "benchmark/benchmark.h"
#include "FieldValue.h"
#include "HashableDimensionKey.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
#include "metric_util.h"
namespace android {
namespace os {
namespace statsd {
using std::vector;
static StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
StatsdConfig config;
*config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = CreateSyncEndAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
auto scheduledJobPredicate = CreateScheduledJobPredicate();
auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
dimensions->add_child()->set_field(2); // job name field.
auto screenIsOffPredicate = CreateScreenIsOffPredicate();
auto isSyncingPredicate = CreateIsSyncingPredicate();
auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
*syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
{Position::FIRST});
if (addExtraDimensionInCondition) {
syncDimension->add_child()->set_field(2 /* name field*/);
}
*config.add_predicate() = scheduledJobPredicate;
*config.add_predicate() = screenIsOffPredicate;
*config.add_predicate() = isSyncingPredicate;
auto combinationPredicate = config.add_predicate();
combinationPredicate->set_id(StringToId("CombinationPredicate"));
combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
auto metric = config.add_duration_metric();
metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("scheduledJob"));
metric->set_what(scheduledJobPredicate.id());
metric->set_condition(combinationPredicate->id());
metric->set_aggregation_type(aggregationType);
auto dimensionWhat = metric->mutable_dimensions_in_what();
dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
dimensionWhat->add_child()->set_field(2); // job name field.
*metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
android::util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
}
static StatsdConfig CreateDurationMetricConfig_Link_AND_CombinationCondition(
DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
StatsdConfig config;
*config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = CreateSyncEndAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
auto scheduledJobPredicate = CreateScheduledJobPredicate();
auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
*dimensions = CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
dimensions->add_child()->set_field(2); // job name field.
auto isSyncingPredicate = CreateIsSyncingPredicate();
auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
*syncDimension = CreateAttributionUidDimensions(
android::util::SYNC_STATE_CHANGED, {Position::FIRST});
if (addExtraDimensionInCondition) {
syncDimension->add_child()->set_field(2 /* name field*/);
}
auto screenIsOffPredicate = CreateScreenIsOffPredicate();
*config.add_predicate() = scheduledJobPredicate;
*config.add_predicate() = screenIsOffPredicate;
*config.add_predicate() = isSyncingPredicate;
auto combinationPredicate = config.add_predicate();
combinationPredicate->set_id(StringToId("CombinationPredicate"));
combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
auto metric = config.add_duration_metric();
metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("scheduledJob"));
metric->set_what(scheduledJobPredicate.id());
metric->set_condition(combinationPredicate->id());
metric->set_aggregation_type(aggregationType);
*metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
auto links = metric->add_links();
links->set_condition(isSyncingPredicate.id());
*links->mutable_fields_in_what() =
CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
*links->mutable_fields_in_condition() =
CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
}
static void BM_DurationMetricNoLink(benchmark::State& state) {
ConfigKey cfgKey;
auto config = CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
DurationMetric::SUM, false);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
std::vector<AttributionNodeInternal> attributions1 = {
CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(222, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions2 = {
CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 11));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 40));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 102));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 450));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 650));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + bucketSizeNs + 100));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + bucketSizeNs + 640));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + bucketSizeNs + 650));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 10));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 50));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 200));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 300));
events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
bucketStartTimeNs + 400));
events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
bucketStartTimeNs + 401));
events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 700));
sortLogEventsByTimestamp(&events);
while (state.KeepRunning()) {
auto processor = CreateStatsLogProcessor(
bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
}
}
BENCHMARK(BM_DurationMetricNoLink);
static void BM_DurationMetricLink(benchmark::State& state) {
ConfigKey cfgKey;
auto config = CreateDurationMetricConfig_Link_AND_CombinationCondition(
DurationMetric::SUM, false);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
std::vector<AttributionNodeInternal> attributions1 = {
CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(222, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions2 = {
CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions3 = {
CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 55));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 120));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 121));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 450));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 501));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + bucketSizeNs + 100));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2",
bucketStartTimeNs + bucketSizeNs + 850));
events.push_back(
CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
bucketStartTimeNs + bucketSizeNs - 2));
events.push_back(
CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
bucketStartTimeNs + bucketSizeNs + 900));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 50));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 110));
events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
bucketStartTimeNs + 300));
events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 700));
events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
bucketStartTimeNs + 400));
events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
bucketStartTimeNs + 550));
events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
bucketStartTimeNs + 800));
events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
bucketStartTimeNs + bucketSizeNs + 700));
sortLogEventsByTimestamp(&events);
while (state.KeepRunning()) {
auto processor = CreateStatsLogProcessor(
bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
}
}
BENCHMARK(BM_DurationMetricLink);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -18,6 +18,7 @@
#include "FieldValue.h"
#include "HashableDimensionKey.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
namespace android {
namespace os {
@@ -25,17 +26,29 @@ namespace statsd {
using std::vector;
static void createLogEventAndMatcher(LogEvent* event, FieldMatcher *field_matcher) {
AttributionNodeInternal node;
node.set_uid(100);
node.set_tag("LOCATION");
std::vector<AttributionNodeInternal> nodes = {node, node};
event->write(nodes);
event->write(3.2f);
event->write("LOCATION");
event->write((int64_t)990);
event->init();
field_matcher->set_field(1);
auto child = field_matcher->add_child();
child->set_field(1);
child->set_position(FIRST);
child->add_child()->set_field(1);
}
static void BM_FilterValue(benchmark::State& state) {
LogEvent event(1, 100000);
event.write(3.2f);
event.write("LOCATION");
event.write((int64_t)990);
event.init();
FieldMatcher field_matcher;
field_matcher.set_field(1);
field_matcher.add_child()->set_field(2);
field_matcher.add_child()->set_field(3);
createLogEventAndMatcher(&event, &field_matcher);
std::vector<Matcher> matchers;
translateFieldMatcher(field_matcher, &matchers);
@@ -48,6 +61,22 @@ static void BM_FilterValue(benchmark::State& state) {
BENCHMARK(BM_FilterValue);
static void BM_FilterValue2(benchmark::State& state) {
LogEvent event(1, 100000);
FieldMatcher field_matcher;
createLogEventAndMatcher(&event, &field_matcher);
std::vector<Matcher> matchers;
translateFieldMatcher(field_matcher, &matchers);
while (state.KeepRunning()) {
HashableDimensionKey output;
filterValues(matchers, event.getValues(), &output);
}
}
BENCHMARK(BM_FilterValue2);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2018 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 <vector>
#include "benchmark/benchmark.h"
#include "FieldValue.h"
#include "HashableDimensionKey.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
namespace android {
namespace os {
namespace statsd {
using std::vector;
static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) {
AttributionNodeInternal node;
node.set_uid(100);
node.set_tag("LOCATION");
std::vector<AttributionNodeInternal> nodes = {node, node};
event->write(nodes);
event->write(3.2f);
event->write("LOCATION");
event->write((int64_t)990);
event->init();
link->conditionId = 1;
FieldMatcher field_matcher;
field_matcher.set_field(event->GetTagId());
auto child = field_matcher.add_child();
child->set_field(1);
child->set_position(FIRST);
child->add_child()->set_field(1);
translateFieldMatcher(field_matcher, &link->metricFields);
field_matcher.set_field(event->GetTagId() + 1);
translateFieldMatcher(field_matcher, &link->conditionFields);
}
static void BM_GetDimensionInCondition(benchmark::State& state) {
Metric2Condition link;
LogEvent event(1, 100000);
createLogEventAndLink(&event, &link);
while (state.KeepRunning()) {
HashableDimensionKey output;
getDimensionForCondition(event.getValues(), link, &output);
}
}
BENCHMARK(BM_GetDimensionInCondition);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -0,0 +1,394 @@
// 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 "metric_util.h"
namespace android {
namespace os {
namespace statsd {
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
simple_atom_matcher->set_atom_id(atomId);
return atom_matcher;
}
AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name,
ScheduledJobStateChanged::State state) {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(3); // State field.
field_value_matcher->set_eq_int(state);
return atom_matcher;
}
AtomMatcher CreateStartScheduledJobAtomMatcher() {
return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart",
ScheduledJobStateChanged::STARTED);
}
AtomMatcher CreateFinishScheduledJobAtomMatcher() {
return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish",
ScheduledJobStateChanged::FINISHED);
}
AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED);
return atom_matcher;
}
AtomMatcher CreateUidProcessStateChangedAtomMatcher() {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId("UidProcessStateChanged"));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED);
return atom_matcher;
}
AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
WakelockStateChanged::State state) {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(4); // State field.
field_value_matcher->set_eq_int(state);
return atom_matcher;
}
AtomMatcher CreateAcquireWakelockAtomMatcher() {
return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE);
}
AtomMatcher CreateReleaseWakelockAtomMatcher() {
return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
}
AtomMatcher CreateScreenStateChangedAtomMatcher(
const string& name, android::view::DisplayStateEnum state) {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(1); // State field.
field_value_matcher->set_eq_int(state);
return atom_matcher;
}
AtomMatcher CreateScreenTurnedOnAtomMatcher() {
return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
}
AtomMatcher CreateScreenTurnedOffAtomMatcher() {
return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff",
::android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
}
AtomMatcher CreateSyncStateChangedAtomMatcher(
const string& name, SyncStateChanged::State state) {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(3); // State field.
field_value_matcher->set_eq_int(state);
return atom_matcher;
}
AtomMatcher CreateSyncStartAtomMatcher() {
return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON);
}
AtomMatcher CreateSyncEndAtomMatcher() {
return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF);
}
AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
const string& name, ActivityForegroundStateChanged::Activity activity) {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(4); // Activity field.
field_value_matcher->set_eq_int(activity);
return atom_matcher;
}
AtomMatcher CreateMoveToBackgroundAtomMatcher() {
return CreateActivityForegroundStateChangedAtomMatcher(
"MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND);
}
AtomMatcher CreateMoveToForegroundAtomMatcher() {
return CreateActivityForegroundStateChangedAtomMatcher(
"MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND);
}
Predicate CreateScheduledJobPredicate() {
Predicate predicate;
predicate.set_id(StringToId("ScheduledJobRunningPredicate"));
predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart"));
predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish"));
return predicate;
}
Predicate CreateBatterySaverModePredicate() {
Predicate predicate;
predicate.set_id(StringToId("BatterySaverIsOn"));
predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart"));
predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop"));
return predicate;
}
Predicate CreateScreenIsOnPredicate() {
Predicate predicate;
predicate.set_id(StringToId("ScreenIsOn"));
predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn"));
predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff"));
return predicate;
}
Predicate CreateScreenIsOffPredicate() {
Predicate predicate;
predicate.set_id(1111123);
predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff"));
predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn"));
return predicate;
}
Predicate CreateHoldingWakelockPredicate() {
Predicate predicate;
predicate.set_id(StringToId("HoldingWakelock"));
predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock"));
predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock"));
return predicate;
}
Predicate CreateIsSyncingPredicate() {
Predicate predicate;
predicate.set_id(33333333333333);
predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart"));
predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd"));
return predicate;
}
Predicate CreateIsInBackgroundPredicate() {
Predicate predicate;
predicate.set_id(StringToId("IsInBackground"));
predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground"));
predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground"));
return predicate;
}
void addPredicateToPredicateCombination(const Predicate& predicate,
Predicate* combinationPredicate) {
combinationPredicate->mutable_combination()->add_predicate(predicate.id());
}
FieldMatcher CreateAttributionUidDimensions(const int atomId,
const std::vector<Position>& positions) {
FieldMatcher dimensions;
dimensions.set_field(atomId);
for (const auto position : positions) {
auto child = dimensions.add_child();
child->set_field(1);
child->set_position(position);
child->add_child()->set_field(1);
}
return dimensions;
}
FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
const std::vector<Position>& positions) {
FieldMatcher dimensions;
dimensions.set_field(atomId);
for (const auto position : positions) {
auto child = dimensions.add_child();
child->set_field(1);
child->set_position(position);
child->add_child()->set_field(1);
child->add_child()->set_field(2);
}
return dimensions;
}
FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) {
FieldMatcher dimensions;
dimensions.set_field(atomId);
for (const int field : fields) {
dimensions.add_child()->set_field(field);
}
return dimensions;
}
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
const android::view::DisplayStateEnum state, uint64_t timestampNs) {
auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs);
event->write(state);
event->init();
return event;
}
std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
int level, uint64_t timestampNs) {
auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
(event->write(level));
event->init();
return event;
}
std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& jobName,
const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
auto event = std::make_unique<LogEvent>(android::util::SCHEDULED_JOB_STATE_CHANGED, timestampNs);
event->write(attributions);
event->write(jobName);
event->write(state);
event->init();
return event;
}
std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
const std::vector<AttributionNodeInternal>& attributions,
const string& name, uint64_t timestampNs) {
return CreateScheduledJobStateChangedEvent(
attributions, name, ScheduledJobStateChanged::STARTED, timestampNs);
}
// Create log event when scheduled job finishes.
std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
const std::vector<AttributionNodeInternal>& attributions,
const string& name, uint64_t timestampNs) {
return CreateScheduledJobStateChangedEvent(
attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs);
}
std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
const WakelockStateChanged::State state, uint64_t timestampNs) {
auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs);
event->write(attributions);
event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
event->write(wakelockName);
event->write(state);
event->init();
return event;
}
std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
uint64_t timestampNs) {
return CreateWakelockStateChangedEvent(
attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
}
std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
uint64_t timestampNs) {
return CreateWakelockStateChangedEvent(
attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs);
}
std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) {
auto event = std::make_unique<LogEvent>(
android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
event->write(uid);
event->write("pkg_name");
event->write("class_name");
event->write(activity);
event->init();
return event;
}
std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
return CreateActivityForegroundStateChangedEvent(
uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs);
}
std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
return CreateActivityForegroundStateChangedEvent(
uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs);
}
std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& name,
const SyncStateChanged::State state, uint64_t timestampNs) {
auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
event->write(attributions);
event->write(name);
event->write(state);
event->init();
return event;
}
std::unique_ptr<LogEvent> CreateSyncStartEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& name,
uint64_t timestampNs) {
return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
}
std::unique_ptr<LogEvent> CreateSyncEndEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& name,
uint64_t timestampNs) {
return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
}
sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
const ConfigKey& key) {
sp<UidMap> uidMap = new UidMap();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
sp<StatsLogProcessor> processor = new StatsLogProcessor(
uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
processor->OnConfigUpdated(key, config);
return processor;
}
AttributionNodeInternal CreateAttribution(const int& uid, const string& tag) {
AttributionNodeInternal attribution;
attribution.set_uid(uid);
attribution.set_tag(tag);
return attribution;
}
void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
std::sort(events->begin(), events->end(),
[](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs();
});
}
int64_t StringToId(const string& str) {
return static_cast<int64_t>(std::hash<std::string>()(str));
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -0,0 +1,161 @@
// 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.
#pragma once
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "src/StatsLogProcessor.h"
#include "src/logd/LogEvent.h"
#include "statslog.h"
namespace android {
namespace os {
namespace statsd {
// Create AtomMatcher proto to simply match a specific atom type.
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
// Create AtomMatcher proto for scheduled job state changed.
AtomMatcher CreateScheduledJobStateChangedAtomMatcher();
// Create AtomMatcher proto for starting a scheduled job.
AtomMatcher CreateStartScheduledJobAtomMatcher();
// Create AtomMatcher proto for a scheduled job is done.
AtomMatcher CreateFinishScheduledJobAtomMatcher();
// Create AtomMatcher proto for screen brightness state changed.
AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
// Create AtomMatcher proto for acquiring wakelock.
AtomMatcher CreateAcquireWakelockAtomMatcher();
// Create AtomMatcher proto for releasing wakelock.
AtomMatcher CreateReleaseWakelockAtomMatcher() ;
// Create AtomMatcher proto for screen turned on.
AtomMatcher CreateScreenTurnedOnAtomMatcher();
// Create AtomMatcher proto for screen turned off.
AtomMatcher CreateScreenTurnedOffAtomMatcher();
// Create AtomMatcher proto for app sync turned on.
AtomMatcher CreateSyncStartAtomMatcher();
// Create AtomMatcher proto for app sync turned off.
AtomMatcher CreateSyncEndAtomMatcher();
// Create AtomMatcher proto for app sync moves to background.
AtomMatcher CreateMoveToBackgroundAtomMatcher();
// Create AtomMatcher proto for app sync moves to foreground.
AtomMatcher CreateMoveToForegroundAtomMatcher();
// Create Predicate proto for screen is off.
Predicate CreateScreenIsOffPredicate();
// Create Predicate proto for a running scheduled job.
Predicate CreateScheduledJobPredicate();
// Create Predicate proto for holding wakelock.
Predicate CreateHoldingWakelockPredicate();
// Create a Predicate proto for app syncing.
Predicate CreateIsSyncingPredicate();
// Create a Predicate proto for app is in background.
Predicate CreateIsInBackgroundPredicate();
// Add a predicate to the predicate combination.
void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
// Create dimensions from primitive fields.
FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields);
// Create dimensions by attribution uid and tag.
FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
const std::vector<Position>& positions);
// Create dimensions by attribution uid only.
FieldMatcher CreateAttributionUidDimensions(const int atomId,
const std::vector<Position>& positions);
// Create log event for screen state changed.
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
const android::view::DisplayStateEnum state, uint64_t timestampNs);
// Create log event for screen brightness state changed.
std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
int level, uint64_t timestampNs);
// Create log event when scheduled job starts.
std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
const std::vector<AttributionNodeInternal>& attributions,
const string& name, uint64_t timestampNs);
// Create log event when scheduled job finishes.
std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
const std::vector<AttributionNodeInternal>& attributions,
const string& name, uint64_t timestampNs);
// Create log event for app moving to background.
std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
// Create log event for app moving to foreground.
std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs);
// Create log event when the app sync starts.
std::unique_ptr<LogEvent> CreateSyncStartEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& name,
uint64_t timestampNs);
// Create log event when the app sync ends.
std::unique_ptr<LogEvent> CreateSyncEndEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& name,
uint64_t timestampNs);
// Create log event when the app sync ends.
std::unique_ptr<LogEvent> CreateAppCrashEvent(
const int uid, uint64_t timestampNs);
// Create log event for acquiring wakelock.
std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
uint64_t timestampNs);
// Create log event for releasing wakelock.
std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
uint64_t timestampNs);
// Create log event for releasing wakelock.
std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs);
// Helper function to create an AttributionNodeInternal proto.
AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
// Create a statsd log event processor upon the start time in seconds, config and key.
sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
const ConfigKey& key);
// Util function to sort the log events by timestamp.
void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
int64_t StringToId(const string& str);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -205,6 +205,29 @@ bool Value::operator<(const Value& that) const {
}
}
bool equalDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b) {
bool eq = dimension_a.size() == dimension_b.size();
for (size_t i = 0; eq && i < dimension_a.size(); ++i) {
if (dimension_b[i] != dimension_a[i]) {
eq = false;
}
}
return eq;
}
bool HasPositionANY(const FieldMatcher& matcher) {
if (matcher.has_position() && matcher.position() == Position::ANY) {
return true;
}
for (const auto& child : matcher.child()) {
if (HasPositionANY(child)) {
return true;
}
}
return false;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -336,11 +336,16 @@ struct FieldValue {
Value mValue;
};
bool HasPositionANY(const FieldMatcher& matcher);
bool isAttributionUidField(const FieldValue& value);
void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
bool isAttributionUidField(const Field& field, const Value& value);
bool equalDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -59,6 +59,33 @@ android::hash_t hashDimension(const HashableDimensionKey& value) {
return JenkinsHashWhiten(hash);
}
bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
HashableDimensionKey* output) {
for (size_t i = 0; i < matcherFields.size(); ++i) {
const auto& matcher = matcherFields[i];
bool found = false;
for (const auto& value : values) {
// TODO: potential optimization here to break early because all fields are naturally
// sorted.
if (value.mField.matches(matcher)) {
output->addValue(value);
output->mutableValue(i)->mField.setTag(value.mField.getTag());
output->mutableValue(i)->mField.setField(value.mField.getField() & matcher.mMask);
found = true;
break;
}
}
if (!found) {
VLOG("We can't find a dimension value for matcher (%d)%#x.", matcher.mMatcher.getTag(),
matcher.mMatcher.getField());
return false;
}
}
return true;
}
// Filter fields using the matchers and output the results as a HashableDimensionKey.
// Note: HashableDimensionKey is just a wrapper for vector<FieldValue>
bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
@@ -168,22 +195,21 @@ void filterGaugeValues(const std::vector<Matcher>& matcherFields,
void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
const Metric2Condition& links,
vector<HashableDimensionKey>* conditionDimension) {
HashableDimensionKey* conditionDimension) {
// Get the dimension first by using dimension from what.
filterValues(links.metricFields, eventValues, conditionDimension);
// Then replace the field with the dimension from condition.
for (auto& dim : *conditionDimension) {
size_t count = dim.getValues().size();
if (count != links.conditionFields.size()) {
// ALOGE("WTF condition link is bad");
return;
}
size_t count = conditionDimension->getValues().size();
if (count != links.conditionFields.size()) {
// ALOGE("WTF condition link is bad");
return;
}
for (size_t i = 0; i < count; i++) {
dim.mutableValue(i)->mField.setField(links.conditionFields[i].mMatcher.getField());
dim.mutableValue(i)->mField.setTag(links.conditionFields[i].mMatcher.getTag());
}
for (size_t i = 0; i < count; i++) {
conditionDimension->mutableValue(i)->mField.setField(
links.conditionFields[i].mMatcher.getField());
conditionDimension->mutableValue(i)->mField.setTag(
links.conditionFields[i].mMatcher.getTag());
}
}

View File

@@ -65,10 +65,6 @@ public:
std::string toString() const;
inline const char* c_str() const {
return toString().c_str();
}
bool operator==(const HashableDimensionKey& that) const;
bool operator<(const HashableDimensionKey& that) const;
@@ -104,6 +100,10 @@ class MetricDimensionKey {
return mDimensionKeyInCondition;
}
inline void setDimensionKeyInCondition(const HashableDimensionKey& key) {
mDimensionKeyInCondition = key;
}
bool hasDimensionKeyInCondition() const {
return mDimensionKeyInCondition.getValues().size() > 0;
}
@@ -112,9 +112,6 @@ class MetricDimensionKey {
bool operator<(const MetricDimensionKey& that) const;
inline const char* c_str() const {
return toString().c_str();
}
private:
HashableDimensionKey mDimensionKeyInWhat;
HashableDimensionKey mDimensionKeyInCondition;
@@ -134,6 +131,9 @@ android::hash_t hashDimension(const HashableDimensionKey& key);
*/
bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& values,
std::vector<HashableDimensionKey>* output);
// This function is used when there is at most one output dimension key. (no ANY matcher)
bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& values,
HashableDimensionKey* output);
/**
* Filter the values from FieldValues using the matchers.
@@ -146,7 +146,7 @@ void filterGaugeValues(const std::vector<Matcher>& matchers, const std::vector<F
void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
const Metric2Condition& links,
std::vector<HashableDimensionKey>* conditionDimension);
HashableDimensionKey* conditionDimension);
} // namespace statsd
} // namespace os

View File

@@ -123,10 +123,21 @@ private:
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
};
} // namespace statsd

View File

@@ -174,11 +174,6 @@ private:
*/
status_t cmd_print_stats(FILE* out, const Vector<String8>& args);
/**
* Print the event log.
*/
status_t cmd_print_stats_log(FILE* out, const Vector<String8>& args);
/**
* Print the event log.
*/

View File

@@ -91,6 +91,9 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
if (allConditionTrackers[childIndex]->isSliced()) {
setSliced(true);
mSlicedChildren.push_back(childIndex);
} else {
mUnSlicedChildren.push_back(childIndex);
}
mChildren.push_back(childIndex);
mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
@@ -107,13 +110,19 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf
void CombinationConditionTracker::isConditionMet(
const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
const std::vector<Matcher>& dimensionFields, vector<ConditionState>& conditionCache,
const std::vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
const bool isPartialLink,
vector<ConditionState>& conditionCache,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
// So far, this is fine as there is at most one child having sliced output.
for (const int childIndex : mChildren) {
if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
dimensionFields, conditionCache,
dimensionFields,
isSubOutputDimensionFields,
isPartialLink,
conditionCache,
dimensionsKeySet);
}
}
@@ -150,7 +159,11 @@ void CombinationConditionTracker::evaluateCondition(
nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
conditionChangedCache[mIndex] = nonSlicedChanged;
mUnSlicedPart = newCondition;
} else {
mUnSlicedPart = evaluateCombinationCondition(
mUnSlicedChildren, mLogicalOperation, nonSlicedConditionCache);
for (const int childIndex : mChildren) {
// If any of the sliced condition in children condition changes, the combination
// condition may be changed too.
@@ -168,13 +181,14 @@ void CombinationConditionTracker::evaluateCondition(
ConditionState CombinationConditionTracker::getMetConditionDimension(
const std::vector<sp<ConditionTracker>>& allConditions,
const std::vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
// So far, this is fine as there is at most one child having sliced output.
for (const int childIndex : mChildren) {
conditionCache[childIndex] = conditionCache[childIndex] |
allConditions[childIndex]->getMetConditionDimension(
allConditions, dimensionFields, dimensionsKeySet);
allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
}
evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
@@ -183,6 +197,18 @@ ConditionState CombinationConditionTracker::getMetConditionDimension(
return conditionCache[mIndex];
}
bool CombinationConditionTracker::equalOutputDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensions) const {
if (mSlicedChildren.size() != 1 ||
mSlicedChildren.front() >= (int)allConditions.size() ||
mLogicalOperation != LogicalOperation::AND) {
return false;
}
const sp<ConditionTracker>& slicedChild = allConditions.at(mSlicedChildren.front());
return slicedChild->equalOutputDimensions(allConditions, dimensions);
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -44,12 +44,15 @@ public:
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
const bool isPartialLink,
std::vector<ConditionState>& conditionCache,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
ConditionState getMetConditionDimension(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
// Only one child predicate can have dimension.
@@ -63,6 +66,7 @@ public:
}
return nullptr;
}
// Only one child predicate can have dimension.
const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
const std::vector<sp<ConditionTracker>>& allConditions) const override {
@@ -75,6 +79,26 @@ public:
return nullptr;
}
bool IsSimpleCondition() const override { return false; }
bool IsChangedDimensionTrackable() const override {
return mLogicalOperation == LogicalOperation::AND && mSlicedChildren.size() == 1;
}
bool equalOutputDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensions) const override;
void getTrueSlicedDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
std::set<HashableDimensionKey>* dimensions) const override {
if (mSlicedChildren.size() == 1) {
return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions(
allConditions, dimensions);
}
}
private:
LogicalOperation mLogicalOperation;
@@ -83,6 +107,10 @@ private:
// map the name to object. We don't want to store smart pointers to children, because it
// increases the risk of circular dependency and memory leak.
std::vector<int> mChildren;
std::vector<int> mSlicedChildren;
std::vector<int> mUnSlicedChildren;
};
} // namespace statsd

View File

@@ -84,18 +84,28 @@ public:
// condition.
// [allConditions]: all condition trackers. This is needed because the condition evaluation is
// done recursively
// [dimensionFields]: the needed dimension fields which should be all or subset of the condition
// tracker output dimension.
// [isSubOutputDimensionFields]: true if the needed dimension fields which is strictly subset of
// the condition tracker output dimension.
// [isPartialLink]: true if the link specified by 'conditionParameters' contains all the fields
// in the condition tracker output dimension.
// [conditionCache]: the cache holding the condition evaluation values.
// [dimensionsKeySet]: the dimensions where the sliced condition is true. For combination
// condition, it assumes that only one child predicate is sliced.
virtual void isConditionMet(
const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields, std::vector<ConditionState>& conditionCache,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
const bool isPartialLink,
std::vector<ConditionState>& conditionCache,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0;
virtual ConditionState getMetConditionDimension(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0;
// return the list of LogMatchingTracker index that this ConditionTracker uses.
@@ -107,7 +117,7 @@ public:
mSliced = mSliced | sliced;
}
bool isSliced() const {
inline bool isSliced() const {
return mSliced;
}
@@ -116,6 +126,26 @@ public:
virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
inline int64_t getConditionId() const {
return mConditionId;
}
virtual void getTrueSlicedDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
std::set<HashableDimensionKey>* dimensions) const = 0;
virtual bool IsChangedDimensionTrackable() const = 0;
virtual bool IsSimpleCondition() const = 0;
virtual bool equalOutputDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensions) const = 0;
inline ConditionState getUnSlicedPartConditionState() const {
return mUnSlicedPart;
}
protected:
const int64_t mConditionId;
@@ -131,6 +161,7 @@ protected:
ConditionState mNonSlicedConditionState;
bool mSliced;
ConditionState mUnSlicedPart;
};
} // namespace statsd

View File

@@ -26,19 +26,24 @@ using std::vector;
ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
const bool isPartialLink,
std::unordered_set<HashableDimensionKey>* dimensionKeySet) {
vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
mAllConditions[index]->isConditionMet(
parameters, mAllConditions, dimensionFields, cache, *dimensionKeySet);
parameters, mAllConditions, dimensionFields, isSubOutputDimensionFields, isPartialLink,
cache, *dimensionKeySet);
return cache[index];
}
ConditionState ConditionWizard::getMetConditionDimension(
const int index, const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const {
return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields,
*dimensionsKeySet);
isSubOutputDimensionFields,
*dimensionsKeySet);
}
const set<HashableDimensionKey>* ConditionWizard::getChangedToTrueDimensions(
@@ -51,6 +56,30 @@ const set<HashableDimensionKey>* ConditionWizard::getChangedToFalseDimensions(
return mAllConditions[index]->getChangedToFalseDimensions(mAllConditions);
}
bool ConditionWizard::IsChangedDimensionTrackable(const int index) {
if (index >= 0 && index < (int)mAllConditions.size()) {
return mAllConditions[index]->IsChangedDimensionTrackable();
} else {
return false;
}
}
bool ConditionWizard::IsSimpleCondition(const int index) {
if (index >= 0 && index < (int)mAllConditions.size()) {
return mAllConditions[index]->IsSimpleCondition();
} else {
return false;
}
}
bool ConditionWizard::equalOutputDimensions(const int index, const vector<Matcher>& dimensions) {
if (index >= 0 && index < (int)mAllConditions.size()) {
return mAllConditions[index]->equalOutputDimensions(mAllConditions, dimensions);
} else {
return false;
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -41,15 +41,30 @@ public:
// the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
const bool isPartialLink,
std::unordered_set<HashableDimensionKey>* dimensionKeySet);
virtual ConditionState getMetConditionDimension(
const int index, const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const;
virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(const int index) const;
virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
const int index) const;
bool equalOutputDimensions(const int index, const vector<Matcher>& dimensions);
bool IsChangedDimensionTrackable(const int index);
bool IsSimpleCondition(const int index);
ConditionState getUnSlicedPartConditionState(const int index) {
return mAllConditions[index]->getUnSlicedPartConditionState();
}
void getTrueSlicedDimensions(const int index,
std::set<HashableDimensionKey>* trueDimensions) const {
return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions);
}
private:
std::vector<sp<ConditionTracker>> mAllConditions;

View File

@@ -34,7 +34,7 @@ SimpleConditionTracker::SimpleConditionTracker(
const ConfigKey& key, const int64_t& id, const int index,
const SimplePredicate& simplePredicate,
const unordered_map<int64_t, int>& trackerNameIndexMap)
: ConditionTracker(id, index), mConfigKey(key) {
: ConditionTracker(id, index), mConfigKey(key), mContainANYPositionInInternalDimensions(false) {
VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId);
mCountNesting = simplePredicate.count_nesting();
@@ -80,6 +80,7 @@ SimpleConditionTracker::SimpleConditionTracker(
mSliced = true;
mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
}
mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions());
}
if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
@@ -90,6 +91,10 @@ SimpleConditionTracker::SimpleConditionTracker(
mNonSlicedConditionState = mInitialValue;
if (!mSliced) {
mUnSlicedPart = mInitialValue;
}
mInitialized = true;
}
@@ -109,7 +114,7 @@ bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
void SimpleConditionTracker::dumpState() {
VLOG("%lld DUMP:", (long long)mConditionId);
for (const auto& pair : mSlicedConditionState) {
VLOG("\t%s : %d", pair.first.c_str(), pair.second);
VLOG("\t%s : %d", pair.first.toString().c_str(), pair.second);
}
VLOG("Changed to true keys: \n");
@@ -140,6 +145,9 @@ void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditio
mInitialValue = ConditionState::kFalse;
mSlicedConditionState.clear();
conditionCache[mIndex] = ConditionState::kFalse;
if (!mSliced) {
mUnSlicedPart = ConditionState::kFalse;
}
}
bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -154,7 +162,7 @@ bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("Predicate %lld dropping data for dimension key %s",
(long long)mConditionId, newKey.c_str());
(long long)mConditionId, newKey.toString().c_str());
return true;
}
}
@@ -177,13 +185,13 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou
// We get a new output key.
newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
if (matchStart && mInitialValue != ConditionState::kTrue) {
mSlicedConditionState.insert(std::make_pair(outputKey, 1));
mSlicedConditionState[outputKey] = 1;
changed = true;
mLastChangedToTrueDimensions.insert(outputKey);
} else if (mInitialValue != ConditionState::kFalse) {
// it's a stop and we don't have history about it.
// If the default condition is not false, it means this stop is valuable to us.
mSlicedConditionState.insert(std::make_pair(outputKey, 0));
mSlicedConditionState[outputKey] = 0;
mLastChangedToFalseDimensions.insert(outputKey);
changed = true;
}
@@ -226,7 +234,7 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou
// if default condition is false, it means we don't need to keep the false values.
if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
mSlicedConditionState.erase(outputIt);
VLOG("erase key %s", outputKey.c_str());
VLOG("erase key %s", outputKey.toString().c_str());
}
}
}
@@ -238,6 +246,7 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou
(*conditionChangedCache) = changed;
(*conditionCache) = newCondition;
VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId,
conditionChangedCache[mIndex] == true);
}
@@ -294,6 +303,7 @@ void SimpleConditionTracker::evaluateCondition(
conditionCache[mIndex] =
itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
}
mUnSlicedPart = conditionCache[mIndex];
}
return;
@@ -305,6 +315,17 @@ void SimpleConditionTracker::evaluateCondition(
if (mOutputDimensions.size() == 0) {
handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState,
&overallChanged);
} else if (!mContainANYPositionInInternalDimensions) {
HashableDimensionKey outputValue;
filterValues(mOutputDimensions, event.getValues(), &outputValue);
// If this event has multiple nodes in the attribution chain, this log event probably will
// generate multiple dimensions. If so, we will find if the condition changes for any
// dimension and ask the corresponding metric producer to verify whether the actual sliced
// condition has changed or not.
// A high level assumption is that a predicate is either sliced or unsliced. We will never
// have both sliced and unsliced version of a predicate.
handleConditionEvent(outputValue, matchedState == 1, &overallState, &overallChanged);
} else {
std::vector<HashableDimensionKey> outputValues;
filterValues(mOutputDimensions, event.getValues(), &outputValues);
@@ -328,11 +349,17 @@ void SimpleConditionTracker::evaluateCondition(
}
conditionCache[mIndex] = overallState;
conditionChangedCache[mIndex] = overallChanged;
if (!mSliced) {
mUnSlicedPart = overallState;
}
}
void SimpleConditionTracker::isConditionMet(
const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields, vector<ConditionState>& conditionCache,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
const bool isPartialLink,
vector<ConditionState>& conditionCache,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
@@ -347,7 +374,7 @@ void SimpleConditionTracker::isConditionMet(
ConditionState conditionState = ConditionState::kNotEvaluated;
if (dimensionFields.size() > 0 && dimensionFields[0].mMatcher.getTag() == mDimensionTag) {
conditionState = conditionState | getMetConditionDimension(
allConditions, dimensionFields, dimensionsKeySet);
allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
} else {
conditionState = conditionState | mInitialValue;
if (!mSliced) {
@@ -362,42 +389,48 @@ void SimpleConditionTracker::isConditionMet(
conditionCache[mIndex] = conditionState;
return;
}
std::vector<HashableDimensionKey> defaultKeys = { DEFAULT_DIMENSION_KEY };
const std::vector<HashableDimensionKey> &keys =
(pair == conditionParameters.end()) ? defaultKeys : pair->second;
ConditionState conditionState = ConditionState::kNotEvaluated;
for (size_t i = 0; i < keys.size(); ++i) {
const HashableDimensionKey& key = keys[i];
const HashableDimensionKey& key = pair->second;
if (isPartialLink) {
// For unseen key, check whether the require dimensions are subset of sliced condition
// output.
conditionState = conditionState | mInitialValue;
for (const auto& slice : mSlicedConditionState) {
ConditionState sliceState =
slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
if (slice.first.contains(key)) {
conditionState = conditionState | sliceState;
if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
if (isSubOutputDimensionFields) {
HashableDimensionKey dimensionKey;
filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
dimensionsKeySet.insert(dimensionKey);
} else {
dimensionsKeySet.insert(slice.first);
}
}
}
}
} else {
auto startedCountIt = mSlicedConditionState.find(key);
conditionState = conditionState | mInitialValue;
if (startedCountIt != mSlicedConditionState.end()) {
ConditionState sliceState =
startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
conditionState = conditionState | sliceState;
if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
vector<HashableDimensionKey> dimensionKeys;
filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKeys);
dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end());
}
} else {
// For unseen key, check whether the require dimensions are subset of sliced condition
// output.
conditionState = conditionState | mInitialValue;
for (const auto& slice : mSlicedConditionState) {
ConditionState sliceState =
slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
if (slice.first.contains(key)) {
conditionState = conditionState | sliceState;
if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
vector<HashableDimensionKey> dimensionKeys;
filterValues(dimensionFields, slice.first.getValues(), &dimensionKeys);
dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end());
}
}
if (isSubOutputDimensionFields) {
HashableDimensionKey dimensionKey;
filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKey);
dimensionsKeySet.insert(dimensionKey);
} else {
dimensionsKeySet.insert(startedCountIt->first);
}
}
}
}
conditionCache[mIndex] = conditionState;
VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
}
@@ -405,6 +438,7 @@ void SimpleConditionTracker::isConditionMet(
ConditionState SimpleConditionTracker::getMetConditionDimension(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
ConditionState conditionState = mInitialValue;
if (dimensionFields.size() == 0 || mOutputDimensions.size() == 0 ||
@@ -424,10 +458,13 @@ ConditionState SimpleConditionTracker::getMetConditionDimension(
conditionState = conditionState | sliceState;
if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
vector<HashableDimensionKey> dimensionKeys;
filterValues(dimensionFields, slice.first.getValues(), &dimensionKeys);
dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end());
if (isSubOutputDimensionFields) {
HashableDimensionKey dimensionKey;
filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
dimensionsKeySet.insert(dimensionKey);
} else {
dimensionsKeySet.insert(slice.first);
}
}
}
return conditionState;

View File

@@ -49,12 +49,15 @@ public:
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
const bool isPartialLink,
std::vector<ConditionState>& conditionCache,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
ConditionState getMetConditionDimension(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
@@ -65,6 +68,7 @@ public:
return nullptr;
}
}
virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
const std::vector<sp<ConditionTracker>>& allConditions) const {
if (mSliced) {
@@ -74,6 +78,26 @@ public:
}
}
void getTrueSlicedDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
std::set<HashableDimensionKey>* dimensions) const override {
for (const auto& itr : mSlicedConditionState) {
if (itr.second > 0) {
dimensions->insert(itr.first);
}
}
}
bool IsChangedDimensionTrackable() const override { return true; }
bool IsSimpleCondition() const override { return true; }
bool equalOutputDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensions) const override {
return equalDimensions(mOutputDimensions, dimensions);
}
private:
const ConfigKey mConfigKey;
// The index of the LogEventMatcher which defines the start.
@@ -92,6 +116,8 @@ private:
std::vector<Matcher> mOutputDimensions;
bool mContainANYPositionInInternalDimensions;
std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
std::set<HashableDimensionKey> mLastChangedToFalseDimensions;

View File

@@ -107,7 +107,7 @@ bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) {
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("Predicate %lld dropping data for dimension key %s",
(long long)mConditionId, newKey.c_str());
(long long)mConditionId, newKey.toString().c_str());
return true;
}
}
@@ -181,7 +181,10 @@ void StateTracker::evaluateCondition(const LogEvent& event,
void StateTracker::isConditionMet(
const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields, vector<ConditionState>& conditionCache,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
const bool isPartialLink,
vector<ConditionState>& conditionCache,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
@@ -203,20 +206,19 @@ void StateTracker::isConditionMet(
return;
}
const auto& primaryKeys = pair->second;
const auto& primaryKey = pair->second;
conditionCache[mIndex] = mInitialValue;
for (const auto& primaryKey : primaryKeys) {
auto it = mSlicedState.find(primaryKey);
if (it != mSlicedState.end()) {
conditionCache[mIndex] = ConditionState::kTrue;
dimensionsKeySet.insert(it->second);
}
auto it = mSlicedState.find(primaryKey);
if (it != mSlicedState.end()) {
conditionCache[mIndex] = ConditionState::kTrue;
dimensionsKeySet.insert(it->second);
}
}
ConditionState StateTracker::getMetConditionDimension(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
if (mSlicedState.size() > 0) {
for (const auto& state : mSlicedState) {

View File

@@ -56,6 +56,8 @@ public:
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
const bool isPartialLink,
std::vector<ConditionState>& conditionCache,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
@@ -67,6 +69,7 @@ public:
ConditionState getMetConditionDimension(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensionFields,
const bool isSubOutputDimensionFields,
std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
@@ -79,6 +82,24 @@ public:
return &mLastChangedToFalseDimensions;
}
bool IsChangedDimensionTrackable() const override { return true; }
bool IsSimpleCondition() const override { return true; }
bool equalOutputDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensions) const override {
return equalDimensions(mOutputDimensions, dimensions);
}
void getTrueSlicedDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
std::set<HashableDimensionKey>* dimensions) const override {
for (const auto& itr : mSlicedState) {
dimensions->insert(itr.second);
}
}
private:
const ConfigKey mConfigKey;

View File

@@ -71,6 +71,7 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric
if (metric.has_dimensions_in_what()) {
translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
if (metric.has_dimensions_in_condition()) {
@@ -113,7 +114,7 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
for (const auto& counter : mPastBuckets) {
const MetricDimensionKey& dimensionKey = counter.first;
VLOG(" dimension key %s", dimensionKey.c_str());
VLOG(" dimension key %s", dimensionKey.toString().c_str());
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
@@ -176,7 +177,7 @@ bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("CountMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.c_str());
(long long)mMetricId, newKey.toString().c_str());
return true;
}
}
@@ -218,7 +219,7 @@ void CountMetricProducer::onMatchedLogEventInternalLocked(
countWholeBucket);
}
VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.c_str(),
VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.toString().c_str(),
(long long)(*mCurrentSlicedCounter)[eventKey]);
}
@@ -253,7 +254,8 @@ void CountMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs)
info.mCount = counter.second;
auto& bucketList = mPastBuckets[counter.first];
bucketList.push_back(info);
VLOG("metric %lld, dump key value: %s -> %lld", (long long)mMetricId, counter.first.c_str(),
VLOG("metric %lld, dump key value: %s -> %lld", (long long)mMetricId,
counter.first.toString().c_str(),
(long long)counter.second);
}

View File

@@ -67,7 +67,8 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat
mStartIndex(startIndex),
mStopIndex(stopIndex),
mStopAllIndex(stopAllIndex),
mNested(nesting) {
mNested(nesting),
mContainANYPositionInInternalDimensions(false) {
// TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
// them in the base class, because the proto generated CountMetric, and DurationMetric are
// not related. Maybe we should add a template in the future??
@@ -80,10 +81,12 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat
if (metric.has_dimensions_in_what()) {
translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
if (internalDimensions.has_field()) {
translateFieldMatcher(internalDimensions, &mInternalDimensions);
mContainANYPositionInInternalDimensions = HasPositionANY(internalDimensions);
}
if (metric.has_dimensions_in_condition()) {
@@ -100,19 +103,18 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat
}
}
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
mUnSlicedPartCondition = ConditionState::kUnknown;
if (mDimensionsInWhat.size() == mInternalDimensions.size()) {
bool mUseWhatDimensionAsInternalDimension = true;
for (size_t i = 0; mUseWhatDimensionAsInternalDimension &&
i < mDimensionsInWhat.size(); ++i) {
if (mDimensionsInWhat[i] != mInternalDimensions[i]) {
mUseWhatDimensionAsInternalDimension = false;
}
mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
if (mWizard != nullptr && mConditionTrackerIndex >= 0) {
mSameConditionDimensionsInTracker =
mWizard->equalOutputDimensions(mConditionTrackerIndex, mDimensionsInCondition);
if (mMetric2ConditionLinks.size() == 1) {
mHasLinksToAllConditionDimensionsInTracker =
mWizard->equalOutputDimensions(mConditionTrackerIndex,
mMetric2ConditionLinks.begin()->conditionFields);
}
} else {
mUseWhatDimensionAsInternalDimension = false;
}
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -139,12 +141,171 @@ unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
return make_unique<OringDurationTracker>(
mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
mStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
mStartTimeNs, mBucketSizeNs, mConditionSliced,
mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
case DurationMetric_AggregationType_MAX_SPARSE:
return make_unique<MaxDurationTracker>(
mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
mStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
mStartTimeNs, mBucketSizeNs, mConditionSliced,
mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
}
}
// SlicedConditionChange optimization case 1:
// 1. If combination condition, logical operation is AND, only one sliced child predicate.
// 2. No condition in dimension
// 3. The links covers all dimension fields in the sliced child condition predicate.
void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(const uint64_t eventTime) {
if (mMetric2ConditionLinks.size() != 1 ||
!mHasLinksToAllConditionDimensionsInTracker ||
!mDimensionsInCondition.empty()) {
return;
}
bool currentUnSlicedPartCondition = true;
if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
ConditionState unslicedPartState =
mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
// When the unsliced part is still false, return directly.
if (mUnSlicedPartCondition == ConditionState::kFalse &&
unslicedPartState == ConditionState::kFalse) {
return;
}
mUnSlicedPartCondition = unslicedPartState;
currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
}
auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
// The condition change is from the unsliced predicates.
// We need to find out the true dimensions from the sliced predicate and flip their condition
// state based on the new unsliced condition state.
if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
(dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
std::set<HashableDimensionKey> trueConditionDimensions;
mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
HashableDimensionKey linkedConditionDimensionKey;
getDimensionForCondition(whatIt.first.getValues(),
mMetric2ConditionLinks[0],
&linkedConditionDimensionKey);
if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
trueConditionDimensions.end()) {
for (auto& condIt : whatIt.second) {
condIt.second->onConditionChanged(
currentUnSlicedPartCondition, eventTime);
}
}
}
} else {
// Handle the condition change from the sliced predicate.
if (currentUnSlicedPartCondition) {
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
HashableDimensionKey linkedConditionDimensionKey;
getDimensionForCondition(whatIt.first.getValues(),
mMetric2ConditionLinks[0],
&linkedConditionDimensionKey);
if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) !=
dimensionsChangedToTrue->end()) {
for (auto& condIt : whatIt.second) {
condIt.second->onConditionChanged(true, eventTime);
}
}
if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
dimensionsChangedToFalse->end()) {
for (auto& condIt : whatIt.second) {
condIt.second->onConditionChanged(false, eventTime);
}
}
}
}
}
}
// SlicedConditionChange optimization case 2:
// 1. If combination condition, logical operation is AND, only one sliced child predicate.
// 2. Has dimensions_in_condition and it equals to the output dimensions of the sliced predicate.
void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(const uint64_t eventTime) {
if (mMetric2ConditionLinks.size() > 1 || !mSameConditionDimensionsInTracker) {
return;
}
auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
bool currentUnSlicedPartCondition = true;
if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
ConditionState unslicedPartState =
mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
// When the unsliced part is still false, return directly.
if (mUnSlicedPartCondition == ConditionState::kFalse &&
unslicedPartState == ConditionState::kFalse) {
return;
}
mUnSlicedPartCondition = unslicedPartState;
currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
}
const std::set<HashableDimensionKey>* trueDimensionsToProcess = nullptr;
const std::set<HashableDimensionKey>* falseDimensionsToProcess = nullptr;
std::set<HashableDimensionKey> currentTrueConditionDimensions;
if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
(dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &currentTrueConditionDimensions);
trueDimensionsToProcess = &currentTrueConditionDimensions;
} else if (currentUnSlicedPartCondition) {
// Handles the condition change from the sliced predicate. If the unsliced condition state
// is not true, not need to do anything.
trueDimensionsToProcess = dimensionsChangedToTrue;
falseDimensionsToProcess = dimensionsChangedToFalse;
}
if (trueDimensionsToProcess == nullptr && falseDimensionsToProcess == nullptr) {
return;
}
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
if (falseDimensionsToProcess != nullptr) {
for (const auto& changedDim : *falseDimensionsToProcess) {
auto condIt = whatIt.second.find(changedDim);
if (condIt != whatIt.second.end()) {
condIt->second->onConditionChanged(false, eventTime);
}
}
}
if (trueDimensionsToProcess != nullptr) {
HashableDimensionKey linkedConditionDimensionKey;
if (!trueDimensionsToProcess->empty() && mMetric2ConditionLinks.size() == 1) {
getDimensionForCondition(whatIt.first.getValues(),
mMetric2ConditionLinks[0],
&linkedConditionDimensionKey);
}
for (auto& trueDim : *trueDimensionsToProcess) {
auto condIt = whatIt.second.find(trueDim);
if (condIt != whatIt.second.end()) {
condIt->second->onConditionChanged(
currentUnSlicedPartCondition, eventTime);
} else {
if (mMetric2ConditionLinks.size() == 0 ||
trueDim.contains(linkedConditionDimensionKey)) {
if (!whatIt.second.empty()) {
unique_ptr<DurationTracker> newTracker =
whatIt.second.begin()->second->clone(eventTime);
if (newTracker != nullptr) {
newTracker->setEventKey(
MetricDimensionKey(whatIt.first, trueDim));
newTracker->onConditionChanged(true, eventTime);
whatIt.second[trueDim] = std::move(newTracker);
}
}
}
}
}
}
}
}
@@ -152,6 +313,23 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eve
VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
flushIfNeededLocked(eventTime);
if (!mConditionSliced) {
return;
}
bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker &&
mDimensionsInCondition.empty()) {
onSlicedConditionMayChangeLocked_opt1(eventTime);
return;
}
if (changeDimTrackable && mSameConditionDimensionsInTracker &&
mMetric2ConditionLinks.size() <= 1) {
onSlicedConditionMayChangeLocked_opt2(eventTime);
return;
}
// Now for each of the on-going event, check if the condition has changed for them.
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
for (auto& pair : whatIt.second) {
@@ -166,6 +344,7 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eve
if (mMetric2ConditionLinks.empty()) {
std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition,
!mSameConditionDimensionsInTracker,
&conditionDimensionsKeySet);
for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
for (const auto& pair : whatIt.second) {
@@ -177,9 +356,12 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eve
if (!whatIt.second.empty()) {
unique_ptr<DurationTracker> newTracker =
whatIt.second.begin()->second->clone(eventTime);
newTracker->setEventKey(MetricDimensionKey(whatIt.first, conditionDimension));
newTracker->onSlicedConditionMayChange(eventTime);
whatIt.second[conditionDimension] = std::move(newTracker);
if (newTracker != nullptr) {
newTracker->setEventKey(MetricDimensionKey(
whatIt.first, conditionDimension));
newTracker->onSlicedConditionMayChange(eventTime);
whatIt.second[conditionDimension] = std::move(newTracker);
}
}
}
}
@@ -192,15 +374,20 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eve
}
std::unordered_set<HashableDimensionKey> conditionDimensionsKeys;
mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
!mSameConditionDimensionsInTracker,
!mHasLinksToAllConditionDimensionsInTracker,
&conditionDimensionsKeys);
for (const auto& conditionDimension : conditionDimensionsKeys) {
if (!whatIt.second.empty() &&
whatIt.second.find(conditionDimension) == whatIt.second.end()) {
auto newTracker = whatIt.second.begin()->second->clone(eventTime);
newTracker->setEventKey(MetricDimensionKey(whatIt.first, conditionDimension));
newTracker->onSlicedConditionMayChange(eventTime);
whatIt.second[conditionDimension] = std::move(newTracker);
if (newTracker != nullptr) {
newTracker->setEventKey(
MetricDimensionKey(whatIt.first, conditionDimension));
newTracker->onSlicedConditionMayChange(eventTime);
whatIt.second[conditionDimension] = std::move(newTracker);
}
}
}
}
@@ -241,7 +428,7 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
for (const auto& pair : mPastBuckets) {
const MetricDimensionKey& dimensionKey = pair.first;
VLOG(" dimension key %s", dimensionKey.c_str());
VLOG(" dimension key %s", dimensionKey.toString().c_str());
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
@@ -291,7 +478,8 @@ void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) {
whatIt != mCurrentSlicedDurationTrackerMap.end();) {
for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) {
VLOG("erase bucket for key %s %s", whatIt->first.c_str(), it->first.c_str());
VLOG("erase bucket for key %s %s",
whatIt->first.toString().c_str(), it->first.toString().c_str());
it = whatIt->second.erase(it);
} else {
++it;
@@ -314,7 +502,8 @@ void DurationMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeN
whatIt != mCurrentSlicedDurationTrackerMap.end();) {
for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
VLOG("erase bucket for key %s %s", whatIt->first.c_str(), it->first.c_str());
VLOG("erase bucket for key %s %s", whatIt->first.toString().c_str(),
it->first.toString().c_str());
it = whatIt->second.erase(it);
} else {
++it;
@@ -338,7 +527,8 @@ void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (verbose) {
for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
for (const auto& slice : whatIt.second) {
fprintf(out, "\t%s\t%s\n", whatIt.first.c_str(), slice.first.c_str());
fprintf(out, "\t(what)%s\t(condition)%s\n", whatIt.first.toString().c_str(),
slice.first.toString().c_str());
slice.second->dumpStates(out, verbose);
}
}
@@ -353,7 +543,7 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("DurationMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.c_str());
(long long)mMetricId, newKey.toString().c_str());
return true;
}
}
@@ -388,14 +578,21 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey
return;
}
std::vector<HashableDimensionKey> values;
filterValues(mInternalDimensions, event.getValues(), &values);
if (values.empty()) {
if (mInternalDimensions.empty()) {
it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
event.GetElapsedTimestampNs(), conditionKeys);
} else {
for (const auto& value : values) {
it->second->noteStart(value, condition, event.GetElapsedTimestampNs(), conditionKeys);
if (mContainANYPositionInInternalDimensions) {
std::vector<HashableDimensionKey> dimensionKeys;
filterValues(mInternalDimensions, event.getValues(), &dimensionKeys);
for (const auto& key : dimensionKeys) {
it->second->noteStart(key, condition, event.GetElapsedTimestampNs(), conditionKeys);
}
} else {
HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
it->second->noteStart(
dimensionKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
}
}
@@ -408,8 +605,113 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked(
ALOGW("Not used in duration tracker.");
}
void DurationMetricProducer::onMatchedLogEventLocked_simple(const size_t matcherIndex,
const LogEvent& event) {
uint64_t eventTimeNs = event.GetElapsedTimestampNs();
if (eventTimeNs < mStartTimeNs) {
return;
}
flushIfNeededLocked(event.GetElapsedTimestampNs());
// Handles Stopall events.
if (matcherIndex == mStopAllIndex) {
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
for (auto& pair : whatIt.second) {
pair.second->noteStopAll(event.GetElapsedTimestampNs());
}
}
return;
}
HashableDimensionKey dimensionInWhat;
if (!mDimensionsInWhat.empty()) {
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
} else {
dimensionInWhat = DEFAULT_DIMENSION_KEY;
}
// Handles Stop events.
if (matcherIndex == mStopIndex) {
if (mUseWhatDimensionAsInternalDimension) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
for (const auto& condIt : whatIt->second) {
condIt.second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
}
}
return;
}
HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
if (!mInternalDimensions.empty()) {
filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey);
}
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
for (const auto& condIt : whatIt->second) {
condIt.second->noteStop(
internalDimensionKey, event.GetElapsedTimestampNs(), false);
}
}
return;
}
bool condition;
ConditionKey conditionKey;
std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
if (mConditionSliced) {
for (const auto& link : mMetric2ConditionLinks) {
getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
}
auto conditionState =
mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
!mSameConditionDimensionsInTracker,
!mHasLinksToAllConditionDimensionsInTracker,
&dimensionKeysInCondition);
condition = (conditionState == ConditionState::kTrue);
if (mDimensionsInCondition.empty() && condition) {
dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
}
} else {
condition = mCondition;
if (condition) {
dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
}
}
if (dimensionKeysInCondition.empty()) {
handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY),
conditionKey, condition, event);
} else {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
// If the what dimension is already there, we should update all the trackers even
// the condition is false.
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
for (const auto& condIt : whatIt->second) {
const bool cond = dimensionKeysInCondition.find(condIt.first) !=
dimensionKeysInCondition.end();
handleStartEvent(MetricDimensionKey(dimensionInWhat, condIt.first),
conditionKey, cond, event);
dimensionKeysInCondition.erase(condIt.first);
}
}
for (const auto& conditionDimension : dimensionKeysInCondition) {
handleStartEvent(MetricDimensionKey(dimensionInWhat, conditionDimension), conditionKey,
condition, event);
}
}
}
void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
const LogEvent& event) {
if (!mContainANYPositionInDimensionsInWhat) {
onMatchedLogEventLocked_simple(matcherIndex, event);
return;
}
uint64_t eventTimeNs = event.GetElapsedTimestampNs();
if (eventTimeNs < mStartTimeNs) {
return;
@@ -448,19 +750,17 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
return;
}
std::vector<HashableDimensionKey> internalDimensionKeys;
filterValues(mInternalDimensions, event.getValues(), &internalDimensionKeys);
if (internalDimensionKeys.empty()) {
internalDimensionKeys.push_back(DEFAULT_DIMENSION_KEY);
HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
if (!mInternalDimensions.empty()) {
filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey);
}
for (const HashableDimensionKey& whatDimension : dimensionInWhatValues) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
for (const auto& condIt : whatIt->second) {
for (const auto& internalDimensionKey : internalDimensionKeys) {
condIt.second->noteStop(
internalDimensionKey, event.GetElapsedTimestampNs(), false);
}
condIt.second->noteStop(
internalDimensionKey, event.GetElapsedTimestampNs(), false);
}
}
}
@@ -477,6 +777,8 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
auto conditionState =
mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
!mSameConditionDimensionsInTracker,
!mHasLinksToAllConditionDimensionsInTracker,
&dimensionKeysInCondition);
condition = (conditionState == ConditionState::kTrue);
if (mDimensionsInCondition.empty() && condition) {
@@ -490,32 +792,30 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
}
for (const auto& whatDimension : dimensionInWhatValues) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension);
// If the what dimension is already there, we should update all the trackers even
// the condition is false.
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
for (const auto& condIt : whatIt->second) {
const bool cond = dimensionKeysInCondition.find(condIt.first) !=
dimensionKeysInCondition.end();
handleStartEvent(MetricDimensionKey(whatDimension, condIt.first),
conditionKey, cond, event);
}
if (dimensionKeysInCondition.empty()) {
handleStartEvent(MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY),
conditionKey, condition, event);
} else {
// If it is a new what dimension key, we need to handle the start events for all current
// condition dimensions.
auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension);
// If the what dimension is already there, we should update all the trackers even
// the condition is false.
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
for (const auto& condIt : whatIt->second) {
const bool cond = dimensionKeysInCondition.find(condIt.first) !=
dimensionKeysInCondition.end();
handleStartEvent(MetricDimensionKey(whatDimension, condIt.first),
conditionKey, cond, event);
dimensionKeysInCondition.erase(condIt.first);
}
}
for (const auto& conditionDimension : dimensionKeysInCondition) {
handleStartEvent(MetricDimensionKey(whatDimension, conditionDimension),
conditionKey, condition, event);
}
}
if (dimensionKeysInCondition.empty()) {
handleStartEvent(MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY),
conditionKey, condition, event);
}
}
}
size_t DurationMetricProducer::byteSizeLocked() const {
size_t totalSize = 0;
for (const auto& pair : mPastBuckets) {

View File

@@ -51,6 +51,9 @@ public:
protected:
void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
void onMatchedLogEventLocked_simple(const size_t matcherIndex, const LogEvent& event);
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition,
@@ -69,6 +72,9 @@ private:
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(const uint64_t eventTime) override;
void onSlicedConditionMayChangeLocked_opt1(const uint64_t eventTime);
void onSlicedConditionMayChangeLocked_opt2(const uint64_t eventTime);
// Internal function to calculate the current used bytes.
size_t byteSizeLocked() const override;
@@ -98,9 +104,14 @@ private:
// The dimension from the atom predicate. e.g., uid, wakelock name.
vector<Matcher> mInternalDimensions;
bool mContainANYPositionInInternalDimensions;
// This boolean is true iff When mInternalDimensions == mDimensionsInWhat
bool mUseWhatDimensionAsInternalDimension;
// Caches the current unsliced part condition.
ConditionState mUnSlicedPartCondition;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;

View File

@@ -83,6 +83,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric
// TODO: use UidMap if uid->pkg_name is required
if (metric.has_dimensions_in_what()) {
translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
if (metric.has_dimensions_in_condition()) {
@@ -140,7 +141,7 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
for (const auto& pair : mPastBuckets) {
const MetricDimensionKey& dimensionKey = pair.first;
VLOG(" dimension key %s", dimensionKey.c_str());
VLOG(" dimension key %s", dimensionKey.toString().c_str());
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
@@ -283,7 +284,7 @@ bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("GaugeMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.c_str());
(long long)mMetricId, newKey.toString().c_str());
return true;
}
}
@@ -398,7 +399,8 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs)
info.mGaugeAtoms = slice.second;
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
VLOG("gauge metric %lld, dump key value: %s", (long long)mMetricId, slice.first.c_str());
VLOG("gauge metric %lld, dump key value: %s", (long long)mMetricId,
slice.first.toString().c_str());
}
// If we have anomaly trackers, we need to update the partial bucket values.

View File

@@ -39,9 +39,10 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo
for (const auto& link : mMetric2ConditionLinks) {
getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
}
auto conditionState =
mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
!mSameConditionDimensionsInTracker,
!mHasLinksToAllConditionDimensionsInTracker,
&dimensionKeysInCondition);
condition = (conditionState == ConditionState::kTrue);
} else {
@@ -52,25 +53,41 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo
dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
}
vector<HashableDimensionKey> dimensionInWhatValues;
if (!mDimensionsInWhat.empty()) {
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
} else {
dimensionInWhatValues.push_back(DEFAULT_DIMENSION_KEY);
}
if (mContainANYPositionInDimensionsInWhat) {
vector<HashableDimensionKey> dimensionInWhatValues;
if (!mDimensionsInWhat.empty()) {
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
} else {
dimensionInWhatValues.push_back(DEFAULT_DIMENSION_KEY);
}
for (const auto& whatDimension : dimensionInWhatValues) {
for (const auto& whatDimension : dimensionInWhatValues) {
for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
onMatchedLogEventInternalLocked(
matcherIndex, MetricDimensionKey(whatDimension, conditionDimensionKey),
conditionKey, condition, event);
}
if (dimensionKeysInCondition.empty()) {
onMatchedLogEventInternalLocked(
matcherIndex, MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY),
conditionKey, condition, event);
}
}
} else {
HashableDimensionKey dimensionInWhat;
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
MetricDimensionKey metricKey(dimensionInWhat, DEFAULT_DIMENSION_KEY);
for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
metricKey.setDimensionKeyInCondition(conditionDimensionKey);
onMatchedLogEventInternalLocked(
matcherIndex, MetricDimensionKey(whatDimension, conditionDimensionKey),
conditionKey, condition, event);
matcherIndex, metricKey, conditionKey, condition, event);
}
if (dimensionKeysInCondition.empty()) {
onMatchedLogEventInternalLocked(
matcherIndex, MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY),
conditionKey, condition, event);
matcherIndex, metricKey, conditionKey, condition, event);
}
}
}
} // namespace statsd

View File

@@ -50,7 +50,11 @@ public:
mCondition(conditionIndex >= 0 ? false : true),
mConditionSliced(false),
mWizard(wizard),
mConditionTrackerIndex(conditionIndex){};
mConditionTrackerIndex(conditionIndex),
mContainANYPositionInDimensionsInWhat(false),
mSameConditionDimensionsInTracker(false),
mHasLinksToAllConditionDimensionsInTracker(false) {
}
virtual ~MetricProducer(){};
@@ -219,6 +223,16 @@ protected:
vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
vector<Matcher> mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config
bool mContainANYPositionInDimensionsInWhat;
// True iff the condition dimensions equal to the sliced dimensions in the simple condition
// tracker. This field is always false for combinational condition trackers.
bool mSameConditionDimensionsInTracker;
// True iff the metric to condition links cover all dimension fields in the condition tracker.
// This field is always false for combinational condition trackers.
bool mHasLinksToAllConditionDimensionsInTracker;
std::vector<Metric2Condition> mMetric2ConditionLinks;
std::vector<sp<AnomalyTracker>> mAnomalyTrackers;

View File

@@ -160,10 +160,18 @@ private:
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
};
} // namespace statsd

View File

@@ -80,6 +80,7 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric
mBucketSizeNs = bucketSizeMills * 1000000;
if (metric.has_dimensions_in_what()) {
translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
if (metric.has_dimensions_in_condition()) {
@@ -146,7 +147,7 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
for (const auto& pair : mPastBuckets) {
const MetricDimensionKey& dimensionKey = pair.first;
VLOG(" dimension key %s", dimensionKey.c_str());
VLOG(" dimension key %s", dimensionKey.toString().c_str());
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
@@ -254,7 +255,7 @@ bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("ValueMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.c_str());
(long long)mMetricId, newKey.toString().c_str());
return true;
}
}

View File

@@ -64,7 +64,7 @@ public:
sp<ConditionWizard> wizard, int conditionIndex,
const std::vector<Matcher>& dimensionInCondition, bool nesting,
uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs,
uint64_t bucketSizeNs, bool conditionSliced,
uint64_t bucketSizeNs, bool conditionSliced, bool fullLink,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
: mConfigKey(key),
mTrackerId(id),
@@ -80,6 +80,7 @@ public:
mCurrentBucketNum(currentBucketNum),
mStartTimeNs(startTimeNs),
mConditionSliced(conditionSliced),
mHasLinksToAllConditionDimensionsInTracker(fullLink),
mAnomalyTrackers(anomalyTrackers){};
virtual ~DurationTracker(){};
@@ -198,6 +199,9 @@ protected:
const bool mConditionSliced;
bool mSameConditionDimensionsInTracker;
bool mHasLinksToAllConditionDimensionsInTracker;
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);

View File

@@ -30,20 +30,33 @@ MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
const vector<Matcher>& dimensionInCondition, bool nesting,
uint64_t currentBucketStartNs, uint64_t currentBucketNum,
uint64_t startTimeNs, uint64_t bucketSizeNs,
bool conditionSliced,
bool conditionSliced, bool fullLink,
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
: DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
conditionSliced, anomalyTrackers) {
conditionSliced, fullLink, anomalyTrackers) {
if (mWizard != nullptr) {
mSameConditionDimensionsInTracker =
mWizard->equalOutputDimensions(conditionIndex, mDimensionInCondition);
}
}
unique_ptr<DurationTracker> MaxDurationTracker::clone(const uint64_t eventTime) {
auto clonedTracker = make_unique<MaxDurationTracker>(*this);
for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end(); ++it) {
it->second.lastStartTime = eventTime;
it->second.lastDuration = 0;
for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end();) {
if (it->second.state != kStopped) {
it->second.lastStartTime = eventTime;
it->second.lastDuration = 0;
it++;
} else {
it = clonedTracker->mInfos.erase(it);
}
}
if (clonedTracker->mInfos.empty()) {
return nullptr;
} else {
return clonedTracker;
}
return clonedTracker;
}
bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -59,7 +72,7 @@ bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("MaxDurTracker %lld dropping data for dimension key %s",
(long long)mTrackerId, newKey.c_str());
(long long)mTrackerId, newKey.toString().c_str());
return true;
}
}
@@ -77,7 +90,7 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi
if (mConditionSliced) {
duration.conditionKeys = conditionKey;
}
VLOG("MaxDuration: key %s start condition %d", key.c_str(), condition);
VLOG("MaxDuration: key %s start condition %d", key.toString().c_str(), condition);
switch (duration.state) {
case kStarted:
@@ -103,7 +116,7 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi
void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
bool forceStop) {
VLOG("MaxDuration: key %s stop", key.c_str());
VLOG("MaxDuration: key %s stop", key.toString().c_str());
if (mInfos.find(key) == mInfos.end()) {
// we didn't see a start event before. do nothing.
return;
@@ -120,7 +133,7 @@ void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_
stopAnomalyAlarm();
duration.state = DurationState::kStopped;
int64_t durationTime = eventTime - duration.lastStartTime;
VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(),
VLOG("Max, key %s, Stop %lld %lld %lld", key.toString().c_str(),
(long long)duration.lastStartTime, (long long)eventTime,
(long long)durationTime);
duration.lastDuration += durationTime;
@@ -241,13 +254,15 @@ void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
ConditionState conditionState = mWizard->query(
mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition,
!mSameConditionDimensionsInTracker,
!mHasLinksToAllConditionDimensionsInTracker,
&conditionDimensionKeySet);
bool conditionMet =
(conditionState == ConditionState::kTrue) &&
(mDimensionInCondition.size() == 0 ||
conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
conditionDimensionKeySet.end());
VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
VLOG("key: %s, condition: %d", pair.first.toString().c_str(), conditionMet);
noteConditionChanged(pair.first, conditionMet, timestamp);
}
}
@@ -277,7 +292,7 @@ void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, b
// In case any other dimensions are still started, we need to set the alarm.
startAnomalyAlarm(timestamp);
}
VLOG("MaxDurationTracker Key: %s Started->Paused ", key.c_str());
VLOG("MaxDurationTracker Key: %s Started->Paused ", key.toString().c_str());
}
break;
case kStopped:
@@ -290,7 +305,7 @@ void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, b
it->second.state = DurationState::kStarted;
it->second.lastStartTime = timestamp;
startAnomalyAlarm(timestamp);
VLOG("MaxDurationTracker Key: %s Paused->Started", key.c_str());
VLOG("MaxDurationTracker Key: %s Paused->Started", key.toString().c_str());
}
break;
}

View File

@@ -33,6 +33,7 @@ public:
const std::vector<Matcher>& dimensionInCondition, bool nesting,
uint64_t currentBucketStartNs, uint64_t currentBucketNum,
uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced,
bool fullLink,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
MaxDurationTracker(const MaxDurationTracker& tracker) = default;

View File

@@ -28,14 +28,18 @@ OringDurationTracker::OringDurationTracker(
const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
sp<ConditionWizard> wizard, int conditionIndex, const vector<Matcher>& dimensionInCondition,
bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum,
uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced,
uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, bool fullLink,
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
: DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
conditionSliced, anomalyTrackers),
conditionSliced, fullLink, anomalyTrackers),
mStarted(),
mPaused() {
mLastStartTime = 0;
if (mWizard != nullptr) {
mSameConditionDimensionsInTracker =
mWizard->equalOutputDimensions(conditionIndex, mDimensionInCondition);
}
}
unique_ptr<DurationTracker> OringDurationTracker::clone(const uint64_t eventTime) {
@@ -57,7 +61,7 @@ bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("OringDurTracker %lld dropping data for dimension key %s",
(long long)mTrackerId, newKey.c_str());
(long long)mTrackerId, newKey.toString().c_str());
return true;
}
}
@@ -83,13 +87,13 @@ void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condi
if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
mConditionKeyMap[key] = conditionKey;
}
VLOG("Oring: %s start, condition %d", key.c_str(), condition);
VLOG("Oring: %s start, condition %d", key.toString().c_str(), condition);
}
void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp,
const bool stopAll) {
declareAnomalyIfAlarmExpired(timestamp);
VLOG("Oring: %s stop", key.c_str());
VLOG("Oring: %s stop", key.toString().c_str());
auto it = mStarted.find(key);
if (it != mStarted.end()) {
(it->second)--;
@@ -217,22 +221,26 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp)
if (!mStarted.empty()) {
for (auto it = mStarted.begin(); it != mStarted.end();) {
const auto& key = it->first;
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
VLOG("Key %s dont have condition key", key.c_str());
const auto& condIt = mConditionKeyMap.find(key);
if (condIt == mConditionKeyMap.end()) {
VLOG("Key %s dont have condition key", key.toString().c_str());
++it;
continue;
}
std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
ConditionState conditionState =
mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
mDimensionInCondition, &conditionDimensionKeySet);
mWizard->query(mConditionTrackerIndex, condIt->second,
mDimensionInCondition,
!mSameConditionDimensionsInTracker,
!mHasLinksToAllConditionDimensionsInTracker,
&conditionDimensionKeySet);
if (conditionState != ConditionState::kTrue ||
(mDimensionInCondition.size() != 0 &&
conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) ==
conditionDimensionKeySet.end())) {
startedToPaused.push_back(*it);
it = mStarted.erase(it);
VLOG("Key %s started -> paused", key.c_str());
VLOG("Key %s started -> paused", key.toString().c_str());
} else {
++it;
}
@@ -250,21 +258,24 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp)
for (auto it = mPaused.begin(); it != mPaused.end();) {
const auto& key = it->first;
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
VLOG("Key %s dont have condition key", key.c_str());
VLOG("Key %s dont have condition key", key.toString().c_str());
++it;
continue;
}
std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
ConditionState conditionState =
mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
mDimensionInCondition, &conditionDimensionKeySet);
mDimensionInCondition,
!mSameConditionDimensionsInTracker,
!mHasLinksToAllConditionDimensionsInTracker,
&conditionDimensionKeySet);
if (conditionState == ConditionState::kTrue &&
(mDimensionInCondition.size() == 0 ||
conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
conditionDimensionKeySet.end())) {
pausedToStarted.push_back(*it);
it = mPaused.erase(it);
VLOG("Key %s paused -> started", key.c_str());
VLOG("Key %s paused -> started", key.toString().c_str());
} else {
++it;
}

View File

@@ -32,6 +32,7 @@ public:
int conditionIndex, const std::vector<Matcher>& dimensionInCondition,
bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum,
uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced,
bool fullLink,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
OringDurationTracker(const OringDurationTracker& tracker) = default;

View File

@@ -32,7 +32,7 @@ const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey();
// Minimum bucket size in seconds
const long kMinBucketSizeSec = 5 * 60;
typedef std::map<int64_t, std::vector<HashableDimensionKey>> ConditionKey;
typedef std::map<int64_t, HashableDimensionKey> ConditionKey;
typedef std::unordered_map<MetricDimensionKey, int64_t> DimToValMap;

View File

@@ -75,10 +75,10 @@ void makeWakeLockEvent(
event->init();
}
std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
std::map<int64_t, HashableDimensionKey> getWakeLockQueryKey(
const Position position,
const std::vector<int> &uids, const string& conditionName) {
std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap;
std::map<int64_t, HashableDimensionKey> outputKeyMap;
std::vector<int> uid_indexes;
int pos[] = {1, 1, 1};
int depth = 2;
@@ -104,7 +104,7 @@ std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
Value value((int32_t)uids[idx]);
HashableDimensionKey dim;
dim.addValue(FieldValue(field, value));
outputKeyMap[StringToId(conditionName)].push_back(dim);
outputKeyMap[StringToId(conditionName)] = dim;
}
return outputKeyMap;
}
@@ -122,6 +122,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*tracker index*/,
simplePredicate, trackerNameIndexMap);
EXPECT_FALSE(conditionTracker.isSliced());
LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
@@ -193,6 +194,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
}
TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
std::vector<sp<ConditionTracker>> allConditions;
SimplePredicate simplePredicate;
simplePredicate.set_start(StringToId("SCREEN_TURNED_ON"));
simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF"));
@@ -205,6 +207,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
0 /*condition tracker index*/, simplePredicate,
trackerNameIndexMap);
EXPECT_FALSE(conditionTracker.isSliced());
LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
@@ -257,14 +260,14 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
changedCache);
// result should still be true
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
EXPECT_TRUE(changedCache[0]);
}
TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
std::vector<sp<ConditionTracker>> allConditions;
for (Position position :
{ Position::ANY, Position::FIRST, Position::LAST}) {
{ Position::FIRST, Position::LAST}) {
vector<Matcher> dimensionInCondition;
std::unordered_set<HashableDimensionKey> dimensionKeys;
@@ -281,6 +284,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
0 /*condition tracker index*/, simplePredicate,
trackerNameIndexMap);
std::vector<int> uids = {111, 222, 333};
LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
@@ -305,12 +309,20 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
}
EXPECT_TRUE(changedCache[0]);
if (position == Position::FIRST ||
position == Position::LAST) {
EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u);
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
} else {
EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), uids.size());
}
// Now test query
const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
false, false,
conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -331,6 +343,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
} else {
EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
}
EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
// wake lock 1 release
LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
@@ -350,6 +365,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
} else {
EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
}
EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
makeWakeLockEvent(&event4, uids, "wl2", 0); // now release it.
@@ -362,18 +379,26 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
changedCache);
EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
EXPECT_TRUE(changedCache[0]);
if (position == Position::FIRST ||
position == Position::LAST) {
EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u);
EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
} else {
EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), uids.size());
}
// query again
conditionCache[0] = ConditionState::kNotEvaluated;
conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
false, false,
conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
}
TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
std::vector<sp<ConditionTracker>> allConditions;
vector<Matcher> dimensionInCondition;
std::unordered_set<HashableDimensionKey> dimensionKeys;
@@ -391,6 +416,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
0 /*condition tracker index*/, simplePredicate,
trackerNameIndexMap);
EXPECT_FALSE(conditionTracker.isSliced());
std::vector<int> uid_list1 = {111, 1111, 11111};
string uid1_wl1 = "wl1_1";
std::vector<int> uid_list2 = {222, 2222, 22222};
@@ -419,6 +446,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
conditionCache[0] = ConditionState::kNotEvaluated;
conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
true, true,
conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -463,13 +491,15 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
conditionCache[0] = ConditionState::kNotEvaluated;
dimensionKeys.clear();
conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
true, true,
conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
TEST(SimpleConditionTrackerTest, TestStopAll) {
std::vector<sp<ConditionTracker>> allConditions;
for (Position position :
{Position::ANY, Position::FIRST, Position::LAST}) {
{ Position::FIRST, Position::LAST }) {
vector<Matcher> dimensionInCondition;
std::unordered_set<HashableDimensionKey> dimensionKeys;
SimplePredicate simplePredicate = getWakeLockHeldCondition(
@@ -510,12 +540,23 @@ TEST(SimpleConditionTrackerTest, TestStopAll) {
EXPECT_EQ(uid_list1.size(), conditionTracker.mSlicedConditionState.size());
}
EXPECT_TRUE(changedCache[0]);
{
if (position == Position::FIRST ||
position == Position::LAST) {
EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
} else {
EXPECT_EQ(uid_list1.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
}
}
// Now test query
const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
false, false,
conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -538,11 +579,23 @@ TEST(SimpleConditionTrackerTest, TestStopAll) {
conditionTracker.mSlicedConditionState.size());
}
EXPECT_TRUE(changedCache[0]);
{
if (position == Position::FIRST ||
position == Position::LAST) {
EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
} else {
EXPECT_EQ(uid_list2.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
}
}
// TEST QUERY
const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
false, false,
conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -561,11 +614,22 @@ TEST(SimpleConditionTrackerTest, TestStopAll) {
changedCache);
EXPECT_TRUE(changedCache[0]);
EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
{
if (position == Position::FIRST || position == Position::LAST) {
EXPECT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
} else {
EXPECT_EQ(uid_list1.size() + uid_list2.size(),
conditionTracker.getChangedToFalseDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
}
}
// TEST QUERY
const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
false, false,
conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
@@ -573,6 +637,7 @@ TEST(SimpleConditionTrackerTest, TestStopAll) {
const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
false, false,
conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}

View File

@@ -0,0 +1,879 @@
// 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 CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
StatsdConfig config;
*config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = CreateSyncEndAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
auto scheduledJobPredicate = CreateScheduledJobPredicate();
auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
dimensions->add_child()->set_field(2); // job name field.
auto screenIsOffPredicate = CreateScreenIsOffPredicate();
auto isSyncingPredicate = CreateIsSyncingPredicate();
auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
*syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
{Position::FIRST});
if (addExtraDimensionInCondition) {
syncDimension->add_child()->set_field(2 /* name field*/);
}
*config.add_predicate() = scheduledJobPredicate;
*config.add_predicate() = screenIsOffPredicate;
*config.add_predicate() = isSyncingPredicate;
auto combinationPredicate = config.add_predicate();
combinationPredicate->set_id(StringToId("CombinationPredicate"));
combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
auto metric = config.add_duration_metric();
metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("scheduledJob"));
metric->set_what(scheduledJobPredicate.id());
metric->set_condition(combinationPredicate->id());
metric->set_aggregation_type(aggregationType);
auto dimensionWhat = metric->mutable_dimensions_in_what();
dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
dimensionWhat->add_child()->set_field(2); // job name field.
*metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
android::util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
}
} // namespace
TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition) {
for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : { true, false }) {
for (auto aggregationType : {DurationMetric::MAX_SPARSE, DurationMetric::SUM}) {
ConfigKey cfgKey;
auto config = CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
aggregationType, isDimensionInConditionSubSetOfConditionTrackerDimension);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
std::vector<AttributionNodeInternal> attributions1 = {
CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(222, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions2 = {
CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 11));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 40));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 102));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 450));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 650));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + bucketSizeNs + 100));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + bucketSizeNs + 640));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + bucketSizeNs + 650));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 10));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 50));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 200));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 300));
events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
bucketStartTimeNs + 400));
events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
bucketStartTimeNs + 401));
events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 700));
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
StatsLogReport::DurationMetricDataWrapper metrics;
sortMetricDataByDimensionsValue(
reports.reports(0).metrics(0).duration_metrics(), &metrics);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(metrics.data_size(), 4);
auto data = metrics.data(0);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job0"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 111, "App1");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40 - 11);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job1"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 10);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(2);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job2"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 111, "App1");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 600);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(3);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job2"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 600);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100 + 650 - 640);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
} else {
EXPECT_EQ(metrics.data_size(), 4);
auto data = metrics.data(0);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job0"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 111, "App1");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40 - 11);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job1"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 10);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(2);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job2"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 111, "App1");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(3);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job2"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 110);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
}
}
}
}
namespace {
StatsdConfig CreateDurationMetricConfig_Link_AND_CombinationCondition(
DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
StatsdConfig config;
*config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = CreateSyncEndAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
auto scheduledJobPredicate = CreateScheduledJobPredicate();
auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
*dimensions = CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
dimensions->add_child()->set_field(2); // job name field.
auto isSyncingPredicate = CreateIsSyncingPredicate();
auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
*syncDimension = CreateAttributionUidDimensions(
android::util::SYNC_STATE_CHANGED, {Position::FIRST});
if (addExtraDimensionInCondition) {
syncDimension->add_child()->set_field(2 /* name field*/);
}
auto screenIsOffPredicate = CreateScreenIsOffPredicate();
*config.add_predicate() = scheduledJobPredicate;
*config.add_predicate() = screenIsOffPredicate;
*config.add_predicate() = isSyncingPredicate;
auto combinationPredicate = config.add_predicate();
combinationPredicate->set_id(StringToId("CombinationPredicate"));
combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
auto metric = config.add_duration_metric();
metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("scheduledJob"));
metric->set_what(scheduledJobPredicate.id());
metric->set_condition(combinationPredicate->id());
metric->set_aggregation_type(aggregationType);
*metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
auto links = metric->add_links();
links->set_condition(isSyncingPredicate.id());
*links->mutable_fields_in_what() =
CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
*links->mutable_fields_in_condition() =
CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
}
} // namespace
TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition) {
for (bool isFullLink : {true, false}) {
for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
ConfigKey cfgKey;
auto config = CreateDurationMetricConfig_Link_AND_CombinationCondition(
aggregationType, !isFullLink);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
std::vector<AttributionNodeInternal> attributions1 = {
CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(222, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions2 = {
CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions3 = {
CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 55));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 120));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 121));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 450));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 501));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + bucketSizeNs + 100));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2",
bucketStartTimeNs + bucketSizeNs + 850));
events.push_back(
CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
bucketStartTimeNs + bucketSizeNs - 2));
events.push_back(
CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
bucketStartTimeNs + bucketSizeNs + 900));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 50));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 110));
events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
bucketStartTimeNs + 300));
events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 700));
events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
bucketStartTimeNs + 400));
events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
bucketStartTimeNs + 550));
events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
bucketStartTimeNs + 800));
events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
bucketStartTimeNs + bucketSizeNs + 700));
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
StatsLogReport::DurationMetricDataWrapper metrics;
sortMetricDataByDimensionsValue(
reports.reports(0).metrics(0).duration_metrics(), &metrics);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(metrics.data_size(), 3);
auto data = metrics.data(0);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300 + bucketSizeNs - 600);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(2);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
} else {
EXPECT_EQ(metrics.data_size(), 3);
auto data = metrics.data(0);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(2);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
}
}
}
}
namespace {
StatsdConfig CreateDurationMetricConfig_PartialLink_AND_CombinationCondition(
DurationMetric::AggregationType aggregationType) {
StatsdConfig config;
*config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = CreateSyncEndAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
auto scheduledJobPredicate = CreateScheduledJobPredicate();
auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
*dimensions = CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
dimensions->add_child()->set_field(2); // job name field.
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 screenIsOffPredicate = CreateScreenIsOffPredicate();
*config.add_predicate() = scheduledJobPredicate;
*config.add_predicate() = screenIsOffPredicate;
*config.add_predicate() = isSyncingPredicate;
auto combinationPredicate = config.add_predicate();
combinationPredicate->set_id(StringToId("CombinationPredicate"));
combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
auto metric = config.add_duration_metric();
metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("scheduledJob"));
metric->set_what(scheduledJobPredicate.id());
metric->set_condition(combinationPredicate->id());
metric->set_aggregation_type(aggregationType);
*metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
*metric->mutable_dimensions_in_condition() = *syncDimension;
auto links = metric->add_links();
links->set_condition(isSyncingPredicate.id());
*links->mutable_fields_in_what() =
CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
*links->mutable_fields_in_condition() =
CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
}
} // namespace
TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition) {
for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
ConfigKey cfgKey;
auto config =
CreateDurationMetricConfig_PartialLink_AND_CombinationCondition(aggregationType);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
std::vector<AttributionNodeInternal> attributions1 = {
CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(222, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions2 = {
CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions3 = {
CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 55));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 120));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 121));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 450));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
bucketStartTimeNs + 501));
events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + bucketSizeNs + 100));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2",
bucketStartTimeNs + bucketSizeNs + 850));
events.push_back(
CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
bucketStartTimeNs + bucketSizeNs - 2));
events.push_back(
CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
bucketStartTimeNs + bucketSizeNs + 900));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 50));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 110));
events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
bucketStartTimeNs + 300));
events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 700));
events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
bucketStartTimeNs + 400));
events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
bucketStartTimeNs + 550));
events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
bucketStartTimeNs + 800));
events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
bucketStartTimeNs + bucketSizeNs + 700));
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
StatsLogReport::DurationMetricDataWrapper metrics;
sortMetricDataByDimensionsValue(
reports.reports(0).metrics(0).duration_metrics(), &metrics);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(metrics.data_size(), 4);
auto data = metrics.data(0);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
EXPECT_EQ("ReadEmail",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
EXPECT_EQ("ReadDoc",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 1 - 600 + 50);
data = metrics.data(2);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
EXPECT_EQ("ReadEmail",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300 + bucketSizeNs - 600);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(3);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
EXPECT_EQ("ReadDoc",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
} else {
EXPECT_EQ(metrics.data_size(), 4);
auto data = metrics.data(0);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
EXPECT_EQ("ReadEmail",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
EXPECT_EQ("ReadDoc",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 50);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 1 - 600);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(2);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
EXPECT_EQ("ReadEmail",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(3);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
EXPECT_EQ("ReadDoc",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
}
}
}
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -28,7 +28,7 @@ namespace statsd {
namespace {
StatsdConfig CreateCountMetricWithNoLinkConfig() {
StatsdConfig CreateCountMetric_NoLink_CombinationCondition_Config() {
StatsdConfig config;
auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher();
*config.add_atom_matcher() = screenBrightnessChangeAtomMatcher;
@@ -67,9 +67,9 @@ StatsdConfig CreateCountMetricWithNoLinkConfig() {
} // namespace
TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) {
TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition) {
ConfigKey cfgKey;
auto config = CreateCountMetricWithNoLinkConfig();
auto config = CreateCountMetric_NoLink_CombinationCondition_Config();
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
@@ -227,7 +227,7 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) {
namespace {
StatsdConfig CreateCountMetricWithLinkConfig() {
StatsdConfig CreateCountMetric_Link_CombinationCondition() {
StatsdConfig config;
auto appCrashMatcher = CreateProcessCrashAtomMatcher();
*config.add_atom_matcher() = appCrashMatcher;
@@ -274,9 +274,9 @@ StatsdConfig CreateCountMetricWithLinkConfig() {
} // namespace
TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) {
TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition) {
ConfigKey cfgKey;
auto config = CreateCountMetricWithLinkConfig();
auto config = CreateCountMetric_Link_CombinationCondition();
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
@@ -413,7 +413,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) {
namespace {
StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType aggregationType) {
StatsdConfig CreateDurationMetricConfig_NoLink_CombinationCondition(
DurationMetric::AggregationType aggregationType) {
StatsdConfig config;
*config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
*config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
@@ -445,6 +446,7 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag
metric->set_id(StringToId("BatterySaverModeDurationMetric"));
metric->set_what(inBatterySaverModePredicate.id());
metric->set_condition(combinationPredicate->id());
metric->set_aggregation_type(aggregationType);
*metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
android::util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
@@ -452,10 +454,10 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag
} // namespace
TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) {
for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition) {
for (auto aggregationType : { DurationMetric::MAX_SPARSE}) { // DurationMetric::SUM,
ConfigKey cfgKey;
auto config = CreateDurationMetricConfigNoLink(aggregationType);
auto config = CreateDurationMetricConfig_NoLink_CombinationCondition(aggregationType);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
@@ -529,43 +531,77 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) {
auto data = metrics.data(0);
EXPECT_FALSE(data.dimensions_in_what().has_field());
EXPECT_FALSE(data.dimensions_in_condition().has_field());
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
} else {
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
}
data = metrics.data(1);
EXPECT_FALSE(data.dimensions_in_what().has_field());
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 111, "App1");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
} else {
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 300);
}
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(2);
EXPECT_FALSE(data.dimensions_in_what().has_field());
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
} else {
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs + 700 - 600);
}
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
}
}
namespace {
StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType aggregationType) {
StatsdConfig CreateDurationMetricConfig_Link_CombinationCondition(
DurationMetric::AggregationType aggregationType) {
StatsdConfig config;
*config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
*config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
@@ -599,6 +635,7 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType
metric->set_id(StringToId("AppInBackgroundMetric"));
metric->set_what(isInBackgroundPredicate.id());
metric->set_condition(combinationPredicate->id());
metric->set_aggregation_type(aggregationType);
*metric->mutable_dimensions_in_what() =
CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */});
*metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
@@ -617,10 +654,10 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType
} // namespace
TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) {
TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition) {
for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
ConfigKey cfgKey;
auto config = CreateDurationMetricConfigWithLink(aggregationType);
auto config = CreateDurationMetricConfig_Link_CombinationCondition(aggregationType);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
@@ -701,26 +738,50 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) {
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 111, "App1");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
} else {
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs + 100 - 201);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
}
data = metrics.data(2);
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(), 333);
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
} else {
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs + 299);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
}
}
}

View File

@@ -0,0 +1,799 @@
// 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 CreateDurationMetricConfig_NoLink_SimpleCondition(
DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
StatsdConfig config;
*config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = CreateSyncEndAtomMatcher();
auto scheduledJobPredicate = CreateScheduledJobPredicate();
auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
dimensions->add_child()->set_field(2); // job name field.
auto isSyncingPredicate = CreateIsSyncingPredicate();
auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
*syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
{Position::FIRST});
if (addExtraDimensionInCondition) {
syncDimension->add_child()->set_field(2 /* name field*/);
}
*config.add_predicate() = scheduledJobPredicate;
*config.add_predicate() = isSyncingPredicate;
auto metric = config.add_duration_metric();
metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("scheduledJob"));
metric->set_what(scheduledJobPredicate.id());
metric->set_condition(isSyncingPredicate.id());
metric->set_aggregation_type(aggregationType);
auto dimensionWhat = metric->mutable_dimensions_in_what();
dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
dimensionWhat->add_child()->set_field(2); // job name field.
*metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
android::util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
}
} // namespace
TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition) {
for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : {true, false}) {
for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
ConfigKey cfgKey;
auto config = CreateDurationMetricConfig_NoLink_SimpleCondition(
aggregationType, isDimensionInConditionSubSetOfConditionTrackerDimension);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
std::vector<AttributionNodeInternal> attributions1 = {
CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(222, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions2 = {
CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 1));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 10));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 50));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 200));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 300));
events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
bucketStartTimeNs + 400));
events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
bucketStartTimeNs + 401));
events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 700));
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
StatsLogReport::DurationMetricDataWrapper metrics;
sortMetricDataByDimensionsValue(
reports.reports(0).metrics(0).duration_metrics(), &metrics);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(metrics.data_size(), 4);
auto data = metrics.data(0);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job0"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 111, "App1");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job1"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(2);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job2"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 111, "App1");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(3);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job2"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
} else {
EXPECT_EQ(metrics.data_size(), 4);
auto data = metrics.data(0);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job0"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 111, "App1");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job1"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(2);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job2"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 111, "App1");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 300);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(3);
EXPECT_EQ(data.dimensions_in_what().field(),
android::util::SCHEDULED_JOB_STATE_CHANGED);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
2); // job name field
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
"job2"); // job name
ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 );
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
}
}
}
}
namespace {
StatsdConfig createDurationMetric_Link_SimpleConditionConfig(
DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
StatsdConfig config;
*config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = CreateSyncEndAtomMatcher();
auto scheduledJobPredicate = CreateScheduledJobPredicate();
auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
*dimensions = CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
dimensions->add_child()->set_field(2); // job name field.
auto isSyncingPredicate = CreateIsSyncingPredicate();
auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
*syncDimension = CreateAttributionUidDimensions(
android::util::SYNC_STATE_CHANGED, {Position::FIRST});
if (addExtraDimensionInCondition) {
syncDimension->add_child()->set_field(2 /* name field*/);
}
*config.add_predicate() = scheduledJobPredicate;
*config.add_predicate() = isSyncingPredicate;
auto metric = config.add_duration_metric();
metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("scheduledJob"));
metric->set_what(scheduledJobPredicate.id());
metric->set_condition(isSyncingPredicate.id());
metric->set_aggregation_type(aggregationType);
*metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
auto links = metric->add_links();
links->set_condition(isSyncingPredicate.id());
*links->mutable_fields_in_what() =
CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
*links->mutable_fields_in_condition() =
CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
}
} // namespace
TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition) {
for (bool isFullLink : {true, false}) {
for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
ConfigKey cfgKey;
auto config = createDurationMetric_Link_SimpleConditionConfig(
aggregationType, !isFullLink);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
std::vector<AttributionNodeInternal> attributions1 = {
CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(222, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions2 = {
CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions3 = {
CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
events.push_back(
CreateFinishScheduledJobEvent({CreateAttribution(333, "App2")}, "job2",
bucketStartTimeNs + bucketSizeNs + 850));
events.push_back(
CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
bucketStartTimeNs + bucketSizeNs - 2));
events.push_back(
CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
bucketStartTimeNs + bucketSizeNs + 900));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 50));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 110));
events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
bucketStartTimeNs + 300));
events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 700));
events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
bucketStartTimeNs + 400));
events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
bucketStartTimeNs + 550));
events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
bucketStartTimeNs + 800));
events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
bucketStartTimeNs + bucketSizeNs + 700));
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
StatsLogReport::DurationMetricDataWrapper metrics;
sortMetricDataByDimensionsValue(
reports.reports(0).metrics(0).duration_metrics(), &metrics);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(metrics.data_size(), 3);
auto data = metrics.data(0);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(2);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
} else {
EXPECT_EQ(metrics.data_size(), 3);
auto data = metrics.data(0);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(2);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
}
}
}
}
namespace {
StatsdConfig createDurationMetric_PartialLink_SimpleConditionConfig(
DurationMetric::AggregationType aggregationType) {
StatsdConfig config;
*config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = CreateSyncEndAtomMatcher();
auto scheduledJobPredicate = CreateScheduledJobPredicate();
auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
*dimensions = CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
dimensions->add_child()->set_field(2); // job name field.
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*/);
*config.add_predicate() = scheduledJobPredicate;
*config.add_predicate() = isSyncingPredicate;
auto metric = config.add_duration_metric();
metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("scheduledJob"));
metric->set_what(scheduledJobPredicate.id());
metric->set_condition(isSyncingPredicate.id());
metric->set_aggregation_type(aggregationType);
*metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
*metric->mutable_dimensions_in_condition() = *syncDimension;
auto links = metric->add_links();
links->set_condition(isSyncingPredicate.id());
*links->mutable_fields_in_what() =
CreateAttributionUidDimensions(
android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
*links->mutable_fields_in_condition() =
CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
}
} // namespace
TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition) {
for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
ConfigKey cfgKey;
auto config = createDurationMetric_PartialLink_SimpleConditionConfig(
aggregationType);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(
bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
std::vector<AttributionNodeInternal> attributions1 = {
CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(222, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions2 = {
CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<AttributionNodeInternal> attributions3 = {
CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
CreateAttribution(555, "GMSCoreModule2")};
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
events.push_back(CreateStartScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
events.push_back(CreateFinishScheduledJobEvent(
{CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + bucketSizeNs + 850));
events.push_back(
CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
bucketStartTimeNs + bucketSizeNs - 2));
events.push_back(
CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
bucketStartTimeNs + bucketSizeNs + 900));
events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 50));
events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
bucketStartTimeNs + 110));
events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
bucketStartTimeNs + 300));
events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
bucketStartTimeNs + bucketSizeNs + 700));
events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
bucketStartTimeNs + 400));
events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
bucketStartTimeNs + 550));
events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
bucketStartTimeNs + 800));
events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
bucketStartTimeNs + bucketSizeNs - 1));
events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
bucketStartTimeNs + bucketSizeNs + 700));
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
StatsLogReport::DurationMetricDataWrapper metrics;
sortMetricDataByDimensionsValue(
reports.reports(0).metrics(0).duration_metrics(), &metrics);
if (aggregationType == DurationMetric::SUM) {
EXPECT_EQ(4, metrics.data_size());
auto data = metrics.data(0);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
EXPECT_EQ("ReadEmail",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
EXPECT_EQ("ReadDoc",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 1 - 400 - 100);
data = metrics.data(2);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
EXPECT_EQ("ReadEmail",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(3);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
EXPECT_EQ("ReadDoc",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
} else {
EXPECT_EQ(metrics.data_size(), 4);
auto data = metrics.data(0);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
EXPECT_EQ("ReadEmail",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
data = metrics.data(1);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
EXPECT_EQ("ReadDoc",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 1 - 600);
data = metrics.data(2);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
EXPECT_EQ("ReadEmail",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
data = metrics.data(3);
ValidateAttributionUidDimension(
data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
ValidateAttributionUidDimension(
data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
EXPECT_EQ("ReadDoc",
data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + 2 * bucketSizeNs);
}
}
}
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -175,9 +175,9 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
{getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse));
EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue));
EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
bucketStartTimeNs);

View File

@@ -113,9 +113,9 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) {
key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse));
EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue));
EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);

View File

@@ -42,14 +42,13 @@ const ConfigKey kConfigKey(0, 12345);
const int TagId = 1;
const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1");
const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
const HashableDimensionKey conditionKey = getMockedDimensionKey(TagId, 4, "1");
const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
const uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
@@ -66,7 +65,7 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
int64_t metricId = 1;
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
false, {});
false, false, {});
tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
// Event starts again. This would not change anything as it already starts.
@@ -86,7 +85,6 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
TEST(MaxDurationTrackerTest, TestStopAll) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
@@ -103,7 +101,7 @@ TEST(MaxDurationTrackerTest, TestStopAll) {
int64_t metricId = 1;
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
false, {});
false, false, {});
tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
@@ -124,7 +122,6 @@ TEST(MaxDurationTrackerTest, TestStopAll) {
TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
vector<Matcher> dimensionInCondition;
@@ -140,7 +137,7 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
int64_t metricId = 1;
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
false, {});
false, false, {});
// The event starts.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
@@ -166,7 +163,6 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
vector<Matcher> dimensionInCondition;
@@ -182,7 +178,7 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
int64_t metricId = 1;
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
false, {});
false, false, {});
// 2 starts
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
@@ -204,14 +200,14 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
}
TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
const std::vector<HashableDimensionKey> conditionKey = {key1};
const HashableDimensionKey conditionDimKey = key1;
vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conditionKey1;
MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 1, "1");
conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
conditionKey1[StringToId("APP_BACKGROUND")] = conditionDimKey;
/**
Start in first bucket, stop in second bucket. Condition turns on and off in the first bucket
@@ -229,7 +225,7 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
int64_t metricId = 1;
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
false, bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, true,
{});
false, {});
EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1);
@@ -250,8 +246,6 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
}
TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -281,7 +275,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
true, {anomalyTracker});
true, false, {anomalyTracker});
tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
@@ -301,8 +295,6 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
// This tests that we correctly compute the predicted time of an anomaly assuming that the current
// state continues forward as-is.
TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) {
const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -310,7 +302,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) {
MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps");
conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
ConditionKey conditionKey2;
conditionKey2[StringToId("APP_BACKGROUND")] = {getMockedDimensionKey(TagId, 4, "2")};
conditionKey2[StringToId("APP_BACKGROUND")] = getMockedDimensionKey(TagId, 4, "2");
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -343,7 +335,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) {
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
true, {anomalyTracker});
true, false, {anomalyTracker});
tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1);
tracker.noteConditionChanged(key1, true, conditionStarts1);
@@ -360,8 +352,6 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) {
// Suppose A starts, then B starts, and then A stops. We still need to set an anomaly based on the
// elapsed duration of B.
TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) {
const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -369,7 +359,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) {
MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps");
conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
ConditionKey conditionKey2;
conditionKey2[StringToId("APP_BACKGROUND")] = {getMockedDimensionKey(TagId, 4, "2")};
conditionKey2[StringToId("APP_BACKGROUND")] = getMockedDimensionKey(TagId, 4, "2");
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -399,7 +389,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) {
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
true, {anomalyTracker});
true, false, {anomalyTracker});
tracker.noteStart(key1, true, eventStartTimeNs1, conditionKey1);
tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2);
@@ -415,4 +405,4 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) {
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
#endif

View File

@@ -40,7 +40,7 @@ const int TagId = 1;
const int64_t metricId = 123;
const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(TagId, 1, "maps");
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
const uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
@@ -48,8 +48,6 @@ const uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
TEST(OringDurationTrackerTest, TestDurationOverlap) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
const std::vector<HashableDimensionKey> kConditionKey1 =
{getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
vector<Matcher> dimensionInCondition;
@@ -65,7 +63,7 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) {
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, false, {});
bucketSizeNs, false, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -83,8 +81,6 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) {
TEST(OringDurationTrackerTest, TestDurationNested) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
const std::vector<HashableDimensionKey> kConditionKey1 =
{getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
vector<Matcher> dimensionInCondition;
@@ -99,7 +95,7 @@ TEST(OringDurationTrackerTest, TestDurationNested) {
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, false, {});
bucketSizeNs, false, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
@@ -132,7 +128,7 @@ TEST(OringDurationTrackerTest, TestStopAll) {
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, false, {});
bucketSizeNs, false, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
@@ -148,8 +144,6 @@ TEST(OringDurationTrackerTest, TestStopAll) {
TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
const std::vector<HashableDimensionKey> kConditionKey1 =
{getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
vector<Matcher> dimensionInCondition;
@@ -165,7 +159,7 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, false, {});
bucketSizeNs, false, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -189,8 +183,6 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
TEST(OringDurationTrackerTest, TestDurationConditionChange) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
const std::vector<HashableDimensionKey> kConditionKey1 =
{getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
vector<Matcher> dimensionInCondition;
@@ -199,7 +191,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) {
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
EXPECT_CALL(*wizard, query(_, key1, _, _)) // #4
EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -212,7 +204,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) {
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, true, {});
bucketSizeNs, true, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -229,8 +221,6 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) {
TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
const std::vector<HashableDimensionKey> kConditionKey1 =
{getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
vector<Matcher> dimensionInCondition;
@@ -239,7 +229,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
EXPECT_CALL(*wizard, query(_, key1, _, _))
EXPECT_CALL(*wizard, query(_, key1, _, _, _, _))
.Times(2)
.WillOnce(Return(ConditionState::kFalse))
.WillOnce(Return(ConditionState::kTrue));
@@ -254,7 +244,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, true, {});
bucketSizeNs, true, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
// condition to false; record duration 5n
@@ -273,8 +263,6 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
const std::vector<HashableDimensionKey> kConditionKey1 =
{getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
vector<Matcher> dimensionInCondition;
@@ -283,7 +271,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
EXPECT_CALL(*wizard, query(_, key1, _, _)) // #4
EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -295,7 +283,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, true, {});
bucketSizeNs, true, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
@@ -315,8 +303,6 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
const std::vector<HashableDimensionKey> kConditionKey1 =
{getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
vector<Matcher> dimensionInCondition;
@@ -339,7 +325,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, true, {anomalyTracker});
bucketSizeNs, true, false, {anomalyTracker});
// Nothing in the past bucket.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
@@ -386,7 +372,6 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
vector<Matcher> dimensionInCondition;
@@ -410,7 +395,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, false, {anomalyTracker});
bucketSizeNs, false, false, {anomalyTracker});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
@@ -437,8 +422,6 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
const std::vector<HashableDimensionKey> kConditionKey1 =
{getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
vector<Matcher> dimensionInCondition;
@@ -462,7 +445,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs,
bucketSizeNs, false, {anomalyTracker});
bucketSizeNs, false, false, {anomalyTracker});
tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
@@ -508,4 +491,4 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
#endif

View File

@@ -26,9 +26,10 @@ namespace statsd {
class MockConditionWizard : public ConditionWizard {
public:
MOCK_METHOD4(query,
MOCK_METHOD6(query,
ConditionState(const int conditionIndex, const ConditionKey& conditionParameters,
const vector<Matcher>& dimensionFields,
const bool isSubsetDim, const bool isPartialLink,
std::unordered_set<HashableDimensionKey>* dimensionKeySet));
};

View File

@@ -26,6 +26,28 @@ AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
return atom_matcher;
}
AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name,
ScheduledJobStateChanged::State state) {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(3); // State field.
field_value_matcher->set_eq_int(state);
return atom_matcher;
}
AtomMatcher CreateStartScheduledJobAtomMatcher() {
return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart",
ScheduledJobStateChanged::STARTED);
}
AtomMatcher CreateFinishScheduledJobAtomMatcher() {
return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish",
ScheduledJobStateChanged::FINISHED);
}
AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
@@ -168,6 +190,14 @@ AtomMatcher CreateProcessCrashAtomMatcher() {
"ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED);
}
Predicate CreateScheduledJobPredicate() {
Predicate predicate;
predicate.set_id(StringToId("ScheduledJobRunningPredicate"));
predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart"));
predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish"));
return predicate;
}
Predicate CreateBatterySaverModePredicate() {
Predicate predicate;
predicate.set_id(StringToId("BatterySaverIsOn"));
@@ -290,6 +320,32 @@ std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
}
std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& jobName,
const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
auto event = std::make_unique<LogEvent>(android::util::SCHEDULED_JOB_STATE_CHANGED, timestampNs);
event->write(attributions);
event->write(jobName);
event->write(state);
event->init();
return event;
}
std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
const std::vector<AttributionNodeInternal>& attributions,
const string& name, uint64_t timestampNs) {
return CreateScheduledJobStateChangedEvent(
attributions, name, ScheduledJobStateChanged::STARTED, timestampNs);
}
// Create log event when scheduled job finishes.
std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
const std::vector<AttributionNodeInternal>& attributions,
const string& name, uint64_t timestampNs) {
return CreateScheduledJobStateChangedEvent(
attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs);
}
std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
const WakelockStateChanged::State state, uint64_t timestampNs) {
@@ -419,7 +475,6 @@ int64_t StringToId(const string& str) {
void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) {
EXPECT_EQ(value.field(), atomId);
EXPECT_EQ(value.value_tuple().dimensions_value_size(), 1);
// Attribution field.
EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
// Uid only.

View File

@@ -28,6 +28,15 @@ namespace statsd {
// Create AtomMatcher proto to simply match a specific atom type.
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
// Create AtomMatcher proto for scheduled job state changed.
AtomMatcher CreateScheduledJobStateChangedAtomMatcher();
// Create AtomMatcher proto for starting a scheduled job.
AtomMatcher CreateStartScheduledJobAtomMatcher();
// Create AtomMatcher proto for a scheduled job is done.
AtomMatcher CreateFinishScheduledJobAtomMatcher();
// Create AtomMatcher proto for screen brightness state changed.
AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
@@ -73,6 +82,9 @@ Predicate CreateScreenIsOnPredicate();
// Create Predicate proto for screen is off.
Predicate CreateScreenIsOffPredicate();
// Create Predicate proto for a running scheduled job.
Predicate CreateScheduledJobPredicate();
// Create Predicate proto for battery saver mode.
Predicate CreateBatterySaverModePredicate();
@@ -107,6 +119,16 @@ std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
int level, uint64_t timestampNs);
// Create log event when scheduled job starts.
std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
const std::vector<AttributionNodeInternal>& attributions,
const string& name, uint64_t timestampNs);
// Create log event when scheduled job finishes.
std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
const std::vector<AttributionNodeInternal>& attributions,
const string& name, uint64_t timestampNs);
// Create log event when battery saver starts.
std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
// Create log event when battery saver stops.