blob: 1803cbb6f5008c4bc61aca39017c535b9220c2e0 [file] [log] [blame]
Yao Chencaf339d2017-10-06 16:01:10 -07001/*
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
Yao Chen5110bed2017-10-23 12:50:02 -070017#define DEBUG false // STOPSHIP if true
Joe Onorato9fc9edf2017-10-15 20:08:52 -070018#include "Log.h"
Yao Chencaf339d2017-10-06 16:01:10 -070019
20#include "SimpleConditionTracker.h"
Yao Chenb3561512017-11-21 18:07:17 -080021#include "guardrail/StatsdStats.h"
Joe Onorato9fc9edf2017-10-15 20:08:52 -070022
Yao Chencaf339d2017-10-06 16:01:10 -070023#include <log/logprint.h>
24
Joe Onorato9fc9edf2017-10-15 20:08:52 -070025namespace android {
26namespace os {
27namespace statsd {
28
Yao Chen729093d2017-10-16 10:33:26 -070029using std::map;
Yao Chencaf339d2017-10-06 16:01:10 -070030using std::string;
31using std::unique_ptr;
32using std::unordered_map;
33using std::vector;
34
Yao Chencaf339d2017-10-06 16:01:10 -070035SimpleConditionTracker::SimpleConditionTracker(
Yao Chenb3561512017-11-21 18:07:17 -080036 const ConfigKey& key, const string& name, const int index,
Stefan Lafon12d01fa2017-12-04 20:56:09 -080037 const SimplePredicate& simplePredicate,
Yao Chencaf339d2017-10-06 16:01:10 -070038 const unordered_map<string, int>& trackerNameIndexMap)
Yao Chenb3561512017-11-21 18:07:17 -080039 : ConditionTracker(name, index), mConfigKey(key) {
Yao Chencaf339d2017-10-06 16:01:10 -070040 VLOG("creating SimpleConditionTracker %s", mName.c_str());
Stefan Lafon12d01fa2017-12-04 20:56:09 -080041 mCountNesting = simplePredicate.count_nesting();
Yao Chencaf339d2017-10-06 16:01:10 -070042
Stefan Lafon12d01fa2017-12-04 20:56:09 -080043 if (simplePredicate.has_start()) {
44 auto pair = trackerNameIndexMap.find(simplePredicate.start());
Yao Chencaf339d2017-10-06 16:01:10 -070045 if (pair == trackerNameIndexMap.end()) {
Stefan Lafon12d01fa2017-12-04 20:56:09 -080046 ALOGW("Start matcher %s not found in the config", simplePredicate.start().c_str());
Yao Chencaf339d2017-10-06 16:01:10 -070047 return;
48 }
49 mStartLogMatcherIndex = pair->second;
50 mTrackerIndex.insert(mStartLogMatcherIndex);
51 } else {
52 mStartLogMatcherIndex = -1;
53 }
54
Stefan Lafon12d01fa2017-12-04 20:56:09 -080055 if (simplePredicate.has_stop()) {
56 auto pair = trackerNameIndexMap.find(simplePredicate.stop());
Yao Chencaf339d2017-10-06 16:01:10 -070057 if (pair == trackerNameIndexMap.end()) {
Stefan Lafon12d01fa2017-12-04 20:56:09 -080058 ALOGW("Stop matcher %s not found in the config", simplePredicate.stop().c_str());
Yao Chencaf339d2017-10-06 16:01:10 -070059 return;
60 }
61 mStopLogMatcherIndex = pair->second;
Yao Chen4b146852017-10-10 15:34:42 -070062 mTrackerIndex.insert(mStopLogMatcherIndex);
Yao Chencaf339d2017-10-06 16:01:10 -070063 } else {
64 mStopLogMatcherIndex = -1;
65 }
66
Stefan Lafon12d01fa2017-12-04 20:56:09 -080067 if (simplePredicate.has_stop_all()) {
68 auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
Yao Chencaf339d2017-10-06 16:01:10 -070069 if (pair == trackerNameIndexMap.end()) {
Stefan Lafon12d01fa2017-12-04 20:56:09 -080070 ALOGW("Stop all matcher %s not found in the config", simplePredicate.stop().c_str());
Yao Chencaf339d2017-10-06 16:01:10 -070071 return;
72 }
73 mStopAllLogMatcherIndex = pair->second;
74 mTrackerIndex.insert(mStopAllLogMatcherIndex);
75 } else {
76 mStopAllLogMatcherIndex = -1;
77 }
78
Yangster-mac20877162017-12-22 17:19:39 -080079 mOutputDimensions = simplePredicate.dimensions();
Yao Chen5154a372017-10-30 22:57:06 -070080
Yangster-mac20877162017-12-22 17:19:39 -080081 if (mOutputDimensions.child_size() > 0) {
Yao Chen5154a372017-10-30 22:57:06 -070082 mSliced = true;
83 }
84
Stefan Lafon12d01fa2017-12-04 20:56:09 -080085 if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
Yao Chen967b2052017-11-07 16:36:43 -080086 mInitialValue = ConditionState::kFalse;
87 } else {
88 mInitialValue = ConditionState::kUnknown;
89 }
90
91 mNonSlicedConditionState = mInitialValue;
92
Yao Chencaf339d2017-10-06 16:01:10 -070093 mInitialized = true;
94}
95
96SimpleConditionTracker::~SimpleConditionTracker() {
97 VLOG("~SimpleConditionTracker()");
98}
99
Stefan Lafon12d01fa2017-12-04 20:56:09 -0800100bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
Yao Chencaf339d2017-10-06 16:01:10 -0700101 const vector<sp<ConditionTracker>>& allConditionTrackers,
102 const unordered_map<string, int>& conditionNameIndexMap,
103 vector<bool>& stack) {
104 // SimpleConditionTracker does not have dependency on other conditions, thus we just return
105 // if the initialization was successful.
106 return mInitialized;
107}
108
Yao Chen967b2052017-11-07 16:36:43 -0800109void print(map<HashableDimensionKey, int>& conditions, const string& name) {
Yao Chen729093d2017-10-16 10:33:26 -0700110 VLOG("%s DUMP:", name.c_str());
Yao Chen729093d2017-10-16 10:33:26 -0700111 for (const auto& pair : conditions) {
Yao Chen967b2052017-11-07 16:36:43 -0800112 VLOG("\t%s : %d", pair.first.c_str(), pair.second);
Yao Chen729093d2017-10-16 10:33:26 -0700113 }
114}
115
Yao Chen967b2052017-11-07 16:36:43 -0800116void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
117 std::vector<bool>& conditionChangedCache) {
118 // Unless the default condition is false, and there was nothing started, otherwise we have
119 // triggered a condition change.
120 conditionChangedCache[mIndex] =
121 (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
122 : true;
123
124 // After StopAll, we know everything has stopped. From now on, default condition is false.
125 mInitialValue = ConditionState::kFalse;
126 mSlicedConditionState.clear();
127 conditionCache[mIndex] = ConditionState::kFalse;
128}
129
Yao Chenb3561512017-11-21 18:07:17 -0800130bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
131 if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) {
132 // if the condition is not sliced or the key is not new, we are good!
133 return false;
134 }
135 // 1. Report the tuple count if the tuple count > soft limit
136 if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
137 size_t newTupleCount = mSlicedConditionState.size() + 1;
138 StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mName, newTupleCount);
139 // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
140 if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
Stefan Lafon12d01fa2017-12-04 20:56:09 -0800141 ALOGE("Predicate %s dropping data for dimension key %s", mName.c_str(), newKey.c_str());
Yao Chenb3561512017-11-21 18:07:17 -0800142 return true;
143 }
144 }
145 return false;
146}
147
Yao Chen967b2052017-11-07 16:36:43 -0800148void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
149 bool matchStart,
150 std::vector<ConditionState>& conditionCache,
151 std::vector<bool>& conditionChangedCache) {
Yangster-mac20877162017-12-22 17:19:39 -0800152 if ((int)conditionChangedCache.size() <= mIndex) {
153 ALOGE("handleConditionEvent: param conditionChangedCache not initialized.");
154 return;
155 }
156 if ((int)conditionCache.size() <= mIndex) {
157 ALOGE("handleConditionEvent: param conditionCache not initialized.");
158 return;
159 }
Yao Chen967b2052017-11-07 16:36:43 -0800160 bool changed = false;
161 auto outputIt = mSlicedConditionState.find(outputKey);
162 ConditionState newCondition;
Yao Chenb3561512017-11-21 18:07:17 -0800163 if (hitGuardRail(outputKey)) {
164 conditionChangedCache[mIndex] = false;
165 // Tells the caller it's evaluated.
166 conditionCache[mIndex] = ConditionState::kUnknown;
167 return;
168 }
Yao Chen967b2052017-11-07 16:36:43 -0800169 if (outputIt == mSlicedConditionState.end()) {
170 // We get a new output key.
171 newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
172 if (matchStart && mInitialValue != ConditionState::kTrue) {
173 mSlicedConditionState[outputKey] = 1;
174 changed = true;
175 } else if (mInitialValue != ConditionState::kFalse) {
176 // it's a stop and we don't have history about it.
177 // If the default condition is not false, it means this stop is valuable to us.
178 mSlicedConditionState[outputKey] = 0;
179 changed = true;
180 }
181 } else {
182 // we have history about this output key.
183 auto& startedCount = outputIt->second;
184 // assign the old value first.
185 newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
186 if (matchStart) {
187 if (startedCount == 0) {
188 // This condition for this output key will change from false -> true
189 changed = true;
190 }
191
192 // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
193 // as 1 if not counting nesting.
194 startedCount++;
195 newCondition = ConditionState::kTrue;
196 } else {
197 // This is a stop event.
198 if (startedCount > 0) {
199 if (mCountNesting) {
200 startedCount--;
201 if (startedCount == 0) {
202 newCondition = ConditionState::kFalse;
203 }
204 } else {
205 // not counting nesting, so ignore the number of starts, stop now.
206 startedCount = 0;
207 newCondition = ConditionState::kFalse;
208 }
209 // if everything has stopped for this output key, condition true -> false;
210 if (startedCount == 0) {
211 changed = true;
212 }
213 }
214
215 // if default condition is false, it means we don't need to keep the false values.
216 if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
217 mSlicedConditionState.erase(outputIt);
218 VLOG("erase key %s", outputKey.c_str());
219 }
220 }
221 }
222
223 // dump all dimensions for debugging
224 if (DEBUG) {
225 print(mSlicedConditionState, mName);
226 }
227
228 conditionChangedCache[mIndex] = changed;
229 conditionCache[mIndex] = newCondition;
230
Stefan Lafon12d01fa2017-12-04 20:56:09 -0800231 VLOG("SimplePredicate %s nonSlicedChange? %d", mName.c_str(),
Yao Chen967b2052017-11-07 16:36:43 -0800232 conditionChangedCache[mIndex] == true);
233}
234
235void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
Yao Chencaf339d2017-10-06 16:01:10 -0700236 const vector<MatchingState>& eventMatcherValues,
237 const vector<sp<ConditionTracker>>& mAllConditions,
238 vector<ConditionState>& conditionCache,
Yao Chen967b2052017-11-07 16:36:43 -0800239 vector<bool>& conditionChangedCache) {
Yao Chencaf339d2017-10-06 16:01:10 -0700240 if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
241 // it has been evaluated.
Yao Chend41c4222017-11-15 19:26:14 -0800242 VLOG("Yes, already evaluated, %s %d", mName.c_str(), conditionCache[mIndex]);
Yao Chen967b2052017-11-07 16:36:43 -0800243 return;
Yao Chencaf339d2017-10-06 16:01:10 -0700244 }
245
David Chenc18abed2017-11-22 16:47:59 -0800246 if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) &&
Yao Chen967b2052017-11-07 16:36:43 -0800247 eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
248 handleStopAll(conditionCache, conditionChangedCache);
249 return;
250 }
Yao Chen729093d2017-10-16 10:33:26 -0700251
Yao Chen967b2052017-11-07 16:36:43 -0800252 int matchedState = -1;
Yao Chencaf339d2017-10-06 16:01:10 -0700253 // Note: The order to evaluate the following start, stop, stop_all matters.
254 // The priority of overwrite is stop_all > stop > start.
255 if (mStartLogMatcherIndex >= 0 &&
256 eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
Yao Chen967b2052017-11-07 16:36:43 -0800257 matchedState = 1;
Yao Chencaf339d2017-10-06 16:01:10 -0700258 }
259
260 if (mStopLogMatcherIndex >= 0 &&
261 eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
Yao Chen967b2052017-11-07 16:36:43 -0800262 matchedState = 0;
Yao Chencaf339d2017-10-06 16:01:10 -0700263 }
264
Yao Chen967b2052017-11-07 16:36:43 -0800265 if (matchedState < 0) {
Yao Chend41c4222017-11-15 19:26:14 -0800266 // The event doesn't match this condition. So we just report existing condition values.
Yao Chen967b2052017-11-07 16:36:43 -0800267 conditionChangedCache[mIndex] = false;
Yao Chend41c4222017-11-15 19:26:14 -0800268 if (mSliced) {
269 // if the condition result is sliced. metrics won't directly get value from the
270 // cache, so just set any value other than kNotEvaluated.
271 conditionCache[mIndex] = ConditionState::kUnknown;
Yao Chend41c4222017-11-15 19:26:14 -0800272 } else {
Yangster7c334a12017-11-22 14:24:24 -0800273 const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
274 if (itr == mSlicedConditionState.end()) {
275 // condition not sliced, but we haven't seen the matched start or stop yet. so
276 // return initial value.
277 conditionCache[mIndex] = mInitialValue;
278 } else {
279 // return the cached condition.
280 conditionCache[mIndex] =
281 itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
282 }
Yao Chend41c4222017-11-15 19:26:14 -0800283 }
Yangster7c334a12017-11-22 14:24:24 -0800284
Yao Chen967b2052017-11-07 16:36:43 -0800285 return;
Yao Chen729093d2017-10-16 10:33:26 -0700286 }
287
Yangster-mac20877162017-12-22 17:19:39 -0800288 // outputKey is the output values. e.g, uid:1234
289 const std::vector<DimensionsValue> outputValues = getDimensionKeys(event, mOutputDimensions);
290 if (outputValues.size() == 0) {
291 // The original implementation would generate an empty string dimension hash when condition
292 // is not sliced.
293 handleConditionEvent(
294 DEFAULT_DIMENSION_KEY, matchedState == 1, conditionCache, conditionChangedCache);
295 } else if (outputValues.size() == 1) {
296 handleConditionEvent(HashableDimensionKey(outputValues[0]), matchedState == 1,
297 conditionCache, conditionChangedCache);
298 } else {
299 // If this event has multiple nodes in the attribution chain, this log event probably will
300 // generate multiple dimensions. If so, we will find if the condition changes for any
301 // dimension and ask the corresponding metric producer to verify whether the actual sliced
302 // condition has changed or not.
303 // A high level assumption is that a predicate is either sliced or unsliced. We will never
304 // have both sliced and unsliced version of a predicate.
305 for (const DimensionsValue& outputValue : outputValues) {
306 vector<ConditionState> dimensionalConditionCache(conditionCache.size(),
307 ConditionState::kNotEvaluated);
308 vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false);
309
310 handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1,
311 dimensionalConditionCache, dimensionalConditionChangedCache);
312
313 OrConditionState(dimensionalConditionCache, &conditionCache);
314 OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache);
315 }
316 }
Yao Chen729093d2017-10-16 10:33:26 -0700317}
318
319void SimpleConditionTracker::isConditionMet(
Yangster-mac20877162017-12-22 17:19:39 -0800320 const ConditionKey& conditionParameters,
Yangster7c334a12017-11-22 14:24:24 -0800321 const vector<sp<ConditionTracker>>& allConditions,
322 vector<ConditionState>& conditionCache) const {
Yao Chen729093d2017-10-16 10:33:26 -0700323 const auto pair = conditionParameters.find(mName);
Yao Chen967b2052017-11-07 16:36:43 -0800324
Yangster-mac20877162017-12-22 17:19:39 -0800325 if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) {
Stefan Lafon12d01fa2017-12-04 20:56:09 -0800326 ALOGE("Predicate %s output has dimension, but it's not specified in the query!",
Yao Chen967b2052017-11-07 16:36:43 -0800327 mName.c_str());
328 conditionCache[mIndex] = mInitialValue;
Yao Chen729093d2017-10-16 10:33:26 -0700329 return;
330 }
Yangster-mac20877162017-12-22 17:19:39 -0800331 std::vector<HashableDimensionKey> defaultKeys = {DEFAULT_DIMENSION_KEY};
332 const std::vector<HashableDimensionKey> &keys =
333 (pair == conditionParameters.end()) ? defaultKeys : pair->second;
Yao Chen729093d2017-10-16 10:33:26 -0700334
Yangster-mac20877162017-12-22 17:19:39 -0800335 ConditionState conditionState = ConditionState::kNotEvaluated;
336 for (const auto& key : keys) {
337 auto startedCountIt = mSlicedConditionState.find(key);
338 if (startedCountIt != mSlicedConditionState.end()) {
339 conditionState = conditionState |
340 (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
341 } else {
342 conditionState = conditionState | mInitialValue;
343 }
Yao Chen729093d2017-10-16 10:33:26 -0700344 }
Yangster-mac20877162017-12-22 17:19:39 -0800345 conditionCache[mIndex] = conditionState;
Stefan Lafon12d01fa2017-12-04 20:56:09 -0800346 VLOG("Predicate %s return %d", mName.c_str(), conditionCache[mIndex]);
Yao Chencaf339d2017-10-06 16:01:10 -0700347}
348
349} // namespace statsd
350} // namespace os
351} // namespace android