symbolize_trace.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. # Copyright 2021 The Chromium Authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. """Symbolizes a perfetto trace file."""
  5. import logging
  6. import os
  7. import shutil
  8. import subprocess
  9. import sys
  10. import tempfile
  11. sys.path.insert(
  12. 0,
  13. os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 'third_party',
  14. 'catapult', 'systrace'))
  15. import breakpad_file_extractor
  16. import flag_utils
  17. import metadata_extractor
  18. import rename_breakpad
  19. import symbol_fetcher
  20. def SymbolizeTrace(trace_file, options):
  21. """Symbolizes a perfetto trace file.
  22. Args:
  23. trace_file: path to proto trace file to symbolize.
  24. options: The options set by the command line args.
  25. Raises:
  26. Exception: if breakpad_output_dir is not empty or if path to local breakpad
  27. directory is invalid.
  28. """
  29. print('Symbolizing file')
  30. need_cleanup = False
  31. if options.local_breakpad_dir is None:
  32. breakpad_file_extractor.EnsureDumpSymsBinary(options.dump_syms_path,
  33. options.local_build_dir)
  34. # Ensure valid |breakpad_output_dir|
  35. if options.breakpad_output_dir is None:
  36. # Create a temp dir if output dir is not provided.
  37. # Temp dir must be cleaned up later.
  38. options.breakpad_output_dir = tempfile.mkdtemp()
  39. need_cleanup = True
  40. flag_utils.GetTracingLogger().warning(
  41. 'Created temporary directory to hold symbol files. These symbols '
  42. 'will be cleaned up after symbolization. Please specify '
  43. '--breakpad_output_dir=<cached_dir> to save the symbols, if you need '
  44. 'to profile multiple times. The future runs need to use '
  45. '--local_breakpad_dir=<cached_dir> flag so the symbolizer uses the '
  46. 'cache.')
  47. else:
  48. if os.path.isdir(options.breakpad_output_dir):
  49. if os.listdir(options.breakpad_output_dir):
  50. raise Exception('Breakpad output directory is not empty: ' +
  51. options.breakpad_output_dir)
  52. else:
  53. os.makedirs(options.breakpad_output_dir)
  54. flag_utils.GetTracingLogger().debug(
  55. 'Created directory to hold symbol files.')
  56. else:
  57. if not os.path.isdir(options.local_breakpad_dir):
  58. raise Exception('Local breakpad directory is not valid.')
  59. options.breakpad_output_dir = options.local_breakpad_dir
  60. _EnsureBreakpadSymbols(trace_file, options)
  61. # Set output file to write trace data and symbols to.
  62. if options.output_file is None:
  63. options.output_file = os.path.join(
  64. os.path.dirname(trace_file),
  65. os.path.basename(trace_file) + '_symbolized_trace')
  66. _Symbolize(trace_file, options.symbolizer_path, options.breakpad_output_dir,
  67. options.output_file)
  68. print('Symbolized trace saved to: ' + os.path.abspath(options.output_file))
  69. # Cleanup
  70. if need_cleanup:
  71. flag_utils.GetTracingLogger().debug('Cleaning up symbol files.')
  72. shutil.rmtree(options.breakpad_output_dir)
  73. def _EnsureBreakpadSymbols(trace_file, options):
  74. """Ensures that there are breakpad symbols to symbolize with.
  75. Args:
  76. trace_file: The trace file to be symbolized.
  77. options: The options set by the command line args. This is used to check if
  78. symbols need to be fetched, extracted, or if they are already present.
  79. Raises:
  80. Exception: if no breakpad files could be extracted.
  81. """
  82. # If |options.local_breakpad_dir| is not None, then this can be skipped and
  83. # |trace_file| can be symbolized using those symbols.
  84. if options.local_breakpad_dir is not None:
  85. return
  86. # Extract Metadata
  87. flag_utils.GetTracingLogger().info('Extracting proto trace metadata.')
  88. trace_metadata = metadata_extractor.MetadataExtractor(
  89. options.trace_processor_path, trace_file)
  90. trace_metadata.Initialize()
  91. flag_utils.GetTracingLogger().info(trace_metadata)
  92. if options.local_build_dir is not None:
  93. # Extract breakpad symbol files from binaries in |options.local_build_dir|.
  94. if not breakpad_file_extractor.ExtractBreakpadFiles(
  95. options.dump_syms_path,
  96. options.local_build_dir,
  97. options.breakpad_output_dir,
  98. search_unstripped=True,
  99. module_ids=breakpad_file_extractor.GetModuleIdsToSymbolize(
  100. trace_metadata)):
  101. raise Exception(
  102. 'No breakpad symbols could be extracted from files in: %s xor %s' %
  103. (options.local_build_dir,
  104. os.path.join(options.local_build_dir, 'lib.unstripped')))
  105. rename_breakpad.RenameBreakpadFiles(options.breakpad_output_dir,
  106. options.breakpad_output_dir)
  107. return
  108. # Fetch trace breakpad symbols from GCS
  109. flag_utils.GetTracingLogger().info(
  110. 'Fetching and extracting trace breakpad symbols.')
  111. symbol_fetcher.GetTraceBreakpadSymbols(options.cloud_storage_bucket,
  112. trace_metadata,
  113. options.breakpad_output_dir,
  114. options.dump_syms_path)
  115. def _Symbolize(trace_file, symbolizer_path, breakpad_output_dir, output_file):
  116. """"Symbolizes a trace.
  117. Args:
  118. trace_file: The trace file to be symbolized.
  119. symbolizer_path: The path to the trace_to_text tool to use.
  120. breakpad_output_dir: Contains the breakpad symbols to use for symbolization.
  121. output_file: The path to the file to output symbols to.
  122. Raises:
  123. RuntimeError: If _RunSymbolizer() fails to execute the given command.
  124. """
  125. # Set environment variable as location of stored breakpad files.
  126. symbolize_env = os.environ.copy()
  127. symbolize_env['BREAKPAD_SYMBOL_DIR'] = os.path.join(breakpad_output_dir, '')
  128. cmd = [symbolizer_path, 'symbolize', trace_file]
  129. # Open temporary file where symbols can be stored.
  130. with tempfile.TemporaryFile(mode='wb+') as temp_symbol_file:
  131. _RunSymbolizer(cmd, symbolize_env, temp_symbol_file)
  132. # Write trace data and symbol data to the same file.
  133. temp_symbol_file.seek(0)
  134. symbol_data = temp_symbol_file.read()
  135. with open(trace_file, 'rb') as f:
  136. trace_data = f.read()
  137. with open(output_file, 'wb') as f:
  138. f.write(trace_data)
  139. f.write(symbol_data)
  140. flag_utils.GetTracingLogger().info(
  141. 'Symbolized %s(%d bytes) with %d bytes of symbol data',
  142. os.path.abspath(trace_file), len(trace_data), len(symbol_data))
  143. def _RunSymbolizer(cmd, env, stdout):
  144. proc = subprocess.Popen(cmd, env=env, stdout=stdout, stderr=subprocess.PIPE)
  145. out, stderr = proc.communicate()
  146. flag_utils.GetTracingLogger().debug('STDOUT:%s', str(out))
  147. flag_utils.GetTracingLogger().debug('STDERR:%s', str(stderr))
  148. if proc.returncode != 0:
  149. raise RuntimeError(str(stderr))