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

Side by Side Diff: tracing/bin/symbolize_trace

Issue 2605183002: Update symbolize_trace to work on macOS. (Closed)
Patch Set: comments from dskiba. Created 3 years, 10 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2016 The Chromium Authors. All rights reserved. 2 # Copyright 2016 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 import argparse 6 import argparse
7 import bisect 7 import bisect
8 import collections 8 import collections
9 import gzip 9 import gzip
10 import json 10 import json
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
45 45
46 def FindInSystemPath(binary_name): 46 def FindInSystemPath(binary_name):
47 paths = os.environ['PATH'].split(os.pathsep) 47 paths = os.environ['PATH'].split(os.pathsep)
48 for path in paths: 48 for path in paths:
49 binary_path = os.path.join(path, binary_name) 49 binary_path = os.path.join(path, binary_name)
50 if os.path.isfile(binary_path): 50 if os.path.isfile(binary_path):
51 return binary_path 51 return binary_path
52 return None 52 return None
53 53
54 54
55 class Symbolizer(object):
56 # Encapsulates platform-specific symbolization logic.
57 def __init__(self):
58 self.is_mac = sys.platform == 'darwin'
59 if self.is_mac:
60 self.binary = 'atos'
61 else:
62 self.binary = 'addr2line'
63 self.symbolizer_path = FindInSystemPath(self.binary)
64
65 def _SymbolizeLinuxAndAndroid(self, sym_file):
DmitrySkiba 2017/02/06 19:28:33 Argument should be 'symfile' (without underscore).
erikchen 2017/02/11 02:45:23 Done.
66 def _SymbolizerCallback(sym_info, frames):
67 # Unwind inline chain to the top.
68 while sym_info.inlined_by:
69 sym_info = sym_info.inlined_by
70
71 symbolized_name = sym_info.name if sym_info.name else unsymbolized_name
72 for frame in frames:
73 frame.name = symbolized_name
74
75 symbolizer = elf_symbolizer.ELFSymbolizer(symfile.symbolizable_path,
76 self.symbolizer_path,
77 _SymbolizerCallback,
78 inlines=True)
79
80 for address, frames in symfile.frames_by_address.iteritems():
81 # SymbolizeAsync() asserts that the type of address is int. We operate
82 # on longs (since they are raw pointers possibly from 64-bit processes).
83 # It's OK to cast here because we're passing relative PC, which should
84 # always fit into int.
85 symbolizer.SymbolizeAsync(int(address), frames)
86
87 symbolizer.Join()
88
89 def _SymbolizeMac(self, symfile):
90 chars_max = int(subprocess.check_output("getconf ARG_MAX", shell=True))
91
92 # 16 for the address, 2 for "0x", 1 for the space
93 chars_per_address = 19
94 chars_for_other_arguments = 2000
DmitrySkiba 2017/02/06 19:28:34 Since symfile doesn't change in this function, we
erikchen 2017/02/11 02:45:23 Done.
95
96 # The maximum number of inputs that can be processed at once is limited by
97 # ARG_MAX. This currently evalutes to ~13000 on macOS.
98 max_inputs = (chars_max - chars_for_other_arguments) / chars_per_address
99
100 all_keys = symfile.frames_by_address.keys()
101 while len(all_keys):
102 input_count = min(len(all_keys), max_inputs)
103 keys_to_process = all_keys[0:input_count]
104
105 cmd = [self.symbolizer_path, '-arch', 'x86_64', '-l',
106 '0x0', '-o' , symfile.symbolizable_path]
107 cmd.extend([hex(int(x)) for x in keys_to_process])
108 output_array = subprocess.check_output(cmd).split('\n')
109 for i in range(len(keys_to_process)):
110 for frame in symfile.frames_by_address.values()[i]:
DmitrySkiba 2017/02/06 19:28:34 |i| should include offset (number of keys already
erikchen 2017/02/11 02:45:23 Good catch, thanks. Done.
111 frame.name = output_array[i]
DmitrySkiba 2017/02/06 19:28:34 Hmm, according to the docs, atos output can includ
erikchen 2017/02/11 02:45:23 what parsing logic are you referring to? Something
112 all_keys = all_keys[input_count:]
113
114 def Symbolize(self, symfile):
115 if self.is_mac:
116 self._SymbolizeMac(symfile)
117 else:
118 self._SymbolizeLinuxAndAndroid(symfile)
119
120
55 def IsSymbolizableFile(file_path): 121 def IsSymbolizableFile(file_path):
56 result = subprocess.check_output(['file', '-0', file_path]) 122 result = subprocess.check_output(['file', '-0', file_path])
57 type_string = result[result.find('\0') + 1:] 123 type_string = result[result.find('\0') + 1:]
58 return bool(re.match(r'\: (ELF|Mach-O) (32|64)-bit\b', type_string)) 124 return bool(re.match(r'.*(ELF|Mach-O) (32|64)-bit\b.*',
125 type_string, re.DOTALL))
59 126
60 127
61 class ProcessMemoryMaps(object): 128 class ProcessMemoryMaps(object):
62 """Represents 'process_mmaps' trace file entry.""" 129 """Represents 'process_mmaps' trace file entry."""
63 130
64 class Region(object): 131 class Region(object):
65 def __init__(self, start_address, size, file_path): 132 def __init__(self, start_address, size, file_path):
66 self._start_address = start_address 133 self._start_address = start_address
67 self._size = size 134 self._size = size
68 self._file_path = file_path 135 self._file_path = file_path
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
264 symfile = symfile_by_path.get(region.file_path) 331 symfile = symfile_by_path.get(region.file_path)
265 if symfile is None: 332 if symfile is None:
266 symfile = SymbolizableFile(region.file_path) 333 symfile = SymbolizableFile(region.file_path)
267 symfile_by_path[symfile.path] = symfile 334 symfile_by_path[symfile.path] = symfile
268 335
269 relative_pc = frame.pc - region.start_address 336 relative_pc = frame.pc - region.start_address
270 symfile.frames_by_address[relative_pc].append(frame) 337 symfile.frames_by_address[relative_pc].append(frame)
271 return symfile_by_path.values() 338 return symfile_by_path.values()
272 339
273 340
274 def SymbolizeFiles(symfiles, addr2line_path): 341 def SymbolizeFiles(symfiles, symbolizer):
275 """Symbolizes each file in the given list of SymbolizableFiles 342 """Symbolizes each file in the given list of SymbolizableFiles
276 and updates stack frames with symbolization results.""" 343 and updates stack frames with symbolization results."""
277 print 'Symbolizing...' 344 print 'Symbolizing...'
278 345
279 def _SubPrintf(message, *args): 346 def _SubPrintf(message, *args):
280 print (' ' + message).format(*args) 347 print (' ' + message).format(*args)
281 348
282 symbolized = False 349 symbolized = False
283 for symfile in symfiles: 350 for symfile in symfiles:
284 unsymbolized_name = '<{}>'.format( 351 unsymbolized_name = '<{}>'.format(
285 symfile.path if symfile.path else 'unnamed') 352 symfile.path if symfile.path else 'unnamed')
286 353
287 problem = None 354 problem = None
288 if not os.path.isabs(symfile.symbolizable_path): 355 if not os.path.isabs(symfile.symbolizable_path):
289 problem = 'not a file' 356 problem = 'not a file'
290 elif not os.path.isfile(symfile.symbolizable_path): 357 elif not os.path.isfile(symfile.symbolizable_path):
291 problem = "file doesn't exist" 358 problem = "file doesn't exist"
292 elif not IsSymbolizableFile(symfile.symbolizable_path): 359 elif not IsSymbolizableFile(symfile.symbolizable_path):
293 problem = 'file is not symbolizable' 360 problem = 'file is not symbolizable'
294 if problem: 361 if problem:
295 _SubPrintf("Won't symbolize {} PCs for '{}': {}.", 362 _SubPrintf("Won't symbolize {} PCs for '{}': {}.",
296 len(symfile.frames_by_address), 363 len(symfile.frames_by_address),
297 symfile.symbolizable_path, 364 symfile.symbolizable_path,
298 problem) 365 problem)
299 for frames in symfile.frames_by_address.itervalues(): 366 for frames in symfile.frames_by_address.itervalues():
300 for frame in frames: 367 for frame in frames:
301 frame.name = unsymbolized_name 368 frame.name = unsymbolized_name
302 continue 369 continue
303 370
304 def _SymbolizerCallback(sym_info, frames):
305 # Unwind inline chain to the top.
306 while sym_info.inlined_by:
307 sym_info = sym_info.inlined_by
308
309 symbolized_name = sym_info.name if sym_info.name else unsymbolized_name
310 for frame in frames:
311 frame.name = symbolized_name
312
313 symbolizer = elf_symbolizer.ELFSymbolizer(symfile.symbolizable_path,
314 addr2line_path,
315 _SymbolizerCallback,
316 inlines=True)
317
318 _SubPrintf('Symbolizing {} PCs from {}...', 371 _SubPrintf('Symbolizing {} PCs from {}...',
319 len(symfile.frames_by_address), 372 len(symfile.frames_by_address),
320 symfile.path) 373 symfile.path)
321 374
322 for address, frames in symfile.frames_by_address.iteritems(): 375 symbolizer.Symbolize(symfile)
323 # SymbolizeAsync() asserts that the type of address is int. We operate
324 # on longs (since they are raw pointers possibly from 64-bit processes).
325 # It's OK to cast here because we're passing relative PC, which should
326 # always fit into int.
327 symbolizer.SymbolizeAsync(int(address), frames)
328
329 symbolizer.Join()
330 symbolized = True 376 symbolized = True
331 377
332 return symbolized 378 return symbolized
333 379
334 380
335 def HaveFilesFromAndroid(symfiles): 381 def HaveFilesFromAndroid(symfiles):
336 return any(ANDROID_PATH_MATCHER.match(f.path) for f in symfiles) 382 return any(ANDROID_PATH_MATCHER.match(f.path) for f in symfiles)
337 383
338 384
339 def RemapAndroidFiles(symfiles, output_path): 385 def RemapAndroidFiles(symfiles, output_path):
(...skipping 25 matching lines...) Expand all
365 'as out/Debug. Only needed for Android.') 411 'as out/Debug. Only needed for Android.')
366 options = parser.parse_args() 412 options = parser.parse_args()
367 413
368 trace_file_path = options.file 414 trace_file_path = options.file
369 def _OpenTraceFile(mode): 415 def _OpenTraceFile(mode):
370 if trace_file_path.endswith('.gz'): 416 if trace_file_path.endswith('.gz'):
371 return gzip.open(trace_file_path, mode + 'b') 417 return gzip.open(trace_file_path, mode + 'b')
372 else: 418 else:
373 return open(trace_file_path, mode + 't') 419 return open(trace_file_path, mode + 't')
374 420
375 addr2line_path = FindInSystemPath('addr2line') 421 symbolizer = Symbolizer()
376 if addr2line_path is None: 422 if symbolizer.symbolizer_path is None:
377 sys.exit("Can't symbolize - no addr2line in PATH.") 423 sys.exit("Can't symbolize - no %s in PATH." % symbolizer.binary)
378 424
379 print 'Reading trace file...' 425 print 'Reading trace file...'
380 with _OpenTraceFile('r') as trace_file: 426 with _OpenTraceFile('r') as trace_file:
381 trace = json.load(trace_file) 427 trace = json.load(trace_file)
382 428
383 processes = CollectProcesses(trace) 429 processes = CollectProcesses(trace)
384 symfiles = ResolveSymbolizableFiles(processes) 430 symfiles = ResolveSymbolizableFiles(processes)
385 431
386 # Android trace files don't have any indication they are from Android. 432 # Android trace files don't have any indication they are from Android.
387 # So we're checking for Android-specific paths. 433 # So we're checking for Android-specific paths.
388 if HaveFilesFromAndroid(symfiles): 434 if HaveFilesFromAndroid(symfiles):
389 if not options.output_directory: 435 if not options.output_directory:
390 parser.error('The trace file appears to be from Android. Please ' 436 parser.error('The trace file appears to be from Android. Please '
391 "specify output directory (e.g. 'out/Debug') to properly " 437 "specify output directory (e.g. 'out/Debug') to properly "
392 'symbolize it.') 438 'symbolize it.')
393 RemapAndroidFiles(symfiles, os.path.abspath(options.output_directory)) 439 RemapAndroidFiles(symfiles, os.path.abspath(options.output_directory))
394 440
395 if SymbolizeFiles(symfiles, addr2line_path): 441 if SymbolizeFiles(symfiles, symbolizer):
396 if options.backup: 442 if options.backup:
397 backup_file_path = trace_file_path + BACKUP_FILE_TAG 443 backup_file_path = trace_file_path + BACKUP_FILE_TAG
398 print 'Backing up trace file to {}...'.format(backup_file_path) 444 print 'Backing up trace file to {}...'.format(backup_file_path)
399 os.rename(trace_file_path, backup_file_path) 445 os.rename(trace_file_path, backup_file_path)
400 446
401 print 'Updating trace file...' 447 print 'Updating trace file...'
402 with _OpenTraceFile('w') as trace_file: 448 with _OpenTraceFile('w') as trace_file:
403 json.dump(trace, trace_file) 449 json.dump(trace, trace_file)
404 else: 450 else:
405 print 'No PCs symbolized - not updating trace file.' 451 print 'No PCs symbolized - not updating trace file.'
406 452
407 453
408 if __name__ == '__main__': 454 if __name__ == '__main__':
409 main() 455 main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698