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 #include "MetricProducer.h"
20
21 using android::util::FIELD_COUNT_REPEATED;
22 using android::util::FIELD_TYPE_ENUM;
23 using android::util::FIELD_TYPE_INT32;
24 using android::util::FIELD_TYPE_INT64;
25 using android::util::FIELD_TYPE_MESSAGE;
26 using android::util::ProtoOutputStream;
27
28 namespace android {
29 namespace os {
30 namespace statsd {
31
32
33 // for ActiveMetric
34 const int FIELD_ID_ACTIVE_METRIC_ID = 1;
35 const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2;
36
37 // for ActiveEventActivation
38 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
39 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
40 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
41
onMatchedLogEventLocked(const size_t matcherIndex,const LogEvent & event)42 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
43 if (!mIsActive) {
44 return;
45 }
46 int64_t eventTimeNs = event.GetElapsedTimestampNs();
47 // this is old event, maybe statsd restarted?
48 if (eventTimeNs < mTimeBaseNs) {
49 return;
50 }
51
52 bool condition;
53 ConditionKey conditionKey;
54 std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
55 if (mConditionSliced) {
56 for (const auto& link : mMetric2ConditionLinks) {
57 getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
58 }
59 auto conditionState =
60 mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
61 !mSameConditionDimensionsInTracker,
62 !mHasLinksToAllConditionDimensionsInTracker,
63 &dimensionKeysInCondition);
64 condition = (conditionState == ConditionState::kTrue);
65 } else {
66 // TODO: The unknown condition state is not handled here, we should fix it.
67 condition = mCondition == ConditionState::kTrue;
68 }
69
70 if (mDimensionsInCondition.empty() && condition) {
71 dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
72 }
73
74 HashableDimensionKey dimensionInWhat;
75 filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
76 MetricDimensionKey metricKey(dimensionInWhat, DEFAULT_DIMENSION_KEY);
77 for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
78 metricKey.setDimensionKeyInCondition(conditionDimensionKey);
79 onMatchedLogEventInternalLocked(
80 matcherIndex, metricKey, conditionKey, condition, event);
81 }
82 if (dimensionKeysInCondition.empty()) {
83 onMatchedLogEventInternalLocked(
84 matcherIndex, metricKey, conditionKey, condition, event);
85 }
86 }
87
evaluateActiveStateLocked(int64_t elapsedTimestampNs)88 bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
89 bool isActive = mEventActivationMap.empty();
90 for (auto& it : mEventActivationMap) {
91 if (it.second->state == ActivationState::kActive &&
92 elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) {
93 it.second->state = ActivationState::kNotActive;
94 }
95 if (it.second->state == ActivationState::kActive) {
96 isActive = true;
97 }
98 }
99 return isActive;
100 }
101
flushIfExpire(int64_t elapsedTimestampNs)102 void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
103 std::lock_guard<std::mutex> lock(mMutex);
104 if (!mIsActive) {
105 return;
106 }
107 mIsActive = evaluateActiveStateLocked(elapsedTimestampNs);
108 if (!mIsActive) {
109 onActiveStateChangedLocked(elapsedTimestampNs);
110 }
111 }
112
addActivation(int activationTrackerIndex,const ActivationType & activationType,int64_t ttl_seconds,int deactivationTrackerIndex)113 void MetricProducer::addActivation(int activationTrackerIndex, const ActivationType& activationType,
114 int64_t ttl_seconds, int deactivationTrackerIndex) {
115 std::lock_guard<std::mutex> lock(mMutex);
116 // When a metric producer does not depend on any activation, its mIsActive is true.
117 // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
118 // change.
119 if (mEventActivationMap.empty()) {
120 mIsActive = false;
121 }
122 std::shared_ptr<Activation> activation =
123 std::make_shared<Activation>(activationType, ttl_seconds * NS_PER_SEC);
124 mEventActivationMap.emplace(activationTrackerIndex, activation);
125 if (-1 != deactivationTrackerIndex) {
126 auto& deactivationList = mEventDeactivationMap[deactivationTrackerIndex];
127 deactivationList.push_back(activation);
128 }
129 }
130
activateLocked(int activationTrackerIndex,int64_t elapsedTimestampNs)131 void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
132 auto it = mEventActivationMap.find(activationTrackerIndex);
133 if (it == mEventActivationMap.end()) {
134 return;
135 }
136 auto& activation = it->second;
137 if (ACTIVATE_ON_BOOT == activation->activationType) {
138 if (ActivationState::kNotActive == activation->state) {
139 activation->state = ActivationState::kActiveOnBoot;
140 }
141 // If the Activation is already active or set to kActiveOnBoot, do nothing.
142 return;
143 }
144 activation->start_ns = elapsedTimestampNs;
145 activation->state = ActivationState::kActive;
146 bool oldActiveState = mIsActive;
147 mIsActive = true;
148 if (!oldActiveState) { // Metric went from not active to active.
149 onActiveStateChangedLocked(elapsedTimestampNs);
150 }
151 }
152
cancelEventActivationLocked(int deactivationTrackerIndex)153 void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
154 auto it = mEventDeactivationMap.find(deactivationTrackerIndex);
155 if (it == mEventDeactivationMap.end()) {
156 return;
157 }
158 for (auto activationToCancelIt : it->second) {
159 activationToCancelIt->state = ActivationState::kNotActive;
160 }
161 }
162
loadActiveMetricLocked(const ActiveMetric & activeMetric,int64_t currentTimeNs)163 void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
164 int64_t currentTimeNs) {
165 if (mEventActivationMap.size() == 0) {
166 return;
167 }
168 for (int i = 0; i < activeMetric.activation_size(); i++) {
169 const auto& activeEventActivation = activeMetric.activation(i);
170 auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index());
171 if (it == mEventActivationMap.end()) {
172 ALOGE("Saved event activation not found");
173 continue;
174 }
175 auto& activation = it->second;
176 // If the event activation does not have a state, assume it is active.
177 if (!activeEventActivation.has_state() ||
178 activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
179 // We don't want to change the ttl for future activations, so we set the start_ns
180 // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
181 activation->start_ns =
182 currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
183 activation->state = ActivationState::kActive;
184 mIsActive = true;
185 } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
186 activation->state = ActivationState::kActiveOnBoot;
187 }
188 }
189 }
190
writeActiveMetricToProtoOutputStream(int64_t currentTimeNs,const DumpReportReason reason,ProtoOutputStream * proto)191 void MetricProducer::writeActiveMetricToProtoOutputStream(
192 int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
193 proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
194 for (auto& it : mEventActivationMap) {
195 const int atom_matcher_index = it.first;
196 const std::shared_ptr<Activation>& activation = it.second;
197
198 if (ActivationState::kNotActive == activation->state ||
199 (ActivationState::kActive == activation->state &&
200 activation->start_ns + activation->ttl_ns < currentTimeNs)) {
201 continue;
202 }
203
204 const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
205 FIELD_ID_ACTIVE_METRIC_ACTIVATION);
206 proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX,
207 atom_matcher_index);
208 if (ActivationState::kActive == activation->state) {
209 const int64_t remainingTtlNs =
210 activation->start_ns + activation->ttl_ns - currentTimeNs;
211 proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
212 (long long)remainingTtlNs);
213 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
214 ActiveEventActivation::ACTIVE);
215
216 } else if (ActivationState::kActiveOnBoot == activation->state) {
217 if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
218 proto->write(
219 FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
220 (long long)activation->ttl_ns);
221 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
222 ActiveEventActivation::ACTIVE);
223 } else if (reason == STATSCOMPANION_DIED) {
224 // We are saving because of system server death, not due to a device shutdown.
225 // Next time we load, we do not want to activate metrics that activate on boot.
226 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
227 ActiveEventActivation::ACTIVATE_ON_BOOT);
228 }
229 }
230 proto->end(activationToken);
231 }
232 }
233
234 } // namespace statsd
235 } // namespace os
236 } // namespace android
237