blob: c2044d85ec9fbebf1736f9b72f46e8b7e52412a7 [file] [log] [blame]
/*
* 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 "../external/StatsPullerManager.h"
#include "../matchers/CombinationLogMatchingTracker.h"
#include "../matchers/SimpleLogMatchingTracker.h"
#include "CountMetricProducer.h"
#include "DurationMetricProducer.h"
#include "EventMetricProducer.h"
#include "GaugeMetricProducer.h"
#include "ValueMetricProducer.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 handleMetricWithLogTrackers(const string what, const int metricIndex,
const bool usedForDimension,
const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
const unordered_map<string, int>& logTrackerMap,
unordered_map<int, std::vector<int>>& trackerToMetricMap,
int& logTrackerIndex) {
auto logTrackerIt = logTrackerMap.find(what);
if (logTrackerIt == logTrackerMap.end()) {
ALOGW("cannot find the LogEntryMatcher %s in config", what.c_str());
return false;
}
if (usedForDimension && allLogEntryMatchers[logTrackerIt->second]->getTagIds().size() > 1) {
ALOGE("LogEntryMatcher %s has more than one tag ids. When a metric has dimension, the "
"\"what\" can only about one atom type.",
what.c_str());
return false;
}
logTrackerIndex = logTrackerIt->second;
auto& metric_list = trackerToMetricMap[logTrackerIndex];
metric_list.push_back(metricIndex);
return true;
}
bool handleMetricWithConditions(
const string condition, const int metricIndex,
const unordered_map<string, int>& conditionTrackerMap,
const ::google::protobuf::RepeatedPtrField<::android::os::statsd::EventConditionLink>&
links,
vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
unordered_map<int, std::vector<int>>& conditionToMetricMap) {
auto condition_it = conditionTrackerMap.find(condition);
if (condition_it == conditionTrackerMap.end()) {
ALOGW("cannot find the Condition %s in the config", condition.c_str());
return false;
}
for (const auto& link : links) {
auto it = conditionTrackerMap.find(link.condition());
if (it == conditionTrackerMap.end()) {
ALOGW("cannot find the Condition %s in the config", link.condition().c_str());
return false;
}
allConditionTrackers[condition_it->second]->setSliced(true);
allConditionTrackers[it->second]->setSliced(true);
// TODO: We need to verify the link is valid.
}
conditionIndex = condition_it->second;
// will create new vector if not exist before.
auto& metricList = conditionToMetricMap[condition_it->second];
metricList.push_back(metricIndex);
return true;
}
bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap,
vector<sp<LogMatchingTracker>>& allLogEntryMatchers, set<int>& allTagIds) {
vector<LogEntryMatcher> matcherConfigs;
const int logEntryMatcherCount = config.log_entry_matcher_size();
matcherConfigs.reserve(logEntryMatcherCount);
allLogEntryMatchers.reserve(logEntryMatcherCount);
for (int i = 0; i < logEntryMatcherCount; 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;
const int conditionTrackerCount = config.condition_size();
conditionConfigs.reserve(conditionTrackerCount);
allConditionTrackers.reserve(conditionTrackerCount);
for (int i = 0; i < conditionTrackerCount; 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,
const 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) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
config.event_metric_size() + config.value_metric_size();
allMetricProducers.reserve(allMetricsCount);
StatsPullerManager statsPullerManager;
uint64_t startTimeNs = time(nullptr) * NS_PER_SEC;
// Build MetricProducers for each metric defined in config.
// 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 %s", metric.name().c_str());
return false;
}
int metricIndex = allMetricProducers.size();
int trackerIndex;
if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
trackerIndex)) {
return false;
}
int conditionIndex = -1;
if (metric.has_condition()) {
handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap);
}
sp<MetricProducer> countProducer =
new CountMetricProducer(metric, conditionIndex, wizard, startTimeNs);
allMetricProducers.push_back(countProducer);
}
// build DurationMetricProducer
for (int i = 0; i < config.duration_metric_size(); i++) {
int metricIndex = allMetricProducers.size();
const DurationMetric& metric = config.duration_metric(i);
auto what_it = conditionTrackerMap.find(metric.what());
if (what_it == conditionTrackerMap.end()) {
ALOGE("DurationMetric's \"what\" is invalid");
return false;
}
const Condition& durationWhat = config.condition(what_it->second);
if (durationWhat.contents_case() != Condition::ContentsCase::kSimpleCondition) {
ALOGE("DurationMetric's \"what\" must be a simple condition");
return false;
}
const auto& simpleCondition = durationWhat.simple_condition();
int trackerIndices[3] = {-1, -1, -1};
if (!simpleCondition.has_start() ||
!handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
metric.dimension_size() > 0, allLogEntryMatchers,
logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
ALOGE("Duration metrics must specify a valid the start event matcher");
return false;
}
if (simpleCondition.has_stop() &&
!handleMetricWithLogTrackers(simpleCondition.stop(), metricIndex,
metric.dimension_size() > 0, allLogEntryMatchers,
logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
return false;
}
if (simpleCondition.has_stop_all() &&
!handleMetricWithLogTrackers(simpleCondition.stop_all(), metricIndex,
metric.dimension_size() > 0, allLogEntryMatchers,
logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
return false;
}
vector<KeyMatcher> internalDimension;
internalDimension.insert(internalDimension.begin(), simpleCondition.dimension().begin(),
simpleCondition.dimension().end());
int conditionIndex = -1;
if (metric.has_predicate()) {
handleMetricWithConditions(metric.predicate(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap);
}
sp<MetricProducer> durationMetric = new DurationMetricProducer(
metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
wizard, internalDimension, startTimeNs);
allMetricProducers.push_back(durationMetric);
}
// build EventMetricProducer
for (int i = 0; i < config.event_metric_size(); i++) {
int metricIndex = allMetricProducers.size();
const EventMetric& metric = config.event_metric(i);
if (!metric.has_name() || !metric.has_what()) {
ALOGW("cannot find the metric name or what in config");
return false;
}
int trackerIndex;
if (!handleMetricWithLogTrackers(metric.what(), metricIndex, false, allLogEntryMatchers,
logTrackerMap, trackerToMetricMap, trackerIndex)) {
return false;
}
int conditionIndex = -1;
if (metric.has_condition()) {
handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap);
}
sp<MetricProducer> eventMetric =
new EventMetricProducer(metric, conditionIndex, wizard, startTimeNs);
allMetricProducers.push_back(eventMetric);
}
// build ValueMetricProducer
for (int i = 0; i < config.value_metric_size(); i++) {
const ValueMetric& metric = config.value_metric(i);
if (!metric.has_what()) {
ALOGW("cannot find what in ValueMetric %s", metric.name().c_str());
return false;
}
int metricIndex = allMetricProducers.size();
int trackerIndex;
if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
trackerIndex)) {
return false;
}
sp<LogMatchingTracker> atomMatcher = allLogEntryMatchers.at(trackerIndex);
// If it is pulled atom, it should be simple matcher with one tagId.
int pullTagId = -1;
for (int tagId : atomMatcher->getTagIds()) {
if (statsPullerManager.PullerForMatcherExists(tagId)) {
if (atomMatcher->getTagIds().size() != 1) {
return false;
}
pullTagId = tagId;
}
}
int conditionIndex = -1;
if (metric.has_condition()) {
handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap);
}
sp<MetricProducer> valueProducer =
new ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
allMetricProducers.push_back(valueProducer);
}
// Gauge metrics.
for (int i = 0; i < config.gauge_metric_size(); i++) {
const GaugeMetric& metric = config.gauge_metric(i);
if (!metric.has_what()) {
ALOGW("cannot find what in ValueMetric %s", metric.name().c_str());
return false;
}
int metricIndex = allMetricProducers.size();
int trackerIndex;
if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
trackerIndex)) {
return false;
}
sp<LogMatchingTracker> atomMatcher = allLogEntryMatchers.at(trackerIndex);
// If it is pulled atom, it should be simple matcher with one tagId.
int pullTagId = -1;
for (int tagId : atomMatcher->getTagIds()) {
if (statsPullerManager.PullerForMatcherExists(tagId)) {
if (atomMatcher->getTagIds().size() != 1) {
return false;
}
pullTagId = tagId;
}
}
int conditionIndex = -1;
if (metric.has_condition()) {
handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap);
}
sp<MetricProducer> gaugeProducer =
new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId);
allMetricProducers.push_back(gaugeProducer);
}
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;
}
ALOGD("initLogMatchingTrackers succeed...");
if (!initConditions(config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
trackerToConditionMap)) {
ALOGE("initConditionTrackers failed");
return false;
}
if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
allConditionTrackers, allMetricProducers, conditionToMetricMap,
trackerToMetricMap)) {
ALOGE("initMetricProducers failed");
return false;
}
return true;
}
} // namespace statsd
} // namespace os
} // namespace android