Merge "Statsd cpu optimizaton" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
641eb554fd
@@ -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))
|
||||
320
cmds/statsd/benchmark/duration_metric_benchmark.cpp
Normal file
320
cmds/statsd/benchmark/duration_metric_benchmark.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
394
cmds/statsd/benchmark/metric_util.cpp
Normal file
394
cmds/statsd/benchmark/metric_util.cpp
Normal 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
|
||||
161
cmds/statsd/benchmark/metric_util.h
Normal file
161
cmds/statsd/benchmark/metric_util.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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, ¤tTrueConditionDimensions);
|
||||
trueDimensionsToProcess = ¤tTrueConditionDimensions;
|
||||
} 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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user