1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define DEBUG false  // STOPSHIP if true
18 #include "Log.h"
19 #include "CombinationConditionTracker.h"
20 
21 #include <log/logprint.h>
22 
23 namespace android {
24 namespace os {
25 namespace statsd {
26 
27 using std::map;
28 using std::string;
29 using std::unique_ptr;
30 using std::unordered_map;
31 using std::vector;
32 
CombinationConditionTracker(const int64_t & id,const int index)33 CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index)
34     : ConditionTracker(id, index) {
35     VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
36 }
37 
~CombinationConditionTracker()38 CombinationConditionTracker::~CombinationConditionTracker() {
39     VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId);
40 }
41 
init(const vector<Predicate> & allConditionConfig,const vector<sp<ConditionTracker>> & allConditionTrackers,const unordered_map<int64_t,int> & conditionIdIndexMap,vector<bool> & stack)42 bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig,
43                                        const vector<sp<ConditionTracker>>& allConditionTrackers,
44                                        const unordered_map<int64_t, int>& conditionIdIndexMap,
45                                        vector<bool>& stack) {
46     VLOG("Combination predicate init() %lld", (long long)mConditionId);
47     if (mInitialized) {
48         return true;
49     }
50 
51     // mark this node as visited in the recursion stack.
52     stack[mIndex] = true;
53 
54     Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination();
55 
56     if (!combinationCondition.has_operation()) {
57         return false;
58     }
59     mLogicalOperation = combinationCondition.operation();
60 
61     if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) {
62         return false;
63     }
64 
65     for (auto child : combinationCondition.predicate()) {
66         auto it = conditionIdIndexMap.find(child);
67 
68         if (it == conditionIdIndexMap.end()) {
69             ALOGW("Predicate %lld not found in the config", (long long)child);
70             return false;
71         }
72 
73         int childIndex = it->second;
74         const auto& childTracker = allConditionTrackers[childIndex];
75         // if the child is a visited node in the recursion -> circle detected.
76         if (stack[childIndex]) {
77             ALOGW("Circle detected!!!");
78             return false;
79         }
80 
81 
82         bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
83                                                      conditionIdIndexMap, stack);
84 
85         if (!initChildSucceeded) {
86             ALOGW("Child initialization failed %lld ", (long long)child);
87             return false;
88         } else {
89             ALOGW("Child initialization success %lld ", (long long)child);
90         }
91 
92         if (allConditionTrackers[childIndex]->isSliced()) {
93             setSliced(true);
94             mSlicedChildren.push_back(childIndex);
95         } else {
96             mUnSlicedChildren.push_back(childIndex);
97         }
98         mChildren.push_back(childIndex);
99         mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
100                              childTracker->getLogTrackerIndex().end());
101     }
102 
103     // unmark this node in the recursion stack.
104     stack[mIndex] = false;
105 
106     mInitialized = true;
107 
108     return true;
109 }
110 
isConditionMet(const ConditionKey & conditionParameters,const vector<sp<ConditionTracker>> & allConditions,const std::vector<Matcher> & dimensionFields,const bool isSubOutputDimensionFields,const bool isPartialLink,vector<ConditionState> & conditionCache,std::unordered_set<HashableDimensionKey> & dimensionsKeySet) const111 void CombinationConditionTracker::isConditionMet(
112         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
113         const std::vector<Matcher>& dimensionFields,
114         const bool isSubOutputDimensionFields,
115         const bool isPartialLink,
116         vector<ConditionState>& conditionCache,
117         std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
118     // So far, this is fine as there is at most one child having sliced output.
119     for (const int childIndex : mChildren) {
120         if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
121             allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
122                                                       dimensionFields,
123                                                       isSubOutputDimensionFields,
124                                                       isPartialLink,
125                                                       conditionCache,
126                                                       dimensionsKeySet);
127         }
128     }
129     conditionCache[mIndex] =
130             evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
131 }
132 
evaluateCondition(const LogEvent & event,const std::vector<MatchingState> & eventMatcherValues,const std::vector<sp<ConditionTracker>> & mAllConditions,std::vector<ConditionState> & nonSlicedConditionCache,std::vector<bool> & conditionChangedCache)133 void CombinationConditionTracker::evaluateCondition(
134         const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
135         const std::vector<sp<ConditionTracker>>& mAllConditions,
136         std::vector<ConditionState>& nonSlicedConditionCache,
137         std::vector<bool>& conditionChangedCache) {
138     // value is up to date.
139     if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
140         return;
141     }
142 
143     for (const int childIndex : mChildren) {
144         // So far, this is fine as there is at most one child having sliced output.
145         if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
146             const sp<ConditionTracker>& child = mAllConditions[childIndex];
147             child->evaluateCondition(event, eventMatcherValues, mAllConditions,
148                                      nonSlicedConditionCache, conditionChangedCache);
149         }
150     }
151 
152     ConditionState newCondition =
153             evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
154     if (!mSliced) {
155 
156         bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
157         mNonSlicedConditionState = newCondition;
158 
159         nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
160 
161         conditionChangedCache[mIndex] = nonSlicedChanged;
162         mUnSlicedPart = newCondition;
163     } else {
164         mUnSlicedPart = evaluateCombinationCondition(
165             mUnSlicedChildren, mLogicalOperation, nonSlicedConditionCache);
166 
167         for (const int childIndex : mChildren) {
168             // If any of the sliced condition in children condition changes, the combination
169             // condition may be changed too.
170             if (conditionChangedCache[childIndex]) {
171                 conditionChangedCache[mIndex] = true;
172                 break;
173             }
174         }
175         nonSlicedConditionCache[mIndex] = newCondition;
176         VLOG("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId,
177             conditionChangedCache[mIndex] == true);
178     }
179 }
180 
getMetConditionDimension(const std::vector<sp<ConditionTracker>> & allConditions,const std::vector<Matcher> & dimensionFields,const bool isSubOutputDimensionFields,std::unordered_set<HashableDimensionKey> & dimensionsKeySet) const181 ConditionState CombinationConditionTracker::getMetConditionDimension(
182         const std::vector<sp<ConditionTracker>>& allConditions,
183         const std::vector<Matcher>& dimensionFields,
184         const bool isSubOutputDimensionFields,
185         std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
186     vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
187     // So far, this is fine as there is at most one child having sliced output.
188     for (const int childIndex : mChildren) {
189         conditionCache[childIndex] = conditionCache[childIndex] |
190             allConditions[childIndex]->getMetConditionDimension(
191                 allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
192     }
193     evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
194     if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
195         dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY);
196     }
197     return conditionCache[mIndex];
198 }
199 
equalOutputDimensions(const std::vector<sp<ConditionTracker>> & allConditions,const vector<Matcher> & dimensions) const200 bool CombinationConditionTracker::equalOutputDimensions(
201         const std::vector<sp<ConditionTracker>>& allConditions,
202         const vector<Matcher>& dimensions) const {
203     if (mSlicedChildren.size() != 1 ||
204         mSlicedChildren.front() >= (int)allConditions.size() ||
205         mLogicalOperation != LogicalOperation::AND) {
206         return false;
207     }
208     const sp<ConditionTracker>& slicedChild = allConditions.at(mSlicedChildren.front());
209     return slicedChild->equalOutputDimensions(allConditions, dimensions);
210 }
211 
212 }  // namespace statsd
213 }  // namespace os
214 }  // namespace android
215