Commit 04d9a73a authored by Egor Pasko's avatar Egor Pasko Committed by Commit Bot

Introduce crashpad_stackwalker for Android

When "release official" builds crash, the Crashpad produces (among other
things), detailed minidumps in the app's "cache" directory. Fetch the
latest of those, extract the likely native library name for it, generate
symbols for it, and print the (symbolized) stack traces.

This script is intented to primarily be used by Catapult to provide more
detailed information about native crashes. The output is more verbose
than our usual stack traces, which is good for understanding what
happens on remote bots.

Bug: 925453
Change-Id: I15981d49b1072123a56b268f5a222311e6e7d1a0
Reviewed-on: https://chromium-review.googlesource.com/c/1477858Reviewed-by: default avatarJuan Antonio Navarro Pérez <perezju@chromium.org>
Auto-Submit: Egor Pasko <pasko@chromium.org>
Commit-Queue: Egor Pasko <pasko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#634314}
parent d2ec19e7
...@@ -16,3 +16,8 @@ And have it actually show output without logcat terminating. ...@@ -16,3 +16,8 @@ And have it actually show output without logcat terminating.
Extracts Breakpad microdumps from a log file and uses `stackwalker` to symbolize Extracts Breakpad microdumps from a log file and uses `stackwalker` to symbolize
them. them.
# crashpad_stackwalker.py
Fetches Crashpad dumps from a given device, walks and symbolizes the stacks.
#!/usr/bin/env python
#
# Copyright 2019 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.
# Fetches Crashpad dumps from a given device, walks and symbolizes the stacks.
# All the non-trivial operations are performed by generate_breakpad_symbols.py,
# minidump_dump and minidump_stackwalk.
import argparse
import logging
import os
import re
import sys
import shutil
import subprocess
import tempfile
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)))
import devil_chromium
from devil.android import device_utils
def _CreateSymbolsDir(build_path, dynamic_library_names):
generator = 'components/crash/content/tools/generate_breakpad_symbols.py'
syms_dir = os.path.join(build_path, 'crashpad_syms')
shutil.rmtree(syms_dir, ignore_errors=True)
os.mkdir(syms_dir)
for lib in dynamic_library_names:
unstripped_library_path = os.path.join(build_path, 'lib.unstripped', lib)
if not os.path.exists(unstripped_library_path):
continue
logging.info('Generating symbols for: %s', unstripped_library_path)
cmd = [
generator,
'--symbols-dir={}'.format(syms_dir),
'--build-dir={}'.format(build_path),
'--binary={}'.format(unstripped_library_path),
]
return_code = subprocess.call(cmd)
if return_code != 0:
logging.error('Could not extract symbols, command failed: %s',
' '.join(cmd))
return syms_dir
def _ChooseLatestCrashpadDump(device, crashpad_dump_path):
latest = None
latest_timestamp = 0
for crashpad_file in device.ListDirectory(crashpad_dump_path):
if crashpad_file.endswith('.dmp'):
stat = device.StatPath(os.path.join(crashpad_dump_path, crashpad_file))
current_timestamp = stat['st_mtime']
if current_timestamp > latest_timestamp:
latest_timestamp = current_timestamp
latest = crashpad_file
return latest
def _ExtractLibraryNamesFromDump(build_path, dump_path):
default_library_name = 'libmonochrome.so'
dumper_path = os.path.join(build_path, 'minidump_dump')
if not os.access(dumper_path, os.X_OK):
logging.warning('Cannot extract library name from dump, default to: %s',
default_library_name)
return [default_library_name]
p = subprocess.Popen([dumper_path, dump_path],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
# Dumper errors often do not affect stack walkability, just a warning.
logging.warning('Reading minidump failed with output:\n%s', stderr)
library_names = []
module_library_line_re = re.compile(r'[(]code_file[)]\s+= '
r'"(?P<library_name>lib[^. ]+.so)"')
in_module = False
for line in stdout.splitlines():
line = line.rstrip('\n')
if line == 'MDRawModule':
in_module = True
continue
if line == '':
in_module = False
continue
if in_module:
m = module_library_line_re.match(line)
if m:
library_names.append(m.group('library_name'))
if not library_names:
return [default_library_name]
return library_names
def main():
logging.basicConfig(level=logging.INFO)
parser = argparse.ArgumentParser(
description='Fetches Crashpad dumps from a given device,'
'walks and symbolizes the stacks.')
parser.add_argument('--device', required=True, help='Device serial number')
parser.add_argument(
'--adb-path', required=True, help='Path to the "adb" command')
parser.add_argument(
'--build-path',
required=True,
help='Build output directory, equivalent to CHROMIUM_OUTPUT_DIR')
parser.add_argument(
'--chrome-cache-path',
required=True,
help='Directory on the device where Chrome stores cached files,'
' crashpad stores dumps in a subdirectory of it')
args = parser.parse_args()
devil_chromium.Initialize(adb_path=args.adb_path)
device = device_utils.DeviceUtils(args.device)
device_crashpad_path = os.path.join(args.chrome_cache_path, 'Crashpad',
'pending')
crashpad_file = _ChooseLatestCrashpadDump(device, device_crashpad_path)
if not crashpad_file:
logging.error('Could not locate a crashpad dump')
return 1
else:
dump_dir = tempfile.mkdtemp()
symbols_dir = None
try:
device.PullFile(
device_path=os.path.join(device_crashpad_path, crashpad_file),
host_path=dump_dir)
dump_full_path = os.path.join(dump_dir, crashpad_file)
library_names = _ExtractLibraryNamesFromDump(args.build_path,
dump_full_path)
symbols_dir = _CreateSymbolsDir(args.build_path, library_names)
stackwalk_cmd = [
os.path.join(args.build_path, 'minidump_stackwalk'), dump_full_path,
symbols_dir
]
subprocess.call(stackwalk_cmd)
finally:
shutil.rmtree(dump_dir, ignore_errors=True)
if symbols_dir:
shutil.rmtree(symbols_dir, ignore_errors=True)
return 0
if __name__ == '__main__':
sys.exit(main())
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment