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 namespace android {
22 namespace os {
23 namespace statsd {
24 
25 using std::unordered_map;
26 using std::vector;
27 
CombinationConditionTracker(const int64_t & id,const int index)28 CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index)
29     : ConditionTracker(id, index) {
30     VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
31 }
32 
~CombinationConditionTracker()33 CombinationConditionTracker::~CombinationConditionTracker() {
34     VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId);
35 }
36 
init(const vector<Predicate> & allConditionConfig,const vector<sp<ConditionTracker>> & allConditionTrackers,const unordered_map<int64_t,int> & conditionIdIndexMap,vector<bool> & stack)37 bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig,
38                                        const vector<sp<ConditionTracker>>& allConditionTrackers,
39                                        const unordered_map<int64_t, int>& conditionIdIndexMap,
40                                        vector<bool>& stack) {
41     VLOG("Combination predicate init() %lld", (long long)mConditionId);
42     if (mInitialized) {
43         return true;
44     }
45 
46     // mark this node as visited in the recursion stack.
47     stack[mIndex] = true;
48 
49     Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination();
50 
51     if (!combinationCondition.has_operation()) {
52         return false;
53     }
54     mLogicalOperation = combinationCondition.operation();
55 
56     if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) {
57         return false;
58     }
59 
60     for (auto child : combinationCondition.predicate()) {
61         auto it = conditionIdIndexMap.find(child);
62 
63         if (it == conditionIdIndexMap.end()) {
64             ALOGW("Predicate %lld not found in the config", (long long)child);
65             return false;
66         }
67 
68         int childIndex = it->second;
69         const auto& childTracker = allConditionTrackers[childIndex];
70         // if the child is a visited node in the recursion -> circle detected.
71         if (stack[childIndex]) {
72             ALOGW("Circle detected!!!");
73             return false;
74         }
75 
76 
77         bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
78                                                      conditionIdIndexMap, stack);
79 
80         if (!initChildSucceeded) {
81             ALOGW("Child initialization failed %lld ", (long long)child);
82             return false;
83         } else {
84             ALOGW("Child initialization success %lld ", (long long)child);
85         }
86 
87         if (allConditionTrackers[childIndex]->isSliced()) {
88             setSliced(true);
89             mSlicedChildren.push_back(childIndex);
90         } else {
91             mUnSlicedChildren.push_back(childIndex);
92         }
93         mChildren.push_back(childIndex);
94         mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
95                              childTracker->getLogTrackerIndex().end());
96     }
97 
98     // unmark this node in the recursion stack.
99     stack[mIndex] = false;
100 
101     mInitialized = true;
102 
103     return true;
104 }
105 
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) const106 void CombinationConditionTracker::isConditionMet(
107         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
108         const std::vector<Matcher>& dimensionFields,
109         const bool isSubOutputDimensionFields,
110         const bool isPartialLink,
111         vector<ConditionState>& conditionCache,
112         std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
113     // So far, this is fine as there is at most one child having sliced output.
114     for (const int childIndex : mChildren) {
115         if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
116             allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
117                                                       dimensionFields,
118                                                       isSubOutputDimensionFields,
119                                                       isPartialLink,
120                                                       conditionCache,
121                                                       dimensionsKeySet);
122         }
123     }
124     conditionCache[mIndex] =
125             evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
126 }
127 
evaluateCondition(const LogEvent & event,const std::vector<MatchingState> & eventMatcherValues,const std::vector<sp<ConditionTracker>> & mAllConditions,std::vector<ConditionState> & nonSlicedConditionCache,std::vector<bool> & conditionChangedCache)128 void CombinationConditionTracker::evaluateCondition(
129         const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
130         const std::vector<sp<ConditionTracker>>& mAllConditions,
131         std::vector<ConditionState>& nonSlicedConditionCache,
132         std::vector<bool>& conditionChangedCache) {
133     // value is up to date.
134     if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
135         return;
136     }
137 
138     for (const int childIndex : mChildren) {
139         // So far, this is fine as there is at most one child having sliced output.
140         if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
141             const sp<ConditionTracker>& child = mAllConditions[childIndex];
142             child->evaluateCondition(event, eventMatcherValues, mAllConditions,
143                                      nonSlicedConditionCache, conditionChangedCache);
144         }
145     }
146 
147     ConditionState newCondition =
148             evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
149     if (!mSliced) {
150 
151         bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
152         mNonSlicedConditionState = newCondition;
153 
154         nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
155 
156         conditionChangedCache[mIndex] = nonSlicedChanged;
157         mUnSlicedPart = newCondition;
158     } else {
159         mUnSlicedPart = evaluateCombinationCondition(
160             mUnSlicedChildren, mLogicalOperation, nonSlicedConditionCache);
161 
162         for (const int childIndex : mChildren) {
163             // If any of the sliced condition in children condition changes, the combination
164             // condition may be changed too.
165             if (conditionChangedCache[childIndex]) {
166                 conditionChangedCache[mIndex] = true;
167                 break;
168             }
169         }
170         nonSlicedConditionCache[mIndex] = newCondition;
171         VLOG("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId,
172             conditionChangedCache[mIndex] == true);
173     }
174 }
175 
getMetConditionDimension(const std::vector<sp<ConditionTracker>> & allConditions,const std::vector<Matcher> & dimensionFields,const bool isSubOutputDimensionFields,std::unordered_set<HashableDimensionKey> & dimensionsKeySet) const176 ConditionState CombinationConditionTracker::getMetConditionDimension(
177         const std::vector<sp<ConditionTracker>>& allConditions,
178         const std::vector<Matcher>& dimensionFields,
179         const bool isSubOutputDimensionFields,
180         std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
181     vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
182     // So far, this is fine as there is at most one child having sliced output.
183     for (const int childIndex : mChildren) {
184         conditionCache[childIndex] = conditionCache[childIndex] |
185             allConditions[childIndex]->getMetConditionDimension(
186                 allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
187     }
188     evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
189     if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
190         dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY);
191     }
192     return conditionCache[mIndex];
193 }
194 
equalOutputDimensions(const std::vector<sp<ConditionTracker>> & allConditions,const vector<Matcher> & dimensions) const195 bool CombinationConditionTracker::equalOutputDimensions(
196         const std::vector<sp<ConditionTracker>>& allConditions,
197         const vector<Matcher>& dimensions) const {
198     if (mSlicedChildren.size() != 1 ||
199         mSlicedChildren.front() >= (int)allConditions.size() ||
200         mLogicalOperation != LogicalOperation::AND) {
201         return false;
202     }
203     const sp<ConditionTracker>& slicedChild = allConditions.at(mSlicedChildren.front());
204     return slicedChild->equalOutputDimensions(allConditions, dimensions);
205 }
206 
207 }  // namespace statsd
208 }  // namespace os
209 }  // namespace android
210