From e699fd34e2eb29777323a2b8f690b17fc26fcf8f Mon Sep 17 00:00:00 2001 From: Patrick McCarty Date: Tue, 21 Mar 2017 16:24:04 -0700 Subject: [PATCH] oops parser: support oops stack traces for Linux 4.10+ Linux 4.10 introduced some format changes for oopses; one of the changes was removing memory addresses from each stack frame. This commit adds support for the new format and retains compatibility for the previous format (4.9 and earlier). Signed-off-by: Patrick McCarty --- src/probes/oops_parser.c | 85 +++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/src/probes/oops_parser.c b/src/probes/oops_parser.c index 7e02828..d70fc03 100644 --- a/src/probes/oops_parser.c +++ b/src/probes/oops_parser.c @@ -388,13 +388,16 @@ void parse_single_line(char *line, size_t size) } else if (strstr(start, "[ end trace")) { end_found = true; } else if (!in_stack_dump) { - if (starts_with(start, line_end, " [<") || - starts_with(start, line_end, " <") || //case : [] do_one_initcall+0xb8/0x1e0 * [] ? __vunmap+0x9a/0x100 + * + * OR + * + * [] dump_stack+0x19/0x1b + * + * OR (in 4.10+) + * + * do_one_initcall+0xb8/0x1e0 + * ? __vunmap+0x9a/0x100 + * */ struct stack_frame *frame = NULL; @@ -448,18 +462,22 @@ void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, ch if (!start) { return; } - start = start + 2; + + // Skip leading spaces + start = skip_spaces(start); //case: [] dump_stack+0x19/0x1b - if (!strncmp(start, "IRQ>", 4) || !strncmp(start, "NMI>", 4) || - !strncmp(start, "EOI>", 4) || !strncmp(start, ">", 6)) { - while (*start && *start != '[') { + if (!strncmp(start, "", 5) || !strncmp(start, "", 5) || + !strncmp(start, "", 5) || !strncmp(start, "<>", 7)) { + while (*start && !isspace(*start)) { start++; } - if (*start == '\0') { + if (start && *start == '\0') { return; } - start++; + + // Skip intervening space + start = skip_spaces(start); } frame = malloc(sizeof(struct stack_frame)); @@ -474,21 +492,31 @@ void stack_frame_append(struct stack_frame **head, struct stack_frame **tail, ch *tail = frame; } - if (*start == '<') { + // Skip memory address parsing if the address is absent (as in Linux 4.10+). + if (!strncmp(start, "[", 1)) { start++; - frame->addr = (uint64_t)strtoll(start, (char **)&start, 16); + + if (*start == '<') { + start++; + frame->addr = (uint64_t)strtoll(start, (char **)&start, 16); + } else { + frame->addr = 0; + } + + // Skip up to the space after the closing bracket + while (*start && !isspace(*start)) { + start++; + } + if (start && *start == '\0') { + return; + } + + // Skip intervening space + start = skip_spaces(start); } else { frame->addr = 0; } - /* Skip up to the space after the closing bracket */ - while (*start && !isspace(*start)) { - start++; - } - if (*start) { - start++; - } - offset_ptr = strchr(start, '+'); end = offset_ptr ? offset_ptr : (start + strlen(start)); frame->function = strndup(start, (size_t)(end - start)); @@ -742,17 +770,28 @@ GString *parse_backtrace(struct oops_log_msg *msg) GString *backtrace = NULL; int frame_counter = 1; char *modules = NULL, *kernel_version = NULL, *tainted = NULL; + // Since lines are processed from last to first, the stack trace lines + // will appear first. + bool in_trace = true; for (int i = msg->length - 1; i > 0; i--) { /* Check if this line is part of a stack trace */ line = msg->lines[i]; - if (starts_with(line, line + strlen(line), " [") || - starts_with(line, line + strlen(line), " <")) { + if (in_trace && starts_with(line, line + strlen(line), " ")) { stack_frame_append(&head, &tail, line); continue; } + // We've reached the end of the stack trace + if (starts_with(line, line + strlen(line), "Call Trace:")) { + in_trace = false; + } + + // Stack trace lines must be consecutive, so if this point is + // reached, we're outside the trace boundary. + in_trace = false; + if (str_starts_with_casei(line, "Modules linked in: ")) { modules = line + strlen("Modules linked in: "); continue;