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

Side by Side Diff: tools/sanitizers/sancov_merger.py

Issue 1782843002: [Coverage] Enable merging coverage data from swarming. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Implement swarming merge Created 4 years, 9 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 | « 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
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2016 the V8 project authors. All rights reserved. 2 # Copyright 2016 the V8 project authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Script for merging sancov files in parallel. 6 """Script for merging sancov files in parallel.
7 7
8 The sancov files are expected 8 When merging test runner output, the sancov files are expected
9 to be located in one directory with the file-name pattern: 9 to be located in one directory with the file-name pattern:
10 <executable name>.test.<id>.sancov 10 <executable name>.test.<id>.sancov
11 11
12 For each executable, this script writes a new file: 12 For each executable, this script writes a new file:
13 <executable name>.result.sancov 13 <executable name>.result.sancov
14 14
15 When --swarming-output-dir is specified, this script will merge the result
16 files found there into the coverage folder.
17
15 The sancov tool is expected to be in the llvm compiler-rt third-party 18 The sancov tool is expected to be in the llvm compiler-rt third-party
16 directory. It's not checked out by default and must be added as a custom deps: 19 directory. It's not checked out by default and must be added as a custom deps:
17 'v8/third_party/llvm/projects/compiler-rt': 20 'v8/third_party/llvm/projects/compiler-rt':
18 'https://chromium.googlesource.com/external/llvm.org/compiler-rt.git' 21 'https://chromium.googlesource.com/external/llvm.org/compiler-rt.git'
19 """ 22 """
20 23
21 import argparse 24 import argparse
22 import logging 25 import logging
23 import math 26 import math
24 import os 27 import os
(...skipping 15 matching lines...) Expand all
40 BASE_DIR, 'third_party', 'llvm', 'projects', 'compiler-rt', 43 BASE_DIR, 'third_party', 'llvm', 'projects', 'compiler-rt',
41 'lib', 'sanitizer_common', 'scripts', 'sancov.py') 44 'lib', 'sanitizer_common', 'scripts', 'sancov.py')
42 45
43 # Number of cpus. 46 # Number of cpus.
44 CPUS = cpu_count() 47 CPUS = cpu_count()
45 48
46 # Regexp to find sancov file as output by the v8 test runner. Also grabs the 49 # Regexp to find sancov file as output by the v8 test runner. Also grabs the
47 # executable name in group 1. 50 # executable name in group 1.
48 SANCOV_FILE_RE = re.compile(r'^(.*)\.test\.\d+\.sancov$') 51 SANCOV_FILE_RE = re.compile(r'^(.*)\.test\.\d+\.sancov$')
49 52
53 # Regexp to find sancov result files as returned from swarming.
54 SANCOV_RESULTS_FILE_RE = re.compile(r'^.*\.result\.sancov$')
55
50 56
51 def merge(args): 57 def merge(args):
52 """Merge several sancov files into one. 58 """Merge several sancov files into one.
53 59
54 Called trough multiprocessing pool. The args are expected to unpack to: 60 Called trough multiprocessing pool. The args are expected to unpack to:
55 keep: Option if source and intermediate sancov files should be kept. 61 keep: Option if source and intermediate sancov files should be kept.
56 coverage_dir: Folder where to find the sancov files. 62 coverage_dir: Folder where to find the sancov files.
57 executable: Name of the executable whose sancov files should be merged. 63 executable: Name of the executable whose sancov files should be merged.
58 index: A number to be put into the intermediate result file name. 64 index: A number to be put into the intermediate result file name.
59 If None, this is a final result. 65 If None, this is a final result.
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 buckets = [files[i:i+n] for i in xrange(0, len(files), n)] 109 buckets = [files[i:i+n] for i in xrange(0, len(files), n)]
104 110
105 # Inputs for multiprocessing. List of tuples containing: 111 # Inputs for multiprocessing. List of tuples containing:
106 # Keep-files option, base path, executable name, index of bucket, 112 # Keep-files option, base path, executable name, index of bucket,
107 # list of files. 113 # list of files.
108 inputs.extend([(keep, coverage_dir, executable, i, b) 114 inputs.extend([(keep, coverage_dir, executable, i, b)
109 for i, b in enumerate(buckets)]) 115 for i, b in enumerate(buckets)])
110 return inputs 116 return inputs
111 117
112 118
113 def merge_parallel(inputs): 119 def merge_parallel(inputs, merge_fun=merge):
Michael Hablich 2016/03/10 14:39:08 nit: I would simply call it merge_function ... I w
Michael Achenbach 2016/03/10 14:41:45 Laziness. fun is commonly used as abbreviation for
114 """Process several merge jobs in parallel.""" 120 """Process several merge jobs in parallel."""
115 pool = Pool(CPUS) 121 pool = Pool(CPUS)
116 try: 122 try:
117 return pool.map(merge, inputs) 123 return pool.map(merge_fun, inputs)
118 finally: 124 finally:
119 pool.close() 125 pool.close()
120 126
121 127
122 def main(): 128 def merge_test_runner_output(options):
123 parser = argparse.ArgumentParser()
124 parser.add_argument('--coverage-dir', required=True,
125 help='Path to the sancov output files.')
126 parser.add_argument('--keep', default=False, action='store_true',
127 help='Keep sancov output files after merging.')
128 options = parser.parse_args()
129
130 # Check if folder with coverage output exists.
131 assert (os.path.exists(options.coverage_dir) and
132 os.path.isdir(options.coverage_dir))
133
134 # Map executable names to their respective sancov files. 129 # Map executable names to their respective sancov files.
135 file_map = {} 130 file_map = {}
136 for f in os.listdir(options.coverage_dir): 131 for f in os.listdir(options.coverage_dir):
137 match = SANCOV_FILE_RE.match(f) 132 match = SANCOV_FILE_RE.match(f)
138 if match: 133 if match:
139 file_map.setdefault(match.group(1), []).append(f) 134 file_map.setdefault(match.group(1), []).append(f)
140 135
141 inputs = generate_inputs( 136 inputs = generate_inputs(
142 options.keep, options.coverage_dir, file_map, CPUS) 137 options.keep, options.coverage_dir, file_map, CPUS)
143 138
144 logging.info('Executing %d merge jobs in parallel for %d executables.' % 139 logging.info('Executing %d merge jobs in parallel for %d executables.' %
145 (len(inputs), len(file_map))) 140 (len(inputs), len(file_map)))
146 141
147 results = merge_parallel(inputs) 142 results = merge_parallel(inputs)
148 143
149 # Map executable names to intermediate bucket result files. 144 # Map executable names to intermediate bucket result files.
150 file_map = {} 145 file_map = {}
151 for executable, f in results: 146 for executable, f in results:
152 file_map.setdefault(executable, []).append(f) 147 file_map.setdefault(executable, []).append(f)
153 148
154 # Merge the bucket results for each executable. 149 # Merge the bucket results for each executable.
155 # The final result has index None, so no index will appear in the 150 # The final result has index None, so no index will appear in the
156 # file name. 151 # file name.
157 inputs = [(options.keep, options.coverage_dir, executable, None, files) 152 inputs = [(options.keep, options.coverage_dir, executable, None, files)
158 for executable, files in file_map.iteritems()] 153 for executable, files in file_map.iteritems()]
159 154
160 logging.info('Merging %d intermediate results.' % len(inputs)) 155 logging.info('Merging %d intermediate results.' % len(inputs))
161 156
162 merge_parallel(inputs) 157 merge_parallel(inputs)
158
159
160 def merge_two(args):
161 """Merge two sancov files.
162
163 Called trough multiprocessing pool. The args are expected to unpack to:
164 swarming_output_dir: Folder where to find the new file.
165 coverage_dir: Folder where to find the existing file.
166 f: File name of the file to be merged.
167 """
168 swarming_output_dir, coverage_dir, f = args
169 input_file = os.path.join(swarming_output_dir, f)
170 output_file = os.path.join(coverage_dir, f)
171 process = subprocess.Popen(
172 [SANCOV_TOOL, 'merge', input_file, output_file],
173 stdout=subprocess.PIPE,
174 stderr=subprocess.PIPE,
175 )
176 output, _ = process.communicate()
177 assert process.returncode == 0
178 with open(output_file, "wb") as f:
179 f.write(output)
180
181
182 def merge_swarming_output(options):
183 # Iterate sancov files from swarming.
184 files = []
185 for f in os.listdir(options.swarming_output_dir):
186 match = SANCOV_RESULTS_FILE_RE.match(f)
187 if match:
188 if os.path.exists(os.path.join(options.coverage_dir, f)):
189 # If the same file already exists, we'll merge the data.
190 files.append(f)
191 else:
192 # No file yet? Just move it.
193 os.rename(os.path.join(options.swarming_output_dir, f),
194 os.path.join(options.coverage_dir, f))
195
196 inputs = [(options.swarming_output_dir, options.coverage_dir, f)
197 for f in files]
198
199 logging.info('Executing %d merge jobs in parallel.' % len(inputs))
tandrii(chromium) 2016/03/10 15:40:06 nit: s/%/, because logging does formatting for you
Michael Achenbach 2016/03/10 16:12:33 Ah right, didn't pay attention. Will clean up all
200 merge_parallel(inputs, merge_two)
201
202
203 def main():
204 parser = argparse.ArgumentParser()
205 parser.add_argument('--coverage-dir', required=True,
206 help='Path to the sancov output files.')
207 parser.add_argument('--keep', default=False, action='store_true',
208 help='Keep sancov output files after merging.')
209 parser.add_argument('--swarming-output-dir',
210 help='Folder containing a results shard from swarming.')
211 options = parser.parse_args()
212
213 # Check if folder with coverage output exists.
214 assert (os.path.exists(options.coverage_dir) and
215 os.path.isdir(options.coverage_dir))
216
217 if options.swarming_output_dir:
218 # Check if folder with swarming output exists.
219 assert (os.path.exists(options.swarming_output_dir) and
220 os.path.isdir(options.swarming_output_dir))
221 merge_swarming_output(options)
222 else:
223 merge_test_runner_output(options)
224
163 return 0 225 return 0
164 226
165 227
166 if __name__ == '__main__': 228 if __name__ == '__main__':
167 sys.exit(main()) 229 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