| #!/usr/bin/env vpython | |
| # | |
| # Copyright 2016 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. | |
| from __future__ import print_function | |
| import argparse | |
| import os | |
| import re | |
| import sys | |
| import tempfile | |
| if __name__ == '__main__': | |
| sys.path.append(os.path.join(os.path.dirname(__file__), '..')) | |
| from pylib.constants import host_paths | |
| if host_paths.DEVIL_PATH not in sys.path: | |
| sys.path.append(host_paths.DEVIL_PATH) | |
| from devil.utils import cmd_helper | |
| _MICRODUMP_BEGIN = re.compile( | |
| '.*google-breakpad: -----BEGIN BREAKPAD MICRODUMP-----') | |
| _MICRODUMP_END = re.compile( | |
| '.*google-breakpad: -----END BREAKPAD MICRODUMP-----') | |
| """ Example Microdump | |
| <timestamp> 6270 6131 F google-breakpad: -----BEGIN BREAKPAD MICRODUMP----- | |
| <timestamp> 6270 6131 F google-breakpad: V Chrome_Android:54.0.2790.0 | |
| ... | |
| <timestamp> 6270 6131 F google-breakpad: -----END BREAKPAD MICRODUMP----- | |
| """ | |
| def GetMicroDumps(dump_path): | |
| """Returns all microdumps found in given log file | |
| Args: | |
| dump_path: Path to the log file. | |
| Returns: | |
| List of all microdumps as lists of lines. | |
| """ | |
| with open(dump_path, 'r') as d: | |
| data = d.read() | |
| all_dumps = [] | |
| current_dump = None | |
| for line in data.splitlines(): | |
| if current_dump is not None: | |
| if _MICRODUMP_END.match(line): | |
| current_dump.append(line) | |
| all_dumps.append(current_dump) | |
| current_dump = None | |
| else: | |
| current_dump.append(line) | |
| elif _MICRODUMP_BEGIN.match(line): | |
| current_dump = [] | |
| current_dump.append(line) | |
| return all_dumps | |
| def SymbolizeMicroDump(stackwalker_binary_path, dump, symbols_path): | |
| """Runs stackwalker on microdump. | |
| Runs the stackwalker binary at stackwalker_binary_path on a given microdump | |
| using the symbols at symbols_path. | |
| Args: | |
| stackwalker_binary_path: Path to the stackwalker binary. | |
| dump: The microdump to run the stackwalker on. | |
| symbols_path: Path the the symbols file to use. | |
| Returns: | |
| Output from stackwalker tool. | |
| """ | |
| with tempfile.NamedTemporaryFile() as tf: | |
| for l in dump: | |
| tf.write('%s\n' % l) | |
| cmd = [stackwalker_binary_path, tf.name, symbols_path] | |
| return cmd_helper.GetCmdOutput(cmd) | |
| def AddArguments(parser): | |
| parser.add_argument('--stackwalker-binary-path', required=True, | |
| help='Path to stackwalker binary.') | |
| parser.add_argument('--stack-trace-path', required=True, | |
| help='Path to stacktrace containing microdump.') | |
| parser.add_argument('--symbols-path', required=True, | |
| help='Path to symbols file.') | |
| parser.add_argument('--output-file', | |
| help='Path to dump stacktrace output to') | |
| def _PrintAndLog(line, fp): | |
| if fp: | |
| fp.write('%s\n' % line) | |
| print(line) | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| AddArguments(parser) | |
| args = parser.parse_args() | |
| micro_dumps = GetMicroDumps(args.stack_trace_path) | |
| if not micro_dumps: | |
| print('No microdump found. Exiting.') | |
| return 0 | |
| symbolized_dumps = [] | |
| for micro_dump in micro_dumps: | |
| symbolized_dumps.append(SymbolizeMicroDump( | |
| args.stackwalker_binary_path, micro_dump, args.symbols_path)) | |
| try: | |
| fp = open(args.output_file, 'w') if args.output_file else None | |
| _PrintAndLog('%d microdumps found.' % len(micro_dumps), fp) | |
| _PrintAndLog('---------- Start output from stackwalker ----------', fp) | |
| for index, symbolized_dump in list(enumerate(symbolized_dumps)): | |
| _PrintAndLog( | |
| '------------------ Start dump %d ------------------' % index, fp) | |
| _PrintAndLog(symbolized_dump, fp) | |
| _PrintAndLog( | |
| '------------------- End dump %d -------------------' % index, fp) | |
| _PrintAndLog('----------- End output from stackwalker -----------', fp) | |
| except Exception: | |
| if fp: | |
| fp.close() | |
| raise | |
| return 0 | |
| if __name__ == '__main__': | |
| sys.exit(main()) |