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 // 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(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,const wp<ConfigMetadataProvider> configMetadataProvider)46 MetricProducer::MetricProducer(
47 int64_t metricId, const ConfigKey& key, const int64_t timeBaseNs, const int conditionIndex,
48 const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
49 const uint64_t protoHash,
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 const optional<bool> splitBucketForAppUpgrade,
56 const wp<ConfigMetadataProvider> configMetadataProvider)
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 mConfigMetadataProvider(configMetadataProvider) {
83 }
84
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)85 optional<InvalidConfigReason> MetricProducer::onConfigUpdatedLocked(
86 const StatsdConfig& config, const int configIndex, const int metricIndex,
87 const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
88 const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
89 const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
90 const sp<EventMatcherWizard>& matcherWizard,
91 const vector<sp<ConditionTracker>>& allConditionTrackers,
92 const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
93 const unordered_map<int64_t, int>& metricToActivationMap,
94 unordered_map<int, vector<int>>& trackerToMetricMap,
95 unordered_map<int, vector<int>>& conditionToMetricMap,
96 unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
97 unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
98 vector<int>& metricsWithActivation) {
99 sp<ConditionWizard> tmpWizard = mWizard;
100 mWizard = wizard;
101
102 unordered_map<int, shared_ptr<Activation>> newEventActivationMap;
103 unordered_map<int, vector<shared_ptr<Activation>>> newEventDeactivationMap;
104 optional<InvalidConfigReason> invalidConfigReason = handleMetricActivationOnConfigUpdate(
105 config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap,
106 newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap,
107 deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap,
108 newEventDeactivationMap);
109 if (invalidConfigReason.has_value()) {
110 return invalidConfigReason;
111 }
112 mEventActivationMap = newEventActivationMap;
113 mEventDeactivationMap = newEventDeactivationMap;
114 mAnomalyTrackers.clear();
115 return nullopt;
116 }
117
onMatchedLogEventLocked(const size_t matcherIndex,const LogEvent & event)118 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
119 if (!mIsActive) {
120 return;
121 }
122 int64_t eventTimeNs = event.GetElapsedTimestampNs();
123 // this is old event, maybe statsd restarted?
124 if (eventTimeNs < mTimeBaseNs) {
125 return;
126 }
127
128 if (!passesSampleCheckLocked(event.getValues())) {
129 return;
130 }
131
132 bool condition;
133 ConditionKey conditionKey;
134 if (mConditionSliced) {
135 for (const auto& link : mMetric2ConditionLinks) {
136 getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
137 }
138 auto conditionState =
139 mWizard->query(mConditionTrackerIndex, conditionKey,
140 !mHasLinksToAllConditionDimensionsInTracker);
141 condition = (conditionState == ConditionState::kTrue);
142 } else {
143 // TODO: The unknown condition state is not handled here, we should fix it.
144 condition = mCondition == ConditionState::kTrue;
145 }
146
147 // Stores atom id to primary key pairs for each state atom that the metric is
148 // sliced by.
149 std::map<int32_t, HashableDimensionKey> statePrimaryKeys;
150
151 // For states with primary fields, use MetricStateLinks to get the primary
152 // field values from the log event. These values will form a primary key
153 // that will be used to query StateTracker for the correct state value.
154 for (const auto& stateLink : mMetric2StateLinks) {
155 getDimensionForState(event.getValues(), stateLink,
156 &statePrimaryKeys[stateLink.stateAtomId]);
157 }
158
159 // For each sliced state, query StateTracker for the state value using
160 // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
161 //
162 // Expected functionality: for any case where the MetricStateLinks are
163 // initialized incorrectly (ex. # of state links != # of primary fields, no
164 // links are provided for a state with primary fields, links are provided
165 // in the wrong order, etc.), StateTracker will simply return kStateUnknown
166 // when queried using an incorrect key.
167 HashableDimensionKey stateValuesKey;
168 for (auto atomId : mSlicedStateAtoms) {
169 FieldValue value;
170 if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
171 // found a primary key for this state, query using the key
172 queryStateValue(atomId, statePrimaryKeys[atomId], &value);
173 } else {
174 // if no MetricStateLinks exist for this state atom,
175 // query using the default dimension key (empty HashableDimensionKey)
176 queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
177 }
178 mapStateValue(atomId, &value);
179 stateValuesKey.addValue(value);
180 }
181
182 HashableDimensionKey dimensionInWhat;
183 filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
184 MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
185 onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
186 statePrimaryKeys);
187 }
188
189 /**
190 * @brief Updates data corruption state overriding lower severity state with a higher
191 * Inherited classes are responsible for proper severity determination according
192 * to loss parameters (see @determineCorruptionSeverity)
193 */
onMatchedLogEventLostLocked(int32_t,DataCorruptedReason reason,LostAtomType atomType)194 void MetricProducer::onMatchedLogEventLostLocked(int32_t /*atomId*/, DataCorruptedReason reason,
195 LostAtomType atomType) {
196 const DataCorruptionSeverity newSeverity = determineCorruptionSeverity(reason, atomType);
197 switch (reason) {
198 case DATA_CORRUPTED_SOCKET_LOSS:
199 mDataCorruptedDueToSocketLoss = std::max(mDataCorruptedDueToSocketLoss, newSeverity);
200 break;
201 case DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW:
202 mDataCorruptedDueToQueueOverflow =
203 std::max(mDataCorruptedDueToQueueOverflow, newSeverity);
204 break;
205 default:
206 ALOGW("Unsupported data corruption reason reported %d", reason);
207 break;
208 }
209 }
210
resetDataCorruptionFlagsLocked()211 void MetricProducer::resetDataCorruptionFlagsLocked() {
212 if (mDataCorruptedDueToSocketLoss != DataCorruptionSeverity::kUnrecoverable) {
213 mDataCorruptedDueToSocketLoss = DataCorruptionSeverity::kNone;
214 }
215 if (mDataCorruptedDueToQueueOverflow != DataCorruptionSeverity::kUnrecoverable) {
216 mDataCorruptedDueToQueueOverflow = DataCorruptionSeverity::kNone;
217 }
218 }
219
evaluateActiveStateLocked(int64_t elapsedTimestampNs)220 bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
221 bool isActive = mEventActivationMap.empty();
222 for (auto& it : mEventActivationMap) {
223 if (it.second->state == ActivationState::kActive &&
224 elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) {
225 it.second->state = ActivationState::kNotActive;
226 }
227 if (it.second->state == ActivationState::kActive) {
228 isActive = true;
229 }
230 }
231 return isActive;
232 }
233
flushIfExpire(int64_t elapsedTimestampNs)234 void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
235 std::lock_guard<std::mutex> lock(mMutex);
236 if (!mIsActive) {
237 return;
238 }
239 const bool isActive = evaluateActiveStateLocked(elapsedTimestampNs);
240 if (!isActive) { // Metric went from active to not active.
241 onActiveStateChangedLocked(elapsedTimestampNs, false);
242
243 // Set mIsActive to false after onActiveStateChangedLocked to ensure any pulls that occur
244 // through onActiveStateChangedLocked are processed.
245 mIsActive = false;
246 }
247 }
248
activateLocked(int activationTrackerIndex,int64_t elapsedTimestampNs)249 void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
250 auto it = mEventActivationMap.find(activationTrackerIndex);
251 if (it == mEventActivationMap.end()) {
252 return;
253 }
254 auto& activation = it->second;
255 if (ACTIVATE_ON_BOOT == activation->activationType) {
256 if (ActivationState::kNotActive == activation->state) {
257 activation->state = ActivationState::kActiveOnBoot;
258 }
259 // If the Activation is already active or set to kActiveOnBoot, do nothing.
260 return;
261 }
262 activation->start_ns = elapsedTimestampNs;
263 activation->state = ActivationState::kActive;
264 if (!mIsActive) { // Metric was previously inactive and now is active.
265 // Set mIsActive to true before onActiveStateChangedLocked to ensure any pulls that occur
266 // through onActiveStateChangedLocked are processed.
267 mIsActive = true;
268
269 onActiveStateChangedLocked(elapsedTimestampNs, true);
270 }
271 }
272
cancelEventActivationLocked(int deactivationTrackerIndex)273 void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
274 auto it = mEventDeactivationMap.find(deactivationTrackerIndex);
275 if (it == mEventDeactivationMap.end()) {
276 return;
277 }
278 for (auto& activationToCancelIt : it->second) {
279 activationToCancelIt->state = ActivationState::kNotActive;
280 }
281 }
282
loadActiveMetricLocked(const ActiveMetric & activeMetric,int64_t currentTimeNs)283 void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
284 int64_t currentTimeNs) {
285 if (mEventActivationMap.size() == 0) {
286 return;
287 }
288 for (int i = 0; i < activeMetric.activation_size(); i++) {
289 const auto& activeEventActivation = activeMetric.activation(i);
290 auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index());
291 if (it == mEventActivationMap.end()) {
292 ALOGE("Saved event activation not found");
293 continue;
294 }
295 auto& activation = it->second;
296 // If the event activation does not have a state, assume it is active.
297 if (!activeEventActivation.has_state() ||
298 activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
299 // We don't want to change the ttl for future activations, so we set the start_ns
300 // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
301 activation->start_ns =
302 currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
303 activation->state = ActivationState::kActive;
304 mIsActive = true;
305 } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
306 activation->state = ActivationState::kActiveOnBoot;
307 }
308 }
309 }
310
writeActiveMetricToProtoOutputStream(int64_t currentTimeNs,const DumpReportReason reason,ProtoOutputStream * proto)311 void MetricProducer::writeActiveMetricToProtoOutputStream(
312 int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
313 proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
314 for (auto& it : mEventActivationMap) {
315 const int atom_matcher_index = it.first;
316 const std::shared_ptr<Activation>& activation = it.second;
317
318 if (ActivationState::kNotActive == activation->state ||
319 (ActivationState::kActive == activation->state &&
320 activation->start_ns + activation->ttl_ns < currentTimeNs)) {
321 continue;
322 }
323
324 const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
325 FIELD_ID_ACTIVE_METRIC_ACTIVATION);
326 proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX,
327 atom_matcher_index);
328 if (ActivationState::kActive == activation->state) {
329 const int64_t remainingTtlNs =
330 activation->start_ns + activation->ttl_ns - currentTimeNs;
331 proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
332 (long long)remainingTtlNs);
333 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
334 ActiveEventActivation::ACTIVE);
335
336 } else if (ActivationState::kActiveOnBoot == activation->state) {
337 if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
338 proto->write(
339 FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
340 (long long)activation->ttl_ns);
341 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
342 ActiveEventActivation::ACTIVE);
343 } else if (reason == STATSCOMPANION_DIED) {
344 // We are saving because of system server death, not due to a device shutdown.
345 // Next time we load, we do not want to activate metrics that activate on boot.
346 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
347 ActiveEventActivation::ACTIVATE_ON_BOOT);
348 }
349 }
350 proto->end(activationToken);
351 }
352 }
353
queryStateValue(int32_t atomId,const HashableDimensionKey & queryKey,FieldValue * value)354 void MetricProducer::queryStateValue(int32_t atomId, const HashableDimensionKey& queryKey,
355 FieldValue* value) {
356 if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
357 value->mValue = Value(StateTracker::kStateUnknown);
358 value->mField.setTag(atomId);
359 ALOGW("StateTracker not found for state atom %d", atomId);
360 return;
361 }
362 }
363
mapStateValue(int32_t atomId,FieldValue * value)364 void MetricProducer::mapStateValue(int32_t atomId, FieldValue* value) {
365 // check if there is a state map for this atom
366 auto atomIt = mStateGroupMap.find(atomId);
367 if (atomIt == mStateGroupMap.end()) {
368 return;
369 }
370 auto valueIt = atomIt->second.find(value->mValue.int_value);
371 if (valueIt == atomIt->second.end()) {
372 // state map exists, but value was not put in a state group
373 // so set mValue to kStateUnknown
374 // TODO(tsaichristine): handle incomplete state maps
375 value->mValue.setInt(StateTracker::kStateUnknown);
376 } else {
377 // set mValue to group_id
378 value->mValue.setLong(valueIt->second);
379 }
380 }
381
getUnknownStateKey()382 HashableDimensionKey MetricProducer::getUnknownStateKey() {
383 HashableDimensionKey stateKey;
384 for (auto atom : mSlicedStateAtoms) {
385 FieldValue fieldValue;
386 fieldValue.mField.setTag(atom);
387 fieldValue.mValue.setInt(StateTracker::kStateUnknown);
388 stateKey.addValue(fieldValue);
389 }
390 return stateKey;
391 }
392
buildDropEvent(const int64_t dropTimeNs,const BucketDropReason reason) const393 DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs,
394 const BucketDropReason reason) const {
395 DropEvent event;
396 event.reason = reason;
397 event.dropTimeNs = dropTimeNs;
398 return event;
399 }
400
maxDropEventsReached() const401 bool MetricProducer::maxDropEventsReached() const {
402 return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents;
403 }
404
passesSampleCheckLocked(const vector<FieldValue> & values) const405 bool MetricProducer::passesSampleCheckLocked(const vector<FieldValue>& values) const {
406 // Only perform sampling if shard count is correct and there is a sampled what field.
407 if (mShardCount <= 1 || mSampledWhatFields.size() == 0) {
408 return true;
409 }
410 // If filtering fails, don't perform sampling. Event could be a gauge trigger event or stop all
411 // event.
412 FieldValue sampleFieldValue;
413 if (!filterValues(mSampledWhatFields[0], values, &sampleFieldValue)) {
414 return true;
415 }
416 return shouldKeepSample(sampleFieldValue, ShardOffsetProvider::getInstance().getShardOffset(),
417 mShardCount);
418 }
419
getConfigMetadataProvider() const420 sp<ConfigMetadataProvider> MetricProducer::getConfigMetadataProvider() const {
421 sp<ConfigMetadataProvider> provider = mConfigMetadataProvider.promote();
422 if (provider == nullptr) {
423 ALOGE("Could not promote ConfigMetadataProvider");
424 StatsdStats::getInstance().noteConfigMetadataProviderPromotionFailed(mConfigKey);
425 }
426 return provider;
427 }
428
computeBucketSizeLocked(const bool isFullBucket,const MetricDimensionKey & dimKey,const bool isFirstBucket) const429 size_t MetricProducer::computeBucketSizeLocked(const bool isFullBucket,
430 const MetricDimensionKey& dimKey,
431 const bool isFirstBucket) const {
432 size_t bucketSize = 0;
433
434 // Bucket timestamps or bucket number
435 bucketSize += isFullBucket ? sizeof(int32_t) : 2 * sizeof(int64_t);
436
437 // Each dimension / state key can have multiple buckets. Add the size only for the first bucket.
438 if (isFirstBucket) {
439 bucketSize += dimKey.getSize(mShouldUseNestedDimensions);
440 }
441
442 return bucketSize;
443 }
444
computeOverheadSizeLocked(const bool hasPastBuckets,const bool dimensionGuardrailHit) const445 size_t MetricProducer::computeOverheadSizeLocked(const bool hasPastBuckets,
446 const bool dimensionGuardrailHit) const {
447 size_t overheadSize = 0;
448
449 // MetricId + isActive
450 overheadSize += sizeof(int64_t) + sizeof(bool);
451
452 if (hasPastBuckets) {
453 if (dimensionGuardrailHit) {
454 overheadSize += sizeof(int32_t);
455 }
456
457 // estimated_memory_bytes
458 overheadSize += sizeof(int32_t);
459 // mTimeBase and mBucketSizeNs
460 overheadSize += 2 * sizeof(int64_t);
461
462 if (!mShouldUseNestedDimensions) {
463 // Assume dimensions data adds an additional atomTag + # of dimension fields
464 overheadSize += sizeof(int32_t);
465 overheadSize += sizeof(int32_t) * mDimensionsInWhat.size();
466 }
467 }
468 return overheadSize;
469 }
470
computeSkippedBucketSizeLocked(const SkippedBucket & skippedBucket) const471 size_t MetricProducer::computeSkippedBucketSizeLocked(const SkippedBucket& skippedBucket) const {
472 size_t skippedBucketSize = 0;
473
474 // Bucket Start, Bucket End
475 skippedBucketSize += 2 * sizeof(int64_t);
476
477 // DropType, Drop Time
478 skippedBucketSize += (sizeof(int32_t) + sizeof(int64_t)) * skippedBucket.dropEvents.size();
479
480 return skippedBucketSize;
481 }
482
483 } // namespace statsd
484 } // namespace os
485 } // namespace android
486