| OLD | NEW |
| 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 # pylint: disable=too-many-lines | 5 # pylint: disable=too-many-lines |
| 6 | 6 |
| 7 """ | 7 """ |
| 8 This script processes trace files and symbolizes stack frames generated by | 8 This script processes trace files and symbolizes stack frames generated by |
| 9 Chrome's native heap profiler. This script assumes that the Chrome binary | 9 Chrome's native heap profiler. This script assumes that the Chrome binary |
| 10 referenced in the trace contains symbols, and is the same binary used to emit | 10 referenced in the trace contains symbols, and is the same binary used to emit |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 230 os.path.join(os.path.dirname(__file__), '..', '..', '..')) | 230 os.path.join(os.path.dirname(__file__), '..', '..', '..')) |
| 231 | 231 |
| 232 _SYMBOLS_PATH = os.path.abspath(os.path.join( | 232 _SYMBOLS_PATH = os.path.abspath(os.path.join( |
| 233 _TRACING_DIR, | 233 _TRACING_DIR, |
| 234 'third_party', | 234 'third_party', |
| 235 'symbols')) | 235 'symbols')) |
| 236 sys.path.append(_SYMBOLS_PATH) | 236 sys.path.append(_SYMBOLS_PATH) |
| 237 # pylint: disable=import-error | 237 # pylint: disable=import-error |
| 238 import symbols.elf_symbolizer as elf_symbolizer | 238 import symbols.elf_symbolizer as elf_symbolizer |
| 239 | 239 |
| 240 from . import symbolize_trace_atos_regex | 240 from tracing.extras.symbolizer import symbolize_trace_atos_regex |
| 241 from . import symbolize_trace_macho_reader | 241 from tracing.extras.symbolizer import symbolize_trace_macho_reader |
| 242 | 242 |
| 243 _PY_UTILS_PATH = os.path.abspath(os.path.join( | 243 _PY_UTILS_PATH = os.path.abspath(os.path.join( |
| 244 _TRACING_DIR, | 244 _TRACING_DIR, |
| 245 '..', | 245 '..', |
| 246 'common', | 246 'common', |
| 247 'py_utils')) | 247 'py_utils')) |
| 248 sys.path.append(_PY_UTILS_PATH) | 248 sys.path.append(_PY_UTILS_PATH) |
| 249 # pylint: disable=import-error | 249 # pylint: disable=import-error |
| 250 import py_utils.cloud_storage as cloud_storage | 250 import py_utils.cloud_storage as cloud_storage |
| 251 | 251 |
| 252 _UNNAMED_FILE = 'unnamed' |
| 253 |
| 254 |
| 252 class NodeWrapper(object): | 255 class NodeWrapper(object): |
| 253 """Wraps an event data node(s). | 256 """Wraps an event data node(s). |
| 254 | 257 |
| 255 A node is a reference into a trace event JSON. Wrappers parse nodes to | 258 A node is a reference into a trace event JSON. Wrappers parse nodes to |
| 256 provide convenient APIs and update nodes when asked to propagate changes | 259 provide convenient APIs and update nodes when asked to propagate changes |
| 257 back (see ApplyModifications() below). | 260 back (see ApplyModifications() below). |
| 258 | 261 |
| 259 Here is an example of legacy metadata event that contains stack frame tree: | 262 Here is an example of legacy metadata event that contains stack frame tree: |
| 260 | 263 |
| 261 { | 264 { |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 310 }, | 313 }, |
| 311 ... | 314 ... |
| 312 ] | 315 ] |
| 313 } | 316 } |
| 314 """ | 317 """ |
| 315 | 318 |
| 316 class Region(object): | 319 class Region(object): |
| 317 def __init__(self, start_address, size, file_path): | 320 def __init__(self, start_address, size, file_path): |
| 318 self._start_address = start_address | 321 self._start_address = start_address |
| 319 self._size = size | 322 self._size = size |
| 320 self._file_path = file_path | 323 self._file_path = file_path if file_path else _UNNAMED_FILE |
| 321 | 324 |
| 322 @property | 325 @property |
| 323 def start_address(self): | 326 def start_address(self): |
| 324 return self._start_address | 327 return self._start_address |
| 325 | 328 |
| 326 @property | 329 @property |
| 327 def end_address(self): | 330 def end_address(self): |
| 328 return self._start_address + self._size | 331 return self._start_address + self._size |
| 329 | 332 |
| 330 @property | 333 @property |
| (...skipping 648 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 979 """Holds file path, addresses to symbolize and stack frames to update. | 982 """Holds file path, addresses to symbolize and stack frames to update. |
| 980 | 983 |
| 981 This class is a link between ELFSymbolizer and a trace file: it specifies | 984 This class is a link between ELFSymbolizer and a trace file: it specifies |
| 982 what to symbolize (addresses) and what to update with the symbolization | 985 what to symbolize (addresses) and what to update with the symbolization |
| 983 result (frames). | 986 result (frames). |
| 984 """ | 987 """ |
| 985 def __init__(self, file_path): | 988 def __init__(self, file_path): |
| 986 self.path = file_path | 989 self.path = file_path |
| 987 self.symbolizable_path = file_path # path to use for symbolization | 990 self.symbolizable_path = file_path # path to use for symbolization |
| 988 self.frames_by_address = collections.defaultdict(list) | 991 self.frames_by_address = collections.defaultdict(list) |
| 992 self.skip_symbolization = False |
| 989 | 993 |
| 990 | 994 |
| 991 def ResolveSymbolizableFiles(processes): | 995 def ResolveSymbolizableFiles(processes): |
| 992 """Resolves and groups PCs into list of SymbolizableFiles. | 996 """Resolves and groups PCs into list of SymbolizableFiles. |
| 993 | 997 |
| 994 As part of the grouping process, this function resolves PC from each stack | 998 As part of the grouping process, this function resolves PC from each stack |
| 995 frame to the corresponding mmap region. Stack frames that failed to resolve | 999 frame to the corresponding mmap region. Stack frames that failed to resolve |
| 996 are symbolized with '<unresolved>'. | 1000 are symbolized with '<unresolved>'. |
| 997 """ | 1001 """ |
| 998 symfile_by_path = {} | 1002 symfile_by_path = {} |
| 999 for process in processes: | 1003 for process in processes: |
| 1000 if not process.memory_map: | 1004 if not process.memory_map: |
| 1001 continue | 1005 continue |
| 1002 for frame in process.stack_frame_map.frame_by_id.itervalues(): | 1006 for frame in process.stack_frame_map.frame_by_id.itervalues(): |
| 1003 if frame.pc is None: | 1007 if frame.pc is None: |
| 1004 continue | 1008 continue |
| 1005 region = process.memory_map.FindRegion(frame.pc) | 1009 region = process.memory_map.FindRegion(frame.pc) |
| 1006 if region is None: | 1010 if region is None: |
| 1007 frame.name = '<unresolved>' | 1011 frame.name = '<unresolved>' |
| 1008 continue | 1012 continue |
| 1009 | 1013 |
| 1010 symfile = symfile_by_path.get(region.file_path) | 1014 symfile = symfile_by_path.get(region.file_path) |
| 1011 if symfile is None: | 1015 if symfile is None: |
| 1012 symfile = SymbolizableFile(region.file_path) | 1016 file_path = region.file_path |
| 1017 symfile = SymbolizableFile(file_path) |
| 1013 symfile_by_path[symfile.path] = symfile | 1018 symfile_by_path[symfile.path] = symfile |
| 1014 | 1019 |
| 1015 relative_pc = frame.pc - region.start_address | 1020 relative_pc = frame.pc - region.start_address |
| 1016 symfile.frames_by_address[relative_pc].append(frame) | 1021 symfile.frames_by_address[relative_pc].append(frame) |
| 1017 return symfile_by_path.values() | 1022 return symfile_by_path.values() |
| 1018 | 1023 |
| 1019 | 1024 |
| 1020 def FindInSystemPath(binary_name): | 1025 def FindInSystemPath(binary_name): |
| 1021 paths = os.environ['PATH'].split(os.pathsep) | 1026 paths = os.environ['PATH'].split(os.pathsep) |
| 1022 for path in paths: | 1027 for path in paths: |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1034 self.is_win = sys.platform == 'win32' | 1039 self.is_win = sys.platform == 'win32' |
| 1035 if self.is_mac: | 1040 if self.is_mac: |
| 1036 self.binary = 'atos' | 1041 self.binary = 'atos' |
| 1037 self._matcher = symbolize_trace_atos_regex.AtosRegexMatcher() | 1042 self._matcher = symbolize_trace_atos_regex.AtosRegexMatcher() |
| 1038 elif self.is_win: | 1043 elif self.is_win: |
| 1039 self.binary = 'addr2line-pdb.exe' | 1044 self.binary = 'addr2line-pdb.exe' |
| 1040 else: | 1045 else: |
| 1041 self.binary = 'addr2line' | 1046 self.binary = 'addr2line' |
| 1042 self.symbolizer_path = FindInSystemPath(self.binary) | 1047 self.symbolizer_path = FindInSystemPath(self.binary) |
| 1043 | 1048 |
| 1044 def _SymbolizeLinuxAndAndroid(self, symfile, unsymbolized_name): | 1049 def _SymbolizeLinuxAndAndroid(self, symfile): |
| 1045 def _SymbolizerCallback(sym_info, frames): | 1050 def _SymbolizerCallback(sym_info, frames): |
| 1046 # Unwind inline chain to the top. | 1051 # Unwind inline chain to the top. |
| 1047 while sym_info.inlined_by: | 1052 while sym_info.inlined_by: |
| 1048 sym_info = sym_info.inlined_by | 1053 sym_info = sym_info.inlined_by |
| 1049 | 1054 |
| 1050 symbolized_name = sym_info.name if sym_info.name else unsymbolized_name | 1055 symbolized_name = (sym_info.name if sym_info.name else |
| 1056 '<{}>'.format(symfile.path)) |
| 1051 for frame in frames: | 1057 for frame in frames: |
| 1052 frame.name = symbolized_name | 1058 frame.name = symbolized_name |
| 1053 | 1059 |
| 1054 symbolizer = elf_symbolizer.ELFSymbolizer(symfile.symbolizable_path, | 1060 symbolizer = elf_symbolizer.ELFSymbolizer(symfile.symbolizable_path, |
| 1055 self.symbolizer_path, | 1061 self.symbolizer_path, |
| 1056 _SymbolizerCallback, | 1062 _SymbolizerCallback, |
| 1057 inlines=True) | 1063 inlines=True) |
| 1058 | 1064 |
| 1059 for address, frames in symfile.frames_by_address.iteritems(): | 1065 for address, frames in symfile.frames_by_address.iteritems(): |
| 1060 # SymbolizeAsync() asserts that the type of address is int. We operate | 1066 # SymbolizeAsync() asserts that the type of address is int. We operate |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1114 stdout_data = stdout_data.split('\n') | 1120 stdout_data = stdout_data.split('\n') |
| 1115 | 1121 |
| 1116 # This is known to be in the same order as stderr_data. | 1122 # This is known to be in the same order as stderr_data. |
| 1117 for i, addr in enumerate(addrs): | 1123 for i, addr in enumerate(addrs): |
| 1118 for frame in symfile.frames_by_address[int(addr, 16)]: | 1124 for frame in symfile.frames_by_address[int(addr, 16)]: |
| 1119 # Output of addr2line with --functions is always 2 outputs per | 1125 # Output of addr2line with --functions is always 2 outputs per |
| 1120 # symbol, function name followed by source line number. Only grab | 1126 # symbol, function name followed by source line number. Only grab |
| 1121 # the function name as line info is not always available. | 1127 # the function name as line info is not always available. |
| 1122 frame.name = stdout_data[i * 2] | 1128 frame.name = stdout_data[i * 2] |
| 1123 | 1129 |
| 1124 def Symbolize(self, symfile, unsymbolized_name): | 1130 def SymbolizeSymfile(self, symfile): |
| 1131 if symfile.skip_symbolization: |
| 1132 for address, frames in symfile.frames_by_address.iteritems(): |
| 1133 unsymbolized_name = ('<' + os.path.basename(symfile.symbolizable_path) |
| 1134 + '>') |
| 1135 # Only append the address if there's a library. |
| 1136 if symfile.symbolizable_path != _UNNAMED_FILE: |
| 1137 unsymbolized_name += ' + ' + str(hex(address)) |
| 1138 |
| 1139 for frame in frames: |
| 1140 frame.name = unsymbolized_name |
| 1141 return |
| 1142 |
| 1125 if self.is_mac: | 1143 if self.is_mac: |
| 1126 self._SymbolizeMac(symfile) | 1144 self._SymbolizeMac(symfile) |
| 1127 elif self.is_win: | 1145 elif self.is_win: |
| 1128 self._SymbolizeWin(symfile) | 1146 self._SymbolizeWin(symfile) |
| 1129 else: | 1147 else: |
| 1130 self._SymbolizeLinuxAndAndroid(symfile, unsymbolized_name) | 1148 self._SymbolizeLinuxAndAndroid(symfile) |
| 1131 | 1149 |
| 1132 def IsSymbolizableFile(self, file_path): | 1150 def IsSymbolizableFile(self, file_path): |
| 1133 if self.is_win: | 1151 if self.is_win: |
| 1134 extension = os.path.splitext(file_path)[1].lower() | 1152 extension = os.path.splitext(file_path)[1].lower() |
| 1135 return extension in ['.dll', '.exe'] | 1153 return extension in ['.dll', '.exe'] |
| 1136 else: | 1154 else: |
| 1137 result = subprocess.check_output(['file', '-0', file_path]) | 1155 result = subprocess.check_output(['file', '-0', file_path]) |
| 1138 type_string = result[result.find('\0') + 1:] | 1156 type_string = result[result.find('\0') + 1:] |
| 1139 return bool(re.match(r'.*(ELF|Mach-O) (32|64)-bit\b.*', | 1157 return bool(re.match(r'.*(ELF|Mach-O) (32|64)-bit\b.*', |
| 1140 type_string, re.DOTALL)) | 1158 type_string, re.DOTALL)) |
| 1141 | 1159 |
| 1142 | 1160 |
| 1143 def SymbolizeFiles(symfiles, symbolizer): | 1161 def SymbolizeFiles(symfiles, symbolizer): |
| 1144 """Symbolizes each file in the given list of SymbolizableFiles | 1162 """Symbolizes each file in the given list of SymbolizableFiles |
| 1145 and updates stack frames with symbolization results.""" | 1163 and updates stack frames with symbolization results.""" |
| 1146 | 1164 |
| 1147 if not symfiles: | 1165 if not symfiles: |
| 1148 print 'Nothing to symbolize.' | 1166 print 'Nothing to symbolize.' |
| 1149 return | 1167 return |
| 1150 | 1168 |
| 1151 print 'Symbolizing...' | 1169 print 'Symbolizing...' |
| 1152 | 1170 |
| 1153 def _SubPrintf(message, *args): | 1171 def _SubPrintf(message, *args): |
| 1154 print (' ' + message).format(*args) | 1172 print (' ' + message).format(*args) |
| 1155 | 1173 |
| 1156 for symfile in symfiles: | 1174 for symfile in symfiles: |
| 1157 unsymbolized_name = '<{}>'.format( | |
| 1158 symfile.path if symfile.path else 'unnamed') | |
| 1159 | |
| 1160 problem = None | 1175 problem = None |
| 1161 if not os.path.isabs(symfile.symbolizable_path): | 1176 if not os.path.isabs(symfile.symbolizable_path): |
| 1162 problem = 'not a file' | 1177 problem = 'not a file' |
| 1163 elif not os.path.isfile(symfile.symbolizable_path): | 1178 elif not os.path.isfile(symfile.symbolizable_path): |
| 1164 problem = "file doesn't exist" | 1179 problem = "file doesn't exist" |
| 1165 elif not symbolizer.IsSymbolizableFile(symfile.symbolizable_path): | 1180 elif not symbolizer.IsSymbolizableFile(symfile.symbolizable_path): |
| 1166 problem = 'file is not symbolizable' | 1181 problem = 'file is not symbolizable' |
| 1167 if problem: | 1182 if problem: |
| 1168 _SubPrintf("Won't symbolize {} PCs for '{}': {}.", | 1183 _SubPrintf("Problem with '{}': {}.", |
| 1169 len(symfile.frames_by_address), | |
| 1170 symfile.symbolizable_path, | 1184 symfile.symbolizable_path, |
| 1171 problem) | 1185 problem) |
| 1172 for frames in symfile.frames_by_address.itervalues(): | 1186 symfile.skip_symbolization = True |
| 1173 for frame in frames: | |
| 1174 frame.name = unsymbolized_name | |
| 1175 continue | |
| 1176 | 1187 |
| 1177 _SubPrintf('Symbolizing {} PCs from {}...', | 1188 _SubPrintf('Symbolizing {} PCs from {}...', |
| 1178 len(symfile.frames_by_address), | 1189 len(symfile.frames_by_address), |
| 1179 symfile.symbolizable_path) | 1190 symfile.symbolizable_path) |
| 1180 | 1191 |
| 1181 symbolizer.Symbolize(symfile, unsymbolized_name) | 1192 symbolizer.SymbolizeSymfile(symfile) |
| 1182 | 1193 |
| 1183 | 1194 |
| 1184 # Matches Android library paths, supports both K (/data/app-lib/<>/lib.so) | 1195 # Matches Android library paths, supports both K (/data/app-lib/<>/lib.so) |
| 1185 # as well as L+ (/data/app/<>/lib/<>/lib.so). Library name is available | 1196 # as well as L+ (/data/app/<>/lib/<>/lib.so). Library name is available |
| 1186 # via 'name' group. | 1197 # via 'name' group. |
| 1187 ANDROID_PATH_MATCHER = re.compile( | 1198 ANDROID_PATH_MATCHER = re.compile( |
| 1188 r'^/data/(?:' | 1199 r'^/data/(?:' |
| 1189 r'app/[^/]+/lib/[^/]+/|' | 1200 r'app/[^/]+/lib/[^/]+/|' |
| 1190 r'app-lib/[^/]+/|' | 1201 r'app-lib/[^/]+/|' |
| 1191 r'data/[^/]+/incremental-install-files/lib/' | 1202 r'data/[^/]+/incremental-install-files/lib/' |
| (...skipping 14 matching lines...) Expand all Loading... |
| 1206 name = match.group('name') | 1217 name = match.group('name') |
| 1207 symfile.symbolizable_path = os.path.join( | 1218 symfile.symbolizable_path = os.path.join( |
| 1208 output_path, ANDROID_UNSTRIPPED_SUBPATH, name) | 1219 output_path, ANDROID_UNSTRIPPED_SUBPATH, name) |
| 1209 else: | 1220 else: |
| 1210 # Clobber file path to trigger "not a file" problem in SymbolizeFiles(). | 1221 # Clobber file path to trigger "not a file" problem in SymbolizeFiles(). |
| 1211 # Without this, files won't be symbolized with "file not found" problem, | 1222 # Without this, files won't be symbolized with "file not found" problem, |
| 1212 # which is not accurate. | 1223 # which is not accurate. |
| 1213 symfile.symbolizable_path = 'android://{}'.format(symfile.path) | 1224 symfile.symbolizable_path = 'android://{}'.format(symfile.path) |
| 1214 | 1225 |
| 1215 | 1226 |
| 1216 def RemapMacFiles(symfiles, symbol_base_directory, version): | 1227 def RemapMacFiles(symfiles, symbol_base_directory, version, |
| 1228 only_symbolize_chrome_symbols): |
| 1217 suffix = ("Google Chrome Framework.dSYM/Contents/Resources/DWARF/" | 1229 suffix = ("Google Chrome Framework.dSYM/Contents/Resources/DWARF/" |
| 1218 "Google Chrome Framework") | 1230 "Google Chrome Framework") |
| 1219 symbol_sub_dir = os.path.join(symbol_base_directory, version) | 1231 symbol_sub_dir = os.path.join(symbol_base_directory, version) |
| 1220 symbolizable_path = os.path.join(symbol_sub_dir, suffix) | 1232 symbolizable_path = os.path.join(symbol_sub_dir, suffix) |
| 1221 | 1233 |
| 1222 for symfile in symfiles: | 1234 for symfile in symfiles: |
| 1223 if symfile.path.endswith("Google Chrome Framework"): | 1235 if symfile.path.endswith("Google Chrome Framework"): |
| 1224 symfile.symbolizable_path = symbolizable_path | 1236 symfile.symbolizable_path = symbolizable_path |
| 1237 elif only_symbolize_chrome_symbols: |
| 1238 symfile.skip_symbolization = True |
| 1225 | 1239 |
| 1226 def RemapWinFiles(symfiles, symbol_base_directory, version, is64bit): | 1240 def RemapWinFiles(symfiles, symbol_base_directory, version, is64bit, |
| 1241 only_symbolize_chrome_symbols): |
| 1227 folder = "win64" if is64bit else "win" | 1242 folder = "win64" if is64bit else "win" |
| 1228 symbol_sub_dir = os.path.join(symbol_base_directory, | 1243 symbol_sub_dir = os.path.join(symbol_base_directory, |
| 1229 "chrome-" + folder + "-" + version) | 1244 "chrome-" + folder + "-" + version) |
| 1230 for symfile in symfiles: | 1245 for symfile in symfiles: |
| 1231 image = os.path.join(symbol_sub_dir, os.path.basename(symfile.path)) | 1246 image = os.path.join(symbol_sub_dir, os.path.basename(symfile.path)) |
| 1232 symbols = image + ".pdb" | 1247 symbols = image + ".pdb" |
| 1233 if os.path.isfile(image) and os.path.isfile(symbols): | 1248 if os.path.isfile(image) and os.path.isfile(symbols): |
| 1234 symfile.symbolizable_path = image | 1249 symfile.symbolizable_path = image |
| 1250 elif only_symbolize_chrome_symbols: |
| 1251 symfile.skip_symbolization = True |
| 1235 | 1252 |
| 1236 def Symbolize(options, trace, symbolizer): | 1253 def SymbolizeTrace(options, trace, symbolizer): |
| 1237 symfiles = ResolveSymbolizableFiles(trace.processes) | 1254 symfiles = ResolveSymbolizableFiles(trace.processes) |
| 1238 | 1255 |
| 1239 # Android trace files don't have any indication they are from Android. | 1256 # Android trace files don't have any indication they are from Android. |
| 1240 # So we're checking for Android-specific paths. | 1257 # So we're checking for Android-specific paths. |
| 1241 if HaveFilesFromAndroid(symfiles): | 1258 if HaveFilesFromAndroid(symfiles): |
| 1242 if not options.output_directory: | 1259 if not options.output_directory: |
| 1243 sys.exit('The trace file appears to be from Android. Please ' | 1260 sys.exit('The trace file appears to be from Android. Please ' |
| 1244 'specify output directory to properly symbolize it.') | 1261 'specify output directory to properly symbolize it.') |
| 1245 RemapAndroidFiles(symfiles, os.path.abspath(options.output_directory)) | 1262 RemapAndroidFiles(symfiles, os.path.abspath(options.output_directory)) |
| 1246 | 1263 |
| 1247 | 1264 |
| 1248 if not trace.is_chromium: | 1265 if not trace.is_chromium: |
| 1266 # A non-chromium trace probably is not coming from the current machine. |
| 1267 # Don't attempt to symbolize system symbols, as that will produce the wrong |
| 1268 # results. |
| 1269 options.only_symbolize_chrome_symbols = True |
| 1249 if symbolizer.is_mac: | 1270 if symbolizer.is_mac: |
| 1250 RemapMacFiles(symfiles, options.symbol_base_directory, trace.version) | 1271 RemapMacFiles(symfiles, options.symbol_base_directory, trace.version, |
| 1272 options.only_symbolize_chrome_symbols) |
| 1251 if symbolizer.is_win: | 1273 if symbolizer.is_win: |
| 1252 RemapWinFiles(symfiles, options.symbol_base_directory, trace.version, | 1274 RemapWinFiles(symfiles, options.symbol_base_directory, trace.version, |
| 1253 trace.is_64bit) | 1275 trace.is_64bit, options.only_symbolize_chrome_symbols) |
| 1254 | 1276 |
| 1255 SymbolizeFiles(symfiles, symbolizer) | 1277 SymbolizeFiles(symfiles, symbolizer) |
| 1256 | 1278 |
| 1257 | 1279 |
| 1258 def OpenTraceFile(file_path, mode): | 1280 def OpenTraceFile(file_path, mode): |
| 1259 if file_path.endswith('.gz'): | 1281 if file_path.endswith('.gz'): |
| 1260 return gzip.open(file_path, mode + 'b') | 1282 return gzip.open(file_path, mode + 'b') |
| 1261 else: | 1283 else: |
| 1262 return open(file_path, mode + 't') | 1284 return open(file_path, mode + 't') |
| 1263 | 1285 |
| 1264 | 1286 |
| 1265 def FetchAndExtractSymbolsMac(symbol_base_directory, version): | 1287 def FetchAndExtractSymbolsMac(symbol_base_directory, version, |
| 1288 cloud_storage_bucket): |
| 1266 def GetLocalPath(base_dir, version): | 1289 def GetLocalPath(base_dir, version): |
| 1267 return os.path.join(base_dir, version + ".tar.bz2") | 1290 return os.path.join(base_dir, version + ".tar.bz2") |
| 1268 def GetSymbolsPath(version): | 1291 def GetSymbolsPath(version): |
| 1269 return "desktop-*/" + version + "/mac64/Google Chrome.dSYM.tar.bz2" | 1292 return "desktop-*/" + version + "/mac64/Google Chrome.dSYM.tar.bz2" |
| 1270 def ExtractSymbolTarFile(symbol_sub_dir, symbol_tar_file): | 1293 def ExtractSymbolTarFile(symbol_sub_dir, symbol_tar_file): |
| 1271 os.makedirs(symbol_sub_dir) | 1294 os.makedirs(symbol_sub_dir) |
| 1272 with tarfile.open(os.path.expanduser(symbol_tar_file), "r:bz2") as tar: | 1295 with tarfile.open(os.path.expanduser(symbol_tar_file), "r:bz2") as tar: |
| 1273 tar.extractall(symbol_sub_dir) | 1296 tar.extractall(symbol_sub_dir) |
| 1274 | 1297 |
| 1275 symbol_sub_dir = os.path.join(symbol_base_directory, version) | 1298 symbol_sub_dir = os.path.join(symbol_base_directory, version) |
| 1276 if os.path.isdir(symbol_sub_dir): | 1299 if os.path.isdir(symbol_sub_dir): |
| 1277 return True | 1300 return True |
| 1278 | 1301 |
| 1279 bzip_path = GetLocalPath(symbol_base_directory, version) | 1302 bzip_path = GetLocalPath(symbol_base_directory, version) |
| 1280 if not os.path.isfile(bzip_path): | 1303 if not os.path.isfile(bzip_path): |
| 1281 | 1304 |
| 1282 cloud_storage_bucket = "chrome-unsigned" | |
| 1283 if not cloud_storage.Exists(cloud_storage_bucket, GetSymbolsPath(version)): | 1305 if not cloud_storage.Exists(cloud_storage_bucket, GetSymbolsPath(version)): |
| 1284 print "Can't find symbols on GCS." | 1306 print "Can't find symbols on GCS." |
| 1285 return False | 1307 return False |
| 1286 print "Downloading symbols files from GCS, please wait." | 1308 print "Downloading symbols files from GCS, please wait." |
| 1287 cloud_storage.Get(cloud_storage_bucket, GetSymbolsPath(version), bzip_path) | 1309 cloud_storage.Get(cloud_storage_bucket, GetSymbolsPath(version), bzip_path) |
| 1288 | 1310 |
| 1289 ExtractSymbolTarFile(symbol_sub_dir, bzip_path) | 1311 ExtractSymbolTarFile(symbol_sub_dir, bzip_path) |
| 1290 return True | 1312 return True |
| 1291 | 1313 |
| 1292 | 1314 |
| 1293 def FetchAndExtractSymbolsWin(symbol_base_directory, version, is64bit): | 1315 def FetchAndExtractSymbolsWin(symbol_base_directory, version, is64bit, |
| 1316 cloud_storage_bucket): |
| 1294 def DownloadAndExtractZipFile(zip_path, source, destination): | 1317 def DownloadAndExtractZipFile(zip_path, source, destination): |
| 1295 if not os.path.isfile(zip_path): | 1318 if not os.path.isfile(zip_path): |
| 1296 cloud_storage_bucket = "chrome-unsigned" | |
| 1297 if not cloud_storage.Exists(cloud_storage_bucket, source): | 1319 if not cloud_storage.Exists(cloud_storage_bucket, source): |
| 1298 print "Can't find symbols on GCS." | 1320 print "Can't find symbols on GCS." |
| 1299 return False | 1321 return False |
| 1300 print "Downloading symbols files from GCS, please wait." | 1322 print "Downloading symbols files from GCS, please wait." |
| 1301 cloud_storage.Get(cloud_storage_bucket, source, zip_path) | 1323 cloud_storage.Get(cloud_storage_bucket, source, zip_path) |
| 1302 if not os.path.isfile(zip_path): | 1324 if not os.path.isfile(zip_path): |
| 1303 print "Can't download symbols on GCS." | 1325 print "Can't download symbols on GCS." |
| 1304 return False | 1326 return False |
| 1305 with zipfile.ZipFile(zip_path, "r") as zip_file: | 1327 with zipfile.ZipFile(zip_path, "r") as zip_file: |
| 1306 for member in zip_file.namelist(): | 1328 for member in zip_file.namelist(): |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1346 help='Trace file to symbolize (.json or .json.gz)') | 1368 help='Trace file to symbolize (.json or .json.gz)') |
| 1347 | 1369 |
| 1348 parser.add_argument( | 1370 parser.add_argument( |
| 1349 '--no-backup', dest='backup', default='true', action='store_false', | 1371 '--no-backup', dest='backup', default='true', action='store_false', |
| 1350 help="Don't create {} files".format(BACKUP_FILE_TAG)) | 1372 help="Don't create {} files".format(BACKUP_FILE_TAG)) |
| 1351 | 1373 |
| 1352 parser.add_argument( | 1374 parser.add_argument( |
| 1353 '--output-directory', | 1375 '--output-directory', |
| 1354 help='The path to the build output directory, such as out/Debug.') | 1376 help='The path to the build output directory, such as out/Debug.') |
| 1355 | 1377 |
| 1378 parser.add_argument( |
| 1379 '--only-symbolize-chrome-symbols', |
| 1380 action='store_true', |
| 1381 help='Prevents symbolization of non-Chrome [system] symbols.') |
| 1382 |
| 1383 parser.add_argument( |
| 1384 '--cloud-storage-bucket', default='chrome-unsigned', |
| 1385 help="Bucket that holds symbols for official Chrome builds. " |
| 1386 "Used by tests, which don't have access to the default bucket.") |
| 1387 |
| 1356 home_dir = os.path.expanduser('~') | 1388 home_dir = os.path.expanduser('~') |
| 1357 default_dir = os.path.join(home_dir, "symbols") | 1389 default_dir = os.path.join(home_dir, "symbols") |
| 1358 parser.add_argument( | 1390 parser.add_argument( |
| 1359 '--symbol-base-directory', | 1391 '--symbol-base-directory', |
| 1360 default=default_dir, | 1392 default=default_dir, |
| 1361 help='Directory where symbols are downloaded and cached.') | 1393 help='Directory where symbols are downloaded and cached.') |
| 1362 | 1394 |
| 1363 symbolizer = Symbolizer() | 1395 symbolizer = Symbolizer() |
| 1364 if symbolizer.symbolizer_path is None: | 1396 if symbolizer.symbolizer_path is None: |
| 1365 sys.exit("Can't symbolize - no %s in PATH." % symbolizer.binary) | 1397 sys.exit("Can't symbolize - no %s in PATH." % symbolizer.binary) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1378 return False | 1410 return False |
| 1379 | 1411 |
| 1380 # If the trace is from Chromium, assume that symbols are already present. | 1412 # If the trace is from Chromium, assume that symbols are already present. |
| 1381 # Otherwise the trace is from Google Chrome. Assume that this is not a local | 1413 # Otherwise the trace is from Google Chrome. Assume that this is not a local |
| 1382 # build of Google Chrome with symbols, and that we need to fetch symbols | 1414 # build of Google Chrome with symbols, and that we need to fetch symbols |
| 1383 # from gcs. | 1415 # from gcs. |
| 1384 if not trace.is_chromium: | 1416 if not trace.is_chromium: |
| 1385 has_symbols = False | 1417 has_symbols = False |
| 1386 if symbolizer.is_mac: | 1418 if symbolizer.is_mac: |
| 1387 has_symbols = FetchAndExtractSymbolsMac(options.symbol_base_directory, | 1419 has_symbols = FetchAndExtractSymbolsMac(options.symbol_base_directory, |
| 1388 trace.version) | 1420 trace.version, |
| 1421 options.cloud_storage_bucket) |
| 1389 if symbolizer.is_win: | 1422 if symbolizer.is_win: |
| 1390 has_symbols = FetchAndExtractSymbolsWin(options.symbol_base_directory, | 1423 has_symbols = FetchAndExtractSymbolsWin(options.symbol_base_directory, |
| 1391 trace.version, trace.is_64bit) | 1424 trace.version, trace.is_64bit, |
| 1425 options.cloud_storage_bucket) |
| 1392 if not has_symbols: | 1426 if not has_symbols: |
| 1393 print 'Cannot fetch symbols from GCS' | 1427 print 'Cannot fetch symbols from GCS' |
| 1394 return False | 1428 return False |
| 1395 | 1429 |
| 1396 Symbolize(options, trace, symbolizer) | 1430 SymbolizeTrace(options, trace, symbolizer) |
| 1397 | 1431 |
| 1398 if trace.modified: | 1432 if trace.modified: |
| 1399 trace.ApplyModifications() | 1433 trace.ApplyModifications() |
| 1400 | 1434 |
| 1401 if options.backup: | 1435 if options.backup: |
| 1402 backup_file_path = trace_file_path + BACKUP_FILE_TAG | 1436 backup_file_path = trace_file_path + BACKUP_FILE_TAG |
| 1403 print 'Backing up trace file to {}'.format(backup_file_path) | 1437 print 'Backing up trace file to {}'.format(backup_file_path) |
| 1404 os.rename(trace_file_path, backup_file_path) | 1438 os.rename(trace_file_path, backup_file_path) |
| 1405 | 1439 |
| 1406 print 'Updating the trace file...' | 1440 print 'Updating the trace file...' |
| 1407 with OpenTraceFile(trace_file_path, 'w') as trace_file: | 1441 with OpenTraceFile(trace_file_path, 'w') as trace_file: |
| 1408 json.dump(trace.node, trace_file) | 1442 json.dump(trace.node, trace_file) |
| 1409 else: | 1443 else: |
| 1410 print 'No modifications were made - not updating the trace file.' | 1444 print 'No modifications were made - not updating the trace file.' |
| 1445 return True |
| 1411 | 1446 |
| 1412 | 1447 |
| 1413 if __name__ == '__main__': | 1448 if __name__ == '__main__': |
| 1414 main(sys.argv[1:]) | 1449 main(sys.argv[1:]) |
| OLD | NEW |