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 28using android::util::FIELD_COUNT_REPEATED; 29using android::util::FIELD_TYPE_BOOL; 30using android::util::FIELD_TYPE_FLOAT; 31using android::util::FIELD_TYPE_INT32; 32using android::util::FIELD_TYPE_INT64; 33using android::util::FIELD_TYPE_UINT64; 34using android::util::FIELD_TYPE_FIXED64; 35using android::util::FIELD_TYPE_MESSAGE; 36using android::util::FIELD_TYPE_STRING; 37using android::util::ProtoOutputStream; 38 39namespace android { 40namespace os { 41namespace statsd { 42 43// for DimensionsValue Proto 44const int DIMENSIONS_VALUE_FIELD = 1; 45const int DIMENSIONS_VALUE_VALUE_STR = 2; 46const int DIMENSIONS_VALUE_VALUE_INT = 3; 47const int DIMENSIONS_VALUE_VALUE_LONG = 4; 48// const int DIMENSIONS_VALUE_VALUE_BOOL = 5; // logd doesn't have bool data type. 49const int DIMENSIONS_VALUE_VALUE_FLOAT = 6; 50const int DIMENSIONS_VALUE_VALUE_TUPLE = 7; 51const int DIMENSIONS_VALUE_VALUE_STR_HASH = 8; 52 53const int DIMENSIONS_VALUE_TUPLE_VALUE = 1; 54 55// for PulledAtomStats proto 56const int FIELD_ID_PULLED_ATOM_STATS = 10; 57const int FIELD_ID_PULL_ATOM_ID = 1; 58const int FIELD_ID_TOTAL_PULL = 2; 59const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3; 60const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4; 61 62namespace { 63 64void 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 131void 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 191void 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 233void 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 246void 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 258void 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// 297void 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 352void 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 361int64_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 370int64_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 398void 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 411int64_t getElapsedRealtimeNs() { 412 return ::android::elapsedRealtimeNano(); 413} 414 415int64_t getElapsedRealtimeSec() { 416 return ::android::elapsedRealtimeNano() / NS_PER_SEC; 417} 418 419int64_t getElapsedRealtimeMillis() { 420 return ::android::elapsedRealtime(); 421} 422 423int64_t getWallClockNs() { 424 return time(nullptr) * NS_PER_SEC; 425} 426 427int64_t getWallClockSec() { 428 return time(nullptr); 429} 430 431int64_t getWallClockMillis() { 432 return time(nullptr) * MS_PER_SEC; 433} 434 435int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs) { 436 return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60); 437} 438 439int64_t NanoToMillis(const int64_t nano) { 440 return nano / 1000000; 441} 442 443int64_t MillisToNano(const int64_t millis) { 444 return millis * 1000000; 445} 446 447} // namespace statsd 448} // namespace os 449} // namespace android 450