1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
'''
Support for parsing strings containing gdb backtraces, turning them into classes
'''
import re
class Frame(object):
'''
Class representing a stack frame of a thread within a backtrace
'''
def __init__(self, index, address, function, info):
self.index = index
self.address = address
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
'''
def __init__(self, index, extra):
self.index = index
self.extra = extra
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)
'''
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 'GOT LINE: %r' % line
m = re.match('^Thread ([0-9]+) \(Thread (.*)\):$', line)
if m:
if debug:
print 'THREAD START:', m.groups()
self._thread = Thread(int(m.group(1)),
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))
self.__on_new_frame(f, debug)
continue
if line.startswith(' '):
if self._frame:
self._frame.info += '\n' + line
#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
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])
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)
|