Migrate frameworks/base/cmds/statsd to packages/modules/StatsD/bin

Add statsd protos to platform_protos rule

BUG: 167962588
TEST: TH
TEST: Local build [ m com.android.os.statsd ]
Change-Id: Ic6703ea7636a00152c8a6483e1d91729f758e24d
Merged-In: I053f2a211ea28c2f181937af3d58ad16b235d096
This commit is contained in:
Baligh Uddin
2020-12-03 19:39:38 +00:00
parent 64790b3d00
commit 0a64604e4d
204 changed files with 1 additions and 60028 deletions

View File

@@ -753,6 +753,7 @@ java_library_host {
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_internal_protos",
":statsd_internal_protos",
"cmds/am/proto/instrumentation_data.proto",
"cmds/statsd/src/**/*.proto",
"core/proto/**/*.proto",

View File

@@ -1,17 +0,0 @@
BasedOnStyle: Google
AllowShortIfStatementsOnASingleLine: true
AllowShortFunctionsOnASingleLine: false
AllowShortLoopsOnASingleLine: true
BinPackArguments: true
BinPackParameters: true
ColumnLimit: 100
CommentPragmas: NOLINT:.*
ContinuationIndentWidth: 8
DerivePointerAlignment: false
IndentWidth: 4
PointerAlignment: Left
TabWidth: 4
AccessModifierOffset: -4
IncludeCategories:
- Regex: '^"Log\.h"'
Priority: -1

View File

@@ -1,408 +0,0 @@
//
// 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.
//
cc_defaults {
name: "statsd_defaults",
srcs: [
"src/active_config_list.proto",
"src/anomaly/AlarmMonitor.cpp",
"src/anomaly/AlarmTracker.cpp",
"src/anomaly/AnomalyTracker.cpp",
"src/anomaly/DurationAnomalyTracker.cpp",
"src/anomaly/subscriber_util.cpp",
"src/condition/CombinationConditionTracker.cpp",
"src/condition/condition_util.cpp",
"src/condition/ConditionWizard.cpp",
"src/condition/SimpleConditionTracker.cpp",
"src/config/ConfigKey.cpp",
"src/config/ConfigListener.cpp",
"src/config/ConfigManager.cpp",
"src/experiment_ids.proto",
"src/external/Perfetto.cpp",
"src/external/PullResultReceiver.cpp",
"src/external/puller_util.cpp",
"src/external/StatsCallbackPuller.cpp",
"src/external/StatsPuller.cpp",
"src/external/StatsPullerManager.cpp",
"src/external/TrainInfoPuller.cpp",
"src/FieldValue.cpp",
"src/guardrail/StatsdStats.cpp",
"src/hash.cpp",
"src/HashableDimensionKey.cpp",
"src/logd/LogEvent.cpp",
"src/logd/LogEventQueue.cpp",
"src/matchers/CombinationLogMatchingTracker.cpp",
"src/matchers/EventMatcherWizard.cpp",
"src/matchers/matcher_util.cpp",
"src/matchers/SimpleLogMatchingTracker.cpp",
"src/metadata_util.cpp",
"src/metrics/CountMetricProducer.cpp",
"src/metrics/duration_helper/MaxDurationTracker.cpp",
"src/metrics/duration_helper/OringDurationTracker.cpp",
"src/metrics/DurationMetricProducer.cpp",
"src/metrics/EventMetricProducer.cpp",
"src/metrics/GaugeMetricProducer.cpp",
"src/metrics/MetricProducer.cpp",
"src/metrics/metrics_manager_util.cpp",
"src/metrics/MetricsManager.cpp",
"src/metrics/ValueMetricProducer.cpp",
"src/packages/UidMap.cpp",
"src/shell/shell_config.proto",
"src/shell/ShellSubscriber.cpp",
"src/socket/StatsSocketListener.cpp",
"src/state/StateManager.cpp",
"src/state/StateTracker.cpp",
"src/stats_log_util.cpp",
"src/statscompanion_util.cpp",
"src/statsd_config.proto",
"src/statsd_metadata.proto",
"src/StatsLogProcessor.cpp",
"src/StatsService.cpp",
"src/storage/StorageManager.cpp",
"src/subscriber/IncidentdReporter.cpp",
"src/subscriber/SubscriberReporter.cpp",
"src/uid_data.proto",
"src/utils/MultiConditionTrigger.cpp",
],
local_include_dirs: [
"src",
],
static_libs: [
"libbase",
"libcutils",
"libgtest_prod",
"libprotoutil",
"libstatslog_statsd",
"libsysutils",
"libutils",
"statsd-aidl-ndk_platform",
],
shared_libs: [
"libbinder_ndk",
"libincident",
"liblog",
],
}
genrule {
name: "statslog_statsd.h",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsd.h --module statsd --namespace android,os,statsd,util",
out: [
"statslog_statsd.h",
],
}
genrule {
name: "statslog_statsd.cpp",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsd.cpp --module statsd --namespace android,os,statsd,util --importHeader statslog_statsd.h",
out: [
"statslog_statsd.cpp",
],
}
genrule {
name: "statslog_statsdtest.h",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsdtest.h --module statsdtest --namespace android,os,statsd,util",
out: [
"statslog_statsdtest.h",
],
}
genrule {
name: "statslog_statsdtest.cpp",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsdtest.cpp --module statsdtest --namespace android,os,statsd,util --importHeader statslog_statsdtest.h",
out: [
"statslog_statsdtest.cpp",
],
}
cc_library_static {
name: "libstatslog_statsdtest",
generated_sources: ["statslog_statsdtest.cpp"],
generated_headers: ["statslog_statsdtest.h"],
export_generated_headers: ["statslog_statsdtest.h"],
shared_libs: [
"libstatssocket",
]
}
cc_library_static {
name: "libstatslog_statsd",
generated_sources: ["statslog_statsd.cpp"],
generated_headers: ["statslog_statsd.h"],
export_generated_headers: ["statslog_statsd.h"],
apex_available: [
"com.android.os.statsd",
"test_com.android.os.statsd",
],
shared_libs: [
"libstatssocket",
]
}
// =========
// statsd
// =========
cc_binary {
name: "statsd",
defaults: ["statsd_defaults"],
srcs: ["src/main.cpp"],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-Wno-unused-parameter",
// optimize for size (protobuf glop can get big)
"-Os",
// "-g",
// "-O0",
],
product_variables: {
eng: {
// Enable sanitizer ONLY on eng builds
//sanitize: {
// address: true,
//},
},
},
proto: {
type: "lite",
static: true,
},
stl: "libc++_static",
shared_libs: [
"libstatssocket",
],
apex_available: [
"com.android.os.statsd",
"test_com.android.os.statsd",
],
}
// ==============
// statsd_test
// ==============
cc_test {
name: "statsd_test",
defaults: ["statsd_defaults"],
test_suites: ["device-tests", "mts"],
test_config: "statsd_test.xml",
//TODO(b/153588990): Remove when the build system properly separates
//32bit and 64bit architectures.
compile_multilib: "both",
multilib: {
lib64: {
suffix: "64",
},
lib32: {
suffix: "32",
},
},
cflags: [
"-Wall",
"-Werror",
"-Wno-missing-field-initializers",
"-Wno-unused-variable",
"-Wno-unused-function",
"-Wno-unused-parameter",
],
require_root: true,
srcs: [
// atom_field_options.proto needs field_options.proto, but that is
// not included in libprotobuf-cpp-lite, so compile it here.
":libprotobuf-internal-protos",
":libstats_internal_protos",
"src/shell/shell_data.proto",
"src/stats_log.proto",
"tests/AlarmMonitor_test.cpp",
"tests/anomaly/AlarmTracker_test.cpp",
"tests/anomaly/AnomalyTracker_test.cpp",
"tests/condition/CombinationConditionTracker_test.cpp",
"tests/condition/ConditionTimer_test.cpp",
"tests/condition/SimpleConditionTracker_test.cpp",
"tests/ConfigManager_test.cpp",
"tests/e2e/Alarm_e2e_test.cpp",
"tests/e2e/Anomaly_count_e2e_test.cpp",
"tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
"tests/e2e/Attribution_e2e_test.cpp",
"tests/e2e/ConfigTtl_e2e_test.cpp",
"tests/e2e/CountMetric_e2e_test.cpp",
"tests/e2e/DurationMetric_e2e_test.cpp",
"tests/e2e/GaugeMetric_e2e_pull_test.cpp",
"tests/e2e/GaugeMetric_e2e_push_test.cpp",
"tests/e2e/MetricActivation_e2e_test.cpp",
"tests/e2e/MetricConditionLink_e2e_test.cpp",
"tests/e2e/PartialBucket_e2e_test.cpp",
"tests/e2e/ValueMetric_pull_e2e_test.cpp",
"tests/e2e/WakelockDuration_e2e_test.cpp",
"tests/external/puller_util_test.cpp",
"tests/external/StatsCallbackPuller_test.cpp",
"tests/external/StatsPuller_test.cpp",
"tests/external/StatsPullerManager_test.cpp",
"tests/FieldValue_test.cpp",
"tests/guardrail/StatsdStats_test.cpp",
"tests/HashableDimensionKey_test.cpp",
"tests/indexed_priority_queue_test.cpp",
"tests/log_event/LogEventQueue_test.cpp",
"tests/LogEntryMatcher_test.cpp",
"tests/LogEvent_test.cpp",
"tests/metadata_util_test.cpp",
"tests/metrics/CountMetricProducer_test.cpp",
"tests/metrics/DurationMetricProducer_test.cpp",
"tests/metrics/EventMetricProducer_test.cpp",
"tests/metrics/GaugeMetricProducer_test.cpp",
"tests/metrics/MaxDurationTracker_test.cpp",
"tests/metrics/metrics_test_helper.cpp",
"tests/metrics/OringDurationTracker_test.cpp",
"tests/metrics/ValueMetricProducer_test.cpp",
"tests/MetricsManager_test.cpp",
"tests/shell/ShellSubscriber_test.cpp",
"tests/state/StateTracker_test.cpp",
"tests/statsd_test_util.cpp",
"tests/StatsLogProcessor_test.cpp",
"tests/StatsService_test.cpp",
"tests/storage/StorageManager_test.cpp",
"tests/UidMap_test.cpp",
"tests/utils/MultiConditionTrigger_test.cpp",
],
static_libs: [
"libgmock",
"libplatformprotos",
"libstatslog_statsdtest",
"libstatssocket_private",
],
proto: {
type: "lite",
include_dirs: [
"external/protobuf/src",
"frameworks/proto_logging/stats",
],
},
}
//#############################
// statsd micro benchmark
//#############################
cc_benchmark {
name: "statsd_benchmark",
defaults: ["statsd_defaults"],
srcs: [
// atom_field_options.proto needs field_options.proto, but that is
// not included in libprotobuf-cpp-lite, so compile it here.
":libprotobuf-internal-protos",
":libstats_internal_protos",
"benchmark/duration_metric_benchmark.cpp",
"benchmark/filter_value_benchmark.cpp",
"benchmark/get_dimensions_for_condition_benchmark.cpp",
"benchmark/hello_world_benchmark.cpp",
"benchmark/log_event_benchmark.cpp",
"benchmark/main.cpp",
"benchmark/metric_util.cpp",
"benchmark/stats_write_benchmark.cpp",
"src/stats_log.proto",
],
proto: {
type: "lite",
include_dirs: [
"external/protobuf/src",
"frameworks/proto_logging/stats",
],
},
cflags: [
"-Wall",
"-Werror",
"-Wno-unused-parameter",
"-Wno-unused-variable",
"-Wno-unused-function",
// Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
"-Wno-varargs",
],
static_libs: [
"libplatformprotos",
"libstatssocket_private",
],
shared_libs: [
"libgtest_prod",
"libprotobuf-cpp-lite",
"libstatslog",
],
}
// ==== java proto device library (for test only) ==============================
java_library {
name: "statsdprotolite",
sdk_version: "core_current",
proto: {
type: "lite",
include_dirs: [
"external/protobuf/src",
"frameworks/proto_logging/stats",
],
},
srcs: [
":libstats_atoms_proto",
"src/shell/shell_config.proto",
"src/shell/shell_data.proto",
"src/stats_log.proto",
"src/statsd_config.proto",
],
static_libs: [
"platformprotoslite",
],
// Protos have lots of MissingOverride and similar.
errorprone: {
javacflags: ["-XepDisableAllChecks"],
},
}
// Filegroup for statsd config proto definition.
filegroup {
name: "statsd-config-proto-def",
srcs: ["src/statsd_config.proto"],
}

View File

@@ -1 +0,0 @@
baligh@google.com

View File

@@ -1,7 +0,0 @@
{
"presubmit" : [
{
"name" : "statsd_test"
}
]
}

View File

@@ -1,314 +0,0 @@
/*
* 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<std::unique_ptr<LogEvent>> events;
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 11,
android::view::DISPLAY_STATE_OFF));
events.push_back(
CreateScreenStateChangedEvent(bucketStartTimeNs + 40, android::view::DISPLAY_STATE_ON));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 102,
android::view::DISPLAY_STATE_OFF));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
android::view::DISPLAY_STATE_ON));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 650,
android::view::DISPLAY_STATE_OFF));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
android::view::DISPLAY_STATE_ON));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 640,
android::view::DISPLAY_STATE_OFF));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 650,
android::view::DISPLAY_STATE_ON));
vector<int> attributionUids1 = {9999};
vector<string> attributionTags1 = {""};
events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 2, attributionUids1,
attributionTags1, "job0"));
events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1,
attributionTags1, "job0"));
events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids1,
attributionTags1, "job2"));
events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids1,
attributionTags1, "job2"));
vector<int> attributionUids2 = {8888};
events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2,
attributionTags1, "job2"));
events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850,
attributionUids2, attributionTags1, "job2"));
events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 600,
attributionUids2, attributionTags1, "job1"));
events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
attributionUids2, attributionTags1, "job1"));
vector<int> attributionUids3 = {111, 222, 222};
vector<string> attributionTags3 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 10, attributionUids3,
attributionTags3, "ReadEmail"));
events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 50, attributionUids3, attributionTags3,
"ReadEmail"));
events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200, attributionUids3,
attributionTags3, "ReadEmail"));
events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids3,
attributionTags3, "ReadEmail"));
events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids3,
attributionTags3, "ReadDoc"));
events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids3,
attributionTags3, "ReadDoc"));
vector<int> attributionUids4 = {333, 222, 555};
vector<string> attributionTags4 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 401, attributionUids4,
attributionTags4, "ReadEmail"));
events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids4,
attributionTags4, "ReadEmail"));
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<std::unique_ptr<LogEvent>> events;
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 55,
android::view::DISPLAY_STATE_OFF));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 120,
android::view::DISPLAY_STATE_ON));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 121,
android::view::DISPLAY_STATE_OFF));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
android::view::DISPLAY_STATE_ON));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 501,
android::view::DISPLAY_STATE_OFF));
events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
android::view::DISPLAY_STATE_ON));
vector<int> attributionUids1 = {111};
vector<string> attributionTags1 = {"App1"};
events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 1, attributionUids1,
attributionTags1, "job1"));
events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1,
attributionTags1, "job1"));
vector<int> attributionUids2 = {333};
vector<string> attributionTags2 = {"App2"};
events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids2,
attributionTags2, "job2"));
events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids2,
attributionTags2, "job2"));
events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2,
attributionTags2, "job2"));
events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850,
attributionUids2, attributionTags2, "job2"));
vector<int> attributionUids3 = {444};
vector<string> attributionTags3 = {"App3"};
events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs - 2,
attributionUids3, attributionTags3, "job3"));
events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
attributionUids3, attributionTags3, "job3"));
vector<int> attributionUids4 = {111, 222, 222};
vector<string> attributionTags4 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids4,
attributionTags4, "ReadEmail"));
events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 110, attributionUids4, attributionTags4,
"ReadEmail"));
vector<int> attributionUids5 = {333, 222, 555};
vector<string> attributionTags5 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 300, attributionUids5,
attributionTags5, "ReadEmail"));
events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids5,
attributionTags5, "ReadEmail"));
events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids5,
attributionTags5, "ReadDoc"));
events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids5,
attributionTags5, "ReadDoc"));
vector<int> attributionUids6 = {444, 222, 555};
vector<string> attributionTags6 = {"App3", "GMSCoreModule1", "GMSCoreModule2"};
events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 550, attributionUids6,
attributionTags6, "ReadDoc"));
events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 800, attributionUids6, attributionTags6,
"ReadDoc"));
events.push_back(CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids6,
attributionTags6, "ReadDoc"));
events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids6,
attributionTags6, "ReadDoc"));
sortLogEventsByTimestamp(&events);
while (state.KeepRunning()) {
auto processor = CreateStatsLogProcessor(
bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
}
}
BENCHMARK(BM_DurationMetricLink);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,72 +0,0 @@
/*
* 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 "FieldValue.h"
#include "HashableDimensionKey.h"
#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
#include "metric_util.h"
#include "stats_event.h"
#include "stats_log_util.h"
namespace android {
namespace os {
namespace statsd {
using std::vector;
static void createLogEventAndMatcher(LogEvent* event, FieldMatcher* field_matcher) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, 1);
AStatsEvent_overwriteTimestamp(statsEvent, 100000);
std::vector<int> attributionUids = {100, 100};
std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeFloat(statsEvent, 3.2f);
AStatsEvent_writeString(statsEvent, "LOCATION");
AStatsEvent_writeInt64(statsEvent, 990);
parseStatsEventToLogEvent(statsEvent, event);
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(/*uid=*/0, /*pid=*/0);
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_FilterValue);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,77 +0,0 @@
/*
* 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 "FieldValue.h"
#include "HashableDimensionKey.h"
#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
#include "metric_util.h"
#include "stats_event.h"
#include "stats_log_util.h"
namespace android {
namespace os {
namespace statsd {
using std::vector;
static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, 1);
AStatsEvent_overwriteTimestamp(statsEvent, 100000);
std::vector<int> attributionUids = {100, 100};
std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeFloat(statsEvent, 3.2f);
AStatsEvent_writeString(statsEvent, "LOCATION");
AStatsEvent_writeInt64(statsEvent, 990);
parseStatsEventToLogEvent(statsEvent, event);
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(/*uid=*/0, /*pid=*/0);
createLogEventAndLink(&event, &link);
while (state.KeepRunning()) {
HashableDimensionKey output;
getDimensionForCondition(event.getValues(), link, &output);
}
}
BENCHMARK(BM_GetDimensionInCondition);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,29 +0,0 @@
/*
* 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 "benchmark/benchmark.h"
static void BM_StringCreation(benchmark::State& state) {
while (state.KeepRunning()) std::string empty_string;
}
// Register the function as a benchmark
BENCHMARK(BM_StringCreation);
// Define another benchmark
static void BM_StringCopy(benchmark::State& state) {
std::string x = "hello";
while (state.KeepRunning()) std::string copy(x);
}
BENCHMARK(BM_StringCopy);

View File

@@ -1,50 +0,0 @@
/*
* 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 "logd/LogEvent.h"
#include "stats_event.h"
namespace android {
namespace os {
namespace statsd {
static size_t createAndParseStatsEvent(uint8_t* msg) {
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, 100);
AStatsEvent_writeInt32(event, 2);
AStatsEvent_writeFloat(event, 2.0);
AStatsEvent_build(event);
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
memcpy(msg, buf, size);
return size;
}
static void BM_LogEventCreation(benchmark::State& state) {
uint8_t msg[LOGGER_ENTRY_MAX_PAYLOAD];
size_t size = createAndParseStatsEvent(msg);
while (state.KeepRunning()) {
LogEvent event(/*uid=*/ 1000, /*pid=*/ 1001);
benchmark::DoNotOptimize(event.parseBuffer(msg, size));
}
}
BENCHMARK(BM_LogEventCreation);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,19 +0,0 @@
/*
* 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 <benchmark/benchmark.h>
BENCHMARK_MAIN();

View File

@@ -1,379 +0,0 @@
// 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"
#include "stats_event.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::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::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(state);
return atom_matcher;
}
AtomMatcher CreateMoveToBackgroundAtomMatcher() {
return CreateActivityForegroundStateChangedAtomMatcher(
"MoveToBackground", ActivityForegroundStateChanged::BACKGROUND);
}
AtomMatcher CreateMoveToForegroundAtomMatcher() {
return CreateActivityForegroundStateChangedAtomMatcher(
"MoveToForeground", ActivityForegroundStateChanged::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;
}
void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
const vector<string>& attributionTags) {
vector<const char*> cTags(attributionTags.size());
for (int i = 0; i < cTags.size(); i++) {
cTags[i] = attributionTags[i].c_str();
}
AStatsEvent_writeAttributionChain(statsEvent,
reinterpret_cast<const uint32_t*>(attributionUids.data()),
cTags.data(), attributionUids.size());
}
void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
AStatsEvent_build(statsEvent);
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
logEvent->parseBuffer(buf, size);
AStatsEvent_release(statsEvent);
}
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
uint64_t timestampNs, const android::view::DisplayStateEnum state) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, state);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
const vector<int>& attributionUids, const vector<string>& attributionTags,
const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, jobName.c_str());
AStatsEvent_writeInt32(statsEvent, state);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
const vector<int>& attributionUids,
const vector<string>& attributionTags,
const string& jobName) {
return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
ScheduledJobStateChanged::STARTED, timestampNs);
}
// Create log event when scheduled job finishes.
std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
const vector<int>& attributionUids,
const vector<string>& attributionTags,
const string& jobName) {
return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
ScheduledJobStateChanged::FINISHED, timestampNs);
}
std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
const vector<int>& attributionUids,
const vector<string>& attributionTags,
const string& name,
const SyncStateChanged::State state) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, name.c_str());
AStatsEvent_writeInt32(statsEvent, state);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
const vector<int>& attributionUids,
const vector<string>& attributionTags,
const string& name) {
return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
SyncStateChanged::ON);
}
std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
const vector<int>& attributionUids,
const vector<string>& attributionTags,
const string& name) {
return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
SyncStateChanged::OFF);
}
sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
const ConfigKey& key) {
sp<UidMap> uidMap = new UidMap();
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
sp<StatsLogProcessor> processor =
new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; },
[](const int&, const vector<int64_t>&) { return true; });
processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
return processor;
}
void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
std::sort(events->begin(), events->end(),
[](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs();
});
}
int64_t StringToId(const string& str) {
return static_cast<int64_t>(std::hash<std::string>()(str));
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,140 +0,0 @@
// 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 "stats_event.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);
void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
const vector<string>& attributionTags);
void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
// Create log event for screen state changed.
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
uint64_t timestampNs, const android::view::DisplayStateEnum state);
// Create log event when scheduled job starts.
std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
const vector<int>& attributionUids,
const vector<string>& attributionTags,
const string& jobName);
// Create log event when scheduled job finishes.
std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
const vector<int>& attributionUids,
const vector<string>& attributionTags,
const string& jobName);
// Create log event when the app sync starts.
std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
const vector<int>& attributionUids,
const vector<string>& attributionTags,
const string& name);
// Create log event when the app sync ends.
std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
const vector<int>& attributionUids,
const vector<string>& attributionTags,
const string& name);
// Create a statsd log event processor upon the start time in seconds, config and key.
sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
const ConfigKey& key);
// Util function to sort the log events by timestamp.
void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
int64_t StringToId(const string& str);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,40 +0,0 @@
/*
* 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 "benchmark/benchmark.h"
#include <statslog.h>
namespace android {
namespace os {
namespace statsd {
static void BM_StatsWrite(benchmark::State& state) {
const char* reason = "test";
int64_t boot_end_time = 1234567;
int64_t total_duration = 100;
int64_t bootloader_duration = 10;
int64_t time_since_last_boot = 99999999;
while (state.KeepRunning()) {
android::util::stats_write(
android::util::BOOT_SEQUENCE_REPORTED, reason, reason,
boot_end_time, total_duration, bootloader_duration, time_since_last_boot);
total_duration++;
}
}
BENCHMARK(BM_StatsWrite);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,474 +0,0 @@
/*
* 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.
*/
#define DEBUG false
#include "Log.h"
#include "FieldValue.h"
#include "HashableDimensionKey.h"
#include "math.h"
namespace android {
namespace os {
namespace statsd {
int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth) {
int32_t field = 0;
for (int32_t i = 0; i <= depth; i++) {
int32_t shiftBits = 8 * (kMaxLogDepth - i);
field |= (pos[i] << shiftBits);
}
if (includeDepth) {
field |= (depth << 24);
}
return field;
}
int32_t encodeMatcherMask(int32_t mask[], int32_t depth) {
return getEncodedField(mask, depth, false) | 0xff000000;
}
bool Field::matches(const Matcher& matcher) const {
if (mTag != matcher.mMatcher.getTag()) {
return false;
}
if ((mField & matcher.mMask) == matcher.mMatcher.getField()) {
return true;
}
if (matcher.hasAllPositionMatcher() &&
(mField & (matcher.mMask & kClearAllPositionMatcherMask)) == matcher.mMatcher.getField()) {
return true;
}
return false;
}
void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* pos, int* mask,
std::vector<Matcher>* output) {
if (depth > kMaxLogDepth) {
ALOGE("depth > 2");
return;
}
pos[depth] = matcher.field();
mask[depth] = 0x7f;
if (matcher.has_position()) {
depth++;
if (depth > 2) {
return;
}
switch (matcher.position()) {
case Position::ALL:
pos[depth] = 0x00;
mask[depth] = 0x7f;
break;
case Position::ANY:
pos[depth] = 0;
mask[depth] = 0;
break;
case Position::FIRST:
pos[depth] = 1;
mask[depth] = 0x7f;
break;
case Position::LAST:
pos[depth] = 0x80;
mask[depth] = 0x80;
break;
case Position::POSITION_UNKNOWN:
pos[depth] = 0;
mask[depth] = 0;
break;
}
}
if (matcher.child_size() == 0) {
output->push_back(Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth)));
} else {
for (const auto& child : matcher.child()) {
translateFieldMatcher(tag, child, depth + 1, pos, mask, output);
}
}
}
void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output) {
int pos[] = {1, 1, 1};
int mask[] = {0x7f, 0x7f, 0x7f};
int tag = matcher.field();
for (const auto& child : matcher.child()) {
translateFieldMatcher(tag, child, 0, pos, mask, output);
}
}
bool isAttributionUidField(const FieldValue& value) {
return isAttributionUidField(value.mField, value.mValue);
}
int32_t getUidIfExists(const FieldValue& value) {
// the field is uid field if the field is the uid field in attribution node
// or annotated as such in the atom
bool isUid = isAttributionUidField(value) || isUidField(value);
return isUid ? value.mValue.int_value : -1;
}
bool isAttributionUidField(const Field& field, const Value& value) {
int f = field.getField() & 0xff007f;
if (f == 0x10001 && value.getType() == INT) {
return true;
}
return false;
}
bool isUidField(const FieldValue& fieldValue) {
return fieldValue.mAnnotations.isUidField();
}
Value::Value(const Value& from) {
type = from.getType();
switch (type) {
case INT:
int_value = from.int_value;
break;
case LONG:
long_value = from.long_value;
break;
case FLOAT:
float_value = from.float_value;
break;
case DOUBLE:
double_value = from.double_value;
break;
case STRING:
str_value = from.str_value;
break;
case STORAGE:
storage_value = from.storage_value;
break;
default:
break;
}
}
std::string Value::toString() const {
switch (type) {
case INT:
return std::to_string(int_value) + "[I]";
case LONG:
return std::to_string(long_value) + "[L]";
case FLOAT:
return std::to_string(float_value) + "[F]";
case DOUBLE:
return std::to_string(double_value) + "[D]";
case STRING:
return str_value + "[S]";
case STORAGE:
return "bytes of size " + std::to_string(storage_value.size()) + "[ST]";
default:
return "[UNKNOWN]";
}
}
bool Value::isZero() const {
switch (type) {
case INT:
return int_value == 0;
case LONG:
return long_value == 0;
case FLOAT:
return fabs(float_value) <= std::numeric_limits<float>::epsilon();
case DOUBLE:
return fabs(double_value) <= std::numeric_limits<double>::epsilon();
case STRING:
return str_value.size() == 0;
case STORAGE:
return storage_value.size() == 0;
default:
return false;
}
}
bool Value::operator==(const Value& that) const {
if (type != that.getType()) return false;
switch (type) {
case INT:
return int_value == that.int_value;
case LONG:
return long_value == that.long_value;
case FLOAT:
return float_value == that.float_value;
case DOUBLE:
return double_value == that.double_value;
case STRING:
return str_value == that.str_value;
case STORAGE:
return storage_value == that.storage_value;
default:
return false;
}
}
bool Value::operator!=(const Value& that) const {
if (type != that.getType()) return true;
switch (type) {
case INT:
return int_value != that.int_value;
case LONG:
return long_value != that.long_value;
case FLOAT:
return float_value != that.float_value;
case DOUBLE:
return double_value != that.double_value;
case STRING:
return str_value != that.str_value;
case STORAGE:
return storage_value != that.storage_value;
default:
return false;
}
}
bool Value::operator<(const Value& that) const {
if (type != that.getType()) return type < that.getType();
switch (type) {
case INT:
return int_value < that.int_value;
case LONG:
return long_value < that.long_value;
case FLOAT:
return float_value < that.float_value;
case DOUBLE:
return double_value < that.double_value;
case STRING:
return str_value < that.str_value;
case STORAGE:
return storage_value < that.storage_value;
default:
return false;
}
}
bool Value::operator>(const Value& that) const {
if (type != that.getType()) return type > that.getType();
switch (type) {
case INT:
return int_value > that.int_value;
case LONG:
return long_value > that.long_value;
case FLOAT:
return float_value > that.float_value;
case DOUBLE:
return double_value > that.double_value;
case STRING:
return str_value > that.str_value;
case STORAGE:
return storage_value > that.storage_value;
default:
return false;
}
}
bool Value::operator>=(const Value& that) const {
if (type != that.getType()) return type >= that.getType();
switch (type) {
case INT:
return int_value >= that.int_value;
case LONG:
return long_value >= that.long_value;
case FLOAT:
return float_value >= that.float_value;
case DOUBLE:
return double_value >= that.double_value;
case STRING:
return str_value >= that.str_value;
case STORAGE:
return storage_value >= that.storage_value;
default:
return false;
}
}
Value Value::operator-(const Value& that) const {
Value v;
if (type != that.type) {
ALOGE("Can't operate on different value types, %d, %d", type, that.type);
return v;
}
if (type == STRING) {
ALOGE("Can't operate on string value type");
return v;
}
if (type == STORAGE) {
ALOGE("Can't operate on storage value type");
return v;
}
switch (type) {
case INT:
v.setInt(int_value - that.int_value);
break;
case LONG:
v.setLong(long_value - that.long_value);
break;
case FLOAT:
v.setFloat(float_value - that.float_value);
break;
case DOUBLE:
v.setDouble(double_value - that.double_value);
break;
default:
break;
}
return v;
}
Value& Value::operator=(const Value& that) {
type = that.type;
switch (type) {
case INT:
int_value = that.int_value;
break;
case LONG:
long_value = that.long_value;
break;
case FLOAT:
float_value = that.float_value;
break;
case DOUBLE:
double_value = that.double_value;
break;
case STRING:
str_value = that.str_value;
break;
case STORAGE:
storage_value = that.storage_value;
break;
default:
break;
}
return *this;
}
Value& Value::operator+=(const Value& that) {
if (type != that.type) {
ALOGE("Can't operate on different value types, %d, %d", type, that.type);
return *this;
}
if (type == STRING) {
ALOGE("Can't operate on string value type");
return *this;
}
if (type == STORAGE) {
ALOGE("Can't operate on storage value type");
return *this;
}
switch (type) {
case INT:
int_value += that.int_value;
break;
case LONG:
long_value += that.long_value;
break;
case FLOAT:
float_value += that.float_value;
break;
case DOUBLE:
double_value += that.double_value;
break;
default:
break;
}
return *this;
}
double Value::getDouble() const {
switch (type) {
case INT:
return int_value;
case LONG:
return long_value;
case FLOAT:
return float_value;
case DOUBLE:
return double_value;
default:
return 0;
}
}
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 subsetDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b) {
if (dimension_a.size() > dimension_b.size()) {
return false;
}
for (size_t i = 0; i < dimension_a.size(); ++i) {
bool found = false;
for (size_t j = 0; j < dimension_b.size(); ++j) {
if (dimension_a[i] == dimension_b[j]) {
found = true;
}
}
if (!found) {
return false;
}
}
return true;
}
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;
}
bool HasPositionALL(const FieldMatcher& matcher) {
if (matcher.has_position() && matcher.position() == Position::ALL) {
return true;
}
for (const auto& child : matcher.child()) {
if (HasPositionALL(child)) {
return true;
}
}
return false;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,462 +0,0 @@
/*
* 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.
*/
#pragma once
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "annotations.h"
namespace android {
namespace os {
namespace statsd {
class HashableDimensionKey;
struct Matcher;
struct Field;
struct FieldValue;
const int32_t kMaxLogDepth = 2;
const int32_t kLastBitMask = 0x80;
const int32_t kClearLastBitDeco = 0x7f;
const int32_t kClearAllPositionMatcherMask = 0xffff00ff;
enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING, STORAGE };
int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth);
int32_t encodeMatcherMask(int32_t mask[], int32_t depth);
// Get the encoded field for a leaf with a [field] number at depth 0;
inline int32_t getSimpleField(size_t field) {
return ((int32_t)field << 8 * 2);
}
/**
* Field is a wrapper class for 2 integers that represents the field of a log element in its Atom
* proto.
* [mTag]: the atom id.
* [mField]: encoded path from the root (atom) to leaf.
*
* For example:
* WakeLockStateChanged {
* repeated AttributionNode = 1;
* int state = 2;
* string tag = 3;
* }
* Read from logd, the items are structured as below:
* [[[1000, "tag"], [2000, "tag2"],], 2,"hello"]
*
* When we read through the list, we will encode each field in a 32bit integer.
* 8bit segments |--------|--------|--------|--------|
* Depth field0 [L]field1 [L]field1
*
* The first 8 bits are the depth of the field. for example, the uid 1000 has depth 2.
* The following 3 8-bit are for the item's position at each level.
* The first bit of each 8bits field is reserved to mark if the item is the last item at that level
* this is to make matching easier later.
*
* The above wakelock event is translated into FieldValue pairs.
* 0x02010101->1000
* 0x02010182->tag
* 0x02018201->2000
* 0x02018282->tag2
* 0x00020000->2
* 0x00030000->"hello"
*
* This encoding is the building block for the later operations.
* Please see the definition for Matcher below to see how the matching is done.
*/
struct Field {
private:
int32_t mTag;
int32_t mField;
public:
Field() {}
Field(int32_t tag, int32_t pos[], int32_t depth) : mTag(tag) {
mField = getEncodedField(pos, depth, true);
}
Field(const Field& from) : mTag(from.getTag()), mField(from.getField()) {
}
Field(int32_t tag, int32_t field) : mTag(tag), mField(field){};
inline void setField(int32_t field) {
mField = field;
}
inline void setTag(int32_t tag) {
mTag = tag;
}
inline void decorateLastPos(int32_t depth) {
int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth);
mField |= mask;
}
inline int32_t getTag() const {
return mTag;
}
inline int32_t getDepth() const {
return (mField >> 24);
}
inline int32_t getPath(int32_t depth) const {
if (depth > 2 || depth < 0) return 0;
int32_t field = (mField & 0x00ffffff);
int32_t mask = 0xffffffff;
return (field & (mask << 8 * (kMaxLogDepth - depth)));
}
inline int32_t getPrefix(int32_t depth) const {
if (depth == 0) return 0;
return getPath(depth - 1);
}
inline int32_t getField() const {
return mField;
}
inline int32_t getRawPosAtDepth(int32_t depth) const {
int32_t field = (mField & 0x00ffffff);
int32_t shift = 8 * (kMaxLogDepth - depth);
int32_t mask = 0xff << shift;
return (field & mask) >> shift;
}
inline int32_t getPosAtDepth(int32_t depth) const {
return getRawPosAtDepth(depth) & kClearLastBitDeco;
}
// Check if the first bit of the 8-bit segment for depth is 1
inline bool isLastPos(int32_t depth) const {
int32_t field = (mField & 0x00ffffff);
int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth);
return (field & mask) != 0;
}
// if the 8-bit segment is all 0's
inline bool isAnyPosMatcher(int32_t depth) const {
return getDepth() >= depth && getRawPosAtDepth(depth) == 0;
}
// if the 8bit is 0x80 (1000 0000)
inline bool isLastPosMatcher(int32_t depth) const {
return getDepth() >= depth && getRawPosAtDepth(depth) == kLastBitMask;
}
inline bool operator==(const Field& that) const {
return mTag == that.getTag() && mField == that.getField();
};
inline bool operator!=(const Field& that) const {
return mTag != that.getTag() || mField != that.getField();
};
bool operator<(const Field& that) const {
if (mTag != that.getTag()) {
return mTag < that.getTag();
}
if (mField != that.getField()) {
return mField < that.getField();
}
return false;
}
bool matches(const Matcher& that) const;
};
/**
* Matcher represents a leaf matcher in the FieldMatcher in statsd_config.
*
* It contains all information needed to match one or more leaf node.
* All information is encoded in a Field(2 ints) and a bit mask(1 int).
*
* For example, to match the first/any/last uid field in attribution chain in Atom 10,
* we have the following FieldMatcher in statsd_config
* FieldMatcher {
* field:10
* FieldMatcher {
* field:1
* position: any/last/first
* FieldMatcher {
* field:1
* }
* }
* }
*
* We translate the FieldMatcher into a Field, and mask
* First: [Matcher Field] 0x02010101 [Mask]0xff7f7f7f
* Last: [Matcher Field] 0x02018001 [Mask]0xff7f807f
* Any: [Matcher Field] 0x02010001 [Mask]0xff7f007f
* All: [Matcher Field] 0x02010001 [Mask]0xff7f7f7f
*
* [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if
* the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are
* equal. Nothing can beat the performance of this matching algorithm.
*
* TODO(b/110561213): ADD EXAMPLE HERE.
*/
struct Matcher {
Matcher(const Field& matcher, int32_t mask) : mMatcher(matcher), mMask(mask){};
const Field mMatcher;
const int32_t mMask;
inline const Field& getMatcher() const {
return mMatcher;
}
inline int32_t getMask() const {
return mMask;
}
inline int32_t getRawMaskAtDepth(int32_t depth) const {
int32_t field = (mMask & 0x00ffffff);
int32_t shift = 8 * (kMaxLogDepth - depth);
int32_t mask = 0xff << shift;
return (field & mask) >> shift;
}
bool hasAllPositionMatcher() const {
return mMatcher.getDepth() == 2 && getRawMaskAtDepth(1) == 0x7f;
}
bool hasAnyPositionMatcher(int* prefix) const {
if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(1) == 0) {
(*prefix) = mMatcher.getPrefix(1);
return true;
}
return false;
}
inline bool operator!=(const Matcher& that) const {
return mMatcher != that.getMatcher() || mMask != that.getMask();
}
inline bool operator==(const Matcher& that) const {
return mMatcher == that.mMatcher && mMask == that.mMask;
}
};
inline Matcher getSimpleMatcher(int32_t tag, size_t field) {
return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000);
}
inline Matcher getFirstUidMatcher(int32_t atomId) {
int32_t pos[] = {1, 1, 1};
return Matcher(Field(atomId, pos, 2), 0xff7f7f7f);
}
/**
* A wrapper for a union type to contain multiple types of values.
*
*/
struct Value {
Value() : type(UNKNOWN) {}
Value(int32_t v) {
int_value = v;
type = INT;
}
Value(int64_t v) {
long_value = v;
type = LONG;
}
Value(float v) {
float_value = v;
type = FLOAT;
}
Value(double v) {
double_value = v;
type = DOUBLE;
}
Value(const std::string& v) {
str_value = v;
type = STRING;
}
Value(const std::vector<uint8_t>& v) {
storage_value = v;
type = STORAGE;
}
void setInt(int32_t v) {
int_value = v;
type = INT;
}
void setLong(int64_t v) {
long_value = v;
type = LONG;
}
void setFloat(float v) {
float_value = v;
type = FLOAT;
}
void setDouble(double v) {
double_value = v;
type = DOUBLE;
}
union {
int32_t int_value;
int64_t long_value;
float float_value;
double double_value;
};
std::string str_value;
std::vector<uint8_t> storage_value;
Type type;
std::string toString() const;
bool isZero() const;
Type getType() const {
return type;
}
double getDouble() const;
Value(const Value& from);
bool operator==(const Value& that) const;
bool operator!=(const Value& that) const;
bool operator<(const Value& that) const;
bool operator>(const Value& that) const;
bool operator>=(const Value& that) const;
Value operator-(const Value& that) const;
Value& operator+=(const Value& that);
Value& operator=(const Value& that);
};
class Annotations {
public:
Annotations() {
setNested(true); // Nested = true by default
}
// This enum stores where particular annotations can be found in the
// bitmask. Note that these pos do not correspond to annotation ids.
enum {
NESTED_POS = 0x0,
PRIMARY_POS = 0x1,
EXCLUSIVE_POS = 0x2,
UID_POS = 0x3
};
inline void setNested(bool nested) { setBitmaskAtPos(NESTED_POS, nested); }
inline void setPrimaryField(bool primary) { setBitmaskAtPos(PRIMARY_POS, primary); }
inline void setExclusiveState(bool exclusive) { setBitmaskAtPos(EXCLUSIVE_POS, exclusive); }
inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); }
// Default value = false
inline bool isNested() const { return getValueFromBitmask(NESTED_POS); }
// Default value = false
inline bool isPrimaryField() const { return getValueFromBitmask(PRIMARY_POS); }
// Default value = false
inline bool isExclusiveState() const { return getValueFromBitmask(EXCLUSIVE_POS); }
// Default value = false
inline bool isUidField() const { return getValueFromBitmask(UID_POS); }
private:
inline void setBitmaskAtPos(int pos, bool value) {
mBooleanBitmask &= ~(1 << pos); // clear
mBooleanBitmask |= (value << pos); // set
}
inline bool getValueFromBitmask(int pos) const {
return (mBooleanBitmask >> pos) & 0x1;
}
// This is a bitmask over all annotations stored in boolean form. Because
// there are only 4 booleans, just one byte is required.
uint8_t mBooleanBitmask = 0;
};
/**
* Represents a log item, or a dimension item (They are essentially the same).
*/
struct FieldValue {
FieldValue() {}
FieldValue(const Field& field, const Value& value) : mField(field), mValue(value) {
}
bool operator==(const FieldValue& that) const {
return mField == that.mField && mValue == that.mValue;
}
bool operator!=(const FieldValue& that) const {
return mField != that.mField || mValue != that.mValue;
}
bool operator<(const FieldValue& that) const {
if (mField != that.mField) {
return mField < that.mField;
}
if (mValue != that.mValue) {
return mValue < that.mValue;
}
return false;
}
Field mField;
Value mValue;
Annotations mAnnotations;
};
bool HasPositionANY(const FieldMatcher& matcher);
bool HasPositionALL(const FieldMatcher& matcher);
bool isAttributionUidField(const FieldValue& value);
/* returns uid if the field is uid field, or -1 if the field is not a uid field */
int getUidIfExists(const FieldValue& value);
void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
bool isAttributionUidField(const Field& field, const Value& value);
bool isUidField(const FieldValue& fieldValue);
bool equalDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b);
// Returns true if dimension_a is a subset of dimension_b.
bool subsetDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,381 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "HashableDimensionKey.h"
#include "FieldValue.h"
namespace android {
namespace os {
namespace statsd {
using std::string;
using std::vector;
using android::base::StringPrintf;
// These constants must be kept in sync with those in StatsDimensionsValue.java
const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3;
const static int STATS_DIMENSIONS_VALUE_LONG_TYPE = 4;
// const static int STATS_DIMENSIONS_VALUE_BOOL_TYPE = 5; (commented out because
// unused -- statsd does not correctly support bool types)
const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6;
const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7;
/**
* Recursive helper function that populates a parent StatsDimensionsValueParcel
* with children StatsDimensionsValueParcels.
*
* \param parent parcel that will be populated with children
* \param childDepth depth of children FieldValues
* \param childPrefix expected FieldValue prefix of children
* \param dims vector of FieldValues stored by HashableDimensionKey
* \param index position in dims to start reading children from
*/
static void populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel& parent,
int childDepth, int childPrefix,
const vector<FieldValue>& dims,
size_t& index) {
if (childDepth > 2) {
ALOGE("Depth > 2 not supported by StatsDimensionsValueParcel.");
return;
}
while (index < dims.size()) {
const FieldValue& dim = dims[index];
int fieldDepth = dim.mField.getDepth();
int fieldPrefix = dim.mField.getPrefix(childDepth);
StatsDimensionsValueParcel child;
child.field = dim.mField.getPosAtDepth(childDepth);
if (fieldDepth == childDepth && fieldPrefix == childPrefix) {
switch (dim.mValue.getType()) {
case INT:
child.valueType = STATS_DIMENSIONS_VALUE_INT_TYPE;
child.intValue = dim.mValue.int_value;
break;
case LONG:
child.valueType = STATS_DIMENSIONS_VALUE_LONG_TYPE;
child.longValue = dim.mValue.long_value;
break;
case FLOAT:
child.valueType = STATS_DIMENSIONS_VALUE_FLOAT_TYPE;
child.floatValue = dim.mValue.float_value;
break;
case STRING:
child.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE;
child.stringValue = dim.mValue.str_value;
break;
default:
ALOGE("Encountered FieldValue with unsupported value type.");
break;
}
index++;
parent.tupleValue.push_back(child);
} else if (fieldDepth > childDepth && fieldPrefix == childPrefix) {
// This FieldValue is not a child of the current parent, but it is
// an indirect descendant. Thus, create a direct child of TUPLE_TYPE
// and recurse to parcel the indirect descendants.
child.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
populateStatsDimensionsValueParcelChildren(child, childDepth + 1,
dim.mField.getPrefix(childDepth + 1), dims,
index);
parent.tupleValue.push_back(child);
} else {
return;
}
}
}
StatsDimensionsValueParcel HashableDimensionKey::toStatsDimensionsValueParcel() const {
StatsDimensionsValueParcel root;
if (mValues.size() == 0) {
return root;
}
root.field = mValues[0].mField.getTag();
root.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
// Children of the root correspond to top-level (depth = 0) FieldValues.
int childDepth = 0;
int childPrefix = 0;
size_t index = 0;
populateStatsDimensionsValueParcelChildren(root, childDepth, childPrefix, mValues, index);
return root;
}
android::hash_t hashDimension(const HashableDimensionKey& value) {
android::hash_t hash = 0;
for (const auto& fieldValue : value.getValues()) {
hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getField()));
hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getTag()));
hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mValue.getType()));
switch (fieldValue.mValue.getType()) {
case INT:
hash = android::JenkinsHashMix(hash,
android::hash_type(fieldValue.mValue.int_value));
break;
case LONG:
hash = android::JenkinsHashMix(hash,
android::hash_type(fieldValue.mValue.long_value));
break;
case STRING:
hash = android::JenkinsHashMix(hash, static_cast<uint32_t>(std::hash<std::string>()(
fieldValue.mValue.str_value)));
break;
case FLOAT: {
hash = android::JenkinsHashMix(hash,
android::hash_type(fieldValue.mValue.float_value));
break;
}
default:
break;
}
}
return JenkinsHashWhiten(hash);
}
bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values,
FieldValue* output) {
for (const auto& value : values) {
if (value.mField.matches(matcherField)) {
(*output) = value;
return true;
}
}
return false;
}
bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
HashableDimensionKey* output) {
size_t num_matches = 0;
for (const auto& value : values) {
for (size_t i = 0; i < matcherFields.size(); ++i) {
const auto& matcher = matcherFields[i];
if (value.mField.matches(matcher)) {
output->addValue(value);
output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
output->mutableValue(num_matches)->mField.setField(
value.mField.getField() & matcher.mMask);
num_matches++;
}
}
}
return num_matches > 0;
}
bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output) {
size_t num_matches = 0;
const int32_t simpleFieldMask = 0xff7f0000;
const int32_t attributionUidFieldMask = 0xff7f7f7f;
for (const auto& value : values) {
if (value.mAnnotations.isPrimaryField()) {
output->addValue(value);
output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
const int32_t mask =
isAttributionUidField(value) ? attributionUidFieldMask : simpleFieldMask;
output->mutableValue(num_matches)->mField.setField(value.mField.getField() & mask);
num_matches++;
}
}
return num_matches > 0;
}
void filterGaugeValues(const std::vector<Matcher>& matcherFields,
const std::vector<FieldValue>& values, std::vector<FieldValue>* output) {
for (const auto& field : matcherFields) {
for (const auto& value : values) {
if (value.mField.matches(field)) {
output->push_back(value);
}
}
}
}
void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
const Metric2Condition& links,
HashableDimensionKey* conditionDimension) {
// Get the dimension first by using dimension from what.
filterValues(links.metricFields, eventValues, conditionDimension);
size_t count = conditionDimension->getValues().size();
if (count != links.conditionFields.size()) {
return;
}
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());
}
}
void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link,
HashableDimensionKey* statePrimaryKey) {
// First, get the dimension from the event using the "what" fields from the
// MetricStateLinks.
filterValues(link.metricFields, eventValues, statePrimaryKey);
// Then check that the statePrimaryKey size equals the number of state fields
size_t count = statePrimaryKey->getValues().size();
if (count != link.stateFields.size()) {
return;
}
// For each dimension Value in the statePrimaryKey, set the field and tag
// using the state atom fields from MetricStateLinks.
for (size_t i = 0; i < count; i++) {
statePrimaryKey->mutableValue(i)->mField.setField(link.stateFields[i].mMatcher.getField());
statePrimaryKey->mutableValue(i)->mField.setTag(link.stateFields[i].mMatcher.getTag());
}
}
bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
const HashableDimensionKey& primaryKey,
const vector<Metric2State>& stateLinks, const int32_t stateAtomId) {
if (whatKey.getValues().size() < primaryKey.getValues().size()) {
ALOGE("Contains linked values false: whatKey is too small");
return false;
}
for (const auto& primaryValue : primaryKey.getValues()) {
bool found = false;
for (const auto& whatValue : whatKey.getValues()) {
if (linked(stateLinks, stateAtomId, primaryValue.mField, whatValue.mField) &&
primaryValue.mValue == whatValue.mValue) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
bool linked(const vector<Metric2State>& stateLinks, const int32_t stateAtomId,
const Field& stateField, const Field& metricField) {
for (auto stateLink : stateLinks) {
if (stateLink.stateAtomId != stateAtomId) {
continue;
}
for (size_t i = 0; i < stateLink.stateFields.size(); i++) {
if (stateLink.stateFields[i].mMatcher == stateField &&
stateLink.metricFields[i].mMatcher == metricField) {
return true;
}
}
}
return false;
}
bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) {
if (s1.size() != s2.size()) {
return s1.size() < s2.size();
}
size_t count = s1.size();
for (size_t i = 0; i < count; i++) {
if (s1[i] != s2[i]) {
return s1[i] < s2[i];
}
}
return false;
}
bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const {
return !((*this) == that);
}
bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
if (mValues.size() != that.getValues().size()) {
return false;
}
size_t count = mValues.size();
for (size_t i = 0; i < count; i++) {
if (mValues[i] != (that.getValues())[i]) {
return false;
}
}
return true;
};
bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {
return LessThan(getValues(), that.getValues());
};
bool HashableDimensionKey::contains(const HashableDimensionKey& that) const {
if (mValues.size() < that.getValues().size()) {
return false;
}
if (mValues.size() == that.getValues().size()) {
return (*this) == that;
}
for (const auto& value : that.getValues()) {
bool found = false;
for (const auto& myValue : mValues) {
if (value.mField == myValue.mField && value.mValue == myValue.mValue) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
string HashableDimensionKey::toString() const {
std::string output;
for (const auto& value : mValues) {
output += StringPrintf("(%d)%#x->%s ", value.mField.getTag(), value.mField.getField(),
value.mValue.toString().c_str());
}
return output;
}
bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
mStateValuesKey == that.getStateValuesKey();
};
string MetricDimensionKey::toString() const {
return mDimensionKeyInWhat.toString() + mStateValuesKey.toString();
}
bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
if (mDimensionKeyInWhat < that.getDimensionKeyInWhat()) {
return true;
} else if (that.getDimensionKeyInWhat() < mDimensionKeyInWhat) {
return false;
}
return mStateValuesKey < that.getStateValuesKey();
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,238 +0,0 @@
/*
* 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 <aidl/android/os/StatsDimensionsValueParcel.h>
#include <utils/JenkinsHash.h>
#include <vector>
#include "android-base/stringprintf.h"
#include "FieldValue.h"
#include "logd/LogEvent.h"
namespace android {
namespace os {
namespace statsd {
using ::aidl::android::os::StatsDimensionsValueParcel;
struct Metric2Condition {
int64_t conditionId;
std::vector<Matcher> metricFields;
std::vector<Matcher> conditionFields;
};
struct Metric2State {
int32_t stateAtomId;
std::vector<Matcher> metricFields;
std::vector<Matcher> stateFields;
};
class HashableDimensionKey {
public:
explicit HashableDimensionKey(const std::vector<FieldValue>& values) {
mValues = values;
}
HashableDimensionKey() {};
HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){};
inline void addValue(const FieldValue& value) {
mValues.push_back(value);
}
inline const std::vector<FieldValue>& getValues() const {
return mValues;
}
inline std::vector<FieldValue>* mutableValues() {
return &mValues;
}
inline FieldValue* mutableValue(size_t i) {
if (i >= 0 && i < mValues.size()) {
return &(mValues[i]);
}
return nullptr;
}
StatsDimensionsValueParcel toStatsDimensionsValueParcel() const;
std::string toString() const;
bool operator!=(const HashableDimensionKey& that) const;
bool operator==(const HashableDimensionKey& that) const;
bool operator<(const HashableDimensionKey& that) const;
bool contains(const HashableDimensionKey& that) const;
private:
std::vector<FieldValue> mValues;
};
class MetricDimensionKey {
public:
explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat,
const HashableDimensionKey& stateValuesKey)
: mDimensionKeyInWhat(dimensionKeyInWhat), mStateValuesKey(stateValuesKey){};
MetricDimensionKey(){};
MetricDimensionKey(const MetricDimensionKey& that)
: mDimensionKeyInWhat(that.getDimensionKeyInWhat()),
mStateValuesKey(that.getStateValuesKey()){};
MetricDimensionKey& operator=(const MetricDimensionKey& from) = default;
std::string toString() const;
inline const HashableDimensionKey& getDimensionKeyInWhat() const {
return mDimensionKeyInWhat;
}
inline const HashableDimensionKey& getStateValuesKey() const {
return mStateValuesKey;
}
inline HashableDimensionKey* getMutableStateValuesKey() {
return &mStateValuesKey;
}
inline void setStateValuesKey(const HashableDimensionKey& key) {
mStateValuesKey = key;
}
bool hasStateValuesKey() const {
return mStateValuesKey.getValues().size() > 0;
}
bool operator==(const MetricDimensionKey& that) const;
bool operator<(const MetricDimensionKey& that) const;
private:
HashableDimensionKey mDimensionKeyInWhat;
HashableDimensionKey mStateValuesKey;
};
android::hash_t hashDimension(const HashableDimensionKey& key);
/**
* Returns true if a FieldValue field matches the matcher field.
* The value of the FieldValue is output.
*/
bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& values,
FieldValue* output);
/**
* Creating HashableDimensionKeys from FieldValues using matcher.
*
* This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
* in it. This is because: for example, when we create dimension from last uid in attribution chain,
* In one event, uid 1000 is at position 5 and it's the last
* In another event, uid 1000 is at position 6, and it's the last
* these 2 events should be mapped to the same dimension. So we will remove the original position
* from the dimension key for the uid field (by applying 0x80 bit mask).
*/
bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& values,
HashableDimensionKey* output);
/**
* Creating HashableDimensionKeys from State Primary Keys in FieldValues.
*
* This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
* in it. This is because: for example, when we create dimension from last uid in attribution chain,
* In one event, uid 1000 is at position 5 and it's the last
* In another event, uid 1000 is at position 6, and it's the last
* these 2 events should be mapped to the same dimension. So we will remove the original position
* from the dimension key for the uid field (by applying 0x80 bit mask).
*/
bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output);
/**
* Filter the values from FieldValues using the matchers.
*
* In contrast to the above function, this function will not do any modification to the original
* data. Considering it as taking a snapshot on the atom event.
*/
void filterGaugeValues(const std::vector<Matcher>& matchers, const std::vector<FieldValue>& values,
std::vector<FieldValue>* output);
void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
const Metric2Condition& links,
HashableDimensionKey* conditionDimension);
/**
* Get dimension values using metric's "what" fields and fill statePrimaryKey's
* mField information using "state" fields.
*/
void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link,
HashableDimensionKey* statePrimaryKey);
/**
* Returns true if the primaryKey values are a subset of the whatKey values.
* The values from the primaryKey come from the state atom, so we need to
* check that a link exists between the state atom field and what atom field.
*
* Example:
* whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}]
* statePrimaryKey = [Atom: 27, {uid: 1005}]
* Returns true IF one of the Metric2State links Atom 10's uid to Atom 27's uid
*
* Example:
* whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}]
* statePrimaryKey = [Atom: 59, {uid: 1005, package_name: "system"}]
* Returns false
*/
bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
const HashableDimensionKey& primaryKey,
const std::vector<Metric2State>& stateLinks,
const int32_t stateAtomId);
/**
* Returns true if there is a Metric2State link that links the stateField and
* the metricField (they are equal fields from different atoms).
*/
bool linked(const std::vector<Metric2State>& stateLinks, const int32_t stateAtomId,
const Field& stateField, const Field& metricField);
} // namespace statsd
} // namespace os
} // namespace android
namespace std {
using android::os::statsd::HashableDimensionKey;
using android::os::statsd::MetricDimensionKey;
template <>
struct hash<HashableDimensionKey> {
std::size_t operator()(const HashableDimensionKey& key) const {
return hashDimension(key);
}
};
template <>
struct hash<MetricDimensionKey> {
std::size_t operator()(const MetricDimensionKey& key) const {
android::hash_t hash = hashDimension(key.getDimensionKeyInWhat());
hash = android::JenkinsHashMix(hash, hashDimension(key.getStateValuesKey()));
return android::JenkinsHashWhiten(hash);
}
};
} // namespace std

View File

@@ -1,33 +0,0 @@
/*
* 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.
*/
/*
* This file must be included at the top of the file. Other header files
* occasionally include log.h, and if LOG_TAG isn't set when that happens
* we'll get a preprocesser error when we try to define it here.
*/
#pragma once
#define LOG_TAG "statsd"
#include <log/log.h>
// Use the local value to turn on/off debug logs instead of using log.tag. properties.
// The advantage is that in production compiler can remove the logging code if the local
// DEBUG/VERBOSE is false.
#define VLOG(...) \
if (DEBUG) ALOGD(__VA_ARGS__);

File diff suppressed because it is too large Load Diff

View File

@@ -1,370 +0,0 @@
/*
* 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 <gtest/gtest_prod.h>
#include "config/ConfigListener.h"
#include "logd/LogEvent.h"
#include "metrics/MetricsManager.h"
#include "packages/UidMap.h"
#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h"
#include <stdio.h>
#include <unordered_map>
namespace android {
namespace os {
namespace statsd {
class StatsLogProcessor : public ConfigListener, public virtual PackageInfoListener {
public:
StatsLogProcessor(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor,
const int64_t timeBaseNs,
const std::function<bool(const ConfigKey&)>& sendBroadcast,
const std::function<bool(const int&,
const vector<int64_t>&)>& sendActivationBroadcast);
virtual ~StatsLogProcessor();
void OnLogEvent(LogEvent* event);
void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
const StatsdConfig& config);
void OnConfigRemoved(const ConfigKey& key);
size_t GetMetricsSize(const ConfigKey& key) const;
void GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs);
void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
const bool include_current_partial_bucket, const bool erase_data,
const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency,
vector<uint8_t>* outData);
void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
const bool include_current_partial_bucket, const bool erase_data,
const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency,
ProtoOutputStream* proto);
/* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */
void onPeriodicAlarmFired(
const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
/* Flushes data to disk. Data on memory will be gone after written to disk. */
void WriteDataToDisk(const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
/* Persist configs containing metrics with active activations to disk. */
void SaveActiveConfigsToDisk(int64_t currentTimeNs);
/* Writes the current active status/ttl for all configs and metrics to ProtoOutputStream. */
void WriteActiveConfigsToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
/* Load configs containing metrics with active activations from disk. */
void LoadActiveConfigsFromDisk();
/* Persist metadata for configs and metrics to disk. */
void SaveMetadataToDisk(int64_t currentWallClockTimeNs, int64_t systemElapsedTimeNs);
/* Writes the statsd metadata for all configs and metrics to StatsMetadataList. */
void WriteMetadataToProto(int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs,
metadata::StatsMetadataList* metadataList);
/* Load stats metadata for configs and metrics from disk. */
void LoadMetadataFromDisk(int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs);
/* Sets the metadata for all configs and metrics */
void SetMetadataState(const metadata::StatsMetadataList& statsMetadataList,
int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs);
/* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
/* Notify all MetricsManagers of app upgrades */
void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
const int64_t version) override;
/* Notify all MetricsManagers of app removals */
void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override;
/* Notify all MetricsManagers of uid map snapshots received */
void onUidMapReceived(const int64_t& eventTimeNs) override;
/* Notify all metrics managers of boot completed
* This will force a bucket split when the boot is finished.
*/
void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
// Reset all configs.
void resetConfigs();
inline sp<UidMap> getUidMap() {
return mUidMap;
}
void dumpStates(int outFd, bool verbose);
void informPullAlarmFired(const int64_t timestampNs);
int64_t getLastReportTimeNs(const ConfigKey& key);
inline void setPrintLogs(bool enabled) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
mPrintAllLogs = enabled;
}
// Add a specific config key to the possible configs to dump ASAP.
void noteOnDiskData(const ConfigKey& key);
void setAnomalyAlarm(const int64_t timeMillis);
void cancelAnomalyAlarm();
private:
// For testing only.
inline sp<AlarmMonitor> getAnomalyAlarmMonitor() const {
return mAnomalyAlarmMonitor;
}
inline sp<AlarmMonitor> getPeriodicAlarmMonitor() const {
return mPeriodicAlarmMonitor;
}
mutable mutex mMetricsMutex;
// Guards mNextAnomalyAlarmTime. A separate mutex is needed because alarms are set/cancelled
// in the onLogEvent code path, which is locked by mMetricsMutex.
// DO NOT acquire mMetricsMutex while holding mAnomalyAlarmMutex. This can lead to a deadlock.
mutable mutex mAnomalyAlarmMutex;
std::unordered_map<ConfigKey, sp<MetricsManager>> mMetricsManagers;
std::unordered_map<ConfigKey, int64_t> mLastBroadcastTimes;
// Last time we sent a broadcast to this uid that the active configs had changed.
std::unordered_map<int, int64_t> mLastActivationBroadcastTimes;
// Tracks when we last checked the bytes consumed for each config key.
std::unordered_map<ConfigKey, int64_t> mLastByteSizeTimes;
// Tracks which config keys has metric reports on disk
std::set<ConfigKey> mOnDiskDataConfigs;
sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid.
sp<StatsPullerManager> mPullerManager; // Reference to StatsPullerManager
sp<AlarmMonitor> mAnomalyAlarmMonitor;
sp<AlarmMonitor> mPeriodicAlarmMonitor;
void OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs);
void resetIfConfigTtlExpiredLocked(const int64_t timestampNs);
void OnConfigUpdatedLocked(
const int64_t currentTimestampNs, const ConfigKey& key, const StatsdConfig& config);
void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs);
void WriteActiveConfigsToProtoOutputStreamLocked(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
int64_t currentTimeNs);
void SetMetadataStateLocked(const metadata::StatsMetadataList& statsMetadataList,
int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs);
void WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs,
metadata::StatsMetadataList* metadataList);
void WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs,
const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
void onConfigMetricsReportLocked(
const ConfigKey& key, const int64_t dumpTimeStampNs,
const bool include_current_partial_bucket, const bool erase_data,
const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
/*if dataSavedToDisk is true, it indicates the caller will write the data to disk
(e.g., before reboot). So no need to further persist local history.*/
const bool dataSavedToDisk, vector<uint8_t>* proto);
/* Check if we should send a broadcast if approaching memory limits and if we're over, we
* actually delete the data. */
void flushIfNecessaryLocked(const ConfigKey& key, MetricsManager& metricsManager);
// Maps the isolated uid in the log event to host uid if the log event contains uid fields.
void mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const;
// Handler over the isolated uid change event.
void onIsolatedUidChangedEventLocked(const LogEvent& event);
// Handler over the binary push state changed event.
void onBinaryPushStateChangedEventLocked(LogEvent* event);
// Handler over the watchdog rollback occurred event.
void onWatchdogRollbackOccurredLocked(LogEvent* event);
// Updates train info on disk based on binary push state changed info and
// write disk info into parameters.
void getAndUpdateTrainInfoOnDisk(bool is_rollback, InstallTrainInfo* trainInfoIn);
// Gets experiment ids on disk for associated train and updates them
// depending on rollback type. Then writes them back to disk and returns
// them.
std::vector<int64_t> processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
const string& packageName);
// Reset all configs.
void resetConfigsLocked(const int64_t timestampNs);
// Reset the specified configs.
void resetConfigsLocked(const int64_t timestampNs, const std::vector<ConfigKey>& configs);
// An anomaly alarm should have fired.
// Check with anomaly alarm manager to find the alarms and process the result.
void informAnomalyAlarmFiredLocked(const int64_t elapsedTimeMillis);
/* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
void processFiredAnomalyAlarmsLocked(
const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
// Function used to send a broadcast so that receiver for the config key can call getData
// to retrieve the stored data.
std::function<bool(const ConfigKey& key)> mSendBroadcast;
// Function used to send a broadcast so that receiver can be notified of which configs
// are currently active.
std::function<bool(const int& uid, const vector<int64_t>& configIds)> mSendActivationBroadcast;
const int64_t mTimeBaseNs;
// Largest timestamp of the events that we have processed.
int64_t mLargestTimestampSeen = 0;
int64_t mLastTimestampSeen = 0;
int64_t mLastPullerCacheClearTimeSec = 0;
// Last time we wrote data to disk.
int64_t mLastWriteTimeNs = 0;
// Last time we wrote active metrics to disk.
int64_t mLastActiveMetricsWriteNs = 0;
//Last time we wrote metadata to disk.
int64_t mLastMetadataWriteNs = 0;
// The time for the next anomaly alarm for alerts.
int64_t mNextAnomalyAlarmTime = 0;
bool mPrintAllLogs = false;
FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
FRIEND_TEST(StatsLogProcessorTest,
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written);
FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk);
FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
};
} // namespace statsd
} // namespace os
} // namespace android

File diff suppressed because it is too large Load Diff

View File

@@ -1,416 +0,0 @@
/*
* 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.
*/
#ifndef STATS_SERVICE_H
#define STATS_SERVICE_H
#include <aidl/android/os/BnStatsd.h>
#include <aidl/android/os/IPendingIntentRef.h>
#include <aidl/android/os/IPullAtomCallback.h>
#include <gtest/gtest_prod.h>
#include <utils/Looper.h>
#include <mutex>
#include "StatsLogProcessor.h"
#include "anomaly/AlarmMonitor.h"
#include "config/ConfigManager.h"
#include "external/StatsPullerManager.h"
#include "logd/LogEventQueue.h"
#include "packages/UidMap.h"
#include "shell/ShellSubscriber.h"
#include "statscompanion_util.h"
#include "utils/MultiConditionTrigger.h"
using namespace android;
using namespace android::os;
using namespace std;
using Status = ::ndk::ScopedAStatus;
using aidl::android::os::BnStatsd;
using aidl::android::os::IPendingIntentRef;
using aidl::android::os::IPullAtomCallback;
using ::ndk::ScopedAIBinder_DeathRecipient;
using ::ndk::ScopedFileDescriptor;
using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
class StatsService : public BnStatsd {
public:
StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue);
virtual ~StatsService();
/** The anomaly alarm registered with AlarmManager won't be updated by less than this. */
const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5;
virtual status_t dump(int fd, const char** args, uint32_t numArgs) override;
virtual status_t handleShellCommand(int in, int out, int err, const char** argv,
uint32_t argc) override;
virtual Status systemRunning();
virtual Status statsCompanionReady();
virtual Status bootCompleted();
virtual Status informPollAlarmFired();
virtual Status informAlarmForSubscriberTriggeringFired();
virtual Status informAllUidData(const ScopedFileDescriptor& fd);
virtual Status informOnePackage(const string& app, int32_t uid, int64_t version,
const string& versionString, const string& installer);
virtual Status informOnePackageRemoved(const string& app, int32_t uid);
virtual Status informDeviceShutdown();
/**
* Called right before we start processing events.
*/
void Startup();
/**
* Called when terminiation signal received.
*/
void Terminate();
/**
* Test ONLY interface. In real world, StatsService reads from LogEventQueue.
*/
virtual void OnLogEvent(LogEvent* event);
/**
* Binder call for clients to request data for this configuration key.
*/
virtual Status getData(int64_t key,
const int32_t callingUid,
vector<int8_t>* output) override;
/**
* Binder call for clients to get metadata across all configs in statsd.
*/
virtual Status getMetadata(vector<int8_t>* output) override;
/**
* Binder call to let clients send a configuration and indicate they're interested when they
* should requestData for this configuration.
*/
virtual Status addConfiguration(int64_t key,
const vector<int8_t>& config,
const int32_t callingUid) override;
/**
* Binder call to let clients register the data fetch operation for a configuration.
*/
virtual Status setDataFetchOperation(int64_t key,
const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid) override;
/**
* Binder call to remove the data fetch operation for the specified config key.
*/
virtual Status removeDataFetchOperation(int64_t key,
const int32_t callingUid) override;
/**
* Binder call to let clients register the active configs changed operation.
*/
virtual Status setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid,
vector<int64_t>* output) override;
/**
* Binder call to remove the active configs changed operation for the specified package..
*/
virtual Status removeActiveConfigsChangedOperation(const int32_t callingUid) override;
/**
* Binder call to allow clients to remove the specified configuration.
*/
virtual Status removeConfiguration(int64_t key,
const int32_t callingUid) override;
/**
* Binder call to associate the given config's subscriberId with the given pendingIntentRef.
*/
virtual Status setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid) override;
/**
* Binder call to unassociate the given config's subscriberId with any pendingIntentRef.
*/
virtual Status unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
const int32_t callingUid) override;
/** Inform statsCompanion that statsd is ready. */
virtual void sayHiToStatsCompanion();
/**
* Binder call to notify statsd that all pullers from boot have been registered.
*/
virtual Status allPullersFromBootRegistered();
/**
* Binder call to register a callback function for a pulled atom.
*/
virtual Status registerPullAtomCallback(
int32_t uid, int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
const std::vector<int32_t>& additiveFields,
const shared_ptr<IPullAtomCallback>& pullerCallback) override;
/**
* Binder call to register a callback function for a pulled atom.
*/
virtual Status registerNativePullAtomCallback(
int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
const std::vector<int32_t>& additiveFields,
const shared_ptr<IPullAtomCallback>& pullerCallback) override;
/**
* Binder call to unregister any existing callback for the given uid and atom.
*/
virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override;
/**
* Binder call to unregister any existing callback for the given atom and calling uid.
*/
virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override;
/**
* Binder call to get registered experiment IDs.
*/
virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);
private:
/**
* Load system properties at init.
*/
void init_system_properties();
/**
* Helper for loading system properties.
*/
static void init_build_type_callback(void* cookie, const char* name, const char* value,
uint32_t serial);
/**
* Proto output of statsd report data dumpsys, wrapped in a StatsDataDumpProto.
*/
void dumpIncidentSection(int outFd);
/**
* Text or proto output of statsdStats dumpsys.
*/
void dumpStatsdStats(int outFd, bool verbose, bool proto);
/**
* Print usage information for the commands
*/
void print_cmd_help(int out);
/* Runs on its dedicated thread to process pushed stats event from socket. */
void readLogs();
/**
* Trigger a broadcast.
*/
status_t cmd_trigger_broadcast(int outFd, Vector<String8>& args);
/**
* Trigger an active configs changed broadcast.
*/
status_t cmd_trigger_active_config_broadcast(int outFd, Vector<String8>& args);
/**
* Handle the config sub-command.
*/
status_t cmd_config(int inFd, int outFd, int err, Vector<String8>& args);
/**
* Prints some basic stats to std out.
*/
status_t cmd_print_stats(int outFd, const Vector<String8>& args);
/**
* Print the event log.
*/
status_t cmd_dump_report(int outFd, const Vector<String8>& args);
/**
* Print the mapping of uids to package names.
*/
status_t cmd_print_uid_map(int outFd, const Vector<String8>& args);
/**
* Flush the data to disk.
*/
status_t cmd_write_data_to_disk(int outFd);
/**
* Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling
* StatsLog.write(APP_BREADCRUMB_REPORTED).
*/
status_t cmd_log_app_breadcrumb(int outFd, const Vector<String8>& args);
/**
* Write an BinaryPushStateChanged event, as if calling StatsLog.logBinaryPushStateChanged().
*/
status_t cmd_log_binary_push(int outFd, const Vector<String8>& args);
/**
* Print contents of a pulled metrics source.
*/
status_t cmd_print_pulled_metrics(int outFd, const Vector<String8>& args);
/**
* Removes all configs stored on disk and on memory.
*/
status_t cmd_remove_all_configs(int outFd);
/*
* Dump memory usage by statsd.
*/
status_t cmd_dump_memory_info(int outFd);
/*
* Clear all puller cached data
*/
status_t cmd_clear_puller_cache(int outFd);
/**
* Print all stats logs received to logcat.
*/
status_t cmd_print_logs(int outFd, const Vector<String8>& args);
/**
* Writes the value of args[uidArgIndex] into uid.
* Returns whether the uid is reasonable (type uid_t) and whether
* 1. it is equal to the calling uid, or
* 2. the device is mEngBuild, or
* 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL).
*/
bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid);
/**
* Writes the value of uidSting into uid.
* Returns whether the uid is reasonable (type uid_t) and whether
* 1. it is equal to the calling uid, or
* 2. the device is mEngBuild, or
* 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL).
*/
bool getUidFromString(const char* uidString, int32_t& uid);
/**
* Adds a configuration after checking permissions and obtaining UID from binder call.
*/
bool addConfigurationChecked(int uid, int64_t key, const vector<int8_t>& config);
/**
* Update a configuration.
*/
void set_config(int uid, const string& name, const StatsdConfig& config);
/**
* Death recipient callback that is called when StatsCompanionService dies.
* The cookie is a pointer to a StatsService object.
*/
static void statsCompanionServiceDied(void* cookie);
/**
* Implementation of statsCompanionServiceDied.
*/
void statsCompanionServiceDiedImpl();
/**
* Tracks the uid <--> package name mapping.
*/
sp<UidMap> mUidMap;
/**
* Fetches external metrics
*/
sp<StatsPullerManager> mPullerManager;
/**
* Tracks the configurations that have been passed to statsd.
*/
sp<ConfigManager> mConfigManager;
/**
* The metrics recorder.
*/
sp<StatsLogProcessor> mProcessor;
/**
* The alarm monitor for anomaly detection.
*/
const sp<AlarmMonitor> mAnomalyAlarmMonitor;
/**
* The alarm monitor for alarms to directly trigger subscriber.
*/
const sp<AlarmMonitor> mPeriodicAlarmMonitor;
/**
* Whether this is an eng build.
*/
bool mEngBuild;
sp<ShellSubscriber> mShellSubscriber;
/**
* Mutex for setting the shell subscriber
*/
mutable mutex mShellSubscriberMutex;
std::shared_ptr<LogEventQueue> mEventQueue;
MultiConditionTrigger mBootCompleteTrigger;
static const inline string kBootCompleteTag = "BOOT_COMPLETE";
static const inline string kUidMapReceivedTag = "UID_MAP";
static const inline string kAllPullersRegisteredTag = "PULLERS_REGISTERED";
ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient;
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit);
FRIEND_TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
};
} // namespace statsd
} // namespace os
} // namespace android
#endif // STATS_SERVICE_H

View File

@@ -1,57 +0,0 @@
/*
* Copyright (C) 2019 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.
*/
syntax = "proto2";
package android.os.statsd;
option java_package = "com.android.os";
option java_multiple_files = true;
option java_outer_classname = "ActiveConfigProto";
message ActiveEventActivation {
optional int32 atom_matcher_index = 1;
// Time left in activation. When this proto is loaded after device boot,
// the activation should be set to active for this duration.
// This field will only be set when the state is ACTIVE
optional int64 remaining_ttl_nanos = 2;
enum State {
UNNKNOWN = 0;
// This metric should activate for remaining_ttl_nanos when we load the activations.
ACTIVE = 1;
// When we load the activations, this metric should activate on next boot for the tll
// specified in the config.
ACTIVATE_ON_BOOT = 2;
}
optional State state = 3;
}
message ActiveMetric {
optional int64 id = 1;
repeated ActiveEventActivation activation = 2;
}
message ActiveConfig {
optional int64 id = 1;
optional int32 uid = 2;
repeated ActiveMetric metric = 3;
}
// all configs and their metrics on device.
message ActiveConfigList {
repeated ActiveConfig config = 1;
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (C) 2020 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
namespace android {
namespace os {
namespace statsd {
const uint8_t ANNOTATION_ID_IS_UID = 1;
const uint8_t ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2;
const uint8_t ANNOTATION_ID_PRIMARY_FIELD = 3;
const uint8_t ANNOTATION_ID_EXCLUSIVE_STATE = 4;
const uint8_t ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5;
const uint8_t ANNOTATION_ID_TRIGGER_STATE_RESET = 7;
const uint8_t ANNOTATION_ID_STATE_NESTED = 8;
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,139 +0,0 @@
/*
* 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.
*/
#define DEBUG false
#include "Log.h"
#include "anomaly/AlarmMonitor.h"
#include "guardrail/StatsdStats.h"
namespace android {
namespace os {
namespace statsd {
AlarmMonitor::AlarmMonitor(
uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
const std::function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>& updateAlarm,
const std::function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm)
: mRegisteredAlarmTimeSec(0),
mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
mUpdateAlarm(updateAlarm),
mCancelAlarm(cancelAlarm) {}
AlarmMonitor::~AlarmMonitor() {}
void AlarmMonitor::setStatsCompanionService(
shared_ptr<IStatsCompanionService> statsCompanionService) {
std::lock_guard<std::mutex> lock(mLock);
shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
if (statsCompanionService == nullptr) {
VLOG("Erasing link to statsCompanionService");
return;
}
VLOG("Creating link to statsCompanionService");
const sp<const InternalAlarm> top = mPq.top();
if (top != nullptr) {
updateRegisteredAlarmTime_l(top->timestampSec);
}
}
void AlarmMonitor::add(sp<const InternalAlarm> alarm) {
std::lock_guard<std::mutex> lock(mLock);
if (alarm == nullptr) {
ALOGW("Asked to add a null alarm.");
return;
}
if (alarm->timestampSec < 1) {
// forbidden since a timestamp 0 is used to indicate no alarm registered
ALOGW("Asked to add a 0-time alarm.");
return;
}
// TODO(b/110563466): Ensure that refractory period is respected.
VLOG("Adding alarm with time %u", alarm->timestampSec);
mPq.push(alarm);
if (mRegisteredAlarmTimeSec < 1 ||
alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) {
updateRegisteredAlarmTime_l(alarm->timestampSec);
}
}
void AlarmMonitor::remove(sp<const InternalAlarm> alarm) {
std::lock_guard<std::mutex> lock(mLock);
if (alarm == nullptr) {
ALOGW("Asked to remove a null alarm.");
return;
}
VLOG("Removing alarm with time %u", alarm->timestampSec);
bool wasPresent = mPq.remove(alarm);
if (!wasPresent) return;
if (mPq.empty()) {
VLOG("Queue is empty. Cancel any alarm.");
cancelRegisteredAlarmTime_l();
return;
}
uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec;
VLOG("Soonest alarm is %u", soonestAlarmTimeSec);
if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) {
updateRegisteredAlarmTime_l(soonestAlarmTimeSec);
}
}
// More efficient than repeatedly calling remove(mPq.top()) since it batches the
// updates to the registered alarm.
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> AlarmMonitor::popSoonerThan(
uint32_t timestampSec) {
VLOG("Removing alarms with time <= %u", timestampSec);
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> oldAlarms;
std::lock_guard<std::mutex> lock(mLock);
for (sp<const InternalAlarm> t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec;
t = mPq.top()) {
oldAlarms.insert(t);
mPq.pop(); // remove t
}
// Always update registered alarm time (if anything has changed).
if (!oldAlarms.empty()) {
if (mPq.empty()) {
VLOG("Queue is empty. Cancel any alarm.");
cancelRegisteredAlarmTime_l();
} else {
// Always update the registered alarm in this case (unlike remove()).
updateRegisteredAlarmTime_l(mPq.top()->timestampSec);
}
}
return oldAlarms;
}
void AlarmMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
VLOG("Updating reg alarm time to %u", timestampSec);
mRegisteredAlarmTimeSec = timestampSec;
mUpdateAlarm(mStatsCompanionService, secToMs(mRegisteredAlarmTimeSec));
}
void AlarmMonitor::cancelRegisteredAlarmTime_l() {
VLOG("Cancelling reg alarm.");
mRegisteredAlarmTimeSec = 0;
mCancelAlarm(mStatsCompanionService);
}
int64_t AlarmMonitor::secToMs(uint32_t timeSec) {
return ((int64_t)timeSec) * 1000;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,162 +0,0 @@
/*
* 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 "anomaly/indexed_priority_queue.h"
#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
#include <unordered_set>
#include <vector>
using namespace android;
using aidl::android::os::IStatsCompanionService;
using std::function;
using std::shared_ptr;
using std::unordered_set;
namespace android {
namespace os {
namespace statsd {
/**
* Represents an alarm, associated with some aggregate metric, holding a
* projected time at which the metric is expected to exceed its anomaly
* threshold.
* Timestamps are in seconds since epoch in a uint32, so will fail in year 2106.
*/
struct InternalAlarm : public RefBase {
explicit InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
}
const uint32_t timestampSec;
/** InternalAlarm a is smaller (higher priority) than b if its timestamp is sooner. */
struct SmallerTimestamp {
bool operator()(sp<const InternalAlarm> a, sp<const InternalAlarm> b) const {
return (a->timestampSec < b->timestampSec);
}
};
};
/**
* Manages internal alarms that may get registered with the AlarmManager.
*/
class AlarmMonitor : public RefBase {
public:
/**
* @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs
* from the registered alarm by more than this amount, update the registered
* alarm.
*/
AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
const function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>&
updateAlarm,
const function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm);
~AlarmMonitor();
/**
* Tells AnomalyMonitor what IStatsCompanionService to use and, if
* applicable, immediately registers an existing alarm with it.
* If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't
* update IStatsCompanionService (until such time as it is set non-null).
*/
void setStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
/**
* Adds the given alarm (reference) to the queue.
*/
void add(sp<const InternalAlarm> alarm);
/**
* Removes the given alarm (reference) from the queue.
* Note that alarm comparison is reference-based; if another alarm exists
* with the same timestampSec, that alarm will still remain in the queue.
*/
void remove(sp<const InternalAlarm> alarm);
/**
* Returns and removes all alarms whose timestamp <= the given timestampSec.
* Always updates the registered alarm if return is non-empty.
*/
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> popSoonerThan(
uint32_t timestampSec);
/**
* Returns the projected alarm timestamp that is registered with
* StatsCompanionService. This may not be equal to the soonest alarm,
* but should be within minDiffToUpdateRegisteredAlarmTimeSec of it.
*/
uint32_t getRegisteredAlarmTimeSec() const {
return mRegisteredAlarmTimeSec;
}
private:
std::mutex mLock;
/**
* Timestamp (seconds since epoch) of the alarm registered with
* StatsCompanionService. This, in general, may not be equal to the soonest
* alarm stored in mPq, but should be within minUpdateTimeSec of it.
* A value of 0 indicates that no alarm is currently registered.
*/
uint32_t mRegisteredAlarmTimeSec;
/**
* Priority queue of alarms, prioritized by soonest alarm.timestampSec.
*/
indexed_priority_queue<InternalAlarm, InternalAlarm::SmallerTimestamp> mPq;
/**
* Binder interface for communicating with StatsCompanionService.
*/
shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
/**
* Amount by which the soonest projected alarm must differ from
* mRegisteredAlarmTimeSec before updateRegisteredAlarmTime_l is called.
*/
uint32_t mMinUpdateTimeSec;
/**
* Updates the alarm registered with StatsCompanionService to the given time.
* Also correspondingly updates mRegisteredAlarmTimeSec.
*/
void updateRegisteredAlarmTime_l(uint32_t timestampSec);
/**
* Cancels the alarm registered with StatsCompanionService.
* Also correspondingly sets mRegisteredAlarmTimeSec to 0.
*/
void cancelRegisteredAlarmTime_l();
/** Converts uint32 timestamp in seconds to a Java long in msec. */
int64_t secToMs(uint32_t timeSec);
// Callback function to update the alarm via StatsCompanionService.
std::function<void(const shared_ptr<IStatsCompanionService>, int64_t)> mUpdateAlarm;
// Callback function to cancel the alarm via StatsCompanionService.
std::function<void(const shared_ptr<IStatsCompanionService>)> mCancelAlarm;
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,94 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "anomaly/AlarmTracker.h"
#include "anomaly/subscriber_util.h"
#include "HashableDimensionKey.h"
#include "stats_util.h"
#include "storage/StorageManager.h"
#include <time.h>
namespace android {
namespace os {
namespace statsd {
AlarmTracker::AlarmTracker(const int64_t startMillis,
const int64_t currentMillis,
const Alarm& alarm, const ConfigKey& configKey,
const sp<AlarmMonitor>& alarmMonitor)
: mAlarmConfig(alarm),
mConfigKey(configKey),
mAlarmMonitor(alarmMonitor) {
VLOG("AlarmTracker() called");
mAlarmSec = (startMillis + mAlarmConfig.offset_millis()) / MS_PER_SEC;
// startMillis is the time statsd is created. We need to find the 1st alarm timestamp after
// the config is added to statsd.
mAlarmSec = findNextAlarmSec(currentMillis / MS_PER_SEC); // round up
mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)};
VLOG("AlarmTracker sets the periodic alarm at: %lld", (long long)mAlarmSec);
if (mAlarmMonitor != nullptr) {
mAlarmMonitor->add(mInternalAlarm);
}
}
AlarmTracker::~AlarmTracker() {
VLOG("~AlarmTracker() called");
if (mInternalAlarm != nullptr && mAlarmMonitor != nullptr) {
mAlarmMonitor->remove(mInternalAlarm);
}
}
void AlarmTracker::addSubscription(const Subscription& subscription) {
mSubscriptions.push_back(subscription);
}
int64_t AlarmTracker::findNextAlarmSec(int64_t currentTimeSec) {
if (currentTimeSec < mAlarmSec) {
return mAlarmSec;
}
int64_t periodsForward =
((currentTimeSec - mAlarmSec) * MS_PER_SEC) / mAlarmConfig.period_millis() + 1;
return mAlarmSec + periodsForward * mAlarmConfig.period_millis() / MS_PER_SEC;
}
void AlarmTracker::informAlarmsFired(
const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
if (firedAlarms.empty() || mInternalAlarm == nullptr ||
firedAlarms.find(mInternalAlarm) == firedAlarms.end()) {
return;
}
if (!mSubscriptions.empty()) {
VLOG("AlarmTracker triggers the subscribers.");
triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY,
0 /* metricValue N/A */, mConfigKey, mSubscriptions);
}
firedAlarms.erase(mInternalAlarm);
mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up
mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)};
VLOG("AlarmTracker sets the periodic alarm at: %lld", (long long)mAlarmSec);
if (mAlarmMonitor != nullptr) {
mAlarmMonitor->add(mInternalAlarm);
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,80 +0,0 @@
/*
* 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.
*/
#pragma once
#include <gtest/gtest_prod.h>
#include "AlarmMonitor.h"
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alarm
#include <stdlib.h>
#include <utils/RefBase.h>
namespace android {
namespace os {
namespace statsd {
class AlarmTracker : public virtual RefBase {
public:
AlarmTracker(const int64_t startMillis,
const int64_t currentMillis,
const Alarm& alarm, const ConfigKey& configKey,
const sp<AlarmMonitor>& subscriberAlarmMonitor);
virtual ~AlarmTracker();
void onAlarmFired();
void addSubscription(const Subscription& subscription);
void informAlarmsFired(const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms);
protected:
// For test only. Returns the alarm timestamp in seconds. Otherwise returns 0.
inline int32_t getAlarmTimestampSec() const {
return mInternalAlarm == nullptr ? 0 : mInternalAlarm->timestampSec;
}
int64_t findNextAlarmSec(int64_t currentTimeMillis);
// statsd_config.proto Alarm message that defines this tracker.
const Alarm mAlarmConfig;
// A reference to the Alarm's config key.
const ConfigKey mConfigKey;
// The subscriptions that depend on this alarm.
std::vector<Subscription> mSubscriptions;
// Alarm monitor.
sp<AlarmMonitor> mAlarmMonitor;
// The current expected alarm time in seconds.
int64_t mAlarmSec;
// The current alarm.
sp<const InternalAlarm> mInternalAlarm;
FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp);
FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,321 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "AnomalyTracker.h"
#include "external/Perfetto.h"
#include "guardrail/StatsdStats.h"
#include "metadata_util.h"
#include "stats_log_util.h"
#include "subscriber_util.h"
#include "subscriber/IncidentdReporter.h"
#include "subscriber/SubscriberReporter.h"
#include <inttypes.h>
#include <statslog_statsd.h>
#include <time.h>
namespace android {
namespace os {
namespace statsd {
AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey)
: mAlert(alert), mConfigKey(configKey), mNumOfPastBuckets(mAlert.num_buckets() - 1) {
VLOG("AnomalyTracker() called");
if (mAlert.num_buckets() <= 0) {
ALOGE("Cannot create AnomalyTracker with %lld buckets", (long long)mAlert.num_buckets());
return;
}
if (!mAlert.has_trigger_if_sum_gt()) {
ALOGE("Cannot create AnomalyTracker without threshold");
return;
}
resetStorage(); // initialization
}
AnomalyTracker::~AnomalyTracker() {
VLOG("~AnomalyTracker() called");
}
void AnomalyTracker::resetStorage() {
VLOG("resetStorage() called.");
mPastBuckets.clear();
// Excludes the current bucket.
mPastBuckets.resize(mNumOfPastBuckets);
mSumOverPastBuckets.clear();
}
size_t AnomalyTracker::index(int64_t bucketNum) const {
if (bucketNum < 0) {
ALOGE("index() was passed a negative bucket number (%lld)!", (long long)bucketNum);
}
return bucketNum % mNumOfPastBuckets;
}
void AnomalyTracker::advanceMostRecentBucketTo(const int64_t& bucketNum) {
VLOG("advanceMostRecentBucketTo() called.");
if (mNumOfPastBuckets <= 0) {
return;
}
if (bucketNum <= mMostRecentBucketNum) {
ALOGW("Cannot advance buckets backwards (bucketNum=%lld but mMostRecentBucketNum=%lld)",
(long long)bucketNum, (long long)mMostRecentBucketNum);
return;
}
// If in the future (i.e. buckets are ancient), just empty out all past info.
if (bucketNum >= mMostRecentBucketNum + mNumOfPastBuckets) {
resetStorage();
mMostRecentBucketNum = bucketNum;
return;
}
// Clear out space by emptying out old mPastBuckets[i] values and update mSumOverPastBuckets.
for (int64_t i = mMostRecentBucketNum + 1; i <= bucketNum; i++) {
const int idx = index(i);
subtractBucketFromSum(mPastBuckets[idx]);
mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket.
}
mMostRecentBucketNum = bucketNum;
}
void AnomalyTracker::addPastBucket(const MetricDimensionKey& key,
const int64_t& bucketValue,
const int64_t& bucketNum) {
VLOG("addPastBucket(bucketValue) called.");
if (mNumOfPastBuckets == 0 ||
bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) {
return;
}
const int bucketIndex = index(bucketNum);
if (bucketNum <= mMostRecentBucketNum && (mPastBuckets[bucketIndex] != nullptr)) {
// We need to insert into an already existing past bucket.
std::shared_ptr<DimToValMap>& bucket = mPastBuckets[bucketIndex];
auto itr = bucket->find(key);
if (itr != bucket->end()) {
// Old entry already exists; update it.
subtractValueFromSum(key, itr->second);
itr->second = bucketValue;
} else {
bucket->insert({key, bucketValue});
}
mSumOverPastBuckets[key] += bucketValue;
} else {
// Bucket does not exist yet (in future or was never made), so we must make it.
std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
bucket->insert({key, bucketValue});
addPastBucket(bucket, bucketNum);
}
}
void AnomalyTracker::addPastBucket(std::shared_ptr<DimToValMap> bucket,
const int64_t& bucketNum) {
VLOG("addPastBucket(bucket) called.");
if (mNumOfPastBuckets == 0 ||
bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) {
return;
}
if (bucketNum <= mMostRecentBucketNum) {
// We are updating an old bucket, not adding a new one.
subtractBucketFromSum(mPastBuckets[index(bucketNum)]);
} else {
// Clear space for the new bucket to be at bucketNum.
advanceMostRecentBucketTo(bucketNum);
}
mPastBuckets[index(bucketNum)] = bucket;
addBucketToSum(bucket);
}
void AnomalyTracker::subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket) {
if (bucket == nullptr) {
return;
}
for (const auto& keyValuePair : *bucket) {
subtractValueFromSum(keyValuePair.first, keyValuePair.second);
}
}
void AnomalyTracker::subtractValueFromSum(const MetricDimensionKey& key,
const int64_t& bucketValue) {
auto itr = mSumOverPastBuckets.find(key);
if (itr == mSumOverPastBuckets.end()) {
return;
}
itr->second -= bucketValue;
if (itr->second == 0) {
mSumOverPastBuckets.erase(itr);
}
}
void AnomalyTracker::addBucketToSum(const shared_ptr<DimToValMap>& bucket) {
if (bucket == nullptr) {
return;
}
// For each dimension present in the bucket, add its value to its corresponding sum.
for (const auto& keyValuePair : *bucket) {
mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
}
}
int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
const int64_t& bucketNum) const {
if (bucketNum < 0 || mMostRecentBucketNum < 0
|| bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets
|| bucketNum > mMostRecentBucketNum) {
return 0;
}
const auto& bucket = mPastBuckets[index(bucketNum)];
if (bucket == nullptr) {
return 0;
}
const auto& itr = bucket->find(key);
return itr == bucket->end() ? 0 : itr->second;
}
int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const {
const auto& itr = mSumOverPastBuckets.find(key);
if (itr != mSumOverPastBuckets.end()) {
return itr->second;
}
return 0;
}
bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum,
const MetricDimensionKey& key,
const int64_t& currentBucketValue) {
// currentBucketNum should be the next bucket after pastBuckets. If not, advance so that it is.
if (currentBucketNum > mMostRecentBucketNum + 1) {
advanceMostRecentBucketTo(currentBucketNum - 1);
}
return mAlert.has_trigger_if_sum_gt() &&
getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
}
void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId,
const MetricDimensionKey& key, int64_t metricValue) {
// TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
// real time right now.
if (isInRefractoryPeriod(timestampNs, key)) {
VLOG("Skipping anomaly declaration since within refractory period");
return;
}
if (mAlert.has_refractory_period_secs()) {
mRefractoryPeriodEndsSec[key] = ((timestampNs + NS_PER_SEC - 1) / NS_PER_SEC) // round up
+ mAlert.refractory_period_secs();
// TODO(b/110563466): If we had access to the bucket_size_millis, consider
// calling resetStorage()
// if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) {resetStorage();}
}
if (!mSubscriptions.empty()) {
ALOGI("An anomaly (%" PRId64 ") %s has occurred! Informing subscribers.",
mAlert.id(), key.toString().c_str());
informSubscribers(key, metricId, metricValue);
} else {
ALOGI("An anomaly has occurred! (But no subscriber for that alert.)");
}
StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
// TODO(b/110564268): This should also take in the const MetricDimensionKey& key?
util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(),
mConfigKey.GetId(), mAlert.id());
}
void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
const int64_t& currBucketNum, int64_t metricId,
const MetricDimensionKey& key,
const int64_t& currentBucketValue) {
if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
declareAnomaly(timestampNs, metricId, key, currentBucketValue);
}
}
bool AnomalyTracker::isInRefractoryPeriod(const int64_t& timestampNs,
const MetricDimensionKey& key) const {
const auto& it = mRefractoryPeriodEndsSec.find(key);
if (it != mRefractoryPeriodEndsSec.end()) {
return timestampNs < (it->second * (int64_t)NS_PER_SEC);
}
return false;
}
void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id,
int64_t metricValue) {
triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
}
bool AnomalyTracker::writeAlertMetadataToProto(int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs,
metadata::AlertMetadata* alertMetadata) {
bool metadataWritten = false;
if (mRefractoryPeriodEndsSec.empty()) {
return false;
}
for (const auto& it: mRefractoryPeriodEndsSec) {
// Do not write the timestamp to disk if it has already expired
if (it.second < systemElapsedTimeNs / NS_PER_SEC) {
continue;
}
metadataWritten = true;
if (alertMetadata->alert_dim_keyed_data_size() == 0) {
alertMetadata->set_alert_id(mAlert.id());
}
metadata::AlertDimensionKeyedData* keyedData = alertMetadata->add_alert_dim_keyed_data();
// We convert and write the refractory_end_sec to wall clock time because we do not know
// when statsd will start again.
int32_t refractoryEndWallClockSec = (int32_t) ((currentWallClockTimeNs / NS_PER_SEC) +
(it.second - systemElapsedTimeNs / NS_PER_SEC));
keyedData->set_last_refractory_ends_sec(refractoryEndWallClockSec);
writeMetricDimensionKeyToMetadataDimensionKey(
it.first, keyedData->mutable_dimension_key());
}
return metadataWritten;
}
void AnomalyTracker::loadAlertMetadata(
const metadata::AlertMetadata& alertMetadata,
int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs) {
for (const metadata::AlertDimensionKeyedData& keyedData :
alertMetadata.alert_dim_keyed_data()) {
if ((uint64_t) keyedData.last_refractory_ends_sec() < currentWallClockTimeNs / NS_PER_SEC) {
// Do not update the timestamp if it has already expired.
continue;
}
MetricDimensionKey metricKey = loadMetricDimensionKeyFromProto(
keyedData.dimension_key());
int32_t refractoryPeriodEndsSec = (int32_t) keyedData.last_refractory_ends_sec() -
currentWallClockTimeNs / NS_PER_SEC + systemElapsedTimeNs / NS_PER_SEC;
mRefractoryPeriodEndsSec[metricKey] = refractoryPeriodEndsSec;
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,204 +0,0 @@
/*
* 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 <stdlib.h>
#include <gtest/gtest_prod.h>
#include <utils/RefBase.h>
#include "AlarmMonitor.h"
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata
#include "stats_util.h" // HashableDimensionKey and DimToValMap
namespace android {
namespace os {
namespace statsd {
using std::shared_ptr;
using std::unordered_map;
// Does NOT allow negative values.
class AnomalyTracker : public virtual RefBase {
public:
AnomalyTracker(const Alert& alert, const ConfigKey& configKey);
virtual ~AnomalyTracker();
// Add subscriptions that depend on this alert.
void addSubscription(const Subscription& subscription) {
mSubscriptions.push_back(subscription);
}
// Adds a bucket for the given bucketNum (index starting at 0).
// If a bucket for bucketNum already exists, it will be replaced.
// Also, advances to bucketNum (if not in the past), effectively filling any intervening
// buckets with 0s.
void addPastBucket(std::shared_ptr<DimToValMap> bucket, const int64_t& bucketNum);
// Inserts (or replaces) the bucket entry for the given bucketNum at the given key to be the
// given bucketValue. If the bucket does not exist, it will be created.
// Also, advances to bucketNum (if not in the past), effectively filling any intervening
// buckets with 0s.
void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
const int64_t& bucketNum);
// Returns true if, based on past buckets plus the new currentBucketValue (which generally
// represents the partially-filled current bucket), an anomaly has happened.
// Also advances to currBucketNum-1.
bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key,
const int64_t& currentBucketValue);
// Informs incidentd about the detected alert.
void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key,
int64_t metricValue);
// Detects if, based on past buckets plus the new currentBucketValue (which generally
// represents the partially-filled current bucket), an anomaly has happened, and if so,
// declares an anomaly and informs relevant subscribers.
// Also advances to currBucketNum-1.
void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum,
int64_t metricId, const MetricDimensionKey& key,
const int64_t& currentBucketValue);
// Init the AlarmMonitor which is shared across anomaly trackers.
virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
return; // Base AnomalyTracker class has no need for the AlarmMonitor.
}
// Returns the sum of all past bucket values for the given dimension key.
int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const;
// Returns the value for a past bucket, or 0 if that bucket doesn't exist.
int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const;
// Returns the anomaly threshold set in the configuration.
inline int64_t getAnomalyThreshold() const {
return mAlert.trigger_if_sum_gt();
}
// Returns the refractory period ending timestamp (in seconds) for the given key.
// Before this moment, any detected anomaly will be ignored.
// If there is no stored refractory period ending timestamp, returns 0.
uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const {
const auto& it = mRefractoryPeriodEndsSec.find(key);
return it != mRefractoryPeriodEndsSec.end() ? it->second : 0;
}
// Returns the (constant) number of past buckets this anomaly tracker can store.
inline int getNumOfPastBuckets() const {
return mNumOfPastBuckets;
}
// Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker,
// and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor.
virtual void informAlarmsFired(const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
return; // The base AnomalyTracker class doesn't have alarms.
}
// Writes metadata of the alert (refractory_period_end_sec) to AlertMetadata.
// Returns true if at least one element is written to alertMetadata.
bool writeAlertMetadataToProto(
int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs, metadata::AlertMetadata* alertMetadata);
void loadAlertMetadata(
const metadata::AlertMetadata& alertMetadata,
int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs);
protected:
// For testing only.
// Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
// returns 0.
virtual uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const {
return 0; // The base AnomalyTracker class doesn't have alarms.
}
// statsd_config.proto Alert message that defines this tracker.
const Alert mAlert;
// The subscriptions that depend on this alert.
std::vector<Subscription> mSubscriptions;
// A reference to the Alert's config key.
const ConfigKey mConfigKey;
// Number of past buckets. One less than the total number of buckets needed
// for the anomaly detection (since the current bucket is not in the past).
const int mNumOfPastBuckets;
// Values for each of the past mNumOfPastBuckets buckets. Always of size mNumOfPastBuckets.
// mPastBuckets[i] can be null, meaning that no data is present in that bucket.
std::vector<shared_ptr<DimToValMap>> mPastBuckets;
// Cached sum over all existing buckets in mPastBuckets.
// Its buckets never contain entries of 0.
DimToValMap mSumOverPastBuckets;
// The bucket number of the last added bucket.
int64_t mMostRecentBucketNum = -1;
// Map from each dimension to the timestamp that its refractory period (if this anomaly was
// declared for that dimension) ends, in seconds. From this moment and onwards, anomalies
// can be declared again.
// Entries may be, but are not guaranteed to be, removed after the period is finished.
unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
// Advances mMostRecentBucketNum to bucketNum, deleting any data that is now too old.
// Specifically, since it is now too old, removes the data for
// [mMostRecentBucketNum - mNumOfPastBuckets + 1, bucketNum - mNumOfPastBuckets].
void advanceMostRecentBucketTo(const int64_t& bucketNum);
// Add the information in the given bucket to mSumOverPastBuckets.
void addBucketToSum(const shared_ptr<DimToValMap>& bucket);
// Subtract the information in the given bucket from mSumOverPastBuckets
// and remove any items with value 0.
void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
// From mSumOverPastBuckets[key], subtracts bucketValue, removing it if it is now 0.
void subtractValueFromSum(const MetricDimensionKey& key, const int64_t& bucketValue);
// Returns true if in the refractory period, else false.
bool isInRefractoryPeriod(const int64_t& timestampNs, const MetricDimensionKey& key) const;
// Calculates the corresponding bucket index within the circular array.
// Requires bucketNum >= 0.
size_t index(int64_t bucketNum) const;
// Resets all bucket data. For use when all the data gets stale.
virtual void resetStorage();
// Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred.
void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue);
FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,115 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "DurationAnomalyTracker.h"
#include "guardrail/StatsdStats.h"
namespace android {
namespace os {
namespace statsd {
DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey,
const sp<AlarmMonitor>& alarmMonitor)
: AnomalyTracker(alert, configKey), mAlarmMonitor(alarmMonitor) {
VLOG("DurationAnomalyTracker() called");
}
DurationAnomalyTracker::~DurationAnomalyTracker() {
VLOG("~DurationAnomalyTracker() called");
cancelAllAlarms();
}
void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
const int64_t& timestampNs) {
// Alarms are stored in secs. Must round up, since if it fires early, it is ignored completely.
uint32_t timestampSec = static_cast<uint32_t>((timestampNs -1) / NS_PER_SEC) + 1; // round up
if (isInRefractoryPeriod(timestampNs, dimensionKey)) {
VLOG("Not setting anomaly alarm since it would fall in the refractory period.");
return;
}
auto itr = mAlarms.find(dimensionKey);
if (itr != mAlarms.end() && mAlarmMonitor != nullptr) {
mAlarmMonitor->remove(itr->second);
}
sp<const InternalAlarm> alarm = new InternalAlarm{timestampSec};
mAlarms[dimensionKey] = alarm;
if (mAlarmMonitor != nullptr) {
mAlarmMonitor->add(alarm);
}
}
void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey,
const int64_t& timestampNs) {
const auto itr = mAlarms.find(dimensionKey);
if (itr == mAlarms.end()) {
return;
}
// If the alarm is set in the past but hasn't fired yet (due to lag), catch it now.
if (itr->second != nullptr && timestampNs >= (int64_t)NS_PER_SEC * itr->second->timestampSec) {
declareAnomaly(timestampNs, mAlert.metric_id(), dimensionKey,
mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) -
itr->second->timestampSec);
}
if (mAlarmMonitor != nullptr) {
mAlarmMonitor->remove(itr->second);
}
mAlarms.erase(dimensionKey);
}
void DurationAnomalyTracker::cancelAllAlarms() {
if (mAlarmMonitor != nullptr) {
for (const auto& itr : mAlarms) {
mAlarmMonitor->remove(itr.second);
}
}
mAlarms.clear();
}
void DurationAnomalyTracker::informAlarmsFired(const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
if (firedAlarms.empty() || mAlarms.empty()) return;
// Find the intersection of firedAlarms and mAlarms.
// The for loop is inefficient, since it loops over all keys, but that's okay since it is very
// seldomly called. The alternative would be having InternalAlarms store information about the
// DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that
// is rarely ever called.
unordered_map<MetricDimensionKey, sp<const InternalAlarm>> matchedAlarms;
for (const auto& kv : mAlarms) {
if (firedAlarms.count(kv.second) > 0) {
matchedAlarms.insert({kv.first, kv.second});
}
}
// Now declare each of these alarms to have fired.
for (const auto& kv : matchedAlarms) {
declareAnomaly(
timestampNs, mAlert.metric_id(), kv.first,
mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - kv.second->timestampSec);
mAlarms.erase(kv.first);
firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it.
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,79 +0,0 @@
/*
* 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 "AlarmMonitor.h"
#include "AnomalyTracker.h"
namespace android {
namespace os {
namespace statsd {
using std::unordered_map;
class DurationAnomalyTracker : public virtual AnomalyTracker {
public:
DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey,
const sp<AlarmMonitor>& alarmMonitor);
virtual ~DurationAnomalyTracker();
// Sets an alarm for the given timestamp.
// Replaces previous alarm if one already exists.
void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime);
// Stops the alarm.
// If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed),
// declare the anomaly now.
void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs);
// Stop all the alarms owned by this tracker. Does not declare any anomalies.
void cancelAllAlarms();
// Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
// and removes it from firedAlarms. The AlarmMonitor is not informed.
// Note that this will generally be called from a different thread from the other functions;
// the caller is responsible for thread safety.
void informAlarmsFired(const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) override;
protected:
// Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
// returns 0.
uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const override {
auto it = mAlarms.find(dimensionKey);
return it == mAlarms.end() ? 0 : it->second->timestampSec;
}
// The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
// are still active.
std::unordered_map<MetricDimensionKey, sp<const InternalAlarm>> mAlarms;
// Anomaly alarm monitor.
sp<AlarmMonitor> mAlarmMonitor;
FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp);
FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,224 +0,0 @@
/*
* 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 <utils/RefBase.h>
#include <unordered_map>
#include <vector>
using namespace android;
namespace android {
namespace os {
namespace statsd {
/** Defines a hash function for sp<const AA>, returning the hash of the underlying pointer. */
template <class AA>
struct SpHash {
size_t operator()(const sp<const AA>& k) const {
return std::hash<const AA*>()(k.get());
}
};
/**
* Min priority queue for generic type AA.
* Unlike a regular priority queue, this class is also capable of removing interior elements.
* @tparam Comparator must implement [bool operator()(sp<const AA> a, sp<const AA> b)], returning
* whether a should be closer to the top of the queue than b.
*/
template <class AA, class Comparator>
class indexed_priority_queue {
public:
indexed_priority_queue();
/** Adds a into the priority queue. If already present or a==nullptr, does nothing. */
void push(sp<const AA> a);
/*
* Removes a from the priority queue. If not present or a==nullptr, does nothing.
* Returns true if a had been present (and is now removed), else false.
*/
bool remove(sp<const AA> a);
/** Removes the top element, if there is one. */
void pop();
/** Removes all elements. */
void clear();
/** Returns whether priority queue contains a (not just a copy of a, but a itself). */
bool contains(sp<const AA> a) const;
/** Returns min element. Returns nullptr iff empty(). */
sp<const AA> top() const;
/** Returns number of elements in priority queue. */
size_t size() const {
return pq.size() - 1;
} // pq is 1-indexed
/** Returns true iff priority queue is empty. */
bool empty() const {
return size() < 1;
}
private:
/** Vector representing a min-heap (1-indexed, with nullptr at 0). */
std::vector<sp<const AA>> pq;
/** Mapping of each element in pq to its index in pq (i.e. the inverse of a=pq[i]). */
std::unordered_map<sp<const AA>, size_t, SpHash<AA>> indices;
void init();
void sift_up(size_t idx);
void sift_down(size_t idx);
/** Returns whether pq[idx1] is considered higher than pq[idx2], according to Comparator. */
bool higher(size_t idx1, size_t idx2) const;
void swap_indices(size_t i, size_t j);
};
// Implementation must be done in this file due to use of template.
template <class AA, class Comparator>
indexed_priority_queue<AA, Comparator>::indexed_priority_queue() {
init();
}
template <class AA, class Comparator>
void indexed_priority_queue<AA, Comparator>::push(sp<const AA> a) {
if (a == nullptr) return;
if (contains(a)) return;
pq.push_back(a);
size_t idx = size(); // index of last element since 1-indexed
indices.insert({a, idx});
sift_up(idx); // get the pq back in order
}
template <class AA, class Comparator>
bool indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) {
if (a == nullptr) return false;
if (!contains(a)) return false;
size_t idx = indices[a];
if (idx >= pq.size()) {
return false;
}
if (idx == size()) { // if a is the last element, i.e. at index idx == size() == (pq.size()-1)
pq.pop_back();
indices.erase(a);
return true;
}
// move last element (guaranteed not to be at idx) to idx, then delete a
sp<const AA> last_a = pq.back();
pq[idx] = last_a;
pq.pop_back();
indices[last_a] = idx;
indices.erase(a);
// get the heap back in order (since the element at idx is not in order)
sift_up(idx);
sift_down(idx);
return true;
}
// The same as, but slightly more efficient than, remove(top()).
template <class AA, class Comparator>
void indexed_priority_queue<AA, Comparator>::pop() {
sp<const AA> a = top();
if (a == nullptr) return;
const size_t idx = 1;
if (idx == size()) { // if a is the last element
pq.pop_back();
indices.erase(a);
return;
}
// move last element (guaranteed not to be at idx) to idx, then delete a
sp<const AA> last_a = pq.back();
pq[idx] = last_a;
pq.pop_back();
indices[last_a] = idx;
indices.erase(a);
// get the heap back in order (since the element at idx is not in order)
sift_down(idx);
}
template <class AA, class Comparator>
void indexed_priority_queue<AA, Comparator>::clear() {
pq.clear();
indices.clear();
init();
}
template <class AA, class Comparator>
sp<const AA> indexed_priority_queue<AA, Comparator>::top() const {
if (empty()) return nullptr;
return pq[1];
}
template <class AA, class Comparator>
void indexed_priority_queue<AA, Comparator>::init() {
pq.push_back(nullptr); // so that pq is 1-indexed.
indices.insert({nullptr, 0}); // just to be consistent with pq.
}
template <class AA, class Comparator>
void indexed_priority_queue<AA, Comparator>::sift_up(size_t idx) {
while (idx > 1) {
size_t parent = idx / 2;
if (higher(idx, parent))
swap_indices(idx, parent);
else
break;
idx = parent;
}
}
template <class AA, class Comparator>
void indexed_priority_queue<AA, Comparator>::sift_down(size_t idx) {
while (2 * idx <= size()) {
size_t child = 2 * idx;
if (child < size() && higher(child + 1, child)) child++;
if (higher(child, idx))
swap_indices(child, idx);
else
break;
idx = child;
}
}
template <class AA, class Comparator>
bool indexed_priority_queue<AA, Comparator>::higher(size_t idx1, size_t idx2) const {
if (!(0u < idx1 && idx1 < pq.size() && 0u < idx2 && idx2 < pq.size())) {
return false; // got to do something.
}
return Comparator()(pq[idx1], pq[idx2]);
}
template <class AA, class Comparator>
bool indexed_priority_queue<AA, Comparator>::contains(sp<const AA> a) const {
if (a == nullptr) return false; // publicly, we pretend that nullptr is not actually in pq.
return indices.count(a) > 0;
}
template <class AA, class Comparator>
void indexed_priority_queue<AA, Comparator>::swap_indices(size_t i, size_t j) {
if (!(0u < i && i < pq.size() && 0u < j && j < pq.size())) {
return;
}
sp<const AA> val_i = pq[i];
sp<const AA> val_j = pq[j];
pq[i] = val_j;
pq[j] = val_i;
indices[val_i] = j;
indices[val_j] = i;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,70 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "external/Perfetto.h"
#include "subscriber/IncidentdReporter.h"
#include "subscriber/SubscriberReporter.h"
namespace android {
namespace os {
namespace statsd {
void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey,
int64_t metricValue, const ConfigKey& configKey,
const std::vector<Subscription>& subscriptions) {
VLOG("informSubscribers called.");
if (subscriptions.empty()) {
VLOG("No Subscriptions were associated.");
return;
}
for (const Subscription& subscription : subscriptions) {
if (subscription.probability_of_informing() < 1
&& ((float)rand() / (float)RAND_MAX) >= subscription.probability_of_informing()) {
// Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
// The config writer was advised to use -0.1 and 1.1 for never/always.
ALOGI("Fate decided that a subscriber would not be informed.");
continue;
}
switch (subscription.subscriber_information_case()) {
case Subscription::SubscriberInformationCase::kIncidentdDetails:
if (!GenerateIncidentReport(subscription.incidentd_details(), ruleId, metricId,
dimensionKey, metricValue, configKey)) {
ALOGW("Failed to generate incident report.");
}
break;
case Subscription::SubscriberInformationCase::kPerfettoDetails:
if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(),
subscription.id(), ruleId, configKey)) {
ALOGW("Failed to generate perfetto traces.");
}
break;
case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
SubscriberReporter::getInstance().alertBroadcastSubscriber(configKey, subscription,
dimensionKey);
break;
default:
break;
}
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,33 +0,0 @@
/*
* 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.
*/
#pragma once
#include "config/ConfigKey.h"
#include "HashableDimensionKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
namespace os {
namespace statsd {
void triggerSubscribers(const int64_t ruleId, const int64_t metricId,
const MetricDimensionKey& dimensionKey, int64_t metricValue,
const ConfigKey& configKey, const std::vector<Subscription>& subscriptions);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,187 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "CombinationConditionTracker.h"
namespace android {
namespace os {
namespace statsd {
using std::unordered_map;
using std::vector;
CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index)
: ConditionTracker(id, index) {
VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
}
CombinationConditionTracker::~CombinationConditionTracker() {
VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId);
}
bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig,
const vector<sp<ConditionTracker>>& allConditionTrackers,
const unordered_map<int64_t, int>& conditionIdIndexMap,
vector<bool>& stack,
vector<ConditionState>& initialConditionCache) {
VLOG("Combination predicate init() %lld", (long long)mConditionId);
if (mInitialized) {
return true;
}
// mark this node as visited in the recursion stack.
stack[mIndex] = true;
Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination();
if (!combinationCondition.has_operation()) {
return false;
}
mLogicalOperation = combinationCondition.operation();
if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) {
return false;
}
for (auto child : combinationCondition.predicate()) {
auto it = conditionIdIndexMap.find(child);
if (it == conditionIdIndexMap.end()) {
ALOGW("Predicate %lld not found in the config", (long long)child);
return false;
}
int childIndex = it->second;
const auto& childTracker = allConditionTrackers[childIndex];
// if the child is a visited node in the recursion -> circle detected.
if (stack[childIndex]) {
ALOGW("Circle detected!!!");
return false;
}
bool initChildSucceeded =
childTracker->init(allConditionConfig, allConditionTrackers, conditionIdIndexMap,
stack, initialConditionCache);
if (!initChildSucceeded) {
ALOGW("Child initialization failed %lld ", (long long)child);
return false;
} else {
VLOG("Child initialization success %lld ", (long long)child);
}
if (allConditionTrackers[childIndex]->isSliced()) {
setSliced(true);
mSlicedChildren.push_back(childIndex);
} else {
mUnSlicedChildren.push_back(childIndex);
}
mChildren.push_back(childIndex);
mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
childTracker->getLogTrackerIndex().end());
}
mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation,
initialConditionCache);
initialConditionCache[mIndex] =
evaluateCombinationCondition(mChildren, mLogicalOperation, initialConditionCache);
// unmark this node in the recursion stack.
stack[mIndex] = false;
mInitialized = true;
return true;
}
void CombinationConditionTracker::isConditionMet(
const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
const bool isPartialLink,
vector<ConditionState>& conditionCache) 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,
isPartialLink,
conditionCache);
}
}
conditionCache[mIndex] =
evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
}
void CombinationConditionTracker::evaluateCondition(
const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& nonSlicedConditionCache,
std::vector<bool>& conditionChangedCache) {
// value is up to date.
if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
return;
}
for (const int childIndex : mChildren) {
// So far, this is fine as there is at most one child having sliced output.
if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
const sp<ConditionTracker>& child = mAllConditions[childIndex];
child->evaluateCondition(event, eventMatcherValues, mAllConditions,
nonSlicedConditionCache, conditionChangedCache);
}
}
ConditionState newCondition =
evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
if (!mSliced) {
bool nonSlicedChanged = (mUnSlicedPartCondition != newCondition);
mUnSlicedPartCondition = newCondition;
nonSlicedConditionCache[mIndex] = mUnSlicedPartCondition;
conditionChangedCache[mIndex] = nonSlicedChanged;
} else {
mUnSlicedPartCondition = 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.
if (conditionChangedCache[childIndex]) {
conditionChangedCache[mIndex] = true;
break;
}
}
nonSlicedConditionCache[mIndex] = newCondition;
VLOG("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId,
conditionChangedCache[mIndex] == true);
}
}
bool CombinationConditionTracker::equalOutputDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensions) const {
if (mSlicedChildren.size() != 1 ||
mSlicedChildren.front() >= (int)allConditions.size() ||
mLogicalOperation != LogicalOperation::AND) {
return false;
}
const sp<ConditionTracker>& slicedChild = allConditions.at(mSlicedChildren.front());
return slicedChild->equalOutputDimensions(allConditions, dimensions);
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,111 +0,0 @@
/*
* 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.
*/
#ifndef COMBINATION_CONDITION_TRACKER_H
#define COMBINATION_CONDITION_TRACKER_H
#include "ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
namespace os {
namespace statsd {
class CombinationConditionTracker : public virtual ConditionTracker {
public:
CombinationConditionTracker(const int64_t& id, const int index);
~CombinationConditionTracker();
bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
std::vector<ConditionState>& initialConditionCache) override;
void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache) override;
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
const bool isPartialLink,
std::vector<ConditionState>& conditionCache) const override;
// Only one child predicate can have dimension.
const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
const std::vector<sp<ConditionTracker>>& allConditions) const override {
for (const auto& child : mChildren) {
auto result = allConditions[child]->getChangedToTrueDimensions(allConditions);
if (result != nullptr) {
return result;
}
}
return nullptr;
}
// Only one child predicate can have dimension.
const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
const std::vector<sp<ConditionTracker>>& allConditions) const override {
for (const auto& child : mChildren) {
auto result = allConditions[child]->getChangedToFalseDimensions(allConditions);
if (result != nullptr) {
return result;
}
}
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;
// Store index of the children Predicates.
// We don't store string name of the Children, because we want to get rid of the hash map to
// 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
} // namespace os
} // namespace android
#endif // COMBINATION_CONDITION_TRACKER_H

View File

@@ -1,83 +0,0 @@
/*
* Copyright (C) 2019 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 <gtest/gtest_prod.h>
#include <stdint.h>
namespace android {
namespace os {
namespace statsd {
/**
* A simple stopwatch to time the duration of condition being true.
*
* The owner of the stopwatch (MetricProducer) is responsible to notify the stopwatch when condition
* changes (start/pause), and when to start a new bucket (a new lap basically). All timestamps
* should be elapsedRealTime in nano seconds.
*
* Keep the timer simple and inline everything. This class is *NOT* thread safe. Caller is
* responsible for thread safety.
*/
class ConditionTimer {
public:
explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) {
if (initCondition) {
mLastConditionTrueTimestampNs = bucketStartNs;
}
};
// Tracks how long the condition has been stayed true in the *current* bucket.
// When a new bucket is created, this value will be reset to 0.
int64_t mTimerNs = 0;
// Last elapsed real timestamp when condition turned to true
// When a new bucket is created and the condition is true, then the timestamp is set
// to be the bucket start timestamp.
int64_t mLastConditionTrueTimestampNs = 0;
bool mCondition = false;
int64_t newBucketStart(int64_t nextBucketStartNs) {
if (mCondition) {
mTimerNs += (nextBucketStartNs - mLastConditionTrueTimestampNs);
mLastConditionTrueTimestampNs = nextBucketStartNs;
}
int64_t temp = mTimerNs;
mTimerNs = 0;
return temp;
}
void onConditionChanged(bool newCondition, int64_t timestampNs) {
if (newCondition == mCondition) {
return;
}
mCondition = newCondition;
if (newCondition) {
mLastConditionTrueTimestampNs = timestampNs;
} else {
mTimerNs += (timestampNs - mLastConditionTrueTimestampNs);
}
}
FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False);
FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_True);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,156 +0,0 @@
/*
* 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 "condition/condition_util.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/LogMatchingTracker.h"
#include "matchers/matcher_util.h"
#include <utils/RefBase.h>
#include <unordered_map>
namespace android {
namespace os {
namespace statsd {
class ConditionTracker : public virtual RefBase {
public:
ConditionTracker(const int64_t& id, const int index)
: mConditionId(id),
mIndex(index),
mInitialized(false),
mTrackerIndex(),
mUnSlicedPartCondition(ConditionState::kUnknown),
mSliced(false){};
virtual ~ConditionTracker(){};
inline const int64_t& getId() { return mConditionId; }
// Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
// be done in the constructor, but we do it separately because (1) easy to return a bool to
// indicate whether the initialization is successful. (2) makes unit test easier.
// allConditionConfig: the list of all Predicate config from statsd_config.
// allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
// need to call init() on children conditions)
// conditionIdIndexMap: the mapping from condition id to its index.
// stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
// initialConditionCache: tracks initial conditions of all ConditionTrackers.
virtual bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& conditionIdIndexMap,
std::vector<bool>& stack,
std::vector<ConditionState>& initialConditionCache) = 0;
// evaluate current condition given the new event.
// event: the new log event
// eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process
// event before ConditionTrackers, because ConditionTracker depends on
// LogMatchingTrackers.
// mAllConditions: the list of all ConditionTracker
// conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event.
// conditionChanged: the bit map to record whether the condition has changed.
// If the condition has dimension, then any sub condition changes will report
// conditionChanged.
virtual void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
std::vector<bool>& conditionChanged) = 0;
// Query the condition with parameters.
// [conditionParameters]: a map from condition name to the HashableDimensionKey to query the
// condition.
// [allConditions]: all condition trackers. This is needed because the condition evaluation is
// done recursively
// [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.
virtual void isConditionMet(
const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
const bool isPartialLink,
std::vector<ConditionState>& conditionCache) const = 0;
// return the list of LogMatchingTracker index that this ConditionTracker uses.
virtual const std::set<int>& getLogTrackerIndex() const {
return mTrackerIndex;
}
virtual void setSliced(bool sliced) {
mSliced = mSliced | sliced;
}
inline bool isSliced() const {
return mSliced;
}
virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
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;
// Return the current condition state of the unsliced part of the condition.
inline ConditionState getUnSlicedPartConditionState() const {
return mUnSlicedPartCondition;
}
protected:
const int64_t mConditionId;
// the index of this condition in the manager's condition list.
const int mIndex;
// if it's properly initialized.
bool mInitialized;
// the list of LogMatchingTracker index that this ConditionTracker uses.
std::set<int> mTrackerIndex;
// This variable is only used for CombinationConditionTrackers.
// SimpleConditionTrackers technically don't have an unsliced part because
// they are either sliced or unsliced.
//
// CombinationConditionTrackers have multiple children ConditionTrackers
// that can be a mixture of sliced or unsliced. This tracks the
// condition of the unsliced part of the combination condition.
ConditionState mUnSlicedPartCondition;
bool mSliced;
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,70 +0,0 @@
/*
* 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 "ConditionWizard.h"
namespace android {
namespace os {
namespace statsd {
using std::vector;
ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters,
const bool isPartialLink) {
vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
mAllConditions[index]->isConditionMet(
parameters, mAllConditions, isPartialLink,
cache);
return cache[index];
}
const set<HashableDimensionKey>* ConditionWizard::getChangedToTrueDimensions(
const int index) const {
return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions);
}
const set<HashableDimensionKey>* ConditionWizard::getChangedToFalseDimensions(
const int index) const {
return mAllConditions[index]->getChangedToFalseDimensions(mAllConditions);
}
bool ConditionWizard::IsChangedDimensionTrackable(const int index) {
if (index >= 0 && index < (int)mAllConditions.size()) {
return mAllConditions[index]->IsChangedDimensionTrackable();
} else {
return false;
}
}
bool ConditionWizard::IsSimpleCondition(const int index) {
if (index >= 0 && index < (int)mAllConditions.size()) {
return mAllConditions[index]->IsSimpleCondition();
} else {
return false;
}
}
bool ConditionWizard::equalOutputDimensions(const int index, const vector<Matcher>& dimensions) {
if (index >= 0 && index < (int)mAllConditions.size()) {
return mAllConditions[index]->equalOutputDimensions(mAllConditions, dimensions);
} else {
return false;
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,68 +0,0 @@
/*
* 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.
*/
#ifndef CONDITION_WIZARD_H
#define CONDITION_WIZARD_H
#include "ConditionTracker.h"
#include "condition_util.h"
#include "stats_util.h"
namespace android {
namespace os {
namespace statsd {
// Held by MetricProducer, to query a condition state with input defined in MetricConditionLink.
class ConditionWizard : public virtual android::RefBase {
public:
ConditionWizard(){}; // for testing
explicit ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers)
: mAllConditions(conditionTrackers){};
virtual ~ConditionWizard(){};
// Query condition state, for a ConditionTracker at [conditionIndex], with [conditionParameters]
// [conditionParameters] mapping from condition name to the HashableDimensionKey to query the
// condition.
// The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case,
// the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters,
const bool isPartialLink);
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;
};
} // namespace statsd
} // namespace os
} // namespace android
#endif // CONDITION_WIZARD_H

View File

@@ -1,386 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "SimpleConditionTracker.h"
#include "guardrail/StatsdStats.h"
namespace android {
namespace os {
namespace statsd {
using std::unordered_map;
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), mContainANYPositionInInternalDimensions(false) {
VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId);
mCountNesting = simplePredicate.count_nesting();
if (simplePredicate.has_start()) {
auto pair = trackerNameIndexMap.find(simplePredicate.start());
if (pair == trackerNameIndexMap.end()) {
ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
return;
}
mStartLogMatcherIndex = pair->second;
mTrackerIndex.insert(mStartLogMatcherIndex);
} else {
mStartLogMatcherIndex = -1;
}
if (simplePredicate.has_stop()) {
auto pair = trackerNameIndexMap.find(simplePredicate.stop());
if (pair == trackerNameIndexMap.end()) {
ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
return;
}
mStopLogMatcherIndex = pair->second;
mTrackerIndex.insert(mStopLogMatcherIndex);
} else {
mStopLogMatcherIndex = -1;
}
if (simplePredicate.has_stop_all()) {
auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
if (pair == trackerNameIndexMap.end()) {
ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all());
return;
}
mStopAllLogMatcherIndex = pair->second;
mTrackerIndex.insert(mStopAllLogMatcherIndex);
} else {
mStopAllLogMatcherIndex = -1;
}
if (simplePredicate.has_dimensions()) {
translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
if (mOutputDimensions.size() > 0) {
mSliced = true;
mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
}
mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions());
}
if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
mInitialValue = ConditionState::kFalse;
} else {
mInitialValue = ConditionState::kUnknown;
}
mInitialized = true;
}
SimpleConditionTracker::~SimpleConditionTracker() {
VLOG("~SimpleConditionTracker()");
}
bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
const vector<sp<ConditionTracker>>& allConditionTrackers,
const unordered_map<int64_t, int>& conditionIdIndexMap,
vector<bool>& stack,
vector<ConditionState>& initialConditionCache) {
// SimpleConditionTracker does not have dependency on other conditions, thus we just return
// if the initialization was successful.
initialConditionCache[mIndex] = mInitialValue;
return mInitialized;
}
void SimpleConditionTracker::dumpState() {
VLOG("%lld DUMP:", (long long)mConditionId);
for (const auto& pair : mSlicedConditionState) {
VLOG("\t%s : %d", pair.first.toString().c_str(), pair.second);
}
VLOG("Changed to true keys: \n");
for (const auto& key : mLastChangedToTrueDimensions) {
VLOG("%s", key.toString().c_str());
}
VLOG("Changed to false keys: \n");
for (const auto& key : mLastChangedToFalseDimensions) {
VLOG("%s", key.toString().c_str());
}
}
void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
std::vector<bool>& conditionChangedCache) {
// Unless the default condition is false, and there was nothing started, otherwise we have
// triggered a condition change.
conditionChangedCache[mIndex] =
(mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
: true;
for (const auto& cond : mSlicedConditionState) {
if (cond.second > 0) {
mLastChangedToFalseDimensions.insert(cond.first);
}
}
// After StopAll, we know everything has stopped. From now on, default condition is false.
mInitialValue = ConditionState::kFalse;
mSlicedConditionState.clear();
conditionCache[mIndex] = ConditionState::kFalse;
}
bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) {
// if the condition is not sliced or the key is not new, we are good!
return false;
}
// 1. Report the tuple count if the tuple count > soft limit
if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mSlicedConditionState.size() + 1;
StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
// 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.toString().c_str());
return true;
}
}
return false;
}
void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
bool matchStart, ConditionState* conditionCache,
bool* conditionChangedCache) {
bool changed = false;
auto outputIt = mSlicedConditionState.find(outputKey);
ConditionState newCondition;
if (hitGuardRail(outputKey)) {
(*conditionChangedCache) = false;
// Tells the caller it's evaluated.
(*conditionCache) = ConditionState::kUnknown;
return;
}
if (outputIt == mSlicedConditionState.end()) {
// We get a new output key.
newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
if (matchStart && mInitialValue != ConditionState::kTrue) {
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[outputKey] = 0;
mLastChangedToFalseDimensions.insert(outputKey);
changed = true;
}
} else {
// we have history about this output key.
auto& startedCount = outputIt->second;
// assign the old value first.
newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
if (matchStart) {
if (startedCount == 0) {
mLastChangedToTrueDimensions.insert(outputKey);
// This condition for this output key will change from false -> true
changed = true;
}
// it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
// as 1 if not counting nesting.
startedCount++;
newCondition = ConditionState::kTrue;
} else {
// This is a stop event.
if (startedCount > 0) {
if (mCountNesting) {
startedCount--;
if (startedCount == 0) {
newCondition = ConditionState::kFalse;
}
} else {
// not counting nesting, so ignore the number of starts, stop now.
startedCount = 0;
newCondition = ConditionState::kFalse;
}
// if everything has stopped for this output key, condition true -> false;
if (startedCount == 0) {
mLastChangedToFalseDimensions.insert(outputKey);
changed = true;
}
}
// 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.toString().c_str());
}
}
}
// dump all dimensions for debugging
if (DEBUG) {
dumpState();
}
(*conditionChangedCache) = changed;
(*conditionCache) = newCondition;
VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId,
conditionChangedCache[mIndex] == true);
}
void SimpleConditionTracker::evaluateCondition(
const LogEvent& event,
const vector<MatchingState>& eventMatcherValues,
const vector<sp<ConditionTracker>>& mAllConditions,
vector<ConditionState>& conditionCache,
vector<bool>& conditionChangedCache) {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
VLOG("Yes, already evaluated, %lld %d",
(long long)mConditionId, conditionCache[mIndex]);
return;
}
mLastChangedToTrueDimensions.clear();
mLastChangedToFalseDimensions.clear();
if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) &&
eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
handleStopAll(conditionCache, conditionChangedCache);
return;
}
int matchedState = -1;
// Note: The order to evaluate the following start, stop, stop_all matters.
// The priority of overwrite is stop_all > stop > start.
if (mStartLogMatcherIndex >= 0 &&
eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
matchedState = 1;
}
if (mStopLogMatcherIndex >= 0 &&
eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
matchedState = 0;
}
if (matchedState < 0) {
// The event doesn't match this condition. So we just report existing condition values.
conditionChangedCache[mIndex] = false;
if (mSliced) {
// if the condition result is sliced. The overall condition is true if any of the sliced
// condition is true
conditionCache[mIndex] = mInitialValue;
for (const auto& slicedCondition : mSlicedConditionState) {
if (slicedCondition.second > 0) {
conditionCache[mIndex] = ConditionState::kTrue;
break;
}
}
} else {
const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
if (itr == mSlicedConditionState.end()) {
// condition not sliced, but we haven't seen the matched start or stop yet. so
// return initial value.
conditionCache[mIndex] = mInitialValue;
} else {
// return the cached condition.
conditionCache[mIndex] =
itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
}
}
return;
}
ConditionState overallState = mInitialValue;
bool overallChanged = false;
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 {
ALOGE("The condition tracker should not be sliced by ANY position matcher.");
}
conditionCache[mIndex] = overallState;
conditionChangedCache[mIndex] = overallChanged;
}
void SimpleConditionTracker::isConditionMet(
const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
const bool isPartialLink,
vector<ConditionState>& conditionCache) const {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
VLOG("Yes, already evaluated, %lld %d",
(long long)mConditionId, conditionCache[mIndex]);
return;
}
const auto pair = conditionParameters.find(mConditionId);
if (pair == conditionParameters.end()) {
ConditionState conditionState = ConditionState::kNotEvaluated;
conditionState = conditionState | mInitialValue;
if (!mSliced) {
const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
if (itr != mSlicedConditionState.end()) {
ConditionState sliceState =
itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
conditionState = conditionState | sliceState;
}
}
conditionCache[mIndex] = conditionState;
return;
}
ConditionState conditionState = ConditionState::kNotEvaluated;
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;
}
}
} 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;
}
}
conditionCache[mIndex] = conditionState;
VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,138 +0,0 @@
/*
* 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.
*/
#ifndef SIMPLE_CONDITION_TRACKER_H
#define SIMPLE_CONDITION_TRACKER_H
#include <gtest/gtest_prod.h>
#include "ConditionTracker.h"
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
namespace android {
namespace os {
namespace statsd {
class SimpleConditionTracker : public virtual ConditionTracker {
public:
SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const int index,
const SimplePredicate& simplePredicate,
const std::unordered_map<int64_t, int>& trackerNameIndexMap);
~SimpleConditionTracker();
bool init(const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
std::vector<ConditionState>& initialConditionCache) override;
void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache) override;
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
const bool isPartialLink,
std::vector<ConditionState>& conditionCache) const override;
virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
const std::vector<sp<ConditionTracker>>& allConditions) const {
if (mSliced) {
return &mLastChangedToTrueDimensions;
} else {
return nullptr;
}
}
virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
const std::vector<sp<ConditionTracker>>& allConditions) const {
if (mSliced) {
return &mLastChangedToFalseDimensions;
} else {
return nullptr;
}
}
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.
int mStartLogMatcherIndex;
// The index of the LogEventMatcher which defines the end.
int mStopLogMatcherIndex;
// if the start end needs to be nested.
bool mCountNesting;
// The index of the LogEventMatcher which defines the stop all.
int mStopAllLogMatcherIndex;
ConditionState mInitialValue;
std::vector<Matcher> mOutputDimensions;
bool mContainANYPositionInInternalDimensions;
std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
int mDimensionTag;
std::map<HashableDimensionKey, int> mSlicedConditionState;
void handleStopAll(std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache);
void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
ConditionState* conditionCache, bool* changedCache);
bool hitGuardRail(const HashableDimensionKey& newKey);
void dumpState();
FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
};
} // namespace statsd
} // namespace os
} // namespace android
#endif // SIMPLE_CONDITION_TRACKER_H

View File

@@ -1,93 +0,0 @@
/*
* 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 "Log.h"
#include "condition_util.h"
#include "../matchers/matcher_util.h"
#include "ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
namespace android {
namespace os {
namespace statsd {
using std::vector;
ConditionState evaluateCombinationCondition(const std::vector<int>& children,
const LogicalOperation& operation,
const std::vector<ConditionState>& conditionCache) {
ConditionState newCondition;
bool hasUnknown = false;
bool hasFalse = false;
bool hasTrue = false;
for (auto childIndex : children) {
ConditionState childState = conditionCache[childIndex];
if (childState == ConditionState::kUnknown) {
hasUnknown = true;
break;
}
if (childState == ConditionState::kFalse) {
hasFalse = true;
}
if (childState == ConditionState::kTrue) {
hasTrue = true;
}
}
// If any child condition is in unknown state, the condition is unknown too.
if (hasUnknown) {
return ConditionState::kUnknown;
}
switch (operation) {
case LogicalOperation::AND: {
newCondition = hasFalse ? ConditionState::kFalse : ConditionState::kTrue;
break;
}
case LogicalOperation::OR: {
newCondition = hasTrue ? ConditionState::kTrue : ConditionState::kFalse;
break;
}
case LogicalOperation::NOT:
newCondition = children.empty() ? ConditionState::kUnknown :
((conditionCache[children[0]] == ConditionState::kFalse) ?
ConditionState::kTrue : ConditionState::kFalse);
break;
case LogicalOperation::NAND:
newCondition = hasFalse ? ConditionState::kTrue : ConditionState::kFalse;
break;
case LogicalOperation::NOR:
newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue;
break;
case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
newCondition = ConditionState::kFalse;
break;
}
return newCondition;
}
ConditionState operator|(ConditionState l, ConditionState r) {
return l >= r ? l : r;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,43 +0,0 @@
/*
* 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.
*/
#ifndef CONDITION_UTIL_H
#define CONDITION_UTIL_H
#include <vector>
#include "../matchers/matcher_util.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
namespace os {
namespace statsd {
enum ConditionState {
kNotEvaluated = -2,
kUnknown = -1,
kFalse = 0,
kTrue = 1,
};
ConditionState operator|(ConditionState l, ConditionState r);
ConditionState evaluateCombinationCondition(const std::vector<int>& children,
const LogicalOperation& operation,
const std::vector<ConditionState>& conditionCache);
} // namespace statsd
} // namespace os
} // namespace android
#endif // CONDITION_UTIL_H

View File

@@ -1,54 +0,0 @@
/*
* 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 "config/ConfigKey.h"
namespace android {
namespace os {
namespace statsd {
ConfigKey::ConfigKey() {
}
ConfigKey::ConfigKey(const ConfigKey& that) : mId(that.mId), mUid(that.mUid) {
}
ConfigKey::ConfigKey(int uid, const int64_t& id) : mId(id), mUid(uid) {
}
ConfigKey::~ConfigKey() {
}
string ConfigKey::ToString() const {
string s;
s += "(" + std::to_string(mUid) + " " + std::to_string(mId) + ")";
return s;
}
int64_t StrToInt64(const string& str) {
char* endp;
int64_t value;
value = strtoll(str.c_str(), &endp, 0);
if (endp == str.c_str() || *endp != '\0') {
value = 0;
}
return value;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,89 +0,0 @@
/*
* 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/statsd_config.pb.h"
#include <string>
namespace android {
namespace os {
namespace statsd {
using std::hash;
using std::string;
/**
* Uniquely identifies a configuration.
*/
class ConfigKey {
public:
ConfigKey();
ConfigKey(const ConfigKey& that);
ConfigKey(int uid, const int64_t& id);
~ConfigKey();
inline int GetUid() const {
return mUid;
}
inline const int64_t& GetId() const {
return mId;
}
inline bool operator<(const ConfigKey& that) const {
if (mUid < that.mUid) {
return true;
}
if (mUid > that.mUid) {
return false;
}
return mId < that.mId;
};
inline bool operator==(const ConfigKey& that) const {
return mUid == that.mUid && mId == that.mId;
};
string ToString() const;
private:
int64_t mId;
int mUid;
};
int64_t StrToInt64(const string& str);
} // namespace statsd
} // namespace os
} // namespace android
/**
* A hash function for ConfigKey so it can be used for unordered_map/set.
* Unfortunately this has to go in std namespace because C++ is fun!
*/
namespace std {
using android::os::statsd::ConfigKey;
template <>
struct hash<ConfigKey> {
std::size_t operator()(const ConfigKey& key) const {
return (7 * key.GetUid()) ^ ((hash<long long>()(key.GetId())));
}
};
} // namespace std

View File

@@ -1,31 +0,0 @@
/*
* 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 "config/ConfigListener.h"
namespace android {
namespace os {
namespace statsd {
ConfigListener::ConfigListener() {
}
ConfigListener::~ConfigListener() {
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,52 +0,0 @@
/*
* 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 "config/ConfigKey.h"
#include <utils/RefBase.h>
namespace android {
namespace os {
namespace statsd {
using android::RefBase;
/**
* Callback for different subsystems inside statsd to implement to find out
* when a configuration has been added, updated or removed.
*/
class ConfigListener : public virtual RefBase {
public:
ConfigListener();
virtual ~ConfigListener();
/**
* A configuration was added or updated.
*/
virtual void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
const StatsdConfig& config) = 0;
/**
* A configuration was removed.
*/
virtual void OnConfigRemoved(const ConfigKey& key) = 0;
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,375 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "config/ConfigManager.h"
#include "storage/StorageManager.h"
#include "guardrail/StatsdStats.h"
#include "stats_log_util.h"
#include "stats_util.h"
#include "stats_log_util.h"
#include <stdio.h>
#include <vector>
#include "android-base/stringprintf.h"
namespace android {
namespace os {
namespace statsd {
using std::pair;
using std::string;
using std::vector;
#define STATS_SERVICE_DIR "/data/misc/stats-service"
using android::base::StringPrintf;
using std::unique_ptr;
struct ConfigReceiverDeathCookie {
ConfigReceiverDeathCookie(const wp<ConfigManager>& configManager, const ConfigKey& configKey,
const shared_ptr<IPendingIntentRef>& pir) :
mConfigManager(configManager), mConfigKey(configKey), mPir(pir) {
}
wp<ConfigManager> mConfigManager;
ConfigKey mConfigKey;
shared_ptr<IPendingIntentRef> mPir;
};
void ConfigManager::configReceiverDied(void* cookie) {
auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
if (!thiz) {
return;
}
ConfigKey& configKey = cookie_->mConfigKey;
shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
// Erase the mapping from the config key to the config receiver (pir) if the
// mapping still exists.
lock_guard<mutex> lock(thiz->mMutex);
auto it = thiz->mConfigReceivers.find(configKey);
if (it != thiz->mConfigReceivers.end() && it->second == pir) {
thiz->mConfigReceivers.erase(configKey);
}
// The death recipient corresponding to this specific pir can never be
// triggered again, so free up resources.
delete cookie_;
}
struct ActiveConfigChangedReceiverDeathCookie {
ActiveConfigChangedReceiverDeathCookie(const wp<ConfigManager>& configManager, const int uid,
const shared_ptr<IPendingIntentRef>& pir) :
mConfigManager(configManager), mUid(uid), mPir(pir) {
}
wp<ConfigManager> mConfigManager;
int mUid;
shared_ptr<IPendingIntentRef> mPir;
};
void ConfigManager::activeConfigChangedReceiverDied(void* cookie) {
auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
if (!thiz) {
return;
}
int uid = cookie_->mUid;
shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
// Erase the mapping from the config key to the active config changed
// receiver (pir) if the mapping still exists.
lock_guard<mutex> lock(thiz->mMutex);
auto it = thiz->mActiveConfigsChangedReceivers.find(uid);
if (it != thiz->mActiveConfigsChangedReceivers.end() && it->second == pir) {
thiz->mActiveConfigsChangedReceivers.erase(uid);
}
// The death recipient corresponding to this specific pir can never
// be triggered again, so free up resources.
delete cookie_;
}
ConfigManager::ConfigManager() :
mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)),
mActiveConfigChangedReceiverDeathRecipient(
AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) {
}
ConfigManager::~ConfigManager() {
}
void ConfigManager::Startup() {
map<ConfigKey, StatsdConfig> configsFromDisk;
StorageManager::readConfigFromDisk(configsFromDisk);
for (const auto& pair : configsFromDisk) {
UpdateConfig(pair.first, pair.second);
}
}
void ConfigManager::StartupForTest() {
// Dummy function to avoid reading configs from disks for tests.
}
void ConfigManager::AddListener(const sp<ConfigListener>& listener) {
lock_guard<mutex> lock(mMutex);
mListeners.push_back(listener);
}
void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& config) {
vector<sp<ConfigListener>> broadcastList;
{
lock_guard <mutex> lock(mMutex);
const int numBytes = config.ByteSize();
vector<uint8_t> buffer(numBytes);
config.SerializeToArray(&buffer[0], numBytes);
auto uidIt = mConfigs.find(key.GetUid());
// GuardRail: Limit the number of configs per uid.
if (uidIt != mConfigs.end()) {
auto it = uidIt->second.find(key);
if (it == uidIt->second.end() &&
uidIt->second.size() >= StatsdStats::kMaxConfigCountPerUid) {
ALOGE("ConfigManager: uid %d has exceeded the config count limit", key.GetUid());
return;
}
}
// Check if it's a duplicate config.
if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end() &&
StorageManager::hasIdenticalConfig(key, buffer)) {
// This is a duplicate config.
ALOGI("ConfigManager This is a duplicate config %s", key.ToString().c_str());
// Update saved file on disk. We still update timestamp of file when
// there exists a duplicate configuration to avoid garbage collection.
update_saved_configs_locked(key, buffer, numBytes);
return;
}
// Update saved file on disk.
update_saved_configs_locked(key, buffer, numBytes);
// Add to set.
mConfigs[key.GetUid()].insert(key);
for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
const int64_t timestampNs = getElapsedRealtimeNs();
// Tell everyone
for (const sp<ConfigListener>& listener : broadcastList) {
listener->OnConfigUpdated(timestampNs, key, config);
}
}
void ConfigManager::SetConfigReceiver(const ConfigKey& key,
const shared_ptr<IPendingIntentRef>& pir) {
lock_guard<mutex> lock(mMutex);
mConfigReceivers[key] = pir;
AIBinder_linkToDeath(pir->asBinder().get(), mConfigReceiverDeathRecipient.get(),
new ConfigReceiverDeathCookie(this, key, pir));
}
void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
lock_guard<mutex> lock(mMutex);
mConfigReceivers.erase(key);
}
void ConfigManager::SetActiveConfigsChangedReceiver(const int uid,
const shared_ptr<IPendingIntentRef>& pir) {
{
lock_guard<mutex> lock(mMutex);
mActiveConfigsChangedReceivers[uid] = pir;
}
AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(),
new ActiveConfigChangedReceiverDeathCookie(this, uid, pir));
}
void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) {
lock_guard<mutex> lock(mMutex);
mActiveConfigsChangedReceivers.erase(uid);
}
void ConfigManager::RemoveConfig(const ConfigKey& key) {
vector<sp<ConfigListener>> broadcastList;
{
lock_guard <mutex> lock(mMutex);
auto uid = key.GetUid();
auto uidIt = mConfigs.find(uid);
if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) {
// Remove from map
uidIt->second.erase(key);
for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
// Remove from disk. There can still be a lingering file on disk so we check
// whether or not the config was on memory.
remove_saved_configs(key);
}
for (const sp<ConfigListener>& listener:broadcastList) {
listener->OnConfigRemoved(key);
}
}
void ConfigManager::remove_saved_configs(const ConfigKey& key) {
string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
StorageManager::deleteSuffixedFiles(STATS_SERVICE_DIR, suffix.c_str());
}
void ConfigManager::RemoveConfigs(int uid) {
vector<ConfigKey> removed;
vector<sp<ConfigListener>> broadcastList;
{
lock_guard <mutex> lock(mMutex);
auto uidIt = mConfigs.find(uid);
if (uidIt == mConfigs.end()) {
return;
}
for (auto it = uidIt->second.begin(); it != uidIt->second.end(); ++it) {
// Remove from map
remove_saved_configs(*it);
removed.push_back(*it);
}
mConfigs.erase(uidIt);
for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
// Remove separately so if they do anything in the callback they can't mess up our iteration.
for (auto& key : removed) {
// Tell everyone
for (const sp<ConfigListener>& listener:broadcastList) {
listener->OnConfigRemoved(key);
}
}
}
void ConfigManager::RemoveAllConfigs() {
vector<ConfigKey> removed;
vector<sp<ConfigListener>> broadcastList;
{
lock_guard <mutex> lock(mMutex);
for (auto uidIt = mConfigs.begin(); uidIt != mConfigs.end();) {
for (auto it = uidIt->second.begin(); it != uidIt->second.end();) {
// Remove from map
removed.push_back(*it);
it = uidIt->second.erase(it);
}
uidIt = mConfigs.erase(uidIt);
}
for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
// Remove separately so if they do anything in the callback they can't mess up our iteration.
for (auto& key : removed) {
// Tell everyone
for (const sp<ConfigListener>& listener:broadcastList) {
listener->OnConfigRemoved(key);
}
}
}
vector<ConfigKey> ConfigManager::GetAllConfigKeys() const {
lock_guard<mutex> lock(mMutex);
vector<ConfigKey> ret;
for (auto uidIt = mConfigs.cbegin(); uidIt != mConfigs.cend(); ++uidIt) {
for (auto it = uidIt->second.cbegin(); it != uidIt->second.cend(); ++it) {
ret.push_back(*it);
}
}
return ret;
}
const shared_ptr<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
lock_guard<mutex> lock(mMutex);
auto it = mConfigReceivers.find(key);
if (it == mConfigReceivers.end()) {
return nullptr;
} else {
return it->second;
}
}
const shared_ptr<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid)
const {
lock_guard<mutex> lock(mMutex);
auto it = mActiveConfigsChangedReceivers.find(uid);
if (it == mActiveConfigsChangedReceivers.end()) {
return nullptr;
} else {
return it->second;
}
}
void ConfigManager::Dump(FILE* out) {
lock_guard<mutex> lock(mMutex);
fprintf(out, "CONFIGURATIONS\n");
fprintf(out, " uid name\n");
for (auto uidIt = mConfigs.cbegin(); uidIt != mConfigs.cend(); ++uidIt) {
for (auto it = uidIt->second.cbegin(); it != uidIt->second.cend(); ++it) {
fprintf(out, " %6d %lld\n", it->GetUid(), (long long)it->GetId());
auto receiverIt = mConfigReceivers.find(*it);
if (receiverIt != mConfigReceivers.end()) {
fprintf(out, " -> received by PendingIntent as binder\n");
}
}
}
}
void ConfigManager::update_saved_configs_locked(const ConfigKey& key,
const vector<uint8_t>& buffer,
const int numBytes) {
// If there is a pre-existing config with same key we should first delete it.
remove_saved_configs(key);
// Then we save the latest config.
string file_name =
StringPrintf("%s/%ld_%d_%lld", STATS_SERVICE_DIR, time(nullptr),
key.GetUid(), (long long)key.GetId());
StorageManager::writeFile(file_name.c_str(), &buffer[0], numBytes);
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,181 +0,0 @@
/*
* 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 "config/ConfigKey.h"
#include "config/ConfigListener.h"
#include <aidl/android/os/IPendingIntentRef.h>
#include <mutex>
#include <string>
#include <stdio.h>
using aidl::android::os::IPendingIntentRef;
using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
/**
* Keeps track of which configurations have been set from various sources.
*/
class ConfigManager : public virtual android::RefBase {
public:
ConfigManager();
virtual ~ConfigManager();
/**
* Initialize ConfigListener by reading from disk and get updates.
*/
void Startup();
/*
* Dummy initializer for tests.
*/
void StartupForTest();
/**
* Someone else wants to know about the configs.
*/
void AddListener(const sp<ConfigListener>& listener);
/**
* A configuration was added or updated.
*
* Reports this to listeners.
*/
void UpdateConfig(const ConfigKey& key, const StatsdConfig& data);
/**
* Sets the broadcast receiver for a configuration key.
*/
void SetConfigReceiver(const ConfigKey& key, const shared_ptr<IPendingIntentRef>& pir);
/**
* Returns the package name and class name representing the broadcast receiver for this config.
*/
const shared_ptr<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
/**
* Returns all config keys registered.
*/
std::vector<ConfigKey> GetAllConfigKeys() const;
/**
* Erase any broadcast receiver associated with this config key.
*/
void RemoveConfigReceiver(const ConfigKey& key);
/**
* Sets the broadcast receiver that is notified whenever the list of active configs
* changes for this uid.
*/
void SetActiveConfigsChangedReceiver(const int uid, const shared_ptr<IPendingIntentRef>& pir);
/**
* Returns the broadcast receiver for active configs changed for this uid.
*/
const shared_ptr<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
/**
* Erase any active configs changed broadcast receiver associated with this uid.
*/
void RemoveActiveConfigsChangedReceiver(const int uid);
/**
* A configuration was removed.
*
* Reports this to listeners.
*/
void RemoveConfig(const ConfigKey& key);
/**
* Remove all of the configs for the given uid.
*/
void RemoveConfigs(int uid);
/**
* Remove all of the configs from memory.
*/
void RemoveAllConfigs();
/**
* Text dump of our state for debugging.
*/
void Dump(FILE* out);
private:
mutable std::mutex mMutex;
/**
* Save the configs to disk.
*/
void update_saved_configs_locked(const ConfigKey& key,
const std::vector<uint8_t>& buffer,
const int numBytes);
/**
* Remove saved configs from disk.
*/
void remove_saved_configs(const ConfigKey& key);
/**
* Maps from uid to the config keys that have been set.
*/
std::map<int, std::set<ConfigKey>> mConfigs;
/**
* Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef.
*/
std::map<ConfigKey, shared_ptr<IPendingIntentRef>> mConfigReceivers;
/**
* Each uid can be subscribed by up to one receiver to notify that the list of active configs
* for this uid has changed. The receiver is specified as IPendingIntentRef.
*/
std::map<int, shared_ptr<IPendingIntentRef>> mActiveConfigsChangedReceivers;
/**
* The ConfigListeners that will be told about changes.
*/
std::vector<sp<ConfigListener>> mListeners;
// Death recipients that are triggered when the host process holding an
// IPendingIntentRef dies.
::ndk::ScopedAIBinder_DeathRecipient mConfigReceiverDeathRecipient;
::ndk::ScopedAIBinder_DeathRecipient mActiveConfigChangedReceiverDeathRecipient;
/**
* Death recipient callback that is called when a config receiver dies.
* The cookie is a pointer to a ConfigReceiverDeathCookie.
*/
static void configReceiverDied(void* cookie);
/**
* Death recipient callback that is called when an active config changed
* receiver dies. The cookie is a pointer to an
* ActiveConfigChangedReceiverDeathCookie.
*/
static void activeConfigChangedReceiverDied(void* cookie);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,29 +0,0 @@
/*
* Copyright (C) 2020 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.
*/
syntax = "proto2";
package android.os.statsd;
option java_package = "com.android.internal.os";
option java_outer_classname = "ExperimentIdsProto";
// StatsLogProcessor uses the proto to parse experiment ids from
// BinaryPushStateChanged atoms. This needs to be in sync with
// TrainExperimentIds in atoms.proto.
message ExperimentIds {
repeated int64 experiment_id = 1;
}

View File

@@ -1,139 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "config/ConfigKey.h"
#include "Log.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
#include <android-base/unique_fd.h>
#include <inttypes.h>
#include <sys/wait.h>
#include <string>
namespace {
const char kDropboxTag[] = "perfetto";
}
namespace android {
namespace os {
namespace statsd {
bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
int64_t subscription_id,
int64_t alert_id,
const ConfigKey& configKey) {
VLOG("Starting trace collection through perfetto");
if (!config.has_trace_config()) {
ALOGE("The perfetto trace config is empty, aborting");
return false;
}
char subscriptionId[25];
char alertId[25];
char configId[25];
char configUid[25];
snprintf(subscriptionId, sizeof(subscriptionId), "%" PRId64, subscription_id);
snprintf(alertId, sizeof(alertId), "%" PRId64, alert_id);
snprintf(configId, sizeof(configId), "%" PRId64, configKey.GetId());
snprintf(configUid, sizeof(configUid), "%d", configKey.GetUid());
android::base::unique_fd readPipe;
android::base::unique_fd writePipe;
if (!android::base::Pipe(&readPipe, &writePipe)) {
ALOGE("pipe() failed while calling the Perfetto client: %s", strerror(errno));
return false;
}
pid_t pid = fork();
if (pid < 0) {
ALOGE("fork() failed while calling the Perfetto client: %s", strerror(errno));
return false;
}
if (pid == 0) {
// Child process.
// No malloc calls or library calls after this point. Remember that even
// ALOGx (aka android_printLog()) can use dynamic memory for vsprintf().
writePipe.reset(); // Close the write end (owned by the main process).
// Replace stdin with |readPipe| so the main process can write into it.
if (dup2(readPipe.get(), STDIN_FILENO) < 0) _exit(1);
readPipe.reset();
// Replace stdout/stderr with /dev/null and close any other file
// descriptor. This is to avoid SELinux complaining about perfetto
// trying to access files accidentally left open by statsd (i.e. files
// that have been opened without the O_CLOEXEC flag).
int devNullFd = open("/dev/null", O_RDWR | O_CLOEXEC);
if (dup2(devNullFd, STDOUT_FILENO) < 0) _exit(2);
if (dup2(devNullFd, STDERR_FILENO) < 0) _exit(3);
close(devNullFd);
for (int i = 0; i < 1024; i++) {
if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) close(i);
}
execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox",
kDropboxTag, "--alert-id", alertId, "--config-id", configId, "--config-uid",
configUid, "--subscription-id", subscriptionId, nullptr);
// execl() doesn't return in case of success, if we get here something
// failed.
_exit(4);
}
// Main process.
readPipe.reset(); // Close the read end (owned by the child process).
// Using fdopen() because fwrite() has the right logic to chunking write()
// over a pipe (see __sfvwrite()).
FILE* writePipeStream = android::base::Fdopen(std::move(writePipe), "wb");
if (!writePipeStream) {
ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno));
return false;
}
const std::string& cfgProto = config.trace_config();
size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream);
fclose(writePipeStream);
if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) {
ALOGE("fwrite() failed (ret: %zd) while calling the Perfetto client: %s", bytesWritten,
strerror(errno));
return false;
}
// This does NOT wait for the full duration of the trace. It just waits until
// the process has read the config from stdin and detached.
int childStatus = 0;
waitpid(pid, &childStatus, 0);
if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) {
ALOGE("Child process failed (0x%x) while calling the Perfetto client", childStatus);
return false;
}
VLOG("CollectPerfettoTraceAndUploadToDropbox() succeeded");
return true;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,37 +0,0 @@
/*
* 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.
*/
#pragma once
namespace android {
namespace os {
namespace statsd {
class ConfigKey;
class PerfettoDetails; // Declared in statsd_config.pb.h
// Starts the collection of a Perfetto trace with the given |config|.
// The trace is uploaded to Dropbox by the perfetto cmdline util once done.
// This method returns immediately after passing the config and does NOT wait
// for the full duration of the trace.
bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
int64_t subscription_id,
int64_t alert_id,
const ConfigKey& configKey);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,41 +0,0 @@
/*
* 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 <utils/RefBase.h>
#include "StatsPuller.h"
#include "logd/LogEvent.h"
namespace android {
namespace os {
namespace statsd {
class PullDataReceiver : virtual public RefBase{
public:
virtual ~PullDataReceiver() {}
/**
* @param data The pulled data.
* @param pullSuccess Whether the pull succeeded. If the pull does not succeed, the data for the
* bucket should be invalidated.
* @param originalPullTimeNs This is when all the pulls have been initiated (elapsed time).
*/
virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data,
bool pullSuccess, int64_t originalPullTimeNs) = 0;
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,39 +0,0 @@
/*
* Copyright (C) 2019 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 "PullResultReceiver.h"
namespace android {
namespace os {
namespace statsd {
PullResultReceiver::PullResultReceiver(
std::function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCb)
: pullFinishCallback(std::move(pullFinishCb)) {
}
Status PullResultReceiver::pullFinished(int32_t atomTag, bool success,
const vector<StatsEventParcel>& output) {
pullFinishCallback(atomTag, success, output);
return Status::ok();
}
PullResultReceiver::~PullResultReceiver() {
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,48 +0,0 @@
/*
* Copyright (C) 2019 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 <aidl/android/os/BnPullAtomResultReceiver.h>
#include <aidl/android/util/StatsEventParcel.h>
using namespace std;
using Status = ::ndk::ScopedAStatus;
using aidl::android::os::BnPullAtomResultReceiver;
using aidl::android::util::StatsEventParcel;
namespace android {
namespace os {
namespace statsd {
class PullResultReceiver : public BnPullAtomResultReceiver {
public:
PullResultReceiver(function<void(int32_t, bool, const vector<StatsEventParcel>&)>
pullFinishCallback);
~PullResultReceiver();
/**
* Binder call for finishing a pull.
*/
Status pullFinished(int32_t atomTag, bool success,
const vector<StatsEventParcel>& output) override;
private:
function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCallback;
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,39 +0,0 @@
/*
* Copyright (C) 2020 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 <utils/RefBase.h>
#include "StatsPuller.h"
#include "logd/LogEvent.h"
namespace android {
namespace os {
namespace statsd {
class PullUidProvider : virtual public RefBase {
public:
virtual ~PullUidProvider() {}
/**
* @param atomId The atom for which to get the uids.
*/
virtual vector<int32_t> getPullAtomUids(int32_t atomId) = 0;
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,116 +0,0 @@
/*
* Copyright (C) 2019 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "StatsCallbackPuller.h"
#include "PullResultReceiver.h"
#include "StatsPullerManager.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
#include <aidl/android/util/StatsEventParcel.h>
using namespace std;
using Status = ::ndk::ScopedAStatus;
using aidl::android::util::StatsEventParcel;
using ::ndk::SharedRefBase;
namespace android {
namespace os {
namespace statsd {
StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
const int64_t coolDownNs, int64_t timeoutNs,
const vector<int> additiveFields)
: StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) {
VLOG("StatsCallbackPuller created for tag %d", tagId);
}
bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
VLOG("StatsCallbackPuller called for tag %d", mTagId);
if(mCallback == nullptr) {
ALOGW("No callback registered");
return false;
}
// Shared variables needed in the result receiver.
shared_ptr<mutex> cv_mutex = make_shared<mutex>();
shared_ptr<condition_variable> cv = make_shared<condition_variable>();
shared_ptr<bool> pullFinish = make_shared<bool>(false);
shared_ptr<bool> pullSuccess = make_shared<bool>(false);
shared_ptr<vector<shared_ptr<LogEvent>>> sharedData =
make_shared<vector<shared_ptr<LogEvent>>>();
shared_ptr<PullResultReceiver> resultReceiver = SharedRefBase::make<PullResultReceiver>(
[cv_mutex, cv, pullFinish, pullSuccess, sharedData](
int32_t atomTag, bool success, const vector<StatsEventParcel>& output) {
// This is the result of the pull, executing in a statsd binder thread.
// The pull could have taken a long time, and we should only modify
// data (the output param) if the pointer is in scope and the pull did not time out.
{
lock_guard<mutex> lk(*cv_mutex);
for (const StatsEventParcel& parcel: output) {
shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1);
bool valid = event->parseBuffer((uint8_t*)parcel.buffer.data(),
parcel.buffer.size());
if (valid) {
sharedData->push_back(event);
} else {
StatsdStats::getInstance().noteAtomError(event->GetTagId(),
/*pull=*/true);
}
}
*pullSuccess = success;
*pullFinish = true;
}
cv->notify_one();
});
// Initiate the pull. This is a oneway call to a different process, except
// in unit tests. In process calls are not oneway.
Status status = mCallback->onPullAtom(mTagId, resultReceiver);
if (!status.isOk()) {
StatsdStats::getInstance().notePullBinderCallFailed(mTagId);
return false;
}
{
unique_lock<mutex> unique_lk(*cv_mutex);
// Wait until the pull finishes, or until the pull timeout.
cv->wait_for(unique_lk, chrono::nanoseconds(mPullTimeoutNs),
[pullFinish] { return *pullFinish; });
if (!*pullFinish) {
// Note: The parent stats puller will also note that there was a timeout and that the
// cache should be cleared. Once we migrate all pullers to this callback, we could
// consolidate the logic.
return true;
} else {
// Only copy the data if we did not timeout and the pull was successful.
if (*pullSuccess) {
*data = std::move(*sharedData);
}
VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId);
return *pullSuccess;
}
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,46 +0,0 @@
/*
* Copyright (C) 2019 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 <aidl/android/os/IPullAtomCallback.h>
#include "StatsPuller.h"
using aidl::android::os::IPullAtomCallback;
using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
class StatsCallbackPuller : public StatsPuller {
public:
explicit StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
const int64_t coolDownNs, const int64_t timeoutNs,
const std::vector<int> additiveFields);
private:
bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
const shared_ptr<IPullAtomCallback> mCallback;
FRIEND_TEST(StatsCallbackPullerTest, PullFail);
FRIEND_TEST(StatsCallbackPullerTest, PullSuccess);
FRIEND_TEST(StatsCallbackPullerTest, PullTimeout);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,126 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "StatsPuller.h"
#include "StatsPullerManager.h"
#include "guardrail/StatsdStats.h"
#include "puller_util.h"
#include "stats_log_util.h"
namespace android {
namespace os {
namespace statsd {
using std::lock_guard;
sp<UidMap> StatsPuller::mUidMap = nullptr;
void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_t pullTimeoutNs,
const std::vector<int> additiveFields)
: mTagId(tagId),
mPullTimeoutNs(pullTimeoutNs),
mCoolDownNs(coolDownNs),
mAdditiveFields(additiveFields),
mLastPullTimeNs(0),
mLastEventTimeNs(0) {
}
bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
lock_guard<std::mutex> lock(mLock);
const int64_t elapsedTimeNs = getElapsedRealtimeNs();
const int64_t systemUptimeMillis = getSystemUptimeMillis();
StatsdStats::getInstance().notePull(mTagId);
const bool shouldUseCache =
(mLastEventTimeNs == eventTimeNs) || (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs);
if (shouldUseCache) {
if (mHasGoodData) {
(*data) = mCachedData;
StatsdStats::getInstance().notePullFromCache(mTagId);
}
return mHasGoodData;
}
if (mLastPullTimeNs > 0) {
StatsdStats::getInstance().updateMinPullIntervalSec(
mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC);
}
mCachedData.clear();
mLastPullTimeNs = elapsedTimeNs;
mLastEventTimeNs = eventTimeNs;
mHasGoodData = PullInternal(&mCachedData);
if (!mHasGoodData) {
return mHasGoodData;
}
const int64_t pullElapsedDurationNs = getElapsedRealtimeNs() - elapsedTimeNs;
const int64_t pullSystemUptimeDurationMillis = getSystemUptimeMillis() - systemUptimeMillis;
StatsdStats::getInstance().notePullTime(mTagId, pullElapsedDurationNs);
const bool pullTimeOut = pullElapsedDurationNs > mPullTimeoutNs;
if (pullTimeOut) {
// Something went wrong. Discard the data.
mCachedData.clear();
mHasGoodData = false;
StatsdStats::getInstance().notePullTimeout(
mTagId, pullSystemUptimeDurationMillis, NanoToMillis(pullElapsedDurationNs));
ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId,
(long long)pullElapsedDurationNs);
return mHasGoodData;
}
if (mCachedData.size() > 0) {
mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId, mAdditiveFields);
}
if (mCachedData.empty()) {
VLOG("Data pulled is empty");
StatsdStats::getInstance().noteEmptyData(mTagId);
}
(*data) = mCachedData;
return mHasGoodData;
}
int StatsPuller::ForceClearCache() {
return clearCache();
}
int StatsPuller::clearCache() {
lock_guard<std::mutex> lock(mLock);
return clearCacheLocked();
}
int StatsPuller::clearCacheLocked() {
int ret = mCachedData.size();
mCachedData.clear();
mLastPullTimeNs = 0;
mLastEventTimeNs = 0;
return ret;
}
int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) {
if (timestampNs - mLastPullTimeNs > mCoolDownNs) {
return clearCache();
} else {
return 0;
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,119 +0,0 @@
/*
* 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 <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
#include <mutex>
#include <vector>
#include "packages/UidMap.h"
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
#include "puller_util.h"
using aidl::android::os::IStatsCompanionService;
using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
class StatsPuller : public virtual RefBase {
public:
explicit StatsPuller(const int tagId,
const int64_t coolDownNs = NS_PER_SEC,
const int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs,
const std::vector<int> additiveFields = std::vector<int>());
virtual ~StatsPuller() {}
// Pulls the most recent data.
// The data may be served from cache if consecutive pulls come within
// predefined cooldown time.
// Returns true if the pull was successful.
// Returns false when
// 1) the pull fails
// 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
// If a metric wants to make any change to the data, like timestamps, it
// should make a copy as this data may be shared with multiple metrics.
bool Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
int ForceClearCache();
// Clear cache if elapsed time is more than cooldown time
int ClearCacheIfNecessary(int64_t timestampNs);
static void SetUidMap(const sp<UidMap>& uidMap);
virtual void SetStatsCompanionService(
shared_ptr<IStatsCompanionService> statsCompanionService) {};
protected:
const int mTagId;
// Max time allowed to pull this atom.
// We cannot reliably kill a pull thread. So we don't terminate the puller.
// The data is discarded if the pull takes longer than this and mHasGoodData
// marked as false.
const int64_t mPullTimeoutNs = StatsdStats::kPullMaxDelayNs;
private:
mutable std::mutex mLock;
// Real puller impl.
virtual bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) = 0;
bool mHasGoodData = false;
// Minimum time before this puller does actual pull again.
// Pullers can cause significant impact to system health and battery.
// So that we don't pull too frequently.
// If a pull request comes before cooldown, a cached version from previous pull
// will be returned.
const int64_t mCoolDownNs = 1 * NS_PER_SEC;
// The field numbers of the fields that need to be summed when merging
// isolated uid with host uid.
const std::vector<int> mAdditiveFields;
int64_t mLastPullTimeNs;
// All pulls happen due to an event (app upgrade, bucket boundary, condition change, etc).
// If multiple pulls need to be done at the same event time, we will always use the cache after
// the first pull.
int64_t mLastEventTimeNs;
// Cache of data from last pull. If next request comes before cool down finishes,
// cached data will be returned.
// Cached data is cleared when
// 1) A pull fails
// 2) A new pull request comes after cooldown time.
// 3) clearCache is called.
std::vector<std::shared_ptr<LogEvent>> mCachedData;
int clearCache();
int clearCacheLocked();
static sp<UidMap> mUidMap;
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,371 +0,0 @@
/*
* 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.
*/
#define DEBUG false
#include "Log.h"
#include "StatsPullerManager.h"
#include <cutils/log.h>
#include <math.h>
#include <stdint.h>
#include <algorithm>
#include <iostream>
#include "../StatsService.h"
#include "../logd/LogEvent.h"
#include "../stats_log_util.h"
#include "../statscompanion_util.h"
#include "StatsCallbackPuller.h"
#include "TrainInfoPuller.h"
#include "statslog_statsd.h"
using std::shared_ptr;
using std::vector;
namespace android {
namespace os {
namespace statsd {
// Stores the puller as a wp to avoid holding a reference in case it is unregistered and
// pullAtomCallbackDied is never called.
struct PullAtomCallbackDeathCookie {
PullAtomCallbackDeathCookie(const wp<StatsPullerManager>& pullerManager,
const PullerKey& pullerKey, const wp<StatsPuller>& puller) :
mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
}
wp<StatsPullerManager> mPullerManager;
PullerKey mPullerKey;
wp<StatsPuller> mPuller;
};
void StatsPullerManager::pullAtomCallbackDied(void* cookie) {
PullAtomCallbackDeathCookie* cookie_ = static_cast<PullAtomCallbackDeathCookie*>(cookie);
sp<StatsPullerManager> thiz = cookie_->mPullerManager.promote();
if (!thiz) {
return;
}
const PullerKey& pullerKey = cookie_->mPullerKey;
wp<StatsPuller> puller = cookie_->mPuller;
// Erase the mapping from the puller key to the puller if the mapping still exists.
// Note that we are removing the StatsPuller object, which internally holds the binder
// IPullAtomCallback. However, each new registration creates a new StatsPuller, so this works.
lock_guard<mutex> lock(thiz->mLock);
const auto& it = thiz->kAllPullAtomInfo.find(pullerKey);
if (it != thiz->kAllPullAtomInfo.end() && puller != nullptr && puller == it->second) {
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(pullerKey.atomTag,
/*registered=*/false);
thiz->kAllPullAtomInfo.erase(pullerKey);
}
// The death recipient corresponding to this specific IPullAtomCallback can never
// be triggered again, so free up resources.
delete cookie_;
}
// Values smaller than this may require to update the alarm.
const int64_t NO_ALARM_UPDATE = INT64_MAX;
StatsPullerManager::StatsPullerManager()
: kAllPullAtomInfo({
// TrainInfo.
{{.atomTag = util::TRAIN_INFO, .uid = AID_STATSD}, new TrainInfoPuller()},
}),
mNextPullTimeNs(NO_ALARM_UPDATE),
mPullAtomCallbackDeathRecipient(AIBinder_DeathRecipient_new(pullAtomCallbackDied)) {
}
bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
vector<shared_ptr<LogEvent>>* data) {
std::lock_guard<std::mutex> _l(mLock);
return PullLocked(tagId, configKey, eventTimeNs, data);
}
bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
std::lock_guard<std::mutex> _l(mLock);
return PullLocked(tagId, uids, eventTimeNs, data);
}
bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
vector<int32_t> uids;
const auto& uidProviderIt = mPullUidProviders.find(configKey);
if (uidProviderIt == mPullUidProviders.end()) {
ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
configKey.ToString().c_str());
StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
return false;
}
sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
if (pullUidProvider == nullptr) {
ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
configKey.ToString().c_str());
StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
return false;
}
uids = pullUidProvider->getPullAtomUids(tagId);
return PullLocked(tagId, uids, eventTimeNs, data);
}
bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
VLOG("Initiating pulling %d", tagId);
for (int32_t uid : uids) {
PullerKey key = {.atomTag = tagId, .uid = uid};
auto pullerIt = kAllPullAtomInfo.find(key);
if (pullerIt != kAllPullAtomInfo.end()) {
bool ret = pullerIt->second->Pull(eventTimeNs, data);
VLOG("pulled %zu items", data->size());
if (!ret) {
StatsdStats::getInstance().notePullFailed(tagId);
}
return ret;
}
}
StatsdStats::getInstance().notePullerNotFound(tagId);
ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
}
bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
// Pulled atoms might be registered after we parse the config, so just make sure the id is in
// an appropriate range.
return isVendorPulledAtom(tagId) || isPulledAtom(tagId);
}
void StatsPullerManager::updateAlarmLocked() {
if (mNextPullTimeNs == NO_ALARM_UPDATE) {
VLOG("No need to set alarms. Skipping");
return;
}
// TODO(b/151045771): do not hold a lock while making a binder call
if (mStatsCompanionService != nullptr) {
mStatsCompanionService->setPullingAlarm(mNextPullTimeNs / 1000000);
} else {
VLOG("StatsCompanionService not available. Alarm not set.");
}
return;
}
void StatsPullerManager::SetStatsCompanionService(
shared_ptr<IStatsCompanionService> statsCompanionService) {
std::lock_guard<std::mutex> _l(mLock);
shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
for (const auto& pulledAtom : kAllPullAtomInfo) {
pulledAtom.second->SetStatsCompanionService(statsCompanionService);
}
if (mStatsCompanionService != nullptr) {
updateAlarmLocked();
}
}
void StatsPullerManager::RegisterReceiver(int tagId, const ConfigKey& configKey,
wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
int64_t intervalNs) {
std::lock_guard<std::mutex> _l(mLock);
auto& receivers = mReceivers[{.atomTag = tagId, .configKey = configKey}];
for (auto it = receivers.begin(); it != receivers.end(); it++) {
if (it->receiver == receiver) {
VLOG("Receiver already registered of %d", (int)receivers.size());
return;
}
}
ReceiverInfo receiverInfo;
receiverInfo.receiver = receiver;
// Round it to the nearest minutes. This is the limit of alarm manager.
// In practice, we should always have larger buckets.
int64_t roundedIntervalNs = intervalNs / NS_PER_SEC / 60 * NS_PER_SEC * 60;
// Scheduled pulling should be at least 1 min apart.
// This can be lower in cts tests, in which case we round it to 1 min.
if (roundedIntervalNs < 60 * (int64_t)NS_PER_SEC) {
roundedIntervalNs = 60 * (int64_t)NS_PER_SEC;
}
receiverInfo.intervalNs = roundedIntervalNs;
receiverInfo.nextPullTimeNs = nextPullTimeNs;
receivers.push_back(receiverInfo);
// There is only one alarm for all pulled events. So only set it to the smallest denom.
if (nextPullTimeNs < mNextPullTimeNs) {
VLOG("Updating next pull time %lld", (long long)mNextPullTimeNs);
mNextPullTimeNs = nextPullTimeNs;
updateAlarmLocked();
}
VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
}
void StatsPullerManager::UnRegisterReceiver(int tagId, const ConfigKey& configKey,
wp<PullDataReceiver> receiver) {
std::lock_guard<std::mutex> _l(mLock);
auto receiversIt = mReceivers.find({.atomTag = tagId, .configKey = configKey});
if (receiversIt == mReceivers.end()) {
VLOG("Unknown pull code or no receivers: %d", tagId);
return;
}
std::list<ReceiverInfo>& receivers = receiversIt->second;
for (auto it = receivers.begin(); it != receivers.end(); it++) {
if (receiver == it->receiver) {
receivers.erase(it);
VLOG("Puller for tagId %d unregistered of %d", tagId, (int)receivers.size());
return;
}
}
}
void StatsPullerManager::RegisterPullUidProvider(const ConfigKey& configKey,
wp<PullUidProvider> provider) {
std::lock_guard<std::mutex> _l(mLock);
mPullUidProviders[configKey] = provider;
}
void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey,
wp<PullUidProvider> provider) {
std::lock_guard<std::mutex> _l(mLock);
const auto& it = mPullUidProviders.find(configKey);
if (it != mPullUidProviders.end() && it->second == provider) {
mPullUidProviders.erase(it);
}
}
void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
std::lock_guard<std::mutex> _l(mLock);
int64_t wallClockNs = getWallClockNs();
int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
vector<pair<const ReceiverKey*, vector<ReceiverInfo*>>> needToPull;
for (auto& pair : mReceivers) {
vector<ReceiverInfo*> receivers;
if (pair.second.size() != 0) {
for (ReceiverInfo& receiverInfo : pair.second) {
if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) {
receivers.push_back(&receiverInfo);
} else {
if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
minNextPullTimeNs = receiverInfo.nextPullTimeNs;
}
}
}
if (receivers.size() > 0) {
needToPull.push_back(make_pair(&pair.first, receivers));
}
}
}
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey,
elapsedTimeNs, &data);
if (!pullSuccess) {
VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
}
// Convention is to mark pull atom timestamp at request time.
// If we pull at t0, puller starts at t1, finishes at t2, and send back
// at t3, we mark t0 as its timestamp, which should correspond to its
// triggering event, such as condition change at t0.
// Here the triggering event is alarm fired from AlarmManager.
// In ValueMetricProducer and GaugeMetricProducer we do same thing
// when pull on condition change, etc.
for (auto& event : data) {
event->setElapsedTimestampNs(elapsedTimeNs);
event->setLogdWallClockTimestampNs(wallClockNs);
}
for (const auto& receiverInfo : pullInfo.second) {
sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
if (receiverPtr != nullptr) {
receiverPtr->onDataPulled(data, pullSuccess, elapsedTimeNs);
// We may have just come out of a coma, compute next pull time.
int numBucketsAhead =
(elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs;
receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs;
if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
minNextPullTimeNs = receiverInfo->nextPullTimeNs;
}
} else {
VLOG("receiver already gone.");
}
}
}
VLOG("mNextPullTimeNs: %lld updated to %lld", (long long)mNextPullTimeNs,
(long long)minNextPullTimeNs);
mNextPullTimeNs = minNextPullTimeNs;
updateAlarmLocked();
}
int StatsPullerManager::ForceClearPullerCache() {
std::lock_guard<std::mutex> _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second->ForceClearCache();
}
return totalCleared;
}
int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
std::lock_guard<std::mutex> _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs);
}
return totalCleared;
}
void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
const int64_t coolDownNs, const int64_t timeoutNs,
const vector<int32_t>& additiveFields,
const shared_ptr<IPullAtomCallback>& callback) {
std::lock_guard<std::mutex> _l(mLock);
VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
if (callback == nullptr) {
ALOGW("SetPullAtomCallback called with null callback for atom %d.", atomTag);
return;
}
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
int64_t actualCoolDownNs = coolDownNs < kMinCoolDownNs ? kMinCoolDownNs : coolDownNs;
int64_t actualTimeoutNs = timeoutNs > kMaxTimeoutNs ? kMaxTimeoutNs : timeoutNs;
sp<StatsCallbackPuller> puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs,
actualTimeoutNs, additiveFields);
PullerKey key = {.atomTag = atomTag, .uid = uid};
AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(),
new PullAtomCallbackDeathCookie(this, key, puller));
kAllPullAtomInfo[key] = puller;
}
void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) {
std::lock_guard<std::mutex> _l(mLock);
PullerKey key = {.atomTag = atomTag, .uid = uid};
if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) {
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag,
/*registered=*/false);
kAllPullAtomInfo.erase(key);
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,189 +0,0 @@
/*
* 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 <aidl/android/os/IPullAtomCallback.h>
#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
#include <list>
#include <vector>
#include "PullDataReceiver.h"
#include "PullUidProvider.h"
#include "StatsPuller.h"
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
#include "packages/UidMap.h"
using aidl::android::os::IPullAtomCallback;
using aidl::android::os::IStatsCompanionService;
using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
typedef struct PullerKey {
// The uid of the process that registers this puller.
const int uid = -1;
// The atom that this puller is for.
const int atomTag;
bool operator<(const PullerKey& that) const {
if (uid < that.uid) {
return true;
}
if (uid > that.uid) {
return false;
}
return atomTag < that.atomTag;
};
bool operator==(const PullerKey& that) const {
return uid == that.uid && atomTag == that.atomTag;
};
} PullerKey;
class StatsPullerManager : public virtual RefBase {
public:
StatsPullerManager();
virtual ~StatsPullerManager() {
}
// Registers a receiver for tagId. It will be pulled on the nextPullTimeNs
// and then every intervalNs thereafter.
virtual void RegisterReceiver(int tagId, const ConfigKey& configKey,
wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
int64_t intervalNs);
// Stop listening on a tagId.
virtual void UnRegisterReceiver(int tagId, const ConfigKey& configKey,
wp<PullDataReceiver> receiver);
// Registers a pull uid provider for the config key. When pulling atoms, it will be used to
// determine which uids to pull from.
virtual void RegisterPullUidProvider(const ConfigKey& configKey, wp<PullUidProvider> provider);
// Unregister a pull uid provider.
virtual void UnregisterPullUidProvider(const ConfigKey& configKey,
wp<PullUidProvider> provider);
// Verify if we know how to pull for this matcher
bool PullerForMatcherExists(int tagId) const;
void OnAlarmFired(int64_t elapsedTimeNs);
// Pulls the most recent data.
// The data may be served from cache if consecutive pulls come within
// mCoolDownNs.
// Returns true if the pull was successful.
// Returns false when
// 1) the pull fails
// 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
// 3) Either a PullUidProvider was not registered for the config, or the there was no puller
// registered for any of the uids for this atom.
// If the metric wants to make any change to the data, like timestamps, they
// should make a copy as this data may be shared with multiple metrics.
virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data);
// Same as above, but directly specify the allowed uids to pull from.
virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data);
// Clear pull data cache immediately.
int ForceClearPullerCache();
// Clear pull data cache if it is beyond respective cool down time.
int ClearPullerCacheIfNecessary(int64_t timestampNs);
void SetStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
const int64_t timeoutNs, const vector<int32_t>& additiveFields,
const shared_ptr<IPullAtomCallback>& callback);
void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
private:
const static int64_t kMinCoolDownNs = NS_PER_SEC;
const static int64_t kMaxTimeoutNs = 10 * NS_PER_SEC;
shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
// A struct containing an atom id and a Config Key
typedef struct ReceiverKey {
const int atomTag;
const ConfigKey configKey;
inline bool operator<(const ReceiverKey& that) const {
return atomTag == that.atomTag ? configKey < that.configKey : atomTag < that.atomTag;
}
} ReceiverKey;
typedef struct {
int64_t nextPullTimeNs;
int64_t intervalNs;
wp<PullDataReceiver> receiver;
} ReceiverInfo;
// mapping from Receiver Key to receivers
std::map<ReceiverKey, std::list<ReceiverInfo>> mReceivers;
// mapping from Config Key to the PullUidProvider for that config
std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data);
bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data);
// locks for data receiver and StatsCompanionService changes
std::mutex mLock;
void updateAlarmLocked();
int64_t mNextPullTimeNs;
// Death recipient that is triggered when the process holding the IPullAtomCallback has died.
::ndk::ScopedAIBinder_DeathRecipient mPullAtomCallbackDeathRecipient;
/**
* Death recipient callback that is called when a pull atom callback dies.
* The cookie is a pointer to a PullAtomCallbackDeathCookie.
*/
static void pullAtomCallbackDied(void* cookie);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
FRIEND_TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,55 +0,0 @@
/*
* Copyright (C) 2019 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "external/StatsPuller.h"
#include "TrainInfoPuller.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
#include "statslog_statsd.h"
#include "storage/StorageManager.h"
using std::make_shared;
using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
TrainInfoPuller::TrainInfoPuller() :
StatsPuller(util::TRAIN_INFO) {
}
bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
vector<InstallTrainInfo> trainInfoList =
StorageManager::readAllTrainInfo();
if (trainInfoList.empty()) {
ALOGW("Train info was empty.");
return true;
}
for (InstallTrainInfo& trainInfo : trainInfoList) {
auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
data->push_back(event);
}
return true;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) 2019 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 "StatsPuller.h"
namespace android {
namespace os {
namespace statsd {
/**
* Reads train info from disk.
*/
class TrainInfoPuller : public StatsPuller {
public:
TrainInfoPuller();
private:
bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,153 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "puller_util.h"
namespace android {
namespace os {
namespace statsd {
using namespace std;
/**
* Process all data and merge isolated with host if necessary.
* For example:
* NetworkBytesAtom {
* int uid = 1;
* State process_state = 2;
* int byte_send = 3;
* int byte_recv = 4;
* }
* additive fields are {3, 4}
* If we pulled the following events (uid1_child is an isolated uid which maps to uid1):
* [uid1, fg, 100, 200]
* [uid1_child, fg, 100, 200]
* [uid1, bg, 100, 200]
*
* We want to merge them and results should be:
* [uid1, fg, 200, 400]
* [uid1, bg, 100, 200]
*
* All atoms should be of the same tagId. All fields should be present.
*/
void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
int tagId, const vector<int>& additiveFieldsVec) {
// Check the first LogEvent for attribution chain or a uid field as either all atoms with this
// tagId have them or none of them do.
std::pair<int, int> attrIndexRange;
const bool hasAttributionChain = data[0]->hasAttributionChain(&attrIndexRange);
bool hasUidField = (data[0]->getUidFieldIndex() != -1);
if (!hasAttributionChain && !hasUidField) {
VLOG("No uid or attribution chain to merge, atom %d", tagId);
return;
}
// 1. Map all isolated uid in-place to host uid
for (shared_ptr<LogEvent>& event : data) {
if (event->GetTagId() != tagId) {
ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId());
return;
}
if (hasAttributionChain) {
vector<FieldValue>* const fieldValues = event->getMutableValues();
for (int i = attrIndexRange.first; i <= attrIndexRange.second; i++) {
FieldValue& fieldValue = fieldValues->at(i);
if (isAttributionUidField(fieldValue)) {
const int hostUid = uidMap->getHostUidOrSelf(fieldValue.mValue.int_value);
fieldValue.mValue.setInt(hostUid);
}
}
} else {
int uidFieldIndex = event->getUidFieldIndex();
if (uidFieldIndex != -1) {
Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
value.setInt(hostUid);
} else {
ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
}
}
}
// 2. sort the data, bit-wise
sort(data.begin(), data.end(),
[](const shared_ptr<LogEvent>& lhs, const shared_ptr<LogEvent>& rhs) {
if (lhs->size() != rhs->size()) {
return lhs->size() < rhs->size();
}
const std::vector<FieldValue>& lhsValues = lhs->getValues();
const std::vector<FieldValue>& rhsValues = rhs->getValues();
for (int i = 0; i < (int)lhs->size(); i++) {
if (lhsValues[i] != rhsValues[i]) {
return lhsValues[i] < rhsValues[i];
}
}
return false;
});
vector<shared_ptr<LogEvent>> mergedData;
const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end());
bool needMerge = true;
// 3. do the merge.
// The loop invariant is this: for every event, check if it differs on
// non-additive fields, or have different attribution chain length.
// If so, no need to merge, add itself to the result.
// Otherwise, merge the value onto the one immediately next to it.
for (int i = 0; i < (int)data.size() - 1; i++) {
// Size different, must be different chains.
if (data[i]->size() != data[i + 1]->size()) {
mergedData.push_back(data[i]);
continue;
}
vector<FieldValue>* lhsValues = data[i]->getMutableValues();
vector<FieldValue>* rhsValues = data[i + 1]->getMutableValues();
needMerge = true;
for (int p = 0; p < (int)lhsValues->size(); p++) {
if ((*lhsValues)[p] != (*rhsValues)[p]) {
int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
// Differ on non-additive field, abort.
if (additiveFields.find(pos) == additiveFields.end()) {
needMerge = false;
break;
}
}
}
if (!needMerge) {
mergedData.push_back(data[i]);
continue;
}
// This should be infrequent operation.
for (int p = 0; p < (int)lhsValues->size(); p++) {
int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
if (additiveFields.find(pos) != additiveFields.end()) {
(*rhsValues)[p].mValue += (*lhsValues)[p].mValue;
}
}
}
mergedData.push_back(data.back());
data.clear();
data = mergedData;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,34 +0,0 @@
/*
* 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.
*/
#pragma once
#include <vector>
#include "StatsPuller.h"
#include "logd/LogEvent.h"
#include "packages/UidMap.h"
namespace android {
namespace os {
namespace statsd {
void mapAndMergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
const sp<UidMap>& uidMap, int tagId,
const vector<int>& additiveFieldsVec);
} // namespace statsd
} // namespace os
} // namespace android

File diff suppressed because it is too large Load Diff

View File

@@ -1,678 +0,0 @@
/*
* Copyright 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 "config/ConfigKey.h"
#include <gtest/gtest_prod.h>
#include <log/log_time.h>
#include <list>
#include <mutex>
#include <string>
#include <vector>
#include <unordered_map>
namespace android {
namespace os {
namespace statsd {
struct ConfigStats {
int32_t uid;
int64_t id;
int32_t creation_time_sec;
int32_t deletion_time_sec = 0;
int32_t reset_time_sec = 0;
int32_t metric_count;
int32_t condition_count;
int32_t matcher_count;
int32_t alert_count;
bool is_valid;
std::list<int32_t> broadcast_sent_time_sec;
// Times at which this config is activated.
std::list<int32_t> activation_time_sec;
// Times at which this config is deactivated.
std::list<int32_t> deactivation_time_sec;
std::list<int32_t> data_drop_time_sec;
// Number of bytes dropped at corresponding time.
std::list<int64_t> data_drop_bytes;
std::list<std::pair<int32_t, int64_t>> dump_report_stats;
// Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount.
std::map<const int64_t, int> matcher_stats;
// Stores the number of output tuple of condition trackers when it's bigger than
// kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
// it means some data has been dropped. The map size is capped by kMaxConfigCount.
std::map<const int64_t, int> condition_stats;
// Stores the number of output tuple of metric producers when it's bigger than
// kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
// it means some data has been dropped. The map size is capped by kMaxConfigCount.
std::map<const int64_t, int> metric_stats;
// Stores the max number of output tuple of dimensions in condition across dimensions in what
// when it's bigger than kDimensionKeySizeSoftLimit. When you see the number is
// kDimensionKeySizeHardLimit +1, it means some data has been dropped. The map size is capped by
// kMaxConfigCount.
std::map<const int64_t, int> metric_dimension_in_condition_stats;
// Stores the number of times an anomaly detection alert has been declared.
// The map size is capped by kMaxConfigCount.
std::map<const int64_t, int> alert_stats;
// Stores the config ID for each sub-config used.
std::list<std::pair<const int64_t, const int32_t>> annotations;
};
struct UidMapStats {
int32_t changes;
int32_t bytes_used;
int32_t dropped_changes;
int32_t deleted_apps = 0;
};
// Keeps track of stats of statsd.
// Single instance shared across the process. All public methods are thread safe.
class StatsdStats {
public:
static StatsdStats& getInstance();
~StatsdStats(){};
const static int kDimensionKeySizeSoftLimit = 500;
const static int kDimensionKeySizeHardLimit = 800;
// Per atom dimension key size limit
static const std::map<int, std::pair<size_t, size_t>> kAtomDimensionKeySizeLimitMap;
const static int kMaxConfigCountPerUid = 20;
const static int kMaxAlertCountPerConfig = 100;
const static int kMaxConditionCountPerConfig = 300;
const static int kMaxMetricCountPerConfig = 1000;
const static int kMaxMatcherCountPerConfig = 800;
// The max number of old config stats we keep.
const static int kMaxIceBoxSize = 20;
const static int kMaxLoggerErrors = 20;
const static int kMaxSystemServerRestarts = 20;
const static int kMaxTimestampCount = 20;
const static int kMaxLogSourceCount = 50;
const static int kMaxPullAtomPackages = 100;
// Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
// drops the metrics data in memory.
static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
// Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
// data subscriber that it's time to call getData.
static const size_t kBytesPerConfigTriggerGetData = 192 * 1024;
// Cap the UID map's memory usage to this. This should be fairly high since the UID information
// is critical for understanding the metrics.
const static size_t kMaxBytesUsedUidMap = 50 * 1024;
// The number of deleted apps that are stored in the uid map.
const static int kMaxDeletedAppsInUidMap = 100;
/* Minimum period between two broadcasts in nanoseconds. */
static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC;
/* Min period between two checks of byte size per config key in nanoseconds. */
static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC;
/* Minimum period between two activation broadcasts in nanoseconds. */
static const int64_t kMinActivationBroadcastPeriodNs = 10 * NS_PER_SEC;
// Maximum age (30 days) that files on disk can exist in seconds.
static const int kMaxAgeSecond = 60 * 60 * 24 * 30;
// Maximum age (2 days) that local history files on disk can exist in seconds.
static const int kMaxLocalHistoryAgeSecond = 60 * 60 * 24 * 2;
// Maximum number of files (1000) that can be in stats directory on disk.
static const int kMaxFileNumber = 1000;
// Maximum size of all files that can be written to stats directory on disk.
static const int kMaxFileSize = 50 * 1024 * 1024;
// How long to try to clear puller cache from last time
static const long kPullerCacheClearIntervalSec = 1;
// Max time to do a pull.
static const int64_t kPullMaxDelayNs = 30 * NS_PER_SEC;
// Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId.
static const int kMaxNonPlatformPushedAtoms = 100;
// Maximum atom id value that we consider a platform pushed atom.
// This should be updated once highest pushed atom id in atoms.proto approaches this value.
static const int kMaxPushedAtomId = 500;
// Atom id that is the start of the pulled atoms.
static const int kPullAtomStartTag = 10000;
// Atom id that is the start of vendor atoms.
static const int kVendorAtomStartTag = 100000;
// Vendor pulled atom start id.
static const int32_t kVendorPulledAtomStartTag = 150000;
// Beginning of range for timestamp truncation.
static const int32_t kTimestampTruncationStartTag = 300000;
// End of range for timestamp truncation.
static const int32_t kTimestampTruncationEndTag = 304999;
// Max accepted atom id.
static const int32_t kMaxAtomTag = 200000;
static const int64_t kInt64Max = 0x7fffffffffffffffLL;
static const int32_t kMaxLoggedBucketDropEvents = 10;
/**
* Report a new config has been received and report the static stats about the config.
*
* The static stats include: the count of metrics, conditions, matchers, and alerts.
* If the config is not valid, this config stats will be put into icebox immediately.
*/
void noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
int matchersCount, int alertCount,
const std::list<std::pair<const int64_t, const int32_t>>& annotations,
bool isValid);
/**
* Report a config has been removed.
*/
void noteConfigRemoved(const ConfigKey& key);
/**
* Report a config has been reset when ttl expires.
*/
void noteConfigReset(const ConfigKey& key);
/**
* Report a broadcast has been sent to a config owner to collect the data.
*/
void noteBroadcastSent(const ConfigKey& key);
/**
* Report that a config has become activated or deactivated.
* This can be different from whether or not a broadcast is sent if the
* guardrail prevented the broadcast from being sent.
*/
void noteActiveStatusChanged(const ConfigKey& key, bool activate);
/**
* Report a config's metrics data has been dropped.
*/
void noteDataDropped(const ConfigKey& key, const size_t totalBytes);
/**
* Report metrics data report has been sent.
*
* The report may be requested via StatsManager API, or through adb cmd.
*/
void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes);
/**
* Report the size of output tuple of a condition.
*
* Note: only report when the condition has an output dimension, and the tuple
* count > kDimensionKeySizeSoftLimit.
*
* [key]: The config key that this condition belongs to.
* [id]: The id of the condition.
* [size]: The output tuple size.
*/
void noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size);
/**
* Report the size of output tuple of a metric.
*
* Note: only report when the metric has an output dimension, and the tuple
* count > kDimensionKeySizeSoftLimit.
*
* [key]: The config key that this metric belongs to.
* [id]: The id of the metric.
* [size]: The output tuple size.
*/
void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size);
/**
* Report the max size of output tuple of dimension in condition across dimensions in what.
*
* Note: only report when the metric has an output dimension in condition, and the max tuple
* count > kDimensionKeySizeSoftLimit.
*
* [key]: The config key that this metric belongs to.
* [id]: The id of the metric.
* [size]: The output tuple size.
*/
void noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t& id, int size);
/**
* Report a matcher has been matched.
*
* [key]: The config key that this matcher belongs to.
* [id]: The id of the matcher.
*/
void noteMatcherMatched(const ConfigKey& key, const int64_t& id);
/**
* Report that an anomaly detection alert has been declared.
*
* [key]: The config key that this alert belongs to.
* [id]: The id of the alert.
*/
void noteAnomalyDeclared(const ConfigKey& key, const int64_t& id);
/**
* Report an atom event has been logged.
*/
void noteAtomLogged(int atomId, int32_t timeSec);
/**
* Report that statsd modified the anomaly alarm registered with StatsCompanionService.
*/
void noteRegisteredAnomalyAlarmChanged();
/**
* Report that statsd modified the periodic alarm registered with StatsCompanionService.
*/
void noteRegisteredPeriodicAlarmChanged();
/**
* Records the number of delta entries that are being dropped from the uid map.
*/
void noteUidMapDropped(int deltas);
/**
* Records that an app was deleted (from statsd's map).
*/
void noteUidMapAppDeletionDropped();
/**
* Updates the number of changes currently stored in the uid map.
*/
void setUidMapChanges(int changes);
void setCurrentUidMapMemory(int bytes);
/*
* Updates minimum interval between pulls for an pulled atom.
*/
void updateMinPullIntervalSec(int pullAtomId, long intervalSec);
/*
* Notes an atom is pulled.
*/
void notePull(int pullAtomId);
/*
* Notes an atom is served from puller cache.
*/
void notePullFromCache(int pullAtomId);
/*
* Notify data error for pulled atom.
*/
void notePullDataError(int pullAtomId);
/*
* Records time for actual pulling, not including those served from cache and not including
* statsd processing delays.
*/
void notePullTime(int pullAtomId, int64_t pullTimeNs);
/*
* Records pull delay for a pulled atom, including those served from cache and including statsd
* processing delays.
*/
void notePullDelay(int pullAtomId, int64_t pullDelayNs);
/*
* Records pull exceeds timeout for the puller.
*/
void notePullTimeout(int pullAtomId, int64_t pullUptimeMillis, int64_t pullElapsedMillis);
/*
* Records pull exceeds max delay for a metric.
*/
void notePullExceedMaxDelay(int pullAtomId);
/*
* Records when system server restarts.
*/
void noteSystemServerRestart(int32_t timeSec);
/**
* Records statsd skipped an event.
*/
void noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError,
int32_t lastAtomTag, int32_t uid, int32_t pid);
/**
* Records that the pull of an atom has failed. Eg, if the client indicated the pull failed, if
* the pull timed out, or if the outgoing binder call failed.
* This count will only increment if the puller was actually invoked.
*
* It does not include a pull not occurring due to not finding the appropriate
* puller. These cases are covered in other counts.
*/
void notePullFailed(int atomId);
/**
* Records that the pull of an atom has failed due to not having a uid provider.
*/
void notePullUidProviderNotFound(int atomId);
/**
* Records that the pull of an atom has failed due not finding a puller registered by a
* trusted uid.
*/
void notePullerNotFound(int atomId);
/**
* Records that the pull has failed due to the outgoing binder call failing.
*/
void notePullBinderCallFailed(int atomId);
/**
* A pull with no data occurred
*/
void noteEmptyData(int atomId);
/**
* Records that a puller callback for the given atomId was registered or unregistered.
*
* @param registered True if the callback was registered, false if was unregistered.
*/
void notePullerCallbackRegistrationChanged(int atomId, bool registered);
/**
* Hard limit was reached in the cardinality of an atom
*/
void noteHardDimensionLimitReached(int64_t metricId);
/**
* A log event was too late, arrived in the wrong bucket and was skipped
*/
void noteLateLogEventSkipped(int64_t metricId);
/**
* Buckets were skipped as time elapsed without any data for them
*/
void noteSkippedForwardBuckets(int64_t metricId);
/**
* An unsupported value type was received
*/
void noteBadValueType(int64_t metricId);
/**
* Buckets were dropped due to reclaim memory.
*/
void noteBucketDropped(int64_t metricId);
/**
* A condition change was too late, arrived in the wrong bucket and was skipped
*/
void noteConditionChangeInNextBucket(int64_t metricId);
/**
* A bucket has been tagged as invalid.
*/
void noteInvalidatedBucket(int64_t metricId);
/**
* Tracks the total number of buckets (include skipped/invalid buckets).
*/
void noteBucketCount(int64_t metricId);
/**
* For pulls at bucket boundaries, it represents the misalignment between the real timestamp and
* the end of the bucket.
*/
void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs);
/**
* Number of buckets with unknown condition.
*/
void noteBucketUnknownCondition(int64_t metricId);
/* Reports one event has been dropped due to queue overflow, and the oldest event timestamp in
* the queue */
void noteEventQueueOverflow(int64_t oldestEventTimestampNs);
/**
* Reports that the activation broadcast guardrail was hit for this uid. Namely, the broadcast
* should have been sent, but instead was skipped due to hitting the guardrail.
*/
void noteActivationBroadcastGuardrailHit(const int uid);
/**
* Reports that an atom is erroneous or cannot be parsed successfully by
* statsd. An atom tag of 0 indicates that the client did not supply the
* atom id within the encoding.
*
* For pushed atoms only, this call should be preceded by a call to
* noteAtomLogged.
*/
void noteAtomError(int atomTag, bool pull=false);
/**
* Reset the historical stats. Including all stats in icebox, and the tracked stats about
* metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
* to collect stats after reset() has been called.
*/
void reset();
/**
* Output the stats in protobuf binary format to [buffer].
*
* [reset]: whether to clear the historical stats after the call.
*/
void dumpStats(std::vector<uint8_t>* buffer, bool reset);
/**
* Output statsd stats in human readable format to [out] file descriptor.
*/
void dumpStats(int outFd) const;
typedef struct PullTimeoutMetadata {
int64_t pullTimeoutUptimeMillis;
int64_t pullTimeoutElapsedMillis;
PullTimeoutMetadata(int64_t uptimeMillis, int64_t elapsedMillis) :
pullTimeoutUptimeMillis(uptimeMillis),
pullTimeoutElapsedMillis(elapsedMillis) {/* do nothing */}
} PullTimeoutMetadata;
typedef struct {
long totalPull = 0;
long totalPullFromCache = 0;
long minPullIntervalSec = LONG_MAX;
int64_t avgPullTimeNs = 0;
int64_t maxPullTimeNs = 0;
long numPullTime = 0;
int64_t avgPullDelayNs = 0;
int64_t maxPullDelayNs = 0;
long numPullDelay = 0;
long dataError = 0;
long pullTimeout = 0;
long pullExceedMaxDelay = 0;
long pullFailed = 0;
long pullUidProviderNotFound = 0;
long pullerNotFound = 0;
long emptyData = 0;
long registeredCount = 0;
long unregisteredCount = 0;
int32_t atomErrorCount = 0;
long binderCallFailCount = 0;
std::list<PullTimeoutMetadata> pullTimeoutMetadata;
} PulledAtomStats;
typedef struct {
long hardDimensionLimitReached = 0;
long lateLogEventSkipped = 0;
long skippedForwardBuckets = 0;
long badValueType = 0;
long conditionChangeInNextBucket = 0;
long invalidatedBucket = 0;
long bucketDropped = 0;
int64_t minBucketBoundaryDelayNs = 0;
int64_t maxBucketBoundaryDelayNs = 0;
long bucketUnknownCondition = 0;
long bucketCount = 0;
} AtomMetricStats;
private:
StatsdStats();
mutable std::mutex mLock;
int32_t mStartTimeSec;
// Track the number of dropped entries used by the uid map.
UidMapStats mUidMapStats;
// The stats about the configs that are still in use.
// The map size is capped by kMaxConfigCount.
std::map<const ConfigKey, std::shared_ptr<ConfigStats>> mConfigStats;
// Stores the stats for the configs that are no longer in use.
// The size of the vector is capped by kMaxIceBoxSize.
std::list<const std::shared_ptr<ConfigStats>> mIceBox;
// Stores the number of times a pushed atom is logged.
// The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
// out of that range will be put in mNonPlatformPushedAtomStats.
// This is a vector, not a map because it will be accessed A LOT -- for each stats log.
std::vector<int> mPushedAtomStats;
// Stores the number of times a pushed atom is logged for atom ids above kMaxPushedAtomId.
// The max size of the map is kMaxNonPlatformPushedAtoms.
std::unordered_map<int, int> mNonPlatformPushedAtomStats;
// Maps PullAtomId to its stats. The size is capped by the puller atom counts.
std::map<int, PulledAtomStats> mPulledAtomStats;
// Stores the number of times a pushed atom was logged erroneously. The
// corresponding counts for pulled atoms are stored in PulledAtomStats.
// The max size of this map is kMaxAtomErrorsStatsSize.
std::map<int, int> mPushedAtomErrorStats;
int kMaxPushedAtomErrorStatsSize = 100;
// Maps metric ID to its stats. The size is capped by the number of metrics.
std::map<int64_t, AtomMetricStats> mAtomMetricStats;
// Maps uids to times when the activation changed broadcast not sent due to hitting the
// guardrail. The size is capped by the number of configs, and up to 20 times per uid.
std::map<int, std::list<int32_t>> mActivationBroadcastGuardrailStats;
struct LogLossStats {
LogLossStats(int32_t sec, int32_t count, int32_t error, int32_t tag, int32_t uid,
int32_t pid)
: mWallClockSec(sec),
mCount(count),
mLastError(error),
mLastTag(tag),
mUid(uid),
mPid(pid) {
}
int32_t mWallClockSec;
int32_t mCount;
// error code defined in linux/errno.h
int32_t mLastError;
int32_t mLastTag;
int32_t mUid;
int32_t mPid;
};
// Max of {(now - oldestEventTimestamp) when overflow happens}.
// This number is helpful to understand how SLOW statsd can be.
int64_t mMaxQueueHistoryNs = 0;
// Min of {(now - oldestEventTimestamp) when overflow happens}.
// This number is helpful to understand how FAST the events floods to statsd.
int64_t mMinQueueHistoryNs = kInt64Max;
// Total number of events that are lost due to queue overflow.
int32_t mOverflowCount = 0;
// Timestamps when we detect log loss, and the number of logs lost.
std::list<LogLossStats> mLogLossStats;
std::list<int32_t> mSystemServerRestartSec;
// Stores the number of times statsd modified the anomaly alarm registered with
// StatsCompanionService.
int mAnomalyAlarmRegisteredStats = 0;
// Stores the number of times statsd registers the periodic alarm changes
int mPeriodicAlarmRegisteredStats = 0;
void noteConfigResetInternalLocked(const ConfigKey& key);
void noteConfigRemovedInternalLocked(const ConfigKey& key);
void resetInternalLocked();
void noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec);
void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes, int32_t timeSec);
void noteBroadcastSent(const ConfigKey& key, int32_t timeSec);
void noteActiveStatusChanged(const ConfigKey& key, bool activate, int32_t timeSec);
void noteActivationBroadcastGuardrailHit(const int uid, int32_t timeSec);
void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats);
int getPushedAtomErrors(int atomId) const;
/**
* Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
* will live as long as `this`.
*/
StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId);
FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
FRIEND_TEST(StatsdStatsTest, TestConfigRemove);
FRIEND_TEST(StatsdStatsTest, TestSubStats);
FRIEND_TEST(StatsdStatsTest, TestAtomLog);
FRIEND_TEST(StatsdStatsTest, TestNonPlatformAtomLog);
FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,142 +0,0 @@
/*
* 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 "hash.h"
#ifndef FALLTHROUGH_INTENDED
#define FALLTHROUGH_INTENDED [[fallthrough]]
#endif
namespace android {
namespace os {
namespace statsd {
namespace {
// Lower-level versions of Get... that read directly from a character buffer
// without any bounds checking.
inline uint32_t DecodeFixed32(const char *ptr) {
return ((static_cast<uint32_t>(static_cast<unsigned char>(ptr[0]))) |
(static_cast<uint32_t>(static_cast<unsigned char>(ptr[1])) << 8) |
(static_cast<uint32_t>(static_cast<unsigned char>(ptr[2])) << 16) |
(static_cast<uint32_t>(static_cast<unsigned char>(ptr[3])) << 24));
}
inline uint64_t DecodeFixed64(const char* ptr) {
uint64_t lo = DecodeFixed32(ptr);
uint64_t hi = DecodeFixed32(ptr + 4);
return (hi << 32) | lo;
}
// 0xff is in case char is signed.
static inline uint32_t ByteAs32(char c) { return static_cast<uint32_t>(c) & 0xff; }
static inline uint64_t ByteAs64(char c) { return static_cast<uint64_t>(c) & 0xff; }
} // namespace
uint32_t Hash32(const char *data, size_t n, uint32_t seed) {
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
const uint32_t m = 0x5bd1e995;
const int r = 24;
// Initialize the hash to a 'random' value
uint32_t h = static_cast<uint32_t>(seed ^ n);
// Mix 4 bytes at a time into the hash
while (n >= 4) {
uint32_t k = DecodeFixed32(data);
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
data += 4;
n -= 4;
}
// Handle the last few bytes of the input array
switch (n) {
case 3:
h ^= ByteAs32(data[2]) << 16;
FALLTHROUGH_INTENDED;
case 2:
h ^= ByteAs32(data[1]) << 8;
FALLTHROUGH_INTENDED;
case 1:
h ^= ByteAs32(data[0]);
h *= m;
}
// Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
uint64_t Hash64(const char* data, size_t n, uint64_t seed) {
const uint64_t m = 0xc6a4a7935bd1e995;
const int r = 47;
uint64_t h = seed ^ (n * m);
while (n >= 8) {
uint64_t k = DecodeFixed64(data);
data += 8;
n -= 8;
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
}
switch (n) {
case 7:
h ^= ByteAs64(data[6]) << 48;
FALLTHROUGH_INTENDED;
case 6:
h ^= ByteAs64(data[5]) << 40;
FALLTHROUGH_INTENDED;
case 5:
h ^= ByteAs64(data[4]) << 32;
FALLTHROUGH_INTENDED;
case 4:
h ^= ByteAs64(data[3]) << 24;
FALLTHROUGH_INTENDED;
case 3:
h ^= ByteAs64(data[2]) << 16;
FALLTHROUGH_INTENDED;
case 2:
h ^= ByteAs64(data[1]) << 8;
FALLTHROUGH_INTENDED;
case 1:
h ^= ByteAs64(data[0]);
h *= m;
}
h ^= h >> r;
h *= m;
h ^= h >> r;
return h;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,46 +0,0 @@
/*
* 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 <string>
namespace android {
namespace os {
namespace statsd {
extern uint32_t Hash32(const char *data, size_t n, uint32_t seed);
extern uint64_t Hash64(const char* data, size_t n, uint64_t seed);
inline uint32_t Hash32(const char *data, size_t n) {
return Hash32(data, n, 0xBEEF);
}
inline uint32_t Hash32(const std::string &input) {
return Hash32(input.data(), input.size());
}
inline uint64_t Hash64(const char* data, size_t n) {
return Hash64(data, n, 0xDECAFCAFFE);
}
inline uint64_t Hash64(const std::string& str) {
return Hash64(str.data(), str.size());
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,599 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "logd/LogEvent.h"
#include <android-base/stringprintf.h>
#include <android/binder_ibinder.h>
#include <private/android_filesystem_config.h>
#include "annotations.h"
#include "stats_log_util.h"
#include "statslog_statsd.h"
namespace android {
namespace os {
namespace statsd {
// for TrainInfo experiment id serialization
const int FIELD_ID_EXPERIMENT_ID = 1;
using namespace android::util;
using android::base::StringPrintf;
using android::util::ProtoOutputStream;
using std::string;
using std::vector;
// stats_event.h socket types. Keep in sync.
/* ERRORS */
#define ERROR_NO_TIMESTAMP 0x1
#define ERROR_NO_ATOM_ID 0x2
#define ERROR_OVERFLOW 0x4
#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
#define ERROR_INVALID_ANNOTATION_ID 0x40
#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
#define ERROR_TOO_MANY_ANNOTATIONS 0x100
#define ERROR_TOO_MANY_FIELDS 0x200
#define ERROR_INVALID_VALUE_TYPE 0x400
#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
/* TYPE IDS */
#define INT32_TYPE 0x00
#define INT64_TYPE 0x01
#define STRING_TYPE 0x02
#define LIST_TYPE 0x03
#define FLOAT_TYPE 0x04
#define BOOL_TYPE 0x05
#define BYTE_ARRAY_TYPE 0x06
#define OBJECT_TYPE 0x07
#define KEY_VALUE_PAIRS_TYPE 0x08
#define ATTRIBUTION_CHAIN_TYPE 0x09
#define ERROR_TYPE 0x0F
LogEvent::LogEvent(int32_t uid, int32_t pid)
: mLogdTimestampNs(time(nullptr)), mLogUid(uid), mLogPid(pid) {
}
LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging,
bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
const std::vector<uint8_t>& experimentIds, int32_t userId) {
mLogdTimestampNs = getWallClockNs();
mElapsedTimestampNs = getElapsedRealtimeNs();
mTagId = util::BINARY_PUSH_STATE_CHANGED;
mLogUid = AIBinder_getCallingUid();
mLogPid = AIBinder_getCallingPid();
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled)));
mValues.push_back(
FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId)));
}
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const InstallTrainInfo& trainInfo) {
mLogdTimestampNs = wallClockTimestampNs;
mElapsedTimestampNs = elapsedTimestampNs;
mTagId = util::TRAIN_INFO;
mValues.push_back(
FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
std::vector<uint8_t> experimentIdsProto;
writeExperimentIdsToProto(trainInfo.experimentIds, &experimentIdsProto);
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(experimentIdsProto)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value(trainInfo.trainName)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status)));
}
void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t value = readNextValue<int32_t>();
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int64_t value = readNextValue<int64_t>();
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numBytes = readNextValue<int32_t>();
if ((uint32_t)numBytes > mRemainingLen) {
mValid = false;
return;
}
string value = string((char*)mBuf, numBytes);
mBuf += numBytes;
mRemainingLen -= numBytes;
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
float value = readNextValue<float>();
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
// cast to int32_t because FieldValue does not support bools
int32_t value = (int32_t)readNextValue<uint8_t>();
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numBytes = readNextValue<int32_t>();
if ((uint32_t)numBytes > mRemainingLen) {
mValid = false;
return;
}
vector<uint8_t> value(mBuf, mBuf + numBytes);
mBuf += numBytes;
mRemainingLen -= numBytes;
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numPairs = readNextValue<uint8_t>();
for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) {
last[1] = (pos[1] == numPairs);
// parse key
pos[2] = 1;
parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
// parse value
last[2] = true;
uint8_t typeInfo = readNextValue<uint8_t>();
switch (getTypeId(typeInfo)) {
case INT32_TYPE:
pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto
parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case INT64_TYPE:
pos[2] = 3;
parseInt64(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case STRING_TYPE:
pos[2] = 4;
parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case FLOAT_TYPE:
pos[2] = 5;
parseFloat(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
default:
mValid = false;
}
}
parseAnnotations(numAnnotations);
pos[1] = pos[2] = 1;
last[1] = last[2] = false;
}
void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
uint8_t numAnnotations) {
const unsigned int firstUidInChainIndex = mValues.size();
const int32_t numNodes = readNextValue<uint8_t>();
for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) {
last[1] = (pos[1] == numNodes);
// parse uid
pos[2] = 1;
parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
// parse tag
pos[2] = 2;
last[2] = true;
parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
}
// Check if at least one node was successfully parsed.
if (mValues.size() - 1 > firstUidInChainIndex) {
mAttributionChainStartIndex = static_cast<int8_t>(firstUidInChainIndex);
mAttributionChainEndIndex = static_cast<int8_t>(mValues.size() - 1);
}
parseAnnotations(numAnnotations, firstUidInChainIndex);
pos[1] = pos[2] = 1;
last[1] = last[2] = false;
}
// Assumes that mValues is not empty
bool LogEvent::checkPreviousValueType(Type expected) {
return mValues[mValues.size() - 1].mValue.getType() == expected;
}
void LogEvent::parseIsUidAnnotation(uint8_t annotationType) {
if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
bool isUid = readNextValue<uint8_t>();
if (isUid) mUidFieldIndex = static_cast<int8_t>(mValues.size() - 1);
mValues[mValues.size() - 1].mAnnotations.setUidField(isUid);
}
void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) {
if (!mValues.empty() || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
mTruncateTimestamp = readNextValue<uint8_t>();
}
void LogEvent::parsePrimaryFieldAnnotation(uint8_t annotationType) {
if (mValues.empty() || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
const bool primaryField = readNextValue<uint8_t>();
mValues[mValues.size() - 1].mAnnotations.setPrimaryField(primaryField);
}
void LogEvent::parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType,
int firstUidInChainIndex) {
if (mValues.empty() || annotationType != BOOL_TYPE || -1 == firstUidInChainIndex) {
mValid = false;
return;
}
const bool primaryField = readNextValue<uint8_t>();
mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(primaryField);
}
void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) {
if (mValues.empty() || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
const bool exclusiveState = readNextValue<uint8_t>();
mExclusiveStateFieldIndex = static_cast<int8_t>(mValues.size() - 1);
mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
}
void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) {
if (mValues.empty() || annotationType != INT32_TYPE) {
mValid = false;
return;
}
mResetState = readNextValue<int32_t>();
}
void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) {
if (mValues.empty() || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
bool nested = readNextValue<uint8_t>();
mValues[mValues.size() - 1].mAnnotations.setNested(nested);
}
// firstUidInChainIndex is a default parameter that is only needed when parsing
// annotations for attribution chains.
void LogEvent::parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex) {
for (uint8_t i = 0; i < numAnnotations; i++) {
uint8_t annotationId = readNextValue<uint8_t>();
uint8_t annotationType = readNextValue<uint8_t>();
switch (annotationId) {
case ANNOTATION_ID_IS_UID:
parseIsUidAnnotation(annotationType);
break;
case ANNOTATION_ID_TRUNCATE_TIMESTAMP:
parseTruncateTimestampAnnotation(annotationType);
break;
case ANNOTATION_ID_PRIMARY_FIELD:
parsePrimaryFieldAnnotation(annotationType);
break;
case ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID:
parsePrimaryFieldFirstUidAnnotation(annotationType, firstUidInChainIndex);
break;
case ANNOTATION_ID_EXCLUSIVE_STATE:
parseExclusiveStateAnnotation(annotationType);
break;
case ANNOTATION_ID_TRIGGER_STATE_RESET:
parseTriggerStateResetAnnotation(annotationType);
break;
case ANNOTATION_ID_STATE_NESTED:
parseStateNestedAnnotation(annotationType);
break;
default:
mValid = false;
return;
}
}
}
// This parsing logic is tied to the encoding scheme used in StatsEvent.java and
// stats_event.c
bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
mBuf = buf;
mRemainingLen = (uint32_t)len;
int32_t pos[] = {1, 1, 1};
bool last[] = {false, false, false};
// Beginning of buffer is OBJECT_TYPE | NUM_FIELDS | TIMESTAMP | ATOM_ID
uint8_t typeInfo = readNextValue<uint8_t>();
if (getTypeId(typeInfo) != OBJECT_TYPE) mValid = false;
uint8_t numElements = readNextValue<uint8_t>();
if (numElements < 2 || numElements > 127) mValid = false;
typeInfo = readNextValue<uint8_t>();
if (getTypeId(typeInfo) != INT64_TYPE) mValid = false;
mElapsedTimestampNs = readNextValue<int64_t>();
numElements--;
typeInfo = readNextValue<uint8_t>();
if (getTypeId(typeInfo) != INT32_TYPE) mValid = false;
mTagId = readNextValue<int32_t>();
numElements--;
parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) {
last[0] = (pos[0] == numElements);
typeInfo = readNextValue<uint8_t>();
uint8_t typeId = getTypeId(typeInfo);
switch (typeId) {
case BOOL_TYPE:
parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case INT32_TYPE:
parseInt32(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case INT64_TYPE:
parseInt64(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case FLOAT_TYPE:
parseFloat(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case BYTE_ARRAY_TYPE:
parseByteArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case STRING_TYPE:
parseString(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case KEY_VALUE_PAIRS_TYPE:
parseKeyValuePairs(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case ATTRIBUTION_CHAIN_TYPE:
parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case ERROR_TYPE:
/* mErrorBitmask =*/ readNextValue<int32_t>();
mValid = false;
break;
default:
mValid = false;
break;
}
}
if (mRemainingLen != 0) mValid = false;
mBuf = nullptr;
return mValid;
}
uint8_t LogEvent::getTypeId(uint8_t typeInfo) {
return typeInfo & 0x0F; // type id in lower 4 bytes
}
uint8_t LogEvent::getNumAnnotations(uint8_t typeInfo) {
return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes
}
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
// TODO(b/110561208): encapsulate the magical operations in Field struct as static functions
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == LONG) {
return value.mValue.long_value;
} else if (value.mValue.getType() == INT) {
return value.mValue.int_value;
} else {
*err = BAD_TYPE;
return 0;
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return 0;
}
int LogEvent::GetInt(size_t key, status_t* err) const {
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == INT) {
return value.mValue.int_value;
} else {
*err = BAD_TYPE;
return 0;
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return 0;
}
const char* LogEvent::GetString(size_t key, status_t* err) const {
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == STRING) {
return value.mValue.str_value.c_str();
} else {
*err = BAD_TYPE;
return 0;
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return NULL;
}
bool LogEvent::GetBool(size_t key, status_t* err) const {
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == INT) {
return value.mValue.int_value != 0;
} else if (value.mValue.getType() == LONG) {
return value.mValue.long_value != 0;
} else {
*err = BAD_TYPE;
return false;
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return false;
}
float LogEvent::GetFloat(size_t key, status_t* err) const {
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == FLOAT) {
return value.mValue.float_value;
} else {
*err = BAD_TYPE;
return 0.0;
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return 0.0;
}
std::vector<uint8_t> LogEvent::GetStorage(size_t key, status_t* err) const {
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == STORAGE) {
return value.mValue.storage_value;
} else {
*err = BAD_TYPE;
return vector<uint8_t>();
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return vector<uint8_t>();
}
string LogEvent::ToString() const {
string result;
result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
(long long)mElapsedTimestampNs, mTagId);
for (const auto& value : mValues) {
result +=
StringPrintf("%#x", value.mField.getField()) + "->" + value.mValue.toString() + " ";
}
result += " }";
return result;
}
void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput);
}
bool LogEvent::hasAttributionChain(std::pair<int, int>* indexRange) const {
if (mAttributionChainStartIndex == -1 || mAttributionChainEndIndex == -1) {
return false;
}
if (nullptr != indexRange) {
indexRange->first = static_cast<int>(mAttributionChainStartIndex);
indexRange->second = static_cast<int>(mAttributionChainEndIndex);
}
return true;
}
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds,
std::vector<uint8_t>* protoOut) {
ProtoOutputStream proto;
for (const auto& expId : experimentIds) {
proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID,
(long long)expId);
}
protoOut->resize(proto.size());
size_t pos = 0;
sp<ProtoReader> reader = proto.data();
while (reader->readBuffer() != NULL) {
size_t toRead = reader->currentToRead();
std::memcpy(protoOut->data() + pos, reader->readBuffer(), toRead);
pos += toRead;
reader->move(toRead);
}
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,331 +0,0 @@
/*
* 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 "FieldValue.h"
#include <android/util/ProtoOutputStream.h>
#include <private/android_logger.h>
#include <string>
#include <vector>
namespace android {
namespace os {
namespace statsd {
struct InstallTrainInfo {
int64_t trainVersionCode;
std::string trainName;
int32_t status;
std::vector<int64_t> experimentIds;
bool requiresStaging;
bool rollbackEnabled;
bool requiresLowLatencyMonitor;
};
/**
* This class decodes the structured, serialized encoding of an atom into a
* vector of FieldValues.
*/
class LogEvent {
public:
/**
* \param uid user id of the logging caller
* \param pid process id of the logging caller
*/
explicit LogEvent(int32_t uid, int32_t pid);
/**
* Parses the atomId, timestamp, and vector of values from a buffer
* containing the StatsEvent/AStatsEvent encoding of an atom.
*
* \param buf a buffer that begins at the start of the serialized atom (it
* should not include the android_log_header_t or the StatsEventTag)
* \param len size of the buffer
*
* \return success of the initialization
*/
bool parseBuffer(uint8_t* buf, size_t len);
// Constructs a BinaryPushStateChanged LogEvent from API call.
explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging,
bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
const std::vector<uint8_t>& experimentIds, int32_t userId);
explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const InstallTrainInfo& installTrainInfo);
~LogEvent() {}
/**
* Get the timestamp associated with this event.
*/
inline int64_t GetLogdTimestampNs() const { return mLogdTimestampNs; }
inline int64_t GetElapsedTimestampNs() const { return mElapsedTimestampNs; }
/**
* Get the tag for this event.
*/
inline int GetTagId() const { return mTagId; }
/**
* Get the uid of the logging client.
* Returns -1 if the uid is unknown/has not been set.
*/
inline int32_t GetUid() const { return mLogUid; }
/**
* Get the pid of the logging client.
* Returns -1 if the pid is unknown/has not been set.
*/
inline int32_t GetPid() const { return mLogPid; }
/**
* Get the nth value, starting at 1.
*
* Returns BAD_INDEX if the index is larger than the number of elements.
* Returns BAD_TYPE if the index is available but the data is the wrong type.
*/
int64_t GetLong(size_t key, status_t* err) const;
int GetInt(size_t key, status_t* err) const;
const char* GetString(size_t key, status_t* err) const;
bool GetBool(size_t key, status_t* err) const;
float GetFloat(size_t key, status_t* err) const;
std::vector<uint8_t> GetStorage(size_t key, status_t* err) const;
/**
* Return a string representation of this event.
*/
std::string ToString() const;
/**
* Write this object to a ProtoOutputStream.
*/
void ToProto(android::util::ProtoOutputStream& out) const;
/**
* Set elapsed timestamp if the original timestamp is missing.
*/
void setElapsedTimestampNs(int64_t timestampNs) {
mElapsedTimestampNs = timestampNs;
}
/**
* Set the timestamp if the original logd timestamp is missing.
*/
void setLogdWallClockTimestampNs(int64_t timestampNs) {
mLogdTimestampNs = timestampNs;
}
inline int size() const {
return mValues.size();
}
const std::vector<FieldValue>& getValues() const {
return mValues;
}
std::vector<FieldValue>* getMutableValues() {
return &mValues;
}
// Default value = false
inline bool shouldTruncateTimestamp() const {
return mTruncateTimestamp;
}
// Returns the index of the uid field within the FieldValues vector if the
// uid exists. If there is no uid field, returns -1.
//
// If the index within the atom definition is desired, do the following:
// int vectorIndex = LogEvent.getUidFieldIndex();
// if (vectorIndex != -1) {
// FieldValue& v = LogEvent.getValues()[vectorIndex];
// int atomIndex = v.mField.getPosAtDepth(0);
// }
// Note that atomIndex is 1-indexed.
inline int getUidFieldIndex() {
return static_cast<int>(mUidFieldIndex);
}
// Returns whether this LogEvent has an AttributionChain.
// If it does and indexRange is not a nullptr, populate indexRange with the start and end index
// of the AttributionChain within mValues.
bool hasAttributionChain(std::pair<int, int>* indexRange = nullptr) const;
// Returns the index of the exclusive state field within the FieldValues vector if
// an exclusive state exists. If there is no exclusive state field, returns -1.
//
// If the index within the atom definition is desired, do the following:
// int vectorIndex = LogEvent.getExclusiveStateFieldIndex();
// if (vectorIndex != -1) {
// FieldValue& v = LogEvent.getValues()[vectorIndex];
// int atomIndex = v.mField.getPosAtDepth(0);
// }
// Note that atomIndex is 1-indexed.
inline int getExclusiveStateFieldIndex() const {
return static_cast<int>(mExclusiveStateFieldIndex);
}
// If a reset state is not sent in the StatsEvent, returns -1. Note that a
// reset state is sent if and only if a reset should be triggered.
inline int getResetState() const {
return mResetState;
}
inline LogEvent makeCopy() {
return LogEvent(*this);
}
template <class T>
status_t updateValue(size_t key, T& value, Type type) {
int field = getSimpleField(key);
for (auto& fieldValue : mValues) {
if (fieldValue.mField.getField() == field) {
if (fieldValue.mValue.getType() == type) {
fieldValue.mValue = Value(value);
return OK;
} else {
return BAD_TYPE;
}
}
}
return BAD_INDEX;
}
bool isValid() const {
return mValid;
}
private:
/**
* Only use this if copy is absolutely needed.
*/
LogEvent(const LogEvent&) = default;
void parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
void parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
void parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
void parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
void parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
void parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
void parseAttributionChain(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
void parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex = -1);
void parseIsUidAnnotation(uint8_t annotationType);
void parseTruncateTimestampAnnotation(uint8_t annotationType);
void parsePrimaryFieldAnnotation(uint8_t annotationType);
void parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType, int firstUidInChainIndex);
void parseExclusiveStateAnnotation(uint8_t annotationType);
void parseTriggerStateResetAnnotation(uint8_t annotationType);
void parseStateNestedAnnotation(uint8_t annotationType);
bool checkPreviousValueType(Type expected);
/**
* The below two variables are only valid during the execution of
* parseBuffer. There are no guarantees about the state of these variables
* before/after.
*/
uint8_t* mBuf;
uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed
bool mValid = true; // stores whether the event we received from the socket is valid
/**
* Side-effects:
* If there is enough space in buffer to read value of type T
* - move mBuf past the value that was just read
* - decrement mRemainingLen by size of T
* Else
* - set mValid to false
*/
template <class T>
T readNextValue() {
T value;
if (mRemainingLen < sizeof(T)) {
mValid = false;
value = 0; // all primitive types can successfully cast 0
} else {
// When alignof(T) == 1, hopefully the compiler can optimize away
// this conditional as always true.
if ((reinterpret_cast<uintptr_t>(mBuf) % alignof(T)) == 0) {
// We're properly aligned, and can safely make this assignment.
value = *((T*)mBuf);
} else {
// We need to use memcpy. It's slower, but safe.
memcpy(&value, mBuf, sizeof(T));
}
mBuf += sizeof(T);
mRemainingLen -= sizeof(T);
}
return value;
}
template <class T>
void addToValues(int32_t* pos, int32_t depth, T& value, bool* last) {
Field f = Field(mTagId, pos, depth);
// do not decorate last position at depth 0
for (int i = 1; i < depth; i++) {
if (last[i]) f.decorateLastPos(i);
}
Value v = Value(value);
mValues.push_back(FieldValue(f, v));
}
uint8_t getTypeId(uint8_t typeInfo);
uint8_t getNumAnnotations(uint8_t typeInfo);
// The items are naturally sorted in DFS order as we read them. this allows us to do fast
// matching.
std::vector<FieldValue> mValues;
// The timestamp set by the logd.
int64_t mLogdTimestampNs;
// The elapsed timestamp set by statsd log writer.
int64_t mElapsedTimestampNs;
// The atom tag of the event (defaults to 0 if client does not
// appropriately set the atom id).
int mTagId = 0;
// The uid of the logging client (defaults to -1).
int32_t mLogUid = -1;
// The pid of the logging client (defaults to -1).
int32_t mLogPid = -1;
// Annotations
bool mTruncateTimestamp = false;
int mResetState = -1;
// Indexes within the FieldValue vector can be stored in 7 bits because
// that's the assumption enforced by the encoding used in FieldValue.
int8_t mUidFieldIndex = -1;
int8_t mAttributionChainStartIndex = -1;
int8_t mAttributionChainEndIndex = -1;
int8_t mExclusiveStateFieldIndex = -1;
};
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2019 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "LogEventQueue.h"
namespace android {
namespace os {
namespace statsd {
using std::unique_lock;
using std::unique_ptr;
unique_ptr<LogEvent> LogEventQueue::waitPop() {
std::unique_lock<std::mutex> lock(mMutex);
if (mQueue.empty()) {
mCondition.wait(lock, [this] { return !this->mQueue.empty(); });
}
unique_ptr<LogEvent> item = std::move(mQueue.front());
mQueue.pop();
return item;
}
bool LogEventQueue::push(unique_ptr<LogEvent> item, int64_t* oldestTimestampNs) {
bool success;
{
std::unique_lock<std::mutex> lock(mMutex);
if (mQueue.size() < mQueueLimit) {
mQueue.push(std::move(item));
success = true;
} else {
// safe operation as queue must not be empty.
*oldestTimestampNs = mQueue.front()->GetElapsedTimestampNs();
success = false;
}
}
mCondition.notify_one();
return success;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,57 +0,0 @@
/*
* Copyright (C) 2019 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 "LogEvent.h"
#include <condition_variable>
#include <mutex>
#include <queue>
namespace android {
namespace os {
namespace statsd {
/**
* A zero copy thread safe queue buffer for producing and consuming LogEvent.
*/
class LogEventQueue {
public:
explicit LogEventQueue(size_t maxSize) : mQueueLimit(maxSize){};
/**
* Blocking read one event from the queue.
*/
std::unique_ptr<LogEvent> waitPop();
/**
* Puts a LogEvent ptr to the end of the queue.
* Returns false on failure when the queue is full, and output the oldest event timestamp
* in the queue.
*/
bool push(std::unique_ptr<LogEvent> event, int64_t* oldestTimestampNs);
private:
const size_t mQueueLimit;
std::condition_variable mCondition;
std::mutex mMutex;
std::queue<std::unique_ptr<LogEvent>> mQueue;
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,113 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "StatsService.h"
#include "socket/StatsSocketListener.h"
#include <android/binder_interface_utils.h>
#include <android/binder_process.h>
#include <android/binder_manager.h>
#include <utils/Looper.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
using namespace android;
using namespace android::os::statsd;
using ::ndk::SharedRefBase;
using std::shared_ptr;
using std::make_shared;
shared_ptr<StatsService> gStatsService = nullptr;
sp<StatsSocketListener> gSocketListener = nullptr;
void signalHandler(int sig) {
if (sig == SIGPIPE) {
// ShellSubscriber uses SIGPIPE as a signal to detect the end of the
// client process. Don't prematurely exit(1) here. Instead, ignore the
// signal and allow the write call to return EPIPE.
ALOGI("statsd received SIGPIPE. Ignoring signal.");
return;
}
if (gSocketListener != nullptr) gSocketListener->stopListener();
if (gStatsService != nullptr) gStatsService->Terminate();
ALOGW("statsd terminated on receiving signal %d.", sig);
exit(1);
}
void registerSignalHandlers()
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = signalHandler;
sigaction(SIGPIPE, &sa, nullptr);
sigaction(SIGHUP, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGQUIT, &sa, nullptr);
sigaction(SIGTERM, &sa, nullptr);
}
int main(int /*argc*/, char** /*argv*/) {
// Set up the looper
sp<Looper> looper(Looper::prepare(0 /* opts */));
// Set up the binder
ABinderProcess_setThreadPoolMaxThreadCount(9);
ABinderProcess_startThreadPool();
std::shared_ptr<LogEventQueue> eventQueue =
std::make_shared<LogEventQueue>(4000 /*buffer limit. Buffer is NOT pre-allocated*/);
// Create the service
gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue);
// TODO(b/149582373): Set DUMP_FLAG_PROTO once libbinder_ndk supports
// setting dumpsys priorities.
binder_status_t status = AServiceManager_addService(gStatsService->asBinder().get(), "stats");
if (status != STATUS_OK) {
ALOGE("Failed to add service as AIDL service");
return -1;
}
registerSignalHandlers();
gStatsService->sayHiToStatsCompanion();
gStatsService->Startup();
gSocketListener = new StatsSocketListener(eventQueue);
ALOGI("Statsd starts to listen to socket.");
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
if (gSocketListener->startListener(600)) {
exit(1);
}
// Loop forever -- the reports run on this thread in a handler, and the
// binder calls remain responsive in their pool of one thread.
while (true) {
looper->pollAll(-1 /* timeoutMillis */);
}
ALOGW("statsd escaped from its loop.");
return 1;
}

View File

@@ -1,120 +0,0 @@
/*
* 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 "Log.h"
#include "CombinationLogMatchingTracker.h"
#include "matchers/matcher_util.h"
namespace android {
namespace os {
namespace statsd {
using std::set;
using std::unordered_map;
using std::vector;
CombinationLogMatchingTracker::CombinationLogMatchingTracker(const int64_t& id, const int index)
: LogMatchingTracker(id, index) {
}
CombinationLogMatchingTracker::~CombinationLogMatchingTracker() {
}
bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers,
const vector<sp<LogMatchingTracker>>& allTrackers,
const unordered_map<int64_t, int>& matcherMap,
vector<bool>& stack) {
if (mInitialized) {
return true;
}
// mark this node as visited in the recursion stack.
stack[mIndex] = true;
AtomMatcher_Combination matcher = allLogMatchers[mIndex].combination();
// LogicalOperation is missing in the config
if (!matcher.has_operation()) {
return false;
}
mLogicalOperation = matcher.operation();
if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
return false;
}
for (const auto& child : matcher.matcher()) {
auto pair = matcherMap.find(child);
if (pair == matcherMap.end()) {
ALOGW("Matcher %lld not found in the config", (long long)child);
return false;
}
int childIndex = pair->second;
// if the child is a visited node in the recursion -> circle detected.
if (stack[childIndex]) {
ALOGE("Circle detected in matcher config");
return false;
}
if (!allTrackers[childIndex]->init(allLogMatchers, allTrackers, matcherMap, stack)) {
ALOGW("child matcher init failed %lld", (long long)child);
return false;
}
mChildren.push_back(childIndex);
const set<int>& childTagIds = allTrackers[childIndex]->getAtomIds();
mAtomIds.insert(childTagIds.begin(), childTagIds.end());
}
mInitialized = true;
// unmark this node in the recursion stack.
stack[mIndex] = false;
return true;
}
void CombinationLogMatchingTracker::onLogEvent(const LogEvent& event,
const vector<sp<LogMatchingTracker>>& allTrackers,
vector<MatchingState>& matcherResults) {
// this event has been processed.
if (matcherResults[mIndex] != MatchingState::kNotComputed) {
return;
}
if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
matcherResults[mIndex] = MatchingState::kNotMatched;
return;
}
// evaluate children matchers if they haven't been evaluated.
for (const int childIndex : mChildren) {
if (matcherResults[childIndex] == MatchingState::kNotComputed) {
const sp<LogMatchingTracker>& child = allTrackers[childIndex];
child->onLogEvent(event, allTrackers, matcherResults);
}
}
bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults);
matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,53 +0,0 @@
/*
* 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.
*/
#ifndef COMBINATION_LOG_MATCHING_TRACKER_H
#define COMBINATION_LOG_MATCHING_TRACKER_H
#include <unordered_map>
#include <vector>
#include "LogMatchingTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
namespace os {
namespace statsd {
// Represents a AtomMatcher_Combination in the StatsdConfig.
class CombinationLogMatchingTracker : public virtual LogMatchingTracker {
public:
CombinationLogMatchingTracker(const int64_t& id, const int index);
bool init(const std::vector<AtomMatcher>& allLogMatchers,
const std::vector<sp<LogMatchingTracker>>& allTrackers,
const std::unordered_map<int64_t, int>& matcherMap,
std::vector<bool>& stack);
~CombinationLogMatchingTracker();
void onLogEvent(const LogEvent& event,
const std::vector<sp<LogMatchingTracker>>& allTrackers,
std::vector<MatchingState>& matcherResults) override;
private:
LogicalOperation mLogicalOperation;
std::vector<int> mChildren;
};
} // namespace statsd
} // namespace os
} // namespace android
#endif // COMBINATION_LOG_MATCHING_TRACKER_H

View File

@@ -1,35 +0,0 @@
/*
* 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 "EventMatcherWizard.h"
namespace android {
namespace os {
namespace statsd {
using std::vector;
MatchingState EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcher_index) {
if (matcher_index < 0 || matcher_index >= (int)mAllEventMatchers.size()) {
return MatchingState::kNotComputed;
}
vector<MatchingState> matcherCache(mAllEventMatchers.size(), MatchingState::kNotComputed);
mAllEventMatchers[matcher_index]->onLogEvent(event, mAllEventMatchers, matcherCache);
return matcherCache[matcher_index];
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,41 +0,0 @@
/*
* 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.
*/
#pragma once
#include "LogMatchingTracker.h"
namespace android {
namespace os {
namespace statsd {
class EventMatcherWizard : public virtual android::RefBase {
public:
EventMatcherWizard(){}; // for testing
EventMatcherWizard(const std::vector<sp<LogMatchingTracker>>& eventTrackers)
: mAllEventMatchers(eventTrackers){};
virtual ~EventMatcherWizard(){};
MatchingState matchLogEvent(const LogEvent& event, int matcher_index);
private:
std::vector<sp<LogMatchingTracker>> mAllEventMatchers;
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,96 +0,0 @@
/*
* 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.
*/
#ifndef LOG_MATCHING_TRACKER_H
#define LOG_MATCHING_TRACKER_H
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "logd/LogEvent.h"
#include "matchers/matcher_util.h"
#include <utils/RefBase.h>
#include <set>
#include <unordered_map>
#include <vector>
namespace android {
namespace os {
namespace statsd {
class LogMatchingTracker : public virtual RefBase {
public:
LogMatchingTracker(const int64_t& id, const int index)
: mId(id), mIndex(index), mInitialized(false){};
virtual ~LogMatchingTracker(){};
// Initialize this LogMatchingTracker.
// allLogMatchers: the list of the AtomMatcher proto config. This is needed because we don't
// store the proto object in memory. We only need it during initilization.
// allTrackers: the list of the LogMatchingTracker objects. It's a one-to-one mapping with
// allLogMatchers. This is needed because the initialization is done recursively
// for CombinationLogMatchingTrackers using DFS.
// stack: a bit map to record which matcher has been visited on the stack. This is for detecting
// circle dependency.
virtual bool init(const std::vector<AtomMatcher>& allLogMatchers,
const std::vector<sp<LogMatchingTracker>>& allTrackers,
const std::unordered_map<int64_t, int>& matcherMap,
std::vector<bool>& stack) = 0;
// Called when a log event comes.
// event: the log event.
// allTrackers: the list of all LogMatchingTrackers. This is needed because the log processing
// is done recursively.
// matcherResults: The cached results for all matchers for this event. Parent matchers can
// directly access the children's matching results if they have been evaluated.
// Otherwise, call children matchers' onLogEvent.
virtual void onLogEvent(const LogEvent& event,
const std::vector<sp<LogMatchingTracker>>& allTrackers,
std::vector<MatchingState>& matcherResults) = 0;
// Get the tagIds that this matcher cares about. The combined collection is stored
// in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
// some memory but hopefully it can save us much CPU time when there is flood of events.
virtual const std::set<int>& getAtomIds() const {
return mAtomIds;
}
const int64_t& getId() const {
return mId;
}
protected:
// Name of this matching. We don't really need the name, but it makes log message easy to debug.
const int64_t mId;
// Index of this LogMatchingTracker in MetricsManager's container.
const int mIndex;
// Whether this LogMatchingTracker has been properly initialized.
bool mInitialized;
// The collection of the event tag ids that this LogMatchingTracker cares. So we can quickly
// return kNotMatched when we receive an event with an id not in the list. This is especially
// useful when we have a complex CombinationLogMatcherTracker.
std::set<int> mAtomIds;
};
} // namespace statsd
} // namespace os
} // namespace android
#endif // LOG_MATCHING_TRACKER_H

View File

@@ -1,73 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "SimpleLogMatchingTracker.h"
namespace android {
namespace os {
namespace statsd {
using std::unordered_map;
using std::vector;
SimpleLogMatchingTracker::SimpleLogMatchingTracker(const int64_t& id, const int index,
const SimpleAtomMatcher& matcher,
const UidMap& uidMap)
: LogMatchingTracker(id, index), mMatcher(matcher), mUidMap(uidMap) {
if (!matcher.has_atom_id()) {
mInitialized = false;
} else {
mAtomIds.insert(matcher.atom_id());
mInitialized = true;
}
}
SimpleLogMatchingTracker::~SimpleLogMatchingTracker() {
}
bool SimpleLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers,
const vector<sp<LogMatchingTracker>>& allTrackers,
const unordered_map<int64_t, int>& matcherMap,
vector<bool>& stack) {
// no need to do anything.
return mInitialized;
}
void SimpleLogMatchingTracker::onLogEvent(const LogEvent& event,
const vector<sp<LogMatchingTracker>>& allTrackers,
vector<MatchingState>& matcherResults) {
if (matcherResults[mIndex] != MatchingState::kNotComputed) {
VLOG("Matcher %lld already evaluated ", (long long)mId);
return;
}
if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
matcherResults[mIndex] = MatchingState::kNotMatched;
return;
}
bool matched = matchesSimple(mUidMap, mMatcher, event);
matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
VLOG("Stats SimpleLogMatcher %lld matched? %d", (long long)mId, matched);
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,55 +0,0 @@
/*
* 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.
*/
#ifndef SIMPLE_LOG_MATCHING_TRACKER_H
#define SIMPLE_LOG_MATCHING_TRACKER_H
#include <unordered_map>
#include <vector>
#include "LogMatchingTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "packages/UidMap.h"
namespace android {
namespace os {
namespace statsd {
class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
public:
SimpleLogMatchingTracker(const int64_t& id, const int index,
const SimpleAtomMatcher& matcher,
const UidMap& uidMap);
~SimpleLogMatchingTracker();
bool init(const std::vector<AtomMatcher>& allLogMatchers,
const std::vector<sp<LogMatchingTracker>>& allTrackers,
const std::unordered_map<int64_t, int>& matcherMap,
std::vector<bool>& stack) override;
void onLogEvent(const LogEvent& event,
const std::vector<sp<LogMatchingTracker>>& allTrackers,
std::vector<MatchingState>& matcherResults) override;
private:
const SimpleAtomMatcher mMatcher;
const UidMap& mUidMap;
};
} // namespace statsd
} // namespace os
} // namespace android
#endif // SIMPLE_LOG_MATCHING_TRACKER_H

View File

@@ -1,372 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/LogMatchingTracker.h"
#include "matchers/matcher_util.h"
#include "stats_util.h"
using std::set;
using std::string;
using std::vector;
namespace android {
namespace os {
namespace statsd {
bool combinationMatch(const vector<int>& children, const LogicalOperation& operation,
const vector<MatchingState>& matcherResults) {
bool matched;
switch (operation) {
case LogicalOperation::AND: {
matched = true;
for (const int childIndex : children) {
if (matcherResults[childIndex] != MatchingState::kMatched) {
matched = false;
break;
}
}
break;
}
case LogicalOperation::OR: {
matched = false;
for (const int childIndex : children) {
if (matcherResults[childIndex] == MatchingState::kMatched) {
matched = true;
break;
}
}
break;
}
case LogicalOperation::NOT:
matched = matcherResults[children[0]] == MatchingState::kNotMatched;
break;
case LogicalOperation::NAND:
matched = false;
for (const int childIndex : children) {
if (matcherResults[childIndex] != MatchingState::kMatched) {
matched = true;
break;
}
}
break;
case LogicalOperation::NOR:
matched = true;
for (const int childIndex : children) {
if (matcherResults[childIndex] == MatchingState::kMatched) {
matched = false;
break;
}
}
break;
case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
matched = false;
break;
}
return matched;
}
bool tryMatchString(const UidMap& uidMap, const FieldValue& fieldValue, const string& str_match) {
if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
int uid = fieldValue.mValue.int_value;
auto aidIt = UidMap::sAidToUidMapping.find(str_match);
if (aidIt != UidMap::sAidToUidMapping.end()) {
return ((int)aidIt->second) == uid;
}
std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
return packageNames.find(str_match) != packageNames.end();
} else if (fieldValue.mValue.getType() == STRING) {
return fieldValue.mValue.str_value == str_match;
}
return false;
}
bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
const vector<FieldValue>& values, int start, int end, int depth) {
if (depth > 2) {
ALOGE("Depth > 3 not supported");
return false;
}
if (start >= end) {
return false;
}
// Filter by entry field first
int newStart = -1;
int newEnd = end;
// because the fields are naturally sorted in the DFS order. we can safely
// break when pos is larger than the one we are searching for.
for (int i = start; i < end; i++) {
int pos = values[i].mField.getPosAtDepth(depth);
if (pos == matcher.field()) {
if (newStart == -1) {
newStart = i;
}
newEnd = i + 1;
} else if (pos > matcher.field()) {
break;
}
}
// Now we have zoomed in to a new range
start = newStart;
end = newEnd;
if (start == -1) {
// No such field found.
return false;
}
vector<pair<int, int>> ranges; // the ranges are for matching ANY position
if (matcher.has_position()) {
// Repeated fields position is stored as a node in the path.
depth++;
if (depth > 2) {
return false;
}
switch (matcher.position()) {
case Position::FIRST: {
for (int i = start; i < end; i++) {
int pos = values[i].mField.getPosAtDepth(depth);
if (pos != 1) {
// Again, the log elements are stored in sorted order. so
// once the position is > 1, we break;
end = i;
break;
}
}
ranges.push_back(std::make_pair(start, end));
break;
}
case Position::LAST: {
// move the starting index to the first LAST field at the depth.
for (int i = start; i < end; i++) {
if (values[i].mField.isLastPos(depth)) {
start = i;
break;
}
}
ranges.push_back(std::make_pair(start, end));
break;
}
case Position::ANY: {
// ANY means all the children matchers match in any of the sub trees, it's a match
newStart = start;
newEnd = end;
// Here start is guaranteed to be a valid index.
int currentPos = values[start].mField.getPosAtDepth(depth);
// Now find all sub trees ranges.
for (int i = start; i < end; i++) {
int newPos = values[i].mField.getPosAtDepth(depth);
if (newPos != currentPos) {
ranges.push_back(std::make_pair(newStart, i));
newStart = i;
currentPos = newPos;
}
}
ranges.push_back(std::make_pair(newStart, end));
break;
}
case Position::ALL:
ALOGE("Not supported: field matcher with ALL position.");
break;
case Position::POSITION_UNKNOWN:
break;
}
} else {
// No position
ranges.push_back(std::make_pair(start, end));
}
// start and end are still pointing to the matched range.
switch (matcher.value_matcher_case()) {
case FieldValueMatcher::kMatchesTuple: {
++depth;
// If any range matches all matchers, good.
for (const auto& range : ranges) {
bool matched = true;
for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) {
if (!matchesSimple(uidMap, subMatcher, values, range.first, range.second,
depth)) {
matched = false;
break;
}
}
if (matched) return true;
}
return false;
}
// Finally, we get to the point of real value matching.
// If the field matcher ends with ANY, then we have [start, end) range > 1.
// In the following, we should return true, when ANY of the values matches.
case FieldValueMatcher::ValueMatcherCase::kEqBool: {
for (int i = start; i < end; i++) {
if ((values[i].mValue.getType() == INT &&
(values[i].mValue.int_value != 0) == matcher.eq_bool()) ||
(values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value != 0) == matcher.eq_bool())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqString: {
for (int i = start; i < end; i++) {
if (tryMatchString(uidMap, values[i], matcher.eq_string())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kNeqAnyString: {
const auto& str_list = matcher.neq_any_string();
for (int i = start; i < end; i++) {
bool notEqAll = true;
for (const auto& str : str_list.str_value()) {
if (tryMatchString(uidMap, values[i], str)) {
notEqAll = false;
break;
}
}
if (notEqAll) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqAnyString: {
const auto& str_list = matcher.eq_any_string();
for (int i = start; i < end; i++) {
for (const auto& str : str_list.str_value()) {
if (tryMatchString(uidMap, values[i], str)) {
return true;
}
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(matcher.eq_int() == values[i].mValue.int_value)) {
return true;
}
// eq_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(matcher.eq_int() == values[i].mValue.long_value)) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kLtInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value < matcher.lt_int())) {
return true;
}
// lt_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value < matcher.lt_int())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kGtInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value > matcher.gt_int())) {
return true;
}
// gt_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value > matcher.gt_int())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kLtFloat: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == FLOAT &&
(values[i].mValue.float_value < matcher.lt_float())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kGtFloat: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == FLOAT &&
(values[i].mValue.float_value > matcher.gt_float())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kLteInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value <= matcher.lte_int())) {
return true;
}
// lte_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value <= matcher.lte_int())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kGteInt: {
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value >= matcher.gte_int())) {
return true;
}
// gte_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value >= matcher.gte_int())) {
return true;
}
}
return false;
}
default:
return false;
}
}
bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
const LogEvent& event) {
if (event.GetTagId() != simpleMatcher.atom_id()) {
return false;
}
for (const auto& matcher : simpleMatcher.field_value_matcher()) {
if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
return false;
}
}
return true;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,44 +0,0 @@
/*
* 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 "logd/LogEvent.h"
#include <vector>
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "packages/UidMap.h"
#include "stats_util.h"
namespace android {
namespace os {
namespace statsd {
enum MatchingState {
kNotComputed = -1,
kNotMatched = 0,
kMatched = 1,
};
bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
const std::vector<MatchingState>& matcherResults);
bool matchesSimple(const UidMap& uidMap,
const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,122 +0,0 @@
/*
* Copyright (C) 2020 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 "FieldValue.h"
#include "metadata_util.h"
namespace android {
namespace os {
namespace statsd {
using google::protobuf::RepeatedPtrField;
void writeValueToProto(metadata::FieldValue* metadataFieldValue, const Value& value) {
std::string storage_value;
switch (value.getType()) {
case INT:
metadataFieldValue->set_value_int(value.int_value);
break;
case LONG:
metadataFieldValue->set_value_long(value.long_value);
break;
case FLOAT:
metadataFieldValue->set_value_float(value.float_value);
break;
case DOUBLE:
metadataFieldValue->set_value_double(value.double_value);
break;
case STRING:
metadataFieldValue->set_value_str(value.str_value.c_str());
break;
case STORAGE: // byte array
storage_value = ((char*) value.storage_value.data());
metadataFieldValue->set_value_storage(storage_value);
break;
default:
break;
}
}
void writeMetricDimensionKeyToMetadataDimensionKey(
const MetricDimensionKey& metricKey,
metadata::MetricDimensionKey* metadataMetricKey) {
for (const FieldValue& fieldValue : metricKey.getDimensionKeyInWhat().getValues()) {
metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_dimension_key_in_what();
metadata::Field* metadataField = metadataFieldValue->mutable_field();
metadataField->set_tag(fieldValue.mField.getTag());
metadataField->set_field(fieldValue.mField.getField());
writeValueToProto(metadataFieldValue, fieldValue.mValue);
}
for (const FieldValue& fieldValue : metricKey.getStateValuesKey().getValues()) {
metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_state_values_key();
metadata::Field* metadataField = metadataFieldValue->mutable_field();
metadataField->set_tag(fieldValue.mField.getTag());
metadataField->set_field(fieldValue.mField.getField());
writeValueToProto(metadataFieldValue, fieldValue.mValue);
}
}
void writeFieldValuesFromMetadata(
const RepeatedPtrField<metadata::FieldValue>& repeatedFieldValueList,
std::vector<FieldValue>* fieldValues) {
for (const metadata::FieldValue& metadataFieldValue : repeatedFieldValueList) {
Field field(metadataFieldValue.field().tag(), metadataFieldValue.field().field());
Value value;
switch (metadataFieldValue.value_case()) {
case metadata::FieldValue::ValueCase::kValueInt:
value = Value(metadataFieldValue.value_int());
break;
case metadata::FieldValue::ValueCase::kValueLong:
value = Value(metadataFieldValue.value_long());
break;
case metadata::FieldValue::ValueCase::kValueFloat:
value = Value(metadataFieldValue.value_float());
break;
case metadata::FieldValue::ValueCase::kValueDouble:
value = Value(metadataFieldValue.value_double());
break;
case metadata::FieldValue::ValueCase::kValueStr:
value = Value(metadataFieldValue.value_str());
break;
case metadata::FieldValue::ValueCase::kValueStorage:
value = Value(metadataFieldValue.value_storage());
break;
default:
break;
}
FieldValue fieldValue(field, value);
fieldValues->emplace_back(field, value);
}
}
MetricDimensionKey loadMetricDimensionKeyFromProto(
const metadata::MetricDimensionKey& metricDimensionKey) {
std::vector<FieldValue> dimKeyInWhatFieldValues;
writeFieldValuesFromMetadata(metricDimensionKey.dimension_key_in_what(),
&dimKeyInWhatFieldValues);
std::vector<FieldValue> stateValuesFieldValues;
writeFieldValuesFromMetadata(metricDimensionKey.state_values_key(), &stateValuesFieldValues);
HashableDimensionKey dimKeyInWhat(dimKeyInWhatFieldValues);
HashableDimensionKey stateValues(stateValuesFieldValues);
MetricDimensionKey metricKey(dimKeyInWhat, stateValues);
return metricKey;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,32 +0,0 @@
/*
* Copyright (C) 2020 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 "HashableDimensionKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata
namespace android {
namespace os {
namespace statsd {
void writeMetricDimensionKeyToMetadataDimensionKey(const MetricDimensionKey& metricKey,
metadata::MetricDimensionKey* metadataMetricKey);
MetricDimensionKey loadMetricDimensionKeyFromProto(
const metadata::MetricDimensionKey& metricDimensionKey);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,397 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "CountMetricProducer.h"
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include "guardrail/StatsdStats.h"
#include "stats_log_util.h"
#include "stats_util.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::map;
using std::string;
using std::unordered_map;
using std::vector;
using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
// for StatsLogReport
const int FIELD_ID_ID = 1;
const int FIELD_ID_COUNT_METRICS = 5;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_IS_ACTIVE = 14;
// for CountMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for CountMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
const int FIELD_ID_SLICE_BY_STATE = 6;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
// for CountBucketInfo
const int FIELD_ID_COUNT = 3;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
CountMetricProducer::CountMetricProducer(
const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
const int64_t timeBaseNs, const int64_t startTimeNs,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
} else {
mBucketSizeNs = LLONG_MAX;
}
if (metric.has_dimensions_in_what()) {
translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
mc.conditionId = link.condition();
translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
mMetric2ConditionLinks.push_back(mc);
}
mConditionSliced = true;
}
for (const auto& stateLink : metric.state_link()) {
Metric2State ms;
ms.stateAtomId = stateLink.state_atom_id();
translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
mMetric2StateLinks.push_back(ms);
}
flushIfNeededLocked(startTimeNs);
// Adjust start for partial bucket
mCurrentBucketStartTimeNs = startTimeNs;
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
CountMetricProducer::~CountMetricProducer() {
VLOG("~CountMetricProducer() called");
}
void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
const HashableDimensionKey& primaryKey,
const FieldValue& oldState, const FieldValue& newState) {
VLOG("CountMetric %lld onStateChanged time %lld, State%d, key %s, %d -> %d",
(long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
oldState.mValue.int_value, newState.mValue.int_value);
}
void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (mCurrentSlicedCounter == nullptr ||
mCurrentSlicedCounter->size() == 0) {
return;
}
fprintf(out, "CountMetric %lld dimension size %lu\n", (long long)mMetricId,
(unsigned long)mCurrentSlicedCounter->size());
if (verbose) {
for (const auto& it : *mCurrentSlicedCounter) {
fprintf(out, "\t(what)%s\t(state)%s %lld\n",
it.first.getDimensionKeyInWhat().toString().c_str(),
it.first.getStateValuesKey().toString().c_str(), (unsigned long long)it.second);
}
}
}
void CountMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
const int64_t eventTime) {
VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
}
void CountMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
mPastBuckets.clear();
}
void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
if (include_current_partial_bucket) {
flushLocked(dumpTimeNs);
} else {
flushIfNeededLocked(dumpTimeNs);
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty()) {
return;
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
// Fills the dimension path if not slicing by ALL.
if (!mSliceByPositionALL) {
if (!mDimensionsInWhat.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
for (const auto& counter : mPastBuckets) {
const MetricDimensionKey& dimensionKey = counter.first;
VLOG(" dimension key %s", dimensionKey.toString().c_str());
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
if (mSliceByPositionALL) {
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
}
// Then fill slice_by_state.
for (auto state : dimensionKey.getStateValuesKey().getValues()) {
uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
FIELD_ID_SLICE_BY_STATE);
writeStateToProto(state, protoOutput);
protoOutput->end(stateToken);
}
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
uint64_t bucketInfoToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
// Partial bucket.
if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketStartNs));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketEndNs));
} else {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
protoOutput->end(bucketInfoToken);
VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
(long long)bucket.mBucketEndNs, (long long)bucket.mCount);
}
protoOutput->end(wrapperToken);
}
protoOutput->end(protoToken);
if (erase_data) {
mPastBuckets.clear();
}
}
void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
flushIfNeededLocked(dropTimeNs);
StatsdStats::getInstance().noteBucketDropped(mMetricId);
mPastBuckets.clear();
}
void CountMetricProducer::onConditionChangedLocked(const bool conditionMet,
const int64_t eventTime) {
VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
}
bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
return false;
}
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedCounter->size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
// 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.toString().c_str());
StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
return false;
}
void CountMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
const map<int, HashableDimensionKey>& statePrimaryKeys) {
int64_t eventTimeNs = event.GetElapsedTimestampNs();
flushIfNeededLocked(eventTimeNs);
if (!condition) {
return;
}
auto it = mCurrentSlicedCounter->find(eventKey);
if (it == mCurrentSlicedCounter->end()) {
// ===========GuardRail==============
if (hitGuardRailLocked(eventKey)) {
return;
}
// create a counter for the new key
(*mCurrentSlicedCounter)[eventKey] = 1;
} else {
// increment the existing value
auto& count = it->second;
count++;
}
for (auto& tracker : mAnomalyTrackers) {
int64_t countWholeBucket = mCurrentSlicedCounter->find(eventKey)->second;
auto prev = mCurrentFullCounters->find(eventKey);
if (prev != mCurrentFullCounters->end()) {
countWholeBucket += prev->second;
}
tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
countWholeBucket);
}
VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.toString().c_str(),
(long long)(*mCurrentSlicedCounter)[eventKey]);
}
// When a new matched event comes in, we check if event falls into the current
// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
void CountMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
if (eventTimeNs < currentBucketEndTimeNs) {
return;
}
// Setup the bucket start time and number.
int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
mCurrentBucketNum += numBucketsForward;
VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
(long long)mCurrentBucketStartTimeNs);
}
void CountMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
CountBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
if (eventTimeNs < fullBucketEndTimeNs) {
info.mBucketEndNs = eventTimeNs;
} else {
info.mBucketEndNs = fullBucketEndTimeNs;
}
for (const auto& counter : *mCurrentSlicedCounter) {
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.toString().c_str(),
(long long)counter.second);
}
// If we have finished a full bucket, then send this to anomaly tracker.
if (eventTimeNs > fullBucketEndTimeNs) {
// Accumulate partial buckets with current value and then send to anomaly tracker.
if (mCurrentFullCounters->size() > 0) {
for (const auto& keyValuePair : *mCurrentSlicedCounter) {
(*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second;
}
for (auto& tracker : mAnomalyTrackers) {
tracker->addPastBucket(mCurrentFullCounters, mCurrentBucketNum);
}
mCurrentFullCounters = std::make_shared<DimToValMap>();
} else {
// Skip aggregating the partial buckets since there's no previous partial bucket.
for (auto& tracker : mAnomalyTrackers) {
tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum);
}
}
} else {
// Accumulate partial bucket.
for (const auto& keyValuePair : *mCurrentSlicedCounter) {
(*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second;
}
}
StatsdStats::getInstance().noteBucketCount(mMetricId);
// Only resets the counters, but doesn't setup the times nor numbers.
// (Do not clear since the old one is still referenced in mAnomalyTrackers).
mCurrentSlicedCounter = std::make_shared<DimToValMap>();
mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
}
// Rough estimate of CountMetricProducer buffer stored. This number will be
// greater than actual data size as it contains each dimension of
// CountMetricData is duplicated.
size_t CountMetricProducer::byteSizeLocked() const {
size_t totalSize = 0;
for (const auto& pair : mPastBuckets) {
totalSize += pair.second.size() * kBucketSize;
}
return totalSize;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,123 +0,0 @@
/*
* 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.
*/
#ifndef COUNT_METRIC_PRODUCER_H
#define COUNT_METRIC_PRODUCER_H
#include <android/util/ProtoOutputStream.h>
#include <gtest/gtest_prod.h>
#include <unordered_map>
#include "MetricProducer.h"
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/matcher_util.h"
#include "stats_util.h"
namespace android {
namespace os {
namespace statsd {
struct CountBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
int64_t mCount;
};
class CountMetricProducer : public MetricProducer {
public:
CountMetricProducer(
const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
const int64_t timeBaseNs, const int64_t startTimeNs,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap = {},
const vector<int>& slicedStateAtoms = {},
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~CountMetricProducer();
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
const HashableDimensionKey& primaryKey, const FieldValue& oldState,
const FieldValue& newState) override;
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
// Internal function to calculate the current used bytes.
size_t byteSizeLocked() const override;
void dumpStatesLocked(FILE* out, bool verbose) const override;
void dropDataLocked(const int64_t dropTimeNs) override;
// Util function to flush the old packet.
void flushIfNeededLocked(const int64_t& newEventTime) override;
void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) override;
std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
// The current bucket (may be a partial bucket).
std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
// The sum of previous partial buckets in the current full bucket (excluding the current
// partial bucket). This is only updated while flushing the current bucket.
std::shared_ptr<DimToValMap> mCurrentFullCounters = std::make_shared<DimToValMap>();
static const size_t kBucketSize = sizeof(CountBucket{});
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit);
FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket);
FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket);
};
} // namespace statsd
} // namespace os
} // namespace android
#endif // COUNT_METRIC_PRODUCER_H

View File

@@ -1,653 +0,0 @@
/*
* 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.
*/
#define DEBUG false
#include "Log.h"
#include "DurationMetricProducer.h"
#include "guardrail/StatsdStats.h"
#include "stats_util.h"
#include "stats_log_util.h"
#include <limits.h>
#include <stdlib.h>
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::string;
using std::unordered_map;
using std::vector;
using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
// for StatsLogReport
const int FIELD_ID_ID = 1;
const int FIELD_ID_DURATION_METRICS = 6;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_IS_ACTIVE = 14;
// for DurationMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for DurationMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
const int FIELD_ID_SLICE_BY_STATE = 6;
// for DurationBucketInfo
const int FIELD_ID_DURATION = 3;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
DurationMetricProducer::DurationMetricProducer(
const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const size_t startIndex,
const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
const int64_t timeBaseNs, const int64_t startTimeNs,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
mAggregationType(metric.aggregation_type()),
mStartIndex(startIndex),
mStopIndex(stopIndex),
mStopAllIndex(stopAllIndex),
mNested(nesting),
mContainANYPositionInInternalDimensions(false) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
} else {
mBucketSizeNs = LLONG_MAX;
}
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 (mContainANYPositionInInternalDimensions) {
ALOGE("Position ANY in internal dimension not supported.");
}
if (mContainANYPositionInDimensionsInWhat) {
ALOGE("Position ANY in dimension_in_what not supported.");
}
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
mc.conditionId = link.condition();
translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
mMetric2ConditionLinks.push_back(mc);
}
mConditionSliced = true;
}
mUnSlicedPartCondition = ConditionState::kUnknown;
for (const auto& stateLink : metric.state_link()) {
Metric2State ms;
ms.stateAtomId = stateLink.state_atom_id();
translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
mMetric2StateLinks.push_back(ms);
}
mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
if (mWizard != nullptr && mConditionTrackerIndex >= 0 &&
mMetric2ConditionLinks.size() == 1) {
mHasLinksToAllConditionDimensionsInTracker = mWizard->equalOutputDimensions(
mConditionTrackerIndex, mMetric2ConditionLinks.begin()->conditionFields);
}
flushIfNeededLocked(startTimeNs);
// Adjust start for partial bucket
mCurrentBucketStartTimeNs = startTimeNs;
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
DurationMetricProducer::~DurationMetricProducer() {
VLOG("~DurationMetric() called");
}
sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
std::lock_guard<std::mutex> lock(mMutex);
if (mAggregationType == DurationMetric_AggregationType_SUM) {
if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) {
ALOGW("invalid alert for SUM: threshold (%f) > possible recordable value (%d x %lld)",
alert.trigger_if_sum_gt(), alert.num_buckets(), (long long)mBucketSizeNs);
return nullptr;
}
}
sp<DurationAnomalyTracker> anomalyTracker =
new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
if (anomalyTracker != nullptr) {
mAnomalyTrackers.push_back(anomalyTracker);
}
return anomalyTracker;
}
void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
const HashableDimensionKey& primaryKey,
const FieldValue& oldState,
const FieldValue& newState) {
// Check if this metric has a StateMap. If so, map the new state value to
// the correct state group id.
FieldValue newStateCopy = newState;
mapStateValue(atomId, &newStateCopy);
flushIfNeededLocked(eventTimeNs);
// Each duration tracker is mapped to a different whatKey (a set of values from the
// dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the
// state change event are a subset of the tracker's whatKey field values.
//
// Ex. For a duration metric dimensioned on uid and tag:
// DurationTracker1 whatKey = uid: 1001, tag: 1
// DurationTracker2 whatKey = uid: 1002, tag 1
//
// If the state change primaryKey = uid: 1001, we only notify DurationTracker1 of a state
// change.
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) {
continue;
}
whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy);
}
}
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
const MetricDimensionKey& eventKey) const {
switch (mAggregationType) {
case DurationMetric_AggregationType_SUM:
return make_unique<OringDurationTracker>(
mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
case DurationMetric_AggregationType_MAX_SPARSE:
return make_unique<MaxDurationTracker>(
mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
}
}
// SlicedConditionChange optimization case 1:
// 1. If combination condition, logical operation is AND, only one sliced child predicate.
// 2. The links covers all dimension fields in the sliced child condition predicate.
void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool condition,
const int64_t eventTime) {
if (mMetric2ConditionLinks.size() != 1 ||
!mHasLinksToAllConditionDimensionsInTracker) {
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()) {
whatIt.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()) {
whatIt.second->onConditionChanged(true, eventTime);
}
if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
dimensionsChangedToFalse->end()) {
whatIt.second->onConditionChanged(false, eventTime);
}
}
}
}
}
void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(bool overallCondition,
const int64_t eventTimeNs) {
bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker) {
onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTimeNs);
return;
}
// Now for each of the on-going event, check if the condition has changed for them.
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
whatIt.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
}
}
void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
const int64_t eventTime) {
VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
if (!mIsActive) {
return;
}
flushIfNeededLocked(eventTime);
if (!mConditionSliced) {
return;
}
onSlicedConditionMayChangeInternalLocked(overallCondition, eventTime);
}
void DurationMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
MetricProducer::onActiveStateChangedLocked(eventTimeNs);
if (!mConditionSliced) {
if (ConditionState::kTrue != mCondition) {
return;
}
if (mIsActive) {
flushIfNeededLocked(eventTimeNs);
}
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
}
} else if (mIsActive) {
flushIfNeededLocked(eventTimeNs);
onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs);
} else { // mConditionSliced == true && !mIsActive
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
}
}
}
void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
const int64_t eventTime) {
VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
if (!mIsActive) {
return;
}
flushIfNeededLocked(eventTime);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
whatIt.second->onConditionChanged(conditionMet, eventTime);
}
}
void DurationMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
flushIfNeededLocked(dropTimeNs);
StatsdStats::getInstance().noteBucketDropped(mMetricId);
mPastBuckets.clear();
}
void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
flushIfNeededLocked(dumpTimeNs);
mPastBuckets.clear();
}
void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
if (include_current_partial_bucket) {
flushLocked(dumpTimeNs);
} else {
flushIfNeededLocked(dumpTimeNs);
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty()) {
VLOG(" Duration metric, empty return");
return;
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
if (!mSliceByPositionALL) {
if (!mDimensionsInWhat.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
VLOG("Duration metric %lld dump report now...", (long long)mMetricId);
for (const auto& pair : mPastBuckets) {
const MetricDimensionKey& dimensionKey = pair.first;
VLOG(" dimension key %s", dimensionKey.toString().c_str());
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
if (mSliceByPositionALL) {
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
}
// Then fill slice_by_state.
for (auto state : dimensionKey.getStateValuesKey().getValues()) {
uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
FIELD_ID_SLICE_BY_STATE);
writeStateToProto(state, protoOutput);
protoOutput->end(stateToken);
}
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
uint64_t bucketInfoToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketStartNs));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketEndNs));
} else {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration);
protoOutput->end(bucketInfoToken);
VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs,
(long long)bucket.mBucketEndNs, (long long)bucket.mDuration);
}
protoOutput->end(wrapperToken);
}
protoOutput->end(protoToken);
if (erase_data) {
mPastBuckets.clear();
}
}
void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
if (currentBucketEndTimeNs > eventTimeNs) {
return;
}
VLOG("flushing...........");
int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
mCurrentBucketNum += numBucketsForward;
}
void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {
for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
whatIt != mCurrentSlicedDurationTrackerMap.end();) {
if (whatIt->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
VLOG("erase bucket for key %s", whatIt->first.toString().c_str());
whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
} else {
++whatIt;
}
}
StatsdStats::getInstance().noteBucketCount(mMetricId);
mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
}
void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (mCurrentSlicedDurationTrackerMap.size() == 0) {
return;
}
fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
(unsigned long)mCurrentSlicedDurationTrackerMap.size());
if (verbose) {
for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str());
whatIt.second->dumpStates(out, verbose);
}
}
}
bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(
mConfigKey, mMetricId, newTupleCount);
// 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 what dimension key %s",
(long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
}
return false;
}
void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys,
bool condition, const LogEvent& event) {
const auto& whatKey = eventKey.getDimensionKeyInWhat();
auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
if (hitGuardRailLocked(eventKey)) {
return;
}
mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey);
}
auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (mUseWhatDimensionAsInternalDimension) {
it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
return;
}
if (mInternalDimensions.empty()) {
it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(),
conditionKeys);
} else {
HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(),
conditionKeys);
}
}
void DurationMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
const map<int, HashableDimensionKey>& statePrimaryKeys) {
ALOGW("Not used in duration tracker.");
}
void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
const LogEvent& event) {
int64_t eventTimeNs = event.GetElapsedTimestampNs();
if (eventTimeNs < mTimeBaseNs) {
return;
}
if (mIsActive) {
flushIfNeededLocked(event.GetElapsedTimestampNs());
}
// Handles Stopall events.
if (matcherIndex == mStopAllIndex) {
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
}
return;
}
HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
if (!mDimensionsInWhat.empty()) {
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
}
// Stores atom id to primary key pairs for each state atom that the metric is
// sliced by.
std::map<int, HashableDimensionKey> statePrimaryKeys;
// For states with primary fields, use MetricStateLinks to get the primary
// field values from the log event. These values will form a primary key
// that will be used to query StateTracker for the correct state value.
for (const auto& stateLink : mMetric2StateLinks) {
getDimensionForState(event.getValues(), stateLink,
&statePrimaryKeys[stateLink.stateAtomId]);
}
// For each sliced state, query StateTracker for the state value using
// either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
//
// Expected functionality: for any case where the MetricStateLinks are
// initialized incorrectly (ex. # of state links != # of primary fields, no
// links are provided for a state with primary fields, links are provided
// in the wrong order, etc.), StateTracker will simply return kStateUnknown
// when queried using an incorrect key.
HashableDimensionKey stateValuesKey = DEFAULT_DIMENSION_KEY;
for (auto atomId : mSlicedStateAtoms) {
FieldValue value;
if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
// found a primary key for this state, query using the key
queryStateValue(atomId, statePrimaryKeys[atomId], &value);
} else {
// if no MetricStateLinks exist for this state atom,
// query using the default dimension key (empty HashableDimensionKey)
queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
}
mapStateValue(atomId, &value);
stateValuesKey.addValue(value);
}
// Handles Stop events.
if (matcherIndex == mStopIndex) {
if (mUseWhatDimensionAsInternalDimension) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
whatIt->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()) {
whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false);
}
return;
}
bool condition;
ConditionKey conditionKey;
if (mConditionSliced) {
for (const auto& link : mMetric2ConditionLinks) {
getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
}
auto conditionState =
mWizard->query(mConditionTrackerIndex, conditionKey,
!mHasLinksToAllConditionDimensionsInTracker);
condition = conditionState == ConditionState::kTrue;
} else {
// TODO: The unknown condition state is not handled here, we should fix it.
condition = mCondition == ConditionState::kTrue;
}
condition = condition && mIsActive;
handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
event);
}
size_t DurationMetricProducer::byteSizeLocked() const {
size_t totalSize = 0;
for (const auto& pair : mPastBuckets) {
totalSize += pair.second.size() * kBucketSize;
}
return totalSize;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,169 +0,0 @@
/*
* 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 <unordered_map>
#include <android/util/ProtoOutputStream.h>
#include "../anomaly/DurationAnomalyTracker.h"
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
#include "duration_helper/DurationTracker.h"
#include "duration_helper/MaxDurationTracker.h"
#include "duration_helper/OringDurationTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
using namespace std;
namespace android {
namespace os {
namespace statsd {
class DurationMetricProducer : public MetricProducer {
public:
DurationMetricProducer(
const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const size_t startIndex,
const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
const int64_t timeBaseNs, const int64_t startTimeNs,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {},
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {},
const vector<int>& slicedStateAtoms = {},
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~DurationMetricProducer();
sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
const HashableDimensionKey& primaryKey, const FieldValue& oldState,
const FieldValue& newState) override;
protected:
void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
bool condition, const LogEvent& event);
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
// Internal interface to handle active state change.
void onActiveStateChangedLocked(const int64_t& eventTimeNs) override;
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
void onSlicedConditionMayChangeInternalLocked(bool overallCondition,
const int64_t eventTimeNs);
void onSlicedConditionMayChangeLocked_opt1(bool overallCondition, const int64_t eventTime);
void onSlicedConditionMayChangeLocked_opt2(bool overallCondition, const int64_t eventTime);
// Internal function to calculate the current used bytes.
size_t byteSizeLocked() const override;
void dumpStatesLocked(FILE* out, bool verbose) const override;
void dropDataLocked(const int64_t dropTimeNs) override;
// Util function to flush the old packet.
void flushIfNeededLocked(const int64_t& eventTime);
void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) override;
const DurationMetric_AggregationType mAggregationType;
// Index of the SimpleAtomMatcher which defines the start.
const size_t mStartIndex;
// Index of the SimpleAtomMatcher which defines the stop.
const size_t mStopIndex;
// Index of the SimpleAtomMatcher which defines the stop all for all dimensions.
const size_t mStopAllIndex;
// nest counting -- for the same key, stops must match the number of starts to make real stop
const bool mNested;
// 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.
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
// The duration trackers in the current bucket.
std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
mCurrentSlicedDurationTrackerMap;
// Helper function to create a duration tracker given the metric aggregation type.
std::unique_ptr<DurationTracker> createDurationTracker(
const MetricDimensionKey& eventKey) const;
// This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(DurationBucket{});
FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestSumDuration);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket,
TestSumDurationWithSplitInFollowingBucket);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration);
FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,170 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "EventMetricProducer.h"
#include "stats_util.h"
#include "stats_log_util.h"
#include <limits.h>
#include <stdlib.h>
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_STRING;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::ProtoOutputStream;
using std::map;
using std::string;
using std::unordered_map;
using std::vector;
using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
// for StatsLogReport
const int FIELD_ID_ID = 1;
const int FIELD_ID_EVENT_METRICS = 4;
const int FIELD_ID_IS_ACTIVE = 14;
// for EventMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for EventMetricData
const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1;
const int FIELD_ID_ATOMS = 2;
EventMetricProducer::EventMetricProducer(
const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
const int64_t startTimeNs,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, startTimeNs, conditionIndex, initialConditionCache, wizard,
eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
mc.conditionId = link.condition();
translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
mMetric2ConditionLinks.push_back(mc);
}
mConditionSliced = true;
}
mProto = std::make_unique<ProtoOutputStream>();
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
EventMetricProducer::~EventMetricProducer() {
VLOG("~EventMetricProducer() called");
}
void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
mProto->clear();
StatsdStats::getInstance().noteBucketDropped(mMetricId);
}
void EventMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
const int64_t eventTime) {
}
std::unique_ptr<std::vector<uint8_t>> serializeProtoLocked(ProtoOutputStream& protoOutput) {
size_t bufferSize = protoOutput.size();
std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize));
size_t pos = 0;
sp<android::util::ProtoReader> reader = protoOutput.data();
while (reader->readBuffer() != NULL) {
size_t toRead = reader->currentToRead();
std::memcpy(&((*buffer)[pos]), reader->readBuffer(), toRead);
pos += toRead;
reader->move(toRead);
}
return buffer;
}
void EventMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
mProto->clear();
}
void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mProto->size() <= 0) {
return;
}
size_t bufferSize = mProto->size();
VLOG("metric %lld dump report now... proto size: %zu ",
(long long)mMetricId, bufferSize);
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked(*mProto);
protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS,
reinterpret_cast<char*>(buffer.get()->data()), buffer.get()->size());
if (erase_data) {
mProto->clear();
}
}
void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
const int64_t eventTime) {
VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
}
void EventMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
const map<int, HashableDimensionKey>& statePrimaryKeys) {
if (!condition) {
return;
}
uint64_t wrapperToken =
mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
const int64_t elapsedTimeNs = truncateTimestampIfNecessary(event);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long) elapsedTimeNs);
uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS);
event.ToProto(*mProto);
mProto->end(eventToken);
mProto->end(wrapperToken);
}
size_t EventMetricProducer::byteSizeLocked() const {
return mProto->bytesWritten();
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,83 +0,0 @@
/*
* 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.
*/
#ifndef EVENT_METRIC_PRODUCER_H
#define EVENT_METRIC_PRODUCER_H
#include <unordered_map>
#include <android/util/ProtoOutputStream.h>
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
namespace android {
namespace os {
namespace statsd {
class EventMetricProducer : public MetricProducer {
public:
EventMetricProducer(
const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
const int64_t startTimeNs,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap = {},
const vector<int>& slicedStateAtoms = {},
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~EventMetricProducer();
private:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
void dropDataLocked(const int64_t dropTimeNs) override;
// Internal function to calculate the current used bytes.
size_t byteSizeLocked() const override;
void dumpStatesLocked(FILE* out, bool verbose) const override{};
// Maps to a EventMetricDataWrapper. Storing atom events in ProtoOutputStream
// is more space efficient than storing LogEvent.
std::unique_ptr<android::util::ProtoOutputStream> mProto;
};
} // namespace statsd
} // namespace os
} // namespace android
#endif // EVENT_METRIC_PRODUCER_H

View File

@@ -1,620 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "../guardrail/StatsdStats.h"
#include "GaugeMetricProducer.h"
#include "../stats_log_util.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::map;
using std::string;
using std::unordered_map;
using std::vector;
using std::make_shared;
using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
// for StatsLogReport
const int FIELD_ID_ID = 1;
const int FIELD_ID_GAUGE_METRICS = 8;
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_IS_ACTIVE = 14;
// for GaugeMetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
// for SkippedBuckets
const int FIELD_ID_SKIPPED_START_MILLIS = 3;
const int FIELD_ID_SKIPPED_END_MILLIS = 4;
const int FIELD_ID_SKIPPED_DROP_EVENT = 5;
// for DumpEvent Proto
const int FIELD_ID_BUCKET_DROP_REASON = 1;
const int FIELD_ID_DROP_TIME = 2;
// for GaugeMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
// for GaugeBucketInfo
const int FIELD_ID_ATOM = 3;
const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4;
const int FIELD_ID_BUCKET_NUM = 6;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 7;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8;
GaugeMetricProducer::GaugeMetricProducer(
const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
/*stateGroupMap=*/{}),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
mPullTagId(pullTagId),
mTriggerAtomId(triggerAtomId),
mAtomId(atomId),
mIsPulled(pullTagId != -1),
mMinBucketSizeNs(metric.min_bucket_size_nanos()),
mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
: StatsdStats::kPullMaxDelayNs),
mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
StatsdStats::kAtomDimensionKeySizeLimitMap.end()
? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first
: StatsdStats::kDimensionKeySizeSoftLimit),
mDimensionHardLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
StatsdStats::kAtomDimensionKeySizeLimitMap.end()
? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second
: StatsdStats::kDimensionKeySizeHardLimit),
mGaugeAtomsPerDimensionLimit(metric.max_num_gauge_atoms_per_bucket()),
mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) {
mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
} else {
bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR);
}
mBucketSizeNs = bucketSizeMills * 1000000;
mSamplingType = metric.sampling_type();
if (!metric.gauge_fields_filter().include_all()) {
translateFieldMatcher(metric.gauge_fields_filter().fields(), &mFieldMatchers);
}
if (metric.has_dimensions_in_what()) {
translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
mc.conditionId = link.condition();
translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
mMetric2ConditionLinks.push_back(mc);
}
mConditionSliced = true;
}
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(),
mBucketSizeNs);
}
// Adjust start for partial first bucket and then pull if needed
mCurrentBucketStartTimeNs = startTimeNs;
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
(long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs,
mConditionSliced);
}
GaugeMetricProducer::~GaugeMetricProducer() {
VLOG("~GaugeMetricProducer() called");
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this);
}
}
void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (mCurrentSlicedBucket == nullptr ||
mCurrentSlicedBucket->size() == 0) {
return;
}
fprintf(out, "GaugeMetric %lld dimension size %lu\n", (long long)mMetricId,
(unsigned long)mCurrentSlicedBucket->size());
if (verbose) {
for (const auto& it : *mCurrentSlicedBucket) {
fprintf(out, "\t(what)%s\t(states)%s %d atoms\n",
it.first.getDimensionKeyInWhat().toString().c_str(),
it.first.getStateValuesKey().toString().c_str(), (int)it.second.size());
}
}
}
void GaugeMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
flushIfNeededLocked(dumpTimeNs);
mPastBuckets.clear();
mSkippedBuckets.clear();
}
void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
VLOG("Gauge metric %lld report now...", (long long)mMetricId);
if (include_current_partial_bucket) {
flushLocked(dumpTimeNs);
} else {
flushIfNeededLocked(dumpTimeNs);
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
return;
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
// Fills the dimension path if not slicing by ALL.
if (!mSliceByPositionALL) {
if (!mDimensionsInWhat.empty()) {
uint64_t dimenPathToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
for (const auto& skippedBucket : mSkippedBuckets) {
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS,
(long long)(NanoToMillis(skippedBucket.bucketStartTimeNs)));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS,
(long long)(NanoToMillis(skippedBucket.bucketEndTimeNs)));
for (const auto& dropEvent : skippedBucket.dropEvents) {
uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
FIELD_ID_SKIPPED_DROP_EVENT);
protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME, (long long) (NanoToMillis(dropEvent.dropTimeNs)));
protoOutput->end(dropEventToken);
}
protoOutput->end(wrapperToken);
}
for (const auto& pair : mPastBuckets) {
const MetricDimensionKey& dimensionKey = pair.first;
VLOG("Gauge dimension key %s", dimensionKey.toString().c_str());
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
if (mSliceByPositionALL) {
uint64_t dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
}
// Then fill bucket_info (GaugeBucketInfo).
for (const auto& bucket : pair.second) {
uint64_t bucketInfoToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketStartNs));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
(long long)NanoToMillis(bucket.mBucketEndNs));
} else {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
if (!bucket.mGaugeAtoms.empty()) {
for (const auto& atom : bucket.mGaugeAtoms) {
uint64_t atomsToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
FIELD_ID_ATOM);
writeFieldValueTreeToStream(mAtomId, *(atom.mFields), protoOutput);
protoOutput->end(atomsToken);
}
for (const auto& atom : bucket.mGaugeAtoms) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED |
FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
(long long)atom.mElapsedTimestampNs);
}
}
protoOutput->end(bucketInfoToken);
VLOG("Gauge \t bucket [%lld - %lld] includes %d atoms.",
(long long)bucket.mBucketStartNs, (long long)bucket.mBucketEndNs,
(int)bucket.mGaugeAtoms.size());
}
protoOutput->end(wrapperToken);
}
protoOutput->end(protoToken);
if (erase_data) {
mPastBuckets.clear();
mSkippedBuckets.clear();
}
}
void GaugeMetricProducer::prepareFirstBucketLocked() {
if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
}
}
void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
bool triggerPuller = false;
switch(mSamplingType) {
// When the metric wants to do random sampling and there is already one gauge atom for the
// current bucket, do not do it again.
case GaugeMetric::RANDOM_ONE_SAMPLE: {
triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty();
break;
}
case GaugeMetric::CONDITION_CHANGE_TO_TRUE: {
triggerPuller = mCondition == ConditionState::kTrue;
break;
}
case GaugeMetric::FIRST_N_SAMPLES: {
triggerPuller = mCondition == ConditionState::kTrue;
break;
}
default:
break;
}
if (!triggerPuller) {
return;
}
vector<std::shared_ptr<LogEvent>> allData;
if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
return;
}
const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
if (pullDelayNs > mMaxPullDelayNs) {
ALOGE("Pull finish too late for atom %d", mPullTagId);
StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
return;
}
for (const auto& data : allData) {
LogEvent localCopy = data->makeCopy();
localCopy.setElapsedTimestampNs(timestampNs);
if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
MatchingState::kMatched) {
onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
}
}
}
void GaugeMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
MetricProducer::onActiveStateChangedLocked(eventTimeNs);
if (ConditionState::kTrue != mCondition || !mIsPulled) {
return;
}
if (mTriggerAtomId == -1 || (mIsActive && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE)) {
pullAndMatchEventsLocked(eventTimeNs);
}
}
void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
const int64_t eventTimeNs) {
VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
if (!mIsActive) {
return;
}
flushIfNeededLocked(eventTimeNs);
if (mIsPulled && mTriggerAtomId == -1) {
pullAndMatchEventsLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
const int64_t eventTimeNs) {
VLOG("GaugeMetric %lld onSlicedConditionMayChange overall condition %d", (long long)mMetricId,
overallCondition);
mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse;
if (!mIsActive) {
return;
}
flushIfNeededLocked(eventTimeNs);
// If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
// pull for every dimension.
if (mIsPulled && mTriggerAtomId == -1) {
pullAndMatchEventsLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
std::shared_ptr<vector<FieldValue>> GaugeMetricProducer::getGaugeFields(const LogEvent& event) {
std::shared_ptr<vector<FieldValue>> gaugeFields;
if (mFieldMatchers.size() > 0) {
gaugeFields = std::make_shared<vector<FieldValue>>();
filterGaugeValues(mFieldMatchers, event.getValues(), gaugeFields.get());
} else {
gaugeFields = std::make_shared<vector<FieldValue>>(event.getValues());
}
// Trim all dimension fields from output. Dimensions will appear in output report and will
// benefit from dictionary encoding. For large pulled atoms, this can give the benefit of
// optional repeated field.
for (const auto& field : mDimensionsInWhat) {
for (auto it = gaugeFields->begin(); it != gaugeFields->end();) {
if (it->mField.matches(field)) {
it = gaugeFields->erase(it);
} else {
it++;
}
}
}
return gaugeFields;
}
void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
bool pullSuccess, int64_t originalPullTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
if (!pullSuccess || allData.size() == 0) {
return;
}
const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs;
StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
if (pullDelayNs > mMaxPullDelayNs) {
ALOGE("Pull finish too late for atom %d", mPullTagId);
StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
return;
}
for (const auto& data : allData) {
if (mEventMatcherWizard->matchLogEvent(
*data, mWhatMatcherIndex) == MatchingState::kMatched) {
onMatchedLogEventLocked(mWhatMatcherIndex, *data);
}
}
}
bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
return false;
}
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedBucket->size() > mDimensionSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > mDimensionHardLimit) {
ALOGE("GaugeMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.toString().c_str());
StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
return false;
}
void GaugeMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
const map<int, HashableDimensionKey>& statePrimaryKeys) {
if (condition == false) {
return;
}
int64_t eventTimeNs = event.GetElapsedTimestampNs();
if (eventTimeNs < mCurrentBucketStartTimeNs) {
VLOG("Gauge Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
return;
}
flushIfNeededLocked(eventTimeNs);
if (mTriggerAtomId == event.GetTagId()) {
pullAndMatchEventsLocked(eventTimeNs);
return;
}
// When gauge metric wants to randomly sample the output atom, we just simply use the first
// gauge in the given bucket.
if (mCurrentSlicedBucket->find(eventKey) != mCurrentSlicedBucket->end() &&
mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
return;
}
if (hitGuardRailLocked(eventKey)) {
return;
}
if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) {
return;
}
const int64_t truncatedElapsedTimestampNs = truncateTimestampIfNecessary(event);
GaugeAtom gaugeAtom(getGaugeFields(event), truncatedElapsedTimestampNs);
(*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
// Anomaly detection on gauge metric only works when there is one numeric
// field specified.
if (mAnomalyTrackers.size() > 0) {
if (gaugeAtom.mFields->size() == 1) {
const Value& value = gaugeAtom.mFields->begin()->mValue;
long gaugeVal = 0;
if (value.getType() == INT) {
gaugeVal = (long)value.int_value;
} else if (value.getType() == LONG) {
gaugeVal = value.long_value;
}
for (auto& tracker : mAnomalyTrackers) {
tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId,
eventKey, gaugeVal);
}
}
}
}
void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() {
for (const auto& slice : *mCurrentSlicedBucket) {
if (slice.second.empty()) {
continue;
}
const Value& value = slice.second.front().mFields->front().mValue;
long gaugeVal = 0;
if (value.getType() == INT) {
gaugeVal = (long)value.int_value;
} else if (value.getType() == LONG) {
gaugeVal = value.long_value;
}
(*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal;
}
}
void GaugeMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
flushIfNeededLocked(dropTimeNs);
StatsdStats::getInstance().noteBucketDropped(mMetricId);
mPastBuckets.clear();
}
// When a new matched event comes in, we check if event falls into the current
// bucket. If not, flush the old counter to past buckets and initialize the new
// bucket.
// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
// the GaugeMetricProducer while holding the lock.
void GaugeMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
if (eventTimeNs < currentBucketEndTimeNs) {
VLOG("Gauge eventTime is %lld, less than next bucket start time %lld",
(long long)eventTimeNs, (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
return;
}
// Adjusts the bucket start and end times.
int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
mCurrentBucketNum += numBucketsForward;
VLOG("Gauge metric %lld: new bucket start time: %lld", (long long)mMetricId,
(long long)mCurrentBucketStartTimeNs);
}
void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
GaugeBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = bucketEndTime;
// Add bucket to mPastBuckets if bucket is large enough.
// Otherwise, drop the bucket data and add bucket metadata to mSkippedBuckets.
bool isBucketLargeEnough = info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
if (isBucketLargeEnough) {
for (const auto& slice : *mCurrentSlicedBucket) {
info.mGaugeAtoms = slice.second;
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
VLOG("Gauge gauge metric %lld, dump key value: %s", (long long)mMetricId,
slice.first.toString().c_str());
}
} else {
mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
if (!maxDropEventsReached()) {
mCurrentSkippedBucket.dropEvents.emplace_back(
buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL));
}
mSkippedBuckets.emplace_back(mCurrentSkippedBucket);
}
// If we have anomaly trackers, we need to update the partial bucket values.
if (mAnomalyTrackers.size() > 0) {
updateCurrentSlicedBucketForAnomaly();
if (eventTimeNs > fullBucketEndTimeNs) {
// This is known to be a full bucket, so send this data to the anomaly tracker.
for (auto& tracker : mAnomalyTrackers) {
tracker->addPastBucket(mCurrentSlicedBucketForAnomaly, mCurrentBucketNum);
}
mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
}
}
StatsdStats::getInstance().noteBucketCount(mMetricId);
mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
mCurrentSkippedBucket.reset();
}
size_t GaugeMetricProducer::byteSizeLocked() const {
size_t totalSize = 0;
for (const auto& pair : mPastBuckets) {
for (const auto& bucket : pair.second) {
totalSize += bucket.mGaugeAtoms.size() * sizeof(GaugeAtom);
for (const auto& atom : bucket.mGaugeAtoms) {
if (atom.mFields != nullptr) {
totalSize += atom.mFields->size() * sizeof(FieldValue);
}
}
}
}
return totalSize;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,211 +0,0 @@
/*
* 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 <unordered_map>
#include <android/util/ProtoOutputStream.h>
#include <gtest/gtest_prod.h>
#include "../condition/ConditionTracker.h"
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
#include "../matchers/matcher_util.h"
#include "../matchers/EventMatcherWizard.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "../stats_util.h"
namespace android {
namespace os {
namespace statsd {
struct GaugeAtom {
GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs)
: mFields(fields), mElapsedTimestampNs(elapsedTimeNs) {
}
std::shared_ptr<vector<FieldValue>> mFields;
int64_t mElapsedTimestampNs;
};
struct GaugeBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
std::vector<GaugeAtom> mGaugeAtoms;
};
typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>>
DimToGaugeAtomsMap;
// This gauge metric producer first register the puller to automatically pull the gauge at the
// beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
// proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
// producer always reports the guage at the earliest time of the bucket when the condition is met.
class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
GaugeMetricProducer(
const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache,
const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap = {});
virtual ~GaugeMetricProducer();
// Handles when the pulled data arrives.
void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data,
bool pullSuccess, int64_t originalPullTimeNs) override;
// GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
void notifyAppUpgrade(const int64_t& eventTimeNs) override {
std::lock_guard<std::mutex> lock(mMutex);
if (!mSplitBucketForAppUpgrade) {
return;
}
flushLocked(eventTimeNs);
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
pullAndMatchEventsLocked(eventTimeNs);
}
};
// GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
std::lock_guard<std::mutex> lock(mMutex);
flushLocked(eventTimeNs);
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
pullAndMatchEventsLocked(eventTimeNs);
}
};
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
// Internal interface to handle active state change.
void onActiveStateChangedLocked(const int64_t& eventTimeNs) override;
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
// Internal function to calculate the current used bytes.
size_t byteSizeLocked() const override;
void dumpStatesLocked(FILE* out, bool verbose) const override;
void dropDataLocked(const int64_t dropTimeNs) override;
// Util function to flush the old packet.
void flushIfNeededLocked(const int64_t& eventTime) override;
void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) override;
void prepareFirstBucketLocked() override;
void pullAndMatchEventsLocked(const int64_t timestampNs);
const int mWhatMatcherIndex;
sp<EventMatcherWizard> mEventMatcherWizard;
sp<StatsPullerManager> mPullerManager;
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
// tagId for atoms that trigger the pulling, if any
const int mTriggerAtomId;
// tagId for output atom
const int mAtomId;
// if this is pulled metric
const bool mIsPulled;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
// The current partial bucket.
std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket;
// The current full bucket for anomaly detection. This is updated to the latest value seen for
// this slice (ie, for partial buckets, we use the last partial bucket in this full bucket).
std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly;
const int64_t mMinBucketSizeNs;
// Translate Atom based bucket to single numeric value bucket for anomaly and updates the map
// for each slice with the latest value.
void updateCurrentSlicedBucketForAnomaly();
// Whitelist of fields to report. Empty means all are reported.
std::vector<Matcher> mFieldMatchers;
GaugeMetric::SamplingType mSamplingType;
const int64_t mMaxPullDelayNs;
// apply a whitelist on the original input
std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event);
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(GaugeBucket{});
const size_t mDimensionSoftLimit;
const size_t mDimensionHardLimit;
const size_t mGaugeAtomsPerDimensionLimit;
const bool mSplitBucketForAppUpgrade;
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger);
FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput);
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
};
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,321 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "MetricProducer.h"
#include "../guardrail/StatsdStats.h"
#include "state/StateTracker.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_ENUM;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::ProtoOutputStream;
namespace android {
namespace os {
namespace statsd {
// for ActiveMetric
const int FIELD_ID_ACTIVE_METRIC_ID = 1;
const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2;
// for ActiveEventActivation
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
MetricProducer::MetricProducer(
const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
const int conditionIndex, const vector<ConditionState>& initialConditionCache,
const sp<ConditionWizard>& wizard,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: mMetricId(metricId),
mConfigKey(key),
mTimeBaseNs(timeBaseNs),
mCurrentBucketStartTimeNs(timeBaseNs),
mCurrentBucketNum(0),
mCondition(initialCondition(conditionIndex, initialConditionCache)),
mConditionTrackerIndex(conditionIndex),
mConditionSliced(false),
mWizard(wizard),
mContainANYPositionInDimensionsInWhat(false),
mSliceByPositionALL(false),
mHasLinksToAllConditionDimensionsInTracker(false),
mEventActivationMap(eventActivationMap),
mEventDeactivationMap(eventDeactivationMap),
mIsActive(mEventActivationMap.empty()),
mSlicedStateAtoms(slicedStateAtoms),
mStateGroupMap(stateGroupMap) {
}
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
if (!mIsActive) {
return;
}
int64_t eventTimeNs = event.GetElapsedTimestampNs();
// this is old event, maybe statsd restarted?
if (eventTimeNs < mTimeBaseNs) {
return;
}
bool condition;
ConditionKey conditionKey;
if (mConditionSliced) {
for (const auto& link : mMetric2ConditionLinks) {
getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
}
auto conditionState =
mWizard->query(mConditionTrackerIndex, conditionKey,
!mHasLinksToAllConditionDimensionsInTracker);
condition = (conditionState == ConditionState::kTrue);
} else {
// TODO: The unknown condition state is not handled here, we should fix it.
condition = mCondition == ConditionState::kTrue;
}
// Stores atom id to primary key pairs for each state atom that the metric is
// sliced by.
std::map<int32_t, HashableDimensionKey> statePrimaryKeys;
// For states with primary fields, use MetricStateLinks to get the primary
// field values from the log event. These values will form a primary key
// that will be used to query StateTracker for the correct state value.
for (const auto& stateLink : mMetric2StateLinks) {
getDimensionForState(event.getValues(), stateLink,
&statePrimaryKeys[stateLink.stateAtomId]);
}
// For each sliced state, query StateTracker for the state value using
// either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
//
// Expected functionality: for any case where the MetricStateLinks are
// initialized incorrectly (ex. # of state links != # of primary fields, no
// links are provided for a state with primary fields, links are provided
// in the wrong order, etc.), StateTracker will simply return kStateUnknown
// when queried using an incorrect key.
HashableDimensionKey stateValuesKey;
for (auto atomId : mSlicedStateAtoms) {
FieldValue value;
if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
// found a primary key for this state, query using the key
queryStateValue(atomId, statePrimaryKeys[atomId], &value);
} else {
// if no MetricStateLinks exist for this state atom,
// query using the default dimension key (empty HashableDimensionKey)
queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
}
mapStateValue(atomId, &value);
stateValuesKey.addValue(value);
}
HashableDimensionKey dimensionInWhat;
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
statePrimaryKeys);
}
bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
bool isActive = mEventActivationMap.empty();
for (auto& it : mEventActivationMap) {
if (it.second->state == ActivationState::kActive &&
elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) {
it.second->state = ActivationState::kNotActive;
}
if (it.second->state == ActivationState::kActive) {
isActive = true;
}
}
return isActive;
}
void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
std::lock_guard<std::mutex> lock(mMutex);
if (!mIsActive) {
return;
}
mIsActive = evaluateActiveStateLocked(elapsedTimestampNs);
if (!mIsActive) {
onActiveStateChangedLocked(elapsedTimestampNs);
}
}
void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
auto it = mEventActivationMap.find(activationTrackerIndex);
if (it == mEventActivationMap.end()) {
return;
}
auto& activation = it->second;
if (ACTIVATE_ON_BOOT == activation->activationType) {
if (ActivationState::kNotActive == activation->state) {
activation->state = ActivationState::kActiveOnBoot;
}
// If the Activation is already active or set to kActiveOnBoot, do nothing.
return;
}
activation->start_ns = elapsedTimestampNs;
activation->state = ActivationState::kActive;
bool oldActiveState = mIsActive;
mIsActive = true;
if (!oldActiveState) { // Metric went from not active to active.
onActiveStateChangedLocked(elapsedTimestampNs);
}
}
void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
auto it = mEventDeactivationMap.find(deactivationTrackerIndex);
if (it == mEventDeactivationMap.end()) {
return;
}
for (auto activationToCancelIt : it->second) {
activationToCancelIt->state = ActivationState::kNotActive;
}
}
void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
int64_t currentTimeNs) {
if (mEventActivationMap.size() == 0) {
return;
}
for (int i = 0; i < activeMetric.activation_size(); i++) {
const auto& activeEventActivation = activeMetric.activation(i);
auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index());
if (it == mEventActivationMap.end()) {
ALOGE("Saved event activation not found");
continue;
}
auto& activation = it->second;
// If the event activation does not have a state, assume it is active.
if (!activeEventActivation.has_state() ||
activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
// We don't want to change the ttl for future activations, so we set the start_ns
// such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
activation->start_ns =
currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
activation->state = ActivationState::kActive;
mIsActive = true;
} else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
activation->state = ActivationState::kActiveOnBoot;
}
}
}
void MetricProducer::writeActiveMetricToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
for (auto& it : mEventActivationMap) {
const int atom_matcher_index = it.first;
const std::shared_ptr<Activation>& activation = it.second;
if (ActivationState::kNotActive == activation->state ||
(ActivationState::kActive == activation->state &&
activation->start_ns + activation->ttl_ns < currentTimeNs)) {
continue;
}
const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
FIELD_ID_ACTIVE_METRIC_ACTIVATION);
proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX,
atom_matcher_index);
if (ActivationState::kActive == activation->state) {
const int64_t remainingTtlNs =
activation->start_ns + activation->ttl_ns - currentTimeNs;
proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
(long long)remainingTtlNs);
proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
ActiveEventActivation::ACTIVE);
} else if (ActivationState::kActiveOnBoot == activation->state) {
if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
proto->write(
FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
(long long)activation->ttl_ns);
proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
ActiveEventActivation::ACTIVE);
} else if (reason == STATSCOMPANION_DIED) {
// We are saving because of system server death, not due to a device shutdown.
// Next time we load, we do not want to activate metrics that activate on boot.
proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
ActiveEventActivation::ACTIVATE_ON_BOOT);
}
}
proto->end(activationToken);
}
}
void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
FieldValue* value) {
if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
value->mValue = Value(StateTracker::kStateUnknown);
value->mField.setTag(atomId);
ALOGW("StateTracker not found for state atom %d", atomId);
return;
}
}
void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) {
// check if there is a state map for this atom
auto atomIt = mStateGroupMap.find(atomId);
if (atomIt == mStateGroupMap.end()) {
return;
}
auto valueIt = atomIt->second.find(value->mValue.int_value);
if (valueIt == atomIt->second.end()) {
// state map exists, but value was not put in a state group
// so set mValue to kStateUnknown
// TODO(tsaichristine): handle incomplete state maps
value->mValue.setInt(StateTracker::kStateUnknown);
} else {
// set mValue to group_id
value->mValue.setLong(valueIt->second);
}
}
HashableDimensionKey MetricProducer::getUnknownStateKey() {
HashableDimensionKey stateKey;
for (auto atom : mSlicedStateAtoms) {
FieldValue fieldValue;
fieldValue.mField.setTag(atom);
fieldValue.mValue.setInt(StateTracker::kStateUnknown);
stateKey.addValue(fieldValue);
}
return stateKey;
}
DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason) {
DropEvent event;
event.reason = reason;
event.dropTimeNs = dropTimeNs;
return event;
}
bool MetricProducer::maxDropEventsReached() {
return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -1,508 +0,0 @@
/*
* 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.
*/
#ifndef METRIC_PRODUCER_H
#define METRIC_PRODUCER_H
#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
#include <utils/RefBase.h>
#include <unordered_map>
#include "HashableDimensionKey.h"
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
#include "config/ConfigKey.h"
#include "matchers/matcher_util.h"
#include "packages/PackageInfoListener.h"
#include "state/StateListener.h"
#include "state/StateManager.h"
namespace android {
namespace os {
namespace statsd {
// Keep this in sync with DumpReportReason enum in stats_log.proto
enum DumpReportReason {
DEVICE_SHUTDOWN = 1,
CONFIG_UPDATED = 2,
CONFIG_REMOVED = 3,
GET_DATA_CALLED = 4,
ADB_DUMP = 5,
CONFIG_RESET = 6,
STATSCOMPANION_DIED = 7,
TERMINATION_SIGNAL_RECEIVED = 8
};
// If the metric has no activation requirement, it will be active once the metric producer is
// created.
// If the metric needs to be activated by atoms, the metric producer will start
// with kNotActive state, turn to kActive or kActiveOnBoot when the activation event arrives, become
// kNotActive when it reaches the duration limit (timebomb). If the activation event arrives again
// before or after it expires, the event producer will be re-activated and ttl will be reset.
enum ActivationState {
kNotActive = 0,
kActive = 1,
kActiveOnBoot = 2,
};
enum DumpLatency {
// In some cases, we only have a short time range to do the dump, e.g. statsd is being killed.
// We might be able to return all the data in this mode. For instance, pull metrics might need
// to be pulled when the current bucket is requested.
FAST = 1,
// In other cases, it is fine for a dump to take more than a few milliseconds, e.g. config
// updates.
NO_TIME_CONSTRAINTS = 2
};
// Keep this in sync with BucketDropReason enum in stats_log.proto
enum BucketDropReason {
// For ValueMetric, a bucket is dropped during a dump report request iff
// current bucket should be included, a pull is needed (pulled metric and
// condition is true), and we are under fast time constraints.
DUMP_REPORT_REQUESTED = 1,
EVENT_IN_WRONG_BUCKET = 2,
CONDITION_UNKNOWN = 3,
PULL_FAILED = 4,
PULL_DELAYED = 5,
DIMENSION_GUARDRAIL_REACHED = 6,
MULTIPLE_BUCKETS_SKIPPED = 7,
// Not an invalid bucket case, but the bucket is dropped.
BUCKET_TOO_SMALL = 8,
// Not an invalid bucket case, but the bucket is skipped.
NO_DATA = 9
};
struct Activation {
Activation(const ActivationType& activationType, const int64_t ttlNs)
: ttl_ns(ttlNs),
start_ns(0),
state(ActivationState::kNotActive),
activationType(activationType) {}
const int64_t ttl_ns;
int64_t start_ns;
ActivationState state;
const ActivationType activationType;
};
struct DropEvent {
// Reason for dropping the bucket and/or marking the bucket invalid.
BucketDropReason reason;
// The timestamp of the drop event.
int64_t dropTimeNs;
};
struct SkippedBucket {
// Start time of the dropped bucket.
int64_t bucketStartTimeNs;
// End time of the dropped bucket.
int64_t bucketEndTimeNs;
// List of events that invalidated this bucket.
std::vector<DropEvent> dropEvents;
void reset() {
bucketStartTimeNs = 0;
bucketEndTimeNs = 0;
dropEvents.clear();
}
};
// A MetricProducer is responsible for compute one single metrics, creating stats log report, and
// writing the report to dropbox. MetricProducers should respond to package changes as required in
// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
// be a no-op.
class MetricProducer : public virtual android::RefBase, public virtual StateListener {
public:
MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
const int conditionIndex, const vector<ConditionState>& initialConditionCache,
const sp<ConditionWizard>& wizard,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap);
virtual ~MetricProducer(){};
ConditionState initialCondition(const int conditionIndex,
const vector<ConditionState>& initialConditionCache) const {
return conditionIndex >= 0 ? initialConditionCache[conditionIndex] : ConditionState::kTrue;
}
/**
* Force a partial bucket split on app upgrade
*/
virtual void notifyAppUpgrade(const int64_t& eventTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
flushLocked(eventTimeNs);
};
void notifyAppRemoved(const int64_t& eventTimeNs) {
// Force buckets to split on removal also.
notifyAppUpgrade(eventTimeNs);
};
/**
* Force a partial bucket split on boot complete.
*/
virtual void onStatsdInitCompleted(const int64_t& eventTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
flushLocked(eventTimeNs);
}
// Consume the parsed stats log entry that already matched the "what" of the metric.
void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
onMatchedLogEventLocked(matcherIndex, event);
}
void onConditionChanged(const bool condition, const int64_t eventTime) {
std::lock_guard<std::mutex> lock(mMutex);
onConditionChangedLocked(condition, eventTime);
}
void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) {
std::lock_guard<std::mutex> lock(mMutex);
onSlicedConditionMayChangeLocked(overallCondition, eventTime);
}
bool isConditionSliced() const {
std::lock_guard<std::mutex> lock(mMutex);
return mConditionSliced;
};
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
const HashableDimensionKey& primaryKey, const FieldValue& oldState,
const FieldValue& newState){};
// Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
// This method clears all the past buckets.
void onDumpReport(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) {
std::lock_guard<std::mutex> lock(mMutex);
return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, erase_data,
dumpLatency, str_set, protoOutput);
}
void clearPastBuckets(const int64_t dumpTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
return clearPastBucketsLocked(dumpTimeNs);
}
void prepareFirstBucket() {
std::lock_guard<std::mutex> lock(mMutex);
prepareFirstBucketLocked();
}
// Returns the memory in bytes currently used to store this metric's data. Does not change
// state.
size_t byteSize() const {
std::lock_guard<std::mutex> lock(mMutex);
return byteSizeLocked();
}
void dumpStates(FILE* out, bool verbose) const {
std::lock_guard<std::mutex> lock(mMutex);
dumpStatesLocked(out, verbose);
}
// Let MetricProducer drop in-memory data to save memory.
// We still need to keep future data valid and anomaly tracking work, which means we will
// have to flush old data, informing anomaly trackers then safely drop old data.
// We still keep current bucket data for future metrics' validity.
void dropData(const int64_t dropTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
dropDataLocked(dropTimeNs);
}
void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
loadActiveMetricLocked(activeMetric, currentTimeNs);
}
void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) {
std::lock_guard<std::mutex> lock(mMutex);
activateLocked(activationTrackerIndex, elapsedTimestampNs);
}
void cancelEventActivation(int deactivationTrackerIndex) {
std::lock_guard<std::mutex> lock(mMutex);
cancelEventActivationLocked(deactivationTrackerIndex);
}
bool isActive() const {
std::lock_guard<std::mutex> lock(mMutex);
return isActiveLocked();
}
void flushIfExpire(int64_t elapsedTimestampNs);
void writeActiveMetricToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
// Start: getters/setters
inline const int64_t& getMetricId() const {
return mMetricId;
}
// For test only.
inline int64_t getCurrentBucketNum() const {
return mCurrentBucketNum;
}
int64_t getBucketSizeInNs() const {
std::lock_guard<std::mutex> lock(mMutex);
return mBucketSizeNs;
}
inline const std::vector<int> getSlicedStateAtoms() {
std::lock_guard<std::mutex> lock(mMutex);
return mSlicedStateAtoms;
}
/* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
const sp<AlarmMonitor>& anomalyAlarmMonitor) {
std::lock_guard<std::mutex> lock(mMutex);
sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
if (anomalyTracker != nullptr) {
mAnomalyTrackers.push_back(anomalyTracker);
}
return anomalyTracker;
}
// End: getters/setters
protected:
/**
* Flushes the current bucket if the eventTime is after the current bucket's end time.
*/
virtual void flushIfNeededLocked(const int64_t& eventTime){};
/**
* For metrics that aggregate (ie, every metric producer except for EventMetricProducer),
* we need to be able to flush the current buckets on demand (ie, end the current bucket and
* start new bucket). If this function is called when eventTimeNs is greater than the current
* bucket's end timestamp, than we flush up to the end of the latest full bucket; otherwise,
* we assume that we want to flush a partial bucket. The bucket start timestamp and bucket
* number are not changed by this function. This method should only be called by
* flushIfNeededLocked or flushLocked or the app upgrade handler; the caller MUST update the
* bucket timestamp and bucket number as needed.
*/
virtual void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {};
/**
* Flushes all the data including the current partial bucket.
*/
virtual void flushLocked(const int64_t& eventTimeNs) {
flushIfNeededLocked(eventTimeNs);
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
};
/*
* Individual metrics can implement their own business logic here. All pre-processing is done.
*
* [matcherIndex]: the index of the matcher which matched this event. This is interesting to
* DurationMetric, because it has start/stop/stop_all 3 matchers.
* [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
* dimensions, it will be DEFAULT_DIMENSION_KEY
* [conditionKey]: the keys of conditions which should be used to query the condition for this
* target event (from MetricConditionLink). This is passed to individual metrics
* because DurationMetric needs it to be cached.
* [condition]: whether condition is met. If condition is sliced, this is the result coming from
* query with ConditionWizard; If condition is not sliced, this is the
* nonSlicedCondition.
* [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
*/
virtual void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition, const LogEvent& event,
const map<int, HashableDimensionKey>& statePrimaryKeys) = 0;
// Consume the parsed stats log entry that already matched the "what" of the metric.
virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
const int64_t eventTime) = 0;
virtual void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
android::util::ProtoOutputStream* protoOutput) = 0;
virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
virtual void prepareFirstBucketLocked(){};
virtual size_t byteSizeLocked() const = 0;
virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
void cancelEventActivationLocked(int deactivationTrackerIndex);
bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
virtual void onActiveStateChangedLocked(const int64_t& eventTimeNs) {
if (!mIsActive) {
flushLocked(eventTimeNs);
}
}
inline bool isActiveLocked() const {
return mIsActive;
}
// Convenience to compute the current bucket's end time, which is always aligned with the
// start time of the metric.
int64_t getCurrentBucketEndTimeNs() const {
return mTimeBaseNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
}
int64_t getBucketNumFromEndTimeNs(const int64_t endNs) {
return (endNs - mTimeBaseNs) / mBucketSizeNs - 1;
}
// Query StateManager for original state value using the queryKey.
// The field and value are output.
void queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
FieldValue* value);
// If a state map exists for the given atom, replace the original state
// value with the group id mapped to the value.
// If no state map exists, keep the original state value.
void mapStateValue(const int32_t atomId, FieldValue* value);
// Returns a HashableDimensionKey with unknown state value for each state
// atom.
HashableDimensionKey getUnknownStateKey();
DropEvent buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason);
// Returns true if the number of drop events in the current bucket has
// exceeded the maximum number allowed, which is currently capped at 10.
bool maxDropEventsReached();
const int64_t mMetricId;
const ConfigKey mConfigKey;
// The time when this metric producer was first created. The end time for the current bucket
// can be computed from this based on mCurrentBucketNum.
int64_t mTimeBaseNs;
// Start time may not be aligned with the start of statsd if there is an app upgrade in the
// middle of a bucket.
int64_t mCurrentBucketStartTimeNs;
// Used by anomaly detector to track which bucket we are in. This is not sent with the produced
// report.
int64_t mCurrentBucketNum;
int64_t mBucketSizeNs;
ConditionState mCondition;
int mConditionTrackerIndex;
bool mConditionSliced;
sp<ConditionWizard> mWizard;
bool mContainANYPositionInDimensionsInWhat;
bool mSliceByPositionALL;
vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
// 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;
mutable std::mutex mMutex;
// When the metric producer has multiple activations, these activations are ORed to determine
// whether the metric producer is ready to generate metrics.
std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap;
// Maps index of atom matcher for deactivation to a list of Activation structs.
std::unordered_map<int, std::vector<std::shared_ptr<Activation>>> mEventDeactivationMap;
bool mIsActive;
// The slice_by_state atom ids defined in statsd_config.
const std::vector<int32_t> mSlicedStateAtoms;
// Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
const std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
// MetricStateLinks defined in statsd_config that link fields in the state
// atom to fields in the "what" atom.
std::vector<Metric2State> mMetric2StateLinks;
SkippedBucket mCurrentSkippedBucket;
// Buckets that were invalidated and had their data dropped.
std::vector<SkippedBucket> mSkippedBuckets;
FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
FRIEND_TEST(StatsLogProcessorTest,
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
FRIEND_TEST(MetricsManagerTest, TestInitialConditions);
};
} // namespace statsd
} // namespace os
} // namespace android
#endif // METRIC_PRODUCER_H

View File

@@ -1,695 +0,0 @@
/*
* 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "MetricsManager.h"
#include <private/android_filesystem_config.h>
#include "CountMetricProducer.h"
#include "condition/CombinationConditionTracker.h"
#include "condition/SimpleConditionTracker.h"
#include "guardrail/StatsdStats.h"
#include "matchers/CombinationLogMatchingTracker.h"
#include "matchers/SimpleLogMatchingTracker.h"
#include "metrics_manager_util.h"
#include "state/StateManager.h"
#include "stats_log_util.h"
#include "stats_util.h"
#include "statslog_statsd.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::set;
using std::string;
using std::vector;
namespace android {
namespace os {
namespace statsd {
const int FIELD_ID_METRICS = 1;
const int FIELD_ID_ANNOTATIONS = 7;
const int FIELD_ID_ANNOTATIONS_INT64 = 1;
const int FIELD_ID_ANNOTATIONS_INT32 = 2;
// for ActiveConfig
const int FIELD_ID_ACTIVE_CONFIG_ID = 1;
const int FIELD_ID_ACTIVE_CONFIG_UID = 2;
const int FIELD_ID_ACTIVE_CONFIG_METRIC = 3;
MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
const int64_t timeBaseNs, const int64_t currentTimeNs,
const sp<UidMap>& uidMap,
const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor)
: mConfigKey(key),
mUidMap(uidMap),
mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
mTtlEndNs(-1),
mLastReportTimeNs(currentTimeNs),
mLastReportWallClockNs(getWallClockNs()),
mPullerManager(pullerManager),
mWhitelistedAtomIds(config.whitelisted_atom_ids().begin(),
config.whitelisted_atom_ids().end()),
mShouldPersistHistory(config.persist_locally()) {
// Init the ttl end timestamp.
refreshTtl(timeBaseNs);
mConfigValid = initStatsdConfig(
key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds);
mHashStringsInReport = config.hash_strings_in_metric_report();
mVersionStringsInReport = config.version_strings_in_metric_report();
mInstallerInReport = config.installer_in_metric_report();
// Init allowed pushed atom uids.
if (config.allowed_log_source_size() == 0) {
mConfigValid = false;
ALOGE("Log source whitelist is empty! This config won't get any data. Suggest adding at "
"least AID_SYSTEM and AID_STATSD to the allowed_log_source field.");
} else {
for (const auto& source : config.allowed_log_source()) {
auto it = UidMap::sAidToUidMapping.find(source);
if (it != UidMap::sAidToUidMapping.end()) {
mAllowedUid.push_back(it->second);
} else {
mAllowedPkg.push_back(source);
}
}
if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) {
ALOGE("Too many log sources. This is likely to be an error in the config.");
mConfigValid = false;
} else {
initLogSourceWhiteList();
}
}
// Init default allowed pull atom uids.
int numPullPackages = 0;
for (const string& pullSource : config.default_pull_packages()) {
auto it = UidMap::sAidToUidMapping.find(pullSource);
if (it != UidMap::sAidToUidMapping.end()) {
numPullPackages++;
mDefaultPullUids.insert(it->second);
} else {
ALOGE("Default pull atom packages must be in sAidToUidMapping");
mConfigValid = false;
}
}
// Init per-atom pull atom packages.
for (const PullAtomPackages& pullAtomPackages : config.pull_atom_packages()) {
int32_t atomId = pullAtomPackages.atom_id();
for (const string& pullPackage : pullAtomPackages.packages()) {
numPullPackages++;
auto it = UidMap::sAidToUidMapping.find(pullPackage);
if (it != UidMap::sAidToUidMapping.end()) {
mPullAtomUids[atomId].insert(it->second);
} else {
mPullAtomPackages[atomId].insert(pullPackage);
}
}
}
if (numPullPackages > StatsdStats::kMaxPullAtomPackages) {
ALOGE("Too many sources in default_pull_packages and pull_atom_packages. This is likely to "
"be an error in the config");
mConfigValid = false;
} else {
initPullAtomSources();
}
mPullerManager->RegisterPullUidProvider(mConfigKey, this);
// Store the sub-configs used.
for (const auto& annotation : config.annotation()) {
mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
}
// Guardrail. Reject the config if it's too big.
if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
mAllAtomMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
ALOGE("This config is too big! Reject!");
mConfigValid = false;
}
if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) {
ALOGE("This config has too many alerts! Reject!");
mConfigValid = false;
}
mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) ||
(mAllMetricProducers.size() == 0);
bool isActive = mIsAlwaysActive;
for (int metric : mMetricIndexesWithActivation) {
isActive |= mAllMetricProducers[metric]->isActive();
}
mIsActive = isActive;
VLOG("mIsActive is initialized to %d", mIsActive)
// no matter whether this config is valid, log it in the stats.
StatsdStats::getInstance().noteConfigReceived(
key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(),
mAllAnomalyTrackers.size(), mAnnotations, mConfigValid);
// Check active
for (const auto& metric : mAllMetricProducers) {
if (metric->isActive()) {
mIsActive = true;
break;
}
}
}
MetricsManager::~MetricsManager() {
for (auto it : mAllMetricProducers) {
for (int atomId : it->getSlicedStateAtoms()) {
StateManager::getInstance().unregisterListener(atomId, it);
}
}
mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
VLOG("~MetricsManager()");
}
void MetricsManager::initLogSourceWhiteList() {
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
mAllowedLogSources.clear();
mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
for (const auto& pkg : mAllowedPkg) {
auto uids = mUidMap->getAppUid(pkg);
mAllowedLogSources.insert(uids.begin(), uids.end());
}
if (DEBUG) {
for (const auto& uid : mAllowedLogSources) {
VLOG("Allowed uid %d", uid);
}
}
}
void MetricsManager::initPullAtomSources() {
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
mCombinedPullAtomUids.clear();
for (const auto& [atomId, uids] : mPullAtomUids) {
mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
}
for (const auto& [atomId, packages] : mPullAtomPackages) {
for (const string& pkg : packages) {
set<int32_t> uids = mUidMap->getAppUid(pkg);
mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
}
}
}
bool MetricsManager::isConfigValid() const {
return mConfigValid;
}
void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
const int64_t version) {
// Inform all metric producers.
for (const auto& it : mAllMetricProducers) {
it->notifyAppUpgrade(eventTimeNs);
}
// check if we care this package
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
// We will re-initialize the whole list because we don't want to keep the multi mapping of
// UID<->pkg inside MetricsManager to reduce the memory usage.
initLogSourceWhiteList();
}
for (const auto& it : mPullAtomPackages) {
if (it.second.find(apk) != it.second.end()) {
initPullAtomSources();
return;
}
}
}
void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
const int uid) {
// Inform all metric producers.
for (const auto& it : mAllMetricProducers) {
it->notifyAppRemoved(eventTimeNs);
}
// check if we care this package
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
// We will re-initialize the whole list because we don't want to keep the multi mapping of
// UID<->pkg inside MetricsManager to reduce the memory usage.
initLogSourceWhiteList();
}
for (const auto& it : mPullAtomPackages) {
if (it.second.find(apk) != it.second.end()) {
initPullAtomSources();
return;
}
}
}
void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
// Purposefully don't inform metric producers on a new snapshot
// because we don't need to flush partial buckets.
// This occurs if a new user is added/removed or statsd crashes.
initPullAtomSources();
if (mAllowedPkg.size() == 0) {
return;
}
initLogSourceWhiteList();
}
void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) {
// Inform all metric producers.
for (const auto& it : mAllMetricProducers) {
it->onStatsdInitCompleted(eventTimeNs);
}
}
void MetricsManager::init() {
for (const auto& producer : mAllMetricProducers) {
producer->prepareFirstBucket();
}
}
vector<int32_t> MetricsManager::getPullAtomUids(int32_t atomId) {
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
vector<int32_t> uids;
const auto& it = mCombinedPullAtomUids.find(atomId);
if (it != mCombinedPullAtomUids.end()) {
uids.insert(uids.end(), it->second.begin(), it->second.end());
}
uids.insert(uids.end(), mDefaultPullUids.begin(), mDefaultPullUids.end());
return uids;
}
void MetricsManager::dumpStates(FILE* out, bool verbose) {
fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
{
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
for (const auto& source : mAllowedLogSources) {
fprintf(out, "%d ", source);
}
}
fprintf(out, "\n");
for (const auto& producer : mAllMetricProducers) {
producer->dumpStates(out, verbose);
}
}
void MetricsManager::dropData(const int64_t dropTimeNs) {
for (const auto& producer : mAllMetricProducers) {
producer->dropData(dropTimeNs);
}
}
void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
const bool include_current_partial_bucket,
const bool erase_data,
const DumpLatency dumpLatency,
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
VLOG("=========================Metric Reports Start==========================");
// one StatsLogReport per MetricProduer
for (const auto& producer : mAllMetricProducers) {
if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
uint64_t token = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
if (mHashStringsInReport) {
producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
dumpLatency, str_set, protoOutput);
} else {
producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
dumpLatency, nullptr, protoOutput);
}
protoOutput->end(token);
} else {
producer->clearPastBuckets(dumpTimeStampNs);
}
}
for (const auto& annotation : mAnnotations) {
uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
FIELD_ID_ANNOTATIONS);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ANNOTATIONS_INT64,
(long long)annotation.first);
protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ANNOTATIONS_INT32, annotation.second);
protoOutput->end(token);
}
// Do not update the timestamps when data is not cleared to avoid timestamps from being
// misaligned.
if (erase_data) {
mLastReportTimeNs = dumpTimeStampNs;
mLastReportWallClockNs = getWallClockNs();
}
VLOG("=========================Metric Reports End==========================");
}
bool MetricsManager::checkLogCredentials(const LogEvent& event) {
if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) {
return true;
}
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
VLOG("log source %d not on the whitelist", event.GetUid());
return false;
}
return true;
}
bool MetricsManager::eventSanityCheck(const LogEvent& event) {
if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) {
// Check that app breadcrumb reported fields are valid.
status_t err = NO_ERROR;
// Uid is 3rd from last field and must match the caller's uid,
// unless that caller is statsd itself (statsd is allowed to spoof uids).
long appHookUid = event.GetLong(event.size()-2, &err);
if (err != NO_ERROR) {
VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid");
return false;
}
// Because the uid within the LogEvent may have been mapped from
// isolated to host, map the loggerUid similarly before comparing.
int32_t loggerUid = mUidMap->getHostUidOrSelf(event.GetUid());
if (loggerUid != appHookUid && loggerUid != AID_STATSD) {
VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d",
appHookUid, loggerUid);
return false;
}
// The state must be from 0,3. This part of code must be manually updated.
long appHookState = event.GetLong(event.size(), &err);
if (err != NO_ERROR) {
VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field");
return false;
} else if (appHookState < 0 || appHookState > 3) {
VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
return false;
}
} else if (event.GetTagId() == util::DAVEY_OCCURRED) {
// Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
// Check that the davey duration is reasonable. Max length check is for privacy.
status_t err = NO_ERROR;
// Uid is the first field provided.
long jankUid = event.GetLong(1, &err);
if (err != NO_ERROR) {
VLOG("Davey occurred had error when parsing the uid");
return false;
}
int32_t loggerUid = event.GetUid();
if (loggerUid != jankUid && loggerUid != AID_STATSD) {
VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid,
loggerUid);
return false;
}
long duration = event.GetLong(event.size(), &err);
if (err != NO_ERROR) {
VLOG("Davey occurred had error when parsing the duration");
return false;
} else if (duration > 100000) {
VLOG("Davey duration is unreasonably long: %ld", duration);
return false;
}
}
return true;
}
// Consume the stats log if it's interesting to this metric.
void MetricsManager::onLogEvent(const LogEvent& event) {
if (!mConfigValid) {
return;
}
if (!checkLogCredentials(event)) {
return;
}
if (!eventSanityCheck(event)) {
return;
}
int tagId = event.GetTagId();
int64_t eventTimeNs = event.GetElapsedTimestampNs();
bool isActive = mIsAlwaysActive;
// Set of metrics that are still active after flushing.
unordered_set<int> activeMetricsIndices;
// Update state of all metrics w/ activation conditions as of eventTimeNs.
for (int metricIndex : mMetricIndexesWithActivation) {
const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex];
metric->flushIfExpire(eventTimeNs);
if (metric->isActive()) {
// If this metric w/ activation condition is still active after
// flushing, remember it.
activeMetricsIndices.insert(metricIndex);
}
}
mIsActive = isActive || !activeMetricsIndices.empty();
if (mTagIds.find(tagId) == mTagIds.end()) {
// Not interesting...
return;
}
vector<MatchingState> matcherCache(mAllAtomMatchers.size(), MatchingState::kNotComputed);
// Evaluate all atom matchers.
for (auto& matcher : mAllAtomMatchers) {
matcher->onLogEvent(event, mAllAtomMatchers, matcherCache);
}
// Set of metrics that received an activation cancellation.
unordered_set<int> metricIndicesWithCanceledActivations;
// Determine which metric activations received a cancellation and cancel them.
for (const auto& it : mDeactivationAtomTrackerToMetricMap) {
if (matcherCache[it.first] == MatchingState::kMatched) {
for (int metricIndex : it.second) {
mAllMetricProducers[metricIndex]->cancelEventActivation(it.first);
metricIndicesWithCanceledActivations.insert(metricIndex);
}
}
}
// Determine whether any metrics are no longer active after cancelling metric activations.
for (const int metricIndex : metricIndicesWithCanceledActivations) {
const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex];
metric->flushIfExpire(eventTimeNs);
if (!metric->isActive()) {
activeMetricsIndices.erase(metricIndex);
}
}
isActive |= !activeMetricsIndices.empty();
// Determine which metric activations should be turned on and turn them on
for (const auto& it : mActivationAtomTrackerToMetricMap) {
if (matcherCache[it.first] == MatchingState::kMatched) {
for (int metricIndex : it.second) {
mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs);
isActive |= mAllMetricProducers[metricIndex]->isActive();
}
}
}
mIsActive = isActive;
// A bitmap to see which ConditionTracker needs to be re-evaluated.
vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
for (const auto& pair : mTrackerToConditionMap) {
if (matcherCache[pair.first] == MatchingState::kMatched) {
const auto& conditionList = pair.second;
for (const int conditionIndex : conditionList) {
conditionToBeEvaluated[conditionIndex] = true;
}
}
}
vector<ConditionState> conditionCache(mAllConditionTrackers.size(),
ConditionState::kNotEvaluated);
// A bitmap to track if a condition has changed value.
vector<bool> changedCache(mAllConditionTrackers.size(), false);
for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
if (conditionToBeEvaluated[i] == false) {
continue;
}
sp<ConditionTracker>& condition = mAllConditionTrackers[i];
condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
changedCache);
}
for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
if (changedCache[i] == false) {
continue;
}
auto pair = mConditionToMetricMap.find(i);
if (pair != mConditionToMetricMap.end()) {
auto& metricList = pair->second;
for (auto metricIndex : metricList) {
// Metric cares about non sliced condition, and it's changed.
// Push the new condition to it directly.
if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
eventTimeNs);
// Metric cares about sliced conditions, and it may have changed. Send
// notification, and the metric can query the sliced conditions that are
// interesting to it.
} else {
mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
eventTimeNs);
}
}
}
}
// For matched AtomMatchers, tell relevant metrics that a matched event has come.
for (size_t i = 0; i < mAllAtomMatchers.size(); i++) {
if (matcherCache[i] == MatchingState::kMatched) {
StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
mAllAtomMatchers[i]->getId());
auto pair = mTrackerToMetricMap.find(i);
if (pair != mTrackerToMetricMap.end()) {
auto& metricList = pair->second;
for (const int metricIndex : metricList) {
// pushed metrics are never scheduled pulls
mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event);
}
}
}
}
}
void MetricsManager::onAnomalyAlarmFired(
const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
for (const auto& itr : mAllAnomalyTrackers) {
itr->informAlarmsFired(timestampNs, alarmSet);
}
}
void MetricsManager::onPeriodicAlarmFired(
const int64_t& timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
for (const auto& itr : mAllPeriodicAlarmTrackers) {
itr->informAlarmsFired(timestampNs, alarmSet);
}
}
// Returns the total byte size of all metrics managed by a single config source.
size_t MetricsManager::byteSize() {
size_t totalSize = 0;
for (const auto& metricProducer : mAllMetricProducers) {
totalSize += metricProducer->byteSize();
}
return totalSize;
}
void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs) {
if (config.metric_size() == 0) {
ALOGW("No active metric for config %s", mConfigKey.ToString().c_str());
return;
}
for (int i = 0; i < config.metric_size(); i++) {
const auto& activeMetric = config.metric(i);
for (int metricIndex : mMetricIndexesWithActivation) {
const auto& metric = mAllMetricProducers[metricIndex];
if (metric->getMetricId() == activeMetric.id()) {
VLOG("Setting active metric: %lld", (long long)metric->getMetricId());
metric->loadActiveMetric(activeMetric, currentTimeNs);
if (!mIsActive && metric->isActive()) {
StatsdStats::getInstance().noteActiveStatusChanged(mConfigKey,
/*activate=*/ true);
}
mIsActive |= metric->isActive();
}
}
}
}
void MetricsManager::writeActiveConfigToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId());
proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid());
for (int metricIndex : mMetricIndexesWithActivation) {
const auto& metric = mAllMetricProducers[metricIndex];
const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
FIELD_ID_ACTIVE_CONFIG_METRIC);
metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto);
proto->end(metricToken);
}
}
bool MetricsManager::writeMetadataToProto(int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs,
metadata::StatsMetadata* statsMetadata) {
bool metadataWritten = false;
metadata::ConfigKey* configKey = statsMetadata->mutable_config_key();
configKey->set_config_id(mConfigKey.GetId());
configKey->set_uid(mConfigKey.GetUid());
for (const auto& anomalyTracker : mAllAnomalyTrackers) {
metadata::AlertMetadata* alertMetadata = statsMetadata->add_alert_metadata();
bool alertWritten = anomalyTracker->writeAlertMetadataToProto(currentWallClockTimeNs,
systemElapsedTimeNs, alertMetadata);
if (!alertWritten) {
statsMetadata->mutable_alert_metadata()->RemoveLast();
}
metadataWritten |= alertWritten;
}
return metadataWritten;
}
void MetricsManager::loadMetadata(const metadata::StatsMetadata& metadata,
int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs) {
for (const metadata::AlertMetadata& alertMetadata : metadata.alert_metadata()) {
int64_t alertId = alertMetadata.alert_id();
auto it = mAlertTrackerMap.find(alertId);
if (it == mAlertTrackerMap.end()) {
ALOGE("No anomalyTracker found for alertId %lld", (long long) alertId);
continue;
}
mAllAnomalyTrackers[it->second]->loadAlertMetadata(alertMetadata,
currentWallClockTimeNs,
systemElapsedTimeNs);
}
}
} // namespace statsd
} // namespace os
} // namespace android

Some files were not shown because too many files have changed in this diff Show More