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

Side by Side Diff: tracing/bin/symbolize_trace.py

Issue 2950723002: Add an end-to-end test for symbolize_trace on macOS. (Closed)
Patch Set: Created 3 years, 6 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
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 """ 6 """
7 This script processes trace files and symbolizes stack frames generated by 7 This script processes trace files and symbolizes stack frames generated by
8 Chrome's native heap profiler. This script assumes that the Chrome binary 8 Chrome's native heap profiler. This script assumes that the Chrome binary
9 referenced in the trace contains symbols, and is the same binary used to emit 9 referenced in the trace contains symbols, and is the same binary used to emit
10 the trace. 10 the trace.
(...skipping 967 matching lines...) Expand 10 before | Expand all | Expand 10 after
978 """Holds file path, addresses to symbolize and stack frames to update. 978 """Holds file path, addresses to symbolize and stack frames to update.
979 979
980 This class is a link between ELFSymbolizer and a trace file: it specifies 980 This class is a link between ELFSymbolizer and a trace file: it specifies
981 what to symbolize (addresses) and what to update with the symbolization 981 what to symbolize (addresses) and what to update with the symbolization
982 result (frames). 982 result (frames).
983 """ 983 """
984 def __init__(self, file_path): 984 def __init__(self, file_path):
985 self.path = file_path 985 self.path = file_path
986 self.symbolizable_path = file_path # path to use for symbolization 986 self.symbolizable_path = file_path # path to use for symbolization
987 self.frames_by_address = collections.defaultdict(list) 987 self.frames_by_address = collections.defaultdict(list)
988 self.skip_symbolization = False
988 989
989 990
990 def ResolveSymbolizableFiles(processes): 991 def ResolveSymbolizableFiles(processes):
991 """Resolves and groups PCs into list of SymbolizableFiles. 992 """Resolves and groups PCs into list of SymbolizableFiles.
992 993
993 As part of the grouping process, this function resolves PC from each stack 994 As part of the grouping process, this function resolves PC from each stack
994 frame to the corresponding mmap region. Stack frames that failed to resolve 995 frame to the corresponding mmap region. Stack frames that failed to resolve
995 are symbolized with '<unresolved>'. 996 are symbolized with '<unresolved>'.
996 """ 997 """
997 symfile_by_path = {} 998 symfile_by_path = {}
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
1059 # SymbolizeAsync() asserts that the type of address is int. We operate 1060 # SymbolizeAsync() asserts that the type of address is int. We operate
1060 # on longs (since they are raw pointers possibly from 64-bit processes). 1061 # on longs (since they are raw pointers possibly from 64-bit processes).
1061 # It's OK to cast here because we're passing relative PC, which should 1062 # It's OK to cast here because we're passing relative PC, which should
1062 # always fit into int. 1063 # always fit into int.
1063 symbolizer.SymbolizeAsync(int(address), frames) 1064 symbolizer.SymbolizeAsync(int(address), frames)
1064 1065
1065 symbolizer.Join() 1066 symbolizer.Join()
1066 1067
1067 1068
1068 def _SymbolizeMac(self, symfile): 1069 def _SymbolizeMac(self, symfile):
1070 if symfile.skip_symbolization:
1071 for address, frames in symfile.frames_by_address.iteritems():
1072 unsymbolized_name = ('<' + symfile.symbolizable_path + '> + ' +
etienneb 2017/06/20 03:28:09 I wonder if we should not use only basename instea
erikchen 2017/06/23 00:33:45 Done.
1073 str(address))
1074 for frame in frames:
1075 frame.name = unsymbolized_name
1076 return
1077
1069 load_address = (symbolize_trace_macho_reader. 1078 load_address = (symbolize_trace_macho_reader.
1070 ReadMachOTextLoadAddress(symfile.symbolizable_path)) 1079 ReadMachOTextLoadAddress(symfile.symbolizable_path))
1071 assert load_address is not None 1080 assert load_address is not None
1072 1081
1073 address_os_file, address_file_path = tempfile.mkstemp() 1082 address_os_file, address_file_path = tempfile.mkstemp()
1074 try: 1083 try:
1075 with os.fdopen(address_os_file, 'w') as address_file: 1084 with os.fdopen(address_os_file, 'w') as address_file:
1076 for address in symfile.frames_by_address.iterkeys(): 1085 for address in symfile.frames_by_address.iterkeys():
1077 address_file.write('{:x} '.format(address + load_address)) 1086 address_file.write('{:x} '.format(address + load_address))
1078 1087
(...skipping 16 matching lines...) Expand all
1095 service that handles communication with the NT symbol servers. This 1104 service that handles communication with the NT symbol servers. This
1096 creates an explicit serialization (and therefor lock contention) of 1105 creates an explicit serialization (and therefor lock contention) of
1097 any process using the symbol API for files do not have a local PDB. 1106 any process using the symbol API for files do not have a local PDB.
1098 1107
1099 Thus, even though the windows symbolizer binary can be make command line 1108 Thus, even though the windows symbolizer binary can be make command line
1100 compatible with the POSIX addr2line interface, parallelizing the 1109 compatible with the POSIX addr2line interface, parallelizing the
1101 symbolization does not yield the same performance effects. Running 1110 symbolization does not yield the same performance effects. Running
1102 just one symbolizer seems good enough for now. Can optimize later 1111 just one symbolizer seems good enough for now. Can optimize later
1103 if this becomes a bottleneck. 1112 if this becomes a bottleneck.
1104 """ 1113 """
1114 if symfile.skip_symbolization:
1115 for address, frames in symfile.frames_by_address.iteritems():
1116 unsymbolized_name = ('<' + symfile.symbolizable_path + '> + ' +
1117 str(address))
etienneb 2017/06/20 03:28:09 nit: address should be hexa!?
erikchen 2017/06/23 00:33:45 Done.
1118 for frame in frames:
1119 frame.name = unsymbolized_name
1120 return
1121
1105 cmd = [self.symbolizer_path, '--functions', '--demangle', '--exe', 1122 cmd = [self.symbolizer_path, '--functions', '--demangle', '--exe',
1106 symfile.symbolizable_path] 1123 symfile.symbolizable_path]
1107 1124
1108 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, 1125 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
1109 stderr=sys.stderr) 1126 stderr=sys.stderr)
1110 addrs = ["%x" % relative_pc for relative_pc in 1127 addrs = ["%x" % relative_pc for relative_pc in
1111 symfile.frames_by_address.keys()] 1128 symfile.frames_by_address.keys()]
1112 (stdout_data, stderr_data) = proc.communicate('\n'.join(addrs)) 1129 (stdout_data, stderr_data) = proc.communicate('\n'.join(addrs))
1113 stdout_data = stdout_data.split('\n') 1130 stdout_data = stdout_data.split('\n')
1114 1131
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
1205 name = match.group('name') 1222 name = match.group('name')
1206 symfile.symbolizable_path = os.path.join( 1223 symfile.symbolizable_path = os.path.join(
1207 output_path, ANDROID_UNSTRIPPED_SUBPATH, name) 1224 output_path, ANDROID_UNSTRIPPED_SUBPATH, name)
1208 else: 1225 else:
1209 # Clobber file path to trigger "not a file" problem in SymbolizeFiles(). 1226 # Clobber file path to trigger "not a file" problem in SymbolizeFiles().
1210 # Without this, files won't be symbolized with "file not found" problem, 1227 # Without this, files won't be symbolized with "file not found" problem,
1211 # which is not accurate. 1228 # which is not accurate.
1212 symfile.symbolizable_path = 'android://{}'.format(symfile.path) 1229 symfile.symbolizable_path = 'android://{}'.format(symfile.path)
1213 1230
1214 1231
1215 def RemapMacFiles(symfiles, symbol_base_directory, version): 1232 def RemapMacFiles(symfiles, symbol_base_directory, version,
1233 only_symbolize_chrome_symbols):
1216 suffix = ("Google Chrome Framework.dSYM/Contents/Resources/DWARF/" 1234 suffix = ("Google Chrome Framework.dSYM/Contents/Resources/DWARF/"
1217 "Google Chrome Framework") 1235 "Google Chrome Framework")
1218 symbol_sub_dir = os.path.join(symbol_base_directory, version) 1236 symbol_sub_dir = os.path.join(symbol_base_directory, version)
1219 symbolizable_path = os.path.join(symbol_sub_dir, suffix) 1237 symbolizable_path = os.path.join(symbol_sub_dir, suffix)
1220 1238
1221 for symfile in symfiles: 1239 for symfile in symfiles:
1222 if symfile.path.endswith("Google Chrome Framework"): 1240 if symfile.path.endswith("Google Chrome Framework"):
1223 symfile.symbolizable_path = symbolizable_path 1241 symfile.symbolizable_path = symbolizable_path
1242 elif only_symbolize_chrome_symbols:
1243 symfile.skip_symbolization = True
1224 1244
1225 def RemapWinFiles(symfiles, symbol_base_directory, version, is64bit): 1245 def RemapWinFiles(symfiles, symbol_base_directory, version, is64bit,
1246 only_symbolize_chrome_symbols):
1226 folder = "win64" if is64bit else "win" 1247 folder = "win64" if is64bit else "win"
1227 symbol_sub_dir = os.path.join(symbol_base_directory, 1248 symbol_sub_dir = os.path.join(symbol_base_directory,
1228 "chrome-" + folder + "-" + version) 1249 "chrome-" + folder + "-" + version)
1229 for symfile in symfiles: 1250 for symfile in symfiles:
1230 image = os.path.join(symbol_sub_dir, os.path.basename(symfile.path)) 1251 image = os.path.join(symbol_sub_dir, os.path.basename(symfile.path))
1231 symbols = image + ".pdb" 1252 symbols = image + ".pdb"
1232 if os.path.isfile(image) and os.path.isfile(symbols): 1253 if os.path.isfile(image) and os.path.isfile(symbols):
1233 symfile.symbolizable_path = image 1254 symfile.symbolizable_path = image
1255 elif only_symbolize_chrome_symbols:
1256 symfile.skip_symbolization = True
1234 1257
1235 def Symbolize(options, trace, symbolizer): 1258 def Symbolize(options, trace, symbolizer):
1236 symfiles = ResolveSymbolizableFiles(trace.processes) 1259 symfiles = ResolveSymbolizableFiles(trace.processes)
1237 1260
1238 # Android trace files don't have any indication they are from Android. 1261 # Android trace files don't have any indication they are from Android.
1239 # So we're checking for Android-specific paths. 1262 # So we're checking for Android-specific paths.
1240 if HaveFilesFromAndroid(symfiles): 1263 if HaveFilesFromAndroid(symfiles):
1241 if not options.output_directory: 1264 if not options.output_directory:
1242 sys.exit('The trace file appears to be from Android. Please ' 1265 sys.exit('The trace file appears to be from Android. Please '
1243 'specify output directory to properly symbolize it.') 1266 'specify output directory to properly symbolize it.')
1244 RemapAndroidFiles(symfiles, os.path.abspath(options.output_directory)) 1267 RemapAndroidFiles(symfiles, os.path.abspath(options.output_directory))
1245 1268
1246 1269
1247 if not trace.is_chromium: 1270 if not trace.is_chromium:
1248 if symbolizer.is_mac: 1271 if symbolizer.is_mac:
1249 RemapMacFiles(symfiles, options.symbol_base_directory, trace.version) 1272 RemapMacFiles(symfiles, options.symbol_base_directory, trace.version,
1273 options.only_symbolize_chrome_symbols)
1250 if symbolizer.is_win: 1274 if symbolizer.is_win:
1251 RemapWinFiles(symfiles, options.symbol_base_directory, trace.version, 1275 RemapWinFiles(symfiles, options.symbol_base_directory, trace.version,
1252 trace.is_64bit) 1276 trace.is_64bit, options.only_symbolize_chrome_symbols)
1253 1277
1254 SymbolizeFiles(symfiles, symbolizer) 1278 SymbolizeFiles(symfiles, symbolizer)
1255 1279
1256 1280
1257 def OpenTraceFile(file_path, mode): 1281 def OpenTraceFile(file_path, mode):
1258 if file_path.endswith('.gz'): 1282 if file_path.endswith('.gz'):
1259 return gzip.open(file_path, mode + 'b') 1283 return gzip.open(file_path, mode + 'b')
1260 else: 1284 else:
1261 return open(file_path, mode + 't') 1285 return open(file_path, mode + 't')
1262 1286
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
1331 os.path.join(symbol_base_directory, 1355 os.path.join(symbol_base_directory,
1332 "chrome-" + folder + "-" + version + ".zip"), 1356 "chrome-" + folder + "-" + version + ".zip"),
1333 gcs_folder + "chrome-" + folder + "-pgo.zip", 1357 gcs_folder + "chrome-" + folder + "-pgo.zip",
1334 symbol_sub_dir) 1358 symbol_sub_dir)
1335 1359
1336 return True 1360 return True
1337 1361
1338 # Suffix used for backup files. 1362 # Suffix used for backup files.
1339 BACKUP_FILE_TAG = '.BACKUP' 1363 BACKUP_FILE_TAG = '.BACKUP'
1340 1364
1341 def main(): 1365 def main(args):
1342 parser = argparse.ArgumentParser() 1366 parser = argparse.ArgumentParser()
1343 parser.add_argument( 1367 parser.add_argument(
1344 'file', 1368 'file',
1345 help='Trace file to symbolize (.json or .json.gz)') 1369 help='Trace file to symbolize (.json or .json.gz)')
1346 1370
1347 parser.add_argument( 1371 parser.add_argument(
1348 '--no-backup', dest='backup', default='true', action='store_false', 1372 '--no-backup', dest='backup', default='true', action='store_false',
1349 help="Don't create {} files".format(BACKUP_FILE_TAG)) 1373 help="Don't create {} files".format(BACKUP_FILE_TAG))
1350 1374
1351 parser.add_argument( 1375 parser.add_argument(
1352 '--output-directory', 1376 '--output-directory',
1353 help='The path to the build output directory, such as out/Debug.') 1377 help='The path to the build output directory, such as out/Debug.')
1354 1378
1379 parser.add_argument(
etienneb 2017/06/20 03:28:09 For not chromium trace (traces from user computers
erikchen 2017/06/23 00:33:45 Done.
1380 '--only-symbolize-chrome-symbols',
1381 action='store_true',
1382 help='Prevents symbolization of non-Chrome [system] symbols.')
1383
1355 home_dir = os.path.expanduser('~') 1384 home_dir = os.path.expanduser('~')
1356 default_dir = os.path.join(home_dir, "symbols") 1385 default_dir = os.path.join(home_dir, "symbols")
1357 parser.add_argument( 1386 parser.add_argument(
1358 '--symbol-base-directory', 1387 '--symbol-base-directory',
1359 default=default_dir, 1388 default=default_dir,
1360 help='Directory where symbols are downloaded and cached.') 1389 help='Directory where symbols are downloaded and cached.')
1361 1390
1362 symbolizer = Symbolizer() 1391 symbolizer = Symbolizer()
1363 if symbolizer.symbolizer_path is None: 1392 if symbolizer.symbolizer_path is None:
1364 sys.exit("Can't symbolize - no %s in PATH." % symbolizer.binary) 1393 sys.exit("Can't symbolize - no %s in PATH." % symbolizer.binary)
1365 1394
1366 options = parser.parse_args() 1395 options = parser.parse_args(args)
1367 1396
1368 trace_file_path = options.file 1397 trace_file_path = options.file
1369 1398
1370 print 'Reading trace file...' 1399 print 'Reading trace file...'
1371 with OpenTraceFile(trace_file_path, 'r') as trace_file: 1400 with OpenTraceFile(trace_file_path, 'r') as trace_file:
1372 trace = Trace(json.load(trace_file)) 1401 trace = Trace(json.load(trace_file))
1373 1402
1374 # Perform some sanity checks. 1403 # Perform some sanity checks.
1375 if trace.is_win and sys.platform != 'win32': 1404 if trace.is_win and sys.platform != 'win32':
1376 print "Cannot symbolize a windows trace on this architecture!" 1405 print "Cannot symbolize a windows trace on this architecture!"
(...skipping 26 matching lines...) Expand all
1403 os.rename(trace_file_path, backup_file_path) 1432 os.rename(trace_file_path, backup_file_path)
1404 1433
1405 print 'Updating the trace file...' 1434 print 'Updating the trace file...'
1406 with OpenTraceFile(trace_file_path, 'w') as trace_file: 1435 with OpenTraceFile(trace_file_path, 'w') as trace_file:
1407 json.dump(trace.node, trace_file) 1436 json.dump(trace.node, trace_file)
1408 else: 1437 else:
1409 print 'No modifications were made - not updating the trace file.' 1438 print 'No modifications were made - not updating the trace file.'
1410 1439
1411 1440
1412 if __name__ == '__main__': 1441 if __name__ == '__main__':
1413 main() 1442 main(sys.argv[1:])
OLDNEW
« tracing/bin/run_symbolize_trace_tests ('K') | « tracing/bin/symbolize_trace ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698