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

Side by Side Diff: scripts/slave/chromium/win_apply_asan.py

Issue 11428043: Add the win_apply_asan client script. (Closed) Base URL: http://git.chromium.org/chromium/tools/build.git@asan_zip
Patch Set: Created 8 years 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 | « no previous file | 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
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 import multiprocessing
7 import optparse
8 import os
9 import shutil
10 import subprocess
11 import sys
12
13
14 SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
15 BLACKLIST = set((
16 'crnss.dll',
17 'gpu.dll',
18 'icuuc.dll',
19 'sql.dll',
20 ))
21
22
23 class ASANitizer(object):
24 def __init__(self, instrument_exe, stopped):
25 self.instrument_exe = instrument_exe
26 self.stopped = stopped
27
28 def __call__(self, job):
29 retval = 0
30 stdout = ''
31 pe_image, pdb = job
32
33 try:
34 if not self.stopped.is_set():
35 out_pe = AddExtensionComponent(pe_image, 'asan')
36 out_pdb = AddExtensionComponent(pdb, 'asan')
37
38 # Note that instrument.exe requires --foo=bar format (including the '=')
39 command = [
40 self.instrument_exe, '--mode=ASAN',
41 '--input-image=%s' % pe_image,
42 '--output-image=%s' % out_pe,
43 '--output-pdb=%s' % out_pdb,
44 '2>&1' # Combine stderr+stdout so that they're in order
45 ]
46
47 for fname in filter(os.path.exists, (out_pe, out_pdb)):
48 os.remove(fname)
49
50 proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
51 stdout, _ = proc.communicate()
52 retval = proc.returncode
53
54 return (retval, stdout, pe_image)
55 except Exception:
56 import traceback
57 return (1, stdout+'\n'+traceback.format_exc(), pe_image)
58
59
60 def AddExtensionComponent(path, new_ext):
61 """Prepends new_ext to the existing extension.
62
63 >>> ChangeExtension('hello.foo.dll', 'asan')
64 'hello.asan.foo.dll'
65 """
66 # Don't use os.path.splitext, because it will split on the rightmost dot
67 # instead of the leftmost dot.
68 base, ext = path.split('.', 1)
69 return base + '.' + new_ext + '.' + ext
70
71
72 def UpdateAsanRuntime(full_directory, runtime_path):
73 """Updates the ASAN runtime dll in the build directory, if it exists."""
74 runtime = os.path.join(full_directory, os.path.basename(runtime_path))
75
76 if os.path.exists(runtime):
77 print('Removing', runtime)
78 os.remove(runtime)
79
80 print 'Copying %s -> %s' % (runtime_path, runtime)
81 shutil.copy2(runtime_path, runtime)
82
83 fname = os.path.basename(runtime_path)
84 print 'Blacklisting %s' % fname
85 BLACKLIST.add(fname)
86
87
88 def GetCompatiblePDB(pe_image):
89 """Returns <path to pdb> or None (if no good pdb exists)."""
90 # TODO(iannucci): Use PE header to look up pdb name.
91 # for now, assume that the pdb is always just PE.pdb
92 pdb_path = pe_image+'.pdb'
93 return pdb_path if os.path.exists(pdb_path) else None
94
95
96 def FindFilesToAsan(directory):
97 """Finds eligible PE images in given directory.
98
99 A PE image is eligible if it has a corresponding pdb and doesn't already have
100 ASAN applied to it. Skips files which have an extra extension (like
101 foo.orig.exe).
102 """
103 ret = []
104
105 def GoodExeOrDll(fname):
106 return (
107 '.' in fname and
108 fname not in BLACKLIST and
109 fname.split('.', 1)[-1].lower() in ('exe', 'dll'))
110
111 for root, _, files in os.walk(directory):
112 for pe_image in (os.path.join(root, f) for f in files if GoodExeOrDll(f)):
113 pdb = GetCompatiblePDB(pe_image)
114 if not pdb:
115 print >> sys.stderr, 'PDB for "%s" does not exist.' % pe_image
116 continue
117
118 ret.append((pe_image, pdb))
119 return ret
120
121
122 def ApplyAsanToBuild(full_directory, instrument_exe, jobs):
123 """Applies ASAN to all exe's/dll's in the build directory."""
124 to_asan = FindFilesToAsan(full_directory)
125
126 if not to_asan:
127 print >> sys.stderr, 'No files to ASAN!'
128 return 1
129
130 stopped = multiprocessing.Event()
M-A Ruel 2012/11/29 23:53:00 BTW, it'd probably be faster (especially latency-w
131 sanitizer = ASANitizer(instrument_exe, stopped)
132 pool = multiprocessing.Pool(jobs)
133
134 ret = 0
135 try:
136 generator = pool.imap_unordered(sanitizer, to_asan)
137 for retval, stdout, failed_image in generator:
138 ostream = (sys.stderr if retval else sys.stdout)
139 print >> ostream, stdout
140 sys.stdout.flush()
141 sys.stderr.flush()
142 if retval:
143 print 'Failed to ASAN %s. Stopping remaining jobs.' % failed_image
144 ret = retval
145 stopped.set()
146 except KeyboardInterrupt:
147 stopped.set()
148 pool.close()
149 pool.join()
150
151 return ret
152
153
154 def main():
155 default_asan_dir = os.path.normpath(os.path.join(
156 SCRIPT_DIR, '..', 'third_party', 'syzygy', 'binaries', 'exe'))
157 default_instrument_exe = os.path.join(default_asan_dir, 'instrument.exe')
158 default_runtime_path = os.path.join(default_asan_dir, 'asan_rtl.dll')
159
160 parser = optparse.OptionParser()
161 parser.add_option(
162 '--build-dir',
163 help='Path to the build directory to asan (required).')
164 parser.add_option(
165 '--target',
166 help='The target in the build directory to asan (required).')
167 parser.add_option(
168 '--jobs', type='int', default=multiprocessing.cpu_count(),
169 help='Specify the number of sub-tasks to use (%default).')
170 parser.add_option(
171 '--instrument_exe', default=default_instrument_exe,
172 help='Specify the path to the ASAN instrument.exe relative to '
173 'build-dir (%default).')
174 parser.add_option(
175 '--runtime_path', default=default_runtime_path,
176 help='Specify the path to the ASAN runtime DLL relative to '
177 'build-dir (%default).')
178 options, args = parser.parse_args()
179 options.build_dir = os.path.abspath(options.build_dir)
180
181 if not options.build_dir:
182 parser.error('Must specify --build-dir')
183 if not options.target:
184 parser.error('Must specify --target')
185 if args:
186 parser.error('Not expecting additional arguments')
187
188 options.full_directory = os.path.join(options.build_dir, options.target)
189 if not os.path.exists(options.full_directory):
190 parser.error('Could not find directory: %s' % options.full_directory)
191 options.instrument_exe = os.path.join(
192 options.build_dir, options.instrument_exe)
193 if not os.path.exists(options.instrument_exe):
194 parser.error('Could not find instrument_exe: %s' % options.instrument_exe)
195 options.runtime_path = os.path.join(
196 options.build_dir, options.runtime_path)
197 if not os.path.exists(options.runtime_path):
198 parser.error('Could not find runtime_path: %s' % options.runtime_path)
199
200 print 'Default BLACKLIST is: %r' % BLACKLIST
201
202 UpdateAsanRuntime(options.full_directory, options.runtime_path)
203 return ApplyAsanToBuild(
204 options.full_directory, options.instrument_exe, options.jobs)
205
206
207 if __name__ == '__main__':
208 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698