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