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:
parent
dc5bd3a1b9
commit
a4a1a4ac85
7 changed files with 128 additions and 15 deletions
|
@ -32,6 +32,7 @@ bootstat_test_src_files := \
|
|||
|
||||
bootstat_shared_libs := \
|
||||
libbase \
|
||||
libcutils \
|
||||
liblog
|
||||
|
||||
bootstat_cflags := \
|
||||
|
|
|
@ -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 ##
|
||||
|
||||
|
|
|
@ -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(), ×) == -1) {
|
||||
PLOG(ERROR) << "Failed to set mtime for " << record_path;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue