Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(228)

Unified Diff: tools/trace/symbolize.py

Issue 1839503002: [tracing] Add native allocation tracing mode. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add type to StackFrame; format thread name Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/trace/symbolize.py
diff --git a/tools/trace/symbolize.py b/tools/trace/symbolize.py
new file mode 100755
index 0000000000000000000000000000000000000000..aaa22084e5cf911a329e6b503ffaa0c2e81c7050
--- /dev/null
+++ b/tools/trace/symbolize.py
@@ -0,0 +1,281 @@
+#!/usr/bin/env python
Primiano Tucci (use gerrit) 2016/04/07 15:51:57 Did you figure out with perezju where to move this
+# Copyright (c) 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.
+
+import argparse
+import bisect
+import collections
+import gzip
+import json
+import os
+import posixpath
+import sys
+
+# This is how tools/binary_size/run_binary_size_analysis.py does it.
+# See http://crbug.com/375725
+elf_symbolizer_path = os.path.abspath(os.path.join(
+ os.path.dirname(__file__),
+ '..',
+ '..',
+ 'build',
+ 'android',
+ 'pylib'))
+sys.path.append(elf_symbolizer_path)
+import symbols.elf_symbolizer as elf_symbolizer # pylint: disable=F0401
+
+
+class ProcessMemoryMaps(object):
+ Region = collections.namedtuple(
+ 'Region',
+ ['start_address', 'end_address', 'file_name'])
+
+ def __init__(self, value):
+ self._regions = []
+ for region_value in value['vm_regions']:
+ start_address = long(region_value['sa'], 16)
+ size = long(region_value['sz'], 16)
+ self._regions.append(self.Region(
+ start_address,
+ start_address + size,
+ region_value['mf']))
+
+ self._regions.sort()
+
+ # Remove duplicates; check for overlaps
+ region_index = 0
+ while region_index < len(self._regions):
+ region = self._regions[region_index]
+ if region_index != 0:
+ last_region = self._regions[region_index - 1]
+ if last_region == region:
+ del self._regions[region_index]
+ continue
+
+ assert region.start_address >= last_region.end_address, \
+ 'Regions {} and {} overlap.'.format(last_region, region)
+ region_index += 1
+
+ @property
+ def regions(self):
+ return self._regions
+
+ def FindRegion(self, address):
+ if not self._regions:
+ return None
+ region_index = bisect.bisect_left(
+ self._regions, self.Region(address, None, None))
+ region = self._regions[region_index]
+ if region.start_address > address and region_index != 0:
+ region_index -= 1
+ region = self._regions[region_index]
+ if address >= region.start_address and address < region.end_address:
+ return region
+ return None
+
+
+class StackFrames(object):
+ def __init__(self, value):
+ self._frames_value = value
+
+ def CollectPCs(self):
+ return [pc for _, pc in self._IteratePCs()]
+
+ def SymbolizePCs(self, pc_symbol_map):
+ symbolized = False
+ for frame, pc in self._IteratePCs():
+ if pc in pc_symbol_map:
+ frame['name'] = pc_symbol_map[pc]
+ symbolized = True
+ return symbolized
+
+ # Details
+
+ _PC_TAG = 'pc:'
+
+ def _ParsePC(self, name):
+ if not name.startswith(self._PC_TAG):
+ return None
+ return long(name[len(self._PC_TAG):], 16)
+
+ def _IteratePCs(self):
+ for frame in self._frames_value.itervalues():
+ pc = self._ParsePC(frame['name'])
+ if pc is not None:
+ yield (frame, pc)
+
+
+class Symbolizer(object):
+ def __init__(self, binary_path, addr2line_path):
+ self._elf_symbolizer = None
+ self._pc_symbol_map = {}
+ self._failed_pcs = set()
+ self._queued_pcs = set()
+
+ if os.path.isfile(binary_path):
+ self._elf_symbolizer = elf_symbolizer.ELFSymbolizer(
+ binary_path,
+ addr2line_path,
+ self._SymbolizerCallback)
+
+ @property
+ def pc_symbol_map(self):
+ return self._pc_symbol_map
+
+ @property
+ def failed_pcs(self):
+ return self._failed_pcs
+
+ def SymbolizeAsync(self, pc, region):
+ if self._elf_symbolizer is None:
+ self._failed_pcs.add(pc)
+ return
+
+ if pc not in self._queued_pcs:
+ self._elf_symbolizer.SymbolizeAsync(int(pc - region.start_address), pc)
+ self._queued_pcs.add(pc)
+
+ def Join(self):
+ if self._elf_symbolizer is None:
+ return
+
+ self._elf_symbolizer.Join()
+
+ # Details
+
+ def _SymbolizerCallback(self, sym_info, pc):
+ self._queued_pcs.remove(pc)
+ if sym_info.name:
+ self._pc_symbol_map[pc] = sym_info.name
+ else:
+ self._failed_pcs.add(pc)
+
+
+class Process(object):
+ def __init__(self, pid):
+ self.pid = pid
+ self.name = None
+ self.mmaps = None
+ self.stack_frames = None
+
+
+def CollectProcesses(trace):
+ process_map = {}
+
+ for event in trace['traceEvents']:
+ name = event.get('name')
+ if not name:
+ continue
+
+ pid = event['pid']
+ process = process_map.get(pid)
+ if process is None:
+ process = Process(pid)
+ process_map[pid] = process
+
+ if name == 'process_name':
+ process.name = event['args']['name']
+ elif name == 'stackFrames':
+ value = event['args']['stackFrames']
+ process.stack_frames = StackFrames(value)
+ elif name == 'periodic_interval':
+ value = event['args']['dumps'].get('process_mmaps')
+ if value:
+ process.mmaps = ProcessMemoryMaps(value)
+
+ processes = []
+ for process in process_map.itervalues():
+ if process.mmaps is not None and process.stack_frames is not None:
+ processes.append(process)
+
+ return processes
+
+
+def SymbolizeProcess(process, addr2line_path):
+ pcs = process.stack_frames.CollectPCs()
+ if not pcs:
+ return False
+
+ print 'Symbolizing {} ({})...'.format(process.name, process.pid)
+
+ def _SubPrintf(message, *args):
+ print (' ' + message).format(*args)
+
+ symbolizer_map = {}
+ unresolved_pcs = set()
+ for pc in pcs:
+ region = process.mmaps.FindRegion(pc)
+ if region is None:
+ unresolved_pcs.add(pc)
+ continue
+ symbolizer = symbolizer_map.get(region.file_name)
+ if symbolizer is None:
+ symbolizer = Symbolizer(region.file_name, addr2line_path)
+ symbolizer_map[region.file_name] = symbolizer
+ symbolizer.SymbolizeAsync(pc, region)
+
+ if unresolved_pcs:
+ _SubPrintf('{} PCs were not resolved.', len(unresolved_pcs))
+
+ pc_symbol_map = {}
+ for file_path, symbolizer in symbolizer_map.iteritems():
+ symbolizer.Join()
+ _SubPrintf('{}: {} PCs symbolized ({} failed)',
+ file_path,
+ len(symbolizer.pc_symbol_map),
+ len(symbolizer.failed_pcs))
+ pc_symbol_map.update(symbolizer.pc_symbol_map)
+
+ return process.stack_frames.SymbolizePCs(pc_symbol_map)
+
+
+def FindInSystemPath(binary_name):
+ paths = os.environ["PATH"].split(os.pathsep)
+ for path in paths:
+ binary_path = os.path.join(path, binary_name)
+ if os.path.isfile(binary_path):
+ return binary_path
+ return None
+
+
+def main():
+ BACKUP_FILE_TAG = '.BACKUP'
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('file', nargs=1,
+ help='Trace file to symbolize (.json or .json.gz)')
+ parser.add_argument('--no-backup', action='store_true',
+ help="Don't create {} files".format(BACKUP_FILE_TAG))
+ options = parser.parse_args()
+
+ trace_file_path = options.file[0]
+ def _OpenTraceFile(mode):
+ if trace_file_path.endswith('.gz'):
+ return gzip.open(trace_file_path, mode)
+ else:
+ return open(trace_file_path, mode)
+
+ with _OpenTraceFile('rb') as trace_file:
+ trace = json.load(trace_file)
+
+ processes = CollectProcesses(trace)
+
+ addr2line_path = FindInSystemPath('addr2line')
+
+ update_trace = False
+ for process in processes:
+ if SymbolizeProcess(process, addr2line_path):
+ update_trace = True
+
+ if update_trace:
+ if not options.no_backup:
+ print 'Backing up trace file...'
+ os.rename(trace_file_path, trace_file_path + BACKUP_FILE_TAG)
+
+ print 'Updating trace file...'
+ with _OpenTraceFile('wb') as trace_file:
+ json.dump(trace, trace_file)
+
+
+if __name__ == '__main__':
+ main()
« base/trace_event/malloc_dump_provider.cc ('K') | « base/trace_event/memory_dump_manager.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698