Signed-off-by: Vratislav Podzimek vpodzime@redhat.com --- src/hooks/abrt_exception_handler.py.in | 84 ++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 4 deletions(-)
diff --git a/src/hooks/abrt_exception_handler.py.in b/src/hooks/abrt_exception_handler.py.in index 6c0659a..ecdb175 100644 --- a/src/hooks/abrt_exception_handler.py.in +++ b/src/hooks/abrt_exception_handler.py.in @@ -24,8 +24,22 @@ Module for the ABRT exception handling hook
import sys import os +import inspect
-def write_dump(tb): +try: + import rpm + HAVE_RPM = True +except ImportError as imperr: + HAVE_RPM = False + import syslog + syslog.syslog("RPM module not available, cannot query RPM db for package "\ + "names") + +class RPMinfoError(Exception): + """Exception class for RPMdb-querying related errors""" + pass + +def write_dump(tb_text, tb): if sys.argv[0][0] == "/": executable = os.path.abspath(sys.argv[0]) else: @@ -33,6 +47,11 @@ def write_dump(tb): # (BTW, we *can't* assume the script is in current directory.) executable = sys.argv[0]
+ if HAVE_RPM: + dso_list = get_dso_list(tb) + else: + dso_list = None + # Open ABRT daemon's socket and write data to it try: import socket @@ -48,8 +67,12 @@ def write_dump(tb): # This handler puts a short(er) crash descr in 1st line of the backtrace. # Example: # CCMainWindow.py:1:<module>:ZeroDivisionError: integer division or modulo by zero - s.sendall("REASON=%s\0" % tb.splitlines()[0]) - s.sendall("BACKTRACE=%s\0" % tb) + s.sendall("REASON=%s\0" % tb_text.splitlines()[0]) + s.sendall("BACKTRACE=%s\0" % tb_text) + + if dso_list: + s.sendall("dso_list=%s\0" % "\n".join(dso_list)) + s.shutdown(socket.SHUT_WR)
@@ -77,6 +100,59 @@ def write_dump(tb): import syslog syslog.syslog("can't communicate with ABRT daemon, is it running? %s" % str(ex))
+def get_package_for_file(fpath): + """ + Returns package name for a given file. + + @param fpath: filename + @type fpath: str + @return: package name for the file + @rtype: str + @throws RPMinfoError: if package for the file cannot be found + + """ + + ts = rpm.TransactionSet() + mi = ts.dbMatch("basenames", fpath) + try: + header = mi.next() + except StopIteration: + raise RPMinfoError("Cannot get package and component for file "+ + fpath) + package = "{0}-{1}-{2}.{3}".format(header["name"], header["version"], + header["release"], header["arch"]) + + return package + +def get_dso_list(tb): + """ + Get the list of names of the packages whose files appear in the traceback. + + @param tb: traceback + @type tb: traceback + @return: list of package names + @rtype: list + + """ + + if inspect.istraceback(tb): + tb = inspect.getinnerframes(tb) + + packages = set() + for (frame, fpath, lineno, func, ctx, idx) in tb: + try: + packages.add(get_package_for_file(fpath)) + except RPMinfoError as rpmerr: + continue + + # remove the package name of the executable itself + try: + packages.discard(get_package_for_file(sys.argv[0])) + except RPMinfoError as rpmerr: + pass + + return list(packages) + def conf_enabled(var_name): try: file = open(@CONF_DIR@ + "/plugins/python.conf", "r") @@ -187,7 +263,7 @@ def handleMyException((etype, value, tb)): text += "".join(elist)
# Send data to the daemon - write_dump(text) + write_dump(text, tb)
except: # Silently ignore any error in this hook,