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
22 namespace android {
23 namespace os {
24 namespace statsd {
25
26 using std::string;
27 using std::unordered_set;
28 using std::vector;
29
StateTracker(const ConfigKey & key,const int64_t & id,const int index,const SimplePredicate & simplePredicate,const unordered_map<int64_t,int> & trackerNameIndexMap,const vector<Matcher> primaryKeys)30 StateTracker::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
~StateTracker()72 StateTracker::~StateTracker() {
73 VLOG("~StateTracker()");
74 }
75
init(const vector<Predicate> & allConditionConfig,const vector<sp<ConditionTracker>> & allConditionTrackers,const unordered_map<int64_t,int> & conditionIdIndexMap,vector<bool> & stack)76 bool 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
dumpState()83 void 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
hitGuardRail(const HashableDimensionKey & newKey)98 bool 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
evaluateCondition(const LogEvent & event,const vector<MatchingState> & eventMatcherValues,const vector<sp<ConditionTracker>> & mAllConditions,vector<ConditionState> & conditionCache,vector<bool> & conditionChangedCache)117 void 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
isConditionMet(const ConditionKey & conditionParameters,const vector<sp<ConditionTracker>> & allConditions,const vector<Matcher> & dimensionFields,const bool isSubOutputDimensionFields,const bool isPartialLink,vector<ConditionState> & conditionCache,std::unordered_set<HashableDimensionKey> & dimensionsKeySet) const179 void 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
getMetConditionDimension(const std::vector<sp<ConditionTracker>> & allConditions,const vector<Matcher> & dimensionFields,const bool isSubOutputDimensionFields,std::unordered_set<HashableDimensionKey> & dimensionsKeySet) const215 ConditionState 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