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
20 #include "SimpleConditionTracker.h"
21 #include "guardrail/StatsdStats.h"
22
23 namespace android {
24 namespace os {
25 namespace statsd {
26
27 using std::unordered_map;
28
SimpleConditionTracker(const ConfigKey & key,const int64_t & id,const int index,const SimplePredicate & simplePredicate,const unordered_map<int64_t,int> & trackerNameIndexMap)29 SimpleConditionTracker::SimpleConditionTracker(
30 const ConfigKey& key, const int64_t& id, const int index,
31 const SimplePredicate& simplePredicate,
32 const unordered_map<int64_t, int>& trackerNameIndexMap)
33 : ConditionTracker(id, index), mConfigKey(key), mContainANYPositionInInternalDimensions(false) {
34 VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId);
35 mCountNesting = simplePredicate.count_nesting();
36
37 if (simplePredicate.has_start()) {
38 auto pair = trackerNameIndexMap.find(simplePredicate.start());
39 if (pair == trackerNameIndexMap.end()) {
40 ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
41 return;
42 }
43 mStartLogMatcherIndex = pair->second;
44 mTrackerIndex.insert(mStartLogMatcherIndex);
45 } else {
46 mStartLogMatcherIndex = -1;
47 }
48
49 if (simplePredicate.has_stop()) {
50 auto pair = trackerNameIndexMap.find(simplePredicate.stop());
51 if (pair == trackerNameIndexMap.end()) {
52 ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
53 return;
54 }
55 mStopLogMatcherIndex = pair->second;
56 mTrackerIndex.insert(mStopLogMatcherIndex);
57 } else {
58 mStopLogMatcherIndex = -1;
59 }
60
61 if (simplePredicate.has_stop_all()) {
62 auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
63 if (pair == trackerNameIndexMap.end()) {
64 ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all());
65 return;
66 }
67 mStopAllLogMatcherIndex = pair->second;
68 mTrackerIndex.insert(mStopAllLogMatcherIndex);
69 } else {
70 mStopAllLogMatcherIndex = -1;
71 }
72
73 if (simplePredicate.has_dimensions()) {
74 translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
75 if (mOutputDimensions.size() > 0) {
76 mSliced = true;
77 mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
78 }
79 mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions());
80 }
81
82 if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
83 mInitialValue = ConditionState::kFalse;
84 } else {
85 mInitialValue = ConditionState::kUnknown;
86 }
87
88 mNonSlicedConditionState = mInitialValue;
89
90 if (!mSliced) {
91 mUnSlicedPart = mInitialValue;
92 }
93
94 mInitialized = true;
95 }
96
~SimpleConditionTracker()97 SimpleConditionTracker::~SimpleConditionTracker() {
98 VLOG("~SimpleConditionTracker()");
99 }
100
init(const vector<Predicate> & allConditionConfig,const vector<sp<ConditionTracker>> & allConditionTrackers,const unordered_map<int64_t,int> & conditionIdIndexMap,vector<bool> & stack)101 bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
102 const vector<sp<ConditionTracker>>& allConditionTrackers,
103 const unordered_map<int64_t, int>& conditionIdIndexMap,
104 vector<bool>& stack) {
105 // SimpleConditionTracker does not have dependency on other conditions, thus we just return
106 // if the initialization was successful.
107 return mInitialized;
108 }
109
dumpState()110 void SimpleConditionTracker::dumpState() {
111 VLOG("%lld DUMP:", (long long)mConditionId);
112 for (const auto& pair : mSlicedConditionState) {
113 VLOG("\t%s : %d", pair.first.toString().c_str(), pair.second);
114 }
115
116 VLOG("Changed to true keys: \n");
117 for (const auto& key : mLastChangedToTrueDimensions) {
118 VLOG("%s", key.toString().c_str());
119 }
120 VLOG("Changed to false keys: \n");
121 for (const auto& key : mLastChangedToFalseDimensions) {
122 VLOG("%s", key.toString().c_str());
123 }
124 }
125
handleStopAll(std::vector<ConditionState> & conditionCache,std::vector<bool> & conditionChangedCache)126 void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
127 std::vector<bool>& conditionChangedCache) {
128 // Unless the default condition is false, and there was nothing started, otherwise we have
129 // triggered a condition change.
130 conditionChangedCache[mIndex] =
131 (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
132 : true;
133
134 for (const auto& cond : mSlicedConditionState) {
135 if (cond.second > 0) {
136 mLastChangedToFalseDimensions.insert(cond.first);
137 }
138 }
139
140 // After StopAll, we know everything has stopped. From now on, default condition is false.
141 mInitialValue = ConditionState::kFalse;
142 mSlicedConditionState.clear();
143 conditionCache[mIndex] = ConditionState::kFalse;
144 if (!mSliced) {
145 mUnSlicedPart = ConditionState::kFalse;
146 }
147 }
148
hitGuardRail(const HashableDimensionKey & newKey)149 bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
150 if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) {
151 // if the condition is not sliced or the key is not new, we are good!
152 return false;
153 }
154 // 1. Report the tuple count if the tuple count > soft limit
155 if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
156 size_t newTupleCount = mSlicedConditionState.size() + 1;
157 StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
158 // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
159 if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
160 ALOGE("Predicate %lld dropping data for dimension key %s",
161 (long long)mConditionId, newKey.toString().c_str());
162 return true;
163 }
164 }
165 return false;
166 }
167
handleConditionEvent(const HashableDimensionKey & outputKey,bool matchStart,ConditionState * conditionCache,bool * conditionChangedCache)168 void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
169 bool matchStart, ConditionState* conditionCache,
170 bool* conditionChangedCache) {
171 bool changed = false;
172 auto outputIt = mSlicedConditionState.find(outputKey);
173 ConditionState newCondition;
174 if (hitGuardRail(outputKey)) {
175 (*conditionChangedCache) = false;
176 // Tells the caller it's evaluated.
177 (*conditionCache) = ConditionState::kUnknown;
178 return;
179 }
180 if (outputIt == mSlicedConditionState.end()) {
181 // We get a new output key.
182 newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
183 if (matchStart && mInitialValue != ConditionState::kTrue) {
184 mSlicedConditionState[outputKey] = 1;
185 changed = true;
186 mLastChangedToTrueDimensions.insert(outputKey);
187 } else if (mInitialValue != ConditionState::kFalse) {
188 // it's a stop and we don't have history about it.
189 // If the default condition is not false, it means this stop is valuable to us.
190 mSlicedConditionState[outputKey] = 0;
191 mLastChangedToFalseDimensions.insert(outputKey);
192 changed = true;
193 }
194 } else {
195 // we have history about this output key.
196 auto& startedCount = outputIt->second;
197 // assign the old value first.
198 newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
199 if (matchStart) {
200 if (startedCount == 0) {
201 mLastChangedToTrueDimensions.insert(outputKey);
202 // This condition for this output key will change from false -> true
203 changed = true;
204 }
205
206 // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
207 // as 1 if not counting nesting.
208 startedCount++;
209 newCondition = ConditionState::kTrue;
210 } else {
211 // This is a stop event.
212 if (startedCount > 0) {
213 if (mCountNesting) {
214 startedCount--;
215 if (startedCount == 0) {
216 newCondition = ConditionState::kFalse;
217 }
218 } else {
219 // not counting nesting, so ignore the number of starts, stop now.
220 startedCount = 0;
221 newCondition = ConditionState::kFalse;
222 }
223 // if everything has stopped for this output key, condition true -> false;
224 if (startedCount == 0) {
225 mLastChangedToFalseDimensions.insert(outputKey);
226 changed = true;
227 }
228 }
229
230 // if default condition is false, it means we don't need to keep the false values.
231 if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
232 mSlicedConditionState.erase(outputIt);
233 VLOG("erase key %s", outputKey.toString().c_str());
234 }
235 }
236 }
237
238 // dump all dimensions for debugging
239 if (DEBUG) {
240 dumpState();
241 }
242
243 (*conditionChangedCache) = changed;
244 (*conditionCache) = newCondition;
245
246 VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId,
247 conditionChangedCache[mIndex] == true);
248 }
249
evaluateCondition(const LogEvent & event,const vector<MatchingState> & eventMatcherValues,const vector<sp<ConditionTracker>> & mAllConditions,vector<ConditionState> & conditionCache,vector<bool> & conditionChangedCache)250 void SimpleConditionTracker::evaluateCondition(
251 const LogEvent& event,
252 const vector<MatchingState>& eventMatcherValues,
253 const vector<sp<ConditionTracker>>& mAllConditions,
254 vector<ConditionState>& conditionCache,
255 vector<bool>& conditionChangedCache) {
256 if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
257 // it has been evaluated.
258 VLOG("Yes, already evaluated, %lld %d",
259 (long long)mConditionId, conditionCache[mIndex]);
260 return;
261 }
262 mLastChangedToTrueDimensions.clear();
263 mLastChangedToFalseDimensions.clear();
264
265 if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) &&
266 eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
267 handleStopAll(conditionCache, conditionChangedCache);
268 return;
269 }
270
271 int matchedState = -1;
272 // Note: The order to evaluate the following start, stop, stop_all matters.
273 // The priority of overwrite is stop_all > stop > start.
274 if (mStartLogMatcherIndex >= 0 &&
275 eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
276 matchedState = 1;
277 }
278
279 if (mStopLogMatcherIndex >= 0 &&
280 eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
281 matchedState = 0;
282 }
283
284 if (matchedState < 0) {
285 // The event doesn't match this condition. So we just report existing condition values.
286 conditionChangedCache[mIndex] = false;
287 if (mSliced) {
288 // if the condition result is sliced. The overall condition is true if any of the sliced
289 // condition is true
290 conditionCache[mIndex] = mInitialValue;
291 for (const auto& slicedCondition : mSlicedConditionState) {
292 if (slicedCondition.second > 0) {
293 conditionCache[mIndex] = ConditionState::kTrue;
294 break;
295 }
296 }
297 } else {
298 const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
299 if (itr == mSlicedConditionState.end()) {
300 // condition not sliced, but we haven't seen the matched start or stop yet. so
301 // return initial value.
302 conditionCache[mIndex] = mInitialValue;
303 } else {
304 // return the cached condition.
305 conditionCache[mIndex] =
306 itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
307 }
308 mUnSlicedPart = conditionCache[mIndex];
309 }
310
311 return;
312 }
313
314 ConditionState overallState = mInitialValue;
315 bool overallChanged = false;
316
317 if (mOutputDimensions.size() == 0) {
318 handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState,
319 &overallChanged);
320 } else if (!mContainANYPositionInInternalDimensions) {
321 HashableDimensionKey outputValue;
322 filterValues(mOutputDimensions, event.getValues(), &outputValue);
323
324 // If this event has multiple nodes in the attribution chain, this log event probably will
325 // generate multiple dimensions. If so, we will find if the condition changes for any
326 // dimension and ask the corresponding metric producer to verify whether the actual sliced
327 // condition has changed or not.
328 // A high level assumption is that a predicate is either sliced or unsliced. We will never
329 // have both sliced and unsliced version of a predicate.
330 handleConditionEvent(outputValue, matchedState == 1, &overallState, &overallChanged);
331 } else {
332 ALOGE("The condition tracker should not be sliced by ANY position matcher.");
333 }
334 conditionCache[mIndex] = overallState;
335 conditionChangedCache[mIndex] = overallChanged;
336 if (!mSliced) {
337 mUnSlicedPart = overallState;
338 }
339 }
340
isConditionMet(const ConditionKey & conditionParameters,const vector<sp<ConditionTracker>> & allConditions,const vector<Matcher> & dimensionFields,const bool isSubOutputDimensionFields,const bool isPartialLink,vector<ConditionState> & conditionCache,std::unordered_set<HashableDimensionKey> & dimensionsKeySet) const341 void SimpleConditionTracker::isConditionMet(
342 const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
343 const vector<Matcher>& dimensionFields,
344 const bool isSubOutputDimensionFields,
345 const bool isPartialLink,
346 vector<ConditionState>& conditionCache,
347 std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
348
349 if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
350 // it has been evaluated.
351 VLOG("Yes, already evaluated, %lld %d",
352 (long long)mConditionId, conditionCache[mIndex]);
353 return;
354 }
355 const auto pair = conditionParameters.find(mConditionId);
356
357 if (pair == conditionParameters.end()) {
358 ConditionState conditionState = ConditionState::kNotEvaluated;
359 if (dimensionFields.size() > 0 && dimensionFields[0].mMatcher.getTag() == mDimensionTag) {
360 conditionState = conditionState | getMetConditionDimension(
361 allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
362 } else {
363 conditionState = conditionState | mInitialValue;
364 if (!mSliced) {
365 const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
366 if (itr != mSlicedConditionState.end()) {
367 ConditionState sliceState =
368 itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
369 conditionState = conditionState | sliceState;
370 }
371 }
372 }
373 conditionCache[mIndex] = conditionState;
374 return;
375 }
376
377 ConditionState conditionState = ConditionState::kNotEvaluated;
378 const HashableDimensionKey& key = pair->second;
379 if (isPartialLink) {
380 // For unseen key, check whether the require dimensions are subset of sliced condition
381 // output.
382 conditionState = conditionState | mInitialValue;
383 for (const auto& slice : mSlicedConditionState) {
384 ConditionState sliceState =
385 slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
386 if (slice.first.contains(key)) {
387 conditionState = conditionState | sliceState;
388 if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
389 if (isSubOutputDimensionFields) {
390 HashableDimensionKey dimensionKey;
391 filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
392 dimensionsKeySet.insert(dimensionKey);
393 } else {
394 dimensionsKeySet.insert(slice.first);
395 }
396 }
397 }
398 }
399 } else {
400 auto startedCountIt = mSlicedConditionState.find(key);
401 conditionState = conditionState | mInitialValue;
402 if (startedCountIt != mSlicedConditionState.end()) {
403 ConditionState sliceState =
404 startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
405 conditionState = conditionState | sliceState;
406 if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
407 if (isSubOutputDimensionFields) {
408 HashableDimensionKey dimensionKey;
409 filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKey);
410 dimensionsKeySet.insert(dimensionKey);
411 } else {
412 dimensionsKeySet.insert(startedCountIt->first);
413 }
414 }
415 }
416
417 }
418 conditionCache[mIndex] = conditionState;
419 VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
420 }
421
getMetConditionDimension(const std::vector<sp<ConditionTracker>> & allConditions,const vector<Matcher> & dimensionFields,const bool isSubOutputDimensionFields,std::unordered_set<HashableDimensionKey> & dimensionsKeySet) const422 ConditionState SimpleConditionTracker::getMetConditionDimension(
423 const std::vector<sp<ConditionTracker>>& allConditions,
424 const vector<Matcher>& dimensionFields,
425 const bool isSubOutputDimensionFields,
426 std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
427 ConditionState conditionState = mInitialValue;
428 if (dimensionFields.size() == 0 || mOutputDimensions.size() == 0 ||
429 dimensionFields[0].mMatcher.getTag() != mOutputDimensions[0].mMatcher.getTag()) {
430 const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
431 if (itr != mSlicedConditionState.end()) {
432 ConditionState sliceState =
433 itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
434 conditionState = conditionState | sliceState;
435 }
436 return conditionState;
437 }
438
439 for (const auto& slice : mSlicedConditionState) {
440 ConditionState sliceState =
441 slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
442 conditionState = conditionState | sliceState;
443
444 if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
445 if (isSubOutputDimensionFields) {
446 HashableDimensionKey dimensionKey;
447 filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
448 dimensionsKeySet.insert(dimensionKey);
449 } else {
450 dimensionsKeySet.insert(slice.first);
451 }
452 }
453 }
454 return conditionState;
455 }
456
457 } // namespace statsd
458 } // namespace os
459 } // namespace android
460