Home | History | Annotate | Download | only in src
      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 
     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 
    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 
    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 
    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 
    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 
    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 //
    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 
    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 
    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 
    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 
    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 
    411 int64_t getElapsedRealtimeNs() {
    412     return ::android::elapsedRealtimeNano();
    413 }
    414 
    415 int64_t getElapsedRealtimeSec() {
    416     return ::android::elapsedRealtimeNano() / NS_PER_SEC;
    417 }
    418 
    419 int64_t getElapsedRealtimeMillis() {
    420     return ::android::elapsedRealtime();
    421 }
    422 
    423 int64_t getWallClockNs() {
    424     return time(nullptr) * NS_PER_SEC;
    425 }
    426 
    427 int64_t getWallClockSec() {
    428     return time(nullptr);
    429 }
    430 
    431 int64_t getWallClockMillis() {
    432     return time(nullptr) * MS_PER_SEC;
    433 }
    434 
    435 int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs) {
    436     return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
    437 }
    438 
    439 int64_t NanoToMillis(const int64_t nano) {
    440     return nano / 1000000;
    441 }
    442 
    443 int64_t MillisToNano(const int64_t millis) {
    444     return millis * 1000000;
    445 }
    446 
    447 }  // namespace statsd
    448 }  // namespace os
    449 }  // namespace android
    450