From f1cfaa78d80f41800fab911442f20e0400576d1e Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 2 Dec 2009 14:09:09 -0500 Subject: Initial commit --- abrt-triage.py | 308 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100755 abrt-triage.py diff --git a/abrt-triage.py b/abrt-triage.py new file mode 100755 index 0000000..70e8279 --- /dev/null +++ b/abrt-triage.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python +from pprint import pprint +import re + +class Frame(object): + def __init__(self, index, address, function, info): + self.index = index + self.address = address + self.function = function + self.info = info + +class Thread(object): + def __init__(self, index, pid): + self.index = index + self.pid = pid + self.frames = {} + self.framelist = [] + +class Backtrace(object): + def __init__(self, string): + debug = False + self._string = string + if debug: + print string + self._crash_site = None + self._thread = None + self._threads = {} + self._frame = None + for line in string.splitlines(): + if debug: + print repr(line) + m = re.match('^Thread ([0-9]+) \(Thread ([0-9]+)\):$', line) + if m: + if debug: + print 'THREAD START:', m.groups() + self._thread = Thread(int(m.group(1)), + int(m.group(2))) + self._threads[self._thread.index] = self._thread + self._frame = None + continue + + m = re.match('^#([0-9]+)\s+(?:0x([0-9a-f]+) in)? (\S+) (.*)$', line) + if m: + if debug: + print 'STACK FRAME:', m.groups() + #print m.groups() + if m.group(2): + address = int(m.group(2), 16) + else: + address = None + f = Frame(int(m.group(1)), + address, + m.group(3), + m.group(4)) + + if self._thread is None: + self._crash_site = f + self._frame = None + continue + else: + self._thread.frames[f.index] = f + self._thread.framelist.append(f) + self._frame = f + continue + + if line.startswith(' '): + if self._frame: + self._frame.info += '\n' + line + + #pprint(self._threads[5].frames[6].__dict__) + + def get_crash_site(self): + '''Get a (Thread, Frame) pair for where the crash happened (or None)''' + debug = False + if debug: + print self._crash_site.__dict__ + for t in self._threads.values(): + if debug: + print t + if 0 in t.frames: + if debug: + print t.frames[0].__dict__ + if t.frames[0].address == self._crash_site.address: + return (t, t.frames[0]) + +class Bug(object): + def __init__(self, bz, id): + self.bz = bz + self.id = id + self._bug = bz.getbug(id) + + def get_backtrace(self): + # Get the backtrace associated with this ABRT bug, as a Backtrace instance + a = self._get_backtrace_info() + if a is None: + raise NoBacktrace() + return Backtrace(self.bz.openattachment(a['id']).read()) + + def _get_backtrace_info(self): + for a in self._bug.attachments: + if a['filename'] == 'backtrace': + return a + + def get_script(self): + '''Parse the ABRT report, extracting the script''' + # print repr(self._bug.longdescs[0]['body']) + for line in self._bug.longdescs[0]['body'].splitlines(): + m = re.match('cmdline: (.+)', line) + if m: + for arg in m.group(1).split()[1:]: + if arg.startswith('/'): + return arg + +class NoBacktrace(Exception): + pass + +class UnsupportedComponent(Exception): + def __init__(self, path, url): + self.path = path + self.url = url + +def describe_thread(bt, thread): + if len(bt._threads) == 1: + return "the program's single thread" + else: + return "thread #%i" % thread.index + + +def characterize_bt(bt, thread, script): + '''Get (newsubject, commentblurb)''' + bt_blurb = 'Looking at the backtrace, it looks like ' + function = None + for (i, frame) in enumerate(thread.framelist): + # Get function name for deepest point in stack that has one: + if function is None or function == '??' or function == 'vtable': + function = frame.function + + if frame.function == 'abort': + # print 'got abort!' + for j in range(i, len(thread.framelist)): + if thread.framelist[j].function == 'gdk_x_error': + return ('Fatal error in gdk_x_error running %s' % script, + (bt_blurb + 'a fatal error happened in frame %(f_idx)s of %(thread)s inside gdk_x_error.' + % dict(thread = describe_thread(bt, thread), + f_idx = j) + ) + ) + + if thread.framelist[j].function == '__assert_fail': + return (('Assertion failure in %s inside %s running %s' + % (thread.framelist[j+1].function, + thread.framelist[j+2].function, + script) + ), + (bt_blurb + 'an assertion failed inside frame %(f_idx)s of %(thread)s inside %(function)s.' + % dict(thread = describe_thread(bt, thread), + f_idx = j, + function = thread.framelist[j+1].function) + ) + ) + + if frame.function in ['_XError', '__fortify_fail']: + function = frame.function + + if frame.function in ['IA__g_assertion_message_expr']: + function = thread.framelist[i+1].function + + if frame.function == 'Py_FatalError': + if thread.framelist[i+1].function == 'PyEval_RestoreThread': + return (('Incorrect thread usage in %s running %s' + % (thread.framelist[i+2].function, + script) + ), + (bt_blurb + "an incorrect usage of Python's internal thread API was detected in %(function)s in frame #%(f_idx)s of %(thread)s" + % dict(function = thread.framelist[i+2].function, + thread = describe_thread(bt, thread), + f_idx = i+2) + ) + ) + + if frame.info == '() from /usr/lib/flash-plugin/libflashplayer.so': + raise UnsupportedComponent('/usr/lib/flash-plugin/libflashplayer.so', + 'https://fedoraproject.org/wiki/ForbiddenItems#Adobe_Flash_Player') + + bt_blurb += 'the problem occurred in %s' % describe_thread(bt, thread) + if function: + bt_blurb += ' in %s' % function + + if function: + summary = 'Fatal error in "%s" in %s' % (function, script) + else: + summary = 'Fatal error in %s' % script + + return (summary, bt_blurb) + + +def what_provides_file(path): + #from subprocess import Popen, PIPE + #p = Popen(['yum', 'whatprovides', path], stdout=PIPE) + #(stdout, stderr) = p.communicate() + #print repr(stdout) + import yum + for pkg, path in yum.YumBase().searchPackageProvides([path]).iteritems(): + #pprint(pkg.__dict__) + return pkg.name # this is the subpackage, not the srpm + +class Change(object): + def __init__(self, + newsummary=None, + newcomponent=None, + comment=None): + self.comment = comment + self.newsummary = newsummary + self.newcomponent = newcomponent + + def __str__(self): + result = '' + if self.newsummary: + result += '---- BEGIN SUMMARY ----\n' + result += self.newsummary + result += '\n---- END SUMMARY ----\n' + + if self.newcomponent: + result += '---- BEGIN COMPONENT ----\n' + result += '"python" -> "%s"\n' % self.newcomponent # is there a way to do this via XML-RPC? + result += '---- END COMPONENT ----\n' + + if self.comment: + result += '---- BEGIN COMMENT ----\n' + result += self.comment + result += '\n---- END COMMENT ----\n' + return result + +def get_change(bz, bug_id): + bug = Bug(bz, bug_id) + script = bug.get_script() + try: + bt = bug.get_backtrace() + (thread, frame) = bt.get_crash_site() + (newsummary, bt_blurb) = characterize_bt(bt, thread, script) + except NoBacktrace, e: + return Change(newsummary='Crash running %s' % script, + comment=('''Thank you for the bug report. + +Unfortunately, without a stack trace from the crash it is impossible to determine what caused the crash. Please see http://fedoraproject.org/wiki/StackTraces for more information about getting a useful stack trace with debugging symbols. Even if you cannot reproduce this crash at will, you can prepare your system now to produce a good stack trace the next time you experience the crash. + +Thank you. +''') + ) + except UnsupportedComponent, e: + return Change(newsummary='Crash in %s' % e.path, + comment=('''Thank you for the bug report. + +Unfortunately the problem appears to be in %(path)s. + +The Fedora Project only ships and maintains Free and Open Source software. Issues such as these are beyond the control of Fedora developers. See %(url)s for more information + +You may find assistance in the Fedora community support forums or mailing list, or you might consider using a commercially supported product.''' + % dict(path=e.path, + url=e.url) + ) + ) + + # script = '/usr/bin/deluged' + if script: + pkg = what_provides_file(script) + else: + pkg = '(unknown)' + + comment = '''Thank you for reporting this bug. + +How reproducable is this problem? If you run the program from a terminal, is an error message printed? + +What version of %(pkg)s do you have installed? + +%(bt_blurb)s + +Reassigning component from "python" to "%(pkg)s" +''' % dict(pkg=pkg, + bt_blurb = bt_blurb) + + ch = Change(newsummary = newsummary, + newcomponent = pkg, + comment = comment + ) + print ch + print '---- BEGIN THREAD ----' + for id in sorted(thread.frames.keys()): + f = thread.frames[id] + if f.address: + addr = hex(f.address) + else: + addr = None + print '#%i %s %s' % (id, addr, f.function) + print '---- END THREAD ----' + +def main(): + import bugzilla + bz=bugzilla.Bugzilla(url='https://bugzilla.redhat.com/xmlrpc.cgi') + import sys + bug_id = int(sys.argv[1]) + print '---- CHANGES FOR BUG %i ----' % bug_id + print get_change(bz, bug_id) + +main() + + + -- cgit