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