diff options
Diffstat (limited to 'third_party/libwebrtc/build/android/stacktrace/crashpad_stackwalker.py')
-rwxr-xr-x | third_party/libwebrtc/build/android/stacktrace/crashpad_stackwalker.py | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/android/stacktrace/crashpad_stackwalker.py b/third_party/libwebrtc/build/android/stacktrace/crashpad_stackwalker.py new file mode 100755 index 0000000000..ab5dfe195c --- /dev/null +++ b/third_party/libwebrtc/build/android/stacktrace/crashpad_stackwalker.py @@ -0,0 +1,175 @@ +#!/usr/bin/env vpython3 +# +# 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, +# dump_syms, minidump_dump and minidump_stackwalk. + +import argparse +import logging +import os +import posixpath +import re +import sys +import shutil +import subprocess +import tempfile + +_BUILD_ANDROID_PATH = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..')) +sys.path.append(_BUILD_ANDROID_PATH) +import devil_chromium +from devil.android import device_utils +from devil.utils import timeout_retry + + +def _CreateSymbolsDir(build_path, dynamic_library_names): + generator = os.path.normpath( + os.path.join(_BUILD_ANDROID_PATH, '..', '..', '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', + syms_dir, + '--build-dir', + build_path, + '--binary', + unstripped_library_path, + '--platform', + 'android', + ] + 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): + if not device.PathExists(crashpad_dump_path): + logging.warning('Crashpad dump directory does not exist: %s', + crashpad_dump_path) + return None + latest = None + latest_timestamp = 0 + for crashpad_file in device.ListDirectory(crashpad_dump_path): + if crashpad_file.endswith('.dmp'): + stat = device.StatPath(posixpath.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 because %s is not found, ' + 'default to: %s', dumper_path, 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.lstrip().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: + logging.warning( + 'Could not find any library name in the dump, ' + 'default to: %s', default_library_name) + 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', 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() + + stackwalk_path = os.path.join(args.build_path, 'minidump_stackwalk') + if not os.path.exists(stackwalk_path): + logging.error('Missing minidump_stackwalk executable') + return 1 + + devil_chromium.Initialize(output_directory=args.build_path, + adb_path=args.adb_path) + device = device_utils.DeviceUtils(args.device) + + device_crashpad_path = posixpath.join(args.chrome_cache_path, 'Crashpad', + 'pending') + + def CrashpadDumpExists(): + return _ChooseLatestCrashpadDump(device, device_crashpad_path) + + crashpad_file = timeout_retry.WaitFor( + CrashpadDumpExists, wait_period=1, max_tries=9) + if not crashpad_file: + logging.error('Could not locate a crashpad dump') + return 1 + + dump_dir = tempfile.mkdtemp() + symbols_dir = None + try: + device.PullFile( + device_path=posixpath.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 = [stackwalk_path, 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()) |