diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/plugins/CCpp.cpp | 1 | ||||
-rw-r--r-- | lib/plugins/Makefile.am | 1 | ||||
-rw-r--r-- | lib/utils/Makefile.am | 4 | ||||
-rw-r--r-- | lib/utils/backtrace.c | 846 | ||||
-rw-r--r-- | lib/utils/backtrace.h | 152 | ||||
-rw-r--r-- | lib/utils/backtrace_parser.y | 684 |
6 files changed, 0 insertions, 1688 deletions
diff --git a/lib/plugins/CCpp.cpp b/lib/plugins/CCpp.cpp index 2bae89d6..e5b50c34 100644 --- a/lib/plugins/CCpp.cpp +++ b/lib/plugins/CCpp.cpp @@ -24,7 +24,6 @@ #include "abrt_exception.h" #include "comm_layer_inner.h" #include "Polkit.h" -#include "backtrace.h" using namespace std; diff --git a/lib/plugins/Makefile.am b/lib/plugins/Makefile.am index 2e50cc2d..f07b376d 100644 --- a/lib/plugins/Makefile.am +++ b/lib/plugins/Makefile.am @@ -66,7 +66,6 @@ UTILS_PATH=$(srcdir)/../utils # CCpp libCCpp_la_SOURCES = CCpp.cpp CCpp.h libCCpp_la_LDFLAGS = -avoid-version -#libCCpp_la_LIBADD = libCCpp_la_CPPFLAGS = -Wall -Werror \ -I$(INC_PATH) -I$(UTILS_PATH) \ -DCCPP_HOOK_PATH=\"${libexecdir}/abrt-hook-ccpp\" \ diff --git a/lib/utils/Makefile.am b/lib/utils/Makefile.am index ac5f4837..79df31e5 100644 --- a/lib/utils/Makefile.am +++ b/lib/utils/Makefile.am @@ -3,7 +3,6 @@ lib_LTLIBRARIES = libABRTUtils.la libABRTdUtils.la HEADER_DIR = $(srcdir)/../../inc AM_CPPFLAGS = -I$(HEADER_DIR) -AM_YFLAGS = --verbose # Not used just yet: # time.cpp @@ -27,8 +26,6 @@ libABRTUtils_la_SOURCES = \ abrt_dbus.c abrt_dbus.h \ CrashTypes.cpp \ ABRTException.cpp \ - backtrace.c backtrace.h \ - backtrace_parser.y \ strbuf.c strbuf.h \ abrt_packages.c abrt_packages.h \ hooklib.c hooklib.h \ @@ -53,7 +50,6 @@ libABRTUtils_la_LIBADD = \ $(DBUS_LIBS) \ -ldl - libABRTdUtils_la_SOURCES = \ parse_release.cpp \ make_descr.cpp \ diff --git a/lib/utils/backtrace.c b/lib/utils/backtrace.c deleted file mode 100644 index f507cba3..00000000 --- a/lib/utils/backtrace.c +++ /dev/null @@ -1,846 +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 <stdlib.h> -#include <string.h> -#include <ctype.h> -#include "backtrace.h" -#include "strbuf.h" -#include "xfuncs.h" - -struct frame *frame_new() -{ - struct frame *f = malloc(sizeof(struct frame)); - if (!f) - { - puts("Error while allocating memory for backtrace frame."); - exit(5); - } - - f->function = NULL; - f->number = 0; - f->sourcefile = NULL; - f->signal_handler_called = false; - f->next = NULL; - return f; -} - -void frame_free(struct frame *f) -{ - if (f->function) - free(f->function); - if (f->sourcefile) - free(f->sourcefile); - free(f); -} - -struct frame *frame_add_sibling(struct frame *a, struct frame *b) -{ - struct frame *aa = a; - while (aa->next) - aa = aa->next; - - aa->next = b; - return a; -} - -/* Appends a string representation of 'frame' to the 'str'. */ -static void frame_append_str(struct frame *frame, struct strbuf *str, bool verbose) -{ - if (verbose) - strbuf_append_strf(str, " #%d", frame->number); - else - strbuf_append_str(str, " "); - - if (frame->function) - 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); - } - - 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) -{ - return (frame->function - && frame->sourcefile - && 0 == strcmp(frame->function, "__run_exit_handlers") - && NULL != strstr(frame->sourcefile, "exit.c")); -} - -/* Checks if a frame contains abort function used - * by operating system to exit application. - * E.g. in C it's called "abort" or "raise". - */ -static bool frame_is_abort_frame(struct frame *frame) -{ - if (!frame->function || !frame->sourcefile) - return false; - - if (0 == strcmp(frame->function, "raise") - && (NULL != strstr(frame->sourcefile, "pt-raise.c") - || NULL != strstr(frame->sourcefile, "/libc.so.6"))) - return true; - else if (0 == strcmp(frame->function, "exit") - && NULL != strstr(frame->sourcefile, "exit.c")) - return true; - else if (0 == strcmp(frame->function, "abort") - && (NULL != strstr(frame->sourcefile, "abort.c") - || NULL != strstr(frame->sourcefile, "/libc.so.6"))) - return true; - else if (frame_is_exit_handler(frame)) - return true; - - return false; -} - -static bool frame_is_noncrash_frame(struct frame *frame) -{ - /* Abort frames. */ - if (frame_is_abort_frame(frame)) - return true; - - if (!frame->function) - return false; - - if (0 == strcmp(frame->function, "__kernel_vsyscall")) - return true; - - if (0 == strcmp(frame->function, "__assert_fail")) - return true; - - if (!frame->sourcefile) - return false; - - /* GDK */ - if (0 == strcmp(frame->function, "gdk_x_error") - && 0 == strcmp(frame->sourcefile, "gdkmain-x11.c")) - return true; - - /* X.org */ - if (0 == strcmp(frame->function, "_XReply") - && 0 == strcmp(frame->sourcefile, "xcb_io.c")) - return true; - if (0 == strcmp(frame->function, "_XError") - && 0 == strcmp(frame->sourcefile, "XlibInt.c")) - return true; - if (0 == strcmp(frame->function, "XSync") - && 0 == strcmp(frame->sourcefile, "Sync.c")) - return true; - if (0 == strcmp(frame->function, "process_responses") - && 0 == strcmp(frame->sourcefile, "xcb_io.c")) - return true; - - /* glib */ - if (0 == strcmp(frame->function, "IA__g_log") - && 0 == strcmp(frame->sourcefile, "gmessages.c")) - return true; - if (0 == strcmp(frame->function, "IA__g_logv") - && 0 == strcmp(frame->sourcefile, "gmessages.c")) - return true; - if (0 == strcmp(frame->function, "IA__g_assertion_message") - && 0 == strcmp(frame->sourcefile, "gtestutils.c")) - return true; - if (0 == strcmp(frame->function, "IA__g_assertion_message_expr") - && 0 == strcmp(frame->sourcefile, "gtestutils.c")) - return true; - - /* DBus */ - if (0 == strcmp(frame->function, "gerror_to_dbus_error_message") - && 0 == strcmp(frame->sourcefile, "dbus-gobject.c")) - return true; - if (0 == strcmp(frame->function, "dbus_g_method_return_error") - && 0 == strcmp(frame->sourcefile, "dbus-gobject.c")) - return true; - - /* libstdc++ */ - if (0 == strcmp(frame->function, "__gnu_cxx::__verbose_terminate_handler") - && NULL != strstr(frame->sourcefile, "/vterminate.cc")) - return true; - if (0 == strcmp(frame->function, "__cxxabiv1::__terminate") - && NULL != strstr(frame->sourcefile, "/eh_terminate.cc")) - return true; - if (0 == strcmp(frame->function, "std::terminate") - && NULL != strstr(frame->sourcefile, "/eh_terminate.cc")) - return true; - if (0 == strcmp(frame->function, "__cxxabiv1::__cxa_throw") - && NULL != strstr(frame->sourcefile, "/eh_throw.cc")) - return true; - - return false; -} - -struct thread *thread_new() -{ - struct thread *t = malloc(sizeof(struct thread)); - if (!t) - { - puts("Error while allocating memory for backtrace thread."); - exit(5); - } - - t->number = 0; - t->frames = NULL; - t->next = NULL; - return t; -} - -void thread_free(struct thread *t) -{ - while (t->frames) - { - struct frame *rm = t->frames; - t->frames = rm->next; - frame_free(rm); - } - - free(t); -} - -struct thread *thread_add_sibling(struct thread *a, struct thread *b) -{ - struct thread *aa = a; - while (aa->next) - aa = aa->next; - - aa->next = b; - return a; -} - -static int thread_get_frame_count(struct thread *thread) -{ - struct frame *f = thread->frames; - int count = 0; - while (f) - { - f = f->next; - ++count; - } - return count; -} - -/* 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) - 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; - } -} - -/* - * Checks whether the thread it contains some known "abort" function. - * If a frame with the function is found, it is returned. - * If there are multiple frames with abort function, the lowest - * one is returned. - * Nonrecursive. - */ -struct frame *thread_find_abort_frame(struct thread *thread) -{ - struct frame *frame = thread->frames; - struct frame *result = NULL; - while (frame) - { - if (frame_is_abort_frame(frame)) - result = frame; - - frame = frame->next; - } - - return result; -} - -static void thread_remove_exit_handlers(struct thread *thread) -{ - struct frame *frame = thread->frames; - while (frame) - { - if (frame_is_exit_handler(frame)) - { - /* Delete all frames from the beginning to this frame. */ - while (thread->frames != frame) - { - struct frame *rm = thread->frames; - thread->frames = thread->frames->next; - frame_free(rm); - } - return; - } - - frame = frame->next; - } -} - -static void thread_remove_noncrash_frames(struct thread *thread) -{ - struct frame *prev = NULL; - struct frame *cur = thread->frames; - while (cur) - { - if (frame_is_noncrash_frame(cur)) - { - /* This frame must be skipped, because it will - be deleted. */ - if (prev) - prev->next = cur->next; - else - thread->frames = cur->next; - - frame_free(cur); - - /* Set cur to be valid, as it will be used to - advance to next item. */ - if (prev) - cur = prev; - else - { - cur = thread->frames; - continue; - } - } - - prev = cur; - cur = cur->next; - } -} - -/* 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)); - if (!bt) - { - puts("Error while allocating memory for backtrace."); - exit(5); - } - - bt->threads = NULL; - bt->crash = NULL; - return bt; -} - -void backtrace_free(struct backtrace *bt) -{ - while (bt->threads) - { - struct thread *rm = bt->threads; - bt->threads = rm->next; - thread_free(rm); - } - - if (bt->crash) - frame_free(bt->crash); - - free(bt); -} - -static int backtrace_get_thread_count(struct backtrace *bt) -{ - struct thread *t = bt->threads; - int count = 0; - while (t) - { - t = t->next; - ++count; - } - return count; -} - -void backtrace_print_tree(struct backtrace *backtrace, bool verbose) -{ - struct strbuf *strbuf = backtrace_tree_as_str(backtrace, verbose); - puts(strbuf->buf); - strbuf_free(strbuf); -} - -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)); - - 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, - struct thread *one) -{ - while (backtrace->threads) - { - struct thread *rm = backtrace->threads; - backtrace->threads = rm->next; - if (rm != one) - thread_free(rm); - } - - one->next = NULL; - backtrace->threads = one; -} - -/* - * Loop through all threads and if a single one contains the crash frame on the top, - * return it. Otherwise, return NULL. - * - * If require_abort is true, it is also required that the thread containing - * the crash frame contains some known "abort" function. In this case there can be - * multiple threads with the crash frame on the top, but only one of them might - * contain the abort function to succeed. - */ -static struct thread *backtrace_find_crash_thread_from_crash_frame(struct backtrace *backtrace, - bool require_abort) -{ - /* - * This code can be extended to compare something else when the function - * name is not available. - */ - if (!backtrace->threads || !backtrace->crash || !backtrace->crash->function) - return NULL; - - struct thread *result = NULL; - struct thread *thread = backtrace->threads; - while (thread) - { - if (thread->frames - && thread->frames->function - && 0 == strcmp(thread->frames->function, backtrace->crash->function) - && (!require_abort || thread_find_abort_frame(thread))) - { - if (result == NULL) - result = thread; - else - { - /* Second frame with the same function. Failure. */ - return NULL; - } - } - - thread = thread->next; - } - - return result; -} - -struct thread *backtrace_find_crash_thread(struct backtrace *backtrace) -{ - /* If there is no thread, be silent and report NULL. */ - if (!backtrace->threads) - return NULL; - - /* If there is just one thread, it is simple. */ - if (!backtrace->threads->next) - return backtrace->threads; - - /* If we have a crash frame *and* there is just one thread which has - * this frame on the top, it is also simple. - */ - struct thread *thread; - thread = backtrace_find_crash_thread_from_crash_frame(backtrace, false); - if (thread) - return thread; - - /* There are multiple threads with a frame indistinguishable from - * the crash frame on the top of stack. - * Try to search for known abort functions. - */ - thread = backtrace_find_crash_thread_from_crash_frame(backtrace, true); - - return thread; /* result or null */ -} - -void backtrace_limit_frame_depth(struct backtrace *backtrace, int depth) -{ - if (depth <= 0) - return; - - struct thread *thread = backtrace->threads; - while (thread) - { - struct frame *frame = thread_find_abort_frame(thread); - if (frame) - frame = frame->next; /* Start counting from the frame following the abort fr. */ - else - frame = thread->frames; /* Start counting from the first frame. */ - - /* Skip some frames to get the required stack depth. */ - int i = depth; - struct frame *last_frame = NULL; - while (frame && i) - { - last_frame = frame; - frame = frame->next; - --i; - } - - /* Delete the remaining frames. */ - if (last_frame) - last_frame->next = NULL; - - while (frame) - { - struct frame *rm = frame; - frame = frame->next; - frame_free(rm); - } - - thread = thread->next; - } -} - -void backtrace_remove_exit_handlers(struct backtrace *backtrace) -{ - struct thread *thread = backtrace->threads; - while (thread) - { - thread_remove_exit_handlers(thread); - thread = thread->next; - } -} - -void backtrace_remove_noncrash_frames(struct backtrace *backtrace) -{ - struct thread *thread = backtrace->threads; - while (thread) - { - 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/lib/utils/backtrace.h b/lib/utils/backtrace.h deleted file mode 100644 index df5def56..00000000 --- a/lib/utils/backtrace.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - 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 - 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 BACKTRACE_H -#define BACKTRACE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdio.h> -#include <stdbool.h> - -struct frame -{ - /* Function name, or NULL. */ - char *function; - /* Frame number. */ - int number; - /* Name of the source file, or binary file, or NULL. */ - char *sourcefile; - bool signal_handler_called; - /* Sibling frame, or NULL if this is the last frame in a thread. */ - struct frame *next; -}; - -struct thread -{ - int number; - struct frame *frames; - /* Sibling thread, or NULL if this is the last thread in a backtrace. */ - struct thread *next; -}; - -struct backtrace -{ - struct thread *threads; - /* - * The frame where the crash happened according to GDB. - * It might be that we can not tell to which thread this frame belongs, - * because all threads end with mutually indistinguishable frames. - */ - struct frame *crash; -}; - -extern struct frame *frame_new(); -extern void frame_free(struct frame *f); -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); - -/* 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. - * Caller must know that. - */ -extern void backtrace_remove_threads_except_one(struct backtrace *backtrace, - struct thread *one); - -/* - * Search all threads and tries to find the one that caused the crash. - * It might return NULL if the thread cannot be determined. - */ -extern struct thread *backtrace_find_crash_thread(struct backtrace *backtrace); - -extern void backtrace_limit_frame_depth(struct backtrace *backtrace, int depth); - -/* - * Exit handlers are all stack frames above __run_exit_handlers() - */ -extern void backtrace_remove_exit_handlers(struct backtrace *backtrace); - -/* - * Removes frames known as not causing crash, but that are often - * a part of a backtrace. - */ -extern void backtrace_remove_noncrash_frames(struct backtrace *backtrace); - -/* 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/lib/utils/backtrace_parser.y b/lib/utils/backtrace_parser.y deleted file mode 100644 index 8e2fc2b1..00000000 --- a/lib/utils/backtrace_parser.y +++ /dev/null @@ -1,684 +0,0 @@ -%{ /* -*- mode: yacc -*- - 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 <math.h> -#include <stdio.h> -#include <stdlib.h> -#include "backtrace.h" -#include "strbuf.h" - -struct backtrace *g_backtrace; - -#define YYDEBUG 1 -#define YYMAXDEPTH 10000000 -void yyerror(char const *s) -{ - fprintf (stderr, "\nParser error: %s\n", s); -} - -int yylex(); - -%} - -/* This defines the type of yylval */ -%union { - struct backtrace *backtrace; - struct thread *thread; - struct frame *frame; - char *str; - int num; - char c; - - struct strbuf *strbuf; -} - -/* Bison declarations. */ -%token END 0 "end of file" - -%type <backtrace> backtrace -%type <thread> threads - thread -%type <frame> frames - frame - frame_head - frame_head_1 - frame_head_2 - frame_head_3 - frame_head_4 - frame_head_5 -%type <strbuf> identifier - hexadecimal_digit_sequence - hexadecimal_number - file_name - file_location - function_call - function_name - digit_sequence - frame_address_in_function - identifier_braces - identifier_braces_inside - identifier_template - identifier_template_inside -%type <c> nondigit - digit - hexadecimal_digit - file_name_char - identifier_char - identifier_char_no_templates - identifier_first_char - identifier_braces_inside_char - identifier_template_inside_char - variables_char - variables_char_no_framestart - ws - ws_nonl - '(' ')' '+' '-' '/' '.' '_' '~' '[' ']' '\r' '?' '{' '}' - 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' - 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' - 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' - 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z' - '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' - '\'' '`' ',' '#' '@' '<' '>' '=' ':' '"' ';' ' ' - '\n' '\t' '\\' '!' '*' '%' '|' '^' '&' '$' -%type <num> frame_start - -%destructor { thread_free($$); } <thread> -%destructor { frame_free($$); } <frame> -%destructor { strbuf_free($$); } <strbuf> - -%start backtrace -%glr-parser -%error-verbose -%locations - -%% /* The grammar follows. */ - -backtrace : /* empty */ %dprec 1 - { $$ = g_backtrace = backtrace_new(); } - | threads wsa %dprec 2 - { - $$ = g_backtrace = backtrace_new(); - $$->threads = $1; - } - | frame_head wss threads wsa %dprec 4 - { - $$ = g_backtrace = backtrace_new(); - $$->threads = $3; - $$->crash = $1; - } - | frame wss threads wsa %dprec 3 - { - $$ = g_backtrace = backtrace_new(); - $$->threads = $3; - $$->crash = $1; - } -; - -threads : thread - | threads '\n' thread { $$ = thread_add_sibling($1, $3); } -; - -thread : keyword_thread wss digit_sequence wsa '(' keyword_thread wss digit_sequence wsa ')' ':' wsa frames - { - $$ = thread_new(); - $$->frames = $13; - - if (sscanf($3->buf, "%d", &$$->number) != 1) - { - printf("Error while parsing thread number '%s'", $3->buf); - exit(5); - } - strbuf_free($3); - strbuf_free($8); - } -; - -frames : frame { $$ = $1; } - | frames frame { $$ = frame_add_sibling($1, $2); } -; - -frame : frame_head_1 wss variables %dprec 3 - | frame_head_2 wss variables %dprec 4 - | frame_head_3 wss variables %dprec 5 - | frame_head_4 wss variables %dprec 2 - | frame_head_5 wss variables %dprec 1 -; - -frame_head : frame_head_1 %dprec 3 - | frame_head_2 %dprec 4 - | frame_head_3 %dprec 5 - | frame_head_4 %dprec 2 - | frame_head_5 %dprec 1 -; - -frame_head_1 : frame_start wss function_call wsa keyword_at wss file_location - { - $$ = frame_new(); - $$->number = $1; - $$->function = $3->buf; - strbuf_free_nobuf($3); - $$->sourcefile = $7->buf; - strbuf_free_nobuf($7); - } -; - -frame_head_2 : frame_start wss frame_address_in_function wss keyword_at wss file_location - { - $$ = frame_new(); - $$->number = $1; - $$->function = $3->buf; - strbuf_free_nobuf($3); - $$->sourcefile = $7->buf; - strbuf_free_nobuf($7); - } -; - -frame_head_3 : frame_start wss frame_address_in_function wss keyword_from wss file_location - { - $$ = frame_new(); - $$->number = $1; - $$->function = $3->buf; - strbuf_free_nobuf($3); - $$->sourcefile = $7->buf; - strbuf_free_nobuf($7); - } -; - -frame_head_4 : frame_start wss frame_address_in_function - { - $$ = frame_new(); - $$->number = $1; - $$->function = $3->buf; - strbuf_free_nobuf($3); - } -; - -frame_head_5 : frame_start wss keyword_sighandler - { - $$ = frame_new(); - $$->number = $1; - $$->signal_handler_called = true; - } - -frame_start: '#' digit_sequence - { - if (sscanf($2->buf, "%d", &$$) != 1) - { - printf("Error while parsing frame number '%s'.\n", $2->buf); - exit(5); - } - strbuf_free($2); - } -; - -frame_address_in_function : hexadecimal_number wss keyword_in wss function_call - { - strbuf_free($1); - $$ = $5; - } - | hexadecimal_number wss keyword_in wss keyword_vtable wss keyword_for wss function_call - { - strbuf_free($1); - $$ = $9; - } -; - -file_location : file_name ':' digit_sequence - { - $$ = $1; - strbuf_free($3); /* line number not needed for now */ - } - | file_name -; - -variables : variables_line '\n' - | variables_line END - | variables_line wss_nonl '\n' - | variables_line wss_nonl END - | variables variables_line '\n' - | variables variables_line END - | variables variables_line wss_nonl '\n' - | variables variables_line wss_nonl END - | variables wss_nonl variables_line '\n' - | variables wss_nonl variables_line END - | variables wss_nonl variables_line wss_nonl '\n' - | variables wss_nonl variables_line wss_nonl END -; - -variables_line : variables_char_no_framestart - | variables_line variables_char - | variables_line wss_nonl variables_char -; - -variables_char : '#' | variables_char_no_framestart -; - -/* Manually synchronized with function_args_char_base, except the first line. */ -variables_char_no_framestart : digit | nondigit | '"' | '(' | ')' | '\\' - | '+' | '-' | '<' | '>' | '/' | '.' - | '[' | ']' | '?' | '\'' | '`' | ',' - | '=' | '{' | '}' | '^' | '&' | '$' - | ':' | ';' | '!' | '@' | '*' - | '%' | '|' | '~' -; - -function_call : function_name wss function_args %dprec 3 - | return_type wss_nonl function_name wss function_args %dprec 2 - { $$ = $3; } - | function_name wss_nonl identifier_template wss function_args %dprec 1 - { $$ = $1; strbuf_free($3); } -; - -return_type : identifier { strbuf_free($1); } -; - -function_name : identifier - | '?' '?' - { - $$ = strbuf_new(); - strbuf_append_str($$, "??"); - } -; - -function_args : '(' wsa ')' - | '(' wsa function_args_sequence wsa ')' -; - -function_args_sequence : function_args_char - | function_args_sequence wsa '(' wsa ')' - | function_args_sequence wsa '(' wsa function_args_string wsa ')' - | function_args_sequence wsa '(' wsa function_args_sequence wsa ')' - | function_args_sequence wsa function_args_char - | function_args_sequence wsa function_args_string -; - -function_args_string : '"' wsa function_args_string_sequence wsa '"' - | '"' wsa '"' -; - -/* Manually synchronized with variables_char_no_framestart, - * except the first line. - */ -function_args_char_base : digit | nondigit | '#' - | '+' | '-' | '<' | '>' | '/' | '.' - | '[' | ']' | '?' | '\'' | '`' | ',' - | '=' | '{' | '}' | '^' | '&' | '$' - | ':' | ';' | '!' | '@' | '*' - | '%' | '|' | '~' -; -function_args_escaped_char : '\\' function_args_char_base - | '\\' '\\' - | '\\' '"' -; -function_args_char : function_args_char_base - | function_args_escaped_char -; - - -function_args_string_sequence : function_args_string_char - | function_args_string_sequence function_args_string_char - | function_args_string_sequence wss_nonl function_args_string_char -; - -function_args_string_char : function_args_char | '(' | ')' -; - -file_name : file_name_char { $$ = strbuf_new(); strbuf_append_char($$, $1); } - | file_name file_name_char { $$ = strbuf_append_char($1, $2); } -; - -file_name_char : digit | nondigit | '-' | '+' | '/' | '.' -; - - /* Function name, sometimes mangled. - * Example: something@GLIB_2_2 - * CClass::operator= - */ -identifier : identifier_first_char %dprec 1 - { - $$ = strbuf_new(); - strbuf_append_char($$, $1); - } - | identifier_braces %dprec 1 /* e.g. (anonymous namespace)::WorkerThread */ - | identifier identifier_char %dprec 1 - { $$ = strbuf_append_char($1, $2); } - | identifier identifier_braces %dprec 1 - { - $$ = strbuf_append_str($1, $2->buf); - strbuf_free($2); - } - | identifier identifier_template %dprec 2 - { - $$ = strbuf_append_str($1, $2->buf); - strbuf_free($2); - } -; - -identifier_first_char: nondigit - | '~' /* destructor */ - | '*' -; - -identifier_char_no_templates : digit | nondigit | '@' | '.' | ':' | '=' - | '!' | '*' | '+' | '-' | '[' | ']' - | '~' | '&' | '/' | '%' | '^' - | '|' | ',' -; - -/* Most of the special characters are required to support C++ - * operator overloading. - */ -identifier_char : identifier_char_no_templates | '<'| '>' -; - -identifier_braces : '(' ')' - { - $$ = strbuf_new(); - strbuf_append_char($$, $1); - strbuf_append_char($$, $2); - } - | '(' identifier_braces_inside ')' - { - $$ = strbuf_new(); - strbuf_append_char($$, $1); - strbuf_append_str($$, $2->buf); - strbuf_free($2); - strbuf_append_char($$, $3); - } -; - -identifier_braces_inside : identifier_braces_inside_char %dprec 1 - { - $$ = strbuf_new(); - strbuf_append_char($$, $1); - } - | identifier_braces_inside identifier_braces_inside_char %dprec 1 - { $$ = strbuf_append_char($1, $2); } - | identifier_braces_inside '(' identifier_braces_inside ')' %dprec 1 - { - $$ = strbuf_append_char($1, $2); - $$ = strbuf_append_str($1, $3->buf); - strbuf_free($3); - $$ = strbuf_append_char($1, $4); - } - | identifier_braces_inside '(' ')' %dprec 1 - { - $$ = strbuf_append_char($1, $2); - $$ = strbuf_append_char($1, $3); - } - | identifier_braces_inside identifier_template %dprec 2 - { - $$ = strbuf_append_str($1, $2->buf); - strbuf_free($2); - } -; - -identifier_braces_inside_char : identifier_char | ws_nonl -; - -identifier_template : '<' identifier_template_inside '>' - { - $$ = strbuf_new(); - strbuf_append_char($$, $1); - strbuf_append_str($$, $2->buf); - strbuf_free($2); - strbuf_append_char($$, $3); - } -; - -identifier_template_inside : identifier_template_inside_char - { - $$ = strbuf_new(); - strbuf_append_char($$, $1); - } - | identifier_template_inside identifier_template_inside_char - { $$ = strbuf_append_char($1, $2); } - | identifier_template_inside '<' identifier_template_inside '>' - { - $$ = strbuf_append_char($1, $2); - $$ = strbuf_append_str($1, $3->buf); - strbuf_free($3); - $$ = strbuf_append_char($1, $4); - } - | identifier_template_inside identifier_braces - { - $$ = strbuf_append_str($1, $2->buf); - strbuf_free($2); - } -; - -identifier_template_inside_char : identifier_char_no_templates | ws_nonl -; - -digit_sequence : digit { $$ = strbuf_new(); strbuf_append_char($$, $1); } - | digit_sequence digit { $$ = strbuf_append_char($1, $2); } -; - -hexadecimal_number : '0' 'x' hexadecimal_digit_sequence - { - $$ = $3; - strbuf_prepend_str($$, "0x"); - } - | '0' 'X' hexadecimal_digit_sequence - { - $$ = $3; - strbuf_prepend_str($$, "0X"); - } -; - -hexadecimal_digit_sequence : hexadecimal_digit - { - $$ = strbuf_new(); - strbuf_append_char($$, $1); - } - | hexadecimal_digit_sequence hexadecimal_digit - { $$ = strbuf_append_char($1, $2); } -; - -hexadecimal_digit : digit - | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' - | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' -; - -digit : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' -; - -nondigit : 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' - | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' - | 'x' | 'y' | 'z' - | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' - | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' - | 'X' | 'Y' | 'Z' - | '_' -; - - /* whitespace */ -ws : ws_nonl | '\n' | '\r' -; - - /* No newline.*/ -ws_nonl : '\t' | ' ' -; - - /* whitespace sequence without a newline */ -wss_nonl : ws_nonl - | wss_nonl ws_nonl -; - - /* whitespace sequence */ -wss : ws - | wss ws -; - - /* whitespace sequence allowed */ -wsa : - | wss -; - -keyword_in : 'i' 'n' -; - -keyword_at : 'a' 't' -; - -keyword_for : 'f' 'o' 'r' -; - -keyword_vtable : 'v' 't' 'a' 'b' 'l' 'e' -; - -keyword_from : 'f' 'r' 'o' 'm' -; - -keyword_thread: 'T' 'h' 'r' 'e' 'a' 'd' -; - -keyword_sighandler: '<' 's' 'i' 'g' 'n' 'a' 'l' ' ' 'h' 'a' 'n' 'd' 'l' 'e' 'r' ' ' 'c' 'a' 'l' 'l' 'e' 'd' '>' -; - -%% - -static bool scanner_echo = false; -static char *yyin; - -int yylex() -{ - char c = *yyin; - if (c == '\0') - return END; - ++yyin; - - /* Debug output. */ - if (scanner_echo) - putchar(c); - - yylval.c = c; - - /* Return a single char. */ - return c; -} - -/* This is the function that is actually called from outside. - * @returns - * Backtrace structure. Caller is responsible for calling - * backtrace_free() on this. - * Returns NULL when parsing failed. - */ -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 = btnoheader; -#if YYDEBUG == 1 - if (debug_parser) - yydebug = 1; -#endif - scanner_echo = debug_scanner; - - /* Parse. */ - int failure = yyparse(); - - /* Separate debugging output. */ - if (scanner_echo) - putchar('\n'); - - if (failure) - { - if (g_backtrace) - { - backtrace_free(g_backtrace); - g_backtrace = NULL; - } - fprintf(stderr, "Error while parsing backtrace.\n"); - } - - return g_backtrace; -} |