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 /src | |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/Backtrace/Makefile.am | 24 | ||||
-rwxr-xr-x | src/Backtrace/abrt-bz-downloader | 82 | ||||
-rwxr-xr-x | src/Backtrace/abrt-bz-dupchecker | 272 | ||||
-rwxr-xr-x | src/Backtrace/abrt-bz-hashchecker | 59 | ||||
-rw-r--r-- | src/Backtrace/backtrace.c | 533 | ||||
-rw-r--r-- | src/Backtrace/backtrace.h | 100 | ||||
-rwxr-xr-x | src/Backtrace/check-bt-parsability | 20 | ||||
-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/parser.y | 610 | ||||
-rw-r--r-- | src/Backtrace/strbuf.c | 112 | ||||
-rw-r--r-- | src/Backtrace/strbuf.h | 38 | ||||
-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 |
17 files changed, 330 insertions, 2418 deletions
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/abrt-bz-downloader b/src/Backtrace/abrt-bz-downloader deleted file mode 100755 index 7f294257..00000000 --- a/src/Backtrace/abrt-bz-downloader +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/python -# -*- mode:python -*- -# ABRT Bugzilla Backtrace Downloader -# Downloads all backtraces reported by ABRT from Bugzilla. -# -# Please do not run this script unless it's neccessary to do so. -# It forces Bugzilla to send data related to thousands of bug reports. - -from bugzilla import RHBugzilla -from optparse import OptionParser -import sys -import os.path - -parser = OptionParser(version="%prog 1.0") -parser.add_option("-u", "--user", dest="user", - help="Bugzilla user name (REQUIRED)", metavar="USERNAME") -parser.add_option("-p", "--password", dest="password", - help="Bugzilla password (REQUIRED)", metavar="PASSWORD") -parser.add_option("-b", "--bugzilla", dest="bugzilla", - help="Bugzilla URL (defaults to Red Hat Bugzilla)", metavar="URL") -parser.add_option("-f", "--fields", - action="store_true", dest="fields", default=False, - help="Print possible bug fields and exit.") - -(options, args) = parser.parse_args() - -if not options.user or len(options.user) == 0: - parser.error("User name is required.\nTry {0} --help".format(sys.argv[0])) - -if not options.password or len(options.password) == 0: - parser.error("Password is required.\nTry {0} --help".format(sys.argv[0])) - -if not options.bugzilla or len(options.bugzilla) == 0: - options.bugzilla = "https://bugzilla.redhat.com/xmlrpc.cgi" - -bz = RHBugzilla() -bz.connect(options.bugzilla) -bz.login(options.user, options.password) - -if options.fields: - print bz.bugfields - exit(0) - -buginfos = bz.query({'status_whiteboard_type':'allwordssubstr','status_whiteboard':'abrt_hash'}) - -print "{0} bugs found.".format(len(buginfos)) - -for buginfo in buginfos: - # Skip bugs with already downloaded backtraces. - filename = "{0}.bt".format(buginfo.bug_id) - if os.path.isfile(filename): - print "Skipping {0} (already exists).".format(filename) - continue - - # Skip bugs with broken or Python backtraces - broken_backtrace_bugs = [ 517116, # binary file :) - 518516, # not a backtrace, GDB fail - 524259, # multiple backtraces in single file - 524427, # multiple backtraces in single file - 528529, # just [New Thread xx] lines - #528915, 10000 frames, out of memory, to be fixed - #529422, 10000 frames, out of memory, to be fixed - #530239, 10000 frames, out of memory, to be fixed - 532264, # no header - 533475, # no backtrace - #537819, 50000 frames, out of memory, to be fixed - #539699, to be fixed, parser bug - 539992] # completely broken backtrace - if buginfo.bug_id in broken_backtrace_bugs: - continue - - # Get backtrace from bug and store it as a file. - bug = bz.getbug(buginfo.bug_id) - for attachment in bug.attachments: - if attachment['filename'] == 'backtrace': - data = bz.openattachment(attachment['id']) - f = open(filename, 'w') - f.write(data.read()) - f.close() - print "Attachment {0} downloaded.".format(filename) - -bz.logout() diff --git a/src/Backtrace/abrt-bz-dupchecker b/src/Backtrace/abrt-bz-dupchecker deleted file mode 100755 index 654a3702..00000000 --- a/src/Backtrace/abrt-bz-dupchecker +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/python -# -*- mode:python -*- -# ABRT Bugzilla Duplication Checker -# Downloads all backtraces reported by ABRT from Bugzilla, -# and search for duplicates using the newest ABRT duplication -# checker. -# -# Some bugs in Bugzilla were reported by older ABRT -# versions, which had poor duplication detection. -# -# Please do not run this script unless it's neccessary to do so. -# It forces Bugzilla to send data related to thousands of bug reports. -# -# -# Useful text to be pasted to Bugzilla: -""" -This bug appears to have been filled using a buggy version of ABRT, because -it contains unusable backtrace. Sorry for the inconvenience. -Closing as INSUFFICIENT_DATA. -""" - -from bugzilla import RHBugzilla -from optparse import OptionParser -import sys -import os.path -import subprocess -import cPickle -import urllib -import json - -parser = OptionParser(version="%prog 1.0") -parser.add_option("-u", "--user", dest="user", - help="Bugzilla user name (REQUIRED)", metavar="USERNAME") -parser.add_option("-p", "--password", dest="password", - help="Bugzilla password (REQUIRED)", metavar="PASSWORD") -parser.add_option("-b", "--bugzilla", dest="bugzilla", default="https://bugzilla.redhat.com/xmlrpc.cgi", - help="Bugzilla URL (defaults to Red Hat Bugzilla)", metavar="URL") -parser.add_option("-v", "--verbose", dest="verbose", - help="Detailed output") -parser.add_option("-c", "--close", help="Close some of the bugs in Bugzilla (DANGEROUS)", - action="store_true", default=False, dest="close") -parser.add_option("-i", "--wiki", help="Generate output in wiki syntax", - action="store_true", default=False, dest="wiki") - -(options, args) = parser.parse_args() - -if not options.user or len(options.user) == 0: - parser.error("User name is required.\nTry {0} --help".format(sys.argv[0])) - -if not options.password or len(options.password) == 0: - parser.error("Password is required.\nTry {0} --help".format(sys.argv[0])) - -bz = RHBugzilla() -bz.connect(options.bugzilla) -bz.login(options.user, options.password) - -buginfos = bz.query({'status_whiteboard_type':'allwordssubstr','status_whiteboard':'abrt_hash'}) - -print "{0} bugs found.".format(len(buginfos)) - -# -# Load cache from previous run. Speeds up the case Bugzilla closes connection. -# The cache should be manually removed after a day or so, because the data in it -# are no longer valid. -# -database = {} -ids = {} -CACHE_FILE = "abrt-bz-dupchecker-cache.tmp" -if os.path.isfile(CACHE_FILE): - f = open(CACHE_FILE, 'r') - database = cPickle.load(f) - ids = cPickle.load(f) - f.close() - -def save_to_cache(): - global database - f = open(CACHE_FILE, 'w') - cPickle.dump(database, f, 2) - cPickle.dump(ids, f, 2) - f.close() - -count = 0 -for buginfo in buginfos: - count += 1 - print "{0}/{1}".format(count, len(buginfos)) - if count % 100 == 0: - save_to_cache() - - if ids.has_key(buginfo.bug_id): - continue - - ids[buginfo.bug_id] = True - - if not buginfo.bug_status in ["NEW", "ASSIGNED", "MODIFIED", "VERIFIED"]: - if options.verbose: - print "Bug {0} has status {1}, skipping.".format(buginfo.bug_id, buginfo.bug_status) - continue - - bug = bz.getbug(buginfo.bug_id) - - # Skip bugs with already downloaded backtraces. - filename = "{0}.bt".format(buginfo.bug_id) - if os.path.isfile(filename): - if options.verbose: - print "Skipping {0} (already exists).".format(filename) - else: - # Get backtrace from bug and store it as a file. - downloaded = False - for attachment in bug.attachments: - if attachment['filename'] == 'backtrace': - data = bz.openattachment(attachment['id']) - f = open(filename, 'w') - f.write(data.read()) - f.close() - downloaded = True - if options.verbose: - print "Attachment {0} downloaded.".format(filename) - - # Silently skip bugs without backtrace. - # Those are usually duplicates of bugs; the duplication copies - # abrt_hash, but it does not copy the attachment. - if not downloaded: - continue - - command = ["./abrt-backtrace"] - command.append(filename) - command.append("--single-thread") - command.append("--frame-depth=5") - command.append("--remove-exit-handlers") - command.append("--remove-noncrash-frames") - - helper = subprocess.Popen(command, stdout=subprocess.PIPE) - backtrace, err = helper.communicate() - helper.wait() - - if helper.returncode != 0: - print "Problems parsing {0}".format(filename) - continue - - # Empty backtrace is provided by Python apps. - if len(backtrace) == 0: - continue - - bugitem = {'id':buginfo.bug_id, 'comments':len(bug.longdescs)} - if backtrace in database: - components = database[backtrace] - if buginfo.component in components: - components[buginfo.component].append(bugitem) - if options.verbose: - print "Duplicate found: {0}".format(database[out]['id']) - print "Backtrace: {0}".format(out) - else: - components[buginfo.component] = [ bugitem ] - else: - database[backtrace] = { buginfo.component: [ bugitem ] } - -# The number of duplicates. -dupcount = 0 -# The number of duplicates that can be closed. -dupclosecount = 0 -for backtrace, components in database.items(): - for component, bugitems in components.items(): - dupcount += len(bugitems) - 1 - dupclosecount += min(len(filter(lambda x: x <= 2, - map(lambda x: x["comments"], - bugitems))), - len(bugitems) - 1) - -# Get the component owner. -# Sort the duplicates by the component owner, and -# filter out those which should not be printed. -dups = [] -for backtrace, components in database.items(): - for component, bugitems in components.items(): - if len(bugitems) <= 1: - continue - - # Get the component owner - owner = "Failed to get component owner" - try: - component_info = json.load(urllib.urlopen("https://admin.fedoraproject.org/pkgdb/packages/name/{0}?tg_format=json".format(component))) - component_packages = component_info['packageListings'] - component_f12 = filter(lambda x:x["collection"]["version"]=="12", component_packages) - if len(component_f12) == 1: - owner = component_f12[0]["owner"] - except KeyError: - pass - - dups.append((component, owner, bugitems, backtrace)) - print "." - -# Close all bugs where it is appropriate. -if options.close: - LIMIT = 1000 - counter = 0 - for (component, owner, bugitems, backtrace) in dups: - # Find the master bug item - # Its the one with the most comments. - - # Sort function sorting by comment count. - def commentCmp(x, y): - if x['comments'] < y['comments']: - return 1 - elif x['comments'] == y['comments']: - # Sort by bug id, older bugs should became the master bug - if x['id'] > y['id']: - return 1 - elif x['id'] == y['id']: - return 0 - else: - return -1 - else: - return -1 - - sorteditems = sorted(bugitems, commentCmp) - - master = sorteditems[0] - for item in sorteditems[1:]: - if item['comments'] > 2: - continue - - bug = bz.getbug(int(item['id'])) - # Check the bug status AGAIN to make sure the bug is still opened. - if not bug.bug_status in ["NEW", "ASSIGNED"]: - continue - - print "Closing bug #{0} with {1} comments as a duplicate of #{2}.".format(item['id'], item['comments'], master['id']) - bug.close("DUPLICATE", int(master['id']), "", - ("This bug appears to have been filled using a buggy version of ABRT, because\n" + - "it contains a backtrace which is a duplicate of backtrace from bug #{0}.\n\n" + - "Sorry for the inconvenience.").format(master['id'])) - - counter += 1 - if counter > LIMIT: - sys.exit(0) - -bz.logout() - -print -print "SUMMARY" -print "==========================================================================" -print "Total number of duplicate bugs detected: {0}".format(dupcount) -print "Number of duplicate bugs that will be closed : {0}".format(dupclosecount) -print "------------------------------" - -# Print the duplicates sorted by package owner. -def cmp(x, y): - if x[1] < y[1]: - return -1 - elif x[1] == y[1]: - return 0 - else: - return 1 - -for (component, owner, bugitems, backtrace) in sorted(dups, cmp): - if options.wiki: - print "----" - print "* component: '''{0}''' ({1})".format(component, owner) - print "* duplicates: {0}".format( - reduce(lambda x,y: x+", "+y, - map(lambda x: "#[https://bugzilla.redhat.com/show_bug.cgi?id={0} {0}] ({1} comments)".format(x['id'],x['comments']), - bugitems))) - print "* backtrace:" - for line in backtrace.replace("Thread\n", "").splitlines(): - print "*# {0}".format(line) - else: - print "Component: {0} ({1})".format(component, owner) - print "Duplicates: {0}".format( - reduce(lambda x,y: x+", "+y, - map(lambda x: "{0} ({1})".format(x['id'],x['comments']), - bugitems))) - print "Backtrace: {0}".format(backtrace) diff --git a/src/Backtrace/abrt-bz-hashchecker b/src/Backtrace/abrt-bz-hashchecker deleted file mode 100755 index ec7ce1a6..00000000 --- a/src/Backtrace/abrt-bz-hashchecker +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/python -# -*- mode:python -*- -# Checks how many bugs in Bugzilla have the same hash. -# -# Please do not run this script unless it's neccessary to do so. -# It forces Bugzilla to send data related to thousands of bug reports. - -from bugzilla import RHBugzilla -from optparse import OptionParser -import sys -import os.path -import subprocess -import re - -parser = OptionParser(version="%prog 1.0") -parser.add_option("-u", "--user", dest="user", - help="Bugzilla user name (REQUIRED)", metavar="USERNAME") -parser.add_option("-p", "--password", dest="password", - help="Bugzilla password (REQUIRED)", metavar="PASSWORD") -parser.add_option("-b", "--bugzilla", dest="bugzilla", - help="Bugzilla URL (defaults to Red Hat Bugzilla)", metavar="URL") - -(options, args) = parser.parse_args() - -if not options.user or len(options.user) == 0: - parser.error("User name is required.\nTry {0} --help".format(sys.argv[0])) - -if not options.password or len(options.password) == 0: - parser.error("Password is required.\nTry {0} --help".format(sys.argv[0])) - -if not options.bugzilla or len(options.bugzilla) == 0: - options.bugzilla = "https://bugzilla.redhat.com/xmlrpc.cgi" - -bz = RHBugzilla() -bz.connect(options.bugzilla) -bz.login(options.user, options.password) - -buginfos = bz.query({'status_whiteboard_type':'allwordssubstr','status_whiteboard':'abrt_hash'}) - -print "{0} bugs found.".format(len(buginfos)) - -hashes = {} -for buginfo in buginfos: - match = re.search("abrt_hash:([^ ]+)", buginfo.status_whiteboard) - if not match: - continue - hash = match.group(1) - if not hash: - continue - if hash in hashes: - hashes[hash].append(buginfo.bug_id) - else: - hashes[hash] = [ buginfo.bug_id ] - print hash -bz.logout() - -for hash, ids in hashes.items(): - if len(ids) > 1: - print "Duplicates found: ", reduce(lambda x,y: str(x)+", "+str(y), ids) diff --git a/src/Backtrace/backtrace.c b/src/Backtrace/backtrace.c deleted file mode 100644 index 9b697da1..00000000 --- a/src/Backtrace/backtrace.c +++ /dev/null @@ -1,533 +0,0 @@ -/* -*-mode:c++;c-file-style:"bsd";c-basic-offset:2;indent-tabs-mode:nil-*- - 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 "backtrace.h" -#include <stdlib.h> -#include <string.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; -} - -static void frame_print_tree(struct frame *frame, bool verbose) -{ - if (verbose) - printf(" #%d", frame->number); - else - printf(" "); - - 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>"); - - puts(""); /* newline */ -} - -static bool frame_is_exit_handler(struct frame *frame) -{ - return (frame->function - && frame->sourcefile - && 0 == strcmp(frame->function, "__run_exit_handlers") - && NULL != strstr(frame->sourcefile, "exit.c")); -} - -/* 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; -} - -static void thread_print_tree(struct thread *thread, 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; - } -} - -/* - * Checks whether the thread it contains some known "abort" function. - * If a frame with the function is found, it is returned. - * If there are multiple frames with abort function, the lowest - * one is returned. - * Nonrecursive. - */ -static struct frame *thread_find_abort_frame(struct thread *thread) -{ - struct frame *frame = thread->frames; - struct frame *result = NULL; - while (frame) - { - 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; - } -} - -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; - } -} - -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) -{ - if (verbose) - printf("Thread count: %d\n", backtrace_get_thread_count(backtrace)); - - if (backtrace->crash && verbose) - { - printf("Crash frame: "); - frame_print_tree(backtrace->crash, verbose); - } - - struct thread *thread = backtrace->threads; - while (thread) - { - thread_print_tree(thread, verbose); - thread = thread->next; - } -} - -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; - } -} - diff --git a/src/Backtrace/backtrace.h b/src/Backtrace/backtrace.h deleted file mode 100644 index 93897e93..00000000 --- a/src/Backtrace/backtrace.h +++ /dev/null @@ -1,100 +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 BACKTRACE_H -#define BACKTRACE_H - -#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 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); - -/* - * 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); - -/* Defined in parser.y. */ -extern struct backtrace *do_parse(char *input, bool debug_parser, bool debug_scanner); - -#endif diff --git a/src/Backtrace/check-bt-parsability b/src/Backtrace/check-bt-parsability deleted file mode 100755 index a5018bfa..00000000 --- a/src/Backtrace/check-bt-parsability +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -*- mode: bash -*- - -PASS=0 -FAIL=0 -for file in *.bt -do - #echo "$file" - ./abrt-backtrace $file 1> /dev/null - if [ "$?" -eq "0" ] - then - echo -n "." - PASS=$(($PASS+1)) - else - echo "-$file" - FAIL=$(($FAIL+1)) - fi -done -echo "" -echo "Passed $PASS and failed $FAIL." 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/parser.y b/src/Backtrace/parser.y deleted file mode 100644 index 42ac2206..00000000 --- a/src/Backtrace/parser.y +++ /dev/null @@ -1,610 +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 *do_parse(char *input, bool debug_parser, bool debug_scanner) -{ - /* Prepare for running parser. */ - g_backtrace = 0; - yyin = input; -#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; -} 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/Backtrace/strbuf.h b/src/Backtrace/strbuf.h deleted file mode 100644 index f2e4459e..00000000 --- a/src/Backtrace/strbuf.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - strbuf.h - string buffer - - 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 STRBUF_H -#define STRBUF_H - -struct strbuf { - int alloc; - int len; - char *buf; -}; - -extern struct strbuf *strbuf_new(); -extern void strbuf_free(struct strbuf *strbuf); -/* Releases strbuf, but not the internal buffer. */ -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); - -#endif 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; +} |