Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 | 2 |
| 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 from third_party import asan_symbolize | 7 from third_party import asan_symbolize |
| 8 | 8 |
| 9 import argparse | 9 import argparse |
| 10 import base64 | 10 import base64 |
| 11 import json | 11 import json |
| 12 import os | 12 import os |
| 13 import re | |
| 14 import subprocess | |
| 13 import sys | 15 import sys |
| 14 | 16 |
| 15 class LineBuffered(object): | 17 class LineBuffered(object): |
| 16 """Disable buffering on a file object.""" | 18 """Disable buffering on a file object.""" |
| 17 def __init__(self, stream): | 19 def __init__(self, stream): |
| 18 self.stream = stream | 20 self.stream = stream |
| 19 | 21 |
| 20 def write(self, data): | 22 def write(self, data): |
| 21 self.stream.write(data) | 23 self.stream.write(data) |
| 22 if '\n' in data: | 24 if '\n' in data: |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 41 script_dir = os.path.dirname(os.path.abspath(__file__)) | 43 script_dir = os.path.dirname(os.path.abspath(__file__)) |
| 42 # Assume this script resides three levels below src/ (i.e. | 44 # Assume this script resides three levels below src/ (i.e. |
| 43 # src/tools/valgrind/asan/). | 45 # src/tools/valgrind/asan/). |
| 44 src_root = os.path.join(script_dir, "..", "..", "..") | 46 src_root = os.path.join(script_dir, "..", "..", "..") |
| 45 symbolizer_path = os.path.join(src_root, 'third_party', | 47 symbolizer_path = os.path.join(src_root, 'third_party', |
| 46 'llvm-build', 'Release+Asserts', 'bin', 'llvm-symbolizer') | 48 'llvm-build', 'Release+Asserts', 'bin', 'llvm-symbolizer') |
| 47 assert(os.path.isfile(symbolizer_path)) | 49 assert(os.path.isfile(symbolizer_path)) |
| 48 os.environ['LLVM_SYMBOLIZER_PATH'] = os.path.abspath(symbolizer_path) | 50 os.environ['LLVM_SYMBOLIZER_PATH'] = os.path.abspath(symbolizer_path) |
| 49 | 51 |
| 50 | 52 |
| 53 def is_hash_name(name): | |
| 54 match = re.match('[0-9a-f]+', name) | |
|
earthdok
2015/01/23 15:41:50
Note that re.match() matches at the beginning of t
Alexander Potapenko
2015/01/23 16:37:55
Done.
| |
| 55 return bool(match) | |
| 56 | |
| 57 | |
| 58 def chrome_product_dir_path(exe_path): | |
| 59 if exe_path is None: | |
| 60 return None | |
| 61 path_parts = exe_path.split(os.path.sep) | |
|
earthdok
2015/01/23 15:41:50
why not os.path.split()/os.path.join()?
Alexander Potapenko
2015/01/23 16:37:55
Done
| |
| 62 if len(path_parts) == 1: | |
|
earthdok
2015/01/23 15:41:50
the logic is not entirely clear here. a comment wo
Alexander Potapenko
2015/01/23 16:37:55
Done.
| |
| 63 path_parts = ['.'] + path_parts | |
| 64 for index, part in enumerate(path_parts): | |
| 65 if part.endswith('.app'): | |
| 66 return os.path.sep.join(path_parts[:index]) | |
| 67 return os.path.sep.join(path_parts[:-1]) | |
| 68 | |
| 69 | |
| 70 inode_path_cache = {} | |
| 71 | |
| 72 | |
| 73 def find_inode_at_path(inode, path): | |
| 74 if inode in inode_path_cache: | |
| 75 return inode_path_cache[inode] | |
| 76 cmd = ['find', path, '-inum', str(inode)] | |
| 77 find_line = subprocess.check_output(cmd).rstrip() | |
| 78 lines = find_line.split('\n') | |
| 79 ret = None | |
| 80 if lines: | |
| 81 # `find` may give us several paths (e.g. 'Chromium Framework' in the | |
| 82 # product dir and 'Chromium Framework' inside 'Chromium.app', | |
| 83 # chrome_dsym_hints() will produce correct .dSYM path for any of them. | |
| 84 ret = lines[0] | |
| 85 inode_path_cache[inode] = ret | |
| 86 return ret | |
| 87 | |
| 88 | |
| 89 # Create a binary name filter that works around https://crbug.com/444835. | |
| 90 # When running tests on OSX swarming servers, ASan sometimes prints paths to | |
| 91 # files in cache (ending with SHA1 filenames) instead of paths to hardlinks to | |
| 92 # those files in the product dir. | |
| 93 # For a given |binary_path| chrome_osx_binary_name_filter() returns one of the | |
| 94 # hardlinks to the same inode in |product_dir_path|. | |
| 95 def make_chrome_osx_binary_name_filter(product_dir_path=''): | |
| 96 def chrome_osx_binary_name_filter(binary_path): | |
| 97 basename = os.path.basename(binary_path) | |
| 98 if is_hash_name(basename) and product_dir_path: | |
| 99 inode = os.stat(binary_path).st_ino | |
| 100 new_binary_path = find_inode_at_path(inode, product_dir_path) | |
| 101 if new_binary_path: | |
| 102 binary_path = new_binary_path | |
|
earthdok
2015/01/23 15:41:50
return new_binary_path ?
Alexander Potapenko
2015/01/23 16:37:55
Done.
| |
| 103 return binary_path | |
| 104 return chrome_osx_binary_name_filter | |
| 105 | |
| 106 | |
| 51 # Construct a path to the .dSYM bundle for the given binary. | 107 # Construct a path to the .dSYM bundle for the given binary. |
| 52 # There are three possible cases for binary location in Chromium: | 108 # There are three possible cases for binary location in Chromium: |
| 53 # 1. The binary is a standalone executable or dynamic library in the product | 109 # 1. The binary is a standalone executable or dynamic library in the product |
| 54 # dir, the debug info is in "binary.dSYM" in the product dir. | 110 # dir, the debug info is in "binary.dSYM" in the product dir. |
| 55 # 2. The binary is a standalone framework or .app bundle, the debug info is in | 111 # 2. The binary is a standalone framework or .app bundle, the debug info is in |
| 56 # "Framework.framework.dSYM" or "App.app.dSYM" in the product dir. | 112 # "Framework.framework.dSYM" or "App.app.dSYM" in the product dir. |
| 57 # 3. The binary is a framework or an .app bundle within another .app bundle | 113 # 3. The binary is a framework or an .app bundle within another .app bundle |
| 58 # (e.g. Outer.app/Contents/Versions/1.2.3.4/Inner.app), and the debug info | 114 # (e.g. Outer.app/Contents/Versions/1.2.3.4/Inner.app), and the debug info |
| 59 # is in Inner.app.dSYM in the product dir. | 115 # is in Inner.app.dSYM in the product dir. |
| 60 # The first case is handled by llvm-symbolizer, so we only need to construct | 116 # The first case is handled by llvm-symbolizer, so we only need to construct |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 162 def main(): | 218 def main(): |
| 163 parser = argparse.ArgumentParser(description='Symbolize sanitizer reports.') | 219 parser = argparse.ArgumentParser(description='Symbolize sanitizer reports.') |
| 164 parser.add_argument('--test-summary-json-file', | 220 parser.add_argument('--test-summary-json-file', |
| 165 help='Path to a JSON file produced by the test launcher. The script will ' | 221 help='Path to a JSON file produced by the test launcher. The script will ' |
| 166 'ignore stdandard input and instead symbolize the output stnippets ' | 222 'ignore stdandard input and instead symbolize the output stnippets ' |
| 167 'inside the JSON file. The result will be written back to the JSON ' | 223 'inside the JSON file. The result will be written back to the JSON ' |
| 168 'file.') | 224 'file.') |
| 169 parser.add_argument('strip_path_prefix', nargs='*', | 225 parser.add_argument('strip_path_prefix', nargs='*', |
| 170 help='When printing source file names, the longest prefix ending in one ' | 226 help='When printing source file names, the longest prefix ending in one ' |
| 171 'of these substrings will be stripped. E.g.: "Release/../../".') | 227 'of these substrings will be stripped. E.g.: "Release/../../".') |
| 228 parser.add_argument('--executable_path', | |
|
earthdok
2015/01/23 15:41:50
dash, not underscore
Alexander Potapenko
2015/01/23 16:37:55
Done.
| |
| 229 help='Path to program executable. Will be used on OSX swarming bots to ' | |
|
earthdok
2015/01/23 15:41:50
Please drop the future tense.
Alexander Potapenko
2015/01/23 16:37:55
Done.
| |
| 230 'locate dSYM bundles for associated frameworks and bundles.') | |
| 172 args = parser.parse_args() | 231 args = parser.parse_args() |
| 173 | 232 |
| 174 disable_buffering() | 233 disable_buffering() |
| 175 set_symbolizer_path() | 234 set_symbolizer_path() |
| 176 asan_symbolize.demangle = True | 235 asan_symbolize.demangle = True |
| 177 asan_symbolize.fix_filename_patterns = args.strip_path_prefix | 236 asan_symbolize.fix_filename_patterns = args.strip_path_prefix |
| 178 loop = asan_symbolize.SymbolizationLoop(dsym_hint_producer=chrome_dsym_hints) | 237 maybe_binary_name_filter = None |
|
earthdok
2015/01/23 15:41:50
"maybe_" is kind of unnecessary
Alexander Potapenko
2015/01/23 16:37:55
Done.
| |
| 238 if os.uname()[0] == 'Darwin': | |
| 239 maybe_binary_name_filter = make_chrome_osx_binary_name_filter( | |
| 240 chrome_product_dir_path(args.executable_path)) | |
| 241 loop = asan_symbolize.SymbolizationLoop( | |
| 242 binary_name_filter=maybe_binary_name_filter, | |
| 243 dsym_hint_producer=chrome_dsym_hints) | |
| 179 | 244 |
| 180 if args.test_summary_json_file: | 245 if args.test_summary_json_file: |
| 181 symbolize_snippets_in_json(args.test_summary_json_file, loop) | 246 symbolize_snippets_in_json(args.test_summary_json_file, loop) |
| 182 else: | 247 else: |
| 183 # Process stdin. | 248 # Process stdin. |
| 184 asan_symbolize.logfile = sys.stdin | 249 asan_symbolize.logfile = sys.stdin |
| 185 loop.process_logfile() | 250 loop.process_logfile() |
| 186 | 251 |
| 187 if __name__ == '__main__': | 252 if __name__ == '__main__': |
| 188 main() | 253 main() |
| OLD | NEW |