bootstat: Add support for logging the boot_reason metric.

This value is read from the ro.boot.bootreason system property.

Bug: 21724738
Change-Id: I43bef3d85ba9c8d87669a91da1aa675d9a86e348
This commit is contained in:
James Hawkins 2016-02-09 15:32:38 -08:00
parent dc5bd3a1b9
commit a4a1a4ac85
7 changed files with 128 additions and 15 deletions

View file

@ -32,6 +32,7 @@ bootstat_test_src_files := \
bootstat_shared_libs := \
libbase \
libcutils \
liblog
bootstat_cflags := \

View file

@ -7,10 +7,11 @@ analysis.
Usage: bootstat [options]
options include:
-d Dump the boot event records to the console.
-h Show this help.
-l Log all metrics to logstorage.
-r Record the relative time of a named boot event.
-h, --help Show this help
-l, --log Log all metrics to logstorage
-p, --print Dump the boot event records to the console
-r, --record Record the timestamp of a named boot event
--record_boot_reason Record the reason why the device booted
## Relative time ##

View file

@ -56,19 +56,31 @@ void BootEventRecordStore::AddBootEvent(const std::string& name) {
LOG(ERROR) << "Failed to read /proc/uptime";
}
// Cast intentionally rounds down.
int32_t uptime = static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
AddBootEventWithValue(name, uptime);
}
// The implementation of AddBootEventValue makes use of the mtime file
// attribute to store the value associated with a boot event in order to
// optimize on-disk size requirements and small-file thrashing.
void BootEventRecordStore::AddBootEventWithValue(
const std::string& name, int32_t value) {
std::string record_path = GetBootEventPath(name);
if (creat(record_path.c_str(), S_IRUSR | S_IWUSR) == -1) {
PLOG(ERROR) << "Failed to create " << record_path;
}
// Fill out the stat structure for |record_path| in order to get the atime to
// set in the utime() call.
struct stat file_stat;
if (stat(record_path.c_str(), &file_stat) == -1) {
PLOG(ERROR) << "Failed to read " << record_path;
}
// Cast intentionally rounds down.
time_t uptime = static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
struct utimbuf times = {file_stat.st_atime, uptime};
// Set the |modtime| of the file to store the value of the boot event while
// preserving the |actime| (as read by stat).
struct utimbuf times = {/* actime */ file_stat.st_atime, /* modtime */ value};
if (utime(record_path.c_str(), &times) == -1) {
PLOG(ERROR) << "Failed to set mtime for " << record_path;
}

View file

@ -37,6 +37,10 @@ class BootEventRecordStore {
// Persists the boot event named |name| in the record store.
void AddBootEvent(const std::string& name);
// Persists the boot event named |name| with the associated |value| in the
// record store.
void AddBootEventWithValue(const std::string& name, int32_t value);
// Returns a list of all of the boot events persisted in the record store.
std::vector<BootEventRecord> GetAllBootEvents() const;
@ -45,6 +49,7 @@ class BootEventRecordStore {
// more test-friendly path.
FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent);
FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);
// Sets the filesystem path of the record store.
void SetStorePath(const std::string& path);

View file

@ -154,3 +154,15 @@ TEST_F(BootEventRecordStoreTest, AddMultipleBootEvents) {
EXPECT_TRUE(FuzzUptimeEquals(uptime, *i));
}
}
TEST_F(BootEventRecordStoreTest, AddBootEventWithValue) {
BootEventRecordStore store;
store.SetStorePath(GetStorePathForTesting());
store.AddBootEventWithValue("permian", 42);
auto events = store.GetAllBootEvents();
ASSERT_EQ(1U, events.size());
EXPECT_EQ("permian", events[0].first);
EXPECT_EQ(42, events[0].second);
}

View file

