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

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 | tracing/bin/symbolize_trace_atos_regex.py » ('j') | 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
11 import os 11 import os
12 import re 12 import re
13 import subprocess 13 import subprocess
14 import sys 14 import sys
15 15
16 _SYMBOLS_PATH = os.path.abspath(os.path.join( 16 _SYMBOLS_PATH = os.path.abspath(os.path.join(
17 os.path.dirname(os.path.realpath(__file__)), 17 os.path.dirname(os.path.realpath(__file__)),
18 '..', 18 '..',
19 'third_party', 19 'third_party',
20 'symbols')) 20 'symbols'))
21 sys.path.append(_SYMBOLS_PATH) 21 sys.path.append(_SYMBOLS_PATH)
22 # pylint: disable=import-error 22 # pylint: disable=import-error
23 import symbols.elf_symbolizer as elf_symbolizer 23 import symbols.elf_symbolizer as elf_symbolizer
24 24
25 import symbolize_trace_atos_regex
26
25 27
26 # Relevant trace event phases from Chromium's 28 # Relevant trace event phases from Chromium's
27 # src/base/trace_event/common/trace_event_common.h. 29 # src/base/trace_event/common/trace_event_common.h.
28 TRACE_EVENT_PHASE_METADATA = 'M' 30 TRACE_EVENT_PHASE_METADATA = 'M'
29 TRACE_EVENT_PHASE_MEMORY_DUMP = 'v' 31 TRACE_EVENT_PHASE_MEMORY_DUMP = 'v'
30 32
31 33
32 # Matches Android library paths, supports both K (/data/app-lib/<>/lib.so) 34 # Matches Android library paths, supports both K (/data/app-lib/<>/lib.so)
33 # as well as L+ (/data/app/<>/lib/<>/lib.so). Library name is available 35 # as well as L+ (/data/app/<>/lib/<>/lib.so). Library name is available
34 # via 'name' group. 36 # via 'name' group.
(...skipping 10 matching lines...) Expand all
45 47
46 def FindInSystemPath(binary_name): 48 def FindInSystemPath(binary_name):
47 paths = os.environ['PATH'].split(os.pathsep) 49 paths = os.environ['PATH'].split(os.pathsep)
48 for path in paths: 50 for path in paths:
49 binary_path = os.path.join(path, binary_name) 51 binary_path = os.path.join(path, binary_name)
50 if os.path.isfile(binary_path): 52 if os.path.isfile(binary_path):
51 return binary_path 53 return binary_path
52 return None 54 return None
53 55
54 56
57 class Symbolizer(object):
58 # Encapsulates platform-specific symbolization logic.
59 def __init__(self):
60 self.is_mac = sys.platform == 'darwin'
61 if self.is_mac:
62 self.binary = 'atos'
63 self._matcher = symbolize_trace_atos_regex.AtosRegexMatcher()
64 else:
65 self.binary = 'addr2line'
66 self.symbolizer_path = FindInSystemPath(self.binary)
67
68 def _SymbolizeLinuxAndAndroid(self, symfile):
69 def _SymbolizerCallback(sym_info, frames):
70 # Unwind inline chain to the top.
71 while sym_info.inlined_by:
72 sym_info = sym_info.inlined_by
73
74 symbolized_name = sym_info.name if sym_info.name else unsymbolized_name
75 for frame in frames:
76 frame.name = symbolized_name
77
78 symbolizer = elf_symbolizer.ELFSymbolizer(symfile.symbolizable_path,
79 self.symbolizer_path,
80 _SymbolizerCallback,
81 inlines=True)
82
83 for address, frames in symfile.frames_by_address.iteritems():
84 # SymbolizeAsync() asserts that the type of address is int. We operate
85 # on longs (since they are raw pointers possibly from 64-bit processes).
86 # It's OK to cast here because we're passing relative PC, which should
87 # always fit into int.
88 symbolizer.SymbolizeAsync(int(address), frames)
89
90 symbolizer.Join()
91
92 def _SymbolizeMac(self, symfile):
93 chars_max = int(subprocess.check_output("getconf ARG_MAX", shell=True))
94
95 # 16 for the address, 2 for "0x", 1 for the space
96 chars_per_address = 19
97
98 cmd_base = [self.symbolizer_path, '-arch', 'x86_64', '-l',
99 '0x0', '-o' , symfile.symbolizable_path]
100 chars_for_other_arguments = len(' '.join(cmd_base)) + 1
101
102 # The maximum number of inputs that can be processed at once is limited by
103 # ARG_MAX. This currently evalutes to ~13000 on macOS.
104 max_inputs = (chars_max - chars_for_other_arguments) / chars_per_address
105
106 all_keys = symfile.frames_by_address.keys()
107 processed_keys_count = 0
108 while len(all_keys):
109 input_count = min(len(all_keys), max_inputs)
110 keys_to_process = all_keys[0:input_count]
111
112 cmd = list(cmd_base)
113 cmd.extend([hex(int(x)) for x in keys_to_process])
114 output_array = subprocess.check_output(cmd).split('\n')
115 for i in range(len(keys_to_process)):
116 for frame in symfile.frames_by_address.values()[i + processed_keys_count ]:
117 frame.name = self._matcher.Match(output_array[i])
118 processed_keys_count += len(keys_to_process)
119 all_keys = all_keys[input_count:]
120
121 def Symbolize(self, symfile):
122 if self.is_mac:
123 self._SymbolizeMac(symfile)
124 else:
125 self._SymbolizeLinuxAndAndroid(symfile)
126
127
55 def IsSymbolizableFile(file_path): 128 def IsSymbolizableFile(file_path):
56 result = subprocess.check_output(['file', '-0', file_path]) 129 result = subprocess.check_output(['file', '-0', file_path])
57 type_string = result[result.find('\0') + 1:] 130 type_string = result[result.find('\0') + 1:]
58 return bool(re.match(r'\: (ELF|Mach-O) (32|64)-bit\b', type_string)) 131 return bool(re.match(r'.*(ELF|Mach-O) (32|64)-bit\b.*',
132 type_string, re.DOTALL))
59 133
60 134
61 class ProcessMemoryMaps(object): 135 class ProcessMemoryMaps(object):
62 """Represents 'process_mmaps' trace file entry.""" 136 """Represents 'process_mmaps' trace file entry."""
63 137
64 class Region(object): 138 class Region(object):
65 def __init__(self, start_address, size, file_path): 139 def __init__(self, start_address, size, file_path):
66 self._start_address = start_address 140 self._start_address = start_address
67 self._size = size 141 self._size = size
68 self._file_path = file_path 142 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) 338 symfile = symfile_by_path.get(region.file_path)
265 if symfile is None: 339 if symfile is None:
266 symfile = SymbolizableFile(region.file_path) 340 symfile = SymbolizableFile(region.file_path)
267 symfile_by_path[symfile.path] = symfile 341 symfile_by_path[symfile.path] = symfile
268 342
269 relative_pc = frame.pc - region.start_address 343 relative_pc = frame.pc - region.start_address
270 symfile.frames_by_address[relative_pc].append(frame) 344 symfile.frames_by_address[relative_pc].append(frame)
271 return symfile_by_path.values() 345 return symfile_by_path.values()
272 346
273 347
274 def SymbolizeFiles(symfiles, addr2line_path): 348 def SymbolizeFiles(symfiles, symbolizer):
275 """Symbolizes each file in the given list of SymbolizableFiles 349 """Symbolizes each file in the given list of SymbolizableFiles
276 and updates stack frames with symbolization results.""" 350 and updates stack frames with symbolization results."""
277 print 'Symbolizing...' 351 print 'Symbolizing...'
278 352
279 def _SubPrintf(message, *args): 353 def _SubPrintf(message, *args):
280 print (' ' + message).format(*args) 354 print (' ' + message).format(*args)
281 355
282 symbolized = False 356 symbolized = False
283 for symfile in symfiles: 357 for symfile in symfiles:
284 unsymbolized_name = '<{}>'.format( 358 unsymbolized_name = '<{}>'.format(
285 symfile.path if symfile.path else 'unnamed') 359 symfile.path if symfile.path else 'unnamed')
286 360
287 problem = None 361 problem = None
288 if not os.path.isabs(symfile.symbolizable_path): 362 if not os.path.isabs(symfile.symbolizable_path):
289 problem = 'not a file' 363 problem = 'not a file'
290 elif not os.path.isfile(symfile.symbolizable_path): 364 elif not os.path.isfile(symfile.symbolizable_path):
291 problem = "file doesn't exist" 365 problem = "file doesn't exist"
292 elif not IsSymbolizableFile(symfile.symbolizable_path): 366 elif not IsSymbolizableFile(symfile.symbolizable_path):
293 problem = 'file is not symbolizable' 367 problem = 'file is not symbolizable'
294 if problem: 368 if problem:
295 _SubPrintf("Won't symbolize {} PCs for '{}': {}.", 369 _SubPrintf("Won't symbolize {} PCs for '{}': {}.",
296 len(symfile.frames_by_address), 370 len(symfile.frames_by_address),
297 symfile.symbolizable_path, 371 symfile.symbolizable_path,
298 problem) 372 problem)
299 for frames in symfile.frames_by_address.itervalues(): 373 for frames in symfile.frames_by_address.itervalues():
300 for frame in frames: 374 for frame in frames:
301 frame.name = unsymbolized_name 375 frame.name = unsymbolized_name
302 continue 376 continue
303 377
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 {}...', 378 _SubPrintf('Symbolizing {} PCs from {}...',
319 len(symfile.frames_by_address), 379 len(symfile.frames_by_address),
320 symfile.path) 380 symfile.path)
321 381
322 for address, frames in symfile.frames_by_address.iteritems(): 382 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 383 symbolized = True
331 384
332 return symbolized 385 return symbolized
333 386
334 387
335 def HaveFilesFromAndroid(symfiles): 388 def HaveFilesFromAndroid(symfiles):
336 return any(ANDROID_PATH_MATCHER.match(f.path) for f in symfiles) 389 return any(ANDROID_PATH_MATCHER.match(f.path) for f in symfiles)
337 390
338 391
339 def RemapAndroidFiles(symfiles, output_path): 392 def RemapAndroidFiles(symfiles, output_path):
(...skipping 25 matching lines...) Expand all
365 'as out/Debug. Only needed for Android.') 418 'as out/Debug. Only needed for Android.')
366 options = parser.parse_args() 419 options = parser.parse_args()
367 420
368 trace_file_path = options.file 421 trace_file_path = options.file
369 def _OpenTraceFile(mode): 422 def _OpenTraceFile(mode):
370 if trace_file_path.endswith('.gz'): 423 if trace_file_path.endswith('.gz'):
371 return gzip.open(trace_file_path, mode + 'b') 424 return gzip.open(trace_file_path, mode + 'b')
372 else: 425 else:
373 return open(trace_file_path, mode + 't') 426 return open(trace_file_path, mode + 't')
374 427
375 addr2line_path = FindInSystemPath('addr2line') 428 symbolizer = Symbolizer()
376 if addr2line_path is None: 429 if symbolizer.symbolizer_path is None:
377 sys.exit("Can't symbolize - no addr2line in PATH.") 430 sys.exit("Can't symbolize - no %s in PATH." % symbolizer.binary)
378 431
379 print 'Reading trace file...' 432 print 'Reading trace file...'
380 with _OpenTraceFile('r') as trace_file: 433 with _OpenTraceFile('r') as trace_file:
381 trace = json.load(trace_file) 434 trace = json.load(trace_file)
382 435
383 processes = CollectProcesses(trace) 436 processes = CollectProcesses(trace)
384 symfiles = ResolveSymbolizableFiles(processes) 437 symfiles = ResolveSymbolizableFiles(processes)
385 438
386 # Android trace files don't have any indication they are from Android. 439 # Android trace files don't have any indication they are from Android.
387 # So we're checking for Android-specific paths. 440 # So we're checking for Android-specific paths.
388 if HaveFilesFromAndroid(symfiles): 441 if HaveFilesFromAndroid(symfiles):
389 if not options.output_directory: 442 if not options.output_directory:
390 parser.error('The trace file appears to be from Android. Please ' 443 parser.error('The trace file appears to be from Android. Please '
391 "specify output directory (e.g. 'out/Debug') to properly " 444 "specify output directory (e.g. 'out/Debug') to properly "
392 'symbolize it.') 445 'symbolize it.')
393 RemapAndroidFiles(symfiles, os.path.abspath(options.output_directory)) 446 RemapAndroidFiles(symfiles, os.path.abspath(options.output_directory))
394 447
395 if SymbolizeFiles(symfiles, addr2line_path): 448 if SymbolizeFiles(symfiles, symbolizer):
396 if options.backup: 449 if options.backup:
397 backup_file_path = trace_file_path + BACKUP_FILE_TAG 450 backup_file_path = trace_file_path + BACKUP_FILE_TAG
398 print 'Backing up trace file to {}...'.format(backup_file_path) 451 print 'Backing up trace file to {}...'.format(backup_file_path)
399 os.rename(trace_file_path, backup_file_path) 452 os.rename(trace_file_path, backup_file_path)
400 453
401 print 'Updating trace file...' 454 print 'Updating trace file...'
402 with _OpenTraceFile('w') as trace_file: 455 with _OpenTraceFile('w') as trace_file:
403 json.dump(trace, trace_file) 456 json.dump(trace, trace_file)
404 else: 457 else:
405 print 'No PCs symbolized - not updating trace file.' 458 print 'No PCs symbolized - not updating trace file.'
406 459
407 460
408 if __name__ == '__main__': 461 if __name__ == '__main__':
409 main() 462 main()
OLDNEW
« no previous file with comments | « no previous file | tracing/bin/symbolize_trace_atos_regex.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698