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 #include "hash.h"
18 #include "stats_log_util.h"
19
20 #include <private/android_filesystem_config.h>
21 #include <set>
22 #include <utils/SystemClock.h>
23
24 using android::util::AtomsInfo;
25 using android::util::FIELD_COUNT_REPEATED;
26 using android::util::FIELD_TYPE_BOOL;
27 using android::util::FIELD_TYPE_FIXED64;
28 using android::util::FIELD_TYPE_FLOAT;
29 using android::util::FIELD_TYPE_INT32;
30 using android::util::FIELD_TYPE_INT64;
31 using android::util::FIELD_TYPE_MESSAGE;
32 using android::util::FIELD_TYPE_STRING;
33 using android::util::FIELD_TYPE_UINT64;
34 using android::util::ProtoOutputStream;
35
36 namespace android {
37 namespace os {
38 namespace statsd {
39
40 // for DimensionsValue Proto
41 const int DIMENSIONS_VALUE_FIELD = 1;
42 const int DIMENSIONS_VALUE_VALUE_STR = 2;
43 const int DIMENSIONS_VALUE_VALUE_INT = 3;
44 const int DIMENSIONS_VALUE_VALUE_LONG = 4;
45 // const int DIMENSIONS_VALUE_VALUE_BOOL = 5; // logd doesn't have bool data type.
46 const int DIMENSIONS_VALUE_VALUE_FLOAT = 6;
47 const int DIMENSIONS_VALUE_VALUE_TUPLE = 7;
48 const int DIMENSIONS_VALUE_VALUE_STR_HASH = 8;
49
50 const int DIMENSIONS_VALUE_TUPLE_VALUE = 1;
51
52 // for PulledAtomStats proto
53 const int FIELD_ID_PULLED_ATOM_STATS = 10;
54 const int FIELD_ID_PULL_ATOM_ID = 1;
55 const int FIELD_ID_TOTAL_PULL = 2;
56 const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3;
57 const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4;
58 const int FIELD_ID_AVERAGE_PULL_TIME_NANOS = 5;
59 const int FIELD_ID_MAX_PULL_TIME_NANOS = 6;
60 const int FIELD_ID_AVERAGE_PULL_DELAY_NANOS = 7;
61 const int FIELD_ID_MAX_PULL_DELAY_NANOS = 8;
62 const int FIELD_ID_DATA_ERROR = 9;
63 const int FIELD_ID_PULL_TIMEOUT = 10;
64 const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11;
65 const int FIELD_ID_PULL_FAILED = 12;
66 const int FIELD_ID_STATS_COMPANION_FAILED = 13;
67 const int FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED = 14;
68 const int FIELD_ID_EMPTY_DATA = 15;
69 const int FIELD_ID_PULL_REGISTERED_COUNT = 16;
70 const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17;
71 // for AtomMetricStats proto
72 const int FIELD_ID_ATOM_METRIC_STATS = 17;
73 const int FIELD_ID_METRIC_ID = 1;
74 const int FIELD_ID_HARD_DIMENSION_LIMIT_REACHED = 2;
75 const int FIELD_ID_LATE_LOG_EVENT_SKIPPED = 3;
76 const int FIELD_ID_SKIPPED_FORWARD_BUCKETS = 4;
77 const int FIELD_ID_BAD_VALUE_TYPE = 5;
78 const int FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET = 6;
79 const int FIELD_ID_INVALIDATED_BUCKET = 7;
80 const int FIELD_ID_BUCKET_DROPPED = 8;
81 const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9;
82 const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10;
83 const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11;
84 const int FIELD_ID_BUCKET_COUNT = 12;
85
86 namespace {
87
writeDimensionToProtoHelper(const std::vector<FieldValue> & dims,size_t * index,int depth,int prefix,std::set<string> * str_set,ProtoOutputStream * protoOutput)88 void writeDimensionToProtoHelper(const std::vector<FieldValue>& dims, size_t* index, int depth,
89 int prefix, std::set<string> *str_set,
90 ProtoOutputStream* protoOutput) {
91 size_t count = dims.size();
92 while (*index < count) {
93 const auto& dim = dims[*index];
94 const int valueDepth = dim.mField.getDepth();
95 const int valuePrefix = dim.mField.getPrefix(depth);
96 const int fieldNum = dim.mField.getPosAtDepth(depth);
97 if (valueDepth > 2) {
98 ALOGE("Depth > 2 not supported");
99 return;
100 }
101
102 if (depth == valueDepth && valuePrefix == prefix) {
103 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
104 DIMENSIONS_VALUE_TUPLE_VALUE);
105 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
106 switch (dim.mValue.getType()) {
107 case INT:
108 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
109 dim.mValue.int_value);
110 break;
111 case LONG:
112 protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
113 (long long)dim.mValue.long_value);
114 break;
115 case FLOAT:
116 protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
117 dim.mValue.float_value);
118 break;
119 case STRING:
120 if (str_set == nullptr) {
121 protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
122 dim.mValue.str_value);
123 } else {
124 str_set->insert(dim.mValue.str_value);
125 protoOutput->write(
126 FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH,
127 (long long)Hash64(dim.mValue.str_value));
128 }
129 break;
130 default:
131 break;
132 }
133 if (token != 0) {
134 protoOutput->end(token);
135 }
136 (*index)++;
137 } else if (valueDepth > depth && valuePrefix == prefix) {
138 // Writing the sub tree
139 uint64_t dimensionToken = protoOutput->start(
140 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE);
141 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
142 uint64_t tupleToken =
143 protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
144 writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth),
145 str_set, protoOutput);
146 protoOutput->end(tupleToken);
147 protoOutput->end(dimensionToken);
148 } else {
149 // Done with the prev sub tree
150 return;
151 }
152 }
153 }
154
writeDimensionLeafToProtoHelper(const std::vector<FieldValue> & dims,const int dimensionLeafField,size_t * index,int depth,int prefix,std::set<string> * str_set,ProtoOutputStream * protoOutput)155 void writeDimensionLeafToProtoHelper(const std::vector<FieldValue>& dims,
156 const int dimensionLeafField,
157 size_t* index, int depth,
158 int prefix, std::set<string> *str_set,
159 ProtoOutputStream* protoOutput) {
160 size_t count = dims.size();
161 while (*index < count) {
162 const auto& dim = dims[*index];
163 const int valueDepth = dim.mField.getDepth();
164 const int valuePrefix = dim.mField.getPrefix(depth);
165 if (valueDepth > 2) {
166 ALOGE("Depth > 2 not supported");
167 return;
168 }
169
170 if (depth == valueDepth && valuePrefix == prefix) {
171 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
172 dimensionLeafField);
173 switch (dim.mValue.getType()) {
174 case INT:
175 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
176 dim.mValue.int_value);
177 break;
178 case LONG:
179 protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
180 (long long)dim.mValue.long_value);
181 break;
182 case FLOAT:
183 protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
184 dim.mValue.float_value);
185 break;
186 case STRING:
187 if (str_set == nullptr) {
188 protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
189 dim.mValue.str_value);
190 } else {
191 str_set->insert(dim.mValue.str_value);
192 protoOutput->write(
193 FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH,
194 (long long)Hash64(dim.mValue.str_value));
195 }
196 break;
197 default:
198 break;
199 }
200 if (token != 0) {
201 protoOutput->end(token);
202 }
203 (*index)++;
204 } else if (valueDepth > depth && valuePrefix == prefix) {
205 writeDimensionLeafToProtoHelper(dims, dimensionLeafField,
206 index, valueDepth, dim.mField.getPrefix(valueDepth),
207 str_set, protoOutput);
208 } else {
209 // Done with the prev sub tree
210 return;
211 }
212 }
213 }
214
writeDimensionPathToProtoHelper(const std::vector<Matcher> & fieldMatchers,size_t * index,int depth,int prefix,ProtoOutputStream * protoOutput)215 void writeDimensionPathToProtoHelper(const std::vector<Matcher>& fieldMatchers,
216 size_t* index, int depth, int prefix,
217 ProtoOutputStream* protoOutput) {
218 size_t count = fieldMatchers.size();
219 while (*index < count) {
220 const Field& field = fieldMatchers[*index].mMatcher;
221 const int valueDepth = field.getDepth();
222 const int valuePrefix = field.getPrefix(depth);
223 const int fieldNum = field.getPosAtDepth(depth);
224 if (valueDepth > 2) {
225 ALOGE("Depth > 2 not supported");
226 return;
227 }
228
229 if (depth == valueDepth && valuePrefix == prefix) {
230 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
231 DIMENSIONS_VALUE_TUPLE_VALUE);
232 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
233 if (token != 0) {
234 protoOutput->end(token);
235 }
236 (*index)++;
237 } else if (valueDepth > depth && valuePrefix == prefix) {
238 // Writing the sub tree
239 uint64_t dimensionToken = protoOutput->start(
240 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE);
241 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
242 uint64_t tupleToken =
243 protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
244 writeDimensionPathToProtoHelper(fieldMatchers, index, valueDepth,
245 field.getPrefix(valueDepth), protoOutput);
246 protoOutput->end(tupleToken);
247 protoOutput->end(dimensionToken);
248 } else {
249 // Done with the prev sub tree
250 return;
251 }
252 }
253 }
254
255 } // namespace
256
writeDimensionToProto(const HashableDimensionKey & dimension,std::set<string> * str_set,ProtoOutputStream * protoOutput)257 void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set,
258 ProtoOutputStream* protoOutput) {
259 if (dimension.getValues().size() == 0) {
260 return;
261 }
262 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD,
263 dimension.getValues()[0].mField.getTag());
264 uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
265 size_t index = 0;
266 writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, str_set, protoOutput);
267 protoOutput->end(topToken);
268 }
269
writeDimensionLeafNodesToProto(const HashableDimensionKey & dimension,const int dimensionLeafFieldId,std::set<string> * str_set,ProtoOutputStream * protoOutput)270 void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension,
271 const int dimensionLeafFieldId,
272 std::set<string> *str_set,
273 ProtoOutputStream* protoOutput) {
274 if (dimension.getValues().size() == 0) {
275 return;
276 }
277 size_t index = 0;
278 writeDimensionLeafToProtoHelper(dimension.getValues(), dimensionLeafFieldId,
279 &index, 0, 0, str_set, protoOutput);
280 }
281
writeDimensionPathToProto(const std::vector<Matcher> & fieldMatchers,ProtoOutputStream * protoOutput)282 void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers,
283 ProtoOutputStream* protoOutput) {
284 if (fieldMatchers.size() == 0) {
285 return;
286 }
287 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD,
288 fieldMatchers[0].mMatcher.getTag());
289 uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
290 size_t index = 0;
291 writeDimensionPathToProtoHelper(fieldMatchers, &index, 0, 0, protoOutput);
292 protoOutput->end(topToken);
293 }
294
295 // Supported Atoms format
296 // XYZ_Atom {
297 // repeated SubMsg field_1 = 1;
298 // SubMsg2 field_2 = 2;
299 // int32/float/string/int63 field_3 = 3;
300 // }
301 // logd's msg format, doesn't allow us to distinguish between the 2 cases below
302 // Case (1):
303 // Atom {
304 // SubMsg {
305 // int i = 1;
306 // int j = 2;
307 // }
308 // repeated SubMsg
309 // }
310 //
311 // and case (2):
312 // Atom {
313 // SubMsg {
314 // repeated int i = 1;
315 // repeated int j = 2;
316 // }
317 // optional SubMsg = 1;
318 // }
319 //
320 //
writeFieldValueTreeToStreamHelper(int tagId,const std::vector<FieldValue> & dims,size_t * index,int depth,int prefix,ProtoOutputStream * protoOutput)321 void writeFieldValueTreeToStreamHelper(int tagId, const std::vector<FieldValue>& dims,
322 size_t* index, int depth, int prefix,
323 ProtoOutputStream* protoOutput) {
324 size_t count = dims.size();
325 while (*index < count) {
326 const auto& dim = dims[*index];
327 const int valueDepth = dim.mField.getDepth();
328 const int valuePrefix = dim.mField.getPrefix(depth);
329 const int fieldNum = dim.mField.getPosAtDepth(depth);
330 if (valueDepth > 2) {
331 ALOGE("Depth > 2 not supported");
332 return;
333 }
334
335 if (depth == valueDepth && valuePrefix == prefix) {
336 switch (dim.mValue.getType()) {
337 case INT:
338 protoOutput->write(FIELD_TYPE_INT32 | fieldNum, dim.mValue.int_value);
339 break;
340 case LONG:
341 protoOutput->write(FIELD_TYPE_INT64 | fieldNum,
342 (long long)dim.mValue.long_value);
343 break;
344 case FLOAT:
345 protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value);
346 break;
347 case STRING: {
348 bool isBytesField = false;
349 // Bytes field is logged via string format in log_msg format. So here we check
350 // if this string field is a byte field.
351 std::map<int, std::vector<int>>::const_iterator itr;
352 if (depth == 0 && (itr = AtomsInfo::kBytesFieldAtoms.find(tagId)) !=
353 AtomsInfo::kBytesFieldAtoms.end()) {
354 const std::vector<int>& bytesFields = itr->second;
355 for (int bytesField : bytesFields) {
356 if (bytesField == fieldNum) {
357 // This is a bytes field
358 isBytesField = true;
359 break;
360 }
361 }
362 }
363 if (isBytesField) {
364 if (dim.mValue.str_value.length() > 0) {
365 protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
366 (const char*)dim.mValue.str_value.c_str(),
367 dim.mValue.str_value.length());
368 }
369 } else {
370 protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
371 }
372 break;
373 }
374 case STORAGE:
375 protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
376 (const char*)dim.mValue.storage_value.data(),
377 dim.mValue.storage_value.size());
378 break;
379 default:
380 break;
381 }
382 (*index)++;
383 } else if (valueDepth > depth && valuePrefix == prefix) {
384 // Writing the sub tree
385 uint64_t msg_token = 0ULL;
386 if (valueDepth == depth + 2) {
387 msg_token =
388 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum);
389 } else if (valueDepth == depth + 1) {
390 msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum);
391 }
392 // Directly jump to the leaf value because the repeated position field is implied
393 // by the position of the sub msg in the parent field.
394 writeFieldValueTreeToStreamHelper(tagId, dims, index, valueDepth,
395 dim.mField.getPrefix(valueDepth), protoOutput);
396 if (msg_token != 0) {
397 protoOutput->end(msg_token);
398 }
399 } else {
400 // Done with the prev sub tree
401 return;
402 }
403 }
404 }
405
writeFieldValueTreeToStream(int tagId,const std::vector<FieldValue> & values,util::ProtoOutputStream * protoOutput)406 void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values,
407 util::ProtoOutputStream* protoOutput) {
408 uint64_t atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId);
409
410 size_t index = 0;
411 writeFieldValueTreeToStreamHelper(tagId, values, &index, 0, 0, protoOutput);
412 protoOutput->end(atomToken);
413 }
414
TimeUnitToBucketSizeInMillisGuardrailed(int uid,TimeUnit unit)415 int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
416 int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
417 if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL &&
418 uid != AID_ROOT) {
419 bucketSizeMillis = 5 * 60 * 1000LL;
420 }
421 return bucketSizeMillis;
422 }
423
TimeUnitToBucketSizeInMillis(TimeUnit unit)424 int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) {
425 switch (unit) {
426 case ONE_MINUTE:
427 return 60 * 1000LL;
428 case FIVE_MINUTES:
429 return 5 * 60 * 1000LL;
430 case TEN_MINUTES:
431 return 10 * 60 * 1000LL;
432 case THIRTY_MINUTES:
433 return 30 * 60 * 1000LL;
434 case ONE_HOUR:
435 return 60 * 60 * 1000LL;
436 case THREE_HOURS:
437 return 3 * 60 * 60 * 1000LL;
438 case SIX_HOURS:
439 return 6 * 60 * 60 * 1000LL;
440 case TWELVE_HOURS:
441 return 12 * 60 * 60 * 1000LL;
442 case ONE_DAY:
443 return 24 * 60 * 60 * 1000LL;
444 case CTS:
445 return 1000;
446 case TIME_UNIT_UNSPECIFIED:
447 default:
448 return -1;
449 }
450 }
451
writePullerStatsToStream(const std::pair<int,StatsdStats::PulledAtomStats> & pair,util::ProtoOutputStream * protoOutput)452 void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
453 util::ProtoOutputStream* protoOutput) {
454 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS |
455 FIELD_COUNT_REPEATED);
456 protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_PULL_ATOM_ID, (int32_t)pair.first);
457 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL, (long long)pair.second.totalPull);
458 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL_FROM_CACHE,
459 (long long)pair.second.totalPullFromCache);
460 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_PULL_INTERVAL_SEC,
461 (long long)pair.second.minPullIntervalSec);
462 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_AVERAGE_PULL_TIME_NANOS,
463 (long long)pair.second.avgPullTimeNs);
464 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_TIME_NANOS,
465 (long long)pair.second.maxPullTimeNs);
466 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_AVERAGE_PULL_DELAY_NANOS,
467 (long long)pair.second.avgPullDelayNs);
468 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_DELAY_NANOS,
469 (long long)pair.second.maxPullDelayNs);
470 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DATA_ERROR, (long long)pair.second.dataError);
471 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT,
472 (long long)pair.second.pullTimeout);
473 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_EXCEED_MAX_DELAY,
474 (long long)pair.second.pullExceedMaxDelay);
475 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED,
476 (long long)pair.second.pullFailed);
477 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_FAILED,
478 (long long)pair.second.statsCompanionPullFailed);
479 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED,
480 (long long)pair.second.statsCompanionPullBinderTransactionFailed);
481 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA,
482 (long long)pair.second.emptyData);
483 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT,
484 (long long) pair.second.registeredCount);
485 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT,
486 (long long) pair.second.unregisteredCount);
487 protoOutput->end(token);
488 }
489
writeAtomMetricStatsToStream(const std::pair<int64_t,StatsdStats::AtomMetricStats> & pair,util::ProtoOutputStream * protoOutput)490 void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
491 util::ProtoOutputStream *protoOutput) {
492 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS |
493 FIELD_COUNT_REPEATED);
494 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first);
495 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED,
496 (long long)pair.second.hardDimensionLimitReached);
497 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED,
498 (long long)pair.second.lateLogEventSkipped);
499 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_FORWARD_BUCKETS,
500 (long long)pair.second.skippedForwardBuckets);
501 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BAD_VALUE_TYPE,
502 (long long)pair.second.badValueType);
503 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET,
504 (long long)pair.second.conditionChangeInNextBucket);
505 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_INVALIDATED_BUCKET,
506 (long long)pair.second.invalidatedBucket);
507 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_DROPPED,
508 (long long)pair.second.bucketDropped);
509 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS,
510 (long long)pair.second.minBucketBoundaryDelayNs);
511 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS,
512 (long long)pair.second.maxBucketBoundaryDelayNs);
513 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION,
514 (long long)pair.second.bucketUnknownCondition);
515 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_COUNT,
516 (long long)pair.second.bucketCount);
517 protoOutput->end(token);
518 }
519
getElapsedRealtimeNs()520 int64_t getElapsedRealtimeNs() {
521 return ::android::elapsedRealtimeNano();
522 }
523
getElapsedRealtimeSec()524 int64_t getElapsedRealtimeSec() {
525 return ::android::elapsedRealtimeNano() / NS_PER_SEC;
526 }
527
getElapsedRealtimeMillis()528 int64_t getElapsedRealtimeMillis() {
529 return ::android::elapsedRealtime();
530 }
531
getWallClockNs()532 int64_t getWallClockNs() {
533 return time(nullptr) * NS_PER_SEC;
534 }
535
getWallClockSec()536 int64_t getWallClockSec() {
537 return time(nullptr);
538 }
539
getWallClockMillis()540 int64_t getWallClockMillis() {
541 return time(nullptr) * MS_PER_SEC;
542 }
543
truncateTimestampIfNecessary(int atomId,int64_t timestampNs)544 int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs) {
545 if (AtomsInfo::kTruncatingTimestampAtomBlackList.find(atomId) !=
546 AtomsInfo::kTruncatingTimestampAtomBlackList.end() ||
547 (atomId >= StatsdStats::kTimestampTruncationStartTag &&
548 atomId <= StatsdStats::kTimestampTruncationEndTag)) {
549 return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
550 } else {
551 return timestampNs;
552 }
553 }
554
NanoToMillis(const int64_t nano)555 int64_t NanoToMillis(const int64_t nano) {
556 return nano / 1000000;
557 }
558
MillisToNano(const int64_t millis)559 int64_t MillisToNano(const int64_t millis) {
560 return millis * 1000000;
561 }
562
563 } // namespace statsd
564 } // namespace os
565 } // namespace android
566