One of the unit tests was flaky because mStopAllLogMatcherIndex may be greater than the size of eventMatcherValues, so we access an element that is greater than the vector size. Test: Check unit-tests still work. Flake is a bit hard to find. Change-Id: Ib177cd0ae00bbe7aa6982c6ec31d9094253a9c10
288 lines
11 KiB
C++
288 lines
11 KiB
C++
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#define DEBUG false // STOPSHIP if true
|
|
#include "Log.h"
|
|
|
|
#include "SimpleConditionTracker.h"
|
|
|
|
#include <log/logprint.h>
|
|
|
|
namespace android {
|
|
namespace os {
|
|
namespace statsd {
|
|
|
|
using std::map;
|
|
using std::string;
|
|
using std::unique_ptr;
|
|
using std::unordered_map;
|
|
using std::vector;
|
|
|
|
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(mStopLogMatcherIndex);
|
|
} else {
|
|
mStopLogMatcherIndex = -1;
|
|
}
|
|
|
|
if (simpleCondition.has_stop_all()) {
|
|
auto pair = trackerNameIndexMap.find(simpleCondition.stop_all());
|
|
if (pair == trackerNameIndexMap.end()) {
|
|
ALOGW("Stop all matcher %s not found in the config", simpleCondition.stop().c_str());
|
|
return;
|
|
}
|
|
mStopAllLogMatcherIndex = pair->second;
|
|
mTrackerIndex.insert(mStopAllLogMatcherIndex);
|
|
} else {
|
|
mStopAllLogMatcherIndex = -1;
|
|
}
|
|
|
|
mOutputDimension.insert(mOutputDimension.begin(), simpleCondition.dimension().begin(),
|
|
simpleCondition.dimension().end());
|
|
|
|
if (mOutputDimension.size() > 0) {
|
|
mSliced = true;
|
|
}
|
|
|
|
if (simpleCondition.initial_value() == SimpleCondition_InitialValue_FALSE) {
|
|
mInitialValue = ConditionState::kFalse;
|
|
} else {
|
|
mInitialValue = ConditionState::kUnknown;
|
|
}
|
|
|
|
mNonSlicedConditionState = mInitialValue;
|
|
|
|
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;
|
|
}
|
|
|
|
void print(map<HashableDimensionKey, int>& conditions, const string& name) {
|
|
VLOG("%s DUMP:", name.c_str());
|
|
for (const auto& pair : conditions) {
|
|
VLOG("\t%s : %d", pair.first.c_str(), pair.second);
|
|
}
|
|
}
|
|
|
|
void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
|
|
std::vector<bool>& conditionChangedCache) {
|
|
// Unless the default condition is false, and there was nothing started, otherwise we have
|
|
// triggered a condition change.
|
|
conditionChangedCache[mIndex] =
|
|
(mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
|
|
: true;
|
|
|
|
// After StopAll, we know everything has stopped. From now on, default condition is false.
|
|
mInitialValue = ConditionState::kFalse;
|
|
mSlicedConditionState.clear();
|
|
conditionCache[mIndex] = ConditionState::kFalse;
|
|
}
|
|
|
|
void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
|
|
bool matchStart,
|
|
std::vector<ConditionState>& conditionCache,
|
|
std::vector<bool>& conditionChangedCache) {
|
|
bool changed = false;
|
|
auto outputIt = mSlicedConditionState.find(outputKey);
|
|
ConditionState newCondition;
|
|
if (outputIt == mSlicedConditionState.end()) {
|
|
// We get a new output key.
|
|
newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
|
|
if (matchStart && mInitialValue != ConditionState::kTrue) {
|
|
mSlicedConditionState[outputKey] = 1;
|
|
changed = true;
|
|
} else if (mInitialValue != ConditionState::kFalse) {
|
|
// it's a stop and we don't have history about it.
|
|
// If the default condition is not false, it means this stop is valuable to us.
|
|
mSlicedConditionState[outputKey] = 0;
|
|
changed = true;
|
|
}
|
|
} else {
|
|
// we have history about this output key.
|
|
auto& startedCount = outputIt->second;
|
|
// assign the old value first.
|
|
newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
|
|
if (matchStart) {
|
|
if (startedCount == 0) {
|
|
// This condition for this output key will change from false -> true
|
|
changed = true;
|
|
}
|
|
|
|
// it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
|
|
// as 1 if not counting nesting.
|
|
startedCount++;
|
|
newCondition = ConditionState::kTrue;
|
|
} else {
|
|
// This is a stop event.
|
|
if (startedCount > 0) {
|
|
if (mCountNesting) {
|
|
startedCount--;
|
|
if (startedCount == 0) {
|
|
newCondition = ConditionState::kFalse;
|
|
}
|
|
} else {
|
|
// not counting nesting, so ignore the number of starts, stop now.
|
|
startedCount = 0;
|
|
newCondition = ConditionState::kFalse;
|
|
}
|
|
// if everything has stopped for this output key, condition true -> false;
|
|
if (startedCount == 0) {
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
// if default condition is false, it means we don't need to keep the false values.
|
|
if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
|
|
mSlicedConditionState.erase(outputIt);
|
|
VLOG("erase key %s", outputKey.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
// dump all dimensions for debugging
|
|
if (DEBUG) {
|
|
print(mSlicedConditionState, mName);
|
|
}
|
|
|
|
conditionChangedCache[mIndex] = changed;
|
|
conditionCache[mIndex] = newCondition;
|
|
|
|
VLOG("SimpleCondition %s nonSlicedChange? %d", mName.c_str(),
|
|
conditionChangedCache[mIndex] == true);
|
|
}
|
|
|
|
void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
|
|
const vector<MatchingState>& eventMatcherValues,
|
|
const vector<sp<ConditionTracker>>& mAllConditions,
|
|
vector<ConditionState>& conditionCache,
|
|
vector<bool>& conditionChangedCache) {
|
|
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
|
|
// it has been evaluated.
|
|
VLOG("Yes, already evaluated, %s %d", mName.c_str(), conditionCache[mIndex]);
|
|
return;
|
|
}
|
|
|
|
if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) &&
|
|
eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
|
|
handleStopAll(conditionCache, conditionChangedCache);
|
|
return;
|
|
}
|
|
|
|
int matchedState = -1;
|
|
// Note: The order to evaluate the following start, stop, stop_all matters.
|
|
// The priority of overwrite is stop_all > stop > start.
|
|
if (mStartLogMatcherIndex >= 0 &&
|
|
eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
|
|
matchedState = 1;
|
|
}
|
|
|
|
if (mStopLogMatcherIndex >= 0 &&
|
|
eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
|
|
matchedState = 0;
|
|
}
|
|
|
|
if (matchedState < 0) {
|
|
// The event doesn't match this condition. So we just report existing condition values.
|
|
conditionChangedCache[mIndex] = false;
|
|
if (mSliced) {
|
|
// if the condition result is sliced. metrics won't directly get value from the
|
|
// cache, so just set any value other than kNotEvaluated.
|
|
conditionCache[mIndex] = ConditionState::kUnknown;
|
|
} else if (mSlicedConditionState.find(DEFAULT_DIMENSION_KEY) ==
|
|
mSlicedConditionState.end()) {
|
|
// condition not sliced, but we haven't seen the matched start or stop yet. so return
|
|
// initial value.
|
|
conditionCache[mIndex] = mInitialValue;
|
|
} else {
|
|
// return the cached condition.
|
|
conditionCache[mIndex] = mSlicedConditionState[DEFAULT_DIMENSION_KEY] > 0
|
|
? ConditionState::kTrue
|
|
: ConditionState::kFalse;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// outputKey is the output key values. e.g, uid:1234
|
|
const HashableDimensionKey outputKey = getHashableKey(getDimensionKey(event, mOutputDimension));
|
|
handleConditionEvent(outputKey, matchedState == 1, conditionCache, conditionChangedCache);
|
|
}
|
|
|
|
void SimpleConditionTracker::isConditionMet(
|
|
const map<string, HashableDimensionKey>& conditionParameters,
|
|
const vector<sp<ConditionTracker>>& allConditions, vector<ConditionState>& conditionCache) {
|
|
const auto pair = conditionParameters.find(mName);
|
|
HashableDimensionKey key =
|
|
(pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second;
|
|
|
|
if (pair == conditionParameters.end() && mOutputDimension.size() > 0) {
|
|
ALOGE("Condition %s output has dimension, but it's not specified in the query!",
|
|
mName.c_str());
|
|
conditionCache[mIndex] = mInitialValue;
|
|
return;
|
|
}
|
|
|
|
VLOG("simpleCondition %s query key: %s", mName.c_str(), key.c_str());
|
|
|
|
auto startedCountIt = mSlicedConditionState.find(key);
|
|
if (startedCountIt == mSlicedConditionState.end()) {
|
|
conditionCache[mIndex] = mInitialValue;
|
|
} else {
|
|
conditionCache[mIndex] =
|
|
startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
|
|
}
|
|
|
|
VLOG("Condition %s return %d", mName.c_str(), conditionCache[mIndex]);
|
|
}
|
|
|
|
} // namespace statsd
|
|
} // namespace os
|
|
} // namespace android
|