blob: 4ad6e26b66a2cd6a311f636dac5a9da9660acb5a [file] [log] [blame]
Dirk Prankec93434f2017-10-03 00:45:551#!/usr/bin/env python
Max Moroz63d7ff92017-10-02 21:40:392#
3# Copyright 2017 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7from __future__ import print_function
8
9import argparse
10import logging
11import os
12import subprocess
13import sys
14
Abhishek Arya6eb0ebc2017-10-03 17:09:5615import SimpleHTTPServer
16import SocketServer
17
Abhishek Arya26bc519a2017-10-03 15:52:5918HELP_MESSAGE = """
Max Moroz63d7ff92017-10-02 21:40:3919This script helps to generate code coverage report. It uses Clang Source-based
20Code coverage (https://clang.llvm.org/docs/SourceBasedCodeCoverage.html).
21
22The output is a directory with HTML files that can be inspected via local web
23server (e.g. "python -m SimpleHTTPServer").
24
25In order to generate code coverage report, you need to build the target program
Jonathan Metzman301ce0272017-10-16 15:43:2826with "use_clang_coverage=true" GN flag. You should also explicitly use the flag
27"is_component_build=false" as explained at the end of this paragraph.
28use_component_build is not compatible with sanitizer flags: "is_asan",
29"is_msan", etc. It is also incompatible with "optimize_for_fuzzing" and with
30"is_component_build". Beware that if "is_debug" is true (it defaults to true),
31then "is_component_build" will be set to true unless specified false as an
32argument. So it is best to pass is_component_build=false when using
33"use_clang_coveage".
Abhishek Arya26bc519a2017-10-03 15:52:5934
Abhishek Arya8fe00152017-10-04 15:47:2435If you are building a fuzz target, you need to add "use_libfuzzer=true" GN flag
36as well.
37
38Sample workflow for a fuzz target (e.g. pdfium_fuzzer):
39
40cd <chromium_checkout_dir>/src
Jonathan Metzman301ce0272017-10-16 15:43:2841gn gen //out/coverage --args='use_clang_coverage=true use_libfuzzer=true \
42is_component_build=false'
Abhishek Arya8fe00152017-10-04 15:47:2443ninja -C out/coverage -j100 pdfium_fuzzer
44./testing/libfuzzer/coverage.py \\
45 --output="coverage_out" \\
46 --command="out/coverage/pdfium_fuzzer -runs=<runs> <corpus_dir>"
Max Morozc9213a42017-10-17 23:07:2547 --filter third_party/pdfium/ pdf/
Abhishek Arya8fe00152017-10-04 15:47:2448
49where:
50 <corpus_dir> - directory containing samples files for this format.
51 <runs> - number of times to fuzz target function. Should be 0 when you just
52 want to see the coverage on corpus and don't want to fuzz at all.
53Then, open http://localhost:9000/report.html to see coverage report.
Max Moroz63d7ff92017-10-02 21:40:3954
55For Googlers, there are examples available at go/chrome-code-coverage-examples.
56
Abhishek Arya8fe00152017-10-04 15:47:2457If you have any questions, please send an email to [email protected].
Abhishek Arya26bc519a2017-10-03 15:52:5958"""
Max Moroz63d7ff92017-10-02 21:40:3959
60HTML_FILE_EXTENSION = '.html'
61
Abhishek Arya26bc519a2017-10-03 15:52:5962CHROME_SRC_PATH = os.path.dirname(
63 os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
Max Moroz63d7ff92017-10-02 21:40:3964LLVM_BUILD_PATH = os.path.join(CHROME_SRC_PATH, 'third_party', 'llvm-build')
65LLVM_BIN_PATH = os.path.join(LLVM_BUILD_PATH, 'Release+Asserts', 'bin')
66LLVM_COV_PATH = os.path.join(LLVM_BIN_PATH, 'llvm-cov')
67LLVM_PROFDATA_PATH = os.path.join(LLVM_BIN_PATH, 'llvm-profdata')
68
69LLVM_PROFILE_FILE_NAME = 'coverage.profraw'
70LLVM_COVERAGE_FILE_NAME = 'coverage.profdata'
71
72REPORT_FILENAME = 'report.html'
73
Abhishek Arya26bc519a2017-10-03 15:52:5974REPORT_TEMPLATE = """<!DOCTYPE html>
Max Moroz63d7ff92017-10-02 21:40:3975<html>
Abhishek Arya2b30f1b82017-10-03 22:29:3776<head>
77<meta name='viewport' content='width=device-width,initial-scale=1'>
78<meta charset='UTF-8'>
79<link rel="stylesheet" type="text/css" href="/style.css">
80</head>
Max Moroz63d7ff92017-10-02 21:40:3981<body>
82{table_data}
Abhishek Arya26bc519a2017-10-03 15:52:5983</body></html>"""
Max Moroz63d7ff92017-10-02 21:40:3984
85SINGLE_FILE_START_MARKER = '<!doctype html><html>'
86SINGLE_FILE_END_MARKER = '</body></html>'
87
88SOURCE_FILENAME_START_MARKER = (
89 "<body><div class='centered'><table><div class='source-name-title'><pre>")
90SOURCE_FILENAME_END_MARKER = '</pre>'
91
92STYLE_START_MARKER = '<style>'
93STYLE_END_MARKER = '</style>'
94STYLE_FILENAME = 'style.css'
95
96ZERO_FUNCTION_FILE_TEXT = 'Files which contain no functions'
97
Abhishek Arya6eb0ebc2017-10-03 17:09:5698HTTP_PORT = 9000
99COVERAGE_REPORT_LINK = 'http://127.0.0.1:%d/report.html' % HTTP_PORT
100
Max Morozc9213a42017-10-17 23:07:25101LARGE_BINARY_THRESHOLD = 128 * 2 ** 20
Max Moroz63d7ff92017-10-02 21:40:39102
Max Morozc9213a42017-10-17 23:07:25103
104def CheckBinaryAndArgs(executable_path, filters):
105 """Verify that the given file has been built with coverage instrumentation,
106 also perform check for "--filter" argument and for the binary size."""
107 CheckFilterArgument(filters)
108
Max Moroz63d7ff92017-10-02 21:40:39109 with open(executable_path) as file_handle:
110 data = file_handle.read()
111
Max Morozc9213a42017-10-17 23:07:25112 if len(data) > LARGE_BINARY_THRESHOLD and not filters:
113 logging.warning('The target binary is quite large. Generating the full '
114 'coverage report may take a while. To generate the report '
115 'faster, consider using the "--filter" argument to specify '
116 'the source code files and directories shown in the report.'
117 )
118
Abhishek Arya8fe00152017-10-04 15:47:24119 # For minimum threshold reference, tiny "Hello World" program has count of 34.
Max Moroz63d7ff92017-10-02 21:40:39120 if data.count('__llvm_profile') > 20:
121 return
122
Abhishek Arya26bc519a2017-10-03 15:52:59123 logging.error('It looks like the target binary has been compiled without '
Max Moroz63d7ff92017-10-02 21:40:39124 'coverage instrumentation.')
125 print('Have you used use_clang_coverage=true flag in GN args? [y/N]')
126 answer = raw_input()
127 if not answer.lower().startswith('y'):
128 print('Exiting.')
129 sys.exit(-1)
130
131
Max Morozc9213a42017-10-17 23:07:25132def CheckFilterArgument(filters):
133 """Verify that all the paths specified in --filter arg exist."""
134 for path in filters:
135 if not os.path.exists(path):
136 logging.error('The path specified does not exist: %s.' % path)
137 sys.exit(-1)
138
139
Max Moroz63d7ff92017-10-02 21:40:39140def CreateOutputDir(dir_path):
141 """Create a directory for the script output files."""
142 if not os.path.exists(dir_path):
143 os.mkdir(dir_path)
144 return
145
146 if os.path.isdir(dir_path):
Abhishek Arya26bc519a2017-10-03 15:52:59147 logging.warning('%s already exists.', dir_path)
Max Moroz63d7ff92017-10-02 21:40:39148 return
149
Abhishek Arya26bc519a2017-10-03 15:52:59150 logging.error('%s exists and does not point to a directory.', dir_path)
Max Moroz63d7ff92017-10-02 21:40:39151 raise Exception('Invalid --output argument specified.')
152
153
154def DownloadCoverageToolsIfNeeded():
155 """Temporary solution to download llvm-profdata and llvm-cov tools."""
156 # TODO(mmoroz): remove this function once tools get included to Clang bundle:
157 # https://chromium-review.googlesource.com/c/chromium/src/+/688221
158 clang_script_path = os.path.join(CHROME_SRC_PATH, 'tools', 'clang', 'scripts')
159 sys.path.append(clang_script_path)
160 import update as clang_update
161 import urllib2
162
Abhishek Arya8fe00152017-10-04 15:47:24163 def _GetRevisionFromStampFile(file_path):
Max Moroz63d7ff92017-10-02 21:40:39164 """Read the build stamp file created by tools/clang/scripts/update.py."""
Abhishek Arya8fe00152017-10-04 15:47:24165 if not os.path.exists(file_path):
Max Moroz63d7ff92017-10-02 21:40:39166 return 0, 0
Abhishek Arya8fe00152017-10-04 15:47:24167
168 with open(file_path) as file_handle:
Max Moroz63d7ff92017-10-02 21:40:39169 revision_stamp_data = file_handle.readline().strip()
170 revision_stamp_data = revision_stamp_data.split('-')
171 return int(revision_stamp_data[0]), int(revision_stamp_data[1])
172
173 clang_revision, clang_sub_revision = _GetRevisionFromStampFile(
174 clang_update.STAMP_FILE)
175
Abhishek Arya26bc519a2017-10-03 15:52:59176 coverage_revision_stamp_file = os.path.join(
Max Moroz63d7ff92017-10-02 21:40:39177 os.path.dirname(clang_update.STAMP_FILE), 'cr_coverage_revision')
178 coverage_revision, coverage_sub_revision = _GetRevisionFromStampFile(
179 coverage_revision_stamp_file)
180
181 if (coverage_revision == clang_revision and
182 coverage_sub_revision == clang_sub_revision):
183 # LLVM coverage tools are up to date, bail out.
Max Morozc9213a42017-10-17 23:07:25184 return clang_revision
Max Moroz63d7ff92017-10-02 21:40:39185
186 package_version = '%d-%d' % (clang_revision, clang_sub_revision)
187 coverage_tools_file = 'llvm-code-coverage-%s.tgz' % package_version
188
189 # The code bellow follows the code from tools/clang/scripts/update.py.
190 if sys.platform == 'win32' or sys.platform == 'cygwin':
191 coverage_tools_url = clang_update.CDS_URL + '/Win/' + coverage_tools_file
192 elif sys.platform == 'darwin':
193 coverage_tools_url = clang_update.CDS_URL + '/Mac/' + coverage_tools_file
194 else:
195 assert sys.platform.startswith('linux')
196 coverage_tools_url = (
197 clang_update.CDS_URL + '/Linux_x64/' + coverage_tools_file)
198
199 try:
200 clang_update.DownloadAndUnpack(coverage_tools_url,
201 clang_update.LLVM_BUILD_DIR)
202 print('Coverage tools %s unpacked.' % package_version)
203 with open(coverage_revision_stamp_file, 'w') as file_handle:
204 file_handle.write(package_version)
205 file_handle.write('\n')
206 except urllib2.URLError:
207 raise Exception(
208 'Failed to download coverage tools: %s.' % coverage_tools_url)
209
Max Morozc9213a42017-10-17 23:07:25210 return clang_revision
211
Max Moroz63d7ff92017-10-02 21:40:39212
213def ExtractAndFixFilename(data, source_dir):
214 """Extract full paths to source code files and replace with relative paths."""
215 filename_start = data.find(SOURCE_FILENAME_START_MARKER)
216 if filename_start == -1:
217 logging.error('Failed to extract source code filename.')
218 raise Exception('Failed to process coverage dump.')
219
220 filename_start += len(SOURCE_FILENAME_START_MARKER)
221 filename_end = data[filename_start:].find(SOURCE_FILENAME_END_MARKER)
222 if filename_end == -1:
223 logging.error('Failed to extract source code filename.')
224 raise Exception('Failed to process coverage dump.')
225
226 filename_end += filename_start
227
Abhishek Arya26bc519a2017-10-03 15:52:59228 filename = data[filename_start:filename_end]
Max Moroz63d7ff92017-10-02 21:40:39229
230 source_dir = os.path.abspath(source_dir)
231
232 if not filename.startswith(source_dir):
233 logging.error('Invalid source code path ("%s") specified.\n'
Abhishek Arya26bc519a2017-10-03 15:52:59234 'Coverage dump refers to "%s".', source_dir, filename)
Max Moroz63d7ff92017-10-02 21:40:39235 raise Exception('Failed to process coverage dump.')
236
Abhishek Arya26bc519a2017-10-03 15:52:59237 filename = filename[len(source_dir):]
Max Moroz63d7ff92017-10-02 21:40:39238 filename = filename.lstrip('/\\')
239
240 # Replace the filename with the shorter version.
Abhishek Arya26bc519a2017-10-03 15:52:59241 data = data[:filename_start] + filename + data[filename_end:]
Max Moroz63d7ff92017-10-02 21:40:39242 return filename, data
243
244
245def GenerateReport(report_data):
246 """Build HTML page with the summary report and links to individual files."""
Abhishek Arya2b30f1b82017-10-03 22:29:37247 table_data = '<table class="centered">\n'
Max Moroz63d7ff92017-10-02 21:40:39248 report_lines = report_data.splitlines()
249
250 # Write header.
Abhishek Arya2b30f1b82017-10-03 22:29:37251 table_data += ' <tr class="source-name-title">\n'
Max Moroz63d7ff92017-10-02 21:40:39252 for column in report_lines[0].split(' '):
253 if not column:
254 continue
Abhishek Arya2b30f1b82017-10-03 22:29:37255 table_data += ' <th><pre>%s</pre></th>\n' % column
Max Moroz63d7ff92017-10-02 21:40:39256 table_data += ' </tr>\n'
257
258 for line in report_lines[1:-1]:
259 if not line or line.startswith('---'):
260 continue
261
262 if line.startswith(ZERO_FUNCTION_FILE_TEXT):
Abhishek Arya2b30f1b82017-10-03 22:29:37263 table_data += ' <tr class="source-name-title">\n'
Abhishek Arya8fe00152017-10-04 15:47:24264 table_data += (
265 ' <th class="column-entry-left"><pre>%s</pre></th>\n' % line)
Max Moroz63d7ff92017-10-02 21:40:39266 table_data += ' </tr>\n'
267 continue
268
269 table_data += ' <tr>\n'
270
271 columns = line.split()
272
273 # First column is a file name, build a link.
Abhishek Arya2b30f1b82017-10-03 22:29:37274 table_data += (' <td class="column-entry-left">\n'
275 ' <a href="/%s"><pre>%s</pre></a>\n'
Abhishek Arya8fe00152017-10-04 15:47:24276 ' </td>\n') % (columns[0] + HTML_FILE_EXTENSION,
277 columns[0])
Max Moroz63d7ff92017-10-02 21:40:39278
Abhishek Arya8fe00152017-10-04 15:47:24279 for column in columns[1:]:
Abhishek Arya2b30f1b82017-10-03 22:29:37280 table_data += ' <td class="column-entry"><pre>%s</pre></td>\n' % column
Max Moroz63d7ff92017-10-02 21:40:39281 table_data += ' </tr>\n'
282
283 # Write the last "TOTAL" row.
Abhishek Arya2b30f1b82017-10-03 22:29:37284 table_data += ' <tr class="source-name-title">\n'
Max Moroz63d7ff92017-10-02 21:40:39285 for column in report_lines[-1].split():
Abhishek Arya2b30f1b82017-10-03 22:29:37286 table_data += ' <td class="column-entry"><pre>%s</pre></td>\n' % column
Max Moroz63d7ff92017-10-02 21:40:39287 table_data += ' </tr>\n'
288 table_data += '</table>\n'
289
290 return REPORT_TEMPLATE.format(table_data=table_data)
291
292
Max Morozc9213a42017-10-17 23:07:25293def GenerateSources(executable_path, output_dir, source_dir, filters,
294 coverage_file):
Max Moroz63d7ff92017-10-02 21:40:39295 """Generate coverage visualization for source code files."""
Abhishek Arya26bc519a2017-10-03 15:52:59296 llvm_cov_command = [
297 LLVM_COV_PATH, 'show', '-format=html', executable_path,
298 '-instr-profile=%s' % coverage_file
299 ]
Max Moroz63d7ff92017-10-02 21:40:39300
Max Morozc9213a42017-10-17 23:07:25301 for path in filters:
302 llvm_cov_command.append(path)
303
Max Moroz63d7ff92017-10-02 21:40:39304 data = subprocess.check_output(llvm_cov_command)
305
306 # Extract CSS style from the data.
307 style_start = data.find(STYLE_START_MARKER)
308 style_end = data.find(STYLE_END_MARKER)
309 if style_end <= style_start or style_start == -1:
310 logging.error('Failed to extract CSS style from coverage report.')
311 raise Exception('Failed to process coverage dump.')
312
Abhishek Arya26bc519a2017-10-03 15:52:59313 style_data = data[style_start + len(STYLE_START_MARKER):style_end]
Abhishek Arya2b30f1b82017-10-03 22:29:37314
315 # Add hover for table <tr>.
Abhishek Arya8fe00152017-10-04 15:47:24316 style_data += '\ntr:hover { background-color: #eee; }'
Abhishek Arya2b30f1b82017-10-03 22:29:37317
Max Moroz63d7ff92017-10-02 21:40:39318 with open(os.path.join(output_dir, STYLE_FILENAME), 'w') as file_handle:
319 file_handle.write(style_data)
320 style_length = (
321 len(style_data) + len(STYLE_START_MARKER) + len(STYLE_END_MARKER))
322
Abhishek Arya8fe00152017-10-04 15:47:24323 # Extract every source code file. Use "offset" to avoid creating new strings.
Max Moroz63d7ff92017-10-02 21:40:39324 offset = 0
325 while True:
326 file_start = data.find(SINGLE_FILE_START_MARKER, offset)
327 if file_start == -1:
328 break
329
330 file_end = data.find(SINGLE_FILE_END_MARKER, offset)
331 if file_end == -1:
332 break
333
334 file_end += len(SINGLE_FILE_END_MARKER)
335 offset += file_end - file_start
336
337 # Remove <style> as it's always the same and has been extracted separately.
Abhishek Arya26bc519a2017-10-03 15:52:59338 file_data = ReplaceStyleWithCss(data[file_start:file_end], style_length,
Max Moroz63d7ff92017-10-02 21:40:39339 STYLE_FILENAME)
340
341 filename, file_data = ExtractAndFixFilename(file_data, source_dir)
Abhishek Arya8fe00152017-10-04 15:47:24342 file_path = os.path.join(output_dir, filename)
343 dirname = os.path.dirname(file_path)
Max Moroz63d7ff92017-10-02 21:40:39344
345 try:
346 os.makedirs(dirname)
347 except OSError:
348 pass
349
Abhishek Arya8fe00152017-10-04 15:47:24350 with open(file_path + HTML_FILE_EXTENSION, 'w') as file_handle:
Max Moroz63d7ff92017-10-02 21:40:39351 file_handle.write(file_data)
352
353
Max Morozc9213a42017-10-17 23:07:25354def GenerateSummary(executable_path, output_dir, filters, coverage_file,
355 clang_revision):
Max Moroz63d7ff92017-10-02 21:40:39356 """Generate code coverage summary report (i.e. a table with all files)."""
Abhishek Arya26bc519a2017-10-03 15:52:59357 llvm_cov_command = [
358 LLVM_COV_PATH, 'report', executable_path,
359 '-instr-profile=%s' % coverage_file
360 ]
Max Moroz63d7ff92017-10-02 21:40:39361
Max Morozc9213a42017-10-17 23:07:25362 for path in filters:
363 llvm_cov_command.append(path)
364
Max Moroz63d7ff92017-10-02 21:40:39365 data = subprocess.check_output(llvm_cov_command)
366 report = GenerateReport(data)
367
368 with open(os.path.join(output_dir, REPORT_FILENAME), 'w') as file_handle:
Max Morozc9213a42017-10-17 23:07:25369 # TODO(mmoroz): remove this hacky warning after next clang roll.
370 if filters and clang_revision < 315685:
371 report = ('Warning: the report below contains information for all the '
372 'sources even though you used "--filter" option. This bug has '
373 'been fixed upstream. It will be fixed in Chromium after next '
374 'clang roll (https://reviews.llvm.org/rL315685).<br>' + report)
Max Moroz63d7ff92017-10-02 21:40:39375 file_handle.write(report)
376
377
Abhishek Arya6eb0ebc2017-10-03 17:09:56378def ServeReportOnHTTP(output_directory):
379 """Serve report directory on HTTP."""
380 os.chdir(output_directory)
381
382 SocketServer.TCPServer.allow_reuse_address = True
383 httpd = SocketServer.TCPServer(('', HTTP_PORT),
384 SimpleHTTPServer.SimpleHTTPRequestHandler)
385 print('Load coverage report using %s. Press Ctrl+C to exit.' %
386 COVERAGE_REPORT_LINK)
387
388 try:
389 httpd.serve_forever()
390 except KeyboardInterrupt:
391 httpd.server_close()
392
393
Max Moroz63d7ff92017-10-02 21:40:39394def ProcessCoverageDump(profile_file, coverage_file):
395 """Process and convert raw LLVM profile data into coverage data format."""
396 print('Processing coverage dump and generating visualization.')
Abhishek Arya26bc519a2017-10-03 15:52:59397 merge_command = [
398 LLVM_PROFDATA_PATH, 'merge', '-sparse', profile_file, '-o', coverage_file
399 ]
Max Moroz63d7ff92017-10-02 21:40:39400 data = subprocess.check_output(merge_command)
401
402 if not os.path.exists(coverage_file) or not os.path.getsize(coverage_file):
Abhishek Arya26bc519a2017-10-03 15:52:59403 logging.error('%s is either not created or empty:\n%s', coverage_file, data)
Max Moroz63d7ff92017-10-02 21:40:39404 raise Exception('Failed to merge coverage information after command run.')
405
406
407def ReplaceStyleWithCss(data, style_data_length, css_file_path):
408 """Replace <style></style> data with the include of common style.css file."""
409 style_start = data.find(STYLE_START_MARKER)
410 # Since "style" data is always the same, try some optimization here.
411 style_end = style_start + style_data_length
412 if (style_end > len(data) or
Abhishek Arya26bc519a2017-10-03 15:52:59413 data[style_end - len(STYLE_END_MARKER):style_end] != STYLE_END_MARKER):
Max Moroz63d7ff92017-10-02 21:40:39414 # Looks like our optimization has failed, find end of "style" data.
415 style_end = data.find(STYLE_END_MARKER)
416 if style_end <= style_start or style_start == -1:
417 logging.error('Failed to extract CSS style from coverage report.')
418 raise Exception('Failed to process coverage dump.')
419 style_end += len(STYLE_END_MARKER)
420
421 css_include = (
422 '<link rel="stylesheet" type="text/css" href="/%s">' % css_file_path)
Abhishek Arya26bc519a2017-10-03 15:52:59423 result = '\n'.join([data[:style_start], css_include, data[style_end:]])
Max Moroz63d7ff92017-10-02 21:40:39424 return result
425
426
427def RunCommand(command, profile_file):
428 """Run the given command in order to generate raw LLVM profile data."""
429 print('Running "%s".' % command)
430 print('-' * 80)
431 os.environ['LLVM_PROFILE_FILE'] = profile_file
432 os.system(command)
433 print('-' * 80)
434 print('Finished command execution.')
435
436 if not os.path.exists(profile_file) or not os.path.getsize(profile_file):
Abhishek Arya26bc519a2017-10-03 15:52:59437 logging.error('%s is either not created or empty.', profile_file)
Max Moroz63d7ff92017-10-02 21:40:39438 raise Exception('Failed to dump coverage information during command run.')
439
440
441def main():
442 """The main routing for processing the arguments and generating coverage."""
443 parser = argparse.ArgumentParser(
444 description=HELP_MESSAGE,
445 formatter_class=argparse.RawDescriptionHelpFormatter)
Abhishek Arya26bc519a2017-10-03 15:52:59446 parser.add_argument(
447 '--command',
448 required=True,
Abhishek Arya8fe00152017-10-04 15:47:24449 help='The command to run target binary for which code coverage is '
450 'required.')
Abhishek Arya26bc519a2017-10-03 15:52:59451 parser.add_argument(
452 '--source',
453 required=False,
454 default=CHROME_SRC_PATH,
Abhishek Arya8fe00152017-10-04 15:47:24455 help='Location of chromium source checkout, if it differs from '
456 'current checkout: %s.' % CHROME_SRC_PATH)
Abhishek Arya26bc519a2017-10-03 15:52:59457 parser.add_argument(
458 '--output',
459 required=True,
Abhishek Arya8fe00152017-10-04 15:47:24460 help='Directory where code coverage files will be written to.')
Max Morozc9213a42017-10-17 23:07:25461 parser.add_argument(
462 '--filter',
463 required=False,
464 nargs='+',
465 default = [],
466 help='(Optional) Paths to source code files/directories shown in the '
467 'report. By default, the report shows all the sources compiled and '
468 'linked into the target executable.')
Abhishek Arya8fe00152017-10-04 15:47:24469
470 if not len(sys.argv[1:]):
471 # Print help when no arguments are provided on command line.
472 parser.print_help()
473 parser.exit()
Max Moroz63d7ff92017-10-02 21:40:39474
475 args = parser.parse_args()
476
477 executable_path = args.command.split()[0]
Abhishek Arya6eb0ebc2017-10-03 17:09:56478
Max Morozc9213a42017-10-17 23:07:25479 CheckBinaryAndArgs(executable_path, args.filter)
Max Moroz63d7ff92017-10-02 21:40:39480
Max Morozc9213a42017-10-17 23:07:25481 clang_revision = DownloadCoverageToolsIfNeeded()
Max Moroz63d7ff92017-10-02 21:40:39482
483 CreateOutputDir(args.output)
484 profile_file = os.path.join(args.output, LLVM_PROFILE_FILE_NAME)
485 RunCommand(args.command, profile_file)
486
487 coverage_file = os.path.join(args.output, LLVM_COVERAGE_FILE_NAME)
488 ProcessCoverageDump(profile_file, coverage_file)
489
Max Morozc9213a42017-10-17 23:07:25490 GenerateSummary(executable_path, args.output, args.filter, coverage_file,
491 clang_revision)
492 GenerateSources(executable_path, args.output, args.source, args.filter,
493 coverage_file)
Max Moroz63d7ff92017-10-02 21:40:39494
Abhishek Arya6eb0ebc2017-10-03 17:09:56495 ServeReportOnHTTP(args.output)
496
497 print('Done.')
Max Moroz63d7ff92017-10-02 21:40:39498
499
Abhishek Arya26bc519a2017-10-03 15:52:59500if __name__ == '__main__':
Max Moroz63d7ff92017-10-02 21:40:39501 main()