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 <logd/LogEvent.h>
21 #include <private/android_filesystem_config.h>
22 #include <utils/Log.h>
23 #include <set>
24 #include <stack>
25 #include <utils/Log.h>
26 #include <utils/SystemClock.h>
27
28 using android::util::FIELD_COUNT_REPEATED;
29 using android::util::FIELD_TYPE_BOOL;
30 using android::util::FIELD_TYPE_FLOAT;
31 using android::util::FIELD_TYPE_INT32;
32 using android::util::FIELD_TYPE_INT64;
33 using android::util::FIELD_TYPE_UINT64;
34 using android::util::FIELD_TYPE_FIXED64;
35 using android::util::FIELD_TYPE_MESSAGE;
36 using android::util::FIELD_TYPE_STRING;
37 using android::util::ProtoOutputStream;
38
39 namespace android {
40 namespace os {
41 namespace statsd {
42
43 // for DimensionsValue Proto
44 const int DIMENSIONS_VALUE_FIELD = 1;
45 const int DIMENSIONS_VALUE_VALUE_STR = 2;
46 const int DIMENSIONS_VALUE_VALUE_INT = 3;
47 const int DIMENSIONS_VALUE_VALUE_LONG = 4;
48 // const int DIMENSIONS_VALUE_VALUE_BOOL = 5; // logd doesn't have bool data type.
49 const int DIMENSIONS_VALUE_VALUE_FLOAT = 6;
50 const int DIMENSIONS_VALUE_VALUE_TUPLE = 7;
51 const int DIMENSIONS_VALUE_VALUE_STR_HASH = 8;
52
53 const int DIMENSIONS_VALUE_TUPLE_VALUE = 1;
54
55 // for PulledAtomStats proto
56 const int FIELD_ID_PULLED_ATOM_STATS = 10;
57 const int FIELD_ID_PULL_ATOM_ID = 1;
58 const int FIELD_ID_TOTAL_PULL = 2;
59 const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3;
60 const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4;
61
62 namespace {
63
writeDimensionToProtoHelper(const std::vector<FieldValue> & dims,size_t * index,int depth,int prefix,std::set<string> * str_set,ProtoOutputStream * protoOutput)64 void writeDimensionToProtoHelper(const std::vector<FieldValue>& dims, size_t* index, int depth,
65 int prefix, std::set<string> *str_set,
66 ProtoOutputStream* protoOutput) {
67 size_t count = dims.size();
68 while (*index < count) {
69 const auto& dim = dims[*index];
70 const int valueDepth = dim.mField.getDepth();
71 const int valuePrefix = dim.mField.getPrefix(depth);
72 const int fieldNum = dim.mField.getPosAtDepth(depth);
73 if (valueDepth > 2) {
74 ALOGE("Depth > 2 not supported");
75 return;
76 }
77
78 if (depth == valueDepth && valuePrefix == prefix) {
79 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
80 DIMENSIONS_VALUE_TUPLE_VALUE);
81 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
82 switch (dim.mValue.getType()) {
83 case INT:
84 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
85 dim.mValue.int_value);
86 break;
87 case LONG:
88 protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
89 (long long)dim.mValue.long_value);
90 break;
91 case FLOAT:
92 protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
93 dim.mValue.float_value);
94 break;
95 case STRING:
96 if (str_set == nullptr) {
97 protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
98 dim.mValue.str_value);
99 } else {
100 str_set->insert(dim.mValue.str_value);
101 protoOutput->write(
102 FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH,
103 (long long)Hash64(dim.mValue.str_value));
104 }
105 break;
106 default:
107 break;
108 }
109 if (token != 0) {
110 protoOutput->end(token);
111 }
112 (*index)++;
113 } else if (valueDepth > depth && valuePrefix == prefix) {
114 // Writing the sub tree
115 uint64_t dimensionToken = protoOutput->start(
116 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE);
117 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
118 uint64_t tupleToken =
119 protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
120 writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth),
121 str_set, protoOutput);
122 protoOutput->end(tupleToken);
123 protoOutput->end(dimensionToken);
124 } else {
125 // Done with the prev sub tree
126 return;
127 }
128 }
129 }
130
writeDimensionLeafToProtoHelper(const std::vector<FieldValue> & dims,const int dimensionLeafField,size_t * index,int depth,int prefix,std::set<string> * str_set,ProtoOutputStream * protoOutput)131 void writeDimensionLeafToProtoHelper(const std::vector<FieldValue>& dims,
132 const int dimensionLeafField,
133 size_t* index, int depth,
134 int prefix, std::set<string> *str_set,
135 ProtoOutputStream* protoOutput) {
136 size_t count = dims.size();
137 while (*index < count) {
138 const auto& dim = dims[*index];
139 const int valueDepth = dim.mField.getDepth();
140 const int valuePrefix = dim.mField.getPrefix(depth);
141 if (valueDepth > 2) {
142 ALOGE("Depth > 2 not supported");
143 return;
144 }
145
146 if (depth == valueDepth && valuePrefix == prefix) {
147 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
148 dimensionLeafField);
149 switch (dim.mValue.getType()) {
150 case INT:
151 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
152 dim.mValue.int_value);
153 break;
154 case LONG:
155 protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
156 (long long)dim.mValue.long_value);
157 break;
158 case FLOAT:
159 protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
160 dim.mValue.float_value);
161 break;
162 case STRING:
163 if (str_set == nullptr) {
164 protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
165 dim.mValue.str_value);
166 } else {
167 str_set->insert(dim.mValue.str_value);
168 protoOutput->write(
169 FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH,
170 (long long)Hash64(dim.mValue.str_value));
171 }
172 break;
173 default:
174 break;
175 }
176 if (token != 0) {
177 protoOutput->end(token);
178 }
179 (*index)++;
180 } else if (valueDepth > depth && valuePrefix == prefix) {
181 writeDimensionLeafToProtoHelper(dims, dimensionLeafField,
182 index, valueDepth, dim.mField.getPrefix(valueDepth),
183 str_set, protoOutput);
184 } else {
185 // Done with the prev sub tree
186 return;
187 }
188 }
189 }
190
writeDimensionPathToProtoHelper(const std::vector<Matcher> & fieldMatchers,size_t * index,int depth,int prefix,ProtoOutputStream * protoOutput)191 void writeDimensionPathToProtoHelper(const std::vector<Matcher>& fieldMatchers,
192 size_t* index, int depth, int prefix,
193 ProtoOutputStream* protoOutput) {
194 size_t count = fieldMatchers.size();
195 while (*index < count) {
196 const Field& field = fieldMatchers[*index].mMatcher;
197 const int valueDepth = field.getDepth();
198 const int valuePrefix = field.getPrefix(depth);
199 const int fieldNum = field.getPosAtDepth(depth);
200 if (valueDepth > 2) {
201 ALOGE("Depth > 2 not supported");
202 return;
203 }
204
205 if (depth == valueDepth && valuePrefix == prefix) {
206 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
207 DIMENSIONS_VALUE_TUPLE_VALUE);
208 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
209 if (token != 0) {
210 protoOutput->end(token);
211 }
212 (*index)++;
213 } else if (valueDepth > depth && valuePrefix == prefix) {
214 // Writing the sub tree
215 uint64_t dimensionToken = protoOutput->start(
216 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE);
217 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
218 uint64_t tupleToken =
219 protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
220 writeDimensionPathToProtoHelper(fieldMatchers, index, valueDepth,
221 field.getPrefix(valueDepth), protoOutput);
222 protoOutput->end(tupleToken);
223 protoOutput->end(dimensionToken);
224 } else {
225 // Done with the prev sub tree
226 return;
227 }
228 }
229 }
230
231 } // namespace
232
writeDimensionToProto(const HashableDimensionKey & dimension,std::set<string> * str_set,ProtoOutputStream * protoOutput)233 void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set,
234 ProtoOutputStream* protoOutput) {
235 if (dimension.getValues().size() == 0) {
236 return;
237 }
238 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD,
239 dimension.getValues()[0].mField.getTag());
240 uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
241 size_t index = 0;
242 writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, str_set, protoOutput);
243 protoOutput->end(topToken);
244 }
245
writeDimensionLeafNodesToProto(const HashableDimensionKey & dimension,const int dimensionLeafFieldId,std::set<string> * str_set,ProtoOutputStream * protoOutput)246 void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension,
247 const int dimensionLeafFieldId,
248 std::set<string> *str_set,
249 ProtoOutputStream* protoOutput) {
250 if (dimension.getValues().size() == 0) {
251 return;
252 }
253 size_t index = 0;
254 writeDimensionLeafToProtoHelper(dimension.getValues(), dimensionLeafFieldId,
255 &index, 0, 0, str_set, protoOutput);
256 }
257
writeDimensionPathToProto(const std::vector<Matcher> & fieldMatchers,ProtoOutputStream * protoOutput)258 void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers,
259 ProtoOutputStream* protoOutput) {
260 if (fieldMatchers.size() == 0) {
261 return;
262 }
263 protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD,
264 fieldMatchers[0].mMatcher.getTag());
265 uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
266 size_t index = 0;
267 writeDimensionPathToProtoHelper(fieldMatchers, &index, 0, 0, protoOutput);
268 protoOutput->end(topToken);
269 }
270
271 // Supported Atoms format
272 // XYZ_Atom {
273 // repeated SubMsg field_1 = 1;
274 // SubMsg2 field_2 = 2;
275 // int32/float/string/int63 field_3 = 3;
276 // }
277 // logd's msg format, doesn't allow us to distinguish between the 2 cases below
278 // Case (1):
279 // Atom {
280 // SubMsg {
281 // int i = 1;
282 // int j = 2;
283 // }
284 // repeated SubMsg
285 // }
286 //
287 // and case (2):
288 // Atom {
289 // SubMsg {
290 // repeated int i = 1;
291 // repeated int j = 2;
292 // }
293 // optional SubMsg = 1;
294 // }
295 //
296 //
writeFieldValueTreeToStreamHelper(const std::vector<FieldValue> & dims,size_t * index,int depth,int prefix,ProtoOutputStream * protoOutput)297 void writeFieldValueTreeToStreamHelper(const std::vector<FieldValue>& dims, size_t* index,
298 int depth, int prefix, ProtoOutputStream* protoOutput) {
299 size_t count = dims.size();
300 while (*index < count) {
301 const auto& dim = dims[*index];
302 const int valueDepth = dim.mField.getDepth();
303 const int valuePrefix = dim.mField.getPrefix(depth);
304 const int fieldNum = dim.mField.getPosAtDepth(depth);
305 if (valueDepth > 2) {
306 ALOGE("Depth > 2 not supported");
307 return;
308 }
309
310 if (depth == valueDepth && valuePrefix == prefix) {
311 switch (dim.mValue.getType()) {
312 case INT:
313 protoOutput->write(FIELD_TYPE_INT32 | fieldNum, dim.mValue.int_value);
314 break;
315 case LONG:
316 protoOutput->write(FIELD_TYPE_INT64 | fieldNum,
317 (long long)dim.mValue.long_value);
318 break;
319 case FLOAT:
320 protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value);
321 break;
322 case STRING:
323 protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
324 break;
325 default:
326 break;
327 }
328 (*index)++;
329 } else if (valueDepth > depth && valuePrefix == prefix) {
330 // Writing the sub tree
331 uint64_t msg_token = 0ULL;
332 if (valueDepth == depth + 2) {
333 msg_token =
334 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum);
335 } else if (valueDepth == depth + 1) {
336 msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum);
337 }
338 // Directly jump to the leaf value because the repeated position field is implied
339 // by the position of the sub msg in the parent field.
340 writeFieldValueTreeToStreamHelper(dims, index, valueDepth,
341 dim.mField.getPrefix(valueDepth), protoOutput);
342 if (msg_token != 0) {
343 protoOutput->end(msg_token);
344 }
345 } else {
346 // Done with the prev sub tree
347 return;
348 }
349 }
350 }
351
writeFieldValueTreeToStream(int tagId,const std::vector<FieldValue> & values,util::ProtoOutputStream * protoOutput)352 void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values,
353 util::ProtoOutputStream* protoOutput) {
354 uint64_t atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId);
355
356 size_t index = 0;
357 writeFieldValueTreeToStreamHelper(values, &index, 0, 0, protoOutput);
358 protoOutput->end(atomToken);
359 }
360
TimeUnitToBucketSizeInMillisGuardrailed(int uid,TimeUnit unit)361 int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
362 int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
363 if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL &&
364 uid != AID_ROOT) {
365 bucketSizeMillis = 5 * 60 * 1000LL;
366 }
367 return bucketSizeMillis;
368 }
369
TimeUnitToBucketSizeInMillis(TimeUnit unit)370 int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) {
371 switch (unit) {
372 case ONE_MINUTE:
373 return 60 * 1000LL;
374 case FIVE_MINUTES:
375 return 5 * 60 * 1000LL;
376 case TEN_MINUTES:
377 return 10 * 60 * 1000LL;
378 case THIRTY_MINUTES:
379 return 30 * 60 * 1000LL;
380 case ONE_HOUR:
381 return 60 * 60 * 1000LL;
382 case THREE_HOURS:
383 return 3 * 60 * 60 * 1000LL;
384 case SIX_HOURS:
385 return 6 * 60 * 60 * 1000LL;
386 case TWELVE_HOURS:
387 return 12 * 60 * 60 * 1000LL;
388 case ONE_DAY:
389 return 24 * 60 * 60 * 1000LL;
390 case CTS:
391 return 1000;
392 case TIME_UNIT_UNSPECIFIED:
393 default:
394 return -1;
395 }
396 }
397
writePullerStatsToStream(const std::pair<int,StatsdStats::PulledAtomStats> & pair,util::ProtoOutputStream * protoOutput)398 void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
399 util::ProtoOutputStream* protoOutput) {
400 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS |
401 FIELD_COUNT_REPEATED);
402 protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_PULL_ATOM_ID, (int32_t)pair.first);
403 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL, (long long)pair.second.totalPull);
404 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL_FROM_CACHE,
405 (long long)pair.second.totalPullFromCache);
406 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_PULL_INTERVAL_SEC,
407 (long long)pair.second.minPullIntervalSec);
408 protoOutput->end(token);
409 }
410
getElapsedRealtimeNs()411 int64_t getElapsedRealtimeNs() {
412 return ::android::elapsedRealtimeNano();
413 }
414
getElapsedRealtimeSec()415 int64_t getElapsedRealtimeSec() {
416 return ::android::elapsedRealtimeNano() / NS_PER_SEC;
417 }
418
getElapsedRealtimeMillis()419 int64_t getElapsedRealtimeMillis() {
420 return ::android::elapsedRealtime();
421 }
422
getWallClockNs()423 int64_t getWallClockNs() {
424 return time(nullptr) * NS_PER_SEC;
425 }
426
getWallClockSec()427 int64_t getWallClockSec() {
428 return time(nullptr);
429 }
430
getWallClockMillis()431 int64_t getWallClockMillis() {
432 return time(nullptr) * MS_PER_SEC;
433 }
434
truncateTimestampNsToFiveMinutes(int64_t timestampNs)435 int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs) {
436 return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
437 }
438
NanoToMillis(const int64_t nano)439 int64_t NanoToMillis(const int64_t nano) {
440 return nano / 1000000;
441 }
442
MillisToNano(const int64_t millis)443 int64_t MillisToNano(const int64_t millis) {
444 return millis * 1000000;
445 }
446
447 } // namespace statsd
448 } // namespace os
449 } // namespace android
450