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

Side by Side Diff: tools/valgrind/asan/asan_symbolize.py

Issue 859293002: Fix report symbolization on swarming bots. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 5 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 | « testing/test_env.py ('k') | no next file » | 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 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
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)
55 return bool(match)
56
57
58 def split_path(path):
59 ret = []
60 while True:
61 head, tail = os.path.split(path)
62 if head == path:
63 return [head] + ret
64 ret, path = [tail] + ret, head
65
66
67 def chrome_product_dir_path(exe_path):
68 if exe_path is None:
69 return None
70 path_parts = split_path(exe_path)
71 # Make sure the product dir path isn't empty if |exe_path| consists of
72 # a single component.
73 if len(path_parts) == 1:
74 path_parts = ['.'] + path_parts
75 for index, part in enumerate(path_parts):
76 if part.endswith('.app'):
77 return os.path.join(*path_parts[:index])
78 # If the executable isn't an .app bundle, it's a commandline binary that
79 # resides right in the product dir.
80 return os.path.join(*path_parts[:-1])
81
82
83 inode_path_cache = {}
84
85
86 def find_inode_at_path(inode, path):
87 if inode in inode_path_cache:
88 return inode_path_cache[inode]
89 cmd = ['find', path, '-inum', str(inode)]
90 find_line = subprocess.check_output(cmd).rstrip()
91 lines = find_line.split('\n')
92 ret = None
93 if lines:
94 # `find` may give us several paths (e.g. 'Chromium Framework' in the
95 # product dir and 'Chromium Framework' inside 'Chromium.app',
96 # chrome_dsym_hints() will produce correct .dSYM path for any of them.
97 ret = lines[0]
98 inode_path_cache[inode] = ret
99 return ret
100
101
102 # Create a binary name filter that works around https://crbug.com/444835.
103 # When running tests on OSX swarming servers, ASan sometimes prints paths to
104 # files in cache (ending with SHA1 filenames) instead of paths to hardlinks to
105 # those files in the product dir.
106 # For a given |binary_path| chrome_osx_binary_name_filter() returns one of the
107 # hardlinks to the same inode in |product_dir_path|.
108 def make_chrome_osx_binary_name_filter(product_dir_path=''):
109 def chrome_osx_binary_name_filter(binary_path):
110 basename = os.path.basename(binary_path)
111 if is_hash_name(basename) and product_dir_path:
112 inode = os.stat(binary_path).st_ino
113 new_binary_path = find_inode_at_path(inode, product_dir_path)
114 if new_binary_path:
115 return new_binary_path
116 return binary_path
117 return chrome_osx_binary_name_filter
118
119
51 # Construct a path to the .dSYM bundle for the given binary. 120 # Construct a path to the .dSYM bundle for the given binary.
52 # There are three possible cases for binary location in Chromium: 121 # There are three possible cases for binary location in Chromium:
53 # 1. The binary is a standalone executable or dynamic library in the product 122 # 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. 123 # 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 124 # 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. 125 # "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 126 # 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 127 # (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. 128 # is in Inner.app.dSYM in the product dir.
60 # The first case is handled by llvm-symbolizer, so we only need to construct 129 # The first case is handled by llvm-symbolizer, so we only need to construct
61 # .dSYM paths for .app bundles and frameworks. 130 # .dSYM paths for .app bundles and frameworks.
62 # We're assuming that there're no more than two nested bundles in the binary 131 # We're assuming that there're no more than two nested bundles in the binary
63 # path. Only one of these bundles may be a framework and frameworks cannot 132 # path. Only one of these bundles may be a framework and frameworks cannot
64 # contain other bundles. 133 # contain other bundles.
65 def chrome_dsym_hints(binary): 134 def chrome_dsym_hints(binary):
66 path_parts = binary.split(os.path.sep) 135 path_parts = split_path(binary)
67 app_positions = [] 136 app_positions = []
68 framework_positions = [] 137 framework_positions = []
69 for index, part in enumerate(path_parts): 138 for index, part in enumerate(path_parts):
70 if part.endswith('.app'): 139 if part.endswith('.app'):
71 app_positions.append(index) 140 app_positions.append(index)
72 elif part.endswith('.framework'): 141 elif part.endswith('.framework'):
73 framework_positions.append(index) 142 framework_positions.append(index)
74 bundle_positions = app_positions + framework_positions 143 bundle_positions = app_positions + framework_positions
75 bundle_positions.sort() 144 bundle_positions.sort()
76 assert len(bundle_positions) <= 2, \ 145 assert len(bundle_positions) <= 2, \
77 "The path contains more than two nested bundles: %s" % binary 146 "The path contains more than two nested bundles: %s" % binary
78 if len(bundle_positions) == 0: 147 if len(bundle_positions) == 0:
79 # Case 1: this is a standalone executable or dylib. 148 # Case 1: this is a standalone executable or dylib.
80 return [] 149 return []
81 assert (not (len(app_positions) == 1 and 150 assert (not (len(app_positions) == 1 and
82 len(framework_positions) == 1 and 151 len(framework_positions) == 1 and
83 app_positions[0] > framework_positions[0])), \ 152 app_positions[0] > framework_positions[0])), \
84 "The path contains an app bundle inside a framework: %s" % binary 153 "The path contains an app bundle inside a framework: %s" % binary
85 # Cases 2 and 3. The outermost bundle (which is the only bundle in the case 2) 154 # Cases 2 and 3. The outermost bundle (which is the only bundle in the case 2)
86 # is located in the product dir. 155 # is located in the product dir.
87 outermost_bundle = bundle_positions[0] 156 outermost_bundle = bundle_positions[0]
88 product_dir = path_parts[:outermost_bundle] 157 product_dir = path_parts[:outermost_bundle]
89 # In case 2 this is the same as |outermost_bundle|. 158 # In case 2 this is the same as |outermost_bundle|.
90 innermost_bundle = bundle_positions[-1] 159 innermost_bundle = bundle_positions[-1]
91 dsym_path = product_dir + [path_parts[innermost_bundle]] 160 dsym_path = product_dir + [path_parts[innermost_bundle]]
92 result = '%s.dSYM' % os.path.sep.join(dsym_path) 161 result = '%s.dSYM' % os.path.join(*dsym_path)
93 return [result] 162 return [result]
94 163
95 164
96 # We want our output to match base::EscapeJSONString(), which produces 165 # We want our output to match base::EscapeJSONString(), which produces
97 # doubly-escaped strings. The first escaping pass is handled by this class. The 166 # doubly-escaped strings. The first escaping pass is handled by this class. The
98 # second pass happens when JSON data is dumped to file. 167 # second pass happens when JSON data is dumped to file.
99 class StringEncoder(json.JSONEncoder): 168 class StringEncoder(json.JSONEncoder):
100 def __init__(self): 169 def __init__(self):
101 json.JSONEncoder.__init__(self) 170 json.JSONEncoder.__init__(self)
102 171
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 def main(): 231 def main():
163 parser = argparse.ArgumentParser(description='Symbolize sanitizer reports.') 232 parser = argparse.ArgumentParser(description='Symbolize sanitizer reports.')
164 parser.add_argument('--test-summary-json-file', 233 parser.add_argument('--test-summary-json-file',
165 help='Path to a JSON file produced by the test launcher. The script will ' 234 help='Path to a JSON file produced by the test launcher. The script will '
166 'ignore stdandard input and instead symbolize the output stnippets ' 235 'ignore stdandard input and instead symbolize the output stnippets '
167 'inside the JSON file. The result will be written back to the JSON ' 236 'inside the JSON file. The result will be written back to the JSON '
168 'file.') 237 'file.')
169 parser.add_argument('strip_path_prefix', nargs='*', 238 parser.add_argument('strip_path_prefix', nargs='*',
170 help='When printing source file names, the longest prefix ending in one ' 239 help='When printing source file names, the longest prefix ending in one '
171 'of these substrings will be stripped. E.g.: "Release/../../".') 240 'of these substrings will be stripped. E.g.: "Release/../../".')
241 parser.add_argument('--executable-path',
242 help='Path to program executable. Used on OSX swarming bots to locate '
243 'dSYM bundles for associated frameworks and bundles.')
172 args = parser.parse_args() 244 args = parser.parse_args()
173 245
174 disable_buffering() 246 disable_buffering()
175 set_symbolizer_path() 247 set_symbolizer_path()
176 asan_symbolize.demangle = True 248 asan_symbolize.demangle = True
177 asan_symbolize.fix_filename_patterns = args.strip_path_prefix 249 asan_symbolize.fix_filename_patterns = args.strip_path_prefix
178 loop = asan_symbolize.SymbolizationLoop(dsym_hint_producer=chrome_dsym_hints) 250 binary_name_filter = None
251 if os.uname()[0] == 'Darwin':
Timur Iskhodzhanov 2015/02/11 21:36:16 this asserts on Windows, again.
252 binary_name_filter = make_chrome_osx_binary_name_filter(
253 chrome_product_dir_path(args.executable_path))
254 loop = asan_symbolize.SymbolizationLoop(
255 binary_name_filter=binary_name_filter,
256 dsym_hint_producer=chrome_dsym_hints)
179 257
180 if args.test_summary_json_file: 258 if args.test_summary_json_file:
181 symbolize_snippets_in_json(args.test_summary_json_file, loop) 259 symbolize_snippets_in_json(args.test_summary_json_file, loop)
182 else: 260 else:
183 # Process stdin. 261 # Process stdin.
184 asan_symbolize.logfile = sys.stdin 262 asan_symbolize.logfile = sys.stdin
185 loop.process_logfile() 263 loop.process_logfile()
186 264
187 if __name__ == '__main__': 265 if __name__ == '__main__':
188 main() 266 main()
OLDNEW
« no previous file with comments | « testing/test_env.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698