/* a special line with instruction pointer may be present in the format * RIP: 0010:[<ffffffff811c6ed5>] [<ffffffff811c6ed5>] __block_write_full_page+0xa5/0x350 * (for x86_64), or * EIP: [<f8e40765>] wdev_priv.part.8+0x3/0x5 [wl] SS:ESP 0068:f180dbf8 * (for i386, where it is AFTER the trace) */ static struct sr_koops_frame* parse_IP(const char **input) { struct sr_koops_frame *frame; const char *local_input = *input; sr_skip_char_span(&local_input, " \t"); if (sr_skip_string(&local_input, "RIP:")) { if (!sr_skip_char_span(&local_input, " \t")) return NULL; /* The address is there twice, skip the first one */ if (!sr_skip_char_cspan(&local_input, " \t")) return NULL; if (!sr_skip_char_span(&local_input, " \t")) return NULL; frame = sr_koops_frame_parse(&local_input); if (!frame) return NULL; } else if(sr_skip_string(&local_input, "EIP:")) { if (!sr_skip_char_span(&local_input, " \t")) return NULL; frame = sr_koops_frame_new(); if (!sr_koops_parse_address(&local_input, &frame->address)) { sr_koops_frame_free(frame); return NULL; } sr_skip_char_span(&local_input, " \t"); /* Question mark means unreliable */ frame->reliable = sr_skip_char(&local_input, '?') != true; sr_skip_char_span(&local_input, " \t"); if (!sr_koops_parse_function(&local_input, &frame->function_name, &frame->function_offset, &frame->function_length, &frame->module_name)) { sr_koops_frame_free(frame); return NULL; } } else return NULL; sr_skip_char_cspan(&local_input, "\n"); *input = local_input; return frame; }
struct sr_python_stacktrace * sr_python_stacktrace_parse(const char **input, struct sr_location *location) { const char *local_input = *input; /* Parse the header. */ if (sr_skip_char(&local_input, '\n')) location->column += 1; const char *HEADER = "Traceback (most recent call last):\n"; local_input = sr_strstr_location(local_input, HEADER, &location->line, &location->column); if (!local_input) { /* SyntaxError stack trace of an exception thrown in the executed file * conforms to the following template: * invalid syntax ($file, line $number) * * File "$file", line $number * $code * ^ * SyntaxError: invalid syntax * * for exceptions thrown from imported files, the stack trace has the * regular form, except the last frame has no function name and is * followed by the pointer line (^). */ HEADER = "invalid syntax (", local_input = sr_strstr_location(*input, HEADER, &location->line, &location->column); if (!local_input) { location->message = "Traceback header not found."; return NULL; } local_input = sr_strstr_location(local_input, " File \"", &location->line, &location->column); if (!local_input) { location->message = "Frame with invalid line not found."; return NULL; } } else { local_input += strlen(HEADER); location->line += 2; location->column = 0; } struct sr_python_stacktrace *stacktrace = sr_python_stacktrace_new(); /* Read the frames. */ struct sr_python_frame *frame; struct sr_location frame_location; sr_location_init(&frame_location); while ((frame = sr_python_frame_parse(&local_input, &frame_location))) { /* * Python stacktraces are in reverse order than other types - we * reverse it here by prepending each frame to the list instead of * appending it. */ frame->next = stacktrace->frames; stacktrace->frames = frame; sr_location_add(location, frame_location.line, frame_location.column); } if (!stacktrace->frames) { location->message = frame_location.message; sr_python_stacktrace_free(stacktrace); return NULL; } bool invalid_syntax_pointer = true; const char *tmp_input = local_input; while (*tmp_input != '\n' && *tmp_input != '\0') { if (*tmp_input != ' ' && *tmp_input != '^') { invalid_syntax_pointer = false; break; } ++tmp_input; } if (invalid_syntax_pointer) { /* Skip line " ^" pointing to the invalid code */ sr_skip_char_cspan(&local_input, "\n"); ++local_input; ++location->line; location->column = 1; } /* Parse exception name. */ if (!sr_parse_char_cspan(&local_input, ":\n", &stacktrace->exception_name)) { location->message = "Unable to find the ':\\n' characters " "identifying the end of exception name."; sr_python_stacktrace_free(stacktrace); return NULL; } *input = local_input; return stacktrace; }
struct sr_java_frame * sr_java_frame_parse(const char **input, struct sr_location *location) { const char *mark = *input; int lines, columns; /* at SimpleTest.throwNullPointerException(SimpleTest.java:36) [file:/usr/lib/java/foo.class] */ const char *cursor = sr_strstr_location(mark, "at", &lines, &columns); if (!cursor) { location->message = "Frame expected"; return NULL; } /* SimpleTest.throwNullPointerException(SimpleTest.java:36) [file:/usr/lib/java/foo.class] */ cursor = mark = cursor + 2; sr_location_add(location, lines, columns + 2); /* SimpleTest.throwNullPointerException(SimpleTest.java:36) [file:/usr/lib/java/foo.class] */ cursor = sr_skip_whitespace(cursor); sr_location_add(location, 0, cursor - mark); mark = cursor; sr_location_add(location, 0, sr_skip_char_cspan(&cursor, "(\n")); struct sr_java_frame *frame = sr_java_frame_new(); if (cursor != mark) frame->name = sr_strndup(mark, cursor - mark); /* (SimpleTest.java:36) [file:/usr/lib/java/foo.class] */ if (*cursor == '(') { ++cursor; sr_location_add(location, 0, 1); mark = cursor; sr_location_add(location, 0, sr_skip_char_cspan(&cursor, ":)\n")); if (mark != cursor) { if (sr_java_frame_parse_is_native_method(mark)) frame->is_native = true; else if (!sr_java_frame_parse_is_unknown_source(mark)) { /* DO NOT set file_name if input says that source isn't known */ frame->file_name = sr_strndup(mark, cursor - mark); frame->file_name = anonymize_path(frame->file_name); } } if (*cursor == ':') { ++cursor; sr_location_add(location, 0, 1); mark = cursor; sr_parse_uint32(&cursor, &(frame->file_line)); sr_location_add(location, 0, cursor - mark); } } /* [file:/usr/lib/java/foo.class] */ mark = sr_java_frame_parse_frame_url(frame, cursor, location); cursor = strchrnul(mark, '\n'); if (*cursor == '\n') { *input = cursor + 1; sr_location_add(location, 2, 0); } else { *input = cursor; /* don't take \0 Byte into account */ sr_location_add(location, 0, (cursor - mark) - 1); } return frame; }
/* [jar:http://locahost/usr/lib/java/foo.jar!/Foo.class] */ static const char *sr_java_frame_parse_frame_url(struct sr_java_frame *frame, const char *mark, struct sr_location *location) { const char *cursor = mark; sr_location_add(location, 0, sr_skip_char_cspan(&cursor, "[\n")); if (*cursor != '[') return cursor; ++cursor; sr_location_add(location, 0, 1); mark = cursor; sr_location_add(location, 0, sr_skip_char_cspan(&cursor, ":\n")); if (*cursor == ':') { const char *path_stop = "]\n"; if (strncmp("jar:", mark, strlen("jar:")) == 0) { /* From jar:file:/usr/lib/java/foo.jar!/Foo.class] */ /* ^ */ ++cursor; sr_location_add(location, 0, 1); mark = cursor; sr_location_add(location, 0, sr_skip_char_cspan(&cursor, ":\n")); path_stop = "!\n"; /* To file:/usr/lib/java/foo.jar!/Foo.class] */ /* ^ */ if (*cursor != ':') return cursor; } if (strncmp("file:", mark, strlen("file:")) != 0) { /* move cursor back in case of http: ... */ sr_location_add(location, 0, -(cursor - mark)); cursor = mark; } else { ++cursor; sr_location_add(location, 0, 1); mark = cursor; } sr_location_add(location, 0, sr_skip_char_cspan(&cursor, path_stop)); if (mark != cursor) { frame->class_path = sr_strndup(mark, cursor - mark); frame->class_path = anonymize_path(frame->class_path); } } if (*cursor != ']' && *cursor != '\n') sr_location_add(location, 0, sr_skip_char_cspan(&cursor, "]\n")); return cursor; }
struct sr_java_frame * sr_java_frame_parse_exception(const char **input, struct sr_location *location) { /* java.lang.NullPointerException: foo */ const char *cursor = sr_skip_whitespace(*input); sr_location_add(location, 0, cursor - *input); const char *mark = cursor; sr_location_add(location, 0, sr_skip_char_cspan(&cursor, ": \t\n")); if (mark == cursor) { location->message = "Expected exception name"; return NULL; } struct sr_java_frame *exception = sr_java_frame_new_exception(); exception->name = sr_strndup(mark, cursor - mark); /* : foo */ if (*cursor == ':') { ++cursor; sr_location_add(location, 0, 1); mark = cursor; /* foo */ cursor = sr_skip_whitespace(mark); sr_location_add(location, 0, cursor - mark); mark = cursor; sr_location_add(location, 0, sr_skip_char_cspan(&cursor, "\n")); if (mark != cursor) exception->message = sr_strndup(mark, cursor - mark); } else { /* just to be sure, that we skip white space behind exception name */ sr_location_add(location, 0, sr_skip_char_cspan(&cursor, "\n")); } if (*cursor == '\n') { ++cursor; /* this adds one line */ sr_location_add(location, 2, 0); } /* else *cursor == '\0' */ mark = cursor; struct sr_java_frame *frame = NULL; /* iterate line by line best effort - continue on error */ while (*cursor != '\0') { cursor = sr_skip_whitespace(mark); sr_location_add(location, 0, cursor - mark); /* Each inner exception has '...' at its end */ if (strncmp("... ", cursor, strlen("... ")) == 0) goto current_exception_done; /* Suppressed exceptions follow after the end of current exception */ if (strncmp("Suppressed: ", cursor, strlen("Suppressed: ")) == 0) goto current_exception_done; /* The top most exception does not have '...' at its end */ if (strncmp("Caused by: ", cursor, strlen("Caused by: ")) == 0) goto parse_inner_exception; struct sr_java_frame *parsed = sr_java_frame_parse(&cursor, location); if (parsed == NULL) { sr_java_frame_free(exception); return NULL; } mark = cursor; if (exception->next == NULL) exception->next = parsed; else { assert(frame); frame->next = parsed; } frame = parsed; } /* We are done with the top most exception without inner exceptions */ /* because of no 'Caused by:' and no '...' */ goto exception_parsing_successful; current_exception_done: sr_skip_to_next_line_location(&cursor, &location->line, &location->column); mark = cursor; cursor = sr_skip_whitespace(mark); sr_location_add(location, 0, cursor - mark); if (strncmp("Suppressed: ", cursor, strlen("Suppressed: ")) == 0) { /* Skip all lines related to the suppressed exception. We can do * this by skipping all lines that begin with a whitespace - the * main exception chain always begins without preceding whitespace. */ sr_skip_to_next_line_location(&cursor, &location->line, &location->column); while (cursor && isspace(*cursor)) sr_skip_to_next_line_location(&cursor, &location->line, &location->column); } if (strncmp("Caused by: ", cursor, strlen("Caused by: ")) == 0) { parse_inner_exception: cursor += strlen("Caused by: "); sr_location_add(location, 0, strlen("Caused by: ")); struct sr_java_frame *inner = sr_java_frame_parse_exception(&cursor, location); if (inner == NULL) { sr_java_frame_free(exception); return NULL; } struct sr_java_frame *last_inner = sr_java_frame_get_last(inner); last_inner->next = exception; exception = inner; } exception_parsing_successful: *input = cursor; return exception; }
struct sr_koops_stacktrace * sr_koops_stacktrace_parse(const char **input, struct sr_location *location) { const char *local_input = *input; struct sr_koops_stacktrace *stacktrace = sr_koops_stacktrace_new(); struct sr_koops_frame *frame; bool parsed_ip = false; char *alt_stack = NULL; /* Include the raw kerneloops text */ stacktrace->raw_oops = sr_strdup(*input); /* Looks for the "Tainted: " line in the whole input */ parse_taint_flags(local_input, stacktrace); while (*local_input) { sr_skip_char_span(&local_input, " \t"); /* Skip timestamp if it's present. */ sr_koops_skip_timestamp(&local_input); sr_skip_char_span(&local_input, " \t"); /* Not sure what it means on s390x but i think it's at the end of the * stack */ if (sr_skip_string(&local_input, "Last Breaking-Event-Address:\n")) { while (*local_input) local_input++; break; } if (!stacktrace->modules && (stacktrace->modules = sr_koops_stacktrace_parse_modules(&local_input))) goto next_line; if (!parsed_ip && (frame = parse_IP(&local_input))) { /* this is the very first frame (even though for i386 it's at the * end), we need to prepend it */ stacktrace->frames = sr_koops_frame_prepend(stacktrace->frames, frame); parsed_ip = true; goto next_line; } /* <IRQ>, <NMI>, ... */ if (parse_alt_stack_end(&local_input)) { free(alt_stack); alt_stack = NULL; } /* <EOI>, <<EOE>> */ char *new_alt_stack = parse_alt_stack_start(&local_input); if (new_alt_stack) { alt_stack = new_alt_stack; } if((frame = sr_koops_frame_parse(&local_input))) { if (alt_stack) frame->special_stack = sr_strdup(alt_stack); stacktrace->frames = sr_koops_frame_append(stacktrace->frames, frame); goto next_line; } sr_skip_char_cspan(&local_input, "\n"); next_line: sr_skip_char(&local_input, '\n'); } *input = local_input; return stacktrace; }