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