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<bool> & stack,vector<ConditionState> & conditionCache)38 bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig,
39 const vector<sp<ConditionTracker>>& allConditionTrackers,
40 const unordered_map<int64_t, int>& conditionIdIndexMap,
41 vector<bool>& 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 true;
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
67 if (!combinationCondition.has_operation()) {
68 return false;
69 }
70 mLogicalOperation = combinationCondition.operation();
71
72 if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) {
73 return false;
74 }
75
76 for (auto child : combinationCondition.predicate()) {
77 auto it = conditionIdIndexMap.find(child);
78
79 if (it == conditionIdIndexMap.end()) {
80 ALOGW("Predicate %lld not found in the config", (long long)child);
81 return false;
82 }
83
84 int childIndex = it->second;
85 const auto& childTracker = allConditionTrackers[childIndex];
86 // if the child is a visited node in the recursion -> circle detected.
87 if (stack[childIndex]) {
88 ALOGW("Circle detected!!!");
89 return false;
90 }
91
92 bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
93 conditionIdIndexMap, stack, conditionCache);
94
95 if (!initChildSucceeded) {
96 ALOGW("Child initialization failed %lld ", (long long)child);
97 return false;
98 } else {
99 VLOG("Child initialization success %lld ", (long long)child);
100 }
101
102 if (allConditionTrackers[childIndex]->isSliced()) {
103 setSliced(true);
104 mSlicedChildren.push_back(childIndex);
105 } else {
106 mUnSlicedChildren.push_back(childIndex);
107 }
108 mChildren.push_back(childIndex);
109 mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
110 childTracker->getAtomMatchingTrackerIndex().end());
111 }
112
113 mUnSlicedPartCondition =
114 evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, conditionCache);
115 conditionCache[mIndex] =
116 evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
117
118 // unmark this node in the recursion stack.
119 stack[mIndex] = false;
120
121 mInitialized = true;
122
123 return true;
124 }
125
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)126 bool CombinationConditionTracker::onConfigUpdated(
127 const vector<Predicate>& allConditionProtos, const int index,
128 const vector<sp<ConditionTracker>>& allConditionTrackers,
129 const unordered_map<int64_t, int>& atomMatchingTrackerMap,
130 const unordered_map<int64_t, int>& conditionTrackerMap) {
131 ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
132 atomMatchingTrackerMap, conditionTrackerMap);
133 mTrackerIndex.clear();
134 mChildren.clear();
135 mUnSlicedChildren.clear();
136 mSlicedChildren.clear();
137 Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination();
138
139 for (const int64_t child : combinationCondition.predicate()) {
140 const auto& it = conditionTrackerMap.find(child);
141
142 if (it == conditionTrackerMap.end()) {
143 ALOGW("Predicate %lld not found in the config", (long long)child);
144 return false;
145 }
146
147 int childIndex = it->second;
148 const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex];
149
150 // Ensures that the child's tracker indices are updated.
151 if (!childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers,
152 atomMatchingTrackerMap, conditionTrackerMap)) {
153 ALOGW("Child update failed %lld ", (long long)child);
154 return false;
155 }
156
157 if (allConditionTrackers[childIndex]->isSliced()) {
158 mSlicedChildren.push_back(childIndex);
159 } else {
160 mUnSlicedChildren.push_back(childIndex);
161 }
162 mChildren.push_back(childIndex);
163 mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
164 childTracker->getAtomMatchingTrackerIndex().end());
165 }
166 return true;
167 }
168
isConditionMet(const ConditionKey & conditionParameters,const vector<sp<ConditionTracker>> & allConditions,const bool isPartialLink,vector<ConditionState> & conditionCache) const169 void CombinationConditionTracker::isConditionMet(
170 const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
171 const bool isPartialLink,
172 vector<ConditionState>& conditionCache) const {
173 // So far, this is fine as there is at most one child having sliced output.
174 for (const int childIndex : mChildren) {
175 if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
176 allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
177 isPartialLink,
178 conditionCache);
179 }
180 }
181 conditionCache[mIndex] =
182 evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
183 }
184
evaluateCondition(const LogEvent & event,const std::vector<MatchingState> & eventMatcherValues,const std::vector<sp<ConditionTracker>> & mAllConditions,std::vector<ConditionState> & nonSlicedConditionCache,std::vector<bool> & conditionChangedCache)185 void CombinationConditionTracker::evaluateCondition(
186 const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
187 const std::vector<sp<ConditionTracker>>& mAllConditions,
188 std::vector<ConditionState>& nonSlicedConditionCache,
189 std::vector<bool>& conditionChangedCache) {
190 // value is up to date.
191 if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
192 return;
193 }
194
195 for (const int childIndex : mChildren) {
196 // So far, this is fine as there is at most one child having sliced output.
197 if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
198 const sp<ConditionTracker>& child = mAllConditions[childIndex];
199 child->evaluateCondition(event, eventMatcherValues, mAllConditions,
200 nonSlicedConditionCache, conditionChangedCache);
201 }
202 }
203
204 ConditionState newCondition =
205 evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
206 if (!mSliced) {
207 bool nonSlicedChanged = (mUnSlicedPartCondition != newCondition);
208 mUnSlicedPartCondition = newCondition;
209
210 nonSlicedConditionCache[mIndex] = mUnSlicedPartCondition;
211 conditionChangedCache[mIndex] = nonSlicedChanged;
212 } else {
213 mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation,
214 nonSlicedConditionCache);
215
216 for (const int childIndex : mChildren) {
217 // If any of the sliced condition in children condition changes, the combination
218 // condition may be changed too.
219 if (conditionChangedCache[childIndex]) {
220 conditionChangedCache[mIndex] = true;
221 break;
222 }
223 }
224 nonSlicedConditionCache[mIndex] = newCondition;
225 VLOG("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId,
226 conditionChangedCache[mIndex] == true);
227 }
228 }
229
equalOutputDimensions(const std::vector<sp<ConditionTracker>> & allConditions,const vector<Matcher> & dimensions) const230 bool CombinationConditionTracker::equalOutputDimensions(
231 const std::vector<sp<ConditionTracker>>& allConditions,
232 const vector<Matcher>& dimensions) const {
233 if (mSlicedChildren.size() != 1 ||
234 mSlicedChildren.front() >= (int)allConditions.size() ||
235 mLogicalOperation != LogicalOperation::AND) {
236 return false;
237 }
238 const sp<ConditionTracker>& slicedChild = allConditions.at(mSlicedChildren.front());
239 return slicedChild->equalOutputDimensions(allConditions, dimensions);
240 }
241
242 } // namespace statsd
243 } // namespace os
244 } // namespace android
245