| /* | |
| * 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 STATSD_DEBUG false // STOPSHIP if true | |
| #include "Log.h" | |
| #include "CombinationConditionTracker.h" | |
| namespace android { | |
| namespace os { | |
| namespace statsd { | |
| using std::unordered_map; | |
| using std::vector; | |
| CombinationConditionTracker::CombinationConditionTracker(const int64_t id, const int index, | |
| const uint64_t protoHash) | |
| : ConditionTracker(id, index, protoHash) { | |
| VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId); | |
| } | |
| CombinationConditionTracker::~CombinationConditionTracker() { | |
| VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId); | |
| } | |
| optional<InvalidConfigReason> CombinationConditionTracker::init( | |
| const vector<Predicate>& allConditionConfig, | |
| const vector<sp<ConditionTracker>>& allConditionTrackers, | |
| const unordered_map<int64_t, int>& conditionIdIndexMap, vector<uint8_t>& stack, | |
| vector<ConditionState>& conditionCache) { | |
| VLOG("Combination predicate init() %lld", (long long)mConditionId); | |
| if (mInitialized) { | |
| // All the children are guaranteed to be initialized, but the recursion is needed to | |
| // fill the conditionCache properly, since another combination condition or metric | |
| // might rely on this. The recursion is needed to compute the current condition. | |
| // Init is called instead of isConditionMet so that the ConditionKey can be filled with the | |
| // default key for sliced conditions, since we do not know all indirect descendants here. | |
| for (const int childIndex : mChildren) { | |
| if (conditionCache[childIndex] == ConditionState::kNotEvaluated) { | |
| allConditionTrackers[childIndex]->init(allConditionConfig, allConditionTrackers, | |
| conditionIdIndexMap, stack, conditionCache); | |
| } | |
| } | |
| conditionCache[mIndex] = | |
| evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); | |
| return nullopt; | |
| } | |
| // mark this node as visited in the recursion stack. | |
| stack[mIndex] = true; | |
| Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination(); | |
| optional<InvalidConfigReason> invalidConfigReason; | |
| if (!combinationCondition.has_operation()) { | |
| return createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_CONDITION_NO_OPERATION, | |
| mConditionId); | |
| } | |
| mLogicalOperation = combinationCondition.operation(); | |
| if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) { | |
| return createInvalidConfigReasonWithPredicate( | |
| INVALID_CONFIG_REASON_CONDITION_NOT_OPERATION_IS_NOT_UNARY, mConditionId); | |
| } | |
| for (auto child : combinationCondition.predicate()) { | |
| auto it = conditionIdIndexMap.find(child); | |
| if (it == conditionIdIndexMap.end()) { | |
| ALOGW("Predicate %lld not found in the config", (long long)child); | |
| invalidConfigReason = createInvalidConfigReasonWithPredicate( | |
| INVALID_CONFIG_REASON_CONDITION_CHILD_NOT_FOUND, mConditionId); | |
| invalidConfigReason->conditionIds.push_back(child); | |
| return invalidConfigReason; | |
| } | |
| 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!!!"); | |
| invalidConfigReason = createInvalidConfigReasonWithPredicate( | |
| INVALID_CONFIG_REASON_CONDITION_CYCLE, mConditionId); | |
| invalidConfigReason->conditionIds.push_back(child); | |
| return invalidConfigReason; | |
| } | |
| invalidConfigReason = childTracker->init(allConditionConfig, allConditionTrackers, | |
| conditionIdIndexMap, stack, conditionCache); | |
| if (invalidConfigReason.has_value()) { | |
| ALOGW("Child initialization failed %lld ", (long long)child); | |
| invalidConfigReason->conditionIds.push_back(mConditionId); | |
| return invalidConfigReason; | |
| } else { | |
| VLOG("Child initialization success %lld ", (long long)child); | |
| } | |
| if (allConditionTrackers[childIndex]->isSliced()) { | |
| setSliced(true); | |
| mSlicedChildren.push_back(childIndex); | |
| } else { | |
| mUnSlicedChildren.push_back(childIndex); | |
| } | |
| mChildren.push_back(childIndex); | |
| mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(), | |
| childTracker->getAtomMatchingTrackerIndex().end()); | |
| } | |
| mUnSlicedPartCondition = | |
| evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, conditionCache); | |
| conditionCache[mIndex] = | |
| evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); | |
| // unmark this node in the recursion stack. | |
| stack[mIndex] = false; | |
| mInitialized = true; | |
| return nullopt; | |
| } | |
| optional<InvalidConfigReason> CombinationConditionTracker::onConfigUpdated( | |
| const vector<Predicate>& allConditionProtos, const int index, | |
| const vector<sp<ConditionTracker>>& allConditionTrackers, | |
| const unordered_map<int64_t, int>& atomMatchingTrackerMap, | |
| const unordered_map<int64_t, int>& conditionTrackerMap) { | |
| ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers, | |
| atomMatchingTrackerMap, conditionTrackerMap); | |
| mTrackerIndex.clear(); | |
| mChildren.clear(); | |
| mUnSlicedChildren.clear(); | |
| mSlicedChildren.clear(); | |
| Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination(); | |
| optional<InvalidConfigReason> invalidConfigReason; | |
| for (const int64_t child : combinationCondition.predicate()) { | |
| const auto& it = conditionTrackerMap.find(child); | |
| if (it == conditionTrackerMap.end()) { | |
| ALOGW("Predicate %lld not found in the config", (long long)child); | |
| invalidConfigReason = createInvalidConfigReasonWithPredicate( | |
| INVALID_CONFIG_REASON_CONDITION_CHILD_NOT_FOUND, mConditionId); | |
| invalidConfigReason->conditionIds.push_back(child); | |
| return invalidConfigReason; | |
| } | |
| int childIndex = it->second; | |
| const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex]; | |
| // Ensures that the child's tracker indices are updated. | |
| invalidConfigReason = | |
| childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers, | |
| atomMatchingTrackerMap, conditionTrackerMap); | |
| if (invalidConfigReason.has_value()) { | |
| ALOGW("Child update failed %lld ", (long long)child); | |
| invalidConfigReason->conditionIds.push_back(child); | |
| return invalidConfigReason; | |
| } | |
| if (allConditionTrackers[childIndex]->isSliced()) { | |
| mSlicedChildren.push_back(childIndex); | |
| } else { | |
| mUnSlicedChildren.push_back(childIndex); | |
| } | |
| mChildren.push_back(childIndex); | |
| mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(), | |
| childTracker->getAtomMatchingTrackerIndex().end()); | |
| } | |
| return nullopt; | |
| } | |
| void CombinationConditionTracker::isConditionMet( | |
| const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, | |
| const bool isPartialLink, | |
| vector<ConditionState>& conditionCache) const { | |
| // So far, this is fine as there is at most one child having sliced output. | |
| for (const int childIndex : mChildren) { | |
| if (conditionCache[childIndex] == ConditionState::kNotEvaluated) { | |
| allConditions[childIndex]->isConditionMet(conditionParameters, allConditions, | |
| isPartialLink, | |
| conditionCache); | |
| } | |
| } | |
| conditionCache[mIndex] = | |
| evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); | |
| } | |
| void CombinationConditionTracker::evaluateCondition( | |
| const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues, | |
| const std::vector<sp<ConditionTracker>>& mAllConditions, | |
| std::vector<ConditionState>& nonSlicedConditionCache, | |
| std::vector<uint8_t>& conditionChangedCache) { | |
| // value is up to date. | |
| if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) { | |
| return; | |
| } | |
| for (const int childIndex : mChildren) { | |
| // So far, this is fine as there is at most one child having sliced output. | |
| if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) { | |
| const sp<ConditionTracker>& child = mAllConditions[childIndex]; | |
| child->evaluateCondition(event, eventMatcherValues, mAllConditions, | |
| nonSlicedConditionCache, conditionChangedCache); | |
| } | |
| } | |
| ConditionState newCondition = | |
| evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache); | |
| if (!mSliced) { | |
| bool nonSlicedChanged = (mUnSlicedPartCondition != newCondition); | |
| mUnSlicedPartCondition = newCondition; | |
| nonSlicedConditionCache[mIndex] = mUnSlicedPartCondition; | |
| conditionChangedCache[mIndex] = nonSlicedChanged; | |
| } else { | |
| mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, | |
| nonSlicedConditionCache); | |
| for (const int childIndex : mChildren) { | |
| // If any of the sliced condition in children condition changes, the combination | |
| // condition may be changed too. | |
| if (conditionChangedCache[childIndex]) { | |
| conditionChangedCache[mIndex] = true; | |
| break; | |
| } | |
| } | |
| nonSlicedConditionCache[mIndex] = newCondition; | |
| VLOG("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId, | |
| conditionChangedCache[mIndex] == true); | |
| } | |
| } | |
| bool CombinationConditionTracker::equalOutputDimensions( | |
| const std::vector<sp<ConditionTracker>>& allConditions, | |
| const vector<Matcher>& dimensions) const { | |
| if (mSlicedChildren.size() != 1 || | |
| mSlicedChildren.front() >= (int)allConditions.size() || | |
| mLogicalOperation != LogicalOperation::AND) { | |
| return false; | |
| } | |
| const sp<ConditionTracker>& slicedChild = allConditions.at(mSlicedChildren.front()); | |
| return slicedChild->equalOutputDimensions(allConditions, dimensions); | |
| } | |
| } // namespace statsd | |
| } // namespace os | |
| } // namespace android |