summaryrefslogtreecommitdiffstats
path: root/src/Backtrace
diff options
context:
space:
mode:
authorKarel Klic <kklic@redhat.com>2009-11-24 15:06:06 +0100
committerKarel Klic <kklic@redhat.com>2009-11-24 15:06:06 +0100
commitf6e929ee12a08a95c263b9c4c3e89f6eab188c4c (patch)
tree7017ff1eefcab214ca587ade874cf50b69e228e5 /src/Backtrace
parent8f2b7508ff42d637e4a3be5f24af3a5915515b92 (diff)
downloadabrt-f6e929ee12a08a95c263b9c4c3e89f6eab188c4c.tar.gz
abrt-f6e929ee12a08a95c263b9c4c3e89f6eab188c4c.tar.xz
abrt-f6e929ee12a08a95c263b9c4c3e89f6eab188c4c.zip
Finishing basic implementation of abrt-backtrace (--single-thread, --frame-depth, --remove-exit-handlers, parser)
Diffstat (limited to 'src/Backtrace')
-rw-r--r--src/Backtrace/backtrace.c194
-rw-r--r--src/Backtrace/backtrace.h19
-rw-r--r--src/Backtrace/main.c104
-rw-r--r--src/Backtrace/parser.y3
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
;