diff options
author | Karel Klic <kklic@redhat.com> | 2010-03-26 17:55:51 +0100 |
---|---|---|
committer | Karel Klic <kklic@redhat.com> | 2010-03-26 18:11:09 +0100 |
commit | 7ccd55eb10921e94a81b699a2c96cb1dc25515d1 (patch) | |
tree | 66297ccd0633eb5b8f817c3d710c36b267b3bc61 | |
parent | 605c31f9d0897c18a900c7a4c0ad75bc439d18e5 (diff) | |
download | abrt-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.ac | 3 | ||||
-rw-r--r-- | inc/abrtlib.h | 50 | ||||
-rw-r--r-- | lib/Utils/Makefile.am | 9 | ||||
-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.c | 130 | ||||
-rw-r--r-- | lib/Utils/strbuf.h (renamed from src/Backtrace/strbuf.h) | 14 | ||||
-rw-r--r-- | lib/Utils/xfuncs.cpp | 8 | ||||
-rw-r--r-- | lib/Utils/xfuncs.h | 104 | ||||
-rwxr-xr-x | scripts/abrt-bz-downloader (renamed from src/Backtrace/abrt-bz-downloader) | 0 | ||||
-rwxr-xr-x | scripts/abrt-bz-dupchecker (renamed from src/Backtrace/abrt-bz-dupchecker) | 0 | ||||
-rwxr-xr-x | scripts/abrt-bz-hashchecker (renamed from src/Backtrace/abrt-bz-hashchecker) | 0 | ||||
-rwxr-xr-x | scripts/check-bt-parsability (renamed from src/Backtrace/check-bt-parsability) | 0 | ||||
-rw-r--r-- | src/Backtrace/Makefile.am | 24 | ||||
-rw-r--r-- | src/Backtrace/fallback.c | 174 | ||||
-rw-r--r-- | src/Backtrace/fallback.h | 32 | ||||
-rw-r--r-- | src/Backtrace/main.c | 361 | ||||
-rw-r--r-- | src/Backtrace/strbuf.c | 112 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/utils/Makefile.am | 11 | ||||
-rw-r--r-- | src/utils/abrt-backtrace.1 (renamed from src/Backtrace/abrt-backtrace.1) | 0 | ||||
-rw-r--r-- | src/utils/abrt-backtrace.c | 318 |
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; +} |