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)']: |
| 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: |
| 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: |
| 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), |
| 68 ] |
| 69 p = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
| 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': lineno, |
| 82 }) |
| 83 return results |
| 84 |
| 85 |
| 86 def Decode(options, core_path, dest_trace): |
| 87 """Given a core.json file, decode and emit a stack trace. |
| 88 |
| 89 Args: |
| 90 options: options object. |
| 91 core_path: source file containing a dump. |
| 92 dest_trace: output file to write the trace to. |
| 93 """ |
| 94 core_text = open(core_path, 'r').read() |
| 95 core = json.loads(core_text) |
| 96 out = open(dest_trace, 'w') |
| 97 for frame in core['frames']: |
| 98 ip_mapped = frame['ip_mapped'] |
| 99 info_list = Addr2Line(options, ip_mapped['file'], ip_mapped['addr']) |
| 100 for info in info_list: |
| 101 if not options.verbose: |
| 102 # In non-verbose mode, emit only the basename, to allow golden traces. |
| 103 info['filename'] = posixpath.basename(info['filename']) |
| 104 out.write('%s at %s:%s\n' % ( |
| 105 info['function'], |
| 106 info['filename'], |
| 107 info['lineno'])) |
| 108 out.close() |
| 109 |
| 110 |
| 111 def Main(args): |
| 112 parser = optparse.OptionParser( |
| 113 usage='USAGE: %prog [options] <core.json> <trace>') |
| 114 parser.add_option('-m', '--main-nexe', dest='main_nexe', |
| 115 help='nexe to resolve NaClMain references from') |
| 116 parser.add_option('-n', '--nmf', dest='nmf', |
| 117 help='nmf to resolve references from') |
| 118 parser.add_option('-a', '--addr2line', dest='addr2line', |
| 119 help='path to appropriate addr2line') |
| 120 parser.add_option('-l', '--toolchain-libs', dest='toolchain_libs', |
| 121 help='path to the toolchain libraries') |
| 122 parser.add_option('-v', '--verbose', dest='verbose', action='store_true', |
| 123 default=False, |
| 124 help='select verbose output') |
| 125 options, args = parser.parse_args(args) |
| 126 if len(args) != 2: |
| 127 parser.print_help() |
| 128 sys.exit(1) |
| 129 if options.nmf: |
| 130 options.nmf_data = json.loads(open(options.nmf, 'r').read()) |
| 131 else: |
| 132 options.nmf_data = {} |
| 133 Decode(options, args[0], args[1]) |
| 134 |
| 135 |
| 136 if __name__ == '__main__': |
| 137 Main(sys.argv[1:]) |
OLD | NEW |