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 #include <cmath> 24 #include <cstddef> 25 #include <cstdio> 26 #include <ctime> 27 #include <map> 28 #include <memory> 29 #include <string> 30 #include <android-base/logging.h> 31 #include <android-base/parseint.h> 32 #include <cutils/properties.h> 33 #include <log/log.h> 34 #include "boot_event_record_store.h" 35 #include "event_log_list_builder.h" 36 #include "histogram_logger.h" 37 #include "uptime_parser.h" 38 39 namespace { 40 41 // Scans the boot event record store for record files and logs each boot event 42 // via EventLog. LogBootEvents()43 void LogBootEvents() { 44 BootEventRecordStore boot_event_store; 45 46 auto events = boot_event_store.GetAllBootEvents(); 47 for (auto i = events.cbegin(); i != events.cend(); ++i) { 48 bootstat::LogHistogram(i->first, i->second); 49 } 50 } 51 PrintBootEvents()52 void PrintBootEvents() { 53 printf("Boot events:\n"); 54 printf("------------\n"); 55 56 BootEventRecordStore boot_event_store; 57 auto events = boot_event_store.GetAllBootEvents(); 58 for (auto i = events.cbegin(); i != events.cend(); ++i) { 59 printf("%s\t%d\n", i->first.c_str(), i->second); 60 } 61 } 62 ShowHelp(const char * cmd)63 void ShowHelp(const char *cmd) { 64 fprintf(stderr, "Usage: %s [options]\n", cmd); 65 fprintf(stderr, 66 "options include:\n" 67 " -h, --help Show this help\n" 68 " -l, --log Log all metrics to logstorage\n" 69 " -p, --print Dump the boot event records to the console\n" 70 " -r, --record Record the timestamp of a named boot event\n" 71 " --record_boot_reason Record the reason why the device booted\n" 72 " --record_time_since_factory_reset Record the time since the device was reset\n"); 73 } 74 75 // Constructs a readable, printable string from the givencommand line 76 // arguments. GetCommandLine(int argc,char ** argv)77 std::string GetCommandLine(int argc, char **argv) { 78 std::string cmd; 79 for (int i = 0; i < argc; ++i) { 80 cmd += argv[i]; 81 cmd += " "; 82 } 83 84 return cmd; 85 } 86 87 // Convenience wrapper over the property API that returns an 88 // std::string. GetProperty(const char * key)89 std::string GetProperty(const char* key) { 90 std::vector<char> temp(PROPERTY_VALUE_MAX); 91 const int len = property_get(key, &temp[0], nullptr); 92 if (len < 0) { 93 return ""; 94 } 95 return std::string(&temp[0], len); 96 } 97 98 constexpr int32_t kUnknownBootReason = 1; 99 100 // A mapping from boot reason string, as read from the ro.boot.bootreason 101 // system property, to a unique integer ID. Viewers of log data dashboards for 102 // the boot_reason metric may refer to this mapping to discern the histogram 103 // values. 104 const std::map<std::string, int32_t> kBootReasonMap = { 105 {"unknown", kUnknownBootReason}, 106 {"normal", 2}, 107 {"recovery", 3}, 108 {"reboot", 4}, 109 {"PowerKey", 5}, 110 {"hard_reset", 6}, 111 {"kernel_panic", 7}, 112 {"rpm_err", 8}, 113 {"hw_reset", 9}, 114 {"tz_err", 10}, 115 {"adsp_err", 11}, 116 {"modem_err", 12}, 117 {"mba_err", 13}, 118 {"Watchdog", 14}, 119 {"Panic", 15}, 120 {"power_key", 16}, 121 {"power_on", 17}, 122 {"Reboot", 18}, 123 {"rtc", 19}, 124 {"edl", 20}, 125 }; 126 127 // Converts a string value representing the reason the system booted to an 128 // integer representation. This is necessary for logging the boot_reason metric 129 // via Tron, which does not accept non-integer buckets in histograms. BootReasonStrToEnum(const std::string & boot_reason)130 int32_t BootReasonStrToEnum(const std::string& boot_reason) { 131 auto mapping = kBootReasonMap.find(boot_reason); 132 if (mapping != kBootReasonMap.end()) { 133 return mapping->second; 134 } 135 136 LOG(INFO) << "Unknown boot reason: " << boot_reason; 137 return kUnknownBootReason; 138 } 139 140 // Returns the appropriate metric key prefix for the boot_complete metric such 141 // that boot metrics after a system update are labeled as ota_boot_complete; 142 // otherwise, they are labeled as boot_complete. This method encapsulates the 143 // bookkeeping required to track when a system update has occurred by storing 144 // the UTC timestamp of the system build date and comparing against the current 145 // system build date. CalculateBootCompletePrefix()146 std::string CalculateBootCompletePrefix() { 147 static const std::string kBuildDateKey = "build_date"; 148 std::string boot_complete_prefix = "boot_complete"; 149 150 std::string build_date_str = GetProperty("ro.build.date.utc"); 151 int32_t build_date; 152 if (!android::base::ParseInt(build_date_str.c_str(), &build_date)) { 153 return std::string(); 154 } 155 156 BootEventRecordStore boot_event_store; 157 BootEventRecordStore::BootEventRecord record; 158 if (!boot_event_store.GetBootEvent(kBuildDateKey, &record) || 159 build_date != record.second) { 160 boot_complete_prefix = "ota_" + boot_complete_prefix; 161 boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date); 162 } 163 164 return boot_complete_prefix; 165 } 166 167 // Records several metrics related to the time it takes to boot the device, 168 // including disambiguating boot time on encrypted or non-encrypted devices. RecordBootComplete()169 void RecordBootComplete() { 170 BootEventRecordStore boot_event_store; 171 BootEventRecordStore::BootEventRecord record; 172 173 time_t uptime = bootstat::ParseUptime(); 174 time_t current_time_utc = time(nullptr); 175 176 if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) { 177 time_t last_boot_time_utc = record.second; 178 time_t time_since_last_boot = difftime(current_time_utc, 179 last_boot_time_utc); 180 boot_event_store.AddBootEventWithValue("time_since_last_boot", 181 time_since_last_boot); 182 } 183 184 boot_event_store.AddBootEventWithValue("last_boot_time_utc", current_time_utc); 185 186 // The boot_complete metric has two variants: boot_complete and 187 // ota_boot_complete. The latter signifies that the device is booting after 188 // a system update. 189 std::string boot_complete_prefix = CalculateBootCompletePrefix(); 190 if (boot_complete_prefix.empty()) { 191 // The system is hosed because the build date property could not be read. 192 return; 193 } 194 195 // post_decrypt_time_elapsed is only logged on encrypted devices. 196 if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) { 197 // Log the amount of time elapsed until the device is decrypted, which 198 // includes the variable amount of time the user takes to enter the 199 // decryption password. 200 boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime); 201 202 // Subtract the decryption time to normalize the boot cycle timing. 203 time_t boot_complete = uptime - record.second; 204 boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt", 205 boot_complete); 206 207 208 } else { 209 boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption", 210 uptime); 211 } 212 213 // Record the total time from device startup to boot complete, regardless of 214 // encryption state. 215 boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime); 216 } 217 218 // Records the boot_reason metric by querying the ro.boot.bootreason system 219 // property. RecordBootReason()220 void RecordBootReason() { 221 int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason")); 222 BootEventRecordStore boot_event_store; 223 boot_event_store.AddBootEventWithValue("boot_reason", boot_reason); 224 } 225 226 // Records two metrics related to the user resetting a device: the time at 227 // which the device is reset, and the time since the user last reset the 228 // device. The former is only set once per-factory reset. RecordFactoryReset()229 void RecordFactoryReset() { 230 BootEventRecordStore boot_event_store; 231 BootEventRecordStore::BootEventRecord record; 232 233 time_t current_time_utc = time(nullptr); 234 235 if (current_time_utc < 0) { 236 // UMA does not display negative values in buckets, so convert to positive. 237 bootstat::LogHistogram( 238 "factory_reset_current_time_failure", std::abs(current_time_utc)); 239 240 // Logging via BootEventRecordStore to see if using bootstat::LogHistogram 241 // is losing records somehow. 242 boot_event_store.AddBootEventWithValue( 243 "factory_reset_current_time_failure", std::abs(current_time_utc)); 244 return; 245 } else { 246 bootstat::LogHistogram("factory_reset_current_time", current_time_utc); 247 248 // Logging via BootEventRecordStore to see if using bootstat::LogHistogram 249 // is losing records somehow. 250 boot_event_store.AddBootEventWithValue( 251 "factory_reset_current_time", current_time_utc); 252 } 253 254 // The factory_reset boot event does not exist after the device is reset, so 255 // use this signal to mark the time of the factory reset. 256 if (!boot_event_store.GetBootEvent("factory_reset", &record)) { 257 boot_event_store.AddBootEventWithValue("factory_reset", current_time_utc); 258 259 // Don't log the time_since_factory_reset until some time has elapsed. 260 // The data is not meaningful yet and skews the histogram buckets. 261 return; 262 } 263 264 // Calculate and record the difference in time between now and the 265 // factory_reset time. 266 time_t factory_reset_utc = record.second; 267 bootstat::LogHistogram("factory_reset_record_value", factory_reset_utc); 268 269 // Logging via BootEventRecordStore to see if using bootstat::LogHistogram 270 // is losing records somehow. 271 boot_event_store.AddBootEventWithValue( 272 "factory_reset_record_value", factory_reset_utc); 273 274 time_t time_since_factory_reset = difftime(current_time_utc, 275 factory_reset_utc); 276 boot_event_store.AddBootEventWithValue("time_since_factory_reset", 277 time_since_factory_reset); 278 } 279 280 } // namespace 281 main(int argc,char ** argv)282 int main(int argc, char **argv) { 283 android::base::InitLogging(argv); 284 285 const std::string cmd_line = GetCommandLine(argc, argv); 286 LOG(INFO) << "Service started: " << cmd_line; 287 288 int option_index = 0; 289 static const char boot_complete_str[] = "record_boot_complete"; 290 static const char boot_reason_str[] = "record_boot_reason"; 291 static const char factory_reset_str[] = "record_time_since_factory_reset"; 292 static const struct option long_options[] = { 293 { "help", no_argument, NULL, 'h' }, 294 { "log", no_argument, NULL, 'l' }, 295 { "print", no_argument, NULL, 'p' }, 296 { "record", required_argument, NULL, 'r' }, 297 { boot_complete_str, no_argument, NULL, 0 }, 298 { boot_reason_str, no_argument, NULL, 0 }, 299 { factory_reset_str, no_argument, NULL, 0 }, 300 { NULL, 0, NULL, 0 } 301 }; 302 303 int opt = 0; 304 while ((opt = getopt_long(argc, argv, "hlpr:", long_options, &option_index)) != -1) { 305 switch (opt) { 306 // This case handles long options which have no single-character mapping. 307 case 0: { 308 const std::string option_name = long_options[option_index].name; 309 if (option_name == boot_complete_str) { 310 RecordBootComplete(); 311 } else if (option_name == boot_reason_str) { 312 RecordBootReason(); 313 } else if (option_name == factory_reset_str) { 314 RecordFactoryReset(); 315 } else { 316 LOG(ERROR) << "Invalid option: " << option_name; 317 } 318 break; 319 } 320 321 case 'h': { 322 ShowHelp(argv[0]); 323 break; 324 } 325 326 case 'l': { 327 LogBootEvents(); 328 break; 329 } 330 331 case 'p': { 332 PrintBootEvents(); 333 break; 334 } 335 336 case 'r': { 337 // |optarg| is an external variable set by getopt representing 338 // the option argument. 339 const char* event = optarg; 340 341 BootEventRecordStore boot_event_store; 342 boot_event_store.AddBootEvent(event); 343 break; 344 } 345 346 default: { 347 DCHECK_EQ(opt, '?'); 348 349 // |optopt| is an external variable set by getopt representing 350 // the value of the invalid option. 351 LOG(ERROR) << "Invalid option: " << optopt; 352 ShowHelp(argv[0]); 353 return EXIT_FAILURE; 354 } 355 } 356 } 357 358 return 0; 359 } 360