bootstat.cpp revision 6b930bf06076644b6b4456ee5eac01f1e1048132
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 <cutils/properties.h> 32#include <log/log.h> 33#include "boot_event_record_store.h" 34#include "event_log_list_builder.h" 35 36namespace { 37 38// Builds an EventLog buffer named |event| containing |data| and writes 39// the log into the Tron histogram logs. 40void LogBootEvent(const std::string& event, int32_t data) { 41 LOG(INFO) << "Logging boot metric: " << event << " " << data; 42 43 EventLogListBuilder log_builder; 44 log_builder.Append(event); 45 log_builder.Append(data); 46 47 std::unique_ptr<uint8_t[]> log; 48 size_t size; 49 log_builder.Release(&log, &size); 50 51 android_bWriteLog(HISTOGRAM_LOG_TAG, log.get(), size); 52} 53 54// Scans the boot event record store for record files and logs each boot event 55// via EventLog. 56void LogBootEvents() { 57 BootEventRecordStore boot_event_store; 58 59 auto events = boot_event_store.GetAllBootEvents(); 60 for (auto i = events.cbegin(); i != events.cend(); ++i) { 61 LogBootEvent(i->first, i->second); 62 } 63} 64 65void PrintBootEvents() { 66 printf("Boot events:\n"); 67 printf("------------\n"); 68 69 BootEventRecordStore boot_event_store; 70 auto events = boot_event_store.GetAllBootEvents(); 71 for (auto i = events.cbegin(); i != events.cend(); ++i) { 72 printf("%s\t%d\n", i->first.c_str(), i->second); 73 } 74} 75 76void ShowHelp(const char *cmd) { 77 fprintf(stderr, "Usage: %s [options]\n", cmd); 78 fprintf(stderr, 79 "options include:\n" 80 " -h, --help Show this help\n" 81 " -l, --log Log all metrics to logstorage\n" 82 " -p, --print Dump the boot event records to the console\n" 83 " -r, --record Record the timestamp of a named boot event\n" 84 " --record_boot_reason Record the reason why the device booted\n" 85 " --record_time_since_factory_reset Record the time since the device was reset\n"); 86} 87 88// Constructs a readable, printable string from the givencommand line 89// arguments. 90std::string GetCommandLine(int argc, char **argv) { 91 std::string cmd; 92 for (int i = 0; i < argc; ++i) { 93 cmd += argv[i]; 94 cmd += " "; 95 } 96 97 return cmd; 98} 99 100// Convenience wrapper over the property API that returns an 101// std::string. 102std::string GetProperty(const char* key) { 103 std::vector<char> temp(PROPERTY_VALUE_MAX); 104 const int len = property_get(key, &temp[0], nullptr); 105 if (len < 0) { 106 return ""; 107 } 108 return std::string(&temp[0], len); 109} 110 111constexpr int32_t kUnknownBootReason = 1; 112 113// A mapping from boot reason string, as read from the ro.boot.bootreason 114// system property, to a unique integer ID. Viewers of log data dashboards for 115// the boot_reason metric may refer to this mapping to discern the histogram 116// values. 117const std::map<std::string, int32_t> kBootReasonMap = { 118 {"unknown", kUnknownBootReason}, 119 {"normal", 2}, 120 {"recovery", 3}, 121 {"reboot", 4}, 122 {"PowerKey", 5}, 123 {"hard_reset", 6}, 124 {"kernel_panic", 7}, 125 {"rpm_err", 8}, 126 {"hw_reset", 9}, 127 {"tz_err", 10}, 128 {"adsp_err", 11}, 129 {"modem_err", 12}, 130 {"mba_err", 13}, 131 {"Watchdog", 14}, 132 {"Panic", 15}, 133 {"power_key", 16}, 134 {"power_on", 17}, 135 {"Reboot", 18}, 136 {"rtc", 19}, 137 {"edl", 20}, 138}; 139 140// Converts a string value representing the reason the system booted to an 141// integer representation. This is necessary for logging the boot_reason metric 142// via Tron, which does not accept non-integer buckets in histograms. 143int32_t BootReasonStrToEnum(const std::string& boot_reason) { 144 auto mapping = kBootReasonMap.find(boot_reason); 145 if (mapping != kBootReasonMap.end()) { 146 return mapping->second; 147 } 148 149 LOG(INFO) << "Unknown boot reason: " << boot_reason; 150 return kUnknownBootReason; 151} 152 153// Records the boot_reason metric by querying the ro.boot.bootreason system 154// property. 155void RecordBootReason() { 156 int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason")); 157 BootEventRecordStore boot_event_store; 158 boot_event_store.AddBootEventWithValue("boot_reason", boot_reason); 159} 160 161// Records two metrics related to the user resetting a device: the time at 162// which the device is reset, and the time since the user last reset the 163// device. The former is only set once per-factory reset. 164void RecordFactoryReset() { 165 BootEventRecordStore boot_event_store; 166 BootEventRecordStore::BootEventRecord record; 167 168 time_t current_time_utc = time(nullptr); 169 170 static const char* factory_reset_current_time = "factory_reset_current_time"; 171 if (current_time_utc < 0) { 172 // UMA does not display negative values in buckets, so convert to positive. 173 LogBootEvent(factory_reset_current_time, std::abs(current_time_utc)); 174 return; 175 } else { 176 LogBootEvent(factory_reset_current_time, current_time_utc); 177 } 178 179 // The factory_reset boot event does not exist after the device is reset, so 180 // use this signal to mark the time of the factory reset. 181 if (!boot_event_store.GetBootEvent("factory_reset", &record)) { 182 boot_event_store.AddBootEventWithValue("factory_reset", current_time_utc); 183 184 // Don't log the time_since_factory_reset until some time has elapsed. 185 // The data is not meaningful yet and skews the histogram buckets. 186 return; 187 } 188 189 // Calculate and record the difference in time between now and the 190 // factory_reset time. 191 time_t factory_reset_utc = record.second; 192 LogBootEvent("factory_reset_record_value", factory_reset_utc); 193 time_t time_since_factory_reset = difftime(current_time_utc, 194 factory_reset_utc); 195 boot_event_store.AddBootEventWithValue("time_since_factory_reset", 196 time_since_factory_reset); 197} 198 199} // namespace 200 201int main(int argc, char **argv) { 202 android::base::InitLogging(argv); 203 204 const std::string cmd_line = GetCommandLine(argc, argv); 205 LOG(INFO) << "Service started: " << cmd_line; 206 207 int option_index = 0; 208 static const char boot_reason_str[] = "record_boot_reason"; 209 static const char factory_reset_str[] = "record_time_since_factory_reset"; 210 static const struct option long_options[] = { 211 { "help", no_argument, NULL, 'h' }, 212 { "log", no_argument, NULL, 'l' }, 213 { "print", no_argument, NULL, 'p' }, 214 { "record", required_argument, NULL, 'r' }, 215 { boot_reason_str, no_argument, NULL, 0 }, 216 { factory_reset_str, no_argument, NULL, 0 }, 217 { NULL, 0, NULL, 0 } 218 }; 219 220 int opt = 0; 221 while ((opt = getopt_long(argc, argv, "hlpr:", long_options, &option_index)) != -1) { 222 switch (opt) { 223 // This case handles long options which have no single-character mapping. 224 case 0: { 225 const std::string option_name = long_options[option_index].name; 226 if (option_name == boot_reason_str) { 227 RecordBootReason(); 228 } else if (option_name == factory_reset_str) { 229 RecordFactoryReset(); 230 } else { 231 LOG(ERROR) << "Invalid option: " << option_name; 232 } 233 break; 234 } 235 236 case 'h': { 237 ShowHelp(argv[0]); 238 break; 239 } 240 241 case 'l': { 242 LogBootEvents(); 243 break; 244 } 245 246 case 'p': { 247 PrintBootEvents(); 248 break; 249 } 250 251 case 'r': { 252 // |optarg| is an external variable set by getopt representing 253 // the option argument. 254 const char* event = optarg; 255 256 BootEventRecordStore boot_event_store; 257 boot_event_store.AddBootEvent(event); 258 break; 259 } 260 261 default: { 262 DCHECK_EQ(opt, '?'); 263 264 // |optopt| is an external variable set by getopt representing 265 // the value of the invalid option. 266 LOG(ERROR) << "Invalid option: " << optopt; 267 ShowHelp(argv[0]); 268 return EXIT_FAILURE; 269 } 270 } 271 } 272 273 return 0; 274} 275