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 : '<subreason>'
to change the canonical boot reason to kernel_panic,sysrq,<subreason>.
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
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index cfea6ee..29988c8 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -291,6 +291,10 @@
{"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 @@
}
return std::string::npos;
}
+
+ operator const std::string&() const { return console; }
};
// If bit error match to needle, correct it.
@@ -503,12 +509,60 @@
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,<subreason> and kernel_panic,sysrq,<subreason>
+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 @@
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 @@
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.