blob: a7f4bbe84fdab277a89c7ba3c42f2df7a7ca52c5 [file] [log] [blame]
#!src/build/run_python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Analyzes the crash dump.
Usage:
$ src/build/crash_analyzer.py
out/target/nacl_i686/intermediates/bionic_test/bionic_test.results.1.tmp
Note that this is also integrated in launch_chrome.py.
"""
import argparse
import os
import re
import subprocess
import sys
from src.build import build_common
from src.build import toolchain
from src.build.build_options import OPTIONS
class CrashAnalyzer(object):
def __init__(self, is_annotating=False):
self._text_segments = []
self._crash_addr = None
# A map from basename to path of binaries. Loaded lazily when
# crash is found.
self._binary_map = None
self._is_annotating = is_annotating
def handle_line(self, line):
if self._is_annotating:
sys.stdout.write(line)
line = line.strip()
matched = re.match(
r'linker: Loaded text: 0x([0-9a-fA-F]+)-0x([0-9a-fA-F]+) (.*)', line)
if matched:
start_addr = int(matched.group(1), 16)
end_addr = int(matched.group(2), 16)
binary_name = matched.group(3)
self._text_segments.append((binary_name, start_addr, end_addr))
return False
if self._is_annotating:
addr_reg = r'.*0x([0-9a-fA-F]+)'
else:
addr_reg = r'\*\* Signal \d+ from untrusted code: pc=([0-9a-fA-F]+)'
matched = re.match(addr_reg, line)
if matched:
self._crash_addr = int(matched.group(1), 16)
# Convert 64bit address to 32bit for x86-64.
self._crash_addr &= (1 << 32) - 1
return True
return False
def init_binary_map(self):
if self._binary_map:
return
self._binary_map = {}
for dirpath, dirnames, filenames in (
os.walk(build_common.get_load_library_path())):
for filename in filenames:
if re.match(r'arc_[^/]*\.nexe$', filename):
name = '/lib/main.nexe'
print 'Used %s as main.nexe' % filename
else:
name = os.path.basename(filename)
if name in self._binary_map:
raise Exception('Duplicated binary: ' + name)
self._binary_map[name] = os.path.join(dirpath, filename)
def get_crash_report(self):
assert self._crash_addr is not None
for binary_name, start_addr, end_addr in self._text_segments:
if start_addr > self._crash_addr or self._crash_addr >= end_addr:
continue
addr = self._crash_addr
# For PIC or PIE, we need to subtract the load bias.
if binary_name.endswith('.so') or OPTIONS.is_bare_metal_build():
addr -= start_addr
if os.path.exists(binary_name):
binary_filename = binary_name
else:
self.init_binary_map()
if binary_name not in self._binary_map:
return '%s %x (binary file not found)\n' % (binary_name, addr)
binary_filename = self._binary_map[binary_name]
pipe = subprocess.Popen([toolchain.get_tool(OPTIONS.target(),
'addr2line'),
'-e', binary_filename],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
addr2line_result = pipe.communicate('%x\n' % addr)[0]
# We can always get clean result using 32 byte aligned start
# address as NaCl binary does never overlap 32 byte boundary.
objdump_start_addr = (addr & ~31) - 32
objdump_end_addr = addr + 64
pipe = subprocess.Popen([toolchain.get_tool(OPTIONS.target(),
'objdump'),
'-SC', binary_filename,
'--start-address', '0x%x' % objdump_start_addr,
'--stop-address', '0x%x' % objdump_end_addr],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
objdump_result = pipe.communicate('%x\n' % addr)[0]
if self._is_annotating:
report = ('[[ %s 0x%x %s ]]' %
(binary_filename, addr, addr2line_result.strip()))
# The result of objdump is too verbose for annotation.
else:
report = '%s 0x%x\n' % (binary_filename, addr)
report += addr2line_result
report += objdump_result
return report
return 'Failed to retrieve a crash report\n'
def _parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('--annotate', action='store_true',
help='All hex values which start with 0x will be '
'annotated.')
parser.add_argument('target_files', nargs='*')
return parser.parse_args()
def main():
OPTIONS.parse_configure_file()
args = _parse_args()
for arg in args.target_files:
with open(arg) as f:
analyzer = CrashAnalyzer(is_annotating=args.annotate)
for line in f:
if analyzer.handle_line(line):
print analyzer.get_crash_report()
if __name__ == '__main__':
sys.exit(main())