Merge "bootstat: refine "Kernel panic - not syncing""

This commit is contained in:
Treehugger Robot 2018-03-22 23:17:40 +00:00 committed by Gerrit Code Review
commit 3b353ed41c
2 changed files with 212 additions and 16 deletions

View file

@ -808,6 +808,63 @@ test_kernel_panic() {
exitPstore
}
[ "USAGE: test_kernel_panic_subreason
kernel_panic_subreason test:
- echo SysRq : Trigger a crash : 'test' | adb shell su root tee /dev/kmsg
- echo c | adb shell su root tee /proc/sysrq-trigger
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report kernel_panic,sysrq,test" ]
test_kernel_panic_subreason() {
checkDebugBuild || return
duration_test ">90"
panic_msg="kernel_panic,sysrq,test"
enterPstore
if [ ${?} != 0 ]; then
echo " or functional bootloader" >&2
panic_msg="\(kernel_panic,sysrq,test\|kernel_panic\)"
pstore_ok=true
fi
echo "SysRq : Trigger a crash : 'test'" | adb shell su root tee /dev/kmsg
echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
report_bootstat_logs kernel_panic,sysrq,test \
"-bootstat: Unknown boot reason: kernel_panic,sysrq,test"
exitPstore
}
[ "USAGE: test_kernel_panic_hung
kernel_panic_hung test:
- echo Kernel panic - not synching: hung_task: blocked tasks |
adb shell su root tee /dev/kmsg
- adb reboot warm
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report kernel_panic,hung" ]
test_kernel_panic_hung() {
checkDebugBuild || return
duration_test
panic_msg="kernel_panic,hung"
enterPstore
if [ ${?} != 0 ]; then
echo " or functional bootloader" >&2
panic_msg="\(kernel_panic,hung\|reboot,hung\)"
pstore_ok=true
fi
echo "Kernel panic - not syncing: hung_task: blocked tasks" |
adb shell su root tee /dev/kmsg
adb reboot warm
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
report_bootstat_logs kernel_panic,hung
exitPstore
}
[ "USAGE: test_warm
warm test
@ -1067,12 +1124,12 @@ if [ -z "$*" ]; then
if [ -z "${2}" ]; then
# Hard coded should shell fail to find them above (search/permission issues)
eval set properties ota cold factory_reset hard battery unknown \
kernel_panic warm thermal_shutdown userrequested_shutdown \
shell_reboot adb_reboot Its_Just_So_Hard_reboot \
bootloader_normal bootloader_watchdog bootloader_kernel_panic \
bootloader_oem_powerkey bootloader_wdog_reset \
bootloader_wdog_reset bootloader_wdog_reset bootloader_hard \
bootloader_recovery
kernel_panic kernel_panic_subreason kernel_panic_hung warm \
thermal_shutdown userrequested_shutdown shell_reboot adb_reboot \
Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
bootloader_kernel_panic bootloader_oem_powerkey \
bootloader_wdog_reset bootloader_wdog_reset bootloader_wdog_reset \
bootloader_hard bootloader_recovery
fi
if [ X"nothing" = X"${1}" ]; then
shift 1

View file

@ -295,6 +295,13 @@ const std::map<std::string, int32_t> kBootReasonMap = {
{"reboot,tool", 151},
{"reboot,wdt", 152},
{"reboot,unknown", 153},
{"kernel_panic,audit", 154},
{"kernel_panic,atomic", 155},
{"kernel_panic,hung", 156},
{"kernel_panic,hung,rcu", 157},
{"kernel_panic,init", 158},
{"kernel_panic,oom", 159},
{"kernel_panic,stack", 160},
};
// Converts a string value representing the reason the system booted to an
@ -519,9 +526,100 @@ void transformReason(std::string& reason) {
[](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) {
// Check subreasons for reboot,<subreason> kernel_panic,sysrq,<subreason> or
// kernel_panic,<subreason>.
//
// If quoted flag is set, pull out and correct single quoted ('), newline (\n)
// or unprintable character terminated subreason, pos is supplied just beyond
// first quote. if quoted false, pull out and correct newline (\n) or
// unprintable character terminated subreason.
//
// Heuristics to find termination is painted into a corner:
// single bit error for quote ' that we can block. It is acceptable for
// the others 7, g in reason. 2/9 chance will miss the terminating quote,
// but there is always the terminating newline that usually immediately
// follows to fortify our chances.
bool likely_single_quote(char c) {
switch (static_cast<uint8_t>(c)) {
case '\'': // '\''
case '\'' ^ 0x01: // '&'
case '\'' ^ 0x02: // '%'
case '\'' ^ 0x04: // '#'
case '\'' ^ 0x08: // '/'
return true;
case '\'' ^ 0x10: // '7'
break;
case '\'' ^ 0x20: // '\a' (unprintable)
return true;
case '\'' ^ 0x40: // 'g'
break;
case '\'' ^ 0x80: // 0xA7 (unprintable)
return true;
}
return false;
}
// ::isprint(c) and likely_space() will prevent us from being called for
// fundamentally printable entries, except for '\r' and '\b'.
//
// Except for * and J, single bit errors for \n, all others are non-
// printable so easy catch. It is _acceptable_ for *, J or j to exist in
// the reason string, so 2/9 chance we will miss the terminating newline.
//
// NB: J might not be acceptable, except if at the beginning or preceded
// with a space, '(' or any of the quotes and their BER aliases.
// NB: * might not be acceptable, except if at the beginning or preceded
// with a space, another *, or any of the quotes or their BER aliases.
//
// To reduce the chances to closer to 1/9 is too complicated for the gain.
bool likely_newline(char c) {
switch (static_cast<uint8_t>(c)) {
case '\n': // '\n' (unprintable)
case '\n' ^ 0x01: // '\r' (unprintable)
case '\n' ^ 0x02: // '\b' (unprintable)
case '\n' ^ 0x04: // 0x0E (unprintable)
case '\n' ^ 0x08: // 0x02 (unprintable)
case '\n' ^ 0x10: // 0x1A (unprintable)
return true;
case '\n' ^ 0x20: // '*'
case '\n' ^ 0x40: // 'J'
break;
case '\n' ^ 0x80: // 0x8A (unprintable)
return true;
}
return false;
}
// ::isprint(c) will prevent us from being called for all the printable
// matches below. If we let unprintables through because of this, they
// get converted to underscore (_) by the validation phase.
bool likely_space(char c) {
switch (static_cast<uint8_t>(c)) {
case ' ': // ' '
case ' ' ^ 0x01: // '!'
case ' ' ^ 0x02: // '"'
case ' ' ^ 0x04: // '$'
case ' ' ^ 0x08: // '('
case ' ' ^ 0x10: // '0'
case ' ' ^ 0x20: // '\0' (unprintable)
case ' ' ^ 0x40: // 'P'
case ' ' ^ 0x80: // 0xA0 (unprintable)
case '\t': // '\t'
case '\t' ^ 0x01: // '\b' (unprintable) (likely_newline counters)
case '\t' ^ 0x02: // '\v' (unprintable)
case '\t' ^ 0x04: // '\r' (unprintable) (likely_newline counters)
case '\t' ^ 0x08: // 0x01 (unprintable)
case '\t' ^ 0x10: // 0x19 (unprintable)
case '\t' ^ 0x20: // ')'
case '\t' ^ 0x40: // '1'
case '\t' ^ 0x80: // 0x89 (unprintable)
return true;
}
return false;
}
std::string getSubreason(const std::string& content, size_t pos, bool quoted) {
static constexpr size_t max_reason_length = 256;
std::string subReason(content.substr(pos, max_reason_length));
@ -529,20 +627,24 @@ std::string getSubreason(const std::string& content, size_t pos) {
for (const auto& s : knownReasons) {
correctForBitErrorOrUnderline(subReason, s);
}
std::string terminator(quoted ? "'" : "");
for (const auto& m : kBootReasonMap) {
if (m.first.length() <= strlen("cold")) continue; // too short?
if (correctForBitErrorOrUnderline(subReason, m.first + "'")) continue;
if (correctForBitErrorOrUnderline(subReason, m.first + terminator)) continue;
if (m.first.length() <= strlen("reboot,cold")) continue; // short?
if (android::base::StartsWith(m.first, "reboot,")) {
correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("reboot,")) + "'");
correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("reboot,")) + terminator);
} else if (android::base::StartsWith(m.first, "kernel_panic,sysrq,")) {
correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("kernel_panic,sysrq,")) + "'");
correctForBitErrorOrUnderline(subReason,
m.first.substr(strlen("kernel_panic,sysrq,")) + terminator);
} else if (android::base::StartsWith(m.first, "kernel_panic,")) {
correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("kernel_panic,")) + terminator);
}
}
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 == '/')) {
if (!(::isprint(c) || likely_space(c)) || likely_newline(c) ||
(quoted && likely_single_quote(c))) {
subReason.erase(pos);
break;
}
@ -561,7 +663,7 @@ bool addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) {
static constexpr char sysrqSubreason[] = "SysRq : Trigger a crash : '";
auto pos = console.rfind(sysrqSubreason);
if (pos != std::string::npos) {
ret += "," + getSubreason(console, pos + strlen(sysrqSubreason));
ret += "," + getSubreason(console, pos + strlen(sysrqSubreason), /* quoted */ true);
}
return true;
}
@ -574,6 +676,43 @@ bool addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) {
ret = "kernel_panic,bug";
return true;
}
std::string panic("Kernel panic - not syncing: ");
auto pos = console.rfind(panic);
if (pos != std::string::npos) {
static const std::vector<std::pair<const std::string, const std::string>> panicReasons = {
{"Out of memory", "oom"},
{"out of memory", "oom"},
{"Oh boy, that early out of memory", "oom"}, // omg
{"BUG!", "bug"},
{"hung_task: blocked tasks", "hung"},
{"audit: ", "audit"},
{"scheduling while atomic", "atomic"},
{"Attempted to kill init!", "init"},
{"Requested init", "init"},
{"No working init", "init"},
{"Could not decompress init", "init"},
{"RCU Stall", "hung,rcu"},
{"stack-protector", "stack"},
{"kernel stack overflow", "stack"},
{"Corrupt kernel stack", "stack"},
{"low stack detected", "stack"},
{"corrupted stack end", "stack"},
};
ret = "kernel_panic";
for (auto& s : panicReasons) {
if (console.find(panic + s.first, pos) != std::string::npos) {
ret += "," + s.second;
return true;
}
}
auto reason = getSubreason(console, pos + panic.length(), /* newline */ false);
if (reason.length() > 3) {
ret += "," + reason;
}
return true;
}
return false;
}
@ -689,7 +828,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) {
std::string subReason(getSubreason(content, pos + strlen(cmd)));
std::string subReason(getSubreason(content, pos + strlen(cmd), /* quoted */ true));
if (subReason != "") { // Will not land "reboot" as that is too blunt.
if (isKernelRebootReason(subReason)) {
ret = "reboot," + subReason; // User space can't talk kernel reasons.