summaryrefslogtreecommitdiffstats
path: root/src/Backtrace
diff options
context:
space:
mode:
authorKarel Klic <kklic@redhat.com>2009-11-23 16:52:50 +0100
committerKarel Klic <kklic@redhat.com>2009-11-23 16:52:50 +0100
commit4d725ba7f708087099496145a953064aded60365 (patch)
tree29551f955fd3ef19b17ef9872ca10d8e24e78013 /src/Backtrace
parentb8da7620a417ef835869da692db140b75e8b7a93 (diff)
downloadabrt-4d725ba7f708087099496145a953064aded60365.tar.gz
abrt-4d725ba7f708087099496145a953064aded60365.tar.xz
abrt-4d725ba7f708087099496145a953064aded60365.zip
Backtrace detector improvements.
Diffstat (limited to 'src/Backtrace')
-rw-r--r--src/Backtrace/backtrace.c117
-rw-r--r--src/Backtrace/backtrace.h20
-rw-r--r--src/Backtrace/parser.y87
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); }