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