| #!/usr/bin/env vpython3 |
| # |
| # Copyright 2016 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| |
| 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()) |