| Index: tests/untrusted_crash_dump/decode_dump.py
|
| diff --git a/tests/untrusted_crash_dump/decode_dump.py b/tests/untrusted_crash_dump/decode_dump.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..29d142d09263ad2d6c4992e2057f4fb6cef20b7a
|
| --- /dev/null
|
| +++ b/tests/untrusted_crash_dump/decode_dump.py
|
| @@ -0,0 +1,137 @@
|
| +#!/usr/bin/python
|
| +# Copyright (c) 2012 The Native Client Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""Utility to decode a crash dump generated by untrusted_crash_dump.[ch]
|
| +
|
| +Currently this produces a simple stack trace.
|
| +"""
|
| +
|
| +import json
|
| +import optparse
|
| +import os
|
| +import posixpath
|
| +import subprocess
|
| +import sys
|
| +
|
| +
|
| +def SelectModulePath(options, filename):
|
| + """Select which path to get a module from.
|
| +
|
| + Args:
|
| + options: option object.
|
| + filename: filename of a module (as appears in phdrs).
|
| + Returns:
|
| + Full local path to the file.
|
| + Derived by consulting the manifest.
|
| + """
|
| + # For some names try the main nexe.
|
| + if options.main_nexe and filename in ['NaClMain', '(null)']:
|
| + return options.main_nexe
|
| + filepart = posixpath.basename(filename)
|
| + nmf_entry = options.nmf_data.get('files', {}).get(filepart, {})
|
| + # TODO(bradnelson): support x86-64 + arm.
|
| + nmf_url = nmf_entry.get('x86-32', {}).get('url')
|
| + # Try filename directly if not in manifest.
|
| + if not nmf_url:
|
| + return filename
|
| + # Look for the module relative to the manifest, then toolchain.
|
| + for path in [os.path.dirname(options.nmf), options.toolchain_libs]:
|
| + if path:
|
| + pfilename = os.path.join(path, nmf_url)
|
| + if os.path.exists(pfilename):
|
| + return pfilename
|
| + # If nothing else, try the path directly.
|
| + return filename
|
| +
|
| +
|
| +def Addr2Line(options, filename, addr):
|
| + """Use addr2line to decode a code address.
|
| +
|
| + Args:
|
| + options: option object.
|
| + filename: filename of module that the address is relative to.
|
| + addr: address.
|
| + Returns:
|
| + A list of dicts containing: function, filename, lineno.
|
| + """
|
| + filename = SelectModulePath(options, filename)
|
| + if not os.path.exists(filename):
|
| + return [{
|
| + 'function': 'Unknown_function',
|
| + 'filename': 'unknown_file',
|
| + 'lineno': '-1',
|
| + }]
|
| + cmd = [
|
| + options.addr2line, '-f', '--inlines', '-e', filename, ('0x%08x' % addr),
|
| + ]
|
| + p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
| + p_stdout, p_stderr = p.communicate()
|
| + assert p.returncode == 0
|
| + lines = p_stdout.splitlines()
|
| + assert len(lines) % 2 == 0
|
| + results = []
|
| + for index in range(len(lines) / 2):
|
| + func = lines[index * 2]
|
| + afilename, lineno = lines[index * 2 + 1].split(':')
|
| + results.append({
|
| + 'function': func,
|
| + 'filename': afilename,
|
| + 'lineno': lineno,
|
| + })
|
| + return results
|
| +
|
| +
|
| +def Decode(options, core_path, dest_trace):
|
| + """Given a core.json file, decode and emit a stack trace.
|
| +
|
| + Args:
|
| + options: options object.
|
| + core_path: source file containing a dump.
|
| + dest_trace: output file to write the trace to.
|
| + """
|
| + core_text = open(core_path, 'r').read()
|
| + core = json.loads(core_text)
|
| + out = open(dest_trace, 'w')
|
| + for frame in core['frames']:
|
| + ip_mapped = frame['ip_mapped']
|
| + info_list = Addr2Line(options, ip_mapped['file'], ip_mapped['addr'])
|
| + for info in info_list:
|
| + if not options.verbose:
|
| + # In non-verbose mode, emit only the basename, to allow golden traces.
|
| + info['filename'] = posixpath.basename(info['filename'])
|
| + out.write('%s at %s:%s\n' % (
|
| + info['function'],
|
| + info['filename'],
|
| + info['lineno']))
|
| + out.close()
|
| +
|
| +
|
| +def Main(args):
|
| + parser = optparse.OptionParser(
|
| + usage='USAGE: %prog [options] <core.json> <trace>')
|
| + parser.add_option('-m', '--main-nexe', dest='main_nexe',
|
| + help='nexe to resolve NaClMain references from')
|
| + parser.add_option('-n', '--nmf', dest='nmf',
|
| + help='nmf to resolve references from')
|
| + parser.add_option('-a', '--addr2line', dest='addr2line',
|
| + help='path to appropriate addr2line')
|
| + parser.add_option('-l', '--toolchain-libs', dest='toolchain_libs',
|
| + help='path to the toolchain libraries')
|
| + parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
|
| + default=False,
|
| + help='select verbose output')
|
| + options, args = parser.parse_args(args)
|
| + if len(args) != 2:
|
| + parser.print_help()
|
| + sys.exit(1)
|
| + if options.nmf:
|
| + options.nmf_data = json.loads(open(options.nmf, 'r').read())
|
| + else:
|
| + options.nmf_data = {}
|
| + Decode(options, args[0], args[1])
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + Main(sys.argv[1:])
|
|
|