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

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

Issue 11379003: Add Windows ASAN bots. (Closed) Base URL: http://git.chromium.org/chromium/tools/build.git@neuter
Patch Set: Cleanup some unnecessary pieces Created 8 years, 1 month 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
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 'sql.dll',
M-A Ruel 2012/11/21 09:06:32 Keep ordered
iannucci 2012/11/21 18:27:42 Done.
17 'gpu.dll',
18 'crnss.dll',
19 'icuuc.dll'
M-A Ruel 2012/11/21 09:06:32 Comma on all items.
iannucci 2012/11/21 18:27:42 Done.
20 ))
21
22
23 ### Multiprocessing functions
24 OPTIONS = None
25 STOPPED = None
26 def _InitializeASANitizer(options, stopped):
27 global OPTIONS, STOPPED
28 OPTIONS = options
29 STOPPED = stopped
30
31
32 def _ASANitize(job):
33 retval = 0
34 stdout = ''
35 pe_image, pdb = job
36
37 try:
38 if not STOPPED.is_set():
39 out_pe = AddExtensionComponent(pe_image, 'asan')
40 out_pdb = AddExtensionComponent(pdb, 'asan')
41
42 # Note that instrument.exe requires --foo=bar format (including the '=')
43 command = [OPTIONS.instrument_exe, '--mode=ASAN',
44 '--input-image=%s' % pe_image,
45 '--output-image=%s' % out_pe,
46 '--output-pdb=%s' % out_pdb,
47 '2>&1' # Combine stderr+stdout so that they're in order
48 ]
49
50 for fname in filter(os.path.exists, (out_pe, out_pdb)):
51 os.remove(fname)
52
53 proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
M-A Ruel 2012/11/21 09:06:32 Creating a process just to start a process is a ta
iannucci 2012/11/21 18:27:42 Yeah I started down that path, but I felt that mul
54 stdout, _ = proc.communicate()
55 retval = proc.returncode
56
57 return (retval, stdout, pe_image)
58 except Exception:
59 import traceback
60 return (1, stdout+'\n'+traceback.format_exc(), pe_image)
61
62
63 ### Normal functions
64
65 def AddExtensionComponent(path, new_ext):
66 """
67 Prepends new_ext to the existing extension
68 >>> ChangeExtension('hello.foo.dll', 'asan')
69 'hello.asan.foo.dll'
70 """
71 # Don't use os.path.splitext, because it will split on the rightmost dot
72 # instead of the leftmost dot.
73 base, ext = path.split('.', 1)
74 return base+'.'+new_ext+'.'+ext
75
76
77 def UpdateAsanRuntime(options):
78 """Updates the ASAN runtime dll in the build directory, if it exists."""
79 runtime = os.path.join(options.full_directory,
80 os.path.basename(options.runtime_path))
81
82 if os.path.exists(runtime):
83 print('Removing', runtime)
84 os.remove(runtime)
85
86 print 'Copying %s -> %s' % (options.runtime_path, runtime)
87 shutil.copy2(options.runtime_path, runtime)
88
89 fname = os.path.basename(options.runtime_path)
90 print 'Blacklisting %s' % fname
91 BLACKLIST.add(fname)
92
93
94 def GetCompatiblePDB(pe_image):
95 """Returns <path to pdb> or None (if no good pdb exists)."""
96 # TODO(iannucci): Use PE header to look up pdb name.
97 # for now, assume that the pdb is always just PE.pdb
98 pdb_path = pe_image+'.pdb'
99 return pdb_path if os.path.exists(pdb_path) else None
100
101
102 def FindFilesToAsan(directory):
103 """Finds eligible PE images in given directory. A PE image is eligible if it
104 has a corresponding pdb and doesn't already have ASAN applied to it. Skips
105 files which have an extra extension (like foo.orig.exe)."""
106 ret = []
107
108 def GoodExeOrDll(fname):
109 return (
110 '.' in fname and
111 fname not in BLACKLIST and
112 fname.split('.', 1)[-1] in ('exe', 'dll'))
113
114 for root, _, files in os.walk(directory):
115 for pe_image in (os.path.join(root, f) for f in files if GoodExeOrDll(f)):
116 pdb = GetCompatiblePDB(pe_image)
117 if not pdb:
118 print >> sys.stderr, 'PDB for "%s" does not exist.' % pe_image
119 continue
120
121 ret.append((pe_image, pdb))
122 return ret
123
124
125 def ApplyAsanToBuild(options):
126 """Applies ASAN to all exe's/dll's in the build directory."""
127 to_asan = FindFilesToAsan(options.full_directory)
128
129 if not to_asan:
130 print >> sys.stderr, 'No files to ASAN!'
131 return 1
132
133 stopped = multiprocessing.Event()
134 pool = multiprocessing.Pool(options.jobs, initializer=_InitializeASANitizer,
135 initargs=(options, stopped))
M-A Ruel 2012/11/21 09:06:32 Is this code I/O bound or CPU bound? I'd guess I/O
iannucci 2012/11/21 18:27:42 That's why jobs is tweakable.
136
137 ret = 0
138 try:
139 generator = pool.imap_unordered(_ASANitize, to_asan)
140 for retval, stdout, failed_image in generator:
141 ostream = (sys.stderr if retval else sys.stdout)
142 print >> ostream, stdout
143 sys.stdout.flush()
144 sys.stderr.flush()
145 if retval:
146 print 'Failed to ASAN %s. Stopping remaining jobs.' % failed_image
147 ret = retval
148 stopped.set()
149 except KeyboardInterrupt:
150 stopped.set()
151 pool.close()
152 pool.join()
153
154 return ret
155
156
157 def main():
158 default_asan_dir = os.path.join(os.pardir,
159 'third_party', 'syzygy', 'binaries', 'exe')
160 default_instrument_exe = os.path.join(default_asan_dir, 'instrument.exe')
161 default_runtime_path = os.path.join(default_asan_dir, 'asan_rtl.dll')
162
163 parser = optparse.OptionParser()
164 parser.add_option('--build_directory',
M-A Ruel 2012/11/21 09:06:32 alignment is wrong, either all +4 or not.
iannucci 2012/11/21 18:27:42 Done.
165 help='Path to the build directory to asan (required).')
166 parser.add_option('--target',
167 help='The target in the build directory to asan (required).')
168 parser.add_option('--jobs', type='int', default=multiprocessing.cpu_count(),
169 help='Specify the number of sub-tasks to use (%default).')
170 parser.add_option('--instrument_exe', default=default_instrument_exe,
171 help='Specify the path to the ASAN instrument.exe relative to '
172 'build_directory (%default).')
173 parser.add_option('--runtime_path', default=default_runtime_path,
174 help='Specify the path to the ASAN runtime DLL relative to '
175 'build_directory (%default).')
176 options, args = parser.parse_args()
177
178 if options.build_directory is None:
M-A Ruel 2012/11/21 09:06:32 if not options.build_directory:
iannucci 2012/11/21 18:27:42 Done.
179 parser.error('Must specify --build_directory')
180
181 if options.target is None:
182 parser.error('Must specify --target')
183
184 options.full_directory = os.path.join(options.build_directory, options.target)
185 if not os.path.exists(options.full_directory):
186 parser.error('Could not find directory: %s' % options.full_directory)
187
188 options.instrument_exe = os.path.abspath(
189 os.path.join(options.build_directory, options.instrument_exe))
190 if not os.path.exists(options.instrument_exe):
191 parser.error('Could not find instrument_exe: %s' % options.instrument_exe)
192
193 options.runtime_path = os.path.abspath(
194 os.path.join(options.build_directory, options.runtime_path))
195 if not os.path.exists(options.runtime_path):
196 parser.error('Could not find runtime_path: %s' % options.runtime_path)
197
198 if args:
199 parser.error('Not expecting additional arguments')
200
201 print 'Default BLACKLIST is: %r' % BLACKLIST
202
203 UpdateAsanRuntime(options)
204 return ApplyAsanToBuild(options)
205
M-A Ruel 2012/11/21 09:06:32 2 lines
iannucci 2012/11/21 18:27:42 Done.
206 if __name__ == '__main__':
207 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698