Loading bootstat/boot_event_record_store.cpp +7 −11 Original line number Diff line number Diff line Loading @@ -66,8 +66,7 @@ void BootEventRecordStore::AddBootEvent(const std::string& event) { // 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& event, int32_t value) { void BootEventRecordStore::AddBootEventWithValue(const std::string& event, int32_t value) { std::string record_path = GetBootEventPath(event); int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR); if (record_fd == -1) { Loading Loading @@ -96,8 +95,7 @@ void BootEventRecordStore::AddBootEventWithValue( close(record_fd); } bool BootEventRecordStore::GetBootEvent( const std::string& event, BootEventRecord* record) const { bool BootEventRecordStore::GetBootEvent(const std::string& event, BootEventRecord* record) const { CHECK_NE(static_cast<BootEventRecord*>(nullptr), record); CHECK(!event.empty()); Loading @@ -112,8 +110,7 @@ bool BootEventRecordStore::GetBootEvent( return true; } std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore:: GetAllBootEvents() const { std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::GetAllBootEvents() const { std::vector<BootEventRecord> events; std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir); Loading Loading @@ -147,8 +144,7 @@ void BootEventRecordStore::SetStorePath(const std::string& path) { store_path_ = path; } std::string BootEventRecordStore::GetBootEventPath( const std::string& event) const { std::string BootEventRecordStore::GetBootEventPath(const std::string& event) const { DCHECK_EQ('/', store_path_.back()); return store_path_ + event; } bootstat/boot_event_record_store.h +2 −2 Original line number Diff line number Diff line Loading @@ -17,12 +17,12 @@ #ifndef BOOT_EVENT_RECORD_STORE_H_ #define BOOT_EVENT_RECORD_STORE_H_ #include <android-base/macros.h> #include <gtest/gtest_prod.h> #include <cstdint> #include <string> #include <utility> #include <vector> #include <android-base/macros.h> #include <gtest/gtest_prod.h> // BootEventRecordStore manages the persistence of boot events to the record // store and the retrieval of all boot event records from the store. Loading bootstat/boot_event_record_store_test.cpp +6 −12 Original line number Diff line number Diff line Loading @@ -101,13 +101,9 @@ time_t GetUptimeSeconds() { class BootEventRecordStoreTest : public ::testing::Test { public: BootEventRecordStoreTest() { store_path_ = std::string(store_dir_.path) + "/"; } BootEventRecordStoreTest() { store_path_ = std::string(store_dir_.path) + "/"; } const std::string& GetStorePathForTesting() const { return store_path_; } const std::string& GetStorePathForTesting() const { return store_path_; } private: void TearDown() { Loading Loading @@ -159,9 +155,7 @@ TEST_F(BootEventRecordStoreTest, AddMultipleBootEvents) { store.AddBootEvent("triassic"); const std::string EXPECTED_NAMES[] = { "cretaceous", "jurassic", "triassic", "cretaceous", "jurassic", "triassic", }; auto events = store.GetAllBootEvents(); Loading bootstat/boot_reason_test.sh +30 −5 Original line number Diff line number Diff line Loading @@ -370,6 +370,7 @@ test_ota() { fast and fake (touch build_date on device to make it different)" ] test_optional_ota() { echo "INFO: expected duration of ${TEST} test about 45 seconds" >&2 echo "WARNING: ${TEST} requires userdebug build" >&2 adb shell su root touch /data/misc/bootstat/build_date >&2 adb reboot ota wait_for_screen Loading Loading @@ -425,6 +426,7 @@ Decision to rummage through bootstat data files was made as a _real_ factory_reset is too destructive to the device." ] test_factory_reset() { echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2 echo "WARNING: ${TEST} requires userdebug build" >&2 adb shell su root rm /data/misc/bootstat/build_date >&2 adb reboot >&2 wait_for_screen Loading Loading @@ -479,7 +481,8 @@ test_hard() { [ "USAGE: test_battery battery test (trick): - echo healthd: battery l=2 | adb shell su root tee /dev/kmsg ; adb reboot cold - echo healthd: battery l=2<space> | adb shell su root tee /dev/kmsg - adb reboot cold - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report reboot,battery, unless healthd managed to log Loading @@ -491,7 +494,7 @@ battery test (trick): + setprop logd.kernel false + rm /sys/fs/pstore/console-ramoops + rm /sys/fs/pstore/console-ramoops-0 + write /dev/kmsg \"healthd: battery l=2 + write /dev/kmsg \"healthd: battery l=2${SPACE} +\" - adb reboot fs - (wait until screen is up, boot has completed) Loading @@ -500,9 +503,10 @@ battery test (trick): - (replace set logd.kernel true to the above, and retry test)" ] test_battery() { echo "INFO: expected duration of ${TEST} test roughly two minutes" >&2 echo "WARNING: ${TEST} requires userdebug build" >&2 # Send it _many_ times to combat devices with flakey pstore for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do echo healthd: battery l=2 | adb shell su root tee /dev/kmsg >/dev/null echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null done adb reboot cold >&2 adb wait-for-device Loading @@ -516,7 +520,7 @@ test_battery() { if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then # retry for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do echo healthd: battery l=2 | adb shell su root tee /dev/kmsg >/dev/null echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null done adb reboot cold >&2 adb wait-for-device Loading Loading @@ -550,6 +554,7 @@ kernel_panic test: - NB: should report kernel_panic,sysrq" ] test_kernel_panic() { echo "INFO: expected duration of ${TEST} test > 2 minutes" >&2 echo "WARNING: ${TEST} requires userdebug build" >&2 echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null wait_for_screen EXPECT_PROPERTY sys.boot.reason kernel_panic,sysrq Loading Loading @@ -640,6 +645,26 @@ test_adb_reboot() { report_bootstat_logs reboot,adb } [ "USAGE: test_Its_Just_So_Hard_reboot Its Just So Hard reboot test: - adb shell reboot 'Its Just So Hard' - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report reboot,its_just_so_hard - NB: expect log \"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\"" ] test_Its_Just_So_Hard_reboot() { echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2 echo "INFO: ${TEST} cleanup requires userdebug build" >&2 adb shell 'reboot "Its Just So Hard"' wait_for_screen EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard" adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard report_bootstat_logs reboot,its_just_so_hard } [ "USAGE: ${0##*/} [-s SERIAL] [tests] Mainline executive to run the above tests" ] Loading bootstat/bootstat.cpp +167 −147 Original line number Diff line number Diff line Loading @@ -19,8 +19,8 @@ // uploaded to Android log storage via Tron. #include <getopt.h> #include <unistd.h> #include <sys/klog.h> #include <unistd.h> #include <chrono> #include <cmath> Loading Loading @@ -61,8 +61,7 @@ void LogBootEvents() { // Records the named boot |event| to the record store. If |value| is non-empty // and is a proper string representation of an integer value, the converted // integer value is associated with the boot event. void RecordBootEventFromCommandLine( const std::string& event, const std::string& value_str) { void RecordBootEventFromCommandLine(const std::string& event, const std::string& value_str) { BootEventRecordStore boot_event_store; if (!value_str.empty()) { int32_t value = 0; Loading Loading @@ -221,6 +220,7 @@ int32_t BootReasonStrToEnum(const std::string& boot_reason) { // Canonical list of supported primary reboot reasons. const std::vector<const std::string> knownReasons = { // clang-format off // kernel "watchdog", "kernel_panic", Loading @@ -233,6 +233,7 @@ const std::vector<const std::string> knownReasons = { "warm", "shutdown", // Can not happen from ro.boot.bootreason "reboot", // Default catch-all for anything unknown // clang-format on }; // Returns true if the supplied reason prefix is considered detailed enough. Loading Loading @@ -320,8 +321,12 @@ bool addKernelPanicSubReason(const std::string& console, std::string& ret) { // std::transform Helper callback functions: // Converts a string value representing the reason the system booted to a // string complying with Android system standard reason. char tounderline(char c) { return ::isblank(c) ? '_' : c; } char toprintable(char c) { return ::isprint(c) ? c : '?'; } char tounderline(char c) { return ::isblank(c) ? '_' : c; } char toprintable(char c) { return ::isprint(c) ? c : '?'; } const char system_reboot_reason_property[] = "sys.boot.reason"; const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY; Loading @@ -329,6 +334,8 @@ const char bootloader_reboot_reason_property[] = "ro.boot.bootreason"; // Scrub, Sanitize, Standardize and Enhance the boot reason string supplied. std::string BootReasonStrToReason(const std::string& boot_reason) { static const size_t max_reason_length = 256; std::string ret(GetProperty(system_reboot_reason_property)); std::string reason(boot_reason); // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate Loading Loading @@ -423,9 +430,15 @@ std::string BootReasonStrToReason(const std::string& boot_reason) { size_t pos = content.rfind(cmd); if (pos != std::string::npos) { pos += strlen(cmd); std::string subReason(content.substr(pos)); pos = subReason.find('\''); if (pos != std::string::npos) subReason.erase(pos); std::string subReason(content.substr(pos, max_reason_length)); for (pos = 0; pos < subReason.length(); ++pos) { char c = tounderline(subReason[pos]); if (!::isprint(c) || (c == '\'')) { subReason.erase(pos); break; } subReason[pos] = ::tolower(c); } if (subReason != "") { // Will not land "reboot" as that is too blunt. if (isKernelRebootReason(subReason)) { ret = "reboot," + subReason; // User space can't talk kernel reasons. Loading Loading @@ -456,13 +469,20 @@ std::string BootReasonStrToReason(const std::string& boot_reason) { static const int battery_dead_threshold = 2; // percent static const char battery[] = "healthd: battery l="; size_t pos = content.rfind(battery); // last one std::string digits; if (pos != std::string::npos) { int level = atoi(content.substr(pos + strlen(battery)).c_str()); digits = content.substr(pos + strlen(battery)); } char* endptr = NULL; unsigned long long level = strtoull(digits.c_str(), &endptr, 10); if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) { LOG(INFO) << "Battery level at shutdown " << level << "%"; if (level <= battery_dead_threshold) { ret = "shutdown,battery"; } } else { // Most likely digits = ""; // reset digits // Content buffer no longer will have console data. Beware if more // checks added below, that depend on parsing console content. content = ""; Loading Loading @@ -496,8 +516,11 @@ std::string BootReasonStrToReason(const std::string& boot_reason) { pos = content.find(match); // The first one it finds. if (pos != std::string::npos) { pos += strlen(match); int level = atoi(content.substr(pos).c_str()); digits = content.substr(pos + strlen(match)); } endptr = NULL; level = strtoull(digits.c_str(), &endptr, 10); if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) { LOG(INFO) << "Battery level at startup " << level << "%"; if (level <= battery_dead_threshold) { ret = "shutdown,battery"; Loading @@ -513,6 +536,10 @@ std::string BootReasonStrToReason(const std::string& boot_reason) { // Content buffer no longer will have console data. Beware if more // checks added below, that depend on parsing console content. content = GetProperty(last_reboot_reason_property); // Cleanup last_boot_reason regarding acceptable character set std::transform(content.begin(), content.end(), content.begin(), ::tolower); std::transform(content.begin(), content.end(), content.begin(), tounderline); std::transform(content.begin(), content.end(), content.begin(), toprintable); // String is either "reboot,<reason>" or "shutdown,<reason>". // We will set if default reasons, only override with detail if thermal. Loading Loading @@ -580,7 +607,7 @@ std::string CalculateBootCompletePrefix() { if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) { boot_complete_prefix = "factory_reset_" + boot_complete_prefix; boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date); LOG(INFO) << "Canonical boot reason: " << "reboot,factory_reset"; LOG(INFO) << "Canonical boot reason: reboot,factory_reset"; SetProperty(system_reboot_reason_property, "reboot,factory_reset"); if (GetProperty(bootloader_reboot_reason_property) == "") { SetProperty(bootloader_reboot_reason_property, "reboot,factory_reset"); Loading @@ -588,7 +615,7 @@ std::string CalculateBootCompletePrefix() { } else if (build_date != record.second) { boot_complete_prefix = "ota_" + boot_complete_prefix; boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date); LOG(INFO) << "Canonical boot reason: " << "reboot,ota"; LOG(INFO) << "Canonical boot reason: reboot,ota"; SetProperty(system_reboot_reason_property, "reboot,ota"); if (GetProperty(bootloader_reboot_reason_property) == "") { SetProperty(bootloader_reboot_reason_property, "reboot,ota"); Loading @@ -599,8 +626,7 @@ std::string CalculateBootCompletePrefix() { } // Records the value of a given ro.boottime.init property in milliseconds. void RecordInitBootTimeProp( BootEventRecordStore* boot_event_store, const char* property) { void RecordInitBootTimeProp(BootEventRecordStore* boot_event_store, const char* property) { std::string value = GetProperty(property); int32_t time_in_ms; Loading Loading @@ -685,10 +711,8 @@ void RecordBootComplete() { if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) { time_t last_boot_time_utc = record.second; time_t time_since_last_boot = difftime(current_time_utc, last_boot_time_utc); boot_event_store.AddBootEventWithValue("time_since_last_boot", time_since_last_boot); time_t time_since_last_boot = difftime(current_time_utc, last_boot_time_utc); boot_event_store.AddBootEventWithValue("time_since_last_boot", time_since_last_boot); } boot_event_store.AddBootEventWithValue("last_boot_time_utc", current_time_utc); Loading @@ -714,8 +738,7 @@ void RecordBootComplete() { boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt", boot_complete.count()); } else { boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption", uptime.count()); boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption", uptime.count()); } // Record the total time from device startup to boot complete, regardless of Loading @@ -738,8 +761,7 @@ void RecordBootComplete() { void RecordBootReason() { const std::string reason(GetProperty(bootloader_reboot_reason_property)); android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT, android::metricslogger::FIELD_PLATFORM_REASON, reason); android::metricslogger::FIELD_PLATFORM_REASON, reason); // Log the raw bootloader_boot_reason property value. int32_t boot_reason = BootReasonStrToEnum(reason); Loading Loading @@ -769,21 +791,20 @@ void RecordFactoryReset() { if (current_time_utc < 0) { // UMA does not display negative values in buckets, so convert to positive. android::metricslogger::LogHistogram( "factory_reset_current_time_failure", std::abs(current_time_utc)); android::metricslogger::LogHistogram("factory_reset_current_time_failure", std::abs(current_time_utc)); // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram // is losing records somehow. boot_event_store.AddBootEventWithValue( "factory_reset_current_time_failure", std::abs(current_time_utc)); boot_event_store.AddBootEventWithValue("factory_reset_current_time_failure", std::abs(current_time_utc)); return; } else { android::metricslogger::LogHistogram("factory_reset_current_time", current_time_utc); // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram // is losing records somehow. boot_event_store.AddBootEventWithValue( "factory_reset_current_time", current_time_utc); boot_event_store.AddBootEventWithValue("factory_reset_current_time", current_time_utc); } // The factory_reset boot event does not exist after the device is reset, so Loading @@ -803,13 +824,10 @@ void RecordFactoryReset() { // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram // is losing records somehow. boot_event_store.AddBootEventWithValue( "factory_reset_record_value", factory_reset_utc); boot_event_store.AddBootEventWithValue("factory_reset_record_value", factory_reset_utc); time_t time_since_factory_reset = difftime(current_time_utc, factory_reset_utc); boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset); time_t time_since_factory_reset = difftime(current_time_utc, factory_reset_utc); boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset); } } // namespace Loading @@ -826,6 +844,7 @@ int main(int argc, char **argv) { static const char boot_reason_str[] = "record_boot_reason"; static const char factory_reset_str[] = "record_time_since_factory_reset"; static const struct option long_options[] = { // clang-format off { "help", no_argument, NULL, 'h' }, { "log", no_argument, NULL, 'l' }, { "print", no_argument, NULL, 'p' }, Loading @@ -835,6 +854,7 @@ int main(int argc, char **argv) { { boot_reason_str, no_argument, NULL, 0 }, { factory_reset_str, no_argument, NULL, 0 }, { NULL, 0, NULL, 0 } // clang-format on }; std::string boot_event; Loading Loading
bootstat/boot_event_record_store.cpp +7 −11 Original line number Diff line number Diff line Loading @@ -66,8 +66,7 @@ void BootEventRecordStore::AddBootEvent(const std::string& event) { // 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& event, int32_t value) { void BootEventRecordStore::AddBootEventWithValue(const std::string& event, int32_t value) { std::string record_path = GetBootEventPath(event); int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR); if (record_fd == -1) { Loading Loading @@ -96,8 +95,7 @@ void BootEventRecordStore::AddBootEventWithValue( close(record_fd); } bool BootEventRecordStore::GetBootEvent( const std::string& event, BootEventRecord* record) const { bool BootEventRecordStore::GetBootEvent(const std::string& event, BootEventRecord* record) const { CHECK_NE(static_cast<BootEventRecord*>(nullptr), record); CHECK(!event.empty()); Loading @@ -112,8 +110,7 @@ bool BootEventRecordStore::GetBootEvent( return true; } std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore:: GetAllBootEvents() const { std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::GetAllBootEvents() const { std::vector<BootEventRecord> events; std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir); Loading Loading @@ -147,8 +144,7 @@ void BootEventRecordStore::SetStorePath(const std::string& path) { store_path_ = path; } std::string BootEventRecordStore::GetBootEventPath( const std::string& event) const { std::string BootEventRecordStore::GetBootEventPath(const std::string& event) const { DCHECK_EQ('/', store_path_.back()); return store_path_ + event; }
bootstat/boot_event_record_store.h +2 −2 Original line number Diff line number Diff line Loading @@ -17,12 +17,12 @@ #ifndef BOOT_EVENT_RECORD_STORE_H_ #define BOOT_EVENT_RECORD_STORE_H_ #include <android-base/macros.h> #include <gtest/gtest_prod.h> #include <cstdint> #include <string> #include <utility> #include <vector> #include <android-base/macros.h> #include <gtest/gtest_prod.h> // BootEventRecordStore manages the persistence of boot events to the record // store and the retrieval of all boot event records from the store. Loading
bootstat/boot_event_record_store_test.cpp +6 −12 Original line number Diff line number Diff line Loading @@ -101,13 +101,9 @@ time_t GetUptimeSeconds() { class BootEventRecordStoreTest : public ::testing::Test { public: BootEventRecordStoreTest() { store_path_ = std::string(store_dir_.path) + "/"; } BootEventRecordStoreTest() { store_path_ = std::string(store_dir_.path) + "/"; } const std::string& GetStorePathForTesting() const { return store_path_; } const std::string& GetStorePathForTesting() const { return store_path_; } private: void TearDown() { Loading Loading @@ -159,9 +155,7 @@ TEST_F(BootEventRecordStoreTest, AddMultipleBootEvents) { store.AddBootEvent("triassic"); const std::string EXPECTED_NAMES[] = { "cretaceous", "jurassic", "triassic", "cretaceous", "jurassic", "triassic", }; auto events = store.GetAllBootEvents(); Loading
bootstat/boot_reason_test.sh +30 −5 Original line number Diff line number Diff line Loading @@ -370,6 +370,7 @@ test_ota() { fast and fake (touch build_date on device to make it different)" ] test_optional_ota() { echo "INFO: expected duration of ${TEST} test about 45 seconds" >&2 echo "WARNING: ${TEST} requires userdebug build" >&2 adb shell su root touch /data/misc/bootstat/build_date >&2 adb reboot ota wait_for_screen Loading Loading @@ -425,6 +426,7 @@ Decision to rummage through bootstat data files was made as a _real_ factory_reset is too destructive to the device." ] test_factory_reset() { echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2 echo "WARNING: ${TEST} requires userdebug build" >&2 adb shell su root rm /data/misc/bootstat/build_date >&2 adb reboot >&2 wait_for_screen Loading Loading @@ -479,7 +481,8 @@ test_hard() { [ "USAGE: test_battery battery test (trick): - echo healthd: battery l=2 | adb shell su root tee /dev/kmsg ; adb reboot cold - echo healthd: battery l=2<space> | adb shell su root tee /dev/kmsg - adb reboot cold - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report reboot,battery, unless healthd managed to log Loading @@ -491,7 +494,7 @@ battery test (trick): + setprop logd.kernel false + rm /sys/fs/pstore/console-ramoops + rm /sys/fs/pstore/console-ramoops-0 + write /dev/kmsg \"healthd: battery l=2 + write /dev/kmsg \"healthd: battery l=2${SPACE} +\" - adb reboot fs - (wait until screen is up, boot has completed) Loading @@ -500,9 +503,10 @@ battery test (trick): - (replace set logd.kernel true to the above, and retry test)" ] test_battery() { echo "INFO: expected duration of ${TEST} test roughly two minutes" >&2 echo "WARNING: ${TEST} requires userdebug build" >&2 # Send it _many_ times to combat devices with flakey pstore for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do echo healthd: battery l=2 | adb shell su root tee /dev/kmsg >/dev/null echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null done adb reboot cold >&2 adb wait-for-device Loading @@ -516,7 +520,7 @@ test_battery() { if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then # retry for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do echo healthd: battery l=2 | adb shell su root tee /dev/kmsg >/dev/null echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null done adb reboot cold >&2 adb wait-for-device Loading Loading @@ -550,6 +554,7 @@ kernel_panic test: - NB: should report kernel_panic,sysrq" ] test_kernel_panic() { echo "INFO: expected duration of ${TEST} test > 2 minutes" >&2 echo "WARNING: ${TEST} requires userdebug build" >&2 echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null wait_for_screen EXPECT_PROPERTY sys.boot.reason kernel_panic,sysrq Loading Loading @@ -640,6 +645,26 @@ test_adb_reboot() { report_bootstat_logs reboot,adb } [ "USAGE: test_Its_Just_So_Hard_reboot Its Just So Hard reboot test: - adb shell reboot 'Its Just So Hard' - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report reboot,its_just_so_hard - NB: expect log \"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\"" ] test_Its_Just_So_Hard_reboot() { echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2 echo "INFO: ${TEST} cleanup requires userdebug build" >&2 adb shell 'reboot "Its Just So Hard"' wait_for_screen EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard" adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard report_bootstat_logs reboot,its_just_so_hard } [ "USAGE: ${0##*/} [-s SERIAL] [tests] Mainline executive to run the above tests" ] Loading
bootstat/bootstat.cpp +167 −147 Original line number Diff line number Diff line Loading @@ -19,8 +19,8 @@ // uploaded to Android log storage via Tron. #include <getopt.h> #include <unistd.h> #include <sys/klog.h> #include <unistd.h> #include <chrono> #include <cmath> Loading Loading @@ -61,8 +61,7 @@ void LogBootEvents() { // Records the named boot |event| to the record store. If |value| is non-empty // and is a proper string representation of an integer value, the converted // integer value is associated with the boot event. void RecordBootEventFromCommandLine( const std::string& event, const std::string& value_str) { void RecordBootEventFromCommandLine(const std::string& event, const std::string& value_str) { BootEventRecordStore boot_event_store; if (!value_str.empty()) { int32_t value = 0; Loading Loading @@ -221,6 +220,7 @@ int32_t BootReasonStrToEnum(const std::string& boot_reason) { // Canonical list of supported primary reboot reasons. const std::vector<const std::string> knownReasons = { // clang-format off // kernel "watchdog", "kernel_panic", Loading @@ -233,6 +233,7 @@ const std::vector<const std::string> knownReasons = { "warm", "shutdown", // Can not happen from ro.boot.bootreason "reboot", // Default catch-all for anything unknown // clang-format on }; // Returns true if the supplied reason prefix is considered detailed enough. Loading Loading @@ -320,8 +321,12 @@ bool addKernelPanicSubReason(const std::string& console, std::string& ret) { // std::transform Helper callback functions: // Converts a string value representing the reason the system booted to a // string complying with Android system standard reason. char tounderline(char c) { return ::isblank(c) ? '_' : c; } char toprintable(char c) { return ::isprint(c) ? c : '?'; } char tounderline(char c) { return ::isblank(c) ? '_' : c; } char toprintable(char c) { return ::isprint(c) ? c : '?'; } const char system_reboot_reason_property[] = "sys.boot.reason"; const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY; Loading @@ -329,6 +334,8 @@ const char bootloader_reboot_reason_property[] = "ro.boot.bootreason"; // Scrub, Sanitize, Standardize and Enhance the boot reason string supplied. std::string BootReasonStrToReason(const std::string& boot_reason) { static const size_t max_reason_length = 256; std::string ret(GetProperty(system_reboot_reason_property)); std::string reason(boot_reason); // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate Loading Loading @@ -423,9 +430,15 @@ std::string BootReasonStrToReason(const std::string& boot_reason) { size_t pos = content.rfind(cmd); if (pos != std::string::npos) { pos += strlen(cmd); std::string subReason(content.substr(pos)); pos = subReason.find('\''); if (pos != std::string::npos) subReason.erase(pos); std::string subReason(content.substr(pos, max_reason_length)); for (pos = 0; pos < subReason.length(); ++pos) { char c = tounderline(subReason[pos]); if (!::isprint(c) || (c == '\'')) { subReason.erase(pos); break; } subReason[pos] = ::tolower(c); } if (subReason != "") { // Will not land "reboot" as that is too blunt. if (isKernelRebootReason(subReason)) { ret = "reboot," + subReason; // User space can't talk kernel reasons. Loading Loading @@ -456,13 +469,20 @@ std::string BootReasonStrToReason(const std::string& boot_reason) { static const int battery_dead_threshold = 2; // percent static const char battery[] = "healthd: battery l="; size_t pos = content.rfind(battery); // last one std::string digits; if (pos != std::string::npos) { int level = atoi(content.substr(pos + strlen(battery)).c_str()); digits = content.substr(pos + strlen(battery)); } char* endptr = NULL; unsigned long long level = strtoull(digits.c_str(), &endptr, 10); if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) { LOG(INFO) << "Battery level at shutdown " << level << "%"; if (level <= battery_dead_threshold) { ret = "shutdown,battery"; } } else { // Most likely digits = ""; // reset digits // Content buffer no longer will have console data. Beware if more // checks added below, that depend on parsing console content. content = ""; Loading Loading @@ -496,8 +516,11 @@ std::string BootReasonStrToReason(const std::string& boot_reason) { pos = content.find(match); // The first one it finds. if (pos != std::string::npos) { pos += strlen(match); int level = atoi(content.substr(pos).c_str()); digits = content.substr(pos + strlen(match)); } endptr = NULL; level = strtoull(digits.c_str(), &endptr, 10); if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) { LOG(INFO) << "Battery level at startup " << level << "%"; if (level <= battery_dead_threshold) { ret = "shutdown,battery"; Loading @@ -513,6 +536,10 @@ std::string BootReasonStrToReason(const std::string& boot_reason) { // Content buffer no longer will have console data. Beware if more // checks added below, that depend on parsing console content. content = GetProperty(last_reboot_reason_property); // Cleanup last_boot_reason regarding acceptable character set std::transform(content.begin(), content.end(), content.begin(), ::tolower); std::transform(content.begin(), content.end(), content.begin(), tounderline); std::transform(content.begin(), content.end(), content.begin(), toprintable); // String is either "reboot,<reason>" or "shutdown,<reason>". // We will set if default reasons, only override with detail if thermal. Loading Loading @@ -580,7 +607,7 @@ std::string CalculateBootCompletePrefix() { if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) { boot_complete_prefix = "factory_reset_" + boot_complete_prefix; boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date); LOG(INFO) << "Canonical boot reason: " << "reboot,factory_reset"; LOG(INFO) << "Canonical boot reason: reboot,factory_reset"; SetProperty(system_reboot_reason_property, "reboot,factory_reset"); if (GetProperty(bootloader_reboot_reason_property) == "") { SetProperty(bootloader_reboot_reason_property, "reboot,factory_reset"); Loading @@ -588,7 +615,7 @@ std::string CalculateBootCompletePrefix() { } else if (build_date != record.second) { boot_complete_prefix = "ota_" + boot_complete_prefix; boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date); LOG(INFO) << "Canonical boot reason: " << "reboot,ota"; LOG(INFO) << "Canonical boot reason: reboot,ota"; SetProperty(system_reboot_reason_property, "reboot,ota"); if (GetProperty(bootloader_reboot_reason_property) == "") { SetProperty(bootloader_reboot_reason_property, "reboot,ota"); Loading @@ -599,8 +626,7 @@ std::string CalculateBootCompletePrefix() { } // Records the value of a given ro.boottime.init property in milliseconds. void RecordInitBootTimeProp( BootEventRecordStore* boot_event_store, const char* property) { void RecordInitBootTimeProp(BootEventRecordStore* boot_event_store, const char* property) { std::string value = GetProperty(property); int32_t time_in_ms; Loading Loading @@ -685,10 +711,8 @@ void RecordBootComplete() { if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) { time_t last_boot_time_utc = record.second; time_t time_since_last_boot = difftime(current_time_utc, last_boot_time_utc); boot_event_store.AddBootEventWithValue("time_since_last_boot", time_since_last_boot); time_t time_since_last_boot = difftime(current_time_utc, last_boot_time_utc); boot_event_store.AddBootEventWithValue("time_since_last_boot", time_since_last_boot); } boot_event_store.AddBootEventWithValue("last_boot_time_utc", current_time_utc); Loading @@ -714,8 +738,7 @@ void RecordBootComplete() { boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt", boot_complete.count()); } else { boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption", uptime.count()); boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption", uptime.count()); } // Record the total time from device startup to boot complete, regardless of Loading @@ -738,8 +761,7 @@ void RecordBootComplete() { void RecordBootReason() { const std::string reason(GetProperty(bootloader_reboot_reason_property)); android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT, android::metricslogger::FIELD_PLATFORM_REASON, reason); android::metricslogger::FIELD_PLATFORM_REASON, reason); // Log the raw bootloader_boot_reason property value. int32_t boot_reason = BootReasonStrToEnum(reason); Loading Loading @@ -769,21 +791,20 @@ void RecordFactoryReset() { if (current_time_utc < 0) { // UMA does not display negative values in buckets, so convert to positive. android::metricslogger::LogHistogram( "factory_reset_current_time_failure", std::abs(current_time_utc)); android::metricslogger::LogHistogram("factory_reset_current_time_failure", std::abs(current_time_utc)); // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram // is losing records somehow. boot_event_store.AddBootEventWithValue( "factory_reset_current_time_failure", std::abs(current_time_utc)); boot_event_store.AddBootEventWithValue("factory_reset_current_time_failure", std::abs(current_time_utc)); return; } else { android::metricslogger::LogHistogram("factory_reset_current_time", current_time_utc); // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram // is losing records somehow. boot_event_store.AddBootEventWithValue( "factory_reset_current_time", current_time_utc); boot_event_store.AddBootEventWithValue("factory_reset_current_time", current_time_utc); } // The factory_reset boot event does not exist after the device is reset, so Loading @@ -803,13 +824,10 @@ void RecordFactoryReset() { // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram // is losing records somehow. boot_event_store.AddBootEventWithValue( "factory_reset_record_value", factory_reset_utc); boot_event_store.AddBootEventWithValue("factory_reset_record_value", factory_reset_utc); time_t time_since_factory_reset = difftime(current_time_utc, factory_reset_utc); boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset); time_t time_since_factory_reset = difftime(current_time_utc, factory_reset_utc); boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset); } } // namespace Loading @@ -826,6 +844,7 @@ int main(int argc, char **argv) { static const char boot_reason_str[] = "record_boot_reason"; static const char factory_reset_str[] = "record_time_since_factory_reset"; static const struct option long_options[] = { // clang-format off { "help", no_argument, NULL, 'h' }, { "log", no_argument, NULL, 'l' }, { "print", no_argument, NULL, 'p' }, Loading @@ -835,6 +854,7 @@ int main(int argc, char **argv) { { boot_reason_str, no_argument, NULL, 0 }, { factory_reset_str, no_argument, NULL, 0 }, { NULL, 0, NULL, 0 } // clang-format on }; std::string boot_event; Loading