Signed-off-by: Vratislav Podzimek <vpodzime(a)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,
--
1.7.11.2