1/* 2 * Copyright 2018, 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#define DEBUG false // STOPSHIP if true 17#include "Log.h" 18 19#include "StateTracker.h" 20#include "guardrail/StatsdStats.h" 21 22namespace android { 23namespace os { 24namespace statsd { 25 26using std::string; 27using std::unordered_set; 28using std::vector; 29 30StateTracker::StateTracker(const ConfigKey& key, const int64_t& id, const int index, 31 const SimplePredicate& simplePredicate, 32 const unordered_map<int64_t, int>& trackerNameIndexMap, 33 const vector<Matcher> primaryKeys) 34 : ConditionTracker(id, index), mConfigKey(key), mPrimaryKeys(primaryKeys) { 35 if (simplePredicate.has_start()) { 36 auto pair = trackerNameIndexMap.find(simplePredicate.start()); 37 if (pair == trackerNameIndexMap.end()) { 38 ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start()); 39 return; 40 } 41 mStartLogMatcherIndex = pair->second; 42 mTrackerIndex.insert(mStartLogMatcherIndex); 43 } else { 44 ALOGW("Condition %lld must have a start matcher", (long long)id); 45 return; 46 } 47 48 if (simplePredicate.has_dimensions()) { 49 translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions); 50 if (mOutputDimensions.size() > 0) { 51 mSliced = true; 52 mDimensionTag = mOutputDimensions[0].mMatcher.getTag(); 53 } else { 54 ALOGW("Condition %lld has invalid dimensions", (long long)id); 55 return; 56 } 57 } else { 58 ALOGW("Condition %lld being a state tracker, but has no dimension", (long long)id); 59 return; 60 } 61 62 if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) { 63 mInitialValue = ConditionState::kFalse; 64 } else { 65 mInitialValue = ConditionState::kUnknown; 66 } 67 68 mNonSlicedConditionState = mInitialValue; 69 mInitialized = true; 70} 71 72StateTracker::~StateTracker() { 73 VLOG("~StateTracker()"); 74} 75 76bool StateTracker::init(const vector<Predicate>& allConditionConfig, 77 const vector<sp<ConditionTracker>>& allConditionTrackers, 78 const unordered_map<int64_t, int>& conditionIdIndexMap, 79 vector<bool>& stack) { 80 return mInitialized; 81} 82 83void StateTracker::dumpState() { 84 VLOG("StateTracker %lld DUMP:", (long long)mConditionId); 85 for (const auto& value : mSlicedState) { 86 VLOG("\t%s -> %s", value.first.toString().c_str(), value.second.toString().c_str()); 87 } 88 VLOG("Last Changed to True: "); 89 for (const auto& value : mLastChangedToTrueDimensions) { 90 VLOG("%s", value.toString().c_str()); 91 } 92 VLOG("Last Changed to False: "); 93 for (const auto& value : mLastChangedToFalseDimensions) { 94 VLOG("%s", value.toString().c_str()); 95 } 96} 97 98bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) { 99 if (mSlicedState.find(newKey) != mSlicedState.end()) { 100 // if the condition is not sliced or the key is not new, we are good! 101 return false; 102 } 103 // 1. Report the tuple count if the tuple count > soft limit 104 if (mSlicedState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { 105 size_t newTupleCount = mSlicedState.size() + 1; 106 StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount); 107 // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. 108 if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { 109 ALOGE("Predicate %lld dropping data for dimension key %s", 110 (long long)mConditionId, newKey.toString().c_str()); 111 return true; 112 } 113 } 114 return false; 115} 116 117void StateTracker::evaluateCondition(const LogEvent& event, 118 const vector<MatchingState>& eventMatcherValues, 119 const vector<sp<ConditionTracker>>& mAllConditions, 120 vector<ConditionState>& conditionCache, 121 vector<bool>& conditionChangedCache) { 122 mLastChangedToTrueDimensions.clear(); 123 mLastChangedToFalseDimensions.clear(); 124 if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { 125 // it has been evaluated. 126 VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]); 127 return; 128 } 129 130 if (mStartLogMatcherIndex >= 0 && 131 eventMatcherValues[mStartLogMatcherIndex] != MatchingState::kMatched) { 132 conditionCache[mIndex] = 133 mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse; 134 conditionChangedCache[mIndex] = false; 135 return; 136 } 137 138 VLOG("StateTracker evaluate event %s", event.ToString().c_str()); 139 140 // Primary key can exclusive fields must be simple fields. so there won't be more than 141 // one keys matched. 142 HashableDimensionKey primaryKey; 143 HashableDimensionKey state; 144 if ((mPrimaryKeys.size() > 0 && !filterValues(mPrimaryKeys, event.getValues(), &primaryKey)) || 145 !filterValues(mOutputDimensions, event.getValues(), &state)) { 146 ALOGE("Failed to filter fields in the event?? panic now!"); 147 conditionCache[mIndex] = 148 mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse; 149 conditionChangedCache[mIndex] = false; 150 return; 151 } 152 hitGuardRail(primaryKey); 153 154 VLOG("StateTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str()); 155 156 auto it = mSlicedState.find(primaryKey); 157 if (it == mSlicedState.end()) { 158 mSlicedState[primaryKey] = state; 159 conditionCache[mIndex] = ConditionState::kTrue; 160 mLastChangedToTrueDimensions.insert(state); 161 conditionChangedCache[mIndex] = true; 162 } else if (!(it->second == state)) { 163 mLastChangedToFalseDimensions.insert(it->second); 164 mLastChangedToTrueDimensions.insert(state); 165 mSlicedState[primaryKey] = state; 166 conditionCache[mIndex] = ConditionState::kTrue; 167 conditionChangedCache[mIndex] = true; 168 } else { 169 conditionCache[mIndex] = ConditionState::kTrue; 170 conditionChangedCache[mIndex] = false; 171 } 172 173 if (DEBUG) { 174 dumpState(); 175 } 176 return; 177} 178 179void StateTracker::isConditionMet( 180 const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, 181 const vector<Matcher>& dimensionFields, 182 const bool isSubOutputDimensionFields, 183 const bool isPartialLink, 184 vector<ConditionState>& conditionCache, 185 std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const { 186 if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { 187 // it has been evaluated. 188 VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]); 189 return; 190 } 191 192 const auto pair = conditionParameters.find(mConditionId); 193 if (pair == conditionParameters.end()) { 194 if (mSlicedState.size() > 0) { 195 conditionCache[mIndex] = ConditionState::kTrue; 196 197 for (const auto& state : mSlicedState) { 198 dimensionsKeySet.insert(state.second); 199 } 200 } else { 201 conditionCache[mIndex] = ConditionState::kUnknown; 202 } 203 return; 204 } 205 206 const auto& primaryKey = pair->second; 207 conditionCache[mIndex] = mInitialValue; 208 auto it = mSlicedState.find(primaryKey); 209 if (it != mSlicedState.end()) { 210 conditionCache[mIndex] = ConditionState::kTrue; 211 dimensionsKeySet.insert(it->second); 212 } 213} 214 215ConditionState StateTracker::getMetConditionDimension( 216 const std::vector<sp<ConditionTracker>>& allConditions, 217 const vector<Matcher>& dimensionFields, 218 const bool isSubOutputDimensionFields, 219 std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const { 220 if (mSlicedState.size() > 0) { 221 for (const auto& state : mSlicedState) { 222 dimensionsKeySet.insert(state.second); 223 } 224 return ConditionState::kTrue; 225 } 226 227 return mInitialValue; 228} 229 230} // namespace statsd 231} // namespace os 232} // namespace android