More complete implementation for condition and log matchers in statsd.
+ also synced proto from google3 to fix the LogEntryMatcher proto
+ MetricsManager represents StatsdConfig, it's responsible for initializing and managing all
LogEntryMatcher, Condition, and Metrics. Start review from here.
+ Added more complete StatsdConfig initialization, including building the map for:
LogEntryMatcher -> Metrics
LogEntryMatcher -> Condition
Condition -> Metrics.
All the maps use index(int). The extra amount of memory for storing mappings help us
quickly process log events.
The StatsdConfig initialization process detects malformed config
- Circle dependency
- Missing definition
etc.
And once we detect ANY error, statsd will reject the config. And the resources related to this
config will be released.
Test: Added unit tests
Change-Id: I2c4aefdbf3e2aa1701eacbb2fb5e653819ec1fbb
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index ca36d27..24a598a 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -42,24 +42,7 @@
LOCAL_SRC_FILES := \
../../core/java/android/os/IStatsCompanionService.aidl \
../../core/java/android/os/IStatsManager.aidl \
- src/StatsService.cpp \
- src/AnomalyMonitor.cpp \
- src/LogEntryPrinter.cpp \
- src/LogReader.cpp \
- src/main.cpp \
- src/DropboxWriter.cpp \
- src/parse_util.cpp \
- src/StatsLogProcessor.cpp \
- src/stats_log.proto \
- src/statsd_config.proto \
- src/StatsPullerManager.cpp \
- src/KernelWakelockPuller.cpp \
- src/DropboxReader.cpp \
- src/matchers/LogEntryMatcherManager.cpp \
- src/metrics/CountMetricProducer.cpp \
- src/metrics/ConditionTracker.cpp \
- src/metrics/MetricsManager.cpp \
- src/metrics/CountAnomalyTracker.cpp \
+ $(call all-cpp-files-under,src) \
LOCAL_CFLAGS += \
-Wall \
@@ -129,13 +112,19 @@
../../core/java/android/os/IStatsCompanionService.aidl \
../../core/java/android/os/IStatsManager.aidl \
src/StatsService.cpp \
- tests/indexed_priority_queue_test.cpp \
- src/parse_util.cpp \
+ src/stats_util.cpp \
src/LogEntryPrinter.cpp \
src/LogReader.cpp \
- src/matchers/LogEntryMatcherManager.cpp \
- tests/LogReader_test.cpp \
- tests/LogEntryMatcher_test.cpp \
+ src/matchers/matcher_util.cpp \
+ src/condition/SimpleConditionTracker.cpp \
+ src/condition/CombinationConditionTracker.cpp \
+ src/matchers/SimpleLogMatchingTracker.cpp \
+ src/matchers/CombinationLogMatchingTracker.cpp \
+ src/metrics/metrics_manager_util.cpp \
+ src/metrics/CountMetricProducer.cpp \
+ src/metrics/CountAnomalyTracker.cpp \
+ src/condition/condition_util.cpp \
+ $(call all-cpp-files-under, tests) \
LOCAL_STATIC_LIBRARIES := \
libgmock \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 117fb5e..1d2f586 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -20,7 +20,6 @@
#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
#include <log/log_event_list.h>
#include <metrics/CountMetricProducer.h>
-#include <parse_util.h>
#include <utils/Errors.h>
using namespace android;
@@ -41,28 +40,6 @@
StatsLogProcessor::~StatsLogProcessor() {
}
-StatsdConfig StatsLogProcessor::buildFakeConfig() {
- // HACK: Hard code a test metric for counting screen on events...
- StatsdConfig config;
- config.set_config_id(12345L);
-
- CountMetric* metric = config.add_count_metric();
- metric->set_metric_id(20150717L);
- metric->set_what("SCREEN_IS_ON");
- metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-
- LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
- eventMatcher->set_name("SCREEN_IS_ON");
-
- SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
- simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
- simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()
- ->set_key(1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleLogEntryMatcher->mutable_key_value_matcher(0)
- ->set_eq_int(2/*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
- return config;
-}
-
// TODO: what if statsd service restarts? How do we know what logs are already processed before?
void StatsLogProcessor::OnLogEvent(const log_msg& msg) {
// TODO: Use EventMetric to filter the events we want to log.
@@ -83,7 +60,14 @@
ALOGD("Updated configuration for source %i", config_source);
- mMetricsManagers.insert({config_source, std::make_unique<MetricsManager>(config)});
+ unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
+ if (newMetricsManager->isConfigValid()) {
+ mMetricsManagers.insert({config_source, std::move(newMetricsManager)});
+ ALOGD("StatsdConfig valid");
+ } else {
+ // If there is any error in the config, don't use it.
+ ALOGD("StatsdConfig NOT valid");
+ }
}
} // namespace statsd
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 88c63fa..ab1b44e 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -16,11 +16,11 @@
#ifndef STATS_LOG_PROCESSOR_H
#define STATS_LOG_PROCESSOR_H
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "DropboxWriter.h"
#include "LogReader.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "metrics/MetricsManager.h"
-#include "parse_util.h"
+#include "stats_util.h"
#include <log/logprint.h>
#include <stdio.h>
@@ -44,8 +44,6 @@
DropboxWriter m_dropbox_writer;
std::unordered_map<int, std::unique_ptr<MetricsManager>> mMetricsManagers;
-
- static StatsdConfig buildFakeConfig();
};
} // namespace statsd
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
new file mode 100644
index 0000000..6188383
--- /dev/null
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 LOG_TAG "CombinationConditionTracker"
+#define DEBUG true // STOPSHIP if true
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "CombinationConditionTracker.h"
+#include <cutils/log.h>
+#include <log/logprint.h>
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+CombinationConditionTracker::CombinationConditionTracker(const string& name, const int index)
+ : ConditionTracker(name, index) {
+ VLOG("creating CombinationConditionTracker %s", mName.c_str());
+}
+
+CombinationConditionTracker::~CombinationConditionTracker() {
+ VLOG("~CombinationConditionTracker() %s", mName.c_str());
+}
+
+bool CombinationConditionTracker::init(const vector<Condition>& allConditionConfig,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<string, int>& conditionNameIndexMap,
+ vector<bool>& stack) {
+ VLOG("Combiniation condition init() %s", mName.c_str());
+ if (mInitialized) {
+ return true;
+ }
+
+ // mark this node as visited in the recursion stack.
+ stack[mIndex] = true;
+
+ Condition_Combination combinationCondition = allConditionConfig[mIndex].combination();
+
+ if (!combinationCondition.has_operation()) {
+ return false;
+ }
+ mLogicalOperation = combinationCondition.operation();
+
+ if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.condition_size() != 1) {
+ return false;
+ }
+
+ for (string child : combinationCondition.condition()) {
+ auto it = conditionNameIndexMap.find(child);
+
+ if (it == conditionNameIndexMap.end()) {
+ ALOGW("Condition %s not found in the config", child.c_str());
+ 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,
+ conditionNameIndexMap, stack);
+
+ if (!initChildSucceeded) {
+ ALOGW("Child initialization failed %s ", child.c_str());
+ return false;
+ } else {
+ ALOGW("Child initialization success %s ", child.c_str());
+ }
+
+ mChildren.push_back(childIndex);
+
+ mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
+ childTracker->getLogTrackerIndex().end());
+ }
+
+ // unmark this node in the recursion stack.
+ stack[mIndex] = false;
+
+ mInitialized = true;
+
+ return true;
+}
+
+bool CombinationConditionTracker::evaluateCondition(
+ const LogEventWrapper& event, const std::vector<MatchingState>& eventMatcherValues,
+ const std::vector<sp<ConditionTracker>>& mAllConditions,
+ std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache) {
+ // value is up to date.
+ if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+ return false;
+ }
+
+ for (const int childIndex : mChildren) {
+ if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
+ const sp<ConditionTracker>& child = mAllConditions[childIndex];
+ child->evaluateCondition(event, eventMatcherValues, mAllConditions, conditionCache,
+ changedCache);
+ }
+ }
+
+ ConditionState newCondition =
+ evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
+
+ bool changed = (mConditionState != newCondition);
+ mConditionState = newCondition;
+
+ conditionCache[mIndex] = mConditionState;
+
+ changedCache[mIndex] = changed;
+ return changed;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
new file mode 100644
index 0000000..38780e7
--- /dev/null
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -0,0 +1,57 @@
+/*
+ * 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 std::string& name, const int index);
+
+ ~CombinationConditionTracker();
+
+ bool init(const std::vector<Condition>& allConditionConfig,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<std::string, int>& conditionNameIndexMap,
+ std::vector<bool>& stack) override;
+
+ bool evaluateCondition(const LogEventWrapper& event,
+ const std::vector<MatchingState>& eventMatcherValues,
+ const std::vector<sp<ConditionTracker>>& mAllConditions,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache) override;
+
+private:
+ LogicalOperation mLogicalOperation;
+ // Store index of the children Conditions.
+ // 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;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // COMBINATION_CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
new file mode 100644
index 0000000..2da8fa0
--- /dev/null
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -0,0 +1,105 @@
+/*
+ * 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_TRACKER_H
+#define CONDITION_TRACKER_H
+
+#include <cutils/log.h>
+#include <log/logprint.h>
+#include <utils/RefBase.h>
+#include <unordered_map>
+#include "../matchers/LogMatchingTracker.h"
+#include "../matchers/matcher_util.h"
+#include "condition_util.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class ConditionTracker : public virtual RefBase {
+public:
+ ConditionTracker(const std::string& name, const int index)
+ : mName(name),
+ mIndex(index),
+ mInitialized(false),
+ mConditionState(ConditionState::kUnknown),
+ mTrackerIndex(){};
+
+ virtual ~ConditionTracker(){};
+
+ // 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 Condition config from statsd_config.
+ // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
+ // need to call init() on children conditions)
+ // conditionNameIndexMap: the mapping from condition name to its index.
+ // stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
+ virtual bool init(const std::vector<Condition>& allConditionConfig,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<std::string, int>& conditionNameIndexMap,
+ std::vector<bool>& stack) = 0;
+
+ // evaluate current condition given the new event.
+ // return true if the condition state changed, false if the condition state is not changed.
+ // 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 results of the ConditionTrackers for this new event.
+ // changedCache: the bit map to record whether the condition has changed.
+ virtual bool evaluateCondition(const LogEventWrapper& event,
+ const std::vector<MatchingState>& eventMatcherValues,
+ const std::vector<sp<ConditionTracker>>& mAllConditions,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache) = 0;
+
+ // Return the current condition state.
+ virtual ConditionState isConditionMet() {
+ ALOGW("Condition %s value %d", mName.c_str(), mConditionState);
+ return mConditionState;
+ };
+
+ // return the list of LogMatchingTracker index that this ConditionTracker uses.
+ virtual const std::set<int>& getLogTrackerIndex() const {
+ return mTrackerIndex;
+ }
+
+protected:
+ // We don't really need the string name, but having a name here makes log messages
+ // easy to debug.
+ const std::string mName;
+
+ // the index of this condition in the manager's condition list.
+ const int mIndex;
+
+ // if it's properly initialized.
+ bool mInitialized;
+
+ // current condition state.
+ ConditionState mConditionState;
+
+ // the list of LogMatchingTracker index that this ConditionTracker uses.
+ std::set<int> mTrackerIndex;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
new file mode 100644
index 0000000..14ec72e
--- /dev/null
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 LOG_TAG "Stats_SimpleConditionTracker"
+#define DEBUG true // STOPSHIP if true
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "SimpleConditionTracker.h"
+#include <cutils/log.h>
+#include <log/logprint.h>
+
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SimpleConditionTracker::SimpleConditionTracker(
+ const string& name, const int index, const SimpleCondition& simpleCondition,
+ const unordered_map<string, int>& trackerNameIndexMap)
+ : ConditionTracker(name, index) {
+ VLOG("creating SimpleConditionTracker %s", mName.c_str());
+ mCountNesting = simpleCondition.count_nesting();
+
+ if (simpleCondition.has_start()) {
+ auto pair = trackerNameIndexMap.find(simpleCondition.start());
+ if (pair == trackerNameIndexMap.end()) {
+ ALOGW("Start matcher %s not found in the config", simpleCondition.start().c_str());
+ return;
+ }
+ mStartLogMatcherIndex = pair->second;
+ mTrackerIndex.insert(mStartLogMatcherIndex);
+ } else {
+ mStartLogMatcherIndex = -1;
+ }
+
+ if (simpleCondition.has_stop()) {
+ auto pair = trackerNameIndexMap.find(simpleCondition.stop());
+ if (pair == trackerNameIndexMap.end()) {
+ ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str());
+ return;
+ }
+ mStopLogMatcherIndex = pair->second;
+ mTrackerIndex.insert(mStartLogMatcherIndex);
+ } else {
+ mStopLogMatcherIndex = -1;
+ }
+
+ if (simpleCondition.has_stop_all()) {
+ auto pair = trackerNameIndexMap.find(simpleCondition.stop_all());
+ if (pair == trackerNameIndexMap.end()) {
+ ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str());
+ return;
+ }
+ mStopAllLogMatcherIndex = pair->second;
+ mTrackerIndex.insert(mStopAllLogMatcherIndex);
+ } else {
+ mStopAllLogMatcherIndex = -1;
+ }
+
+ mInitialized = true;
+}
+
+SimpleConditionTracker::~SimpleConditionTracker() {
+ VLOG("~SimpleConditionTracker()");
+}
+
+bool SimpleConditionTracker::init(const vector<Condition>& allConditionConfig,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<string, int>& conditionNameIndexMap,
+ vector<bool>& stack) {
+ // SimpleConditionTracker does not have dependency on other conditions, thus we just return
+ // if the initialization was successful.
+ return mInitialized;
+}
+
+bool SimpleConditionTracker::evaluateCondition(const LogEventWrapper& event,
+ const vector<MatchingState>& eventMatcherValues,
+ const vector<sp<ConditionTracker>>& mAllConditions,
+ vector<ConditionState>& conditionCache,
+ vector<bool>& changedCache) {
+ if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+ // it has been evaluated.
+ VLOG("Yes, already evaluated, %s %d", mName.c_str(), mConditionState);
+ return false;
+ }
+
+ // Ignore nesting, because we know we cannot trust ourselves on tracking nesting conditions.
+ ConditionState newCondition = mConditionState;
+ // 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) {
+ newCondition = ConditionState::kTrue;
+ }
+
+ if (mStopLogMatcherIndex >= 0 &&
+ eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
+ newCondition = ConditionState::kFalse;
+ }
+
+ if (mStopAllLogMatcherIndex >= 0 &&
+ eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
+ newCondition = ConditionState::kFalse;
+ }
+
+ bool changed = (mConditionState != newCondition);
+ mConditionState = newCondition;
+ conditionCache[mIndex] = mConditionState;
+ changedCache[mIndex] = changed;
+ return changed;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
new file mode 100644
index 0000000..41e1707
--- /dev/null
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -0,0 +1,64 @@
+/*
+ * 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 "ConditionTracker.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class SimpleConditionTracker : public virtual ConditionTracker {
+public:
+ SimpleConditionTracker(const std::string& name, const int index,
+ const SimpleCondition& simpleCondition,
+ const std::unordered_map<std::string, int>& trackerNameIndexMap);
+
+ ~SimpleConditionTracker();
+
+ bool init(const std::vector<Condition>& allConditionConfig,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<std::string, int>& conditionNameIndexMap,
+ std::vector<bool>& stack) override;
+
+ bool evaluateCondition(const LogEventWrapper& event,
+ const std::vector<MatchingState>& eventMatcherValues,
+ const std::vector<sp<ConditionTracker>>& mAllConditions,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache) override;
+
+private:
+ // 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;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // SIMPLE_CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
new file mode 100644
index 0000000..cb07d15
--- /dev/null
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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 "condition_util.h"
+
+#include <cutils/log.h>
+#include <log/event_tag_map.h>
+#include <log/log_event_list.h>
+#include <log/logprint.h>
+#include <utils/Errors.h>
+#include <unordered_map>
+#include "ConditionTracker.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "stats_util.h"
+
+using std::set;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+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 = (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;
+ }
+ return newCondition;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/ConditionTracker.h b/cmds/statsd/src/condition/condition_util.h
similarity index 60%
rename from cmds/statsd/src/metrics/ConditionTracker.h
rename to cmds/statsd/src/condition/condition_util.h
index b94d5ab..a4fcea3 100644
--- a/cmds/statsd/src/metrics/ConditionTracker.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -14,38 +14,28 @@
* limitations under the License.
*/
-#ifndef CONDITION_TRACKER_H
-#define CONDITION_TRACKER_H
+#ifndef CONDITION_UTIL_H
+#define CONDITION_UTIL_H
-#include <utils/RefBase.h>
-#include "../matchers/LogEntryMatcherManager.h"
+#include <vector>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
namespace os {
namespace statsd {
-class ConditionTracker : public RefBase {
-public:
- ConditionTracker();
-
- ConditionTracker(const Condition& condition);
-
- ~ConditionTracker();
-
- void evaluateCondition(const LogEventWrapper& event);
-
- bool isConditionMet() const;
-
-private:
- // this is the definition of the Condition.
- Condition mCondition;
-
- bool mIsConditionMet;
+enum ConditionState {
+ kNotEvaluated = -2,
+ kUnknown = -1,
+ kFalse = 0,
+ kTrue = 1,
};
+ConditionState evaluateCombinationCondition(const std::vector<int>& children,
+ const LogicalOperation& operation,
+ const std::vector<ConditionState>& conditionCache);
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // CONDITION_TRACKER_H
+#endif // CONDITION_UTIL_H
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
new file mode 100644
index 0000000..9f9b648
--- /dev/null
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 "CombinationLogMatchingTracker.h"
+
+#include <cutils/log.h>
+#include "matcher_util.h"
+using std::set;
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+CombinationLogMatchingTracker::CombinationLogMatchingTracker(const string& name, const int index)
+ : LogMatchingTracker(name, index) {
+}
+
+CombinationLogMatchingTracker::~CombinationLogMatchingTracker() {
+}
+
+bool CombinationLogMatchingTracker::init(const vector<LogEntryMatcher>& allLogMatchers,
+ const vector<sp<LogMatchingTracker>>& allTrackers,
+ const unordered_map<string, int>& matcherMap,
+ vector<bool>& stack) {
+ if (mInitialized) {
+ return true;
+ }
+
+ // mark this node as visited in the recursion stack.
+ stack[mIndex] = true;
+
+ LogEntryMatcher_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 string& child : matcher.matcher()) {
+ auto pair = matcherMap.find(child);
+ if (pair == matcherMap.end()) {
+ ALOGW("Matcher %s not found in the config", child.c_str());
+ 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 %s", child.c_str());
+ return false;
+ }
+
+ mChildren.push_back(childIndex);
+
+ const set<int>& childTagIds = allTrackers[childIndex]->getTagIds();
+ mTagIds.insert(childTagIds.begin(), childTagIds.end());
+ }
+
+ mInitialized = true;
+ // unmark this node in the recursion stack.
+ stack[mIndex] = false;
+ return true;
+}
+
+void CombinationLogMatchingTracker::onLogEvent(const LogEventWrapper& event,
+ const vector<sp<LogMatchingTracker>>& allTrackers,
+ vector<MatchingState>& matcherResults) {
+ // this event has been processed.
+ if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+ return;
+ }
+
+ if (mTagIds.find(event.tagId) == mTagIds.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
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
new file mode 100644
index 0000000..51ee232
--- /dev/null
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
@@ -0,0 +1,57 @@
+/*
+ * 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 <log/log_read.h>
+#include <log/logprint.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+#include "LogMatchingTracker.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Represents a LogEntryMatcher_Combination in the StatsdConfig.
+class CombinationLogMatchingTracker : public virtual LogMatchingTracker {
+public:
+ CombinationLogMatchingTracker(const std::string& name, const int index);
+
+ bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+ const std::vector<sp<LogMatchingTracker>>& allTrackers,
+ const std::unordered_map<std::string, int>& matcherMap,
+ std::vector<bool>& stack);
+
+ ~CombinationLogMatchingTracker();
+
+ void onLogEvent(const LogEventWrapper& 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
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
new file mode 100644
index 0000000..4244bd5
--- /dev/null
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -0,0 +1,95 @@
+/*
+ * 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 <log/log_read.h>
+#include <log/logprint.h>
+#include <utils/RefBase.h>
+#include <set>
+#include <unordered_map>
+
+#include <vector>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matcher_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class LogMatchingTracker : public virtual RefBase {
+public:
+ LogMatchingTracker(const std::string& name, const int index)
+ : mName(name), mIndex(index), mInitialized(false){};
+
+ virtual ~LogMatchingTracker(){};
+
+ // Initialize this LogMatchingTracker.
+ // allLogMatchers: the list of the LogEntryMatcher 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<LogEntryMatcher>& allLogMatchers,
+ const std::vector<sp<LogMatchingTracker>>& allTrackers,
+ const std::unordered_map<std::string, 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 LogEventWrapper& 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>& getTagIds() const {
+ return mTagIds;
+ }
+
+protected:
+ // Name of this matching. We don't really need the name, but it makes log message easy to debug.
+ const std::string mName;
+
+ // 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.
+ // TODO: Consider use an array instead of stl set. In reality, the number of the tag ids a
+ // LogMatchingTracker cares is only a few.
+ std::set<int> mTagIds;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // LOG_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
new file mode 100644
index 0000000..1c83039
--- /dev/null
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 LOG_TAG "SimpleLogMatchingTracker"
+#define DEBUG true // STOPSHIP if true
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "SimpleLogMatchingTracker.h"
+#include <cutils/log.h>
+#include <log/logprint.h>
+
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index,
+ const SimpleLogEntryMatcher& matcher)
+ : LogMatchingTracker(name, index), mMatcher(matcher) {
+ for (int i = 0; i < matcher.tag_size(); i++) {
+ mTagIds.insert(matcher.tag(i));
+ }
+ mInitialized = true;
+}
+
+SimpleLogMatchingTracker::~SimpleLogMatchingTracker() {
+}
+
+bool SimpleLogMatchingTracker::init(const vector<LogEntryMatcher>& allLogMatchers,
+ const vector<sp<LogMatchingTracker>>& allTrackers,
+ const unordered_map<string, int>& matcherMap,
+ vector<bool>& stack) {
+ // no need to do anything.
+ return true;
+}
+
+void SimpleLogMatchingTracker::onLogEvent(const LogEventWrapper& event,
+ const vector<sp<LogMatchingTracker>>& allTrackers,
+ vector<MatchingState>& matcherResults) {
+ if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+ VLOG("Matcher %s already evaluated ", mName.c_str());
+ return;
+ }
+
+ if (mTagIds.find(event.tagId) == mTagIds.end()) {
+ matcherResults[mIndex] = MatchingState::kNotMatched;
+ return;
+ }
+
+ bool matched = matchesSimple(mMatcher, event);
+ matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+ VLOG("Stats SimpleLogMatcher %s matched? %d", mName.c_str(), matched);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
new file mode 100644
index 0000000..65dbe64
--- /dev/null
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -0,0 +1,56 @@
+/*
+ * 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 <log/log_read.h>
+#include <log/logprint.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+#include "LogMatchingTracker.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
+public:
+ SimpleLogMatchingTracker(const std::string& name, const int index,
+ const SimpleLogEntryMatcher& matcher);
+
+ ~SimpleLogMatchingTracker();
+
+ bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+ const std::vector<sp<LogMatchingTracker>>& allTrackers,
+ const std::unordered_map<std::string, int>& matcherMap,
+ std::vector<bool>& stack) override;
+
+ void onLogEvent(const LogEventWrapper& event,
+ const std::vector<sp<LogMatchingTracker>>& allTrackers,
+ std::vector<MatchingState>& matcherResults) override;
+
+private:
+ const SimpleLogEntryMatcher mMatcher;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // SIMPLE_LOG_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
similarity index 69%
rename from cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
rename to cmds/statsd/src/matchers/matcher_util.cpp
index ab7b2b1d..557c032 100644
--- a/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -14,26 +14,28 @@
* limitations under the License.
*/
-#include "LogEntryMatcherManager.h"
+#include "matcher_util.h"
#include <cutils/log.h>
#include <log/event_tag_map.h>
#include <log/log_event_list.h>
#include <log/logprint.h>
#include <utils/Errors.h>
#include <unordered_map>
+#include "LogMatchingTracker.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "parse_util.h"
+#include "stats_util.h"
using std::set;
using std::string;
using std::unordered_map;
+using std::vector;
namespace android {
namespace os {
namespace statsd {
-LogEventWrapper LogEntryMatcherManager::parseLogEvent(log_msg msg) {
+LogEventWrapper parseLogEvent(log_msg msg) {
LogEventWrapper wrapper;
wrapper.timestamp_ns = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
wrapper.tagId = getTagId(msg);
@@ -67,7 +69,9 @@
break;
case EVENT_TYPE_STRING:
if (index % 2 == 1) {
- wrapper.strMap[key] = elem.data.string;
+ // without explicit calling string() constructor, there will be an
+ // additional 0 in the end of the string.
+ wrapper.strMap[key] = string(elem.data.string);
}
index++;
break;
@@ -99,57 +103,56 @@
return wrapper;
}
-bool LogEntryMatcherManager::matches(const LogEntryMatcher& matcher, const LogEventWrapper& event) {
- const int tagId = event.tagId;
- const unordered_map<int, long>& intMap = event.intMap;
- const unordered_map<int, string>& strMap = event.strMap;
- const unordered_map<int, float>& floatMap = event.floatMap;
- const unordered_map<int, bool>& boolMap = event.boolMap;
-
- if (matcher.has_combination()) { // Need to evaluate composite matching
- switch (matcher.combination().operation()) {
- case LogicalOperation::AND:
- for (auto nestedMatcher : matcher.combination().matcher()) {
- if (!matches(nestedMatcher, event)) {
- return false; // return false if any nested matcher is false;
- }
+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;
}
- return true; // Otherwise, return true.
- case LogicalOperation::OR:
- for (auto nestedMatcher : matcher.combination().matcher()) {
- if (matches(nestedMatcher, event)) {
- return true; // return true if any nested matcher is true;
- }
- }
- return false;
- case LogicalOperation::NOT:
- return !matches(matcher.combination().matcher(0), event);
-
- // Case NAND is just inverting the return statement of AND
- case LogicalOperation::NAND:
- for (auto nestedMatcher : matcher.combination().matcher()) {
- auto simple = nestedMatcher.simple_log_entry_matcher();
- if (!matches(nestedMatcher, event)) {
- return true; // return false if any nested matcher is false;
- }
- }
- return false; // Otherwise, return true.
- case LogicalOperation::NOR:
- for (auto nestedMatcher : matcher.combination().matcher()) {
- if (matches(nestedMatcher, event)) {
- return false; // return true if any nested matcher is true;
- }
- }
- return true;
+ }
+ break;
}
- return false;
- } else {
- return matchesSimple(matcher.simple_log_entry_matcher(), event);
+ 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;
}
+ return matched;
}
-bool LogEntryMatcherManager::matchesSimple(const SimpleLogEntryMatcher& simpleMatcher,
- const LogEventWrapper& event) {
+bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEventWrapper& event) {
const int tagId = event.tagId;
const unordered_map<int, long>& intMap = event.intMap;
const unordered_map<int, string>& strMap = event.strMap;
@@ -249,26 +252,6 @@
return false;
}
-set<int> LogEntryMatcherManager::getTagIdsFromMatcher(const LogEntryMatcher& matcher) {
- set<int> result;
- switch (matcher.contents_case()) {
- case LogEntryMatcher::kCombination:
- for (auto sub_matcher : matcher.combination().matcher()) {
- set<int> tagSet = getTagIdsFromMatcher(sub_matcher);
- result.insert(tagSet.begin(), tagSet.end());
- }
- break;
- case LogEntryMatcher::kSimpleLogEntryMatcher:
- for (int i = 0; i < matcher.simple_log_entry_matcher().tag_size(); i++) {
- result.insert(matcher.simple_log_entry_matcher().tag(i));
- }
- break;
- case LogEntryMatcher::CONTENTS_NOT_SET:
- break;
- }
- return result;
-}
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.h b/cmds/statsd/src/matchers/matcher_util.h
similarity index 63%
rename from cmds/statsd/src/matchers/LogEntryMatcherManager.h
rename to cmds/statsd/src/matchers/matcher_util.h
index fc8e6a1..6d8e762 100644
--- a/cmds/statsd/src/matchers/LogEntryMatcherManager.h
+++ b/cmds/statsd/src/matchers/matcher_util.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef LOG_ENTRY_MATCHER_MANAGER_H
-#define LOG_ENTRY_MATCHER_MANAGER_H
+#ifndef MATCHER_UTIL_H
+#define MATCHER_UTIL_H
#include <log/log_read.h>
#include <log/logprint.h>
@@ -25,9 +25,6 @@
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-using std::string;
-using std::unordered_map;
-
namespace android {
namespace os {
namespace statsd {
@@ -41,26 +38,20 @@
std::unordered_map<int, float> floatMap;
} LogEventWrapper;
-/**
- * Keeps track per log entry which simple log entry matchers match.
- */
-class LogEntryMatcherManager {
-public:
- LogEntryMatcherManager();
-
- ~LogEntryMatcherManager(){};
-
- static LogEventWrapper parseLogEvent(log_msg msg);
-
- static std::set<int> getTagIdsFromMatcher(const LogEntryMatcher& matcher);
-
- static bool matches(const LogEntryMatcher& matcher, const LogEventWrapper& wrapper);
-
- static bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher,
- const LogEventWrapper& wrapper);
+enum MatchingState {
+ kNotComputed = -1,
+ kNotMatched = 0,
+ kMatched = 1,
};
+LogEventWrapper parseLogEvent(log_msg msg);
+
+bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
+ const std::vector<MatchingState>& matcherResults);
+
+bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEventWrapper& wrapper);
+
} // namespace statsd
} // namespace os
} // namespace android
-#endif // LOG_ENTRY_MATCHER_MANAGER_H
+#endif // MATCHER_UTIL_H
diff --git a/cmds/statsd/src/metrics/ConditionTracker.cpp b/cmds/statsd/src/metrics/ConditionTracker.cpp
deleted file mode 100644
index 684ffdb..0000000
--- a/cmds/statsd/src/metrics/ConditionTracker.cpp
+++ /dev/null
@@ -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.
- */
-
-#define LOG_TAG "ConditionTracker"
-#define DEBUG true // STOPSHIP if true
-#define VLOG(...) \
- if (DEBUG) ALOGD(__VA_ARGS__);
-
-#include "ConditionTracker.h"
-#include <cutils/log.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-ConditionTracker::ConditionTracker() : mIsConditionMet(true) {
- VLOG("ConditionTracker()");
-}
-
-ConditionTracker::ConditionTracker(const Condition& condition)
- : mCondition(condition), mIsConditionMet(true) {
- VLOG("ConditionTracker()");
-}
-
-ConditionTracker::~ConditionTracker() {
- VLOG("~ConditionTracker()");
-}
-
-void ConditionTracker::evaluateCondition(const LogEventWrapper& event) {
- // modify condition.
- VLOG("evaluateCondition");
-}
-
-bool ConditionTracker::isConditionMet() const {
- VLOG("isConditionMet() %d", mIsConditionMet);
- return mIsConditionMet;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 635777f..e98999e 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -21,7 +21,6 @@
#include "CountMetricProducer.h"
#include "CountAnomalyTracker.h"
-#include "parse_util.h"
#include <cutils/log.h>
#include <limits.h>
@@ -33,15 +32,14 @@
namespace os {
namespace statsd {
-CountMetricProducer::CountMetricProducer(const CountMetric& metric,
- const sp<ConditionTracker> condition)
+CountMetricProducer::CountMetricProducer(const CountMetric& metric, const bool hasCondition)
: mMetric(metric),
- mConditionTracker(condition),
- mStartTime(std::time(nullptr)),
+ mStartTime(time(nullptr)),
mCounter(0),
mCurrentBucketStartTime(mStartTime),
// TODO: read mAnomalyTracker parameters from config file.
- mAnomalyTracker(6, 10) {
+ mAnomalyTracker(6, 10),
+ mCondition(hasCondition ? ConditionState::kUnknown : ConditionState::kTrue) {
// TODO: evaluate initial conditions. and set mConditionMet.
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
mBucketSize_sec = metric.bucket().bucket_size_millis() / 1000;
@@ -52,10 +50,6 @@
VLOG("created. bucket size %lu start_time: %lu", mBucketSize_sec, mStartTime);
}
-CountMetricProducer::CountMetricProducer(const CountMetric& metric)
- : CountMetricProducer(metric, new ConditionTracker()) {
-}
-
CountMetricProducer::~CountMetricProducer() {
VLOG("~CountMetricProducer() called");
}
@@ -63,13 +57,17 @@
void CountMetricProducer::finish() {
// TODO: write the StatsLogReport to dropbox using
// DropboxWriter.
- onDumpReport();
}
void CountMetricProducer::onDumpReport() {
VLOG("dump report now...");
}
+void CountMetricProducer::onConditionChanged(const bool conditionMet) {
+ VLOG("onConditionChanged");
+ mCondition = conditionMet;
+}
+
void CountMetricProducer::onMatchedLogEvent(const LogEventWrapper& event) {
time_t eventTime = event.timestamp_ns / 1000000000;
@@ -78,22 +76,24 @@
return;
}
- if (mConditionTracker->isConditionMet()) {
+ if (mCondition == ConditionState::kTrue) {
flushCounterIfNeeded(eventTime);
mCounter++;
mAnomalyTracker.checkAnomaly(mCounter);
+ VLOG("metric %lld count %d", mMetric.metric_id(), mCounter);
}
}
-// When a new matched event comes in, we check if it falls into the current bucket. And flush the
-// counter to the StatsLogReport and adjust the bucket if needed.
+// When a new matched event comes in, we check if it falls into the current
+// bucket. And flush the counter to the StatsLogReport and adjust the bucket if
+// needed.
void CountMetricProducer::flushCounterIfNeeded(const time_t& eventTime) {
if (mCurrentBucketStartTime + mBucketSize_sec > eventTime) {
return;
}
// TODO: add a KeyValuePair to StatsLogReport.
- ALOGD("CountMetric: dump counter %d", mCounter);
+ ALOGD("%lld: dump counter %d", mMetric.metric_id(), mCounter);
// adjust the bucket start time
time_t numBucketsForward = (eventTime - mCurrentBucketStartTime)
@@ -106,7 +106,7 @@
mAnomalyTracker.addPastBucket(mCounter, numBucketsForward);
mCounter = 0;
- VLOG("new bucket start time: %lu", mCurrentBucketStartTime);
+ VLOG("%lld: new bucket start time: %lu", mMetric.metric_id(), mCurrentBucketStartTime);
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 7502320..0729e2c 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -17,13 +17,11 @@
#ifndef COUNT_METRIC_PRODUCER_H
#define COUNT_METRIC_PRODUCER_H
-#include <mutex>
-#include <thread>
#include <unordered_map>
-#include "../matchers/LogEntryMatcherManager.h"
+
#include "CountAnomalyTracker.h"
-#include "ConditionTracker.h"
-#include "DropboxWriter.h"
+#include "../condition/ConditionTracker.h"
+#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
@@ -34,14 +32,14 @@
class CountMetricProducer : public MetricProducer {
public:
- CountMetricProducer(const CountMetric& countMetric, const sp<ConditionTracker> condition);
-
- CountMetricProducer(const CountMetric& countMetric);
+ CountMetricProducer(const CountMetric& countMetric, const bool hasCondition);
virtual ~CountMetricProducer();
void onMatchedLogEvent(const LogEventWrapper& event) override;
+ void onConditionChanged(const bool conditionMet) override;
+
void finish() override;
void onDumpReport() override;
@@ -49,8 +47,6 @@
private:
const CountMetric mMetric;
- const sp<ConditionTracker> mConditionTracker;
-
const time_t mStartTime;
// TODO: Add dimensions.
// Counter value for the current bucket.
@@ -62,6 +58,8 @@
CountAnomalyTracker mAnomalyTracker;
+ bool mCondition;
+
void flushCounterIfNeeded(const time_t& newEventTime);
};
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 44a778b..7d3d661 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -18,7 +18,8 @@
#define METRIC_PRODUCER_H
#include <log/logprint.h>
-#include "../matchers/LogEntryMatcherManager.h"
+#include <utils/RefBase.h>
+#include "../matchers/matcher_util.h"
namespace android {
namespace os {
@@ -26,13 +27,15 @@
// A MetricProducer is responsible for compute one single metrics, creating stats log report, and
// writing the report to dropbox.
-class MetricProducer {
+class MetricProducer : public virtual RefBase {
public:
virtual ~MetricProducer(){};
- // Consume the stats log if it's interesting to this metric.
+ // Consume the parsed stats log entry that already matched the "what" of the metric.
virtual void onMatchedLogEvent(const LogEventWrapper& event) = 0;
+ virtual void onConditionChanged(const bool condition) = 0;
+
// This is called when the metric collecting is done, e.g., when there is a new configuration
// coming. MetricProducer should do the clean up, and dump existing data to dropbox.
virtual void finish() = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index cb74206..1e65f58 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -21,13 +21,17 @@
#include "MetricsManager.h"
#include <cutils/log.h>
#include <log/logprint.h>
+#include "../condition/CombinationConditionTracker.h"
+#include "../condition/SimpleConditionTracker.h"
+#include "../matchers/CombinationLogMatchingTracker.h"
+#include "../matchers/SimpleLogMatchingTracker.h"
#include "CountMetricProducer.h"
-#include "parse_util.h"
+#include "metrics_manager_util.h"
+#include "stats_util.h"
using std::make_unique;
using std::set;
using std::string;
-using std::unique_ptr;
using std::unordered_map;
using std::vector;
@@ -35,93 +39,90 @@
namespace os {
namespace statsd {
-MetricsManager::MetricsManager(const StatsdConfig& config) : mConfig(config), mLogMatchers() {
- std::unordered_map<string, LogEntryMatcher> matcherMap;
- std::unordered_map<string, sp<ConditionTracker>> conditionMap;
-
- for (int i = 0; i < config.log_entry_matcher_size(); i++) {
- const LogEntryMatcher& logMatcher = config.log_entry_matcher(i);
- mMatchers.push_back(logMatcher);
-
- matcherMap[config.log_entry_matcher(i).name()] = logMatcher;
-
- mLogMatchers[logMatcher.name()] = vector<unique_ptr<MetricProducer>>();
- // Collect all the tag ids that are interesting
- set<int> tagIds = LogEntryMatcherManager::getTagIdsFromMatcher(logMatcher);
-
- mTagIds.insert(tagIds.begin(), tagIds.end());
- }
-
- for (int i = 0; i < config.condition_size(); i++) {
- const Condition& condition = config.condition(i);
- conditionMap[condition.name()] = new ConditionTracker(condition);
- }
-
- // Build MetricProducers for each metric defined in config.
- // (1) build CountMetricProducer
- for (int i = 0; i < config.count_metric_size(); i++) {
- const CountMetric& metric = config.count_metric(i);
- auto it = mLogMatchers.find(metric.what());
- if (it == mLogMatchers.end()) {
- ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str());
- continue;
- }
-
- if (metric.has_condition()) {
- auto condition_it = conditionMap.find(metric.condition());
- if (condition_it == conditionMap.end()) {
- ALOGW("cannot find the Condition %s in the config", metric.condition().c_str());
- continue;
- }
- it->second.push_back(make_unique<CountMetricProducer>(metric, condition_it->second));
- } else {
- it->second.push_back(make_unique<CountMetricProducer>(metric));
- }
- }
-
- // TODO: build other types of metrics too.
+MetricsManager::MetricsManager(const StatsdConfig& config) {
+ mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
+ mAllMetricProducers, mConditionToMetricMap, mTrackerToMetricMap,
+ mTrackerToConditionMap);
}
MetricsManager::~MetricsManager() {
VLOG("~MetricManager()");
}
+bool MetricsManager::isConfigValid() const {
+ return mConfigValid;
+}
+
void MetricsManager::finish() {
- for (auto const& entryPair : mLogMatchers) {
- for (auto const& metric : entryPair.second) {
- metric->finish();
- }
+ for (auto& metricProducer : mAllMetricProducers) {
+ metricProducer->finish();
}
}
// Consume the stats log if it's interesting to this metric.
void MetricsManager::onLogEvent(const log_msg& logMsg) {
+ if (!mConfigValid) {
+ return;
+ }
+
int tagId = getTagId(logMsg);
if (mTagIds.find(tagId) == mTagIds.end()) {
// not interesting...
return;
}
- // Since at least one of the metrics is interested in this event, we parse it now.
- LogEventWrapper event = LogEntryMatcherManager::parseLogEvent(logMsg);
- // Evaluate the conditions. Order matters, this should happen
- // before sending the event to metrics
- for (auto& condition : mConditionTracker) {
- condition->evaluateCondition(event);
+ // Since at least one of the metrics is interested in this event, we parse it now.
+ LogEventWrapper event = parseLogEvent(logMsg);
+ vector<MatchingState> matcherCache(mAllLogEntryMatchers.size(), MatchingState::kNotComputed);
+
+ for (auto& matcher : mAllLogEntryMatchers) {
+ matcher->onLogEvent(event, mAllLogEntryMatchers, matcherCache);
}
- // Now find out which LogMatcher matches this event, and let relevant metrics know.
- for (auto matcher : mMatchers) {
- if (LogEntryMatcherManager::matches(matcher, event)) {
- auto it = mLogMatchers.find(matcher.name());
- if (it != mLogMatchers.end()) {
- for (auto const& it2 : it->second) {
- // Only metrics that matches this event get notified.
- it2->onMatchedLogEvent(event);
+ // 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);
+ if (changedCache[i]) {
+ auto pair = mConditionToMetricMap.find(i);
+ if (pair != mConditionToMetricMap.end()) {
+ auto& metricList = pair->second;
+ for (auto metricIndex : metricList) {
+ mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i]);
}
- } else {
- // TODO: we should remove any redundant matchers that the config provides.
- ALOGW("Matcher not used by any metrics.");
+ }
+ }
+ }
+
+ // For matched LogEntryMatchers, tell relevant metrics that a matched event has come.
+ for (size_t i = 0; i < mAllLogEntryMatchers.size(); i++) {
+ if (matcherCache[i] == MatchingState::kMatched) {
+ auto pair = mTrackerToMetricMap.find(i);
+ if (pair != mTrackerToMetricMap.end()) {
+ auto& metricList = pair->second;
+ for (const int metricIndex : metricList) {
+ mAllMetricProducers[metricIndex]->onMatchedLogEvent(event);
+ }
}
}
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 77d7535..70c34db 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -20,8 +20,8 @@
#include <cutils/log.h>
#include <log/logprint.h>
#include <unordered_map>
-#include "../matchers/LogEntryMatcherManager.h"
-#include "ConditionTracker.h"
+#include "../condition/ConditionTracker.h"
+#include "../matchers/LogMatchingTracker.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
@@ -36,25 +36,58 @@
~MetricsManager();
- // Consume the stats log if it's interesting to this metric.
+ // Return whether the configuration is valid.
+ bool isConfigValid() const;
+
void onLogEvent(const log_msg& logMsg);
+ // Called when everything should wrap up. We are about to finish (e.g., new config comes).
void finish();
private:
- const StatsdConfig mConfig;
-
// All event tags that are interesting to my metrics.
std::set<int> mTagIds;
- // The matchers that my metrics share.
- std::vector<LogEntryMatcher> mMatchers;
+ // We only store the sp of LogMatchingTracker, MetricProducer, and ConditionTracker in
+ // MetricManager. There are relationship between them, and the relationship are denoted by index
+ // instead of poiters. The reasons for this are: (1) the relationship between them are
+ // complicated, store index instead of pointers reduce the risk of A holds B's sp, and B holds
+ // A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get the
+ // related results from a cache using the index.
+ // TODO: using unique_ptr may be more appriopreate?
- // The conditions that my metrics share.
- std::vector<sp<ConditionTracker>> mConditionTracker;
+ // Hold all the log entry matchers from the config.
+ std::vector<sp<LogMatchingTracker>> mAllLogEntryMatchers;
- // the map from LogEntryMatcher names to the metrics that use this matcher.
- std::unordered_map<std::string, std::vector<std::unique_ptr<MetricProducer>>> mLogMatchers;
+ // Hold all the conditions from the config.
+ std::vector<sp<ConditionTracker>> mAllConditionTrackers;
+
+ // Hold all metrics from the config.
+ std::vector<sp<MetricProducer>> mAllMetricProducers;
+
+ // To make the log processing more efficient, we want to do as much filtering as possible
+ // before we go into individual trackers and conditions to match.
+
+ // 1st filter: check if the event tag id is in mTagIds.
+ // 2nd filter: if it is, we parse the event because there is at least one member is interested.
+ // then pass to all LogMatchingTrackers (itself also filter events by ids).
+ // 3nd filter: for LogMatchingTrackers that matched this event, we pass this event to the
+ // ConditionTrackers and MetricProducers that use this matcher.
+ // 4th filter: for ConditionTrackers that changed value due to this event, we pass
+ // new conditions to metrics that use this condition.
+
+ // The following map is initialized from the statsd_config.
+
+ // maps from the index of the LogMatchingTracker to index of MetricProducer.
+ std::unordered_map<int, std::vector<int>> mTrackerToMetricMap;
+
+ // maps from LogMatchingTracker to ConditionTracker
+ std::unordered_map<int, std::vector<int>> mTrackerToConditionMap;
+
+ // maps from ConditionTracker to MetricProducer
+ std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
+
+ bool mConfigValid;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
new file mode 100644
index 0000000..6fdd228
--- /dev/null
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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 "../condition/CombinationConditionTracker.h"
+#include "../condition/SimpleConditionTracker.h"
+#include "../matchers/CombinationLogMatchingTracker.h"
+#include "../matchers/SimpleLogMatchingTracker.h"
+#include "CountMetricProducer.h"
+#include "stats_util.h"
+
+using std::set;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap,
+ vector<sp<LogMatchingTracker>>& allLogEntryMatchers, set<int>& allTagIds) {
+ vector<LogEntryMatcher> matcherConfigs;
+
+ for (int i = 0; i < config.log_entry_matcher_size(); i++) {
+ const LogEntryMatcher& logMatcher = config.log_entry_matcher(i);
+
+ int index = allLogEntryMatchers.size();
+ switch (logMatcher.contents_case()) {
+ case LogEntryMatcher::ContentsCase::kSimpleLogEntryMatcher:
+ allLogEntryMatchers.push_back(new SimpleLogMatchingTracker(
+ logMatcher.name(), index, logMatcher.simple_log_entry_matcher()));
+ break;
+ case LogEntryMatcher::ContentsCase::kCombination:
+ allLogEntryMatchers.push_back(
+ new CombinationLogMatchingTracker(logMatcher.name(), index));
+ break;
+ default:
+ ALOGE("Matcher %s malformed", logMatcher.name().c_str());
+ return false;
+ // continue;
+ }
+ if (logTrackerMap.find(logMatcher.name()) != logTrackerMap.end()) {
+ ALOGE("Duplicate LogEntryMatcher found!");
+ return false;
+ }
+ logTrackerMap[logMatcher.name()] = index;
+ matcherConfigs.push_back(logMatcher);
+ }
+
+ vector<bool> stackTracker2(allLogEntryMatchers.size(), false);
+ for (auto& matcher : allLogEntryMatchers) {
+ if (!matcher->init(matcherConfigs, allLogEntryMatchers, logTrackerMap, stackTracker2)) {
+ return false;
+ }
+ // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
+ const set<int>& tagIds = matcher->getTagIds();
+ allTagIds.insert(tagIds.begin(), tagIds.end());
+ }
+ return true;
+}
+
+bool initConditions(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
+ unordered_map<string, int>& conditionTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ unordered_map<int, std::vector<int>>& trackerToConditionMap) {
+ vector<Condition> conditionConfigs;
+
+ for (int i = 0; i < config.condition_size(); i++) {
+ const Condition& condition = config.condition(i);
+ int index = allConditionTrackers.size();
+ switch (condition.contents_case()) {
+ case Condition::ContentsCase::kSimpleCondition: {
+ allConditionTrackers.push_back(new SimpleConditionTracker(
+ condition.name(), index, condition.simple_condition(), logTrackerMap));
+ break;
+ }
+ case Condition::ContentsCase::kCombination: {
+ allConditionTrackers.push_back(
+ new CombinationConditionTracker(condition.name(), index));
+ break;
+ }
+ default:
+ ALOGE("Condition %s malformed", condition.name().c_str());
+ return false;
+ }
+ if (conditionTrackerMap.find(condition.name()) != conditionTrackerMap.end()) {
+ ALOGE("Duplicate Condition found!");
+ return false;
+ }
+ conditionTrackerMap[condition.name()] = index;
+ conditionConfigs.push_back(condition);
+ }
+
+ vector<bool> stackTracker(allConditionTrackers.size(), false);
+ for (size_t i = 0; i < allConditionTrackers.size(); i++) {
+ auto& conditionTracker = allConditionTrackers[i];
+ if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap,
+ stackTracker)) {
+ return false;
+ }
+ for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) {
+ auto& conditionList = trackerToConditionMap[trackerIndex];
+ conditionList.push_back(i);
+ }
+ }
+ return true;
+}
+
+bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
+ const unordered_map<string, int>& conditionTrackerMap,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ unordered_map<int, std::vector<int>>& trackerToMetricMap) {
+ // Build MetricProducers for each metric defined in config.
+ // (1) build CountMetricProducer
+ for (int i = 0; i < config.count_metric_size(); i++) {
+ const CountMetric& metric = config.count_metric(i);
+ if (!metric.has_what()) {
+ ALOGW("cannot find what in CountMetric %lld", metric.metric_id());
+ return false;
+ }
+
+ auto logTrackerIt = logTrackerMap.find(metric.what());
+ if (logTrackerIt == logTrackerMap.end()) {
+ ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str());
+ return false;
+ }
+
+ sp<MetricProducer> countProducer;
+ int metricIndex = allMetricProducers.size();
+ if (metric.has_condition()) {
+ auto condition_it = conditionTrackerMap.find(metric.condition());
+ if (condition_it == conditionTrackerMap.end()) {
+ ALOGW("cannot find the Condition %s in the config", metric.condition().c_str());
+ return false;
+ }
+ countProducer = new CountMetricProducer(metric, true /*has condition*/);
+ // will create new vector if not exist before.
+ auto& metricList = conditionToMetricMap[condition_it->second];
+ metricList.push_back(metricIndex);
+ } else {
+ countProducer = new CountMetricProducer(metric, false /*no condition*/);
+ }
+
+ int logTrackerIndex = logTrackerIt->second;
+ auto& metric_list = trackerToMetricMap[logTrackerIndex];
+ metric_list.push_back(metricIndex);
+ allMetricProducers.push_back(countProducer);
+ }
+
+ // TODO: build other types of metrics too.
+
+ return true;
+}
+
+bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
+ vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ unordered_map<int, std::vector<int>>& trackerToConditionMap) {
+ unordered_map<string, int> logTrackerMap;
+ unordered_map<string, int> conditionTrackerMap;
+
+ if (!initLogTrackers(config, logTrackerMap, allLogEntryMatchers, allTagIds)) {
+ ALOGE("initLogMatchingTrackers failed");
+ return false;
+ }
+
+ if (!initConditions(config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
+ trackerToConditionMap)) {
+ ALOGE("initConditionTrackers failed");
+ return false;
+ }
+
+ if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allMetricProducers,
+ conditionToMetricMap, trackerToMetricMap)) {
+ ALOGE("initMetricProducers failed");
+ return false;
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
new file mode 100644
index 0000000..5f1f295
--- /dev/null
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -0,0 +1,94 @@
+/*
+ * 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_UTIL_H
+#define METRIC_UTIL_H
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "../condition/ConditionTracker.h"
+#include "../matchers/LogMatchingTracker.h"
+#include "CountMetricProducer.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Helper functions for MetricsManager to initialize from StatsdConfig.
+// *Note*: only initStatsdConfig() should be called from outside.
+// All other functions are intermediate
+// steps, created to make unit tests easier. And most of the parameters in these
+// functions are temporary objects in the initialization phase.
+
+// Initialize the LogMatchingTrackers.
+// input:
+// [config]: the input StatsdConfig
+// output:
+// [logTrackerMap]: this map should contain matcher name to index mapping
+// [allLogEntryMatchers]: should store the sp to all the LogMatchingTracker
+// [allTagIds]: contains the set of all interesting tag ids to this config.
+bool initLogTrackers(const StatsdConfig& config,
+ std::unordered_map<std::string, int>& logTrackerMap,
+ std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+ std::set<int>& allTagIds);
+
+// Initialize ConditionTrackers
+// input:
+// [config]: the input config
+// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
+// output:
+// [conditionTrackerMap]: this map should contain condition name to index mapping
+// [allConditionTrackers]: stores the sp to all the ConditionTrackers
+// [trackerToConditionMap]: contain the mapping from index of
+// log tracker to condition trackers that use the log tracker
+bool initConditions(const StatsdConfig& config,
+ const std::unordered_map<std::string, int>& logTrackerMap,
+ std::unordered_map<std::string, int>& conditionTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
+
+// Initialize MetricProducers.
+// input:
+// [config]: the input config
+// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
+// [conditionTrackerMap]: condition name to index mapping
+// output:
+// [allMetricProducers]: contains the list of sp to the MetricProducers created.
+// [conditionToMetricMap]: contains the mapping from condition tracker index to
+// the list of MetricProducer index
+// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
+bool initMetrics(const StatsdConfig& config,
+ const std::unordered_map<std::string, int>& logTrackerMap,
+ const std::unordered_map<std::string, int>& conditionTrackerMap,
+ std::vector<sp<MetricProducer>>& allMetricProducers,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap);
+
+// Initialize MetricManager from StatsdConfig.
+// Parameters are the members of MetricsManager. See MetricsManager for declaration.
+bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds,
+ std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ std::vector<sp<MetricProducer>>& allMetricProducers,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // METRIC_UTIL_H
diff --git a/cmds/statsd/src/parse_util.cpp b/cmds/statsd/src/parse_util.cpp
deleted file mode 100644
index 61421880..0000000
--- a/cmds/statsd/src/parse_util.cpp
+++ /dev/null
@@ -1,132 +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/log_event_list.h>
-#include <parse_util.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static inline uint32_t get4LE(const char* src) {
- return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
-int getTagId(log_msg msg) {
- return get4LE(msg.msg());
-}
-
-EventMetricData parse(log_msg msg) {
- // dump all statsd logs to dropbox for now.
- // TODO: Add filtering, aggregation, etc.
- EventMetricData eventMetricData;
-
- // set tag.
- int tag = getTagId(msg);
- // TODO: Replace the following line when we can serialize on the fly.
- //eventMetricData.set_tag(tag);
-
- // set timestamp of the event.
- eventMetricData.set_timestamp_nanos(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec);
-
- // start iterating k,v pairs.
- android_log_context context =
- create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t),
- const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t));
- android_log_list_element elem;
-
- if (context) {
- memset(&elem, 0, sizeof(elem));
- size_t index = 0;
- int32_t key = -1;
-
- do {
- elem = android_log_read_next(context);
- switch ((int)elem.type) {
- case EVENT_TYPE_INT:
- if (index % 2 == 0) {
- key = elem.data.int32;
- } else {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- int32_t val = elem.data.int32;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_int(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_FLOAT:
- if (index % 2 == 1) {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- float val = elem.data.float32;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_float(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_STRING:
- if (index % 2 == 1) {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- char* val = elem.data.string;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_str(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_LONG:
- if (index % 2 == 1) {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- int64_t val = elem.data.int64;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_int(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_LIST:
- break;
- case EVENT_TYPE_LIST_STOP:
- break;
- case EVENT_TYPE_UNKNOWN:
- break;
- default:
- elem.complete = true;
- break;
- }
-
- if (elem.complete) {
- break;
- }
- } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
-
- android_log_destroy(&context);
- }
-
- return eventMetricData;
-}
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 2dc0cc7..6421b70 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -55,6 +55,43 @@
repeated CountBucketInfo bucket_info = 2;
}
+message DurationBucketInfo {
+ optional int64 start_bucket_nanos = 1;
+
+ optional int64 end_bucket_nanos = 2;
+
+ optional int64 duration_nanos = 3;
+}
+
+message DurationMetricData {
+ repeated KeyValuePair dimension = 1;
+
+ repeated DurationBucketInfo bucket_info = 2;
+}
+
+message UidMapping {
+ message AppInfo {
+ optional string app = 1;
+
+ optional int32 version = 2;
+
+ optional int32 uid = 3;
+ }
+
+ repeated AppInfo initial = 1;
+
+ message Change {
+ optional bool deletion = 1;
+
+ optional int64 timestamp = 2;
+ optional string app = 3;
+ optional int32 uid = 4;
+
+ optional int32 version = 5;
+ }
+ repeated Change changes = 2;
+}
+
message StatsLogReport {
optional int32 metric_id = 1;
@@ -68,8 +105,12 @@
message CountMetricDataWrapper {
repeated CountMetricData data = 1;
}
+ message DurationMetricDataWrapper {
+ repeated CountMetricData data = 1;
+ }
oneof data {
EventMetricDataWrapper event_metrics = 4;
CountMetricDataWrapper count_metrics = 5;
+ DurationMetricDataWrapper duration_metrics = 6;
}
}
diff --git a/cmds/statsd/src/stats_util.cpp b/cmds/statsd/src/stats_util.cpp
new file mode 100644
index 0000000..978b228
--- /dev/null
+++ b/cmds/statsd/src/stats_util.cpp
@@ -0,0 +1,289 @@
+/*
+ * 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/log_event_list.h>
+#include "stats_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static inline uint32_t get4LE(const char* src) {
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+int getTagId(log_msg msg) {
+ return get4LE(msg.msg());
+}
+
+EventMetricData parse(log_msg msg) {
+ // dump all statsd logs to dropbox for now.
+ // TODO: Add filtering, aggregation, etc.
+ EventMetricData eventMetricData;
+
+ // set tag.
+ int tag = getTagId(msg);
+ // TODO: Replace the following line when we can serialize on the fly.
+ // eventMetricData.set_tag(tag);
+
+ // set timestamp of the event.
+ eventMetricData.set_timestamp_nanos(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec);
+
+ // start iterating k,v pairs.
+ android_log_context context =
+ create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t),
+ const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t));
+ android_log_list_element elem;
+
+ if (context) {
+ memset(&elem, 0, sizeof(elem));
+ size_t index = 0;
+ int32_t key = -1;
+
+ do {
+ elem = android_log_read_next(context);
+ switch ((int)elem.type) {
+ case EVENT_TYPE_INT:
+ if (index % 2 == 0) {
+ key = elem.data.int32;
+ } else {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
+ int32_t val = elem.data.int32;
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
+ keyValuePair->set_value_int(val);
+ */
+ }
+ index++;
+ break;
+ case EVENT_TYPE_FLOAT:
+ if (index % 2 == 1) {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
+ float val = elem.data.float32;
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
+ keyValuePair->set_value_float(val);
+ */
+ }
+ index++;
+ break;
+ case EVENT_TYPE_STRING:
+ if (index % 2 == 1) {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
+ char* val = elem.data.string;
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
+ keyValuePair->set_value_str(val);
+ */
+ }
+ index++;
+ break;
+ case EVENT_TYPE_LONG:
+ if (index % 2 == 1) {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
+ int64_t val = elem.data.int64;
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
+ keyValuePair->set_value_int(val);
+ */
+ }
+ index++;
+ break;
+ case EVENT_TYPE_LIST:
+ break;
+ case EVENT_TYPE_LIST_STOP:
+ break;
+ case EVENT_TYPE_UNKNOWN:
+ break;
+ default:
+ elem.complete = true;
+ break;
+ }
+
+ if (elem.complete) {
+ break;
+ }
+ } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
+
+ android_log_destroy(&context);
+ }
+
+ return eventMetricData;
+}
+
+StatsdConfig buildFakeConfig() {
+ // HACK: Hard code a test metric for counting screen on events...
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ // One count metric to count screen on
+ CountMetric* metric = config.add_count_metric();
+ metric->set_metric_id(20150717L);
+ metric->set_what("SCREEN_IS_ON");
+ metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+
+ // One count metric to count PHOTO_CHANGE_OR_CHROME_CRASH
+ metric = config.add_count_metric();
+ metric->set_metric_id(20150718L);
+ metric->set_what("PHOTO_PROCESS_STATE_CHANGE");
+ metric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
+ metric->set_condition("SCREEN_IS_ON");
+
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_OFF");
+
+ simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+
+
+ LogEntryMatcher* procEventMatcher = config.add_log_entry_matcher();
+ procEventMatcher->set_name("PHOTO_CRASH");
+
+ SimpleLogEntryMatcher* simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/);
+ KeyValueMatcher* keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1002 /*pkg*/);
+ keyValueMatcher->set_eq_string(
+ "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ keyValueMatcher->set_eq_int(2);
+
+
+ procEventMatcher = config.add_log_entry_matcher();
+ procEventMatcher->set_name("PHOTO_START");
+
+ simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/);
+ keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1002 /*pkg*/);
+ keyValueMatcher->set_eq_string(
+ "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1 /*STATE*/);
+ keyValueMatcher->set_eq_int(1);
+
+
+ procEventMatcher = config.add_log_entry_matcher();
+ procEventMatcher->set_name("PHOTO_PROCESS_STATE_CHANGE");
+ LogEntryMatcher_Combination* combinationMatcher = procEventMatcher->mutable_combination();
+ combinationMatcher->set_operation(LogicalOperation::OR);
+ combinationMatcher->add_matcher("PHOTO_START");
+ combinationMatcher->add_matcher("PHOTO_CRASH");
+
+
+ procEventMatcher = config.add_log_entry_matcher();
+ procEventMatcher->set_name("CHROME_CRASH");
+
+ simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/);
+ keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1002 /*pkg*/);
+ keyValueMatcher->set_eq_string(
+ "com.android.chrome" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1 /*STATE*/);
+ keyValueMatcher->set_eq_int(2);
+
+
+
+ procEventMatcher = config.add_log_entry_matcher();
+ procEventMatcher->set_name("PHOTO_CHANGE_OR_CHROME_CRASH");
+ combinationMatcher = procEventMatcher->mutable_combination();
+ combinationMatcher->set_operation(LogicalOperation::OR);
+ combinationMatcher->add_matcher("PHOTO_PROCESS_STATE_CHANGE");
+ combinationMatcher->add_matcher("CHROME_CRASH");
+
+
+
+ Condition* condition = config.add_condition();
+ condition->set_name("SCREEN_IS_ON");
+ SimpleCondition* simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("SCREEN_IS_ON");
+ simpleCondition->set_stop("SCREEN_IS_OFF");
+
+
+ condition = config.add_condition();
+ condition->set_name("PHOTO_STARTED");
+
+ simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("PHOTO_START");
+ simpleCondition->set_stop("PHOTO_CRASH");
+
+
+ condition = config.add_condition();
+ condition->set_name("SCREEN_IS_OFF");
+
+ simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("SCREEN_IS_OFF");
+ simpleCondition->set_stop("SCREEN_IS_ON");
+
+
+ condition = config.add_condition();
+ condition->set_name("SCREEN_IS_EITHER_ON_OFF");
+
+ Condition_Combination* combination = condition->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_condition("SCREEN_IS_ON");
+ combination->add_condition("SCREEN_IS_OFF");
+
+
+ condition = config.add_condition();
+ condition->set_name("SCREEN_IS_NEITHER_ON_OFF");
+
+ combination = condition->mutable_combination();
+ combination->set_operation(LogicalOperation::NOR);
+ combination->add_condition("SCREEN_IS_ON");
+ combination->add_condition("SCREEN_IS_OFF");
+
+ return config;
+}
+
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/parse_util.h b/cmds/statsd/src/stats_util.h
similarity index 90%
rename from cmds/statsd/src/parse_util.h
rename to cmds/statsd/src/stats_util.h
index 8b82e7b..25b9bba 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -20,6 +20,7 @@
#include "LogReader.h"
#include <log/logprint.h>
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
namespace os {
@@ -29,6 +30,8 @@
int getTagId(log_msg msg);
+StatsdConfig buildFakeConfig();
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 3e4ebaf..d7702cd 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -65,7 +65,8 @@
message Combination {
optional LogicalOperation operation = 1;
- repeated LogEntryMatcher matcher = 2;
+
+ repeated string matcher = 2;
}
oneof contents {
SimpleLogEntryMatcher simple_log_entry_matcher = 2;
@@ -122,6 +123,24 @@
optional Bucket bucket = 5;
}
+message DurationMetric {
+ optional int64 metric_id = 1;
+
+ enum AggregationType {
+ DURATION_SUM = 1;
+
+ DURATION_MAX_SPARSE = 2;
+ DURATION_MIN_SPARSE = 3;
+ }
+ optional AggregationType type = 2;
+
+ optional string predicate = 3;
+
+ repeated KeyMatcher dimension = 4;
+
+ optional Bucket bucket = 5;
+}
+
message StatsdConfig {
optional int64 config_id = 1;
diff --git a/cmds/statsd/tests/ConditionTracker_test.cpp b/cmds/statsd/tests/ConditionTracker_test.cpp
new file mode 100644
index 0000000..f8b0fd0
--- /dev/null
+++ b/cmds/statsd/tests/ConditionTracker_test.cpp
@@ -0,0 +1,162 @@
+// 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 LOG_TAG "statsd_test"
+
+#include <gtest/gtest.h>
+#include "../src/condition/condition_util.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <stdio.h>
+#include <vector>
+
+using namespace android::os::statsd;
+using std::vector;
+
+
+#ifdef __ANDROID__
+TEST(ConditionTrackerTest, TestUnknownCondition) {
+ LogicalOperation operation = LogicalOperation::AND;
+
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+ children.push_back(2);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kUnknown);
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kTrue);
+
+ EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults),
+ ConditionState::kUnknown);
+}
+TEST(ConditionTrackerTest, TestAndCondition) {
+ // Set up the matcher
+ LogicalOperation operation = LogicalOperation::AND;
+
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+ children.push_back(2);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kTrue);
+
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kTrue);
+
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestOrCondition) {
+ // Set up the matcher
+ LogicalOperation operation = LogicalOperation::OR;
+
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+ children.push_back(2);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kTrue);
+
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kFalse);
+
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestNotCondition) {
+ // Set up the matcher
+ LogicalOperation operation = LogicalOperation::NOT;
+
+ vector<int> children;
+ children.push_back(0);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kTrue);
+
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kFalse);
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestNandCondition) {
+ // Set up the matcher
+ LogicalOperation operation = LogicalOperation::NAND;
+
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kFalse);
+
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kFalse);
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kTrue);
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestNorCondition) {
+ // Set up the matcher
+ LogicalOperation operation = LogicalOperation::NOR;
+
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kFalse);
+
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kFalse);
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kTrue);
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 473704a..6069801 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -18,14 +18,15 @@
#include <log/log_event_list.h>
#include <log/log_read.h>
#include <log/logprint.h>
-#include "../src/matchers/LogEntryMatcherManager.h"
-#include "../src/parse_util.h"
+#include "../src/matchers/matcher_util.h"
+#include "../src/stats_util.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include <stdio.h>
using namespace android::os::statsd;
using std::unordered_map;
+using std::vector;
const int kTagIdWakelock = 123;
const int kKeyIdState = 45;
@@ -41,7 +42,7 @@
LogEventWrapper wrapper;
wrapper.tagId = kTagIdWakelock;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestBoolMatcher) {
@@ -57,13 +58,13 @@
keyValue->set_eq_bool(true);
wrapper.boolMap[kKeyIdState] = true;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
keyValue->set_eq_bool(false);
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
- wrapper.boolMap[kTagIdWakelock] = false;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.boolMap[kKeyIdState] = false;
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestStringMatcher) {
@@ -80,7 +81,7 @@
wrapper.strMap[kKeyIdState] = "wakelock_name";
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
@@ -96,19 +97,19 @@
keyValue->set_lt_int(10);
wrapper.intMap[kKeyIdState] = 11;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 10;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 9;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
keyValue->set_gt_int(10);
wrapper.intMap[kKeyIdState] = 11;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 10;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 9;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestIntWithEqualityComparisonMatcher) {
@@ -124,19 +125,19 @@
keyValue->set_lte_int(10);
wrapper.intMap[kKeyIdState] = 11;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 10;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 9;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
keyValue->set_gte_int(10);
wrapper.intMap[kKeyIdState] = 11;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 10;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 9;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) {
@@ -152,15 +153,15 @@
keyValue->set_lt_float(10.0);
wrapper.floatMap[kKeyIdState] = 10.1;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
wrapper.floatMap[kKeyIdState] = 9.9;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
keyValue->set_gt_float(10.0);
wrapper.floatMap[kKeyIdState] = 10.1;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
wrapper.floatMap[kKeyIdState] = 9.9;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
}
// Helper for the composite matchers.
@@ -173,141 +174,117 @@
TEST(LogEntryMatcherTest, TestAndMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::AND);
+ LogicalOperation operation = LogicalOperation::AND;
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdPackageVersion, 4);
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+ children.push_back(2);
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
+ vector<MatchingState> matcherResults;
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kMatched);
- wrapper.intMap[1003] = 4;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap.clear();
- wrapper.intMap[1] = 3;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap.clear();
- wrapper.intMap[1] = 3;
- wrapper.intMap[1003] = 4;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kMatched);
+
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
}
TEST(LogEntryMatcherTest, TestOrMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::OR);
+ LogicalOperation operation = LogicalOperation::OR;
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdPackageVersion, 4);
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+ children.push_back(2);
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
+ vector<MatchingState> matcherResults;
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kMatched);
- // Don't set any key-value pairs.
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[1003] = 4;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap.clear();
- wrapper.intMap[1] = 3;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap.clear();
- wrapper.intMap[1] = 3;
- wrapper.intMap[1003] = 4;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
}
TEST(LogEntryMatcherTest, TestNotMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::NOT);
+ LogicalOperation operation = LogicalOperation::NOT;
- // Define first simpleMatcher
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
+ vector<int> children;
+ children.push_back(0);
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
+ vector<MatchingState> matcherResults;
+ matcherResults.push_back(MatchingState::kMatched);
- // Don't set any key-value pairs.
- wrapper.intMap[kKeyIdState] = 3;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kNotMatched);
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
}
-TEST(LogEntryMatcherTest, TestNANDMatcher) {
+TEST(LogEntryMatcherTest, TestNandMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::NAND);
+ LogicalOperation operation = LogicalOperation::NAND;
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdPackageVersion, 4);
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
+ vector<MatchingState> matcherResults;
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
- // Don't set any key-value pairs.
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdState] = 3;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdPackageVersion] = 4;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kMatched);
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
}
-TEST(LogEntryMatcherTest, TestNORMatcher) {
+TEST(LogEntryMatcherTest, TestNorMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::NOR);
+ LogicalOperation operation = LogicalOperation::NOR;
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdPackageVersion, 4);
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
+ vector<MatchingState> matcherResults;
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
- // Don't set any key-value pairs.
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdState] = 3;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdPackageVersion] = 4;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
-}
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
-// Tests that a NOT on top of AND is the same as NAND
-TEST(LogEntryMatcherTest, TestMultipleLayerMatcher) {
- LogEntryMatcher matcher;
- auto not_combination = matcher.mutable_combination();
- not_combination->set_operation(LogicalOperation::NOT);
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
- // Now add the AND
- auto combination = not_combination->add_matcher()->mutable_combination();
- combination->set_operation(LogicalOperation::AND);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdPackageVersion, 4);
-
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
-
- // Don't set any key-value pairs.
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdState] = 3;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdPackageVersion] = 4;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kMatched);
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
}
#else
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
new file mode 100644
index 0000000..673c156
--- /dev/null
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -0,0 +1,231 @@
+// 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 LOG_TAG "statsd_test"
+
+#include <gtest/gtest.h>
+#include "../src/condition/ConditionTracker.h"
+#include "../src/matchers/LogMatchingTracker.h"
+#include "../src/metrics/CountMetricProducer.h"
+#include "../src/metrics/MetricProducer.h"
+#include "../src/metrics/metrics_manager_util.h"
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <stdio.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+using namespace android::os::statsd;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+// TODO: ADD MORE TEST CASES.
+
+StatsdConfig buildGoodConfig() {
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_OFF");
+
+ simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_ON_OR_OFF");
+
+ LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher("SCREEN_IS_ON");
+ combination->add_matcher("SCREEN_IS_OFF");
+
+ return config;
+}
+
+StatsdConfig buildCircleMatchers() {
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_ON_OR_OFF");
+
+ LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher("SCREEN_IS_ON");
+ // Circle dependency
+ combination->add_matcher("SCREEN_ON_OR_OFF");
+
+ return config;
+}
+
+StatsdConfig buildMissingMatchers() {
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_ON_OR_OFF");
+
+ LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher("SCREEN_IS_ON");
+ // undefined matcher
+ combination->add_matcher("ABC");
+
+ return config;
+}
+
+StatsdConfig buildCircleConditions() {
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_OFF");
+
+ simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+ Condition* condition = config.add_condition();
+ condition->set_name("SCREEN_IS_ON");
+ SimpleCondition* simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("SCREEN_IS_ON");
+ simpleCondition->set_stop("SCREEN_IS_OFF");
+
+ condition = config.add_condition();
+ condition->set_name("SCREEN_IS_EITHER_ON_OFF");
+
+ Condition_Combination* combination = condition->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_condition("SCREEN_IS_ON");
+ combination->add_condition("SCREEN_IS_EITHER_ON_OFF");
+
+ return config;
+}
+
+TEST(MetricsManagerTest, TestGoodConfig) {
+ StatsdConfig config = buildGoodConfig();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
+ StatsdConfig config = buildCircleMatchers();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, TestMissingMatchers) {
+ StatsdConfig config = buildMissingMatchers();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, TestCircleConditionDependency) {
+ StatsdConfig config = buildCircleConditions();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif