1/* 2 * Copyright (C) 2016 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// The bootstat command provides options to persist boot events with the current 18// timestamp, dump the persisted events, and log all events to EventLog to be 19// uploaded to Android log storage via Tron. 20 21#include <getopt.h> 22#include <unistd.h> 23 24#include <chrono> 25#include <cmath> 26#include <cstddef> 27#include <cstdio> 28#include <ctime> 29#include <map> 30#include <memory> 31#include <string> 32#include <vector> 33 34#include <android-base/chrono_utils.h> 35#include <android-base/logging.h> 36#include <android-base/parseint.h> 37#include <android-base/strings.h> 38#include <android/log.h> 39#include <cutils/properties.h> 40#include <metricslogger/metrics_logger.h> 41 42#include "boot_event_record_store.h" 43 44namespace { 45 46// Scans the boot event record store for record files and logs each boot event 47// via EventLog. 48void LogBootEvents() { 49 BootEventRecordStore boot_event_store; 50 51 auto events = boot_event_store.GetAllBootEvents(); 52 for (auto i = events.cbegin(); i != events.cend(); ++i) { 53 android::metricslogger::LogHistogram(i->first, i->second); 54 } 55} 56 57// Records the named boot |event| to the record store. If |value| is non-empty 58// and is a proper string representation of an integer value, the converted 59// integer value is associated with the boot event. 60void RecordBootEventFromCommandLine( 61 const std::string& event, const std::string& value_str) { 62 BootEventRecordStore boot_event_store; 63 if (!value_str.empty()) { 64 int32_t value = 0; 65 if (android::base::ParseInt(value_str, &value)) { 66 boot_event_store.AddBootEventWithValue(event, value); 67 } 68 } else { 69 boot_event_store.AddBootEvent(event); 70 } 71} 72 73void PrintBootEvents() { 74 printf("Boot events:\n"); 75 printf("------------\n"); 76 77 BootEventRecordStore boot_event_store; 78 auto events = boot_event_store.GetAllBootEvents(); 79 for (auto i = events.cbegin(); i != events.cend(); ++i) { 80 printf("%s\t%d\n", i->first.c_str(), i->second); 81 } 82} 83 84void ShowHelp(const char *cmd) { 85 fprintf(stderr, "Usage: %s [options]\n", cmd); 86 fprintf(stderr, 87 "options include:\n" 88 " -h, --help Show this help\n" 89 " -l, --log Log all metrics to logstorage\n" 90 " -p, --print Dump the boot event records to the console\n" 91 " -r, --record Record the timestamp of a named boot event\n" 92 " --value Optional value to associate with the boot event\n" 93 " --record_boot_complete Record metrics related to the time for the device boot\n" 94 " --record_boot_reason Record the reason why the device booted\n" 95 " --record_time_since_factory_reset Record the time since the device was reset\n"); 96} 97 98// Constructs a readable, printable string from the givencommand line 99// arguments. 100std::string GetCommandLine(int argc, char **argv) { 101 std::string cmd; 102 for (int i = 0; i < argc; ++i) { 103 cmd += argv[i]; 104 cmd += " "; 105 } 106 107 return cmd; 108} 109 110// Convenience wrapper over the property API that returns an 111// std::string. 112std::string GetProperty(const char* key) { 113 std::vector<char> temp(PROPERTY_VALUE_MAX); 114 const int len = property_get(key, &temp[0], nullptr); 115 if (len < 0) { 116 return ""; 117 } 118 return std::string(&temp[0], len); 119} 120 121constexpr int32_t kUnknownBootReason = 1; 122 123// A mapping from boot reason string, as read from the ro.boot.bootreason 124// system property, to a unique integer ID. Viewers of log data dashboards for 125// the boot_reason metric may refer to this mapping to discern the histogram 126// values. 127const std::map<std::string, int32_t> kBootReasonMap = { 128 {"unknown", kUnknownBootReason}, 129 {"normal", 2}, 130 {"recovery", 3}, 131 {"reboot", 4}, 132 {"PowerKey", 5}, 133 {"hard_reset", 6}, 134 {"kernel_panic", 7}, 135 {"rpm_err", 8}, 136 {"hw_reset", 9}, 137 {"tz_err", 10}, 138 {"adsp_err", 11}, 139 {"modem_err", 12}, 140 {"mba_err", 13}, 141 {"Watchdog", 14}, 142 {"Panic", 15}, 143 {"power_key", 16}, 144 {"power_on", 17}, 145 {"Reboot", 18}, 146 {"rtc", 19}, 147 {"edl", 20}, 148 {"oem_pon1", 21}, 149 {"oem_powerkey", 22}, 150 {"oem_unknown_reset", 23}, 151 {"srto: HWWDT reset SC", 24}, 152 {"srto: HWWDT reset platform", 25}, 153 {"srto: bootloader", 26}, 154 {"srto: kernel panic", 27}, 155 {"srto: kernel watchdog reset", 28}, 156 {"srto: normal", 29}, 157 {"srto: reboot", 30}, 158 {"srto: reboot-bootloader", 31}, 159 {"srto: security watchdog reset", 32}, 160 {"srto: wakesrc", 33}, 161 {"srto: watchdog", 34}, 162 {"srto:1-1", 35}, 163 {"srto:omap_hsmm", 36}, 164 {"srto:phy0", 37}, 165 {"srto:rtc0", 38}, 166 {"srto:touchpad", 39}, 167 {"watchdog", 40}, 168 {"watchdogr", 41}, 169 {"wdog_bark", 42}, 170 {"wdog_bite", 43}, 171 {"wdog_reset", 44}, 172 {"shutdown,", 45}, // Trailing comma is intentional. 173 {"shutdown,userrequested", 46}, 174 {"reboot,bootloader", 47}, 175 {"reboot,cold", 48}, 176 {"reboot,recovery", 49}, 177 {"thermal_shutdown", 50}, 178 {"s3_wakeup", 51} 179}; 180 181// Converts a string value representing the reason the system booted to an 182// integer representation. This is necessary for logging the boot_reason metric 183// via Tron, which does not accept non-integer buckets in histograms. 184int32_t BootReasonStrToEnum(const std::string& boot_reason) { 185 auto mapping = kBootReasonMap.find(boot_reason); 186 if (mapping != kBootReasonMap.end()) { 187 return mapping->second; 188 } 189 190 LOG(INFO) << "Unknown boot reason: " << boot_reason; 191 return kUnknownBootReason; 192} 193 194// Returns the appropriate metric key prefix for the boot_complete metric such 195// that boot metrics after a system update are labeled as ota_boot_complete; 196// otherwise, they are labeled as boot_complete. This method encapsulates the 197// bookkeeping required to track when a system update has occurred by storing 198// the UTC timestamp of the system build date and comparing against the current 199// system build date. 200std::string CalculateBootCompletePrefix() { 201 static const std::string kBuildDateKey = "build_date"; 202 std::string boot_complete_prefix = "boot_complete"; 203 204 std::string build_date_str = GetProperty("ro.build.date.utc"); 205 int32_t build_date; 206 if (!android::base::ParseInt(build_date_str, &build_date)) { 207 return std::string(); 208 } 209 210 BootEventRecordStore boot_event_store; 211 BootEventRecordStore::BootEventRecord record; 212 if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) { 213 boot_complete_prefix = "factory_reset_" + boot_complete_prefix; 214 boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date); 215 } else if (build_date != record.second) { 216 boot_complete_prefix = "ota_" + boot_complete_prefix; 217 boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date); 218 } 219 220 return boot_complete_prefix; 221} 222 223// Records the value of a given ro.boottime.init property in milliseconds. 224void RecordInitBootTimeProp( 225 BootEventRecordStore* boot_event_store, const char* property) { 226 std::string value = GetProperty(property); 227 228 int32_t time_in_ms; 229 if (android::base::ParseInt(value, &time_in_ms)) { 230 boot_event_store->AddBootEventWithValue(property, time_in_ms); 231 } 232} 233 234// A map from bootloader timing stage to the time that stage took during boot. 235typedef std::map<std::string, int32_t> BootloaderTimingMap; 236 237// Returns a mapping from bootloader stage names to the time those stages 238// took to boot. 239const BootloaderTimingMap GetBootLoaderTimings() { 240 BootloaderTimingMap timings; 241 242 // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN', 243 // where timeN is in milliseconds. 244 std::string value = GetProperty("ro.boot.boottime"); 245 if (value.empty()) { 246 // ro.boot.boottime is not reported on all devices. 247 return BootloaderTimingMap(); 248 } 249 250 auto stages = android::base::Split(value, ","); 251 for (const auto& stageTiming : stages) { 252 // |stageTiming| is of the form 'stage:time'. 253 auto stageTimingValues = android::base::Split(stageTiming, ":"); 254 DCHECK_EQ(2U, stageTimingValues.size()); 255 256 std::string stageName = stageTimingValues[0]; 257 int32_t time_ms; 258 if (android::base::ParseInt(stageTimingValues[1], &time_ms)) { 259 timings[stageName] = time_ms; 260 } 261 } 262 263 return timings; 264} 265 266// Parses and records the set of bootloader stages and associated boot times 267// from the ro.boot.boottime system property. 268void RecordBootloaderTimings(BootEventRecordStore* boot_event_store, 269 const BootloaderTimingMap& bootloader_timings) { 270 int32_t total_time = 0; 271 for (const auto& timing : bootloader_timings) { 272 total_time += timing.second; 273 boot_event_store->AddBootEventWithValue("boottime.bootloader." + timing.first, timing.second); 274 } 275 276 boot_event_store->AddBootEventWithValue("boottime.bootloader.total", total_time); 277} 278 279// Records the closest estimation to the absolute device boot time, i.e., 280// from power on to boot_complete, including bootloader times. 281void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store, 282 const BootloaderTimingMap& bootloader_timings, 283 std::chrono::milliseconds uptime) { 284 int32_t bootloader_time_ms = 0; 285 286 for (const auto& timing : bootloader_timings) { 287 if (timing.first.compare("SW") != 0) { 288 bootloader_time_ms += timing.second; 289 } 290 } 291 292 auto bootloader_duration = std::chrono::milliseconds(bootloader_time_ms); 293 auto absolute_total = 294 std::chrono::duration_cast<std::chrono::seconds>(bootloader_duration + uptime); 295 boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total.count()); 296} 297 298// Records several metrics related to the time it takes to boot the device, 299// including disambiguating boot time on encrypted or non-encrypted devices. 300void RecordBootComplete() { 301 BootEventRecordStore boot_event_store; 302 BootEventRecordStore::BootEventRecord record; 303 304 auto time_since_epoch = android::base::boot_clock::now().time_since_epoch(); 305 auto uptime = std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch); 306 time_t current_time_utc = time(nullptr); 307 308 if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) { 309 time_t last_boot_time_utc = record.second; 310 time_t time_since_last_boot = difftime(current_time_utc, 311 last_boot_time_utc); 312 boot_event_store.AddBootEventWithValue("time_since_last_boot", 313 time_since_last_boot); 314 } 315 316 boot_event_store.AddBootEventWithValue("last_boot_time_utc", current_time_utc); 317 318 // The boot_complete metric has two variants: boot_complete and 319 // ota_boot_complete. The latter signifies that the device is booting after 320 // a system update. 321 std::string boot_complete_prefix = CalculateBootCompletePrefix(); 322 if (boot_complete_prefix.empty()) { 323 // The system is hosed because the build date property could not be read. 324 return; 325 } 326 327 // post_decrypt_time_elapsed is only logged on encrypted devices. 328 if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) { 329 // Log the amount of time elapsed until the device is decrypted, which 330 // includes the variable amount of time the user takes to enter the 331 // decryption password. 332 boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime.count()); 333 334 // Subtract the decryption time to normalize the boot cycle timing. 335 std::chrono::seconds boot_complete = std::chrono::seconds(uptime.count() - record.second); 336 boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt", 337 boot_complete.count()); 338 } else { 339 boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption", 340 uptime.count()); 341 } 342 343 // Record the total time from device startup to boot complete, regardless of 344 // encryption state. 345 boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime.count()); 346 347 RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init"); 348 RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux"); 349 RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait"); 350 351 const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings(); 352 RecordBootloaderTimings(&boot_event_store, bootloader_timings); 353 354 auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_since_epoch); 355 RecordAbsoluteBootTime(&boot_event_store, bootloader_timings, uptime_ms); 356} 357 358// Records the boot_reason metric by querying the ro.boot.bootreason system 359// property. 360void RecordBootReason() { 361 std::string boot_reason_str = GetProperty("ro.boot.bootreason"); 362 android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT, 363 android::metricslogger::FIELD_PLATFORM_REASON, 364 boot_reason_str); 365 366 int32_t boot_reason = BootReasonStrToEnum(boot_reason_str); 367 BootEventRecordStore boot_event_store; 368 boot_event_store.AddBootEventWithValue("boot_reason", boot_reason); 369} 370 371// Records two metrics related to the user resetting a device: the time at 372// which the device is reset, and the time since the user last reset the 373// device. The former is only set once per-factory reset. 374void RecordFactoryReset() { 375 BootEventRecordStore boot_event_store; 376 BootEventRecordStore::BootEventRecord record; 377 378 time_t current_time_utc = time(nullptr); 379 380 if (current_time_utc < 0) { 381 // UMA does not display negative values in buckets, so convert to positive. 382 android::metricslogger::LogHistogram( 383 "factory_reset_current_time_failure", std::abs(current_time_utc)); 384 385 // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram 386 // is losing records somehow. 387 boot_event_store.AddBootEventWithValue( 388 "factory_reset_current_time_failure", std::abs(current_time_utc)); 389 return; 390 } else { 391 android::metricslogger::LogHistogram("factory_reset_current_time", current_time_utc); 392 393 // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram 394 // is losing records somehow. 395 boot_event_store.AddBootEventWithValue( 396 "factory_reset_current_time", current_time_utc); 397 } 398 399 // The factory_reset boot event does not exist after the device is reset, so 400 // use this signal to mark the time of the factory reset. 401 if (!boot_event_store.GetBootEvent("factory_reset", &record)) { 402 boot_event_store.AddBootEventWithValue("factory_reset", current_time_utc); 403 404 // Don't log the time_since_factory_reset until some time has elapsed. 405 // The data is not meaningful yet and skews the histogram buckets. 406 return; 407 } 408 409 // Calculate and record the difference in time between now and the 410 // factory_reset time. 411 time_t factory_reset_utc = record.second; 412 android::metricslogger::LogHistogram("factory_reset_record_value", factory_reset_utc); 413 414 // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram 415 // is losing records somehow. 416 boot_event_store.AddBootEventWithValue( 417 "factory_reset_record_value", factory_reset_utc); 418 419 time_t time_since_factory_reset = difftime(current_time_utc, 420 factory_reset_utc); 421 boot_event_store.AddBootEventWithValue("time_since_factory_reset", 422 time_since_factory_reset); 423} 424 425} // namespace 426 427int main(int argc, char **argv) { 428 android::base::InitLogging(argv); 429 430 const std::string cmd_line = GetCommandLine(argc, argv); 431 LOG(INFO) << "Service started: " << cmd_line; 432 433 int option_index = 0; 434 static const char value_str[] = "value"; 435 static const char boot_complete_str[] = "record_boot_complete"; 436 static const char boot_reason_str[] = "record_boot_reason"; 437 static const char factory_reset_str[] = "record_time_since_factory_reset"; 438 static const struct option long_options[] = { 439 { "help", no_argument, NULL, 'h' }, 440 { "log", no_argument, NULL, 'l' }, 441 { "print", no_argument, NULL, 'p' }, 442 { "record", required_argument, NULL, 'r' }, 443 { value_str, required_argument, NULL, 0 }, 444 { boot_complete_str, no_argument, NULL, 0 }, 445 { boot_reason_str, no_argument, NULL, 0 }, 446 { factory_reset_str, no_argument, NULL, 0 }, 447 { NULL, 0, NULL, 0 } 448 }; 449 450 std::string boot_event; 451 std::string value; 452 int opt = 0; 453 while ((opt = getopt_long(argc, argv, "hlpr:", long_options, &option_index)) != -1) { 454 switch (opt) { 455 // This case handles long options which have no single-character mapping. 456 case 0: { 457 const std::string option_name = long_options[option_index].name; 458 if (option_name == value_str) { 459 // |optarg| is an external variable set by getopt representing 460 // the option argument. 461 value = optarg; 462 } else if (option_name == boot_complete_str) { 463 RecordBootComplete(); 464 } else if (option_name == boot_reason_str) { 465 RecordBootReason(); 466 } else if (option_name == factory_reset_str) { 467 RecordFactoryReset(); 468 } else { 469 LOG(ERROR) << "Invalid option: " << option_name; 470 } 471 break; 472 } 473 474 case 'h': { 475 ShowHelp(argv[0]); 476 break; 477 } 478 479 case 'l': { 480 LogBootEvents(); 481 break; 482 } 483 484 case 'p': { 485 PrintBootEvents(); 486 break; 487 } 488 489 case 'r': { 490 // |optarg| is an external variable set by getopt representing 491 // the option argument. 492 boot_event = optarg; 493 break; 494 } 495 496 default: { 497 DCHECK_EQ(opt, '?'); 498 499 // |optopt| is an external variable set by getopt representing 500 // the value of the invalid option. 501 LOG(ERROR) << "Invalid option: " << optopt; 502 ShowHelp(argv[0]); 503 return EXIT_FAILURE; 504 } 505 } 506 } 507 508 if (!boot_event.empty()) { 509 RecordBootEventFromCommandLine(boot_event, value); 510 } 511 512 return 0; 513} 514