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