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 STATSD_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,const uint64_t protoHash)28 CombinationConditionTracker::CombinationConditionTracker(const int64_t id, const int index,
29                                                          const uint64_t protoHash)
30     : ConditionTracker(id, index, protoHash) {
31     VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
32 }
33 
~CombinationConditionTracker()34 CombinationConditionTracker::~CombinationConditionTracker() {
35     VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId);
36 }
37 
init(const vector<Predicate> & allConditionConfig,const vector<sp<ConditionTracker>> & allConditionTrackers,const unordered_map<int64_t,int> & conditionIdIndexMap,vector<uint8_t> & stack,vector<ConditionState> & conditionCache)38 optional<InvalidConfigReason> CombinationConditionTracker::init(
39         const vector<Predicate>& allConditionConfig,
40         const vector<sp<ConditionTracker>>& allConditionTrackers,
41         const unordered_map<int64_t, int>& conditionIdIndexMap, vector<uint8_t>& stack,
42         vector<ConditionState>& conditionCache) {
43     VLOG("Combination predicate init() %lld", (long long)mConditionId);
44     if (mInitialized) {
45         // All the children are guaranteed to be initialized, but the recursion is needed to
46         // fill the conditionCache properly, since another combination condition or metric
47         // might rely on this. The recursion is needed to compute the current condition.
48 
49         // Init is called instead of isConditionMet so that the ConditionKey can be filled with the
50         // default key for sliced conditions, since we do not know all indirect descendants here.
51         for (const int childIndex : mChildren) {
52             if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
53                 allConditionTrackers[childIndex]->init(allConditionConfig, allConditionTrackers,
54                                                        conditionIdIndexMap, stack, conditionCache);
55             }
56         }
57         conditionCache[mIndex] =
58                 evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
59         return nullopt;
60     }
61 
62     // mark this node as visited in the recursion stack.
63     stack[mIndex] = true;
64 
65     Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination();
66     optional<InvalidConfigReason> invalidConfigReason;
67 
68     if (!combinationCondition.has_operation()) {
69         return createInvalidConfigReasonWithPredicate(INVALID_CONFIG_REASON_CONDITION_NO_OPERATION,
70                                                       mConditionId);
71     }
72     mLogicalOperation = combinationCondition.operation();
73 
74     if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) {
75         return createInvalidConfigReasonWithPredicate(
76                 INVALID_CONFIG_REASON_CONDITION_NOT_OPERATION_IS_NOT_UNARY, mConditionId);
77     }
78 
79     for (auto child : combinationCondition.predicate()) {
80         auto it = conditionIdIndexMap.find(child);
81 
82         if (it == conditionIdIndexMap.end()) {
83             ALOGW("Predicate %lld not found in the config", (long long)child);
84             invalidConfigReason = createInvalidConfigReasonWithPredicate(
85                     INVALID_CONFIG_REASON_CONDITION_CHILD_NOT_FOUND, mConditionId);
86             invalidConfigReason->conditionIds.push_back(child);
87             return invalidConfigReason;
88         }
89 
90         int childIndex = it->second;
91         const auto& childTracker = allConditionTrackers[childIndex];
92         // if the child is a visited node in the recursion -> circle detected.
93         if (stack[childIndex]) {
94             ALOGW("Circle detected!!!");
95             invalidConfigReason = createInvalidConfigReasonWithPredicate(
96                     INVALID_CONFIG_REASON_CONDITION_CYCLE, mConditionId);
97             invalidConfigReason->conditionIds.push_back(child);
98             return invalidConfigReason;
99         }
100 
101         invalidConfigReason = childTracker->init(allConditionConfig, allConditionTrackers,
102                                                  conditionIdIndexMap, stack, conditionCache);
103 
104         if (invalidConfigReason.has_value()) {
105             ALOGW("Child initialization failed %lld ", (long long)child);
106             invalidConfigReason->conditionIds.push_back(mConditionId);
107             return invalidConfigReason;
108         } else {
109             VLOG("Child initialization success %lld ", (long long)child);
110         }
111 
112         if (allConditionTrackers[childIndex]->isSliced()) {
113             setSliced(true);
114             mSlicedChildren.push_back(childIndex);
115         } else {
116             mUnSlicedChildren.push_back(childIndex);
117         }
118         mChildren.push_back(childIndex);
119         mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
120                              childTracker->getAtomMatchingTrackerIndex().end());
121     }
122 
123     mUnSlicedPartCondition =
124             evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, conditionCache);
125     conditionCache[mIndex] =
126             evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
127 
128     // unmark this node in the recursion stack.
129     stack[mIndex] = false;
130 
131     mInitialized = true;
132 
133     return nullopt;
134 }
135 
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)136 optional<InvalidConfigReason> CombinationConditionTracker::onConfigUpdated(
137         const vector<Predicate>& allConditionProtos, const int index,
138         const vector<sp<ConditionTracker>>& allConditionTrackers,
139         const unordered_map<int64_t, int>& atomMatchingTrackerMap,
140         const unordered_map<int64_t, int>& conditionTrackerMap) {
141     ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
142                                       atomMatchingTrackerMap, conditionTrackerMap);
143     mTrackerIndex.clear();
144     mChildren.clear();
145     mUnSlicedChildren.clear();
146     mSlicedChildren.clear();
147     Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination();
148     optional<InvalidConfigReason> invalidConfigReason;
149 
150     for (const int64_t child : combinationCondition.predicate()) {
151         const auto& it = conditionTrackerMap.find(child);
152 
153         if (it == conditionTrackerMap.end()) {
154             ALOGW("Predicate %lld not found in the config", (long long)child);
155             invalidConfigReason = createInvalidConfigReasonWithPredicate(
156                     INVALID_CONFIG_REASON_CONDITION_CHILD_NOT_FOUND, mConditionId);
157             invalidConfigReason->conditionIds.push_back(child);
158             return invalidConfigReason;
159         }
160 
161         int childIndex = it->second;
162         const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex];
163 
164         // Ensures that the child's tracker indices are updated.
165         invalidConfigReason =
166                 childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers,
167                                               atomMatchingTrackerMap, conditionTrackerMap);
168         if (invalidConfigReason.has_value()) {
169             ALOGW("Child update failed %lld ", (long long)child);
170             invalidConfigReason->conditionIds.push_back(child);
171             return invalidConfigReason;
172         }
173 
174         if (allConditionTrackers[childIndex]->isSliced()) {
175             mSlicedChildren.push_back(childIndex);
176         } else {
177             mUnSlicedChildren.push_back(childIndex);
178         }
179         mChildren.push_back(childIndex);
180         mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
181                              childTracker->getAtomMatchingTrackerIndex().end());
182     }
183     return nullopt;
184 }
185 
isConditionMet(const ConditionKey & conditionParameters,const vector<sp<ConditionTracker>> & allConditions,const bool isPartialLink,vector<ConditionState> & conditionCache) const186 void CombinationConditionTracker::isConditionMet(
187         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
188         const bool isPartialLink,
189         vector<ConditionState>& conditionCache) const {
190     // So far, this is fine as there is at most one child having sliced output.
191     for (const int childIndex : mChildren) {
192         if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
193             allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
194                                                       isPartialLink,
195                                                       conditionCache);
196         }
197     }
198     conditionCache[mIndex] =
199             evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
200 }
201 
evaluateCondition(const LogEvent & event,const std::vector<MatchingState> & eventMatcherValues,const std::vector<sp<ConditionTracker>> & mAllConditions,std::vector<ConditionState> & nonSlicedConditionCache,std::vector<uint8_t> & conditionChangedCache)202 void CombinationConditionTracker::evaluateCondition(
203         const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
204         const std::vector<sp<ConditionTracker>>& mAllConditions,
205         std::vector<ConditionState>& nonSlicedConditionCache,
206         std::vector<uint8_t>& conditionChangedCache) {
207     // value is up to date.
208     if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
209         return;
210     }
211 
212     for (const int childIndex : mChildren) {
213         // So far, this is fine as there is at most one child having sliced output.
214         if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
215             const sp<ConditionTracker>& child = mAllConditions[childIndex];
216             child->evaluateCondition(event, eventMatcherValues, mAllConditions,
217                                      nonSlicedConditionCache, conditionChangedCache);
218         }
219     }
220 
221     ConditionState newCondition =
222             evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
223     if (!mSliced) {
224         bool nonSlicedChanged = (mUnSlicedPartCondition != newCondition);
225         mUnSlicedPartCondition = newCondition;
226 
227         nonSlicedConditionCache[mIndex] = mUnSlicedPartCondition;
228         conditionChangedCache[mIndex] = nonSlicedChanged;
229     } else {
230         mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation,
231                                                               nonSlicedConditionCache);
232 
233         for (const int childIndex : mChildren) {
234             // If any of the sliced condition in children condition changes, the combination
235             // condition may be changed too.
236             if (conditionChangedCache[childIndex]) {
237                 conditionChangedCache[mIndex] = true;
238                 break;
239             }
240         }
241         nonSlicedConditionCache[mIndex] = newCondition;
242         VLOG("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId,
243             conditionChangedCache[mIndex] == true);
244     }
245 }
246 
equalOutputDimensions(const std::vector<sp<ConditionTracker>> & allConditions,const vector<Matcher> & dimensions) const247 bool CombinationConditionTracker::equalOutputDimensions(
248         const std::vector<sp<ConditionTracker>>& allConditions,
249         const vector<Matcher>& dimensions) const {
250     if (mSlicedChildren.size() != 1 ||
251         mSlicedChildren.front() >= (int)allConditions.size() ||
252         mLogicalOperation != LogicalOperation::AND) {
253         return false;
254     }
255     const sp<ConditionTracker>& slicedChild = allConditions.at(mSlicedChildren.front());
256     return slicedChild->equalOutputDimensions(allConditions, dimensions);
257 }
258 
259 }  // namespace statsd
260 }  // namespace os
261 }  // namespace android
262