summaryrefslogtreecommitdiffstats
path: root/src/utils
diff options
context:
space:
mode:
authorKarel Klic <kklic@redhat.com>2010-03-26 17:55:51 +0100
committerKarel Klic <kklic@redhat.com>2010-03-26 18:11:09 +0100
commit7ccd55eb10921e94a81b699a2c96cb1dc25515d1 (patch)
tree66297ccd0633eb5b8f817c3d710c36b267b3bc61 /src/utils
parent605c31f9d0897c18a900c7a4c0ad75bc439d18e5 (diff)
downloadabrt-7ccd55eb10921e94a81b699a2c96cb1dc25515d1.tar.gz
abrt-7ccd55eb10921e94a81b699a2c96cb1dc25515d1.tar.xz
abrt-7ccd55eb10921e94a81b699a2c96cb1dc25515d1.zip
Move backtrace parser from src/Backtrace to lib/Utils.
Move abrt-backtrace app from src/Backtrace/main.c to src/utils/abrt-backtrace. Move backtrace preprocessign code from abrt-backtrace to the parser. Implemented new backtrace rating algorithm. Added old bt rating algorithm to backtrace.c Move strbuf to lib/Utils, and updated it to use xfuncs. Created separate header for xfuncs. Some functions in xfuncs marked as extern "c", so they can be used in C code. Merged backtrace fallback (independent_backtrace) "parser" into backtrace.{h/c}. Added option --rate to abrt-backtrace, to be able to use the new backtrace rating algorithm in scripts.
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/Makefile.am11
-rw-r--r--src/utils/abrt-backtrace.152
-rw-r--r--src/utils/abrt-backtrace.c318
3 files changed, 381 insertions, 0 deletions
diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
new file mode 100644
index 00000000..0cecf26a
--- /dev/null
+++ b/src/utils/Makefile.am
@@ -0,0 +1,11 @@
+bin_PROGRAMS = abrt-backtrace
+abrt_backtrace_CFLAGS = -Wall
+abrt_backtrace_SOURCES = abrt-backtrace.c
+abrt_backtrace_CPPFLAGS = \
+ -I$(srcdir)/../../inc \
+ -I$(srcdir)/../../lib/Utils
+abrt_backtrace_LDADD = \
+ ../../lib/Utils/libABRTUtils.la
+
+man_MANS = abrt-backtrace.1
+EXTRA_DIST = $(man_MANS)
diff --git a/src/utils/abrt-backtrace.1 b/src/utils/abrt-backtrace.1
new file mode 100644
index 00000000..ff7c2be2
--- /dev/null
+++ b/src/utils/abrt-backtrace.1
@@ -0,0 +1,52 @@
+.TH abrt\-backtrace "1" "23 Nov 2009" ""
+.SH NAME
+abrt\-backtrace \- a backtrace analyzer for abrt
+.SH SYNOPSIS
+.B abrt\-backtrace
+[option]... [FILE]
+.SH DESCRIPTION
+.I abrt\-backtrace
+is a command line tool that analyzes backtraces produced by
+GDB and provides their textual representation useful for
+crash duplication detection.
+
+By default, abrt\-backtrace prints the backtrace tree created by
+parsing the input file.
+
+.SH OPTIONS
+.B Basic startup options
+.IP "\-V, \-\-version"
+Displays version of abrt\-backtrace.
+.IP "\-?, \-\-help"
+Print a help message describing all of abrt-backtrace’s command-line options.
+
+.PP
+.B Actions
+.IP "\-i, \-\-independent"
+Prints independent backtrace fallback. The "independent backtrace"
+is used as a fallback internal representation of backtrace
+when the internal parser fails to process the input due to
+unsupported format.
+
+.PP
+.B Various options
+.IP "\-n, \-\-single-thread"
+Removes all threads except the one that caused the crash from
+the internal representation of the backtrace.
+.IP "\-d=N, \-\-frame-depth=N"
+Display only the top N frames in every thread.
+Frames that are a part of system error handling do not count to the N.
+So more than N frames can be displayed, but N of them are useful for examination.
+N must be larger than 1.
+.IP "\-r, \-\-remove-exit-handlers"
+Do not display exit handlers and frames that comes after them in the backtrace.
+.IP "\-p, \-\-debug-parser"
+Prints debug information when parsing the backtrace.
+.IP "\-s, \-\-debug-scanner"
+Prints debug information when scanning the input file for the parser.
+
+.SH "SEE ALSO"
+.IR abrtd (8),
+.IR abrt.conf (5),
+.IR abrt-cli (1),
+.IR abrt-plugins (7)
diff --git a/src/utils/abrt-backtrace.c b/src/utils/abrt-backtrace.c
new file mode 100644
index 00000000..f5f8e64d
--- /dev/null
+++ b/src/utils/abrt-backtrace.c
@@ -0,0 +1,318 @@
+/* -*-mode:c++;c-file-style:"bsd";c-basic-offset:4;indent-tabs-mode:nil-*-
+ main.cpp - parses command line arguments
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <argp.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <string.h>
+#include "config.h"
+#include "backtrace.h"
+#include "strbuf.h"
+
+/* Too large files are trimmed. */
+#define FILE_SIZE_LIMIT 20000000 /* ~ 20 MB */
+
+#define EX_PARSINGFAILED EX__MAX + 1 /* = 79 */
+#define EX_THREADDETECTIONFAILED EX__MAX + 2 /* = 80 */
+
+const char *argp_program_version = "abrt-backtrace " VERSION;
+const char *argp_program_bug_address = "<crash-catcher@lists.fedorahosted.org>";
+
+static char doc[] = "abrt-backtrace -- backtrace analyzer";
+
+/* A description of the arguments we accept. */
+static char args_doc[] = "FILE";
+
+static struct argp_option options[] = {
+ {"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 handler frames from the displayed backtrace"},
+ {"remove-noncrash-frames", 'm', 0 , 0, "Removes common frames known as not causing crash"},
+ {"rate" , 'a', 0 , 0, "Prints the backtrace rating from 0 to 4"},
+ {"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 single_thread;
+ int frame_depth; /* negative == do not limit the depth */
+ bool remove_exit_handlers;
+ bool remove_noncrash_frames;
+ bool debug_parser;
+ bool debug_scanner;
+ bool verbose;
+ bool rate;
+ char *filename;
+};
+
+static error_t
+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 '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 'm': arguments->remove_noncrash_frames = true; break;
+ case 'p': arguments->debug_parser = true; break;
+ case 's': arguments->debug_scanner = true; break;
+ case 'v': arguments->verbose = true; break;
+ case 'a': arguments->rate = true; break;
+
+ case ARGP_KEY_ARG:
+ if (arguments->filename)
+ {
+ /* Too many arguments. */
+ argp_usage(state);
+ exit(EX_USAGE); /* Invalid argument */
+ }
+ arguments->filename = arg;
+ break;
+
+ case ARGP_KEY_END:
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Our argp parser. */
+static struct argp argp = { options, parse_opt, args_doc, doc };
+
+#define PYTHON_BACKTRACE_ID1 "\n\nTraceback (most recent call last):\n"
+#define PYTHON_BACKTRACE_ID2 "\n\nLocal variables in innermost frame:\n"
+
+int main(int argc, char **argv)
+{
+ /* Set options default values and parse program command line. */
+ struct arguments arguments;
+ arguments.independent = false;
+ arguments.frame_depth = -1;
+ arguments.single_thread = false;
+ arguments.remove_exit_handlers = false;
+ arguments.remove_noncrash_frames = false;
+ arguments.debug_parser = false;
+ arguments.debug_scanner = false;
+ arguments.verbose = false;
+ arguments.filename = 0;
+ arguments.rate = false;
+ argp_parse(&argp, argc, argv, 0, 0, &arguments);
+
+ /* If we are about to rate a backtrace, the other values must be set accordingly,
+ no matter what the user set on the command line. */
+ if (arguments.rate)
+ {
+ arguments.independent = false;
+ arguments.frame_depth = 5;
+ arguments.single_thread = true;
+ arguments.remove_exit_handlers = true;
+ arguments.remove_noncrash_frames = true;
+ }
+
+ char *bttext = NULL;
+
+ /* If a filename was provided, read input from file.
+ Otherwise read from stdin. */
+ if (arguments.filename)
+ {
+ /* Open input file, and parse it. */
+ FILE *fp = fopen(arguments.filename, "r");
+ if (!fp)
+ {
+ fprintf(stderr, "Unable to open '%s'.\n", arguments.filename);
+ exit(EX_NOINPUT); /* No such file or directory */
+ }
+
+ /* Header and footer of the backtrace is stripped to simplify the parser.
+ * A drawback is that the backtrace must be loaded to memory.
+ */
+ fseek(fp, 0, SEEK_END);
+ size_t size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ if (size > FILE_SIZE_LIMIT)
+ {
+ fprintf(stderr, "Input file too big (%zd). Maximum size is %d.\n",
+ size, FILE_SIZE_LIMIT);
+ exit(EX_IOERR);
+ }
+
+ /* Handle the case that the input file is empty.
+ * The code is not designed to support completely empty backtrace.
+ * Silently exit indicating success.
+ */
+ if (size == 0)
+ {
+ fclose(fp);
+ exit(0);
+ }
+
+ bttext = malloc(size + 1);
+ if (!bttext)
+ {
+ fclose(fp);
+ fputs("malloc failed", stderr);
+ exit(EX_OSERR);
+ }
+
+ if (1 != fread(bttext, size, 1, fp))
+ {
+ fclose(fp);
+ fprintf(stderr, "Unable to read from '%s'.\n", arguments.filename);
+ exit(EX_IOERR); /* IO Error */
+ }
+
+ bttext[size] = '\0';
+ fclose(fp);
+ }
+ else
+ {
+ struct strbuf *btin = strbuf_new();
+ int c;
+ while ((c = getchar()) != EOF && c != '\0')
+ strbuf_append_char(btin, (char)c);
+
+ strbuf_append_char(btin, '\0');
+ bttext = btin->buf;
+ strbuf_free_nobuf(btin); /* free btin, but not its internal buffer */
+ }
+
+ /* Detect Python backtraces. If it is a Python backtrace,
+ * silently exit for now.
+ */
+ if (strstr(bttext, PYTHON_BACKTRACE_ID1) != NULL
+ && strstr(bttext, PYTHON_BACKTRACE_ID2) != NULL)
+ {
+ if (arguments.rate)
+ puts("4");
+ exit(0);
+ }
+
+ /* Print independent backtrace and exit. */
+ if (arguments.independent)
+ {
+ struct strbuf *ibt = independent_backtrace(bttext);
+ puts(ibt->buf);
+ strbuf_free(ibt);
+ free(bttext);
+ return 0; /* OK */
+ }
+
+ /* Try to parse the backtrace. */
+ struct backtrace *backtrace;
+ backtrace = backtrace_parse(bttext, arguments.debug_parser, arguments.debug_scanner);
+
+ /* If the parser failed print independent backtrace. */
+ if (!backtrace)
+ {
+ if (arguments.rate)
+ {
+ free(bttext);
+ puts("0");
+ /* Parsing failed, but the output can be used. */
+ return EX_PARSINGFAILED;
+ }
+ struct strbuf *ibt = independent_backtrace(bttext);
+ puts(ibt->buf);
+ strbuf_free(ibt);
+ free(bttext);
+ /* Parsing failed, but the output can be used. */
+ return EX_PARSINGFAILED;
+ }
+
+ free(bttext);
+
+ /* [--rate] Get the quality of the full backtrace. */
+ float q1 = backtrace_quality(backtrace);
+
+ /* 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;
+ }
+ }
+
+ /* [--rate] Get the quality of the crash thread. */
+ float q2 = backtrace_quality(backtrace);
+
+ if (arguments.remove_noncrash_frames)
+ backtrace_remove_noncrash_frames(backtrace);
+
+ /* 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);
+
+ /* [--rate] Get the quality of frames around the crash. */
+ float q3 = backtrace_quality(backtrace);
+
+ if (arguments.rate)
+ {
+ /* Compute and store backtrace rating. */
+ /* Compute and store backtrace rating. The crash frame
+ is more important that the others. The frames around
+ the crash are more important than the rest. */
+ float qtot = 0.25f * q1 + 0.35f * q2 + 0.4f * q3;
+
+ /* Turn the quality to rating. */
+ const char *rating;
+ if (qtot < 0.6f) rating = "0";
+ else if (qtot < 0.7f) rating = "1";
+ else if (qtot < 0.8f) rating = "2";
+ else if (qtot < 0.9f) rating = "3";
+ else rating = "4";
+ puts(rating);
+ }
+ else
+ backtrace_print_tree(backtrace, arguments.verbose);
+
+ backtrace_free(backtrace);
+ return retval;
+}