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

Side by Side Diff: tracing/bin/symbolize_trace

Issue 2605183002: Update symbolize_trace to work on macOS. (Closed)
Patch Set: Clean up Created 3 years, 11 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 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 def IsSymbolizableFile(file_path): 55 def IsSymbolizableFile(file_path):
56 result = subprocess.check_output(['file', '-0', file_path]) 56 result = subprocess.check_output(['file', '-0', file_path])
57 type_string = result[result.find('\0') + 1:] 57 type_string = result[result.find('\0') + 1:]
58 return bool(re.match(r'\: (ELF|Mach-O) (32|64)-bit\b', type_string)) 58 return bool(re.match(r'.*(ELF|Mach-O) (32|64)-bit\b.*',
59 type_string, re.DOTALL))
59 60
60 61
61 class ProcessMemoryMaps(object): 62 class ProcessMemoryMaps(object):
62 """Represents 'process_mmaps' trace file entry.""" 63 """Represents 'process_mmaps' trace file entry."""
63 64
64 class Region(object): 65 class Region(object):
65 def __init__(self, start_address, size, file_path): 66 def __init__(self, start_address, size, file_path):
66 self._start_address = start_address 67 self._start_address = start_address
67 self._size = size 68 self._size = size
68 self._file_path = file_path 69 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) 265 symfile = symfile_by_path.get(region.file_path)
265 if symfile is None: 266 if symfile is None:
266 symfile = SymbolizableFile(region.file_path) 267 symfile = SymbolizableFile(region.file_path)
267 symfile_by_path[symfile.path] = symfile 268 symfile_by_path[symfile.path] = symfile
268 269
269 relative_pc = frame.pc - region.start_address 270 relative_pc = frame.pc - region.start_address
270 symfile.frames_by_address[relative_pc].append(frame) 271 symfile.frames_by_address[relative_pc].append(frame)
271 return symfile_by_path.values() 272 return symfile_by_path.values()
272 273
273 274
274 def SymbolizeFiles(symfiles, addr2line_path): 275 def SymbolizeFiles(symfiles, symbolizer_path):
275 """Symbolizes each file in the given list of SymbolizableFiles 276 """Symbolizes each file in the given list of SymbolizableFiles
276 and updates stack frames with symbolization results.""" 277 and updates stack frames with symbolization results."""
277 print 'Symbolizing...' 278 print 'Symbolizing...'
278 279
279 def _SubPrintf(message, *args): 280 def _SubPrintf(message, *args):
280 print (' ' + message).format(*args) 281 print (' ' + message).format(*args)
281 282
282 symbolized = False 283 symbolized = False
283 for symfile in symfiles: 284 for symfile in symfiles:
284 unsymbolized_name = '<{}>'.format( 285 unsymbolized_name = '<{}>'.format(
285 symfile.path if symfile.path else 'unnamed') 286 symfile.path if symfile.path else 'unnamed')
286 287
287 problem = None 288 problem = None
288 if not os.path.isabs(symfile.symbolizable_path): 289 if not os.path.isabs(symfile.symbolizable_path):
289 problem = 'not a file' 290 problem = 'not a file'
290 elif not os.path.isfile(symfile.symbolizable_path): 291 elif not os.path.isfile(symfile.symbolizable_path):
291 problem = "file doesn't exist" 292 problem = "file doesn't exist"
292 elif not IsSymbolizableFile(symfile.symbolizable_path): 293 elif not IsSymbolizableFile(symfile.symbolizable_path):
293 problem = 'file is not symbolizable' 294 problem = 'file is not symbolizable'
294 if problem: 295 if problem:
295 _SubPrintf("Won't symbolize {} PCs for '{}': {}.", 296 _SubPrintf("Won't symbolize {} PCs for '{}': {}.",
296 len(symfile.frames_by_address), 297 len(symfile.frames_by_address),
297 symfile.symbolizable_path, 298 symfile.symbolizable_path,
298 problem) 299 problem)
299 for frames in symfile.frames_by_address.itervalues(): 300 for frames in symfile.frames_by_address.itervalues():
300 for frame in frames: 301 for frame in frames:
301 frame.name = unsymbolized_name 302 frame.name = unsymbolized_name
302 continue 303 continue
303 304
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 {}...', 305 _SubPrintf('Symbolizing {} PCs from {}...',
319 len(symfile.frames_by_address), 306 len(symfile.frames_by_address),
320 symfile.path) 307 symfile.path)
321 308
322 for address, frames in symfile.frames_by_address.iteritems(): 309 if sys.platform == 'darwin':
DmitrySkiba 2017/01/10 21:28:09 We have this check in two places - first when we d
erikchen 2017/02/04 02:34:56 Introducing a class to avoid an extra conditional
323 # SymbolizeAsync() asserts that the type of address is int. We operate 310 SymbolizeMac(symfile, symbolizer_path)
324 # on longs (since they are raw pointers possibly from 64-bit processes). 311 else:
325 # It's OK to cast here because we're passing relative PC, which should 312 SymbolizeLinuxAndAndroid(sym_file, symbolizer_path)
326 # always fit into int.
327 symbolizer.SymbolizeAsync(int(address), frames)
328
329 symbolizer.Join()
330 symbolized = True 313 symbolized = True
331 314
332 return symbolized 315 return symbolized
333 316
334 317
318 def SymbolizeLinuxAndAndroid(sym_file, symbolizer_path):
319 def _SymbolizerCallback(sym_info, frames):
320 # Unwind inline chain to the top.
321 while sym_info.inlined_by:
322 sym_info = sym_info.inlined_by
323
324 symbolized_name = sym_info.name if sym_info.name else unsymbolized_name
325 for frame in frames:
326 frame.name = symbolized_name
327
328 symbolizer = elf_symbolizer.ELFSymbolizer(symfile.symbolizable_path,
329 symbolizer_path,
330 _SymbolizerCallback,
331 inlines=True)
332
333 for address, frames in symfile.frames_by_address.iteritems():
334 # SymbolizeAsync() asserts that the type of address is int. We operate
335 # on longs (since they are raw pointers possibly from 64-bit processes).
336 # It's OK to cast here because we're passing relative PC, which should
337 # always fit into int.
338 symbolizer.SymbolizeAsync(int(address), frames)
339
340 symbolizer.Join()
341
342
343 def SymbolizeMac(symfile, symbolizer_path):
344 cmd = [symbolizer_path, '-arch', 'x86_64', '-l',
345 '0x0', '-o' , symfile.symbolizable_path]
346 cmd.extend([hex(int(x)) for x in symfile.frames_by_address.keys()])
DmitrySkiba 2017/01/10 21:28:09 What is the max command line length on macOS? This
erikchen 2017/02/04 02:34:56 By default, the max length is 260k characters. I'v
347 output_array = subprocess.check_output(cmd).split('\n')
348 for i in range(len(symfile.frames_by_address.keys())):
349 for frame in symfile.frames_by_address.values()[i]:
350 frame.name = output_array[i]
351
352
335 def HaveFilesFromAndroid(symfiles): 353 def HaveFilesFromAndroid(symfiles):
336 return any(ANDROID_PATH_MATCHER.match(f.path) for f in symfiles) 354 return any(ANDROID_PATH_MATCHER.match(f.path) for f in symfiles)
337 355
338 356
339 def RemapAndroidFiles(symfiles, output_path): 357 def RemapAndroidFiles(symfiles, output_path):
340 for symfile in symfiles: 358 for symfile in symfiles:
341 match = ANDROID_PATH_MATCHER.match(symfile.path) 359 match = ANDROID_PATH_MATCHER.match(symfile.path)
342 if match: 360 if match:
343 name = match.group('name') 361 name = match.group('name')
344 symfile.symbolizable_path = os.path.join( 362 symfile.symbolizable_path = os.path.join(
(...skipping 20 matching lines...) Expand all
365 'as out/Debug. Only needed for Android.') 383 'as out/Debug. Only needed for Android.')
366 options = parser.parse_args() 384 options = parser.parse_args()
367 385
368 trace_file_path = options.file 386 trace_file_path = options.file
369 def _OpenTraceFile(mode): 387 def _OpenTraceFile(mode):
370 if trace_file_path.endswith('.gz'): 388 if trace_file_path.endswith('.gz'):
371 return gzip.open(trace_file_path, mode + 'b') 389 return gzip.open(trace_file_path, mode + 'b')
372 else: 390 else:
373 return open(trace_file_path, mode + 't') 391 return open(trace_file_path, mode + 't')
374 392
375 addr2line_path = FindInSystemPath('addr2line') 393 if sys.platform == 'darwin':
376 if addr2line_path is None: 394 symbolizer = 'atos'
377 sys.exit("Can't symbolize - no addr2line in PATH.") 395 else:
396 symbolizer = 'addr2line'
397 symbolizer_path = FindInSystemPath(symbolizer)
398 if symbolizer_path is None:
399 sys.exit("Can't symbolize - no %s in PATH." % symbolizer)
378 400
379 print 'Reading trace file...' 401 print 'Reading trace file...'
380 with _OpenTraceFile('r') as trace_file: 402 with _OpenTraceFile('r') as trace_file:
381 trace = json.load(trace_file) 403 trace = json.load(trace_file)
382 404
383 processes = CollectProcesses(trace) 405 processes = CollectProcesses(trace)
384 symfiles = ResolveSymbolizableFiles(processes) 406 symfiles = ResolveSymbolizableFiles(processes)
385 407
386 # Android trace files don't have any indication they are from Android. 408 # Android trace files don't have any indication they are from Android.
387 # So we're checking for Android-specific paths. 409 # So we're checking for Android-specific paths.
388 if HaveFilesFromAndroid(symfiles): 410 if HaveFilesFromAndroid(symfiles):
389 if not options.output_directory: 411 if not options.output_directory:
390 parser.error('The trace file appears to be from Android. Please ' 412 parser.error('The trace file appears to be from Android. Please '
391 "specify output directory (e.g. 'out/Debug') to properly " 413 "specify output directory (e.g. 'out/Debug') to properly "
392 'symbolize it.') 414 'symbolize it.')
393 RemapAndroidFiles(symfiles, os.path.abspath(options.output_directory)) 415 RemapAndroidFiles(symfiles, os.path.abspath(options.output_directory))
394 416
395 if SymbolizeFiles(symfiles, addr2line_path): 417 if SymbolizeFiles(symfiles, symbolizer_path):
396 if options.backup: 418 if options.backup:
397 backup_file_path = trace_file_path + BACKUP_FILE_TAG 419 backup_file_path = trace_file_path + BACKUP_FILE_TAG
398 print 'Backing up trace file to {}...'.format(backup_file_path) 420 print 'Backing up trace file to {}...'.format(backup_file_path)
399 os.rename(trace_file_path, backup_file_path) 421 os.rename(trace_file_path, backup_file_path)
400 422
401 print 'Updating trace file...' 423 print 'Updating trace file...'
402 with _OpenTraceFile('w') as trace_file: 424 with _OpenTraceFile('w') as trace_file:
403 json.dump(trace, trace_file) 425 json.dump(trace, trace_file)
404 else: 426 else:
405 print 'No PCs symbolized - not updating trace file.' 427 print 'No PCs symbolized - not updating trace file.'
406 428
407 429
408 if __name__ == '__main__': 430 if __name__ == '__main__':
409 main() 431 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