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