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