Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 51f29ce4 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Make SimpleCondition satisfactorily complicated."

parents 3f6d0dca 967b2051
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -151,19 +151,19 @@ LOCAL_SRC_FILES := \
    $(statsd_common_src) \
    tests/AnomalyMonitor_test.cpp \
    tests/anomaly/AnomalyTracker_test.cpp \
    tests/ConditionTracker_test.cpp \
    tests/ConfigManager_test.cpp \
    tests/indexed_priority_queue_test.cpp \
    tests/LogEntryMatcher_test.cpp \
    tests/LogReader_test.cpp \
    tests/MetricsManager_test.cpp \
    tests/UidMap_test.cpp \
    tests/condition/CombinationConditionTracker_test.cpp \
    tests/condition/SimpleConditionTracker_test.cpp \
    tests/metrics/OringDurationTracker_test.cpp \
    tests/metrics/MaxDurationTracker_test.cpp \
    tests/metrics/CountMetricProducer_test.cpp \
    tests/metrics/EventMetricProducer_test.cpp


LOCAL_STATIC_LIBRARIES := \
    libgmock

+16 −18
Original line number Diff line number Diff line
@@ -115,25 +115,25 @@ void CombinationConditionTracker::isConditionMet(
            evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
}

bool CombinationConditionTracker::evaluateCondition(
void CombinationConditionTracker::evaluateCondition(
        const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
        const std::vector<sp<ConditionTracker>>& mAllConditions,
        std::vector<ConditionState>& nonSlicedConditionCache,
        std::vector<bool>& nonSlicedChangedCache, vector<bool>& slicedConditionChanged) {
        std::vector<bool>& conditionChangedCache) {
    // value is up to date.
    if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
        return false;
        return;
    }

    for (const int childIndex : mChildren) {
        if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
            const sp<ConditionTracker>& child = mAllConditions[childIndex];
            child->evaluateCondition(event, eventMatcherValues, mAllConditions,
                                     nonSlicedConditionCache, nonSlicedChangedCache,
                                     slicedConditionChanged);
                                     nonSlicedConditionCache, conditionChangedCache);
        }
    }

    if (!mSliced) {
        ConditionState newCondition =
                evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);

@@ -142,22 +142,20 @@ bool CombinationConditionTracker::evaluateCondition(

        nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;

    nonSlicedChangedCache[mIndex] = nonSlicedChanged;

    if (mSliced) {
        conditionChangedCache[mIndex] = nonSlicedChanged;
    } else {
        for (const int childIndex : mChildren) {
            // If any of the sliced condition in children condition changes, the combination
            // condition may be changed too.
            if (slicedConditionChanged[childIndex]) {
                slicedConditionChanged[mIndex] = true;
            if (conditionChangedCache[childIndex]) {
                conditionChangedCache[mIndex] = true;
                break;
            }
        }
        nonSlicedConditionCache[mIndex] = ConditionState::kUnknown;
        ALOGD("CombinationCondition %s sliced may changed? %d", mName.c_str(),
              slicedConditionChanged[mIndex] == true);
              conditionChangedCache[mIndex] == true);
    }

    return nonSlicedChanged;
}

}  // namespace statsd
+2 −3
Original line number Diff line number Diff line
@@ -35,12 +35,11 @@ public:
              const std::unordered_map<std::string, int>& conditionNameIndexMap,
              std::vector<bool>& stack) override;

    bool evaluateCondition(const LogEvent& event,
    void evaluateCondition(const LogEvent& event,
                           const std::vector<MatchingState>& eventMatcherValues,
                           const std::vector<sp<ConditionTracker>>& mAllConditions,
                           std::vector<ConditionState>& conditionCache,
                           std::vector<bool>& changedCache,
                           std::vector<bool>& slicedConditionMayChanged) override;
                           std::vector<bool>& changedCache) override;

    void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
                        const std::vector<sp<ConditionTracker>>& allConditions,
+5 −10
Original line number Diff line number Diff line
@@ -56,25 +56,20 @@ public:
                      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 non-sliced condition of the ConditionTrackers for this new event.
    // nonSlicedConditionChanged: the bit map to record whether non-sliced condition has changed.
    // slicedConditionMayChanged: the bit map to record whether sliced condition may have changed.
    //      Because sliced condition needs parameters to determine the value. So the sliced
    //      condition is not pushed to metrics. We only inform the relevant metrics that the sliced
    //      condition may have changed, and metrics should pull the conditions that they are
    //      interested in.
    virtual bool evaluateCondition(const LogEvent& event,
    // conditionChanged: the bit map to record whether the condition has changed.
    //                   If the condition has dimension, then any sub condition changes will report
    //                   conditionChanged.
    virtual void evaluateCondition(const LogEvent& event,
                                   const std::vector<MatchingState>& eventMatcherValues,
                                   const std::vector<sp<ConditionTracker>>& mAllConditions,
                                   std::vector<ConditionState>& conditionCache,
                                   std::vector<bool>& nonSlicedConditionChanged,
                                   std::vector<bool>& slicedConditionMayChanged) = 0;
                                   std::vector<bool>& conditionChanged) = 0;

    // Return the current condition state.
    virtual ConditionState isConditionMet() {
+129 −82
Original line number Diff line number Diff line
@@ -74,13 +74,21 @@ SimpleConditionTracker::SimpleConditionTracker(
        mStopAllLogMatcherIndex = -1;
    }

    mDimension.insert(mDimension.begin(), simpleCondition.dimension().begin(),
    mOutputDimension.insert(mOutputDimension.begin(), simpleCondition.dimension().begin(),
                            simpleCondition.dimension().end());

    if (mDimension.size() > 0) {
    if (mOutputDimension.size() > 0) {
        mSliced = true;
    }

    if (simpleCondition.initial_value() == SimpleCondition_InitialValue_FALSE) {
        mInitialValue = ConditionState::kFalse;
    } else {
        mInitialValue = ConditionState::kUnknown;
    }

    mNonSlicedConditionState = mInitialValue;

    mInitialized = true;
}

@@ -97,127 +105,166 @@ bool SimpleConditionTracker::init(const vector<Condition>& allConditionConfig,
    return mInitialized;
}

void print(unordered_map<HashableDimensionKey, ConditionState>& conditions, const string& name) {
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);
        VLOG("\t%s : %d", pair.first.c_str(), pair.second);
    }
}

bool SimpleConditionTracker::evaluateCondition(const LogEvent& event,
                                               const vector<MatchingState>& eventMatcherValues,
                                               const vector<sp<ConditionTracker>>& mAllConditions,
                                               vector<ConditionState>& conditionCache,
                                               vector<bool>& nonSlicedConditionChanged,
                                               std::vector<bool>& slicedConditionChanged) {
    if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
        // it has been evaluated.
        VLOG("Yes, already evaluated, %s %d", mName.c_str(), mNonSlicedConditionState);
        return false;
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;
}

    // Ignore nesting, because we know we cannot trust ourselves on tracking nesting conditions.

    ConditionState newCondition = mNonSlicedConditionState;
    bool matched = false;
    // 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) {
        matched = true;
        newCondition = ConditionState::kTrue;
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;
            }

    if (mStopLogMatcherIndex >= 0 &&
        eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
        matched = 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;
                    }

    bool stopAll = false;
    if (mStopAllLogMatcherIndex >= 0 &&
        eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
        matched = true;
                } else {
                    // not counting nesting, so ignore the number of starts, stop now.
                    startedCount = 0;
                    newCondition = ConditionState::kFalse;
        stopAll = true;
                }
                // if everything has stopped for this output key, condition true -> false;
                if (startedCount == 0) {
                    changed = true;
                }
            }

    if (matched == false) {
        slicedConditionChanged[mIndex] = false;
        nonSlicedConditionChanged[mIndex] = false;
        conditionCache[mIndex] = mNonSlicedConditionState;
        return false;
            // 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());
            }
        }
    }

    bool nonSlicedChanged = mNonSlicedConditionState != newCondition;
    // dump all dimensions for debugging
    if (DEBUG) {
        print(mSlicedConditionState, mName);
    }

    bool slicedChanged = false;
    conditionChangedCache[mIndex] = changed;
    conditionCache[mIndex] = newCondition;

    if (stopAll) {
        // TODO: handle stop all; all dimension should be cleared.
    VLOG("SimpleCondition %s nonSlicedChange? %d", mName.c_str(),
         conditionChangedCache[mIndex] == true);
}


    if (mDimension.size() > 0) {
        HashableDimensionKey hashableKey = getHashableKey(getDimensionKey(event, mDimension));
        if (mSlicedConditionState.find(hashableKey) == mSlicedConditionState.end() ||
            mSlicedConditionState[hashableKey] != newCondition) {
            slicedChanged = true;
            mSlicedConditionState[hashableKey] = newCondition;
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(), mNonSlicedConditionState);
        return;
    }
        VLOG("key: %s %d", hashableKey.c_str(), newCondition);
        // dump all dimensions for debugging
        if (DEBUG) {
            print(mSlicedConditionState, mName);

    if (mStopAllLogMatcherIndex >= 0 &&
        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;
    }

    // even if this SimpleCondition is not sliced, it may be part of a sliced CombinationCondition
    // if the nonSliced condition changed, it may affect the sliced condition in the parent node.
    // so mark the slicedConditionChanged to be true.
    // For example: APP_IN_BACKGROUND_OR_SCREEN_OFF
    //     APP_IN_BACKGROUND is sliced [App_A->True, App_B->False].
    //     SCREEN_OFF is not sliced, and it changes from False -> True;
    //     We need to populate this change to parent condition. Because for App_B,
    //     the APP_IN_BACKGROUND_OR_SCREEN_OFF condition would change from False->True.
    slicedConditionChanged[mIndex] = mSliced ? slicedChanged : nonSlicedChanged;
    nonSlicedConditionChanged[mIndex] = nonSlicedChanged;
    if (mStopLogMatcherIndex >= 0 &&
        eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
        matchedState = 0;
    }

    VLOG("SimpleCondition %s nonSlicedChange? %d  SlicedChanged? %d", mName.c_str(),
         nonSlicedConditionChanged[mIndex] == true, slicedConditionChanged[mIndex] == true);
    mNonSlicedConditionState = newCondition;
    if (matchedState < 0) {
        conditionChangedCache[mIndex] = false;
        conditionCache[mIndex] = mNonSlicedConditionState;
        return;
    }

    return nonSlicedConditionChanged[mIndex];
    // 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);
    if (pair == conditionParameters.end()) {
        // the query does not need my sliced condition. just return the non sliced condition.
        conditionCache[mIndex] = mNonSlicedConditionState;
        VLOG("Condition %s return %d", mName.c_str(), mNonSlicedConditionState);
    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;
    }

    const HashableDimensionKey& key = pair->second;
    VLOG("simpleCondition %s query key: %s", mName.c_str(), key.c_str());

    if (mSlicedConditionState.find(key) == mSlicedConditionState.end()) {
        // never seen this key before. the condition is unknown to us.
        conditionCache[mIndex] = ConditionState::kUnknown;
    auto startedCountIt = mSlicedConditionState.find(key);
    if (startedCountIt == mSlicedConditionState.end()) {
        conditionCache[mIndex] = mInitialValue;
    } else {
        conditionCache[mIndex] = mSlicedConditionState[key];
        conditionCache[mIndex] =
                startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
    }

    VLOG("Condition %s return %d", mName.c_str(), conditionCache[mIndex]);

    if (DEBUG) {
        print(mSlicedConditionState, mName);
    }
}

}  // namespace statsd
Loading