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     mInitialized = true;
89 }
90 
~SimpleConditionTracker()91 SimpleConditionTracker::~SimpleConditionTracker() {
92     VLOG("~SimpleConditionTracker()");
93 }
94 
init(const vector<Predicate> & allConditionConfig,const vector<sp<ConditionTracker>> & allConditionTrackers,const unordered_map<int64_t,int> & conditionIdIndexMap,vector<bool> & stack,vector<ConditionState> & initialConditionCache)95 bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
96                                   const vector<sp<ConditionTracker>>& allConditionTrackers,
97                                   const unordered_map<int64_t, int>& conditionIdIndexMap,
98                                   vector<bool>& stack,
99                                   vector<ConditionState>& initialConditionCache) {
100     // SimpleConditionTracker does not have dependency on other conditions, thus we just return
101     // if the initialization was successful.
102     initialConditionCache[mIndex] = mInitialValue;
103     return mInitialized;
104 }
105 
dumpState()106 void SimpleConditionTracker::dumpState() {
107     VLOG("%lld DUMP:", (long long)mConditionId);
108     for (const auto& pair : mSlicedConditionState) {
109         VLOG("\t%s : %d", pair.first.toString().c_str(), pair.second);
110     }
111 
112     VLOG("Changed to true keys: \n");
113     for (const auto& key : mLastChangedToTrueDimensions) {
114         VLOG("%s", key.toString().c_str());
115     }
116     VLOG("Changed to false keys: \n");
117     for (const auto& key : mLastChangedToFalseDimensions) {
118         VLOG("%s", key.toString().c_str());
119     }
120 }
121 
handleStopAll(std::vector<ConditionState> & conditionCache,std::vector<bool> & conditionChangedCache)122 void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
123                                            std::vector<bool>& conditionChangedCache) {
124     // Unless the default condition is false, and there was nothing started, otherwise we have
125     // triggered a condition change.
126     conditionChangedCache[mIndex] =
127             (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
128                                                                                            : true;
129 
130     for (const auto& cond : mSlicedConditionState) {
131         if (cond.second > 0) {
132             mLastChangedToFalseDimensions.insert(cond.first);
133         }
134     }
135 
136     // After StopAll, we know everything has stopped. From now on, default condition is false.
137     mInitialValue = ConditionState::kFalse;
138     mSlicedConditionState.clear();
139     conditionCache[mIndex] = ConditionState::kFalse;
140 }
141 
hitGuardRail(const HashableDimensionKey & newKey)142 bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
143     if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) {
144         // if the condition is not sliced or the key is not new, we are good!
145         return false;
146     }
147     // 1. Report the tuple count if the tuple count > soft limit
148     if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
149         size_t newTupleCount = mSlicedConditionState.size() + 1;
150         StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
151         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
152         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
153             ALOGE("Predicate %lld dropping data for dimension key %s",
154                 (long long)mConditionId, newKey.toString().c_str());
155             return true;
156         }
157     }
158     return false;
159 }
160 
handleConditionEvent(const HashableDimensionKey & outputKey,bool matchStart,ConditionState * conditionCache,bool * conditionChangedCache)161 void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
162                                                   bool matchStart, ConditionState* conditionCache,
163                                                   bool* conditionChangedCache) {
164     bool changed = false;
165     auto outputIt = mSlicedConditionState.find(outputKey);
166     ConditionState newCondition;
167     if (hitGuardRail(outputKey)) {
168         (*conditionChangedCache) = false;
169         // Tells the caller it's evaluated.
170         (*conditionCache) = ConditionState::kUnknown;
171         return;
172     }
173     if (outputIt == mSlicedConditionState.end()) {
174         // We get a new output key.
175         newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
176         if (matchStart && mInitialValue != ConditionState::kTrue) {
177             mSlicedConditionState[outputKey] = 1;
178             changed = true;
179             mLastChangedToTrueDimensions.insert(outputKey);
180         } else if (mInitialValue != ConditionState::kFalse) {
181             // it's a stop and we don't have history about it.
182             // If the default condition is not false, it means this stop is valuable to us.
183             mSlicedConditionState[outputKey] = 0;
184             mLastChangedToFalseDimensions.insert(outputKey);
185             changed = true;
186         }
187     } else {
188         // we have history about this output key.
189         auto& startedCount = outputIt->second;
190         // assign the old value first.
191         newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
192         if (matchStart) {
193             if (startedCount == 0) {
194                 mLastChangedToTrueDimensions.insert(outputKey);
195                 // This condition for this output key will change from false -> true
196                 changed = true;
197             }
198 
199             // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
200             // as 1 if not counting nesting.
201             startedCount++;
202             newCondition = ConditionState::kTrue;
203         } else {
204             // This is a stop event.
205             if (startedCount > 0) {
206                 if (mCountNesting) {
207                     startedCount--;
208                     if (startedCount == 0) {
209                         newCondition = ConditionState::kFalse;
210                     }
211                 } else {
212                     // not counting nesting, so ignore the number of starts, stop now.
213                     startedCount = 0;
214                     newCondition = ConditionState::kFalse;
215                 }
216                 // if everything has stopped for this output key, condition true -> false;
217                 if (startedCount == 0) {
218                     mLastChangedToFalseDimensions.insert(outputKey);
219                     changed = true;
220                 }
221             }
222 
223             // if default condition is false, it means we don't need to keep the false values.
224             if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
225                 mSlicedConditionState.erase(outputIt);
226                 VLOG("erase key %s", outputKey.toString().c_str());
227             }
228         }
229     }
230 
231     // dump all dimensions for debugging
232     if (DEBUG) {
233         dumpState();
234     }
235 
236     (*conditionChangedCache) = changed;
237     (*conditionCache) = newCondition;
238 
239     VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId,
240          conditionChangedCache[mIndex] == true);
241 }
242 
evaluateCondition(const LogEvent & event,const vector<MatchingState> & eventMatcherValues,const vector<sp<ConditionTracker>> & mAllConditions,vector<ConditionState> & conditionCache,vector<bool> & conditionChangedCache)243 void SimpleConditionTracker::evaluateCondition(
244         const LogEvent& event,
245         const vector<MatchingState>& eventMatcherValues,
246         const vector<sp<ConditionTracker>>& mAllConditions,
247         vector<ConditionState>& conditionCache,
248         vector<bool>& conditionChangedCache) {
249     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
250         // it has been evaluated.
251         VLOG("Yes, already evaluated, %lld %d",
252             (long long)mConditionId, conditionCache[mIndex]);
253         return;
254     }
255     mLastChangedToTrueDimensions.clear();
256     mLastChangedToFalseDimensions.clear();
257 
258     if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) &&
259         eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
260         handleStopAll(conditionCache, conditionChangedCache);
261         return;
262     }
263 
264     int matchedState = -1;
265     // Note: The order to evaluate the following start, stop, stop_all matters.
266     // The priority of overwrite is stop_all > stop > start.
267     if (mStartLogMatcherIndex >= 0 &&
268         eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
269         matchedState = 1;
270     }
271 
272     if (mStopLogMatcherIndex >= 0 &&
273         eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
274         matchedState = 0;
275     }
276 
277     if (matchedState < 0) {
278         // The event doesn't match this condition. So we just report existing condition values.
279         conditionChangedCache[mIndex] = false;
280         if (mSliced) {
281             // if the condition result is sliced. The overall condition is true if any of the sliced
282             // condition is true
283             conditionCache[mIndex] = mInitialValue;
284             for (const auto& slicedCondition : mSlicedConditionState) {
285                 if (slicedCondition.second > 0) {
286                     conditionCache[mIndex] = ConditionState::kTrue;
287                     break;
288                 }
289             }
290         } else {
291             const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
292             if (itr == mSlicedConditionState.end()) {
293                 // condition not sliced, but we haven't seen the matched start or stop yet. so
294                 // return initial value.
295                 conditionCache[mIndex] = mInitialValue;
296             } else {
297                 // return the cached condition.
298                 conditionCache[mIndex] =
299                         itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
300             }
301         }
302         return;
303     }
304 
305     ConditionState overallState = mInitialValue;
306     bool overallChanged = false;
307 
308     if (mOutputDimensions.size() == 0) {
309         handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState,
310                              &overallChanged);
311     } else if (!mContainANYPositionInInternalDimensions) {
312         HashableDimensionKey outputValue;
313         filterValues(mOutputDimensions, event.getValues(), &outputValue);
314 
315         // If this event has multiple nodes in the attribution chain,  this log event probably will
316         // generate multiple dimensions. If so, we will find if the condition changes for any
317         // dimension and ask the corresponding metric producer to verify whether the actual sliced
318         // condition has changed or not.
319         // A high level assumption is that a predicate is either sliced or unsliced. We will never
320         // have both sliced and unsliced version of a predicate.
321         handleConditionEvent(outputValue, matchedState == 1, &overallState, &overallChanged);
322     } else {
323         ALOGE("The condition tracker should not be sliced by ANY position matcher.");
324     }
325     conditionCache[mIndex] = overallState;
326     conditionChangedCache[mIndex] = overallChanged;
327 }
328 
isConditionMet(const ConditionKey & conditionParameters,const vector<sp<ConditionTracker>> & allConditions,const bool isPartialLink,vector<ConditionState> & conditionCache) const329 void SimpleConditionTracker::isConditionMet(
330         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
331         const bool isPartialLink,
332         vector<ConditionState>& conditionCache) const {
333 
334     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
335         // it has been evaluated.
336         VLOG("Yes, already evaluated, %lld %d",
337             (long long)mConditionId, conditionCache[mIndex]);
338         return;
339     }
340     const auto pair = conditionParameters.find(mConditionId);
341 
342     if (pair == conditionParameters.end()) {
343         ConditionState conditionState = ConditionState::kNotEvaluated;
344         conditionState = conditionState | mInitialValue;
345         if (!mSliced) {
346             const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
347             if (itr != mSlicedConditionState.end()) {
348                 ConditionState sliceState =
349                     itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
350                 conditionState = conditionState | sliceState;
351             }
352         }
353         conditionCache[mIndex] = conditionState;
354         return;
355     }
356 
357     ConditionState conditionState = ConditionState::kNotEvaluated;
358     const HashableDimensionKey& key = pair->second;
359     if (isPartialLink) {
360         // For unseen key, check whether the require dimensions are subset of sliced condition
361         // output.
362         conditionState = conditionState | mInitialValue;
363         for (const auto& slice : mSlicedConditionState) {
364             ConditionState sliceState =
365                 slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
366             if (slice.first.contains(key)) {
367                 conditionState = conditionState | sliceState;
368             }
369         }
370     } else {
371         auto startedCountIt = mSlicedConditionState.find(key);
372         conditionState = conditionState | mInitialValue;
373         if (startedCountIt != mSlicedConditionState.end()) {
374             ConditionState sliceState =
375                 startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
376             conditionState = conditionState | sliceState;
377         }
378 
379     }
380     conditionCache[mIndex] = conditionState;
381     VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
382 }
383 
384 }  // namespace statsd
385 }  // namespace os
386 }  // namespace android
387