summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2009-12-02 14:09:09 -0500
committerDavid Malcolm <dmalcolm@redhat.com>2009-12-02 14:09:09 -0500
commitf1cfaa78d80f41800fab911442f20e0400576d1e (patch)
treec3f680c7e79e7244c6630886bf749528560ac026
downloadtriage-f1cfaa78d80f41800fab911442f20e0400576d1e.tar.gz
triage-f1cfaa78d80f41800fab911442f20e0400576d1e.tar.xz
triage-f1cfaa78d80f41800fab911442f20e0400576d1e.zip
Initial commit
-rwxr-xr-xabrt-triage.py308
1 files changed, 308 insertions, 0 deletions
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()
+
+
+