diff options
author | Karel Klic <kklic@redhat.com> | 2009-11-23 16:52:50 +0100 |
---|---|---|
committer | Karel Klic <kklic@redhat.com> | 2009-11-23 16:52:50 +0100 |
commit | 4d725ba7f708087099496145a953064aded60365 (patch) | |
tree | 29551f955fd3ef19b17ef9872ca10d8e24e78013 /src/Backtrace | |
parent | b8da7620a417ef835869da692db140b75e8b7a93 (diff) | |
download | abrt-4d725ba7f708087099496145a953064aded60365.tar.gz abrt-4d725ba7f708087099496145a953064aded60365.tar.xz abrt-4d725ba7f708087099496145a953064aded60365.zip |
Backtrace detector improvements.
Diffstat (limited to 'src/Backtrace')
-rw-r--r-- | src/Backtrace/backtrace.c | 117 | ||||
-rw-r--r-- | src/Backtrace/backtrace.h | 20 | ||||
-rw-r--r-- | src/Backtrace/parser.y | 87 |
3 files changed, 188 insertions, 36 deletions
diff --git a/src/Backtrace/backtrace.c b/src/Backtrace/backtrace.c index 6090298f..62f99a36 100644 --- a/src/Backtrace/backtrace.c +++ b/src/Backtrace/backtrace.c @@ -28,11 +28,8 @@ struct frame *frame_new() } f->function = NULL; - f->args = NULL; f->number = 0; - f->binfile = NULL; f->sourcefile = NULL; - f->crash = false; f->next = NULL; return f; } @@ -41,10 +38,6 @@ void frame_free(struct frame *f) { if (f->function) free(f->function); - if (f->args) - free(f->args); - if (f->binfile) - free(f->binfile); if (f->sourcefile) free(f->sourcefile); free(f); @@ -145,6 +138,7 @@ struct backtrace *backtrace_new() } bt->threads = NULL; + bt->crash = NULL; } void backtrace_free(struct backtrace *bt) @@ -156,6 +150,9 @@ void backtrace_free(struct backtrace *bt) thread_free(rm); } + if (bt->crash) + frame_free(bt->crash); + free(bt); } @@ -174,6 +171,12 @@ static int backtrace_get_thread_count(struct backtrace *bt) void backtrace_print_tree(struct backtrace *bt) { printf("Thread count: %d\n", backtrace_get_thread_count(bt)); + if (bt->crash) + { + printf("Crash frame: "); + frame_print_tree(bt->crash); + } + struct thread *thread = bt->threads; while (thread) { @@ -181,3 +184,103 @@ void backtrace_print_tree(struct backtrace *bt) thread = thread->next; } } + +/* + * Checks whether the thread it contains some known "abort" function. + * Nonrecursive. + */ +static bool thread_contain_abort_frame(struct thread *thread) +{ + int depth = 15; /* check only 15 top frames on the stack */ + struct frame *frame = thread->frames; + while (frame && depth) + { + if (frame->function + && 0 == strcmp(frame->function, "raise") + && frame->sourcefile + && 0 == strcmp(frame->sourcefile, "../nptl/sysdeps/unix/sysv/linux/pt-raise.c")) + { + return true; + } + + --depth; + frame = frame->next; + } + + return false; +} + +/* + * Loop through all threads and if a single one contains the crash frame on the top, + * return it. Otherwise, return NULL. + * + * If require_abort is true, it is also required that the thread containing + * the crash frame contains some known "abort" function. In this case there can be + * multiple threads with the crash frame on the top, but only one of them might + * contain the abort function to succeed. + */ +static struct thread *backtrace_find_crash_thread_from_crash_frame(struct thread *first_thread, + struct frame *crash_frame, + bool require_abort) +{ + /* + * This code can be extended to compare something else when the function + * name is not available. + */ + if (!first_thread || !crash_frame || !crash_frame->function) + return NULL; + + struct thread *result = NULL; + struct thread *thread = first_thread; + while (thread) + { + if (thread->frames + && thread->frames->function + && 0 == strcmp(thread->frames->funciton, backtrace->crash->function) + && (!require_abort || thread_contain_abort_frame(thread))) + { + if (result == NULL) + result = thread; + else + { + /* Second frame with the same function. Failure. */ + return NULL; + } + } + + thread = thread->next; + } + + return result; +} + +struct thread *backtrace_find_crash_thread(struct backtrace *backtrace) +{ + /* If there is no thread, be silent and report NULL. */ + if (!backtrace->threads) + return NULL; + + /* If there is just one thread, it is simple. */ + if (!backtrace->threads->next) + return backtrace->threads; + + /* If we have a crash frame *and* there is just one thread which has + * this frame on the top, it is also simple. + */ + struct thread *thread; + thread = backtrace_find_crash_thread_from_crash_frame(backtrace->threads, + backtrace->crash, + false); + if (thread) + return thread; + + /* There are multiple threads with a frame indistinguishable from + * the crash frame on the top of stack. + * Try to search for known abort functions. + */ + thread = backtrace_find_crash_thread_from_crash_frame(backtrace->threads, + backtrace->crash, + true); + + return thread; /* result or null */ +} diff --git a/src/Backtrace/backtrace.h b/src/Backtrace/backtrace.h index 0f99a191..bef0e351 100644 --- a/src/Backtrace/backtrace.h +++ b/src/Backtrace/backtrace.h @@ -25,16 +25,10 @@ struct frame { /* Function name, or NULL. */ char *function; - /* Arguments of the function call, or NULL. */ - char *args; /* Frame number. */ int number; - /* ?? */ - char *binfile; - /* Name of the source file, or NULL. */ + /* Name of the source file, or binary file, or NULL. */ char *sourcefile; - /* True if this is the frame where the crash happened. */ - bool crash; /* Sibling frame, or NULL if this is the last frame in a thread. */ struct frame *next; }; @@ -50,6 +44,12 @@ struct thread struct backtrace { struct thread *threads; + /* + * The frame where the crash happened according to GDB. + * It might be that we can not tell to which thread this frame belongs, + * because all threads end with mutually indistinguishable frames. + */ + struct frame *crash; }; extern struct frame *frame_new(); @@ -65,6 +65,12 @@ extern void backtrace_free(struct backtrace *bt); /* Prints how internal backtrace representation looks to stdout. */ extern void backtrace_print_tree(struct backtrace *bt); +/* + * Search all threads and tries to find the one that caused the crash. + * It might return NULL if the thread cannot be determined. + */ +extern struct thread *backtrace_find_crash_thread(struct backtrace *backtrace); + /* Defined in parser.y. */ extern struct backtrace *do_parse(FILE *input, bool debug_parser, bool debug_scanner); diff --git a/src/Backtrace/parser.y b/src/Backtrace/parser.y index 1062ae87..d9767ce7 100644 --- a/src/Backtrace/parser.y +++ b/src/Backtrace/parser.y @@ -48,10 +48,18 @@ void yyerror(char const *s) /* Bison declarations. */ %type <backtrace> backtrace ignoredpart_backtrace %type <thread> threads thread -%type <frame> frames frame +%type <frame> frames frame frame_head frame_head_1 frame_head_2 frame_head_3 frame_head_4 frame_head_5 %type <strbuf> identifier hexadecimal_digit_sequence hexadecimal_number file_name file_location function_call function_name digit_sequence frame_address_in_function -%type <c> nondigit digit hexadecimal_digit file_name_char '(' ')' '+' '-' '/' '.' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z' '_' '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' '\'' '`' ',' '#' '@' '<' '>' '=' ':' '"' ';' ' ' '\n' '\t' '\\' '!' '*' '%' '|' '^' '&' '$' -%type <num> frame_head +%type <c> nondigit digit hexadecimal_digit file_name_char + '(' ')' '+' '-' '/' '.' '_' '~' + 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' + 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' + 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' + 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z' + '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' + '\'' '`' ',' '#' '@' '<' '>' '=' ':' '"' ';' ' ' + '\n' '\t' '\\' '!' '*' '%' '|' '^' '&' '$' +%type <num> frame_start %destructor { thread_free($$); } <thread> %destructor { frame_free($$); } <frame> @@ -69,7 +77,18 @@ backtrace : /* empty */ { $$ = g_backtrace = backtrace_new(); } ; /**/ -ignoredpart_backtrace : threads { $$ = backtrace_new(); $$->threads = $1; } +ignoredpart_backtrace : frame_head wss threads + { + $$ = backtrace_new(); + $$->threads = $3; + $$->crash = $1; + } + | frame wss threads + { + $$ = backtrace_new(); + $$->threads = $3; + $$->crash = $1; + } | anychar ignoredpart_backtrace { $$ = $2; } ; @@ -100,7 +119,14 @@ frames : frame { $$ = $1; } | frames wsa frame { $$ = frame_add_sibling($1, $3); } ; -frame : frame_head wss function_call wsa keyword_at wss file_location wss variables %dprec 2 +frame : frame_head_1 wss variables %dprec 2 + | frame_head_2 wss variables %dprec 3 + | frame_head_3 wss variables %dprec 3 + | frame_head_4 wss variables %dprec 1 + | frame_head_5 wss variables +; + +frame_head_1 : frame_start wss function_call wsa keyword_at wss file_location { $$ = frame_new(); $$->number = $1; @@ -109,7 +135,9 @@ frame : frame_head wss function_call wsa keyword_at wss file_location wss va $$->sourcefile = $7->buf; strbuf_free_nobuf($7); } - | frame_head wss frame_address_in_function wss keyword_at wss file_location wss variables %dprec 3 +; + +frame_head_2 : frame_start wss frame_address_in_function wss keyword_at wss file_location { $$ = frame_new(); $$->number = $1; @@ -118,7 +146,9 @@ frame : frame_head wss function_call wsa keyword_at wss file_location wss va $$->sourcefile = $7->buf; strbuf_free_nobuf($7); } - | frame_head wss frame_address_in_function wss keyword_from wss file_location wss variables %dprec 3 +; + +frame_head_3 : frame_start wss frame_address_in_function wss keyword_from wss file_location { $$ = frame_new(); $$->number = $1; @@ -127,22 +157,31 @@ frame : frame_head wss function_call wsa keyword_at wss file_location wss va $$->sourcefile = $7->buf; strbuf_free_nobuf($7); } - | frame_head wss frame_address_in_function wss variables %dprec 1 +; + +frame_head_4 : frame_start wss frame_address_in_function { $$ = frame_new(); $$->number = $1; $$->function = $3->buf; strbuf_free_nobuf($3); } - | frame_head wss keyword_sighandler wss variables +; + +frame_head_5 : frame_start wss keyword_sighandler { $$ = frame_new(); $$->number = $1; } -; +frame_head : frame_head_1 %dprec 2 + | frame_head_2 %dprec 3 + | frame_head_3 %dprec 3 + | frame_head_4 %dprec 1 + | frame_head_5 +; -frame_head : '#' digit_sequence +frame_start: '#' digit_sequence { if (sscanf($2->buf, "%d", &$$) != 1) { @@ -168,13 +207,16 @@ file_location : file_name ':' digit_sequence | file_name ; -variables : variables_char - | variables variables_char - | variables variables_wss variables_char - | variables '\n' variables_char_no_framestart - | variables variables_wss '\n' variables_wss variables_char_no_framestart - | variables variables_wss '\n' variables_char_no_framestart - | variables '\n' variables_wss variables_char_no_framestart +variables : variables_line '\n' + | variables_line variables_wss '\n' + | variables variables_line '\n' + | variables variables_wss variables_line '\n' + | variables variables_wss variables_line variables_wss '\n' +; + +variables_line : variables_char_no_framestart + | variables_line variables_char + | variables_line variables_wss variables_char ; variables_ws : '\t' | ' ' @@ -184,14 +226,14 @@ variables_wss : variables_ws | variables_wss variables_ws ; - /* We hope variables in frame never contain # character. */ variables_char : '#' | variables_char_no_framestart ; variables_char_no_framestart : digit | nondigit | '(' | ')' | '+' | '-' | '<' | '>' | '"' | '/' | '.' | '[' | ']' | '?' | '\'' | '`' | ',' | '=' | '{' | '}' | '^' | '&' | '$' - | ':' | ';' | '\\' | '!' | '@' | '*' | '%' | '|' + | ':' | ';' | '\\' | '!' | '@' | '*' | '%' | '|' + | '~' function_call : function_name wsa function_args ; @@ -215,8 +257,9 @@ function_args_sequence : function_args_char | function_args_sequence wsa function_args_char ; -function_args_char : digit | nondigit | '{' | '}' | '<' | '>' | '"' - | '=' | '-' | '+' | '@' | ',' | '.' | '[' | ']' | '/' +function_args_char : digit | nondigit | '{' | '}' | '<' | '>' | '"' | ':' | '~' + | '=' | '-' | '+' | '@' | ',' | '.' | '[' | ']' | '/' | '%' + | '\\' ; file_name : file_name_char { $$ = strbuf_new(); strbuf_append_char($$, $1); } |