summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKarel Klic <kklic@redhat.com>2010-03-26 17:55:51 +0100
committerKarel Klic <kklic@redhat.com>2010-03-26 18:11:09 +0100
commit7ccd55eb10921e94a81b699a2c96cb1dc25515d1 (patch)
tree66297ccd0633eb5b8f817c3d710c36b267b3bc61 /src
parent605c31f9d0897c18a900c7a4c0ad75bc439d18e5 (diff)
downloadabrt-7ccd55eb10921e94a81b699a2c96cb1dc25515d1.tar.gz
abrt-7ccd55eb10921e94a81b699a2c96cb1dc25515d1.tar.xz
abrt-7ccd55eb10921e94a81b699a2c96cb1dc25515d1.zip
Move backtrace parser from src/Backtrace to lib/Utils.
Move abrt-backtrace app from src/Backtrace/main.c to src/utils/abrt-backtrace. Move backtrace preprocessign code from abrt-backtrace to the parser. Implemented new backtrace rating algorithm. Added old bt rating algorithm to backtrace.c Move strbuf to lib/Utils, and updated it to use xfuncs. Created separate header for xfuncs. Some functions in xfuncs marked as extern "c", so they can be used in C code. Merged backtrace fallback (independent_backtrace) "parser" into backtrace.{h/c}. Added option --rate to abrt-backtrace, to be able to use the new backtrace rating algorithm in scripts.
Diffstat (limited to 'src')
-rw-r--r--src/Backtrace/Makefile.am24
-rwxr-xr-xsrc/Backtrace/abrt-bz-downloader82
-rwxr-xr-xsrc/Backtrace/abrt-bz-dupchecker272
-rwxr-xr-xsrc/Backtrace/abrt-bz-hashchecker59
-rw-r--r--src/Backtrace/backtrace.c533
-rw-r--r--src/Backtrace/backtrace.h100
-rwxr-xr-xsrc/Backtrace/check-bt-parsability20
-rw-r--r--src/Backtrace/fallback.c174
-rw-r--r--src/Backtrace/fallback.h32
-rw-r--r--src/Backtrace/main.c361
-rw-r--r--src/Backtrace/parser.y610
-rw-r--r--src/Backtrace/strbuf.c112
-rw-r--r--src/Backtrace/strbuf.h38
-rw-r--r--src/Makefile.am2
-rw-r--r--src/utils/Makefile.am11
-rw-r--r--src/utils/abrt-backtrace.1 (renamed from src/Backtrace/abrt-backtrace.1)0
-rw-r--r--src/utils/abrt-backtrace.c318
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;
+}