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
23namespace android {
24namespace os {
25namespace statsd {
26
27using std::map;
28using std::string;
29using std::unique_ptr;
30using std::unordered_map;
31using std::vector;
32
33SimpleConditionTracker::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
101SimpleConditionTracker::~SimpleConditionTracker() {
102    VLOG("~SimpleConditionTracker()");
103}
104
105bool 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
114void 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
130void 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
153bool 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
172void 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
254void 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
345void 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
426ConditionState 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