• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 STATSD_DEBUG false  // STOPSHIP if true
18 #include "Log.h"
19 
20 #include "MetricProducer.h"
21 
22 #include "../guardrail/StatsdStats.h"
23 #include "metrics/parsing_utils/metrics_manager_util.h"
24 #include "state/StateTracker.h"
25 
26 using android::util::FIELD_COUNT_REPEATED;
27 using android::util::FIELD_TYPE_ENUM;
28 using android::util::FIELD_TYPE_INT32;
29 using android::util::FIELD_TYPE_INT64;
30 using android::util::FIELD_TYPE_MESSAGE;
31 using android::util::ProtoOutputStream;
32 
33 namespace android {
34 namespace os {
35 namespace statsd {
36 
37 
38 // for ActiveMetric
39 const int FIELD_ID_ACTIVE_METRIC_ID = 1;
40 const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2;
41 
42 // for ActiveEventActivation
43 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
44 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
45 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
46 
MetricProducer(const int64_t & metricId,const ConfigKey & key,const int64_t timeBaseNs,const int conditionIndex,const vector<ConditionState> & initialConditionCache,const sp<ConditionWizard> & wizard,const uint64_t protoHash,const std::unordered_map<int,std::shared_ptr<Activation>> & eventActivationMap,const std::unordered_map<int,std::vector<std::shared_ptr<Activation>>> & eventDeactivationMap,const vector<int> & slicedStateAtoms,const unordered_map<int,unordered_map<int,int64_t>> & stateGroupMap,const optional<bool> splitBucketForAppUpgrade)47 MetricProducer::MetricProducer(
48         const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
49         const int conditionIndex, const vector<ConditionState>& initialConditionCache,
50         const sp<ConditionWizard>& wizard, const uint64_t protoHash,
51         const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
52         const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
53                 eventDeactivationMap,
54         const vector<int>& slicedStateAtoms,
55         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap,
56         const optional<bool> splitBucketForAppUpgrade)
57     : mMetricId(metricId),
58       mProtoHash(protoHash),
59       mConfigKey(key),
60       mValid(true),
61       mTimeBaseNs(timeBaseNs),
62       mCurrentBucketStartTimeNs(timeBaseNs),
63       mCurrentBucketNum(0),
64       mCondition(initialCondition(conditionIndex, initialConditionCache)),
65       // For metrics with pull events, condition timer will be set later within the constructor
66       mConditionTimer(false, timeBaseNs),
67       mConditionTrackerIndex(conditionIndex),
68       mConditionSliced(false),
69       mWizard(wizard),
70       mContainANYPositionInDimensionsInWhat(false),
71       mShouldUseNestedDimensions(false),
72       mHasLinksToAllConditionDimensionsInTracker(false),
73       mEventActivationMap(eventActivationMap),
74       mEventDeactivationMap(eventDeactivationMap),
75       mIsActive(mEventActivationMap.empty()),
76       mSlicedStateAtoms(slicedStateAtoms),
77       mStateGroupMap(stateGroupMap),
78       mSplitBucketForAppUpgrade(splitBucketForAppUpgrade),
79       mHasHitGuardrail(false),
80       mSampledWhatFields({}),
81       mShardCount(0) {
82 }
83 
onConfigUpdatedLocked(const StatsdConfig & config,const int configIndex,const int metricIndex,const vector<sp<AtomMatchingTracker>> & allAtomMatchingTrackers,const unordered_map<int64_t,int> & oldAtomMatchingTrackerMap,const unordered_map<int64_t,int> & newAtomMatchingTrackerMap,const sp<EventMatcherWizard> & matcherWizard,const vector<sp<ConditionTracker>> & allConditionTrackers,const unordered_map<int64_t,int> & conditionTrackerMap,const sp<ConditionWizard> & wizard,const unordered_map<int64_t,int> & metricToActivationMap,unordered_map<int,vector<int>> & trackerToMetricMap,unordered_map<int,vector<int>> & conditionToMetricMap,unordered_map<int,vector<int>> & activationAtomTrackerToMetricMap,unordered_map<int,vector<int>> & deactivationAtomTrackerToMetricMap,vector<int> & metricsWithActivation)84 optional<InvalidConfigReason> MetricProducer::onConfigUpdatedLocked(
85         const StatsdConfig& config, const int configIndex, const int metricIndex,
86         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
87         const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
88         const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
89         const sp<EventMatcherWizard>& matcherWizard,
90         const vector<sp<ConditionTracker>>& allConditionTrackers,
91         const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
92         const unordered_map<int64_t, int>& metricToActivationMap,
93         unordered_map<int, vector<int>>& trackerToMetricMap,
94         unordered_map<int, vector<int>>& conditionToMetricMap,
95         unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
96         unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
97         vector<int>& metricsWithActivation) {
98     sp<ConditionWizard> tmpWizard = mWizard;
99     mWizard = wizard;
100 
101     unordered_map<int, shared_ptr<Activation>> newEventActivationMap;
102     unordered_map<int, vector<shared_ptr<Activation>>> newEventDeactivationMap;
103     optional<InvalidConfigReason> invalidConfigReason = handleMetricActivationOnConfigUpdate(
104             config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap,
105             newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap,
106             deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap,
107             newEventDeactivationMap);
108     if (invalidConfigReason.has_value()) {
109         return invalidConfigReason;
110     }
111     mEventActivationMap = newEventActivationMap;
112     mEventDeactivationMap = newEventDeactivationMap;
113     mAnomalyTrackers.clear();
114     return nullopt;
115 }
116 
onMatchedLogEventLocked(const size_t matcherIndex,const LogEvent & event)117 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
118     if (!mIsActive) {
119         return;
120     }
121     int64_t eventTimeNs = event.GetElapsedTimestampNs();
122     // this is old event, maybe statsd restarted?
123     if (eventTimeNs < mTimeBaseNs) {
124         return;
125     }
126 
127     if (!passesSampleCheckLocked(event.getValues())) {
128         return;
129     }
130 
131     bool condition;
132     ConditionKey conditionKey;
133     if (mConditionSliced) {
134         for (const auto& link : mMetric2ConditionLinks) {
135             getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
136         }
137         auto conditionState =
138             mWizard->query(mConditionTrackerIndex, conditionKey,
139                            !mHasLinksToAllConditionDimensionsInTracker);
140         condition = (conditionState == ConditionState::kTrue);
141     } else {
142         // TODO: The unknown condition state is not handled here, we should fix it.
143         condition = mCondition == ConditionState::kTrue;
144     }
145 
146     // Stores atom id to primary key pairs for each state atom that the metric is
147     // sliced by.
148     std::map<int32_t, HashableDimensionKey> statePrimaryKeys;
149 
150     // For states with primary fields, use MetricStateLinks to get the primary
151     // field values from the log event. These values will form a primary key
152     // that will be used to query StateTracker for the correct state value.
153     for (const auto& stateLink : mMetric2StateLinks) {
154         getDimensionForState(event.getValues(), stateLink,
155                              &statePrimaryKeys[stateLink.stateAtomId]);
156     }
157 
158     // For each sliced state, query StateTracker for the state value using
159     // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
160     //
161     // Expected functionality: for any case where the MetricStateLinks are
162     // initialized incorrectly (ex. # of state links != # of primary fields, no
163     // links are provided for a state with primary fields, links are provided
164     // in the wrong order, etc.), StateTracker will simply return kStateUnknown
165     // when queried using an incorrect key.
166     HashableDimensionKey stateValuesKey;
167     for (auto atomId : mSlicedStateAtoms) {
168         FieldValue value;
169         if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
170             // found a primary key for this state, query using the key
171             queryStateValue(atomId, statePrimaryKeys[atomId], &value);
172         } else {
173             // if no MetricStateLinks exist for this state atom,
174             // query using the default dimension key (empty HashableDimensionKey)
175             queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
176         }
177         mapStateValue(atomId, &value);
178         stateValuesKey.addValue(value);
179     }
180 
181     HashableDimensionKey dimensionInWhat;
182     filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
183     MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
184     onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
185                                     statePrimaryKeys);
186 }
187 
evaluateActiveStateLocked(int64_t elapsedTimestampNs)188 bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
189     bool isActive = mEventActivationMap.empty();
190     for (auto& it : mEventActivationMap) {
191         if (it.second->state == ActivationState::kActive &&
192             elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) {
193             it.second->state = ActivationState::kNotActive;
194         }
195         if (it.second->state == ActivationState::kActive) {
196             isActive = true;
197         }
198     }
199     return isActive;
200 }
201 
flushIfExpire(int64_t elapsedTimestampNs)202 void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
203     std::lock_guard<std::mutex> lock(mMutex);
204     if (!mIsActive) {
205         return;
206     }
207     const bool isActive = evaluateActiveStateLocked(elapsedTimestampNs);
208     if (!isActive) {  // Metric went from active to not active.
209         onActiveStateChangedLocked(elapsedTimestampNs, false);
210 
211         // Set mIsActive to false after onActiveStateChangedLocked to ensure any pulls that occur
212         // through onActiveStateChangedLocked are processed.
213         mIsActive = false;
214     }
215 }
216 
activateLocked(int activationTrackerIndex,int64_t elapsedTimestampNs)217 void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
218     auto it = mEventActivationMap.find(activationTrackerIndex);
219     if (it == mEventActivationMap.end()) {
220         return;
221     }
222     auto& activation = it->second;
223     if (ACTIVATE_ON_BOOT == activation->activationType) {
224         if (ActivationState::kNotActive == activation->state) {
225             activation->state = ActivationState::kActiveOnBoot;
226         }
227         // If the Activation is already active or set to kActiveOnBoot, do nothing.
228         return;
229     }
230     activation->start_ns = elapsedTimestampNs;
231     activation->state = ActivationState::kActive;
232     if (!mIsActive) {  // Metric was previously inactive and now is active.
233         // Set mIsActive to true before onActiveStateChangedLocked to ensure any pulls that occur
234         // through onActiveStateChangedLocked are processed.
235         mIsActive = true;
236 
237         onActiveStateChangedLocked(elapsedTimestampNs, true);
238     }
239 }
240 
cancelEventActivationLocked(int deactivationTrackerIndex)241 void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
242     auto it = mEventDeactivationMap.find(deactivationTrackerIndex);
243     if (it == mEventDeactivationMap.end()) {
244         return;
245     }
246     for (auto activationToCancelIt : it->second)  {
247         activationToCancelIt->state = ActivationState::kNotActive;
248     }
249 }
250 
loadActiveMetricLocked(const ActiveMetric & activeMetric,int64_t currentTimeNs)251 void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
252                                             int64_t currentTimeNs) {
253     if (mEventActivationMap.size() == 0) {
254         return;
255     }
256     for (int i = 0; i < activeMetric.activation_size(); i++) {
257         const auto& activeEventActivation = activeMetric.activation(i);
258         auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index());
259         if (it == mEventActivationMap.end()) {
260             ALOGE("Saved event activation not found");
261             continue;
262         }
263         auto& activation = it->second;
264         // If the event activation does not have a state, assume it is active.
265         if (!activeEventActivation.has_state() ||
266                 activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
267             // We don't want to change the ttl for future activations, so we set the start_ns
268             // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
269             activation->start_ns =
270                 currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
271             activation->state = ActivationState::kActive;
272             mIsActive = true;
273         } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
274             activation->state = ActivationState::kActiveOnBoot;
275         }
276     }
277 }
278 
writeActiveMetricToProtoOutputStream(int64_t currentTimeNs,const DumpReportReason reason,ProtoOutputStream * proto)279 void MetricProducer::writeActiveMetricToProtoOutputStream(
280         int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
281     proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
282     for (auto& it : mEventActivationMap) {
283         const int atom_matcher_index = it.first;
284         const std::shared_ptr<Activation>& activation = it.second;
285 
286         if (ActivationState::kNotActive == activation->state ||
287                 (ActivationState::kActive == activation->state &&
288                  activation->start_ns + activation->ttl_ns < currentTimeNs)) {
289             continue;
290         }
291 
292         const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
293                 FIELD_ID_ACTIVE_METRIC_ACTIVATION);
294         proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX,
295                 atom_matcher_index);
296         if (ActivationState::kActive == activation->state) {
297             const int64_t remainingTtlNs =
298                     activation->start_ns + activation->ttl_ns - currentTimeNs;
299             proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
300                     (long long)remainingTtlNs);
301             proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
302                     ActiveEventActivation::ACTIVE);
303 
304         } else if (ActivationState::kActiveOnBoot == activation->state) {
305             if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
306                 proto->write(
307                         FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
308                         (long long)activation->ttl_ns);
309                 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
310                                     ActiveEventActivation::ACTIVE);
311             } else if (reason == STATSCOMPANION_DIED) {
312                 // We are saving because of system server death, not due to a device shutdown.
313                 // Next time we load, we do not want to activate metrics that activate on boot.
314                 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
315                                                     ActiveEventActivation::ACTIVATE_ON_BOOT);
316             }
317         }
318         proto->end(activationToken);
319     }
320 }
321 
queryStateValue(const int32_t atomId,const HashableDimensionKey & queryKey,FieldValue * value)322 void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
323                                      FieldValue* value) {
324     if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
325         value->mValue = Value(StateTracker::kStateUnknown);
326         value->mField.setTag(atomId);
327         ALOGW("StateTracker not found for state atom %d", atomId);
328         return;
329     }
330 }
331 
mapStateValue(const int32_t atomId,FieldValue * value)332 void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) {
333     // check if there is a state map for this atom
334     auto atomIt = mStateGroupMap.find(atomId);
335     if (atomIt == mStateGroupMap.end()) {
336         return;
337     }
338     auto valueIt = atomIt->second.find(value->mValue.int_value);
339     if (valueIt == atomIt->second.end()) {
340         // state map exists, but value was not put in a state group
341         // so set mValue to kStateUnknown
342         // TODO(tsaichristine): handle incomplete state maps
343         value->mValue.setInt(StateTracker::kStateUnknown);
344     } else {
345         // set mValue to group_id
346         value->mValue.setLong(valueIt->second);
347     }
348 }
349 
getUnknownStateKey()350 HashableDimensionKey MetricProducer::getUnknownStateKey() {
351     HashableDimensionKey stateKey;
352     for (auto atom : mSlicedStateAtoms) {
353         FieldValue fieldValue;
354         fieldValue.mField.setTag(atom);
355         fieldValue.mValue.setInt(StateTracker::kStateUnknown);
356         stateKey.addValue(fieldValue);
357     }
358     return stateKey;
359 }
360 
buildDropEvent(const int64_t dropTimeNs,const BucketDropReason reason) const361 DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs,
362                                          const BucketDropReason reason) const {
363     DropEvent event;
364     event.reason = reason;
365     event.dropTimeNs = dropTimeNs;
366     return event;
367 }
368 
maxDropEventsReached() const369 bool MetricProducer::maxDropEventsReached() const {
370     return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents;
371 }
372 
passesSampleCheckLocked(const vector<FieldValue> & values) const373 bool MetricProducer::passesSampleCheckLocked(const vector<FieldValue>& values) const {
374     // Only perform sampling if shard count is correct and there is a sampled what field.
375     if (mShardCount <= 1 || mSampledWhatFields.size() == 0) {
376         return true;
377     }
378     // If filtering fails, don't perform sampling. Event could be a gauge trigger event or stop all
379     // event.
380     FieldValue sampleFieldValue;
381     if (!filterValues(mSampledWhatFields[0], values, &sampleFieldValue)) {
382         return true;
383     }
384     return shouldKeepSample(sampleFieldValue, ShardOffsetProvider::getInstance().getShardOffset(),
385                             mShardCount);
386 }
387 
388 }  // namespace statsd
389 }  // namespace os
390 }  // namespace android
391