Add more checks to investigate SupervisedUserPrefStore crash at startup.
[chromium-blink-merge.git] / native_client_sdk / src / tools / decode_dump.py
blob60bf70210fb6b30215238e5b710c66995a629e63
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium 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.
6 """Utility to decode a crash dump generated by untrusted_crash_dump.[ch]
8 Currently this produces a simple stack trace.
9 """
11 import argparse
12 import json
13 import os
14 import posixpath
15 import subprocess
16 import sys
19 class CoreDecoder(object):
20 """Class to process core dumps."""
22 def __init__(self, main_nexe, nmf_filename,
23 addr2line, library_paths, platform):
24 """Construct and object to process core dumps.
26 Args:
27 main_nexe: nexe to resolve NaClMain references from.
28 nmf_filename: nmf to resolve references from.
29 addr2line: path to appropriate addr2line.
30 library_paths: list of paths to search for libraries.
31 platform: platform string to use in nmf files.
32 """
33 self.main_nexe = main_nexe
34 self.nmf_filename = nmf_filename
35 if nmf_filename == '-':
36 self.nmf_data = {}
37 else:
38 self.nmf_data = json.load(open(nmf_filename))
39 self.addr2line = addr2line
40 self.library_paths = library_paths
41 self.platform = platform
43 def _SelectModulePath(self, filename):
44 """Select which path to get a module from.
46 Args:
47 filename: filename of a module (as appears in phdrs).
48 Returns:
49 Full local path to the file.
50 Derived by consulting the manifest.
51 """
52 # For some names try the main nexe.
53 # NaClMain is the argv[0] setup in sel_main.c
54 # (null) shows up in chrome.
55 if self.main_nexe is not None and filename in ['NaClMain', '(null)']:
56 return self.main_nexe
57 filepart = posixpath.basename(filename)
58 nmf_entry = self.nmf_data.get('files', {}).get(filepart, {})
59 nmf_url = nmf_entry.get(self.platform, {}).get('url')
60 # Try filename directly if not in manifest.
61 if nmf_url is None:
62 return filename
63 # Look for the module relative to the manifest (if any),
64 # then in other search paths.
65 paths = []
66 if self.nmf_filename != '-':
67 paths.append(os.path.dirname(self.nmf_filename))
68 paths.extend(self.library_paths)
69 for path in paths:
70 pfilename = os.path.join(path, nmf_url)
71 if os.path.exists(pfilename):
72 return pfilename
73 # If nothing else, try the path directly.
74 return filename
76 def _DecodeAddressSegment(self, segments, address):
77 """Convert an address to a segment relative one, plus filename.
79 Args:
80 segments: a list of phdr segments.
81 address: a process wide code address.
82 Returns:
83 A tuple of filename and segment relative address.
84 """
85 for segment in segments:
86 for phdr in segment['dlpi_phdr']:
87 start = segment['dlpi_addr'] + phdr['p_vaddr']
88 end = start + phdr['p_memsz']
89 if address >= start and address < end:
90 return (segment['dlpi_name'], address - segment['dlpi_addr'])
91 return ('(null)', address)
93 def _Addr2Line(self, segments, address):
94 """Use addr2line to decode a code address.
96 Args:
97 segments: A list of phdr segments.
98 address: a code address.
99 Returns:
100 A list of dicts containing: function, filename, lineno.
102 filename, address = self._DecodeAddressSegment(segments, address)
103 filename = self._SelectModulePath(filename)
104 if not os.path.exists(filename):
105 return [{
106 'function': 'Unknown_function',
107 'filename': 'unknown_file',
108 'lineno': -1,
110 # Use address - 1 to get the call site instead of the line after.
111 address -= 1
112 cmd = [
113 self.addr2line, '-f', '--inlines', '-e', filename, '0x%08x' % address,
115 process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
116 process_stdout, _ = process.communicate()
117 assert process.returncode == 0
118 lines = process_stdout.splitlines()
119 assert len(lines) % 2 == 0
120 results = []
121 for index in xrange(len(lines) / 2):
122 func = lines[index * 2]
123 afilename, lineno = lines[index * 2 + 1].split(':', 1)
124 results.append({
125 'function': func,
126 'filename': afilename,
127 'lineno': int(lineno),
129 return results
131 def Decode(self, text):
132 core = json.loads(text)
133 for frame in core['frames']:
134 frame['scopes'] = self._Addr2Line(core['segments'], frame['prog_ctr'])
135 return core
137 def LoadAndDecode(self, core_path):
138 """Given a core.json file, load and embellish with decoded addresses.
140 Args:
141 core_path: source file containing a dump.
142 Returns:
143 An embellished core dump dict (decoded code addresses).
145 core = json.load(open(core_path))
146 for frame in core['frames']:
147 frame['scopes'] = self._Addr2Line(core['segments'], frame['prog_ctr'])
148 return core
150 def StackTrace(self, info):
151 """Convert a decoded core.json dump to a simple stack trace.
153 Args:
154 info: core.json info with decoded code addresses.
155 Returns:
156 A list of dicts with filename, lineno, function (deepest first).
158 trace = []
159 for frame in info['frames']:
160 for scope in frame['scopes']:
161 trace.append(scope)
162 return trace
164 def PrintTrace(self, trace, out):
165 """Print a trace to a file like object.
167 Args:
168 trace: A list of [filename, lineno, function] (deepest first).
169 out: file like object to output the trace to.
171 for scope in trace:
172 out.write('%s at %s:%d\n' % (
173 scope['function'],
174 scope['filename'],
175 scope['lineno']))
178 def main(args):
179 parser = argparse.ArgumentParser(description=__doc__)
180 parser.add_argument('-m', '--main-nexe',
181 help='nexe to resolve NaClMain references from')
182 parser.add_argument('-n', '--nmf', default='-',
183 help='nmf to resolve references from')
184 parser.add_argument('-a', '--addr2line',
185 help='path to appropriate addr2line')
186 parser.add_argument('-L', '--library-path', dest='library_paths',
187 action='append', default=[],
188 help='path to search for shared libraries')
189 parser.add_argument('-p', '--platform',
190 help='platform in a style match nmf files')
191 parser.add_argument('core_json')
192 options = parser.parse_args(args)
193 decoder = CoreDecoder(
194 main_nexe=options.main_nexe,
195 nmf_filename=options.nmf,
196 addr2line=options.addr2line,
197 library_paths=options.library_paths,
198 platform=options.platform)
199 info = decoder.LoadAndDecode(options.core_json)
200 trace = decoder.StackTrace(info)
201 decoder.PrintTrace(trace, sys.stdout)
202 return 0
205 if __name__ == '__main__':
206 sys.exit(main(sys.argv[1:]))