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