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