@ -18,14 +18,15 @@
// timestamp, dump the persisted events, and log all events to EventLog to be
// uploaded to Android log storage via Tron.
//#define LOG_TAG "bootstat"
#include <getopt.h>
#include <unistd.h>
#include <cstddef>
#include <cstdio>
#include <map>
#include <memory>
#include <string>
#include <android-base/logging.h>
#include <cutils/properties.h>
#include <log/log.h>
#include "boot_event_record_store.h"
#include "event_log_list_builder.h"
@ -35,7 +36,7 @@ namespace {
// Builds an EventLog buffer named |event| containing |data| and writes
// the log into the Tron histogram logs.
void LogBootEvent(const std::string& event, int32_t data) {
LOG(INFO) << "Logging boot time: " << event << " " << data;
LOG(INFO) << "Logging boot metric: " << event << " " << data;
EventLogListBuilder log_builder;
log_builder.Append(event);
@ -74,10 +75,11 @@ void ShowHelp(const char *cmd) {
fprintf(stderr, "Usage: %s [options]\n", cmd);
fprintf(stderr,
"options include:\n"
" -d Dump the boot event records to the console.\n"
" -h Show this help.\n"
" -l Log all metrics to logstorage.\n"
" -r Record the timestamp of a named boot event.\n");
" -h, --help Show this help\n"
" -l, --log Log all metrics to logstorage\n"
" -p, --print Dump the boot event records to the console\n"
" -r, --record Record the timestamp of a named boot event\n"
" --record_boot_reason Record the reason why the device booted\n");
}
// Constructs a readable, printable string from the givencommand line
@ -92,6 +94,61 @@ std::string GetCommandLine(int argc, char **argv) {
return cmd;
}
// Convenience wrapper over the property API that returns an
// std::string.
std::string GetProperty(const char* key) {
std::vector<char> temp(PROPERTY_VALUE_MAX);
const int len = property_get(key, &temp[0], nullptr);
if (len < 0) {
return "";
}
return std::string(&temp[0], len);
}
// A mapping from boot reason string, as read from the ro.boot.bootreason
// system property, to a unique integer ID. Viewers of log data dashboards for
// the boot_reason metric may refer to this mapping to discern the histogram
// values.
const std::map<std::string, int> kBootReasonMap = {
{"normal", 0},
{"recovery", 1},
{"reboot", 2},
{"PowerKey", 3},
{"hard_reset", 4},
{"kernel_panic", 5},
{"rpm_err", 6},
{"hw_reset", 7},
{"tz_err", 8},
{"adsp_err", 9},
{"modem_err", 10},
{"mba_err", 11},
{"Watchdog", 12},
{"Panic", 13},
};
// Converts a string value representing the reason the system booted to an
// integer representation. This is necessary for logging the boot_reason metric
// via Tron, which does not accept non-integer buckets in histograms.
int32_t BootReasonStrToEnum(const std::string& boot_reason) {
static const int32_t kUnknownBootReason = -1;
auto mapping = kBootReasonMap.find(boot_reason);
if (mapping != kBootReasonMap.end()) {
return mapping->second;
}
LOG(INFO) << "Unknown boot reason: " << boot_reason;
return kUnknownBootReason;
}
// Records the boot_reason metric by querying the ro.boot.bootreason system
// property.
void RecordBootReason() {
int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason"));
BootEventRecordStore boot_event_store;
boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
}
} // namespace
int main(int argc, char **argv) {
@ -100,9 +157,31 @@ int main(int argc, char **argv) {
const std::string cmd_line = GetCommandLine(argc, argv);
LOG(INFO) << "Service started: " << cmd_line;
int option_index = 0;
static const char boot_reason_str[] = "record_boot_reason";
static const struct option long_options[] = {
{ "help", no_argument, NULL, 'h' },
{ "log", no_argument, NULL, 'l' },
{ "print", no_argument, NULL, 'p' },
{ "record", required_argument, NULL, 'r' },
{ boot_reason_str, no_argument, NULL, 0 },
{ NULL, 0, NULL, 0 }
};
int opt = 0;
while ((opt = getopt(argc, argv, "hlpr:")) != -1) {
while ((opt = getopt_long(argc, argv, "hlpr:", long_options, &option_index)) != -1) {
switch (opt) {
// This case handles long options which have no single-character mapping.
case 0: {
const std::string option_name = long_options[option_index].name;
if (option_name == boot_reason_str) {
RecordBootReason();
} else {
LOG(ERROR) << "Invalid option: " << option_name;
}
break;
}
case 'h': {
ShowHelp(argv[0]);
break;

View file

@ -10,5 +10,8 @@ on property:init.svc.bootanim=stopped
# Record boot_complete timing event.
exec - root root -- /system/bin/bootstat -r boot_complete
# Record the boot reason.
exec - root root -- /system/bin/bootstat --record_boot_reason
# Log all boot events.
exec - root root -- /system/bin/bootstat -l