diff options
33 files changed, 2377 insertions, 585 deletions
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index ca36d27cf684..24a598a5432a 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -42,24 +42,7 @@ LOCAL_MODULE := statsd 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 @@ LOCAL_SRC_FILES := \ ../../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 117fb5e2cefc..1d2f586c42dc 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() : m_dropbox_writer("all-logs") { 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 @@ void StatsLogProcessor::UpdateConfig(const int config_source, const StatsdConfig 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 88c63fa5a149..ab1b44eb6875 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 @@ private: 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 000000000000..6188383d4e8d --- /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 000000000000..38780e7062ed --- /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 000000000000..2da8fa0e8655 --- /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 000000000000..14ec72ef8aab --- /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 000000000000..41e17076cbdc --- /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 000000000000..cb07d1530dab --- /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 index b94d5abc2cf5..a4fcea38d6b5 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 000000000000..9f9b648ae1a5 --- /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 000000000000..51ee2328ee19 --- /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 000000000000..4244bd597b46 --- /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 000000000000..1c83039072da --- /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 000000000000..65dbe6476565 --- /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 index ab7b2b1dc57b..557c03228436 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 @@ LogEventWrapper LogEntryMatcherManager::parseLogEvent(log_msg msg) { 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 @@ LogEventWrapper LogEntryMatcherManager::parseLogEvent(log_msg msg) { 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; - } + } + break; + } + case LogicalOperation::OR: { + matched = false; + for (const int childIndex : children) { + if (matcherResults[childIndex] == MatchingState::kMatched) { + matched = true; + break; } - 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; - } + } + 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; } - 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; - } + } + break; + case LogicalOperation::NOR: + matched = true; + for (const int childIndex : children) { + if (matcherResults[childIndex] == MatchingState::kMatched) { + matched = false; + break; } - return true; - } - return false; - } else { - return matchesSimple(matcher.simple_log_entry_matcher(), event); + } + 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 @@ bool LogEntryMatcherManager::matchesSimple(const SimpleLogEntryMatcher& simpleMa 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 index fc8e6a1378a7..6d8e762382f0 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 @@ typedef struct { 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); +enum MatchingState { + kNotComputed = -1, + kNotMatched = 0, + kMatched = 1, +}; - static std::set<int> getTagIdsFromMatcher(const LogEntryMatcher& matcher); +LogEventWrapper parseLogEvent(log_msg msg); - static bool matches(const LogEntryMatcher& matcher, const LogEventWrapper& wrapper); +bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation, + const std::vector<MatchingState>& matcherResults); - static bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, - const LogEventWrapper& wrapper); -}; +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 684ffdb5883a..000000000000 --- 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 635777fe2267..e98999e73223 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 android { 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 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, 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 @@ CountMetricProducer::~CountMetricProducer() { 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 @@ void CountMetricProducer::onMatchedLogEvent(const LogEventWrapper& event) { 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 @@ void CountMetricProducer::flushCounterIfNeeded(const time_t& eventTime) { 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 7502320410ac..0729e2c45f58 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 @@ namespace statsd { 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 @@ public: 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 @@ private: 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 44a778b3d903..7d3d661ca433 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 @@ namespace statsd { // 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 cb7420683380..1e65f5888233 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 android { 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); + LogEventWrapper event = parseLogEvent(logMsg); + vector<MatchingState> matcherCache(mAllLogEntryMatchers.size(), MatchingState::kNotComputed); - // Evaluate the conditions. Order matters, this should happen - // before sending the event to metrics - for (auto& condition : mConditionTracker) { - condition->evaluateCondition(event); + for (auto& matcher : mAllLogEntryMatchers) { + matcher->onLogEvent(event, mAllLogEntryMatchers, matcherCache); + } + + // 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]); + } + } + } } - // 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); + // 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); } - } else { - // TODO: we should remove any redundant matchers that the config provides. - ALOGW("Matcher not used by any metrics."); } } } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 77d7535a1ba1..70c34db6b80a 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 @@ public: ~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? + + // Hold all the log entry matchers from the config. + std::vector<sp<LogMatchingTracker>> mAllLogEntryMatchers; + + // 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; - // The conditions that my metrics share. - std::vector<sp<ConditionTracker>> mConditionTracker; + // maps from ConditionTracker to MetricProducer + std::unordered_map<int, std::vector<int>> mConditionToMetricMap; - // the map from LogEntryMatcher names to the metrics that use this matcher. - std::unordered_map<std::string, std::vector<std::unique_ptr<MetricProducer>>> mLogMatchers; + 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 000000000000..6fdd228f4910 --- /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 000000000000..5f1f295d450a --- /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 61421880efd6..000000000000 --- 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 2dc0cc7b4a6b..6421b70f1f86 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -55,6 +55,43 @@ message CountMetricData { 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 StatsLogReport { 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 000000000000..978b228891b5 --- /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 index 8b82e7bc15a7..25b9bba56280 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 @@ EventMetricData parse(log_msg msg); 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 3e4ebaf06268..d7702cdd42ad 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -65,7 +65,8 @@ message LogEntryMatcher { 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 @@ message CountMetric { 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 000000000000..f8b0fd0796e7 --- /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 473704a7e7b9..606980178e2a 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 @@ TEST(LogEntryMatcherTest, TestSimpleMatcher) { LogEventWrapper wrapper; wrapper.tagId = kTagIdWakelock; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); } TEST(LogEntryMatcherTest, TestBoolMatcher) { @@ -57,13 +58,13 @@ TEST(LogEntryMatcherTest, TestBoolMatcher) { 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 @@ TEST(LogEntryMatcherTest, TestStringMatcher) { wrapper.strMap[kKeyIdState] = "wakelock_name"; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); } TEST(LogEntryMatcherTest, TestIntComparisonMatcher) { @@ -96,19 +97,19 @@ TEST(LogEntryMatcherTest, TestIntComparisonMatcher) { 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 @@ TEST(LogEntryMatcherTest, TestIntWithEqualityComparisonMatcher) { 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 @@ TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) { 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 @@ void addSimpleMatcher(SimpleLogEntryMatcher* simpleMatcher, int tag, int key, in 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); + + EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - 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)); + 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); + + EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - // 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)); + 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); + + EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - // Don't set any key-value pairs. - wrapper.intMap[kKeyIdState] = 3; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + 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)); -TEST(LogEntryMatcherTest, TestNORMatcher) { - // Set up the matcher - LogEntryMatcher matcher; - auto combination = matcher.mutable_combination(); - combination->set_operation(LogicalOperation::NOR); + matcherResults.clear(); + matcherResults.push_back(MatchingState::kNotMatched); + matcherResults.push_back(MatchingState::kNotMatched); + EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdState, 3); - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdPackageVersion, 4); + matcherResults.clear(); + matcherResults.push_back(MatchingState::kMatched); + matcherResults.push_back(MatchingState::kMatched); + EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); +} - LogEventWrapper wrapper; - wrapper.tagId = kTagIdWakelock; +TEST(LogEntryMatcherTest, TestNorMatcher) { + // Set up the matcher + LogicalOperation operation = LogicalOperation::NOR; - // 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)); -} + vector<int> children; + children.push_back(0); + children.push_back(1); -// 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); + vector<MatchingState> matcherResults; + matcherResults.push_back(MatchingState::kMatched); + matcherResults.push_back(MatchingState::kNotMatched); - // 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); + EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - LogEventWrapper wrapper; - wrapper.tagId = kTagIdWakelock; + matcherResults.clear(); + matcherResults.push_back(MatchingState::kNotMatched); + matcherResults.push_back(MatchingState::kNotMatched); + EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - // 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 000000000000..673c15686f71 --- /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 |