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