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