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