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 |