123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- # Copyright 2021 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.
- """Symbolizes a perfetto trace file."""
- import logging
- import os
- import shutil
- import subprocess
- import sys
- import tempfile
- sys.path.insert(
- 0,
- os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 'third_party',
- 'catapult', 'systrace'))
- import breakpad_file_extractor
- import flag_utils
- import metadata_extractor
- import rename_breakpad
- import symbol_fetcher
- def SymbolizeTrace(trace_file, options):
- """Symbolizes a perfetto trace file.
- Args:
- trace_file: path to proto trace file to symbolize.
- options: The options set by the command line args.
- Raises:
- Exception: if breakpad_output_dir is not empty or if path to local breakpad
- directory is invalid.
- """
- print('Symbolizing file')
- need_cleanup = False
- if options.local_breakpad_dir is None:
- breakpad_file_extractor.EnsureDumpSymsBinary(options.dump_syms_path,
- options.local_build_dir)
- # Ensure valid |breakpad_output_dir|
- if options.breakpad_output_dir is None:
- # Create a temp dir if output dir is not provided.
- # Temp dir must be cleaned up later.
- options.breakpad_output_dir = tempfile.mkdtemp()
- need_cleanup = True
- flag_utils.GetTracingLogger().warning(
- 'Created temporary directory to hold symbol files. These symbols '
- 'will be cleaned up after symbolization. Please specify '
- '--breakpad_output_dir=<cached_dir> to save the symbols, if you need '
- 'to profile multiple times. The future runs need to use '
- '--local_breakpad_dir=<cached_dir> flag so the symbolizer uses the '
- 'cache.')
- else:
- if os.path.isdir(options.breakpad_output_dir):
- if os.listdir(options.breakpad_output_dir):
- raise Exception('Breakpad output directory is not empty: ' +
- options.breakpad_output_dir)
- else:
- os.makedirs(options.breakpad_output_dir)
- flag_utils.GetTracingLogger().debug(
- 'Created directory to hold symbol files.')
- else:
- if not os.path.isdir(options.local_breakpad_dir):
- raise Exception('Local breakpad directory is not valid.')
- options.breakpad_output_dir = options.local_breakpad_dir
- _EnsureBreakpadSymbols(trace_file, options)
- # Set output file to write trace data and symbols to.
- if options.output_file is None:
- options.output_file = os.path.join(
- os.path.dirname(trace_file),
- os.path.basename(trace_file) + '_symbolized_trace')
- _Symbolize(trace_file, options.symbolizer_path, options.breakpad_output_dir,
- options.output_file)
- print('Symbolized trace saved to: ' + os.path.abspath(options.output_file))
- # Cleanup
- if need_cleanup:
- flag_utils.GetTracingLogger().debug('Cleaning up symbol files.')
- shutil.rmtree(options.breakpad_output_dir)
- def _EnsureBreakpadSymbols(trace_file, options):
- """Ensures that there are breakpad symbols to symbolize with.
- Args:
- trace_file: The trace file to be symbolized.
- options: The options set by the command line args. This is used to check if
- symbols need to be fetched, extracted, or if they are already present.
- Raises:
- Exception: if no breakpad files could be extracted.
- """
- # If |options.local_breakpad_dir| is not None, then this can be skipped and
- # |trace_file| can be symbolized using those symbols.
- if options.local_breakpad_dir is not None:
- return
- # Extract Metadata
- flag_utils.GetTracingLogger().info('Extracting proto trace metadata.')
- trace_metadata = metadata_extractor.MetadataExtractor(
- options.trace_processor_path, trace_file)
- trace_metadata.Initialize()
- flag_utils.GetTracingLogger().info(trace_metadata)
- if options.local_build_dir is not None:
- # Extract breakpad symbol files from binaries in |options.local_build_dir|.
- if not breakpad_file_extractor.ExtractBreakpadFiles(
- options.dump_syms_path,
- options.local_build_dir,
- options.breakpad_output_dir,
- search_unstripped=True,
- module_ids=breakpad_file_extractor.GetModuleIdsToSymbolize(
- trace_metadata)):
- raise Exception(
- 'No breakpad symbols could be extracted from files in: %s xor %s' %
- (options.local_build_dir,
- os.path.join(options.local_build_dir, 'lib.unstripped')))
- rename_breakpad.RenameBreakpadFiles(options.breakpad_output_dir,
- options.breakpad_output_dir)
- return
- # Fetch trace breakpad symbols from GCS
- flag_utils.GetTracingLogger().info(
- 'Fetching and extracting trace breakpad symbols.')
- symbol_fetcher.GetTraceBreakpadSymbols(options.cloud_storage_bucket,
- trace_metadata,
- options.breakpad_output_dir,
- options.dump_syms_path)
- def _Symbolize(trace_file, symbolizer_path, breakpad_output_dir, output_file):
- """"Symbolizes a trace.
- Args:
- trace_file: The trace file to be symbolized.
- symbolizer_path: The path to the trace_to_text tool to use.
- breakpad_output_dir: Contains the breakpad symbols to use for symbolization.
- output_file: The path to the file to output symbols to.
- Raises:
- RuntimeError: If _RunSymbolizer() fails to execute the given command.
- """
- # Set environment variable as location of stored breakpad files.
- symbolize_env = os.environ.copy()
- symbolize_env['BREAKPAD_SYMBOL_DIR'] = os.path.join(breakpad_output_dir, '')
- cmd = [symbolizer_path, 'symbolize', trace_file]
- # Open temporary file where symbols can be stored.
- with tempfile.TemporaryFile(mode='wb+') as temp_symbol_file:
- _RunSymbolizer(cmd, symbolize_env, temp_symbol_file)
- # Write trace data and symbol data to the same file.
- temp_symbol_file.seek(0)
- symbol_data = temp_symbol_file.read()
- with open(trace_file, 'rb') as f:
- trace_data = f.read()
- with open(output_file, 'wb') as f:
- f.write(trace_data)
- f.write(symbol_data)
- flag_utils.GetTracingLogger().info(
- 'Symbolized %s(%d bytes) with %d bytes of symbol data',
- os.path.abspath(trace_file), len(trace_data), len(symbol_data))
- def _RunSymbolizer(cmd, env, stdout):
- proc = subprocess.Popen(cmd, env=env, stdout=stdout, stderr=subprocess.PIPE)
- out, stderr = proc.communicate()
- flag_utils.GetTracingLogger().debug('STDOUT:%s', str(out))
- flag_utils.GetTracingLogger().debug('STDERR:%s', str(stderr))
- if proc.returncode != 0:
- raise RuntimeError(str(stderr))
|