diff options
Diffstat (limited to 'backtrace.py')
-rw-r--r-- | backtrace.py | 72 |
1 files changed, 59 insertions, 13 deletions
diff --git a/backtrace.py b/backtrace.py index 57e907e..cebba2f 100644 --- a/backtrace.py +++ b/backtrace.py @@ -13,6 +13,26 @@ class Frame(object): self.function = function self.info = info + def as_python_frame(self): + '''For relevant frames, return (file, line, funcname, args) tuple; otherwise None''' + if self.function != 'PyEval_EvalFrameEx': + return None + return PythonFrame(self) + #result += '#%i %s\n' % (frame.index, frame.info) + +class PythonFrame(object): + ''' + Class representating an interpretation of a C stack frame as a Python stack frame + ''' + def __init__(self, c_frame): + self.c_frame = c_frame + m = re.match('\(f=Frame 0x[0-9a-f]+, for file (\S+), line ([0-9]+), in (\S+) \((.*), throwflag=0\) at (\S+)', + c_frame.info) + self.file = m.group(1) + self.linenum = m.group(2) + self.funcname = m.group(3) + self.vars = m.group(4) + class Thread(object): ''' Class representing a thread within a backtrace @@ -23,10 +43,30 @@ class Thread(object): self.frames = {} self.framelist = [] + def as_python_backtrace(self, with_vars): + '''Generate a textual guess at a python-level backtrace''' + result = '' + for frame in self.framelist: + pyframe = frame.as_python_frame() + if pyframe: + result += '#%i %s:%s %s\n' % (frame.index, + pyframe.file, + pyframe.linenum, + pyframe.funcname) + if with_vars: + result += ' %s\n' % (pyframe.vars, ) + return result + class Backtrace(object): ''' Class representing a parsed backtrace ''' + @classmethod + def from_text_file(cls, filename, debug=False): + with open(filename, 'r') as f: + text = f.read() + return Backtrace(text, debug) + def __init__(self, string, debug=False): ''' Parse the given string (from gdb) @@ -51,7 +91,7 @@ class Backtrace(object): self._frame = None continue - m = re.match('^#([0-9]+)\s+(?:0x([0-9a-f]+) in)? (\S+) (.*)$', line) + m = re.match('^#([0-9]+)\s+(?:0x([0-9a-f]+) in )?(\S+) (.*)$', line) if m: if debug: print 'STACK FRAME:', m.groups() @@ -64,18 +104,8 @@ class Backtrace(object): address, m.group(3), m.group(4)) - - if self._thread is None: - if debug: - print 'GOT CRASH SITE' - self._crash_site = f - self._frame = None - continue - else: - self._thread.frames[f.index] = f - self._thread.framelist.append(f) - self._frame = f - continue + self.__on_new_frame(f, debug) + continue if line.startswith(' '): if self._frame: @@ -83,6 +113,17 @@ class Backtrace(object): #pprint(self._threads[5].frames[6].__dict__) + def __on_new_frame(self, f, debug): + if self._thread is None: + if debug: + print 'GOT CRASH SITE' + self._crash_site = f + self._frame = None + else: + self._thread.frames[f.index] = f + self._thread.framelist.append(f) + self._frame = f + def get_crash_site(self): '''Get a (Thread, Frame) pair for where the crash happened (or None)''' debug = False @@ -96,3 +137,8 @@ class Backtrace(object): print t.frames[0].__dict__ if t.frames[0].address == self._crash_site.address: return (t, t.frames[0]) + + def as_python_backtrace(self, with_vars): + '''Generate a textual guess at a python-level backtrace''' + thread, frame = self.get_crash_site() + return thread.as_python_backtrace(with_vars) |