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