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

Side by Side 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: WalkStackFrames (wants frame pointers) 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 unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
Primiano Tucci (use gerrit) 2016/04/01 15:56:28 not sure whether this is the right folder for this
2 # Copyright (c) 2016 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 import argparse
7 import bisect
8 import collections
9 import gzip
10 import json
11 import os
12 import posixpath
13 import sys
14
15 # This is how tools/binary_size/run_binary_size_analysis.py does it.
16 # See http://crbug.com/375725
17 elf_symbolizer_path = os.path.abspath(os.path.join(
18 os.path.dirname(__file__),
19 '..',
20 '..',
21 'build',
22 'android',
23 'pylib'))
24 sys.path.append(elf_symbolizer_path)
25 import symbols.elf_symbolizer as elf_symbolizer # pylint: disable=F0401
26
27
28 class ProcessMemoryMaps(object):
29 Region = collections.namedtuple(
30 'Region',
31 ['start_address', 'end_address', 'file_name'])
32
33 def __init__(self, value):
34 self._regions = []
35 for region_value in value['vm_regions']:
36 start_address = long(region_value['sa'], 16)
37 size = long(region_value['sz'], 16)
38 self._regions.append(self.Region(
39 start_address,
40 start_address + size,
41 region_value['mf']))
42
43 self._regions.sort()
44
45 # Remove duplicates; check for overlaps
46 region_index = 0
47 while region_index < len(self._regions):
48 region = self._regions[region_index]
49 if region_index != 0:
50 last_region = self._regions[region_index - 1]
51 if last_region == region:
52 del self._regions[region_index]
53 continue
54
55 assert region.start_address >= last_region.end_address, \
56 'Regions {} and {} overlap.'.format(last_region, region)
57 region_index += 1
58
59 @property
60 def regions(self):
61 return self._regions
62
63 def FindRegion(self, address):
64 if not self._regions:
65 return None
66 region_index = bisect.bisect_left(
67 self._regions, self.Region(address, None, None))
68 region = self._regions[region_index]
69 if region.start_address > address and region_index != 0:
70 region_index -= 1
71 region = self._regions[region_index]
72 if address >= region.start_address and address < region.end_address:
73 return region
74 return None
75
76
77 class StackFrames(object):
78 def __init__(self, value):
79 self._frames_value = value
80
81 def CollectPCs(self):
82 return [pc for _, pc in self._IteratePCs()]
83
84 def SymbolizePCs(self, pc_symbol_map):
85 symbolized = False
86 for frame, pc in self._IteratePCs():
87 if pc in pc_symbol_map:
88 frame['name'] = pc_symbol_map[pc]
89 symbolized = True
90 return symbolized
91
92 # Details
93
94 _PC_TAG = 'pc:'
95
96 def _ParsePC(self, name):
97 if not name.startswith(self._PC_TAG):
98 return None
99 return long(name[len(self._PC_TAG):], 16)
100
101 def _IteratePCs(self):
102 for frame in self._frames_value.itervalues():
103 pc = self._ParsePC(frame['name'])
104 if pc is not None:
105 yield (frame, pc)
106
107
108 class Symbolizer(object):
109 def __init__(self, binary_path, addr2line_path):
110 self._elf_symbolizer = None
111 self._pc_symbol_map = {}
112 self._failed_pcs = set()
113 self._queued_pcs = set()
114
115 if os.path.isfile(binary_path):
116 self._elf_symbolizer = elf_symbolizer.ELFSymbolizer(
117 binary_path,
118 addr2line_path,
119 self._SymbolizerCallback)
120
121 @property
122 def pc_symbol_map(self):
123 return self._pc_symbol_map
124
125 @property
126 def failed_pcs(self):
127 return self._failed_pcs
128
129 def SymbolizeAsync(self, pc, region):
130 if self._elf_symbolizer is None:
131 self._failed_pcs.add(pc)
132 return
133
134 if pc not in self._queued_pcs:
135 self._elf_symbolizer.SymbolizeAsync(int(pc - region.start_address), pc)
136 self._queued_pcs.add(pc)
137
138 def Join(self):
139 if self._elf_symbolizer is None:
140 return
141
142 self._elf_symbolizer.Join()
143
144 # Details
145
146 def _SymbolizerCallback(self, sym_info, pc):
147 self._queued_pcs.remove(pc)
148 if sym_info.name:
149 self._pc_symbol_map[pc] = sym_info.name
150 else:
151 self._failed_pcs.add(pc)
152
153
154 class Process(object):
155 def __init__(self, pid):
156 self.pid = pid
157 self.name = None
158 self.mmaps = None
159 self.stack_frames = None
160
161
162 def CollectProcesses(trace):
163 process_map = {}
164
165 for event in trace['traceEvents']:
166 name = event.get('name')
167 if not name:
168 continue
169
170 pid = event['pid']
171 process = process_map.get(pid)
172 if process is None:
173 process = Process(pid)
174 process_map[pid] = process
175
176 if name == 'process_name':
177 process.name = event['args']['name']
178 elif name == 'stackFrames':
179 value = event['args']['stackFrames']
180 process.stack_frames = StackFrames(value)
181 elif name == 'periodic_interval':
182 value = event['args']['dumps'].get('process_mmaps')
183 if value:
184 process.mmaps = ProcessMemoryMaps(value)
185
186 processes = []
187 for process in process_map.itervalues():
188 if process.mmaps is not None and process.stack_frames is not None:
189 processes.append(process)
190
191 return processes
192
193
194 def SymbolizeProcess(process, addr2line_path):
195 pcs = process.stack_frames.CollectPCs()
196 if not pcs:
197 return False
198
199 print 'Symbolizing {} ({})...'.format(process.name, process.pid)
200
201 def _SubPrintf(message, *args):
202 print (' ' + message).format(*args)
203
204 symbolizer_map = {}
205 unresolved_pcs = set()
206 for pc in pcs:
207 region = process.mmaps.FindRegion(pc)
208 if region is None:
209 unresolved_pcs.add(pc)
210 continue
211 symbolizer = symbolizer_map.get(region.file_name)
212 if symbolizer is None:
213 symbolizer = Symbolizer(region.file_name, addr2line_path)
214 symbolizer_map[region.file_name] = symbolizer
215 symbolizer.SymbolizeAsync(pc, region)
216
217 if unresolved_pcs:
218 _SubPrintf('{} PCs were not resolved.', len(unresolved_pcs))
219
220 pc_symbol_map = {}
221 for file_path, symbolizer in symbolizer_map.iteritems():
222 symbolizer.Join()
223 _SubPrintf('{}: {} PCs symbolized ({} failed)',
224 file_path,
225 len(symbolizer.pc_symbol_map),
226 len(symbolizer.failed_pcs))
227 pc_symbol_map.update(symbolizer.pc_symbol_map)
228
229 return process.stack_frames.SymbolizePCs(pc_symbol_map)
230
231
232 def FindInSystemPath(binary_name):
233 paths = os.environ["PATH"].split(os.pathsep)
234 for path in paths:
235 binary_path = os.path.join(path, binary_name)
236 if os.path.isfile(binary_path):
237 return binary_path
238 return None
239
240
241 def main():
242 BACKUP_FILE_TAG = '.BACKUP'
243
244 parser = argparse.ArgumentParser()
245 parser.add_argument('file', nargs=1,
246 help='Trace file to symbolize (.json or .json.gz)')
247 parser.add_argument('--no-backup', action='store_true',
248 help="Don't create {} files".format(BACKUP_FILE_TAG))
249 options = parser.parse_args()
250
251 trace_file_path = options.file[0]
252 def _OpenTraceFile(mode):
253 if trace_file_path.endswith('.gz'):
254 return gzip.open(trace_file_path, mode)
255 else:
256 return open(trace_file_path, mode)
257
258 with _OpenTraceFile('rb') as trace_file:
259 trace = json.load(trace_file)
260
261 processes = CollectProcesses(trace)
262
263 addr2line_path = FindInSystemPath('addr2line')
264
265 update_trace = False
266 for process in processes:
267 if SymbolizeProcess(process, addr2line_path):
268 update_trace = True
269
270 if update_trace:
271 if not options.no_backup:
272 print 'Backing up trace file...'
273 os.rename(trace_file_path, trace_file_path + BACKUP_FILE_TAG)
274
275 print 'Updating trace file...'
276 with _OpenTraceFile('wb') as trace_file:
277 json.dump(trace, trace_file)
278
279
280 if __name__ == '__main__':
281 main()
OLDNEW
« base/trace_event/heap_profiler_heap_dump_writer.cc ('K') | « build/config/allocator.gni ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698