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 "MetricProducer.h"
21
22 #include "../guardrail/StatsdStats.h"
23 #include "state/StateTracker.h"
24
25 using android::util::FIELD_COUNT_REPEATED;
26 using android::util::FIELD_TYPE_ENUM;
27 using android::util::FIELD_TYPE_INT32;
28 using android::util::FIELD_TYPE_INT64;
29 using android::util::FIELD_TYPE_MESSAGE;
30 using android::util::ProtoOutputStream;
31
32 namespace android {
33 namespace os {
34 namespace statsd {
35
36
37 // for ActiveMetric
38 const int FIELD_ID_ACTIVE_METRIC_ID = 1;
39 const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2;
40
41 // for ActiveEventActivation
42 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
43 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
44 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
45
MetricProducer(const int64_t & metricId,const ConfigKey & key,const int64_t timeBaseNs,const int conditionIndex,const vector<ConditionState> & initialConditionCache,const sp<ConditionWizard> & wizard,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)46 MetricProducer::MetricProducer(
47 const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
48 const int conditionIndex, const vector<ConditionState>& initialConditionCache,
49 const sp<ConditionWizard>& wizard,
50 const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
51 const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
52 eventDeactivationMap,
53 const vector<int>& slicedStateAtoms,
54 const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
55 : mMetricId(metricId),
56 mConfigKey(key),
57 mTimeBaseNs(timeBaseNs),
58 mCurrentBucketStartTimeNs(timeBaseNs),
59 mCurrentBucketNum(0),
60 mCondition(initialCondition(conditionIndex, initialConditionCache)),
61 mConditionTrackerIndex(conditionIndex),
62 mConditionSliced(false),
63 mWizard(wizard),
64 mContainANYPositionInDimensionsInWhat(false),
65 mSliceByPositionALL(false),
66 mHasLinksToAllConditionDimensionsInTracker(false),
67 mEventActivationMap(eventActivationMap),
68 mEventDeactivationMap(eventDeactivationMap),
69 mIsActive(mEventActivationMap.empty()),
70 mSlicedStateAtoms(slicedStateAtoms),
71 mStateGroupMap(stateGroupMap) {
72 }
73
onMatchedLogEventLocked(const size_t matcherIndex,const LogEvent & event)74 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
75 if (!mIsActive) {
76 return;
77 }
78 int64_t eventTimeNs = event.GetElapsedTimestampNs();
79 // this is old event, maybe statsd restarted?
80 if (eventTimeNs < mTimeBaseNs) {
81 return;
82 }
83
84 bool condition;
85 ConditionKey conditionKey;
86 if (mConditionSliced) {
87 for (const auto& link : mMetric2ConditionLinks) {
88 getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
89 }
90 auto conditionState =
91 mWizard->query(mConditionTrackerIndex, conditionKey,
92 !mHasLinksToAllConditionDimensionsInTracker);
93 condition = (conditionState == ConditionState::kTrue);
94 } else {
95 // TODO: The unknown condition state is not handled here, we should fix it.
96 condition = mCondition == ConditionState::kTrue;
97 }
98
99 // Stores atom id to primary key pairs for each state atom that the metric is
100 // sliced by.
101 std::map<int32_t, HashableDimensionKey> statePrimaryKeys;
102
103 // For states with primary fields, use MetricStateLinks to get the primary
104 // field values from the log event. These values will form a primary key
105 // that will be used to query StateTracker for the correct state value.
106 for (const auto& stateLink : mMetric2StateLinks) {
107 getDimensionForState(event.getValues(), stateLink,
108 &statePrimaryKeys[stateLink.stateAtomId]);
109 }
110
111 // For each sliced state, query StateTracker for the state value using
112 // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
113 //
114 // Expected functionality: for any case where the MetricStateLinks are
115 // initialized incorrectly (ex. # of state links != # of primary fields, no
116 // links are provided for a state with primary fields, links are provided
117 // in the wrong order, etc.), StateTracker will simply return kStateUnknown
118 // when queried using an incorrect key.
119 HashableDimensionKey stateValuesKey;
120 for (auto atomId : mSlicedStateAtoms) {
121 FieldValue value;
122 if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
123 // found a primary key for this state, query using the key
124 queryStateValue(atomId, statePrimaryKeys[atomId], &value);
125 } else {
126 // if no MetricStateLinks exist for this state atom,
127 // query using the default dimension key (empty HashableDimensionKey)
128 queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
129 }
130 mapStateValue(atomId, &value);
131 stateValuesKey.addValue(value);
132 }
133
134 HashableDimensionKey dimensionInWhat;
135 filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
136 MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
137 onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
138 statePrimaryKeys);
139 }
140
evaluateActiveStateLocked(int64_t elapsedTimestampNs)141 bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
142 bool isActive = mEventActivationMap.empty();
143 for (auto& it : mEventActivationMap) {
144 if (it.second->state == ActivationState::kActive &&
145 elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) {
146 it.second->state = ActivationState::kNotActive;
147 }
148 if (it.second->state == ActivationState::kActive) {
149 isActive = true;
150 }
151 }
152 return isActive;
153 }
154
flushIfExpire(int64_t elapsedTimestampNs)155 void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
156 std::lock_guard<std::mutex> lock(mMutex);
157 if (!mIsActive) {
158 return;
159 }
160 mIsActive = evaluateActiveStateLocked(elapsedTimestampNs);
161 if (!mIsActive) {
162 onActiveStateChangedLocked(elapsedTimestampNs);
163 }
164 }
165
activateLocked(int activationTrackerIndex,int64_t elapsedTimestampNs)166 void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
167 auto it = mEventActivationMap.find(activationTrackerIndex);
168 if (it == mEventActivationMap.end()) {
169 return;
170 }
171 auto& activation = it->second;
172 if (ACTIVATE_ON_BOOT == activation->activationType) {
173 if (ActivationState::kNotActive == activation->state) {
174 activation->state = ActivationState::kActiveOnBoot;
175 }
176 // If the Activation is already active or set to kActiveOnBoot, do nothing.
177 return;
178 }
179 activation->start_ns = elapsedTimestampNs;
180 activation->state = ActivationState::kActive;
181 bool oldActiveState = mIsActive;
182 mIsActive = true;
183 if (!oldActiveState) { // Metric went from not active to active.
184 onActiveStateChangedLocked(elapsedTimestampNs);
185 }
186 }
187
cancelEventActivationLocked(int deactivationTrackerIndex)188 void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
189 auto it = mEventDeactivationMap.find(deactivationTrackerIndex);
190 if (it == mEventDeactivationMap.end()) {
191 return;
192 }
193 for (auto activationToCancelIt : it->second) {
194 activationToCancelIt->state = ActivationState::kNotActive;
195 }
196 }
197
loadActiveMetricLocked(const ActiveMetric & activeMetric,int64_t currentTimeNs)198 void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
199 int64_t currentTimeNs) {
200 if (mEventActivationMap.size() == 0) {
201 return;
202 }
203 for (int i = 0; i < activeMetric.activation_size(); i++) {
204 const auto& activeEventActivation = activeMetric.activation(i);
205 auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index());
206 if (it == mEventActivationMap.end()) {
207 ALOGE("Saved event activation not found");
208 continue;
209 }
210 auto& activation = it->second;
211 // If the event activation does not have a state, assume it is active.
212 if (!activeEventActivation.has_state() ||
213 activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
214 // We don't want to change the ttl for future activations, so we set the start_ns
215 // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
216 activation->start_ns =
217 currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
218 activation->state = ActivationState::kActive;
219 mIsActive = true;
220 } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
221 activation->state = ActivationState::kActiveOnBoot;
222 }
223 }
224 }
225
writeActiveMetricToProtoOutputStream(int64_t currentTimeNs,const DumpReportReason reason,ProtoOutputStream * proto)226 void MetricProducer::writeActiveMetricToProtoOutputStream(
227 int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
228 proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
229 for (auto& it : mEventActivationMap) {
230 const int atom_matcher_index = it.first;
231 const std::shared_ptr<Activation>& activation = it.second;
232
233 if (ActivationState::kNotActive == activation->state ||
234 (ActivationState::kActive == activation->state &&
235 activation->start_ns + activation->ttl_ns < currentTimeNs)) {
236 continue;
237 }
238
239 const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
240 FIELD_ID_ACTIVE_METRIC_ACTIVATION);
241 proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX,
242 atom_matcher_index);
243 if (ActivationState::kActive == activation->state) {
244 const int64_t remainingTtlNs =
245 activation->start_ns + activation->ttl_ns - currentTimeNs;
246 proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
247 (long long)remainingTtlNs);
248 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
249 ActiveEventActivation::ACTIVE);
250
251 } else if (ActivationState::kActiveOnBoot == activation->state) {
252 if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
253 proto->write(
254 FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
255 (long long)activation->ttl_ns);
256 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
257 ActiveEventActivation::ACTIVE);
258 } else if (reason == STATSCOMPANION_DIED) {
259 // We are saving because of system server death, not due to a device shutdown.
260 // Next time we load, we do not want to activate metrics that activate on boot.
261 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
262 ActiveEventActivation::ACTIVATE_ON_BOOT);
263 }
264 }
265 proto->end(activationToken);
266 }
267 }
268
queryStateValue(const int32_t atomId,const HashableDimensionKey & queryKey,FieldValue * value)269 void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
270 FieldValue* value) {
271 if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
272 value->mValue = Value(StateTracker::kStateUnknown);
273 value->mField.setTag(atomId);
274 ALOGW("StateTracker not found for state atom %d", atomId);
275 return;
276 }
277 }
278
mapStateValue(const int32_t atomId,FieldValue * value)279 void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) {
280 // check if there is a state map for this atom
281 auto atomIt = mStateGroupMap.find(atomId);
282 if (atomIt == mStateGroupMap.end()) {
283 return;
284 }
285 auto valueIt = atomIt->second.find(value->mValue.int_value);
286 if (valueIt == atomIt->second.end()) {
287 // state map exists, but value was not put in a state group
288 // so set mValue to kStateUnknown
289 // TODO(tsaichristine): handle incomplete state maps
290 value->mValue.setInt(StateTracker::kStateUnknown);
291 } else {
292 // set mValue to group_id
293 value->mValue.setLong(valueIt->second);
294 }
295 }
296
getUnknownStateKey()297 HashableDimensionKey MetricProducer::getUnknownStateKey() {
298 HashableDimensionKey stateKey;
299 for (auto atom : mSlicedStateAtoms) {
300 FieldValue fieldValue;
301 fieldValue.mField.setTag(atom);
302 fieldValue.mValue.setInt(StateTracker::kStateUnknown);
303 stateKey.addValue(fieldValue);
304 }
305 return stateKey;
306 }
307
buildDropEvent(const int64_t dropTimeNs,const BucketDropReason reason)308 DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason) {
309 DropEvent event;
310 event.reason = reason;
311 event.dropTimeNs = dropTimeNs;
312 return event;
313 }
314
maxDropEventsReached()315 bool MetricProducer::maxDropEventsReached() {
316 return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents;
317 }
318
319 } // namespace statsd
320 } // namespace os
321 } // namespace android
322