OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/python | |
2 # Copyright (c) 2012 The Native Client Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """Utility to decode a crash dump generated by untrusted_crash_dump.[ch] | |
7 | |
8 Currently this produces a simple stack trace. | |
9 """ | |
10 | |
11 import json | |
12 import optparse | |
13 import os | |
14 import posixpath | |
15 import subprocess | |
16 import sys | |
17 | |
18 | |
19 def SelectModulePath(options, filename): | |
20 """Select which path to get a module from. | |
21 | |
22 Args: | |
23 options: option object. | |
24 filename: filename of a module (as appears in phdrs). | |
25 Returns: | |
26 Full local path to the file. | |
27 Derived by consulting the manifest. | |
28 """ | |
29 # For some names try the main nexe. | |
30 if options.main_nexe and filename in ['NaClMain', '(null)']: | |
Mark Seaborn
2012/02/13 19:04:08
You could comment that "NaClMain" is the argv[0] v
bradn
2012/02/13 23:42:03
Done.
| |
31 return options.main_nexe | |
32 filepart = posixpath.basename(filename) | |
33 nmf_entry = options.nmf_data.get('files', {}).get(filepart, {}) | |
34 # TODO(bradnelson): support x86-64 + arm. | |
35 nmf_url = nmf_entry.get('x86-32', {}).get('url') | |
36 # Try filename directly if not in manifest. | |
37 if not nmf_url: | |
Mark Seaborn
2012/02/13 19:04:08
"if nmf_url is not None"
bradn
2012/02/13 23:42:03
Done (you mean is None).
| |
38 return filename | |
39 # Look for the module relative to the manifest, then toolchain. | |
40 for path in [os.path.dirname(options.nmf), options.toolchain_libs]: | |
41 if path: | |
Mark Seaborn
2012/02/13 19:04:08
"if path is not None"
bradn
2012/02/13 23:42:03
Done and changed round to make correct!
| |
42 pfilename = os.path.join(path, nmf_url) | |
43 if os.path.exists(pfilename): | |
44 return pfilename | |
45 # If nothing else, try the path directly. | |
46 return filename | |
47 | |
48 | |
49 def Addr2Line(options, filename, addr): | |
50 """Use addr2line to decode a code address. | |
51 | |
52 Args: | |
53 options: option object. | |
54 filename: filename of module that the address is relative to. | |
55 addr: address. | |
56 Returns: | |
57 A list of dicts containing: function, filename, lineno. | |
58 """ | |
59 filename = SelectModulePath(options, filename) | |
60 if not os.path.exists(filename): | |
61 return [{ | |
62 'function': 'Unknown_function', | |
63 'filename': 'unknown_file', | |
64 'lineno': -1, | |
65 }] | |
66 cmd = [ | |
67 options.addr2line, '-f', '--inlines', '-e', filename, ('0x%08x' % addr), | |
Mark Seaborn
2012/02/13 19:04:08
Nit: Don't need brackets around '0x%08x' % addr
bradn
2012/02/13 23:42:03
Done.
| |
68 ] | |
69 p = subprocess.Popen(cmd, stdout=subprocess.PIPE) | |
Mark Seaborn
2012/02/13 19:04:08
Try to avoid one-char variable names. e.g. 'p' ->
bradn
2012/02/13 23:42:03
Done.
| |
70 p_stdout, p_stderr = p.communicate() | |
71 assert p.returncode == 0 | |
72 lines = p_stdout.splitlines() | |
73 assert len(lines) % 2 == 0 | |
74 results = [] | |
75 for index in range(len(lines) / 2): | |
76 func = lines[index * 2] | |
77 afilename, lineno = lines[index * 2 + 1].split(':') | |
78 results.append({ | |
79 'function': func, | |
80 'filename': afilename, | |
81 'lineno': int(lineno), | |
82 }) | |
83 return results | |
84 | |
85 | |
86 def LoadAndDecode(options, core_path): | |
87 """Given a core.json file, load and embellish with decoded addresses. | |
88 | |
89 Args: | |
90 options: options object. | |
91 core_path: source file containing a dump. | |
92 Returns: | |
93 | |
94 """ | |
95 if options.nmf: | |
96 options.nmf_data = json.loads(open(options.nmf, 'r').read()) | |
Mark Seaborn
2012/02/13 19:04:08
You can do json.load(open(...)) instead.
bradn
2012/02/13 23:42:03
Done.
| |
97 else: | |
98 options.nmf_data = {} | |
99 core_text = open(core_path, 'r').read() | |
100 core = json.loads(core_text) | |
Mark Seaborn
2012/02/13 19:04:08
Ditto. json.load()
bradn
2012/02/13 23:42:03
Done.
| |
101 for frame in core['frames']: | |
102 ip_mapped = frame['ip_mapped'] | |
103 ip_mapped['scopes'] = Addr2Line( | |
104 options, ip_mapped['file'], ip_mapped['addr']) | |
105 return core | |
106 | |
107 | |
108 def StackTrace(info): | |
109 """Convert a decoded core.json dump to a simple stack trace. | |
110 | |
111 Args: | |
112 info: core.json info with decoded code addresses. | |
113 Returns: | |
114 A list of dicts with filename, lineno, function (deepest first). | |
115 """ | |
116 trace = [] | |
117 for frame in info['frames']: | |
118 ip_mapped = frame['ip_mapped'] | |
119 for info in ip_mapped['scopes']: | |
Mark Seaborn
2012/02/13 19:04:08
Just "frame['ip_mapped']['scopes']"?
bradn
2012/02/13 23:42:03
Done.
| |
120 trace.append(info) | |
121 return trace | |
122 | |
123 | |
124 def PrintTrace(trace, out): | |
125 """Print a trace to a file like object. | |
126 | |
127 Args: | |
128 trace: A list of [filename, lineno, function] (deepest first). | |
129 out: file like object to output the trace to. | |
130 """ | |
131 for scope in trace: | |
132 out.write('%s at %s:%d\n' % ( | |
133 scope['function'], | |
134 scope['filename'], | |
135 scope['lineno'])) | |
136 | |
137 | |
138 def Main(args): | |
139 parser = optparse.OptionParser( | |
140 usage='USAGE: %prog [options] <core.json> <trace>') | |
Mark Seaborn
2012/02/13 19:04:08
You show two args here but check for one later.
bradn
2012/02/13 23:42:03
Fixed (changed at some point to one)
| |
141 parser.add_option('-m', '--main-nexe', dest='main_nexe', | |
142 help='nexe to resolve NaClMain references from') | |
143 parser.add_option('-n', '--nmf', dest='nmf', | |
144 help='nmf to resolve references from') | |
145 parser.add_option('-a', '--addr2line', dest='addr2line', | |
146 help='path to appropriate addr2line') | |
147 parser.add_option('-l', '--toolchain-libs', dest='toolchain_libs', | |
148 help='path to the toolchain libraries') | |
149 options, args = parser.parse_args(args) | |
150 if len(args) != 1: | |
151 parser.print_help() | |
152 sys.exit(1) | |
153 info = LoadAndDecode(options, args[0]) | |
154 trace = StackTrace(info) | |
155 PrintTrace(trace, sys.stdout) | |
156 | |
157 | |
158 if __name__ == '__main__': | |
159 Main(sys.argv[1:]) | |
OLD | NEW |