summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--configure.ac3
-rw-r--r--inc/abrtlib.h50
-rw-r--r--lib/Utils/Makefile.am9
-rw-r--r--lib/Utils/backtrace.c (renamed from src/Backtrace/backtrace.c)403
-rw-r--r--lib/Utils/backtrace.h (renamed from src/Backtrace/backtrace.h)58
-rw-r--r--lib/Utils/backtrace_parser.y (renamed from src/Backtrace/parser.y)78
-rw-r--r--lib/Utils/strbuf.c130
-rw-r--r--lib/Utils/strbuf.h (renamed from src/Backtrace/strbuf.h)14
-rw-r--r--lib/Utils/xfuncs.cpp8
-rw-r--r--lib/Utils/xfuncs.h104
-rwxr-xr-xscripts/abrt-bz-downloader (renamed from src/Backtrace/abrt-bz-downloader)0
-rwxr-xr-xscripts/abrt-bz-dupchecker (renamed from src/Backtrace/abrt-bz-dupchecker)0
-rwxr-xr-xscripts/abrt-bz-hashchecker (renamed from src/Backtrace/abrt-bz-hashchecker)0
-rwxr-xr-xscripts/check-bt-parsability (renamed from src/Backtrace/check-bt-parsability)0
-rw-r--r--src/Backtrace/Makefile.am24
-rw-r--r--src/Backtrace/fallback.c174
-rw-r--r--src/Backtrace/fallback.h32
-rw-r--r--src/Backtrace/main.c361
-rw-r--r--src/Backtrace/strbuf.c112
-rw-r--r--src/Makefile.am2
-rw-r--r--src/utils/Makefile.am11
-rw-r--r--src/utils/abrt-backtrace.1 (renamed from src/Backtrace/abrt-backtrace.1)0
-rw-r--r--src/utils/abrt-backtrace.c318
23 files changed, 1079 insertions, 812 deletions
diff --git a/configure.ac b/configure.ac
index c3925eec..ccaf9793 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,6 +9,7 @@ AC_ARG_ENABLE(debug,
AC_DISABLE_STATIC
AC_PROG_LIBTOOL
+AC_PROG_CC
AC_PROG_CXX
AC_SYS_LARGEFILE
@@ -103,7 +104,7 @@ AC_CONFIG_FILES([
src/Applet/Makefile
src/Gui/Makefile
src/CLI/Makefile
- src/Backtrace/Makefile
+ src/utils/Makefile
inc/Makefile
po/Makefile.in
icons/Makefile
diff --git a/inc/abrtlib.h b/inc/abrtlib.h
index 7b00fe35..7cafc99c 100644
--- a/inc/abrtlib.h
+++ b/inc/abrtlib.h
@@ -41,6 +41,7 @@
#include <string>
#include "abrt_types.h"
+#include "xfuncs.h"
/* Some libc's forget to declare these, do it ourself */
extern char **environ;
@@ -95,13 +96,6 @@ extern int g_verbose;
#define VERB3 if (g_verbose >= 3)
/* there is no level > 3 */
-
-void* xmalloc(size_t size);
-void* xrealloc(void *ptr, size_t size);
-void* xzalloc(size_t size);
-char* xstrdup(const char *s);
-char* xstrndup(const char *s, int n);
-
char* skip_whitespace(const char *s);
char* skip_non_whitespace(const char *s);
@@ -123,38 +117,6 @@ extern ssize_t safe_write(int fd, const void *buf, size_t count);
// NB: will return short write on error, not -1,
// if some data was written before error occurred
extern ssize_t full_write(int fd, const void *buf, size_t count);
-extern void xwrite(int fd, const void *buf, size_t count);
-extern void xwrite_str(int fd, const char *str);
-
-void xpipe(int filedes[2]);
-void xdup(int from);
-void xdup2(int from, int to);
-off_t xlseek(int fd, off_t offset, int whence);
-void xsetenv(const char *key, const char *value);
-int xsocket(int domain, int type, int protocol);
-void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
-void xlisten(int s, int backlog);
-ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, socklen_t tolen);
-void xchdir(const char *path);
-void xstat(const char *name, struct stat *stat_buf);
-/* Just testing dent->d_type == DT_REG is wrong: some filesystems
- * do not report the type, they report DT_UNKNOWN for every dirent
- * (and this is not a bug in filesystem, this is allowed by standards).
- * This function handles this case. Note: it returns 0 on symlinks
- * even if they point to regular files.
- */
-int is_regular_file(struct dirent *dent, const char *dirname);
-
-void xmove_fd(int from, int to);
-int ndelay_on(int fd);
-int ndelay_off(int fd);
-int close_on_exec_on(int fd);
-char* xasprintf(const char *format, ...);
-char* xvasprintf(const char *format, va_list p);
-
-int xopen(const char *pathname, int flags);
-int xopen3(const char *pathname, int flags, int mode);
-void xunlink(const char *pathname);
/* copyfd_XX print read/write errors and return -1 if they occur */
off_t copyfd_eof(int src_fd, int dst_fd);
@@ -162,9 +124,6 @@ off_t copyfd_size(int src_fd, int dst_fd, off_t size);
void copyfd_exact_size(int src_fd, int dst_fd, off_t size);
off_t copy_file(const char *src_name, const char *dst_name, int mode);
-
-void xsetreuid(uid_t ruid, uid_t euid);
-void xsetregid(gid_t rgid, gid_t egid);
enum {
EXECFLG_INPUT = 1 << 0,
EXECFLG_OUTPUT = 1 << 1,
@@ -249,15 +208,8 @@ int daemon_is_ok();
/* Returns malloc'ed block */
char *encode_base64(const void *src, int length);
-bool dot_or_dotdot(const char *filename);
-char *last_char_is(const char *s, int c);
-bool string_to_bool(const char *s);
/* C++ style stuff */
-
-std::string ssprintf(const char *format, ...);
-std::string get_home_dir(uid_t uid);
-std::string concat_path_file(const char *path, const char *filename);
double get_dirsize(const char *pPath);
double get_dirsize_find_largest_dir(
const char *pPath,
diff --git a/lib/Utils/Makefile.am b/lib/Utils/Makefile.am
index 2667b18a..d3c7a255 100644
--- a/lib/Utils/Makefile.am
+++ b/lib/Utils/Makefile.am
@@ -2,12 +2,14 @@
# ABRTdUtils has much more. It is used by daemon and plugins only
lib_LTLIBRARIES = libABRTUtils.la libABRTdUtils.la
+AM_YFLAGS = --verbose
+
# Not used just yet:
# time.cpp
# xconnect.cpp
libABRTUtils_la_SOURCES = \
- xfuncs.cpp \
+ xfuncs.h xfuncs.cpp \
encbase64.cpp \
read_write.cpp \
logging.cpp \
@@ -21,7 +23,10 @@ libABRTUtils_la_SOURCES = \
DebugDump.h DebugDump.cpp \
abrt_dbus.h abrt_dbus.cpp \
CrashTypes.cpp \
- ABRTException.cpp
+ ABRTException.cpp \
+ backtrace.h backtrace.c \
+ backtrace_parser.y \
+ strbuf.h strbuf.c
libABRTUtils_la_CPPFLAGS = \
-Wall -Werror \
-I$(srcdir)/../../inc \
diff --git a/src/Backtrace/backtrace.c b/lib/Utils/backtrace.c
index 9b697da1..746a1cd0 100644
--- a/src/Backtrace/backtrace.c
+++ b/lib/Utils/backtrace.c
@@ -1,4 +1,4 @@
-/* -*-mode:c++;c-file-style:"bsd";c-basic-offset:2;indent-tabs-mode:nil-*-
+/* -*-mode:c;c-file-style:"bsd";c-basic-offset:4;indent-tabs-mode:nil-*-
Copyright (C) 2009 RedHat inc.
This program is free software; you can redistribute it and/or modify
@@ -16,8 +16,11 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "backtrace.h"
+#include "strbuf.h"
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
+#include "xfuncs.h"
struct frame *frame_new()
{
@@ -55,26 +58,27 @@ struct frame *frame_add_sibling(struct frame *a, struct frame *b)
return a;
}
-static void frame_print_tree(struct frame *frame, bool verbose)
+/* Appends a string representation of 'frame' to the 'str'. */
+static void frame_append_str(struct frame *frame, struct strbuf *str, bool verbose)
{
- if (verbose)
- printf(" #%d", frame->number);
- else
- printf(" ");
+ if (verbose)
+ strbuf_append_strf(str, " #%d", frame->number);
+ else
+ strbuf_append_str(str, " ");
- if (frame->function)
- printf(" %s", frame->function);
- if (frame->sourcefile)
- {
if (frame->function)
- printf(" at");
- printf(" %s", frame->sourcefile);
- }
-
- if (frame->signal_handler_called)
- printf(" <signal handler called>");
+ strbuf_append_strf(str, " %s", frame->function);
+ if (verbose && frame->sourcefile)
+ {
+ if (frame->function)
+ strbuf_append_str(str, " at");
+ strbuf_append_strf(str, " %s", frame->sourcefile);
+ }
- puts(""); /* newline */
+ if (frame->signal_handler_called)
+ strbuf_append_str(str, " <signal handler called>");
+
+ strbuf_append_str(str, "\n");
}
static bool frame_is_exit_handler(struct frame *frame)
@@ -236,19 +240,20 @@ static int thread_get_frame_count(struct thread *thread)
return count;
}
-static void thread_print_tree(struct thread *thread, bool verbose)
+/* Appends string representation of 'thread' to the 'str'. */
+static void thread_append_str(struct thread *thread, struct strbuf *str, bool verbose)
{
- int framecount = thread_get_frame_count(thread);
- 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;
- }
+ int framecount = thread_get_frame_count(thread);
+ if (verbose)
+ strbuf_append_strf(str, "Thread no. %d (%d frames)\n", thread->number, framecount);
+ else
+ strbuf_append_str(str, "Thread\n");
+ struct frame *frame = thread->frames;
+ while (frame)
+ {
+ frame_append_str(frame, str, verbose);
+ frame = frame->next;
+ }
}
/*
@@ -258,7 +263,7 @@ static void thread_print_tree(struct thread *thread, bool verbose)
* one is returned.
* Nonrecursive.
*/
-static struct frame *thread_find_abort_frame(struct thread *thread)
+struct frame *thread_find_abort_frame(struct thread *thread)
{
struct frame *frame = thread->frames;
struct frame *result = NULL;
@@ -294,7 +299,7 @@ static void thread_remove_exit_handlers(struct thread *thread)
}
}
-void thread_remove_noncrash_frames(struct thread *thread)
+static void thread_remove_noncrash_frames(struct thread *thread)
{
struct frame *prev = NULL;
struct frame *cur = thread->frames;
@@ -327,6 +332,28 @@ void thread_remove_noncrash_frames(struct thread *thread)
}
}
+/* Counts the number of quality frames and the number of all frames
+ * in a thread.
+ * @param ok_count
+ * @param all_count
+ * Not zeroed. This function just adds the numbers to
+ * ok_count and all_count.
+ */
+static void thread_rating(struct thread *thread, int *ok_count, int *all_count)
+{
+ struct frame *frame = thread->frames;
+ while (frame)
+ {
+ *all_count += 1;
+ if (frame->signal_handler_called ||
+ (frame->function && 0 != strcmp(frame->function, "??")))
+ {
+ *ok_count += 1;
+ }
+ frame = frame->next;
+ }
+}
+
struct backtrace *backtrace_new()
{
struct backtrace *bt = malloc(sizeof(struct backtrace));
@@ -370,21 +397,31 @@ static int backtrace_get_thread_count(struct backtrace *bt)
void backtrace_print_tree(struct backtrace *backtrace, bool verbose)
{
- if (verbose)
- printf("Thread count: %d\n", backtrace_get_thread_count(backtrace));
+ struct strbuf *strbuf = backtrace_tree_as_str(backtrace, verbose);
+ puts(strbuf->buf);
+ strbuf_free(strbuf);
+}
- if (backtrace->crash && verbose)
- {
- printf("Crash frame: ");
- frame_print_tree(backtrace->crash, verbose);
- }
+struct strbuf *backtrace_tree_as_str(struct backtrace *backtrace, bool verbose)
+{
+ struct strbuf *str = strbuf_new();
+ if (verbose)
+ strbuf_append_strf(str, "Thread count: %d\n", backtrace_get_thread_count(backtrace));
- struct thread *thread = backtrace->threads;
- while (thread)
- {
- thread_print_tree(thread, verbose);
- thread = thread->next;
- }
+ if (backtrace->crash && verbose)
+ {
+ strbuf_append_str(str, "Crash frame: ");
+ frame_append_str(backtrace->crash, str, verbose);
+ }
+
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ thread_append_str(thread, str, verbose);
+ thread = thread->next;
+ }
+
+ return str;
}
void backtrace_remove_threads_except_one(struct backtrace *backtrace,
@@ -528,6 +565,282 @@ void backtrace_remove_noncrash_frames(struct backtrace *backtrace)
{
thread_remove_noncrash_frames(thread);
thread = thread->next;
- }
+ }
+}
+
+/* Belongs to independent_backtrace(). */
+struct header
+{
+ struct strbuf *text;
+ struct header *next;
+};
+
+/* Belongs to independent_backtrace(). */
+static struct header *header_new()
+{
+ struct header *head = malloc(sizeof(struct header));
+ if (!head)
+ {
+ puts("Error while allocating memory for backtrace header.");
+ exit(5);
+ }
+ head->text = NULL;
+ head->next = NULL;
+ return head;
+}
+
+/* Recursively frees siblings. */
+/* Belongs to independent_backtrace(). */
+static void header_free(struct header *head)
+{
+ if (head->text)
+ strbuf_free(head->text);
+ if (head->next)
+ header_free(head->next);
+ free(head);
+}
+
+/* Inserts new header to array if it is not already there. */
+/* Belongs to independent_backtrace(). */
+static void header_set_insert(struct header *cur, struct strbuf *new)
+{
+ /* Duplicate found case. */
+ if (strcmp(cur->text->buf, new->buf) == 0)
+ return;
+
+ /* Last item case, insert new header here. */
+ if (cur->next == NULL)
+ {
+ cur->next = header_new();
+ cur->next->text = new;
+ return;
+ }
+
+ /* Move to next item in array case. */
+ header_set_insert(cur->next, new);
}
+struct strbuf *independent_backtrace(const char *input)
+{
+ struct strbuf *header = strbuf_new();
+ bool in_bracket = false;
+ bool in_quote = false;
+ bool in_header = false;
+ bool in_digit = false;
+ bool has_at = false;
+ bool has_filename = false;
+ bool has_bracket = false;
+ struct header *headers = NULL;
+
+ const char *bk = input;
+ while (*bk)
+ {
+ if (bk[0] == '#'
+ && bk[1] >= '0' && bk[1] <= '7'
+ && bk[2] == ' ' /* take only #0...#7 (8 last stack frames) */
+ && !in_quote)
+ {
+ if (in_header && !has_filename)
+ strbuf_clear(header);
+ in_header = true;
+ }
+
+ if (!in_header)
+ {
+ ++bk;
+ continue;
+ }
+
+ if (isdigit(*bk) && !in_quote && !has_at)
+ in_digit = true;
+ else if (bk[0] == '\\' && bk[1] == '\"')
+ bk++;
+ else if (*bk == '\"')
+ in_quote = in_quote == true ? false : true;
+ else if (*bk == '(' && !in_quote)
+ {
+ in_bracket = true;
+ in_digit = false;
+ strbuf_append_char(header, '(');
+ }
+ else if (*bk == ')' && !in_quote)
+ {
+ in_bracket = false;
+ has_bracket = true;
+ in_digit = false;
+ strbuf_append_char(header, '(');
+ }
+ else if (*bk == '\n' && has_filename)
+ {
+ if (headers == NULL)
+ {
+ headers = header_new();
+ headers->text = header;
+ }
+ else
+ header_set_insert(headers, header);
+
+ header = strbuf_new();
+ in_bracket = false;
+ in_quote = false;
+ in_header = false;
+ in_digit = false;
+ has_at = false;
+ has_filename = false;
+ has_bracket = false;
+ }
+ else if (*bk == ',' && !in_quote)
+ in_digit = false;
+ else if (isspace(*bk) && !in_quote)
+ in_digit = false;
+ else if (bk[0] == 'a' && bk[1] == 't' && has_bracket && !in_quote)
+ {
+ has_at = true;
+ strbuf_append_char(header, 'a');
+ }
+ else if (bk[0] == ':' && has_at && isdigit(bk[1]) && !in_quote)
+ has_filename = true;
+ else if (in_header && !in_digit && !in_quote && !in_bracket)
+ strbuf_append_char(header, *bk);
+
+ bk++;
+ }
+
+ strbuf_free(header);
+
+ struct strbuf *result = strbuf_new();
+ struct header *loop = headers;
+ while (loop)
+ {
+ strbuf_append_str(result, loop->text->buf);
+ strbuf_append_char(result, '\n');
+ loop = loop->next;
+ }
+
+ if (headers)
+ header_free(headers); /* recursive */
+
+ return result;
+}
+
+/* Belongs to backtrace_rate_old(). */
+enum line_rating
+{
+ // RATING EXAMPLE
+ MissingEverything = 0, // #0 0x0000dead in ?? ()
+ MissingFunction = 1, // #0 0x0000dead in ?? () from /usr/lib/libfoobar.so.4
+ MissingLibrary = 2, // #0 0x0000dead in foobar()
+ MissingSourceFile = 3, // #0 0x0000dead in FooBar::FooBar () from /usr/lib/libfoobar.so.4
+ Good = 4, // #0 0x0000dead in FooBar::crash (this=0x0) at /home/user/foobar.cpp:204
+ BestRating = Good,
+};
+
+/* Belongs to backtrace_rate_old(). */
+static enum line_rating rate_line(const char *line)
+{
+#define FOUND(x) (strstr(line, x) != NULL)
+ /* see the comments at enum line_rating for possible combinations */
+ if (FOUND(" at "))
+ return Good;
+ const char *function = strstr(line, " in ");
+ if (function && function[4] == '?') /* " in ??" does not count */
+ function = NULL;
+ bool library = FOUND(" from ");
+ if (function && library)
+ return MissingSourceFile;
+ if (function)
+ return MissingLibrary;
+ if (library)
+ return MissingFunction;
+
+ return MissingEverything;
+#undef FOUND
+}
+
+/* just a fallback function, to be removed one day */
+int backtrace_rate_old(const char *backtrace)
+{
+ int i, len;
+ int multiplier = 0;
+ int rating = 0;
+ int best_possible_rating = 0;
+ char last_lvl = 0;
+
+ /* We look at the frames in reversed order, since:
+ * - rate_line() checks starting from the first line of the frame
+ * (note: it may need to look at more than one line!)
+ * - we increase weight (multiplier) for every frame,
+ * so that topmost frames end up most important
+ */
+ len = 0;
+ for (i = strlen(backtrace) - 1; i >= 0; i--)
+ {
+ if (backtrace[i] == '#'
+ && (backtrace[i+1] >= '0' && backtrace[i+1] <= '9') /* #N */
+ && (i == 0 || backtrace[i-1] == '\n')) /* it's at line start */
+ {
+ /* For one, "#0 xxx" always repeats, skip repeats */
+ if (backtrace[i+1] == last_lvl)
+ continue;
+ last_lvl = backtrace[i+1];
+
+ char *s = xstrndup(backtrace + i + 1, len);
+ /* Replace tabs with spaces, rate_line() does not expect tabs.
+ * Actually, even newlines may be there. Example of multiline frame
+ * where " at SRCFILE" is on 2nd line:
+ * #3 0x0040b35d in __libc_message (do_abort=<value optimized out>,
+ * fmt=<value optimized out>) at ../sysdeps/unix/sysv/linux/libc_fatal.c:186
+ */
+ char *p;
+ for (p = s; *p; p++)
+ {
+ if (*p == '\t' || *p == '\n')
+ *p = ' ';
+ }
+ int lrate = rate_line(s);
+ multiplier++;
+ rating += lrate * multiplier;
+ best_possible_rating += BestRating * multiplier;
+ //log("lrate:%d rating:%d best_possible_rating:%d s:'%-.40s'",
+ // lrate, rating, best_possible_rating, s);
+ free(s);
+ len = 0; /* starting new line */
+ }
+ else
+ {
+ len++;
+ }
+ }
+
+ /* Bogus 'backtrace' with zero frames? */
+ if (best_possible_rating == 0)
+ return 0;
+
+ /* Returning number of "stars" to show */
+ if (rating*10 >= best_possible_rating*8) /* >= 0.8 */
+ return 4;
+ if (rating*10 >= best_possible_rating*6)
+ return 3;
+ if (rating*10 >= best_possible_rating*4)
+ return 2;
+ if (rating*10 >= best_possible_rating*2)
+ return 1;
+
+ return 0;
+}
+
+float backtrace_quality(struct backtrace *backtrace)
+{
+ int ok_count = 0;
+ int all_count = 0;
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ thread_rating(thread, &ok_count, &all_count);
+ thread = thread->next;
+ }
+
+ if (all_count == 0)
+ return 0;
+ return ok_count / (float)all_count;
+}
diff --git a/src/Backtrace/backtrace.h b/lib/Utils/backtrace.h
index 93897e93..dd6d69c2 100644
--- a/src/Backtrace/backtrace.h
+++ b/lib/Utils/backtrace.h
@@ -1,5 +1,13 @@
/*
- Copyright (C) 2009 RedHat inc.
+ Backtrace parsing and processing.
+
+ If we transform analyzer plugins to separate applications one day,
+ this functionality should be moved to CCpp analyzer, which will
+ then easily provide what abrt-backtrace utility provides now. Currently
+ the code is used by abrt-backtrace, so it is shared in the utils
+ library.
+
+ Copyright (C) 2009, 2010 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
@@ -18,6 +26,10 @@
#ifndef BACKTRACE_H
#define BACKTRACE_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <stdio.h>
#include <stdbool.h>
@@ -60,6 +72,7 @@ extern struct frame *frame_add_sibling(struct frame *a, struct frame *b);
extern struct thread *thread_new();
extern void thread_free(struct thread *t);
extern struct thread *thread_add_sibling(struct thread *a, struct thread *b);
+extern struct frame *thread_find_abort_frame(struct thread *thread);
extern struct backtrace *backtrace_new();
extern void backtrace_free(struct backtrace *bt);
@@ -67,6 +80,9 @@ extern void backtrace_free(struct backtrace *bt);
/* Prints how internal backtrace representation looks to stdout. */
extern void backtrace_print_tree(struct backtrace *backtrace, bool verbose);
+/* Returns the backtrace tree string representation. */
+extern struct strbuf *backtrace_tree_as_str(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.
@@ -94,7 +110,43 @@ extern void backtrace_remove_exit_handlers(struct backtrace *backtrace);
*/
extern void backtrace_remove_noncrash_frames(struct backtrace *backtrace);
-/* Defined in parser.y. */
-extern struct backtrace *do_parse(char *input, bool debug_parser, bool debug_scanner);
+/* Parses the backtrace and stores it to a structure.
+ * @returns
+ * Returns the backtrace struct representation, or NULL if the parser failed.
+ * Caller of this function is responsible for backtrace_free()ing the returned value.
+ * Defined in backtrace_parser.y.
+ */
+extern struct backtrace *backtrace_parse(char *input, bool debug_parser, bool debug_scanner);
+
+/* Reads the input file and calculates "independent" backtrace from it. "Independent" means
+ * that the memory addresses that differ from run to run are removed from the backtrace, and
+ * also variable names and values are removed.
+ *
+ * This function can be called when backtrace_parse() call fails. It provides a shorter
+ * version of backtrace, with a chance that hash calculated from the returned value can be used
+ * to detect duplicates. However, this kind of duplicate detection is very low-quality.
+ * @returns
+ * The independent backtrace. Caller is responsible for calling
+ * strbuf_free() on it.
+ */
+extern struct strbuf *independent_backtrace(const char *input);
+
+/* Get the quality of backtrace, as a number of "stars".
+ * @returns
+ * Value 0 to 4.
+ */
+extern int backtrace_rate_old(const char *backtrace);
+
+/* Evaluates the quality of the backtrace, meaning the ratio of frames
+ * with function name fully known to all frames.
+ * @returns
+ * A number between 0 and 1. 0 means the lowest quality,
+ * 1 means full backtrace is known.
+ */
+extern float backtrace_quality(struct backtrace *backtrace);
+
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/src/Backtrace/parser.y b/lib/Utils/backtrace_parser.y
index 42ac2206..de81a3eb 100644
--- a/src/Backtrace/parser.y
+++ b/lib/Utils/backtrace_parser.y
@@ -578,11 +578,85 @@ int yylex()
* backtrace_free() on this.
* Returns NULL when parsing failed.
*/
-struct backtrace *do_parse(char *input, bool debug_parser, bool debug_scanner)
+struct backtrace *backtrace_parse(char *input, bool debug_parser, bool debug_scanner)
{
+ /* Skip the backtrace header information. */
+ char *btnoheader_a = strstr(input, "\nThread ");
+ char *btnoheader_b = strstr(input, "\n#");
+ char *btnoheader = input;
+ if (btnoheader_a)
+ {
+ if (btnoheader_b && btnoheader_b < btnoheader_a)
+ btnoheader = btnoheader_b + 1;
+ else
+ btnoheader = btnoheader_a + 1;
+ }
+ else if (btnoheader_b)
+ btnoheader = btnoheader_b + 1;
+
+ /* Bug fixing hack for broken backtraces.
+ * Sometimes the empty line is missing before new Thread section.
+ * This is against rules, but a bug (now fixed) in Linux kernel caused
+ * this.
+ */
+ char *thread_fixer = btnoheader + 1;
+ while ((thread_fixer = strstr(thread_fixer, "\nThread")) != NULL)
+ {
+ if (thread_fixer[-1] != '\n')
+ thread_fixer[-1] = '\n';
+
+ ++thread_fixer;
+ }
+
+ /* Bug fixing hack for GDB - remove wrongly placed newlines from the backtrace.
+ * Sometimes there is a newline in the local variable section.
+ * This is caused by some GDB hooks.
+ * Example: rhbz#538440
+ * #1 0x0000000000420939 in sync_deletions (mse=0x0, mfld=0x1b85020)
+ * at mail-stub-exchange.c:1119
+ * status = <value optimized out>
+ * iter = 0x1af38d0
+ * known_messages = 0x1b5c460Traceback (most recent call last):
+ * File "/usr/share/glib-2.0/gdb/glib.py", line 98, in next
+ * if long (node["key_hash"]) >= 2:
+ * RuntimeError: Cannot access memory at address 0x11
+ *
+ * __PRETTY_FUNCTION__ = "sync_deletions"
+ * #2 0x0000000000423e6b in refresh_folder (stub=0x1b77f10 [MailStubExchange],
+ * ...
+ *
+ * The code removes every empty line (also those containing only spaces),
+ * which is not followed by a new Thread section.
+ *
+ * rhbz#555251 contains empty lines with spaces
+ */
+ char *empty_line = btnoheader;
+ char *c = btnoheader;
+ while (*c)
+ {
+ if (*c == '\n')
+ {
+ char *cend = c + 1;
+ while (*cend == ' ' || *cend == '\t')
+ ++cend;
+ if (*cend == '\n' && 0 != strncmp(cend, "\nThread", strlen("\nThread")))
+ memmove(c, cend, strlen(cend) + 1);
+ }
+ ++c;
+ }
+ while ((empty_line = strstr(empty_line, "\n\n")) != NULL)
+ {
+ if (0 != strncmp(empty_line, "\n\nThread", strlen("\n\nThread")))
+ {
+ /* Remove the empty line by converting the first newline to char. */
+ empty_line[0] = 'X';
+ }
+ ++empty_line;
+ }
+
/* Prepare for running parser. */
g_backtrace = 0;
- yyin = input;
+ yyin = btnoheader;
#if YYDEBUG == 1
if (debug_parser)
yydebug = 1;
diff --git a/lib/Utils/strbuf.c b/lib/Utils/strbuf.c
new file mode 100644
index 00000000..6153ffe1
--- /dev/null
+++ b/lib/Utils/strbuf.c
@@ -0,0 +1,130 @@
+/* -*-mode:c;c-file-style:"bsd";c-basic-offset:4;indent-tabs-mode:nil-*-
+ String buffer implementation
+
+ 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 "strbuf.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+#include "xfuncs.h"
+
+struct strbuf *strbuf_new()
+{
+ struct strbuf *buf = xmalloc(sizeof(struct strbuf));
+ buf->alloc = 8;
+ buf->len = 0;
+ buf->buf = xmalloc(buf->alloc);
+ buf->buf[buf->len] = '\0';
+ return buf;
+}
+
+void strbuf_free(struct strbuf *strbuf)
+{
+ free(strbuf->buf);
+ free(strbuf);
+}
+
+void strbuf_free_nobuf(struct strbuf *strbuf)
+{
+ free(strbuf);
+}
+
+
+void strbuf_clear(struct strbuf *strbuf)
+{
+ assert(strbuf->alloc > 0);
+ strbuf->len = 0;
+ strbuf->buf[0] = '\0';
+}
+
+/* Ensures that the buffer can be extended by num characters
+ * without touching malloc/realloc.
+ */
+static void strbuf_grow(struct strbuf *strbuf, int num)
+{
+ if (strbuf->len + num + 1 > strbuf->alloc)
+ {
+ while (strbuf->len + num + 1 > strbuf->alloc)
+ strbuf->alloc *= 2; /* huge grow = infinite loop */
+
+ strbuf->buf = realloc(strbuf->buf, strbuf->alloc);
+ if (!strbuf->buf)
+ {
+ puts("Error while allocating memory for string buffer.");
+ exit(5);
+ }
+ }
+}
+
+struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c)
+{
+ strbuf_grow(strbuf, 1);
+ strbuf->buf[strbuf->len++] = c;
+ strbuf->buf[strbuf->len] = '\0';
+ return strbuf;
+}
+
+struct strbuf *strbuf_append_str(struct strbuf *strbuf, const char *str)
+{
+ int len = strlen(str);
+ strbuf_grow(strbuf, len);
+ assert(strbuf->len + len < strbuf->alloc);
+ strcpy(strbuf->buf + strbuf->len, str);
+ strbuf->len += len;
+ return strbuf;
+}
+
+struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, const char *str)
+{
+ int len = strlen(str);
+ strbuf_grow(strbuf, len);
+ assert(strbuf->len + len < strbuf->alloc);
+ memmove(strbuf->buf + len, strbuf->buf, strbuf->len + 1);
+ memcpy(strbuf->buf, str, len);
+ return strbuf;
+}
+
+struct strbuf *strbuf_append_strf(struct strbuf *strbuf, const char *format, ...)
+{
+ va_list p;
+ char *string_ptr;
+
+ va_start(p, format);
+ string_ptr = xvasprintf(format, p);
+ va_end(p);
+
+ strbuf_append_str(strbuf, string_ptr);
+ free(string_ptr);
+ return strbuf;
+}
+
+struct strbuf *strbuf_prepend_strf(struct strbuf *strbuf, const char *format, ...)
+{
+ va_list p;
+ char *string_ptr;
+
+ va_start(p, format);
+ string_ptr = xvasprintf(format, p);
+ va_end(p);
+
+ strbuf_prepend_str(strbuf, string_ptr);
+ free(string_ptr);
+ return strbuf;
+}
diff --git a/src/Backtrace/strbuf.h b/lib/Utils/strbuf.h
index f2e4459e..d0c9c981 100644
--- a/src/Backtrace/strbuf.h
+++ b/lib/Utils/strbuf.h
@@ -20,6 +20,10 @@
#ifndef STRBUF_H
#define STRBUF_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct strbuf {
int alloc;
int len;
@@ -32,7 +36,13 @@ extern void strbuf_free(struct strbuf *strbuf);
extern void strbuf_free_nobuf(struct strbuf *strbuf);
extern void strbuf_clear(struct strbuf *strbuf);
extern struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c);
-extern struct strbuf *strbuf_append_str(struct strbuf *strbuf, char *str);
-extern struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, char *str);
+extern struct strbuf *strbuf_append_str(struct strbuf *strbuf, const char *str);
+extern struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, const char *str);
+extern struct strbuf *strbuf_append_strf(struct strbuf *strbuf, const char *format, ...);
+extern struct strbuf *strbuf_prepend_strf(struct strbuf *strbuf, const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/lib/Utils/xfuncs.cpp b/lib/Utils/xfuncs.cpp
index 74fdac76..fd29b704 100644
--- a/lib/Utils/xfuncs.cpp
+++ b/lib/Utils/xfuncs.cpp
@@ -389,12 +389,12 @@ bool string_to_bool(const char *s)
void xsetreuid(uid_t ruid, uid_t euid)
{
- if (setreuid(ruid, euid) != 0)
- perror_msg_and_die("can't set %cid %lu", 'u', (long)ruid);
+ if (setreuid(ruid, euid) != 0)
+ perror_msg_and_die("can't set %cid %lu", 'u', (long)ruid);
}
void xsetregid(gid_t rgid, gid_t egid)
{
- if (setregid(rgid, egid) != 0)
- perror_msg_and_die("can't set %cid %lu", 'g', (long)rgid);
+ if (setregid(rgid, egid) != 0)
+ perror_msg_and_die("can't set %cid %lu", 'g', (long)rgid);
}
diff --git a/lib/Utils/xfuncs.h b/lib/Utils/xfuncs.h
new file mode 100644
index 00000000..327d9c9a
--- /dev/null
+++ b/lib/Utils/xfuncs.h
@@ -0,0 +1,104 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 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.
+*/
+#ifndef ABRT_XFUNCS_H
+#define ABRT_XFUNCS_H
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdbool.h>
+
+int ndelay_on(int fd);
+int ndelay_off(int fd);
+int close_on_exec_on(int fd);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void* xmalloc(size_t size);
+void* xrealloc(void *ptr, size_t size);
+void* xzalloc(size_t size);
+char* xstrdup(const char *s);
+char* xstrndup(const char *s, int n);
+
+#ifdef __cplusplus
+}
+#endif
+
+void xpipe(int filedes[2]);
+void xdup(int from);
+void xdup2(int from, int to);
+void xmove_fd(int from, int to);
+
+extern void xwrite(int fd, const void *buf, size_t count);
+extern void xwrite_str(int fd, const char *str);
+
+off_t xlseek(int fd, off_t offset, int whence);
+
+void xchdir(const char *path);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char* xvasprintf(const char *format, va_list p);
+char* xasprintf(const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+std::string ssprintf(const char *format, ...);
+#endif
+
+void xsetenv(const char *key, const char *value);
+int xsocket(int domain, int type, int protocol);
+void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
+void xlisten(int s, int backlog);
+ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, socklen_t tolen);
+void xstat(const char *name, struct stat *stat_buf);
+
+#ifdef __cplusplus
+std::string get_home_dir(uid_t uid);
+#endif
+
+int xopen3(const char *pathname, int flags, int mode);
+int xopen(const char *pathname, int flags);
+void xunlink(const char *pathname);
+
+/* Just testing dent->d_type == DT_REG is wrong: some filesystems
+ * do not report the type, they report DT_UNKNOWN for every dirent
+ * (and this is not a bug in filesystem, this is allowed by standards).
+ * This function handles this case. Note: it returns 0 on symlinks
+ * even if they point to regular files.
+ */
+int is_regular_file(struct dirent *dent, const char *dirname);
+bool dot_or_dotdot(const char *filename);
+char *last_char_is(const char *s, int c);
+#ifdef __cplusplus
+std::string concat_path_file(const char *path, const char *filename);
+#endif
+bool string_to_bool(const char *s);
+
+void xsetreuid(uid_t ruid, uid_t euid);
+void xsetregid(gid_t rgid, gid_t egid);
+
+#endif
diff --git a/src/Backtrace/abrt-bz-downloader b/scripts/abrt-bz-downloader
index 7f294257..7f294257 100755
--- a/src/Backtrace/abrt-bz-downloader
+++ b/scripts/abrt-bz-downloader
diff --git a/src/Backtrace/abrt-bz-dupchecker b/scripts/abrt-bz-dupchecker
index 654a3702..654a3702 100755
--- a/src/Backtrace/abrt-bz-dupchecker
+++ b/scripts/abrt-bz-dupchecker
diff --git a/src/Backtrace/abrt-bz-hashchecker b/scripts/abrt-bz-hashchecker
index ec7ce1a6..ec7ce1a6 100755
--- a/src/Backtrace/abrt-bz-hashchecker
+++ b/scripts/abrt-bz-hashchecker
diff --git a/src/Backtrace/check-bt-parsability b/scripts/check-bt-parsability
index a5018bfa..a5018bfa 100755
--- a/src/Backtrace/check-bt-parsability
+++ b/scripts/check-bt-parsability
diff --git a/src/Backtrace/Makefile.am b/src/Backtrace/Makefile.am
deleted file mode 100644
index c39575ca..00000000
--- a/src/Backtrace/Makefile.am
+++ /dev/null
@@ -1,24 +0,0 @@
-bin_PROGRAMS = abrt-backtrace
-
-#BUILT_SOURCES = parser.h
-#AM_YFLAGS = -d
-AM_YFLAGS = --verbose
-
-abrt_backtrace_CFLAGS = -Wall
-
-abrt_backtrace_SOURCES = \
- main.c \
- backtrace.h backtrace.c \
- strbuf.h strbuf.c \
- fallback.h fallback.c \
- parser.y
-
-#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/Backtrace/fallback.c b/src/Backtrace/fallback.c
deleted file mode 100644
index 77a16283..00000000
--- a/src/Backtrace/fallback.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- 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 "fallback.h"
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-
-struct header
-{
- struct strbuf *text;
- struct header *next;
-};
-
-static struct header *header_new()
-{
- struct header *head = malloc(sizeof(struct header));
- if (!head)
- {
- puts("Error while allocating memory for backtrace header.");
- exit(5);
- }
- head->text = NULL;
- head->next = NULL;
- return head;
-}
-
-/* Recursively frees siblings. */
-static void header_free(struct header *head)
-{
- if (head->text)
- strbuf_free(head->text);
- if (head->next)
- header_free(head->next);
- free(head);
-}
-
-/* Inserts new header to array if it is not already there. */
-static void header_set_insert(struct header *cur, struct strbuf *new)
-{
- /* Duplicate found case. */
- if (strcmp(cur->text->buf, new->buf) == 0)
- return;
-
- /* Last item case, insert new header here. */
- if (cur->next == NULL)
- {
- cur->next = header_new();
- cur->next->text = new;
- return;
- }
-
- /* Move to next item in array case. */
- header_set_insert(cur->next, new);
-}
-
-struct strbuf *independent_backtrace(char *input)
-{
- struct strbuf *header = strbuf_new();
- bool in_bracket = false;
- bool in_quote = false;
- bool in_header = false;
- bool in_digit = false;
- bool has_at = false;
- bool has_filename = false;
- bool has_bracket = false;
- struct header *headers = NULL;
-
- const char *bk = input;
- while (*bk)
- {
- if (bk[0] == '#'
- && bk[1] >= '0' && bk[1] <= '7'
- && bk[2] == ' ' /* take only #0...#7 (8 last stack frames) */
- && !in_quote)
- {
- if (in_header && !has_filename)
- strbuf_clear(header);
- in_header = true;
- }
-
- if (!in_header)
- {
- ++bk;
- continue;
- }
-
- if (isdigit(*bk) && !in_quote && !has_at)
- in_digit = true;
- else if (bk[0] == '\\' && bk[1] == '\"')
- bk++;
- else if (*bk == '\"')
- in_quote = in_quote == true ? false : true;
- else if (*bk == '(' && !in_quote)
- {
- in_bracket = true;
- in_digit = false;
- strbuf_append_char(header, '(');
- }
- else if (*bk == ')' && !in_quote)
- {
- in_bracket = false;
- has_bracket = true;
- in_digit = false;
- strbuf_append_char(header, '(');
- }
- else if (*bk == '\n' && has_filename)
- {
- if (headers == NULL)
- {
- headers = header_new();
- headers->text = header;
- }
- else
- header_set_insert(headers, header);
-
- header = strbuf_new();
- in_bracket = false;
- in_quote = false;
- in_header = false;
- in_digit = false;
- has_at = false;
- has_filename = false;
- has_bracket = false;
- }
- else if (*bk == ',' && !in_quote)
- in_digit = false;
- else if (isspace(*bk) && !in_quote)
- in_digit = false;
- else if (bk[0] == 'a' && bk[1] == 't' && has_bracket && !in_quote)
- {
- has_at = true;
- strbuf_append_char(header, 'a');
- }
- else if (bk[0] == ':' && has_at && isdigit(bk[1]) && !in_quote)
- has_filename = true;
- else if (in_header && !in_digit && !in_quote && !in_bracket)
- strbuf_append_char(header, *bk);
-
- bk++;
- }
-
- strbuf_free(header);
-
- struct strbuf *result = strbuf_new();
- struct header *loop = headers;
- while (loop)
- {
- strbuf_append_str(result, loop->text->buf);
- strbuf_append_char(result, '\n');
- loop = loop->next;
- }
-
- if (headers)
- header_free(headers); /* recursive */
-
- return result;
-}
diff --git a/src/Backtrace/fallback.h b/src/Backtrace/fallback.h
deleted file mode 100644
index 85b06320..00000000
--- a/src/Backtrace/fallback.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- 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.
-*/
-#ifndef FALLBACK_H
-#define FALLBACK_H
-
-#include "strbuf.h"
-#include <stdio.h>
-
-/*
- * Reads the input file and calculates independent backtrace from it.
- * @returns
- * The independent backtrace. Caller is responsible for calling
- * strbuf_free() on it.
- */
-extern struct strbuf *independent_backtrace(char *input);
-
-#endif
diff --git a/src/Backtrace/main.c b/src/Backtrace/main.c
deleted file mode 100644
index cddf8979..00000000
--- a/src/Backtrace/main.c
+++ /dev/null
@@ -1,361 +0,0 @@
-/* -*-mode:c++;c-file-style:"bsd";c-basic-offset:2;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 "fallback.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"},
- {"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;
- 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 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;
- argp_parse(&argp, argc, argv, 0, 0, &arguments);
-
- 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)
- {
- 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 */
- }
-
- /* Skip the backtrace header information. */
- char *btnoheader_a = strstr(bttext, "\nThread ");
- char *btnoheader_b = strstr(bttext, "\n#");
- char *btnoheader = bttext;
- if (btnoheader_a)
- {
- if (btnoheader_b && btnoheader_b < btnoheader_a)
- btnoheader = btnoheader_b + 1;
- else
- btnoheader = btnoheader_a + 1;
- }
- else if (btnoheader_b)
- btnoheader = btnoheader_b + 1;
-
- /* Bug fixing hack for broken backtraces.
- * Sometimes the empty line is missing before new Thread section.
- * This is against rules, but a bug (now fixed) in Linux kernel caused
- * this.
- */
- char *thread_fixer = btnoheader + 1;
- while ((thread_fixer = strstr(thread_fixer, "\nThread")) != NULL)
- {
- if (thread_fixer[-1] != '\n')
- thread_fixer[-1] = '\n';
-
- ++thread_fixer;
- }
-
- /* Bug fixing hack for GDB - remove wrongly placed newlines from the backtrace.
- * Sometimes there is a newline in the local variable section.
- * This is caused by some GDB hooks.
- * Example: rhbz#538440
- * #1 0x0000000000420939 in sync_deletions (mse=0x0, mfld=0x1b85020)
- * at mail-stub-exchange.c:1119
- * status = <value optimized out>
- * iter = 0x1af38d0
- * known_messages = 0x1b5c460Traceback (most recent call last):
- * File "/usr/share/glib-2.0/gdb/glib.py", line 98, in next
- * if long (node["key_hash"]) >= 2:
- * RuntimeError: Cannot access memory at address 0x11
- *
- * __PRETTY_FUNCTION__ = "sync_deletions"
- * #2 0x0000000000423e6b in refresh_folder (stub=0x1b77f10 [MailStubExchange],
- * ...
- *
- * The code removes every empty line (also those containing only spaces),
- * which is not followed by a new Thread section.
- *
- * rhbz#555251 contains empty lines with spaces
- */
- char *empty_line = btnoheader;
- char *c = btnoheader;
- while (*c)
- {
- if (*c == '\n')
- {
- char *cend = c + 1;
- while (*cend == ' ' || *cend == '\t')
- ++cend;
- if (*cend == '\n' && 0 != strncmp(cend, "\nThread", strlen("\nThread")))
- memmove(c, cend, strlen(cend) + 1);
- }
- ++c;
- }
- while ((empty_line = strstr(empty_line, "\n\n")) != NULL)
- {
- if (0 != strncmp(empty_line, "\n\nThread", strlen("\n\nThread")))
- {
- /* Remove the empty line by converting the first newline to char. */
- empty_line[0] = 'X';
- }
- ++empty_line;
- }
-
- /* Cut the backtrace footer.
- * Footer: lines not starting with # or "Thread", and separated from
- * the backtrace body by a newline.
- */
- /* It is not necessary for now, because of the bug fixing hack for GDB.
- int i;
- for (i = size - 1; i > 0; --i)
- {
- if (bttext[i] != '\n')
- continue;
- if (strncmp(bttext + i + 1, "Thread", strlen("Thread")) == 0)
- break;
- if (bttext[i + 1] == '#')
- break;
- if (bttext[i - 1] == '\n')
- {
- bttext[i] = '\0';
- break;
- }
- }*/
-
- /* Try to parse the backtrace. */
- struct backtrace *backtrace;
- backtrace = do_parse(btnoheader, arguments.debug_parser, arguments.debug_scanner);
-
- /* If the parser failed print independent backtrace. */
- if (!backtrace)
- {
- 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);
-
- /* 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;
- }
- }
-
- 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);
-
- backtrace_print_tree(backtrace, arguments.verbose);
- backtrace_free(backtrace);
- return retval;
-}
diff --git a/src/Backtrace/strbuf.c b/src/Backtrace/strbuf.c
deleted file mode 100644
index 4c6d886b..00000000
--- a/src/Backtrace/strbuf.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- strbuf.c - string buffer implementation
-
- 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 "strbuf.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <string.h>
-
-struct strbuf *strbuf_new()
-{
- struct strbuf *buf = malloc(sizeof(struct strbuf));
- if (!buf)
- {
- puts("Error while allocating memory for string buffer.");
- exit(5);
- }
-
- buf->alloc = 8;
- buf->len = 0;
- buf->buf = malloc(buf->alloc);
- if (!buf->buf)
- {
- puts("Error while allocating memory for string buffer.");
- exit(5);
- }
-
- buf->buf[buf->len] = '\0';
- return buf;
-}
-
-void strbuf_free(struct strbuf *strbuf)
-{
- free(strbuf->buf);
- free(strbuf);
-}
-
-void strbuf_free_nobuf(struct strbuf *strbuf)
-{
- free(strbuf);
-}
-
-
-void strbuf_clear(struct strbuf *strbuf)
-{
- assert(strbuf->alloc > 0);
- strbuf->len = 0;
- strbuf->buf[0] = '\0';
-}
-
-/* Ensures that the buffer can be extended by num characters
- * without touching malloc/realloc.
- */
-static void strbuf_grow(struct strbuf *strbuf, int num)
-{
- if (strbuf->len + num + 1 > strbuf->alloc)
- {
- while (strbuf->len + num + 1 > strbuf->alloc)
- strbuf->alloc *= 2; /* huge grow = infinite loop */
-
- strbuf->buf = realloc(strbuf->buf, strbuf->alloc);
- if (!strbuf->buf)
- {
- puts("Error while allocating memory for string buffer.");
- exit(5);
- }
- }
-}
-
-struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c)
-{
- strbuf_grow(strbuf, 1);
- strbuf->buf[strbuf->len++] = c;
- strbuf->buf[strbuf->len] = '\0';
- return strbuf;
-}
-
-struct strbuf *strbuf_append_str(struct strbuf *strbuf, char *str)
-{
- int len = strlen(str);
- strbuf_grow(strbuf, len);
- assert(strbuf->len + len < strbuf->alloc);
- strcpy(strbuf->buf + strbuf->len, str);
- strbuf->len += len;
- return strbuf;
-}
-
-struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, char *str)
-{
- int len = strlen(str);
- strbuf_grow(strbuf, len);
- assert(strbuf->len + len < strbuf->alloc);
- memmove(strbuf->buf + len, strbuf->buf, strbuf->len + 1);
- memcpy(strbuf->buf, str, len);
- return strbuf;
-}
diff --git a/src/Makefile.am b/src/Makefile.am
index dbd7ebbc..42266b31 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1 +1 @@
-SUBDIRS = Hooks Daemon Applet Gui CLI Backtrace
+SUBDIRS = Hooks Daemon Applet Gui CLI utils
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/Backtrace/abrt-backtrace.1 b/src/utils/abrt-backtrace.1
index ff7c2be2..ff7c2be2 100644
--- a/src/Backtrace/abrt-backtrace.1
+++ b/src/utils/abrt-backtrace.1
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;
+}