From 855d205817f642652d0944a3002ec977d5a47e4f Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Tue, 20 Mar 2018 11:44:38 -0700 Subject: [PATCH 1/2] bootstat: test error out only on first bad bootloader reason Hopefully the quick property test is first, setting the stage for ignoring future failures. This solves a problem with multiple test failures directly attributed to a CTS compliance issue with the bootloader's boot reason. One test fails, the remainder get a pass on this one issue. Test: boot_reason_test.sh Bug: 63736262 Change-Id: Id56946b6f2f3a33d65bd1830543838f153290759 --- bootstat/boot_reason_test.sh | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh index 86e87896c..1043df16f 100755 --- a/bootstat/boot_reason_test.sh +++ b/bootstat/boot_reason_test.sh @@ -239,6 +239,8 @@ EXPECT_EQ() { return 0 } +BAD_BOOTLOADER_REASON= + [ "USAGE: EXPECT_PROPERTY [--allow_failure] Returns true (0) if current return (regex) value is true and the result matches @@ -249,9 +251,20 @@ EXPECT_PROPERTY() { value="${2}" shift 2 val=`adb shell getprop ${property} 2>&1` - EXPECT_EQ "${value}" "${val}" for Android property ${property} || - [ -n "${1}" ] || - save_ret=${?} + EXPECT_EQ "${value}" "${val}" for Android property ${property} + local_ret=${?} + if [ 0 != ${local_ret} -a "ro.boot.bootreason" = "${property}" ]; then + if [ -z "${BAD_BOOTLOADER_REASON}" ]; then + BAD_BOOTLOADER_REASON=${val} + elif [ X"${BAD_BOOTLOADER_REASON}" = X"${val}" ]; then + local_ret=0 + fi + fi + if [ 0 != ${local_ret} ]; then + if [ -z "${1}" ] ; then + save_ret=${local_ret} + fi + fi return ${save_ret} } From 39cc3e70734779503db0ddaf2a6fe604cac08f8d Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Mon, 19 Mar 2018 15:16:29 -0700 Subject: [PATCH 2/2] bootstat: allow kernel_panic,sysrq to propagate subreason Allow for a daemon to write to last kmsg to propagate a detailed subreason to kernel_panic,sysrq actions. A minor refactor moves common code into a helper function getSubreason for retrieval and bit error correction operations. A sysrq crash generally produces a kernel-provided message: SysRq : Trigger a crash which is used to generate a canonical boot reason kernel_panic,sysrq. A user daemon could write to /dev/kmsg just prior to the sysrq with SysRq : Trigger a crash : '' to change the canonical boot reason to kernel_panic,sysrq,. Administration added pending kBootReasonMap entries present in TRON. Test: manual echo into /dev/kmsg and /proc/sysrq-trigger and check Test: boot_reason_test.sh Bug: 33808187 Bug: 63736262 Change-Id: Ibf5432737e5a3449ebe40a8c6cf2d3e912ed6bbc --- bootstat/bootstat.cpp | 91 ++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp index cfea6ee79..29988c837 100644 --- a/bootstat/bootstat.cpp +++ b/bootstat/bootstat.cpp @@ -291,6 +291,10 @@ const std::map kBootReasonMap = { {"software_master", 147}, {"cold,charger", 148}, {"cold,rtc", 149}, + {"cold,rtc,2sec", 150}, + {"reboot,tool", 151}, + {"reboot,wdt", 152}, + {"reboot,unknown", 153}, }; // Converts a string value representing the reason the system booted to an @@ -467,6 +471,8 @@ class pstoreConsole { } return std::string::npos; } + + operator const std::string&() const { return console; } }; // If bit error match to needle, correct it. @@ -503,12 +509,60 @@ bool correctForBitErrorOrUnderline(std::string& reason, const std::string& needl return corrected; } +// Converts a string value representing the reason the system booted to a +// string complying with Android system standard reason. +void transformReason(std::string& reason) { + std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower); + std::transform(reason.begin(), reason.end(), reason.begin(), + [](char c) { return ::isblank(c) ? '_' : c; }); + std::transform(reason.begin(), reason.end(), reason.begin(), + [](char c) { return ::isprint(c) ? c : '?'; }); +} + +// Pull out and correct quoted (') subreason, pos just beyond first quote. +// Check subreasons for reboot, and kernel_panic,sysrq, +std::string getSubreason(const std::string& content, size_t pos) { + static constexpr size_t max_reason_length = 256; + + std::string subReason(content.substr(pos, max_reason_length)); + // Correct against any known strings that Bit Error Match + for (const auto& s : knownReasons) { + correctForBitErrorOrUnderline(subReason, s); + } + for (const auto& m : kBootReasonMap) { + if (m.first.length() <= strlen("cold")) continue; // too short? + if (correctForBitErrorOrUnderline(subReason, m.first + "'")) continue; + if (m.first.length() <= strlen("reboot,cold")) continue; // short? + if (android::base::StartsWith(m.first, "reboot,")) { + correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("reboot,")) + "'"); + } else if (android::base::StartsWith(m.first, "kernel_panic,sysrq,")) { + correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("kernel_panic,sysrq,")) + "'"); + } + } + for (pos = 0; pos < subReason.length(); ++pos) { + char c = subReason[pos]; + // #, &, %, / are common single bit error for ' that we can block + if (!::isprint(c) || (c == '\'') || (c == '#') || (c == '&') || (c == '%') || (c == '/')) { + subReason.erase(pos); + break; + } + } + transformReason(subReason); + return subReason; +} + bool addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) { // Check for kernel panic types to refine information if ((console.rfind("SysRq : Trigger a crash") != std::string::npos) || (console.rfind("PC is at sysrq_handle_crash+") != std::string::npos)) { - // Can not happen, except on userdebug, during testing/debugging. ret = "kernel_panic,sysrq"; + // Invented for Android to allow daemons that specifically trigger sysrq + // to communicate more accurate boot subreasons via last console messages. + static constexpr char sysrqSubreason[] = "SysRq : Trigger a crash : '"; + auto pos = console.rfind(sysrqSubreason); + if (pos != std::string::npos) { + ret += "," + getSubreason(console, pos + strlen(sysrqSubreason)); + } return true; } if (console.rfind("Unable to handle kernel NULL pointer dereference at virtual address") != @@ -527,24 +581,12 @@ bool addKernelPanicSubReason(const std::string& content, std::string& ret) { return addKernelPanicSubReason(pstoreConsole(content), ret); } -// Converts a string value representing the reason the system booted to a -// string complying with Android system standard reason. -void transformReason(std::string& reason) { - std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower); - std::transform(reason.begin(), reason.end(), reason.begin(), - [](char c) { return ::isblank(c) ? '_' : c; }); - std::transform(reason.begin(), reason.end(), reason.begin(), - [](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; 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 @@ -647,28 +689,7 @@ std::string BootReasonStrToReason(const std::string& boot_reason) { static const char cmd[] = "reboot: Restarting system with command '"; size_t pos = console.rfind(cmd); if (pos != std::string::npos) { - pos += strlen(cmd); - std::string subReason(content.substr(pos, max_reason_length)); - // Correct against any known strings that Bit Error Match - for (const auto& s : knownReasons) { - correctForBitErrorOrUnderline(subReason, s); - } - for (const auto& m : kBootReasonMap) { - if (m.first.length() <= strlen("cold")) continue; // too short? - if (correctForBitErrorOrUnderline(subReason, m.first + "'")) continue; - if (m.first.length() <= strlen("reboot,cold")) continue; // short? - if (!android::base::StartsWith(m.first, "reboot,")) continue; - correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("reboot,")) + "'"); - } - for (pos = 0; pos < subReason.length(); ++pos) { - char c = subReason[pos]; - // #, &, %, / are common single bit error for ' that we can block - if (!::isprint(c) || (c == '\'') || (c == '#') || (c == '&') || (c == '%') || (c == '/')) { - subReason.erase(pos); - break; - } - } - transformReason(subReason); + std::string subReason(getSubreason(content, pos + strlen(cmd))); if (subReason != "") { // Will not land "reboot" as that is too blunt. if (isKernelRebootReason(subReason)) { ret = "reboot," + subReason; // User space can't talk kernel reasons.