diff options
-rw-r--r-- | src/Backtrace/backtrace.c | 194 | ||||
-rw-r--r-- | src/Backtrace/backtrace.h | 19 | ||||
-rw-r--r-- | src/Backtrace/main.c | 104 | ||||
-rw-r--r-- | src/Backtrace/parser.y | 3 |
4 files changed, 252 insertions, 68 deletions
diff --git a/src/Backtrace/backtrace.c b/src/Backtrace/backtrace.c index 62f99a36..5e2d4bbd 100644 --- a/src/Backtrace/backtrace.c +++ b/src/Backtrace/backtrace.c @@ -17,6 +17,7 @@ */ #include "backtrace.h" #include <stdlib.h> +#include <string.h> struct frame *frame_new() { @@ -30,6 +31,7 @@ struct frame *frame_new() f->function = NULL; f->number = 0; f->sourcefile = NULL; + f->signal_handler_called = false; f->next = NULL; return f; } @@ -53,9 +55,13 @@ struct frame *frame_add_sibling(struct frame *a, struct frame *b) return a; } -static void frame_print_tree(struct frame *frame) +static void frame_print_tree(struct frame *frame, bool verbose) { - printf(" #%d", frame->number); + if (verbose) + printf(" #%d", frame->number); + else + printf(" "); + if (frame->function) printf(" %s", frame->function); if (frame->sourcefile) @@ -65,9 +71,20 @@ static void frame_print_tree(struct frame *frame) printf(" %s", frame->sourcefile); } + if (frame->signal_handler_called) + printf(" <signal handler called>"); + puts(""); /* newline */ } +static bool frame_is_exit_handler(struct frame *frame) +{ + return (frame->function + && frame->sourcefile + && 0 == strcmp(frame->function, "__run_exit_handlers") + && NULL != strstr(frame->sourcefile, "exit.c")); +} + struct thread *thread_new() { struct thread *t = malloc(sizeof(struct thread)); @@ -116,14 +133,72 @@ static int thread_get_frame_count(struct thread *thread) return count; } -static void thread_print_tree(struct thread *thread) +static void thread_print_tree(struct thread *thread, bool verbose) { int framecount = thread_get_frame_count(thread); - printf("Thread no. %d (%d frames)\n", thread->number, framecount); + if (verbose) + printf("Thread no. %d (%d frames)\n", thread->number, framecount); + else + printf("Thread\n"); + struct frame *frame = thread->frames; + while (frame) + { + frame_print_tree(frame, verbose); + frame = frame->next; + } +} + +/* + * Checks whether the thread it contains some known "abort" function. + * If a frame with the function is found, it is returned. + * If there are multiple frames with abort function, the lowest + * one is returned. + * Nonrecursive. + */ +static struct frame *thread_find_abort_frame(struct thread *thread) +{ struct frame *frame = thread->frames; + struct frame *result = NULL; while (frame) { - frame_print_tree(frame); + if (frame->function && frame->sourcefile) + { + if (0 == strcmp(frame->function, "raise") + && NULL != strstr(frame->sourcefile, "pt-raise.c")) + result = frame; + else if (0 == strcmp(frame->function, "exit") + && NULL != strstr(frame->sourcefile, "exit.c")) + result = frame; + else if (0 == strcmp(frame->function, "abort") + && NULL != strstr(frame->sourcefile, "abort.c")) + result = frame; + else if (frame_is_exit_handler(frame)) + result = frame; + } + + frame = frame->next; + } + + return result; +} + +static void thread_remove_exit_handlers(struct thread *thread) +{ + struct frame *frame = thread->frames; + while (frame) + { + if (frame_is_exit_handler(frame)) + { + /* Delete all frames from the beginning to this frame. */ + while (thread->frames != frame) + { + struct frame *rm = thread->frames; + thread->frames = thread->frames->next; + frame_free(rm); + } + return; + } + frame = frame->next; } } @@ -168,46 +243,38 @@ static int backtrace_get_thread_count(struct backtrace *bt) return count; } -void backtrace_print_tree(struct backtrace *bt) +void backtrace_print_tree(struct backtrace *backtrace, bool verbose) { - printf("Thread count: %d\n", backtrace_get_thread_count(bt)); - if (bt->crash) + if (verbose) + printf("Thread count: %d\n", backtrace_get_thread_count(backtrace)); + + if (backtrace->crash && verbose) { printf("Crash frame: "); - frame_print_tree(bt->crash); + frame_print_tree(backtrace->crash, verbose); } - struct thread *thread = bt->threads; + struct thread *thread = backtrace->threads; while (thread) { - thread_print_tree(thread); + thread_print_tree(thread, verbose); thread = thread->next; } } -/* - * Checks whether the thread it contains some known "abort" function. - * Nonrecursive. - */ -static bool thread_contain_abort_frame(struct thread *thread) +void backtrace_remove_threads_except_one(struct backtrace *backtrace, + struct thread *one) { - int depth = 15; /* check only 15 top frames on the stack */ - struct frame *frame = thread->frames; - while (frame && depth) + while (backtrace->threads) { - 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; + struct thread *rm = backtrace->threads; + backtrace->threads = rm->next; + if (rm != one) + thread_free(rm); } - return false; + one->next = NULL; + backtrace->threads = one; } /* @@ -219,25 +286,24 @@ static bool thread_contain_abort_frame(struct thread *thread) * 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, +static struct thread *backtrace_find_crash_thread_from_crash_frame(struct backtrace *backtrace, 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) + if (!backtrace->threads || !backtrace->crash || !backtrace->crash->function) return NULL; struct thread *result = NULL; - struct thread *thread = first_thread; + struct thread *thread = backtrace->threads; while (thread) { if (thread->frames && thread->frames->function - && 0 == strcmp(thread->frames->funciton, backtrace->crash->function) - && (!require_abort || thread_contain_abort_frame(thread))) + && 0 == strcmp(thread->frames->function, backtrace->crash->function) + && (!require_abort || thread_find_abort_frame(thread))) { if (result == NULL) result = thread; @@ -268,9 +334,7 @@ struct thread *backtrace_find_crash_thread(struct backtrace *backtrace) * 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); + thread = backtrace_find_crash_thread_from_crash_frame(backtrace, false); if (thread) return thread; @@ -278,9 +342,55 @@ struct thread *backtrace_find_crash_thread(struct backtrace *backtrace) * 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); + thread = backtrace_find_crash_thread_from_crash_frame(backtrace, true); return thread; /* result or null */ } + +void backtrace_limit_frame_depth(struct backtrace *backtrace, int depth) +{ + if (depth <= 0) + return; + + struct thread *thread = backtrace->threads; + while (thread) + { + struct frame *frame = thread_find_abort_frame(thread); + if (frame) + frame = frame->next; /* Start counting from the frame following the abort fr. */ + else + frame = thread->frames; /* Start counting from the first frame. */ + + /* Skip some frames to get the required stack depth. */ + int i = depth; + struct frame *last_frame; + while (frame && i) + { + last_frame = frame; + frame = frame->next; + --i; + } + + /* Delete the remaining frames. */ + last_frame->next = NULL; + while (frame) + { + struct frame *rm = frame; + frame = frame->next; + frame_free(rm); + } + + thread = thread->next; + } +} + +void backtrace_remove_exit_handlers(struct backtrace *backtrace) +{ + struct thread *thread = backtrace->threads; + while (thread) + { + thread_remove_exit_handlers(thread); + thread = thread->next; + } +} + diff --git a/src/Backtrace/backtrace.h b/src/Backtrace/backtrace.h index bef0e351..3091c630 100644 --- a/src/Backtrace/backtrace.h +++ b/src/Backtrace/backtrace.h @@ -29,6 +29,7 @@ struct frame int number; /* Name of the source file, or binary file, or NULL. */ char *sourcefile; + bool signal_handler_called; /* Sibling frame, or NULL if this is the last frame in a thread. */ struct frame *next; }; @@ -62,8 +63,17 @@ extern struct thread *thread_add_sibling(struct thread *a, struct thread *b); extern struct backtrace *backtrace_new(); extern void backtrace_free(struct backtrace *bt); + /* Prints how internal backtrace representation looks to stdout. */ -extern void backtrace_print_tree(struct backtrace *bt); +extern void backtrace_print_tree(struct backtrace *backtrace, bool verbose); + +/* + * Frees all threads except the one provided as parameters. + * It does not check whether one is a member of backtrace. + * Caller must know that. + */ +extern void backtrace_remove_threads_except_one(struct backtrace *backtrace, + struct thread *one); /* * Search all threads and tries to find the one that caused the crash. @@ -71,6 +81,13 @@ extern void backtrace_print_tree(struct backtrace *bt); */ extern struct thread *backtrace_find_crash_thread(struct backtrace *backtrace); +extern void backtrace_limit_frame_depth(struct backtrace *backtrace, int depth); + +/* + * Exit handlers are all stack frames above __run_exit_handlers() + */ +extern void backtrace_remove_exit_handlers(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/main.c b/src/Backtrace/main.c index d4c860f1..459185eb 100644 --- a/src/Backtrace/main.c +++ b/src/Backtrace/main.c @@ -19,10 +19,14 @@ */ #include <argp.h> #include <stdlib.h> +#include <sysexits.h> #include "config.h" #include "backtrace.h" #include "fallback.h" +#define EX_PARSINGFAILED EX__MAX + 1 +#define EX_THREADDETECTIONFAILED EX__MAX + 2 + const char *argp_program_version = "abrt-backtrace " VERSION; const char *argp_program_bug_address = "<crash-catcher@lists.fedorahosted.org>"; @@ -32,21 +36,25 @@ static char doc[] = "abrt-backtrace -- backtrace analyzer"; static char args_doc[] = "FILE"; static struct argp_option options[] = { - {"independent" , 'i', 0, 0, "Prints independent backtrace (fallback)" }, - {"tree" , 't', 0, 0, "Prints backtrace analysis tree"}, - {"verbose" , 'v', 0, 0, "Prints debug information"}, - {"debug-parser" , 'p', 0, 0, "Prints parser debug information"}, - {"debug-scanner", 's', 0, 0, "Prints scanner debug information"}, + {"independent" , 'i', 0 , 0, "Prints independent backtrace (fallback)" }, + {"single-thread" , 'n', 0 , 0, "Display the crash thread only in the backtrace" }, + {"frame-depth" , 'd', "N", 0, "Display only top N frames under the crash frame" }, + {"remove-exit-handlers" , 'r', 0 , 0, "Removes exit handlers from the displayed backtrace" }, + {"debug-parser" , 'p', 0 , 0, "Prints parser debug information"}, + {"debug-scanner" , 's', 0 , 0, "Prints scanner debug information"}, + {"verbose" , 'v', 0 , 0, "Print human-friendly superfluous output."}, { 0 } }; struct arguments { bool independent; - bool tree; - bool verbose; + bool single_thread; + int frame_depth; /* negative == do not limit the depth */ + bool remove_exit_handlers; bool debug_parser; bool debug_scanner; + bool verbose; char *filename; }; @@ -56,21 +64,30 @@ parse_opt (int key, char *arg, struct argp_state *state) /* Get the input argument from argp_parse, which we know is a pointer to our arguments structure. */ struct arguments *arguments = (struct arguments*)state->input; - + switch (key) { case 'i': arguments->independent = true; break; - case 't': arguments->tree = true; break; - case 'v': arguments->verbose = true; break; + case 'n': arguments->single_thread = true; break; + case 'd': + if (1 != sscanf(arg, "%d", &arguments->frame_depth)) + { + /* Must be a number. */ + argp_usage(state); + exit(EX_USAGE); /* Invalid argument */ + } + break; + case 'r': arguments->remove_exit_handlers = true; break; case 'p': arguments->debug_parser = true; break; case 's': arguments->debug_scanner = true; break; + case 'v': arguments->verbose = true; break; case ARGP_KEY_ARG: if (arguments->filename) { /* Too many arguments. */ argp_usage(state); - exit(2); + exit(EX_USAGE); /* Invalid argument */ } arguments->filename = arg; break; @@ -80,7 +97,7 @@ parse_opt (int key, char *arg, struct argp_state *state) { /* Not enough arguments. */ argp_usage(state); - exit(1); + exit(EX_USAGE); /* is there a better errno? */ } break; @@ -99,37 +116,76 @@ int main(int argc, char **argv) /* Set options default values and parse program command line. */ struct arguments arguments; arguments.independent = false; - arguments.verbose = false; - arguments.tree = false; + arguments.frame_depth = -1; + arguments.single_thread = false; + arguments.remove_exit_handlers = false; arguments.debug_parser = false; arguments.debug_scanner = false; + arguments.verbose = false; arguments.filename = 0; - argp_parse (&argp, argc, argv, 0, 0, &arguments); + argp_parse(&argp, argc, argv, 0, 0, &arguments); /* Open input file and parse it. */ FILE *fp = fopen(arguments.filename, "r"); if (!fp) { - printf("Unable to open '%s'.\n", arguments.filename); - exit(3); + fprintf(stderr, "Unable to open '%s'.\n", arguments.filename); + exit(EX_NOINPUT); /* No such file or directory */ } + /* Print independent backtrace and exit. */ if (arguments.independent) { struct strbuf *ibt = independent_backtrace(fp); fclose(fp); puts(ibt->buf); strbuf_free(ibt); - return; + return 0; /* OK */ + } + + /* Try to parse the backtrace. */ + struct backtrace *backtrace; + backtrace = do_parse(fp, arguments.debug_parser, arguments.debug_scanner); + + /* If the parser failed print independent backtrace. */ + if (!backtrace) + { + fseek(fp, 0, SEEK_SET); + struct strbuf *ibt = independent_backtrace(fp); + fclose(fp); + puts(ibt->buf); + strbuf_free(ibt); + /* PARSING FAILED, BUT OUTPUT CAN BE USED */ + return EX_PARSINGFAILED; } - struct backtrace *bt; - bt = do_parse(fp, arguments.debug_parser, arguments.debug_scanner); fclose(fp); - if (arguments.tree) - backtrace_print_tree(bt); + /* If a single thread is requested, remove all other threads. */ + int retval = 0; + struct thread *crash_thread = NULL; + if (arguments.single_thread) + { + crash_thread = backtrace_find_crash_thread(backtrace); + if (crash_thread) + backtrace_remove_threads_except_one(backtrace, crash_thread); + else + { + fprintf(stderr, "Detection of crash thread failed.\n"); + /* THREAD DETECTION FAILED, BUT THE OUTPUT CAN BE USED */ + retval = EX_THREADDETECTIONFAILED; + } + } - backtrace_free(bt); - return 0; + /* If a frame removal is requested, do it now. */ + if (arguments.frame_depth > 0) + backtrace_limit_frame_depth(backtrace, arguments.frame_depth); + + /* Frame removal can be done before removing exit handlers */ + if (arguments.remove_exit_handlers > 0) + backtrace_remove_exit_handlers(backtrace); + + backtrace_print_tree(backtrace, arguments.verbose); + backtrace_free(backtrace); + return retval; } diff --git a/src/Backtrace/parser.y b/src/Backtrace/parser.y index d9767ce7..8021cb0d 100644 --- a/src/Backtrace/parser.y +++ b/src/Backtrace/parser.y @@ -172,6 +172,7 @@ frame_head_5 : frame_start wss keyword_sighandler { $$ = frame_new(); $$->number = $1; + $$->signal_handler_called = true; } frame_head : frame_head_1 %dprec 2 @@ -329,7 +330,7 @@ wss : ws | wss ws ; -/* whitespace sequence allowed */ + /* whitespace sequence allowed */ wsa : | wss ; |