Re: [PATCH] have anaconda write anacdump.txt on demand

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> I thought it'd be nice if we could generate an anacdump.txt on demand,
> to help debug where anaconda's stuck but hasn't crashed.  The attached
> patch causes anaconda to write /tmp/anacdump.txt when SIGUSR2 is
> received.  It requires the stuff Peter just mailed.

Nice idea.

What I don't like is adding new code to isys to do things we can easily
do in pure python. Attached are two patches. The first one changes our
current code to use stacks instead of tracebacks in
AnacondaExceptionDump. It contains the same information, just in a
slightly different form. What we gain is the ability to generate
traceback-like data by simply calling inspect.stack() instead of having
to resort to growing isys. To be clear, this first patch is intended as
an alternative to Peter's isys.traceback patch.

The second patch is just an update of Chris' original patch that uses
the aforementioned modifications.

Again, the only thing we gain here is not growing isys. Otherwise the
two approaches are functionally equivalent as verified by diffing two
artificially generated anacdump.txt files.

Dave

diff --git a/exception.py b/exception.py
index b146315..713e1aa 100644
--- a/exception.py
+++ b/exception.py
@@ -31,6 +31,7 @@ import os
 import shutil
 import signal
 import traceback
+import inspect
 import iutil
 import types
 import bdb
@@ -47,10 +48,17 @@ import logging
 log = logging.getLogger("anaconda")
 
 class AnacondaExceptionDump:
-    def __init__(self, type, value, tb):
+    def __init__(self, type, value, stack):
         self.type = type
         self.value = value
-        self.tb = tb
+
+        # this isn't used, but it's an option if we want to leave the
+        # two instantiations of this class as they are instead of
+        # passing in a stack
+        if inspect.istraceback(stack):
+            stack = inspect.getinnerframes(stack)
+
+        self.stack = stack
 
         self.tbFile = None
 
@@ -59,13 +67,27 @@ class AnacondaExceptionDump:
     # Reverse the order that tracebacks are printed so people will hopefully quit
     # giving us the least useful part of the exception in bug reports.
     def __str__(self):
-        lst = traceback.format_tb(self.tb)
+        lst = self.format_stack()
         lst.reverse()
         lst.insert(0, "anaconda %s exception report\n" % os.getenv("ANACONDAVERSION"))
         lst.insert(1, 'Traceback (most recent call first):\n')
+
         lst.extend(traceback.format_exception_only(self.type, self.value))
+
         return joinfields(lst, "")
 
+    def format_stack(self):
+        frames = []
+        for (frame, file, lineno, func, ctx, idx) in self.stack:
+            if type(ctx) == type([]):
+                code = "".join(ctx)
+            else:
+                code = ctx
+
+            frames.append((file, lineno, func, code))
+
+        return traceback.format_list(frames)
+
     # Create a string representation of a class and write it to fd.  This
     # method will recursively handle all attributes of the base given class.
     def _dumpClass(self, instance, fd, level=0, parentkey="", skipList=[]):
@@ -180,11 +202,8 @@ class AnacondaExceptionDump:
 
         fd.write(str(self))
 
-        trace = self.tb
-        if trace is not None:
-            while trace.tb_next:
-                trace = trace.tb_next
-            frame = trace.tb_frame
+        if self.stack:
+            frame = self.stack[-1][0]
             fd.write ("\nLocal variables in innermost frame:\n")
             try:
                 for (key, value) in frame.f_locals.items():
@@ -220,7 +239,9 @@ class AnacondaExceptionDump:
         import hashlib
         s = ""
 
-        for (file, lineno, func, text) in traceback.extract_tb(self.tb):
+        for (file, lineno, func, text) in [f[1:5] for f in self.stack]:
+            if type(text) == type([]):
+                text = "".join(text)
             s += "%s %s %s\n" % (file, func, text)
 
         return hashlib.sha256(s).hexdigest()
@@ -548,8 +569,11 @@ def handleException(anaconda, (type, value, tb)):
     # restore original exception handler
     sys.excepthook = sys.__excepthook__
 
+    # convert the traceback to a stack
+    stack = inspect.getinnerframes(tb)
+
     # Save the exception file to local storage first.
-    exn = AnacondaExceptionDump(type, value, tb)
+    exn = AnacondaExceptionDump(type, value, stack)
     exn.write(anaconda)
     text = str(exn)
 
diff --git a/partedUtils.py b/partedUtils.py
index fac1433..ea6f4a0 100644
--- a/partedUtils.py
+++ b/partedUtils.py
@@ -37,7 +37,7 @@ import raid
 import dmraid
 import block
 import lvm
-import traceback
+import inspect
 from flags import flags
 from errors import *
 from constants import *
@@ -1142,7 +1142,8 @@ class DiskSet:
                 raise
         except:
             (type, value, tb) = sys.exc_info()
-            exn = exception.AnacondaExceptionDump(type, value, tb)
+            stack = inspect.getinnerframes(tb)
+            exn = exception.AnacondaExceptionDump(type, value, stack)
             lines = exn.__str__()
             for line in lines:
                 log.error(line)
--- a/anaconda
+++ b/anaconda
@@ -502,6 +502,13 @@ class Anaconda:
         # *sigh* we still need to be able to write this out
         self.xdriver = None
 
+    def dumpState(self):
+        from exception import AnacondaExceptionDump
+        from inspect import stack as _stack
+        # Skip the frames for isys.traceback, dumpState, and the signal handler.
+        exn = AnacondaExceptionDump(None, None, _stack())
+        exn.write(anaconda)
+
     def writeXdriver(self, instPath="/"):
         # this should go away at some point, but until it does, we
         # need to keep it around.  it could go into instdata but this
@@ -588,7 +595,7 @@ if __name__ == "__main__":
     # this handles setting up updates for pypackages to minimize the set needed
     setupPythonUpdates()
 
-    import signal, traceback, string, isys, iutil, time
+    import signal, string, isys, iutil, time
     from exception import handleException
     import dispatch
     import warnings
@@ -611,6 +618,9 @@ if __name__ == "__main__":
     signal.signal(signal.SIGINT, signal.SIG_DFL)
     signal.signal(signal.SIGSEGV, isys.handleSegv)
 
+    # add our own additional signal handlers
+    signal.signal(signal.SIGUSR2, lambda signum, frame: anaconda.dumpState())
+
     setupEnvironment()
 
     # we need to do this really early so we make sure its done before rpm

--- a/exception.py	2008-12-04 15:05:23.000000000 -0600
+++ b/exception.py	2008-12-04 15:04:14.000000000 -0600
@@ -72,7 +72,8 @@
         lst.insert(0, "anaconda %s exception report\n" % os.getenv("ANACONDAVERSION"))
         lst.insert(1, 'Traceback (most recent call first):\n')
 
-        lst.extend(traceback.format_exception_only(self.type, self.value))
+        if self.type is not None and self.value is not None:
+            lst.extend(traceback.format_exception_only(self.type, self.value))
 
         return joinfields(lst, "")
 
_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/anaconda-devel-list

[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux