Chromium Code Reviews| 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 |