Commit c9213a4f authored by Max Moroz's avatar Max Moroz Committed by Commit Bot

Coverage script: add "--filter" option for specifying sources to be shown in the report.

R=metzman@chromium.org, ochang@chromium.org

Bug: 759794
Change-Id: I5dff2a11d985a6fb1bf9a9b821edd000b6227fe4
Reviewed-on: https://chromium-review.googlesource.com/721634
Commit-Queue: Max Moroz <mmoroz@chromium.org>
Reviewed-by: default avatarJonathan Metzman <metzman@chromium.org>
Reviewed-by: default avatarOliver Chang <ochang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#509582}
parent d8605ed9
...@@ -44,6 +44,7 @@ ninja -C out/coverage -j100 pdfium_fuzzer ...@@ -44,6 +44,7 @@ ninja -C out/coverage -j100 pdfium_fuzzer
./testing/libfuzzer/coverage.py \\ ./testing/libfuzzer/coverage.py \\
--output="coverage_out" \\ --output="coverage_out" \\
--command="out/coverage/pdfium_fuzzer -runs=<runs> <corpus_dir>" --command="out/coverage/pdfium_fuzzer -runs=<runs> <corpus_dir>"
--filter third_party/pdfium/ pdf/
where: where:
<corpus_dir> - directory containing samples files for this format. <corpus_dir> - directory containing samples files for this format.
...@@ -97,12 +98,24 @@ ZERO_FUNCTION_FILE_TEXT = 'Files which contain no functions' ...@@ -97,12 +98,24 @@ ZERO_FUNCTION_FILE_TEXT = 'Files which contain no functions'
HTTP_PORT = 9000 HTTP_PORT = 9000
COVERAGE_REPORT_LINK = 'http://127.0.0.1:%d/report.html' % HTTP_PORT COVERAGE_REPORT_LINK = 'http://127.0.0.1:%d/report.html' % HTTP_PORT
LARGE_BINARY_THRESHOLD = 128 * 2 ** 20
def CheckBinaryAndArgs(executable_path, filters):
"""Verify that the given file has been built with coverage instrumentation,
also perform check for "--filter" argument and for the binary size."""
CheckFilterArgument(filters)
def CheckBuildInstrumentation(executable_path):
"""Verify that the given file has been built with coverage instrumentation."""
with open(executable_path) as file_handle: with open(executable_path) as file_handle:
data = file_handle.read() data = file_handle.read()
if len(data) > LARGE_BINARY_THRESHOLD and not filters:
logging.warning('The target binary is quite large. Generating the full '
'coverage report may take a while. To generate the report '
'faster, consider using the "--filter" argument to specify '
'the source code files and directories shown in the report.'
)
# For minimum threshold reference, tiny "Hello World" program has count of 34. # For minimum threshold reference, tiny "Hello World" program has count of 34.
if data.count('__llvm_profile') > 20: if data.count('__llvm_profile') > 20:
return return
...@@ -116,6 +129,14 @@ def CheckBuildInstrumentation(executable_path): ...@@ -116,6 +129,14 @@ def CheckBuildInstrumentation(executable_path):
sys.exit(-1) sys.exit(-1)
def CheckFilterArgument(filters):
"""Verify that all the paths specified in --filter arg exist."""
for path in filters:
if not os.path.exists(path):
logging.error('The path specified does not exist: %s.' % path)
sys.exit(-1)
def CreateOutputDir(dir_path): def CreateOutputDir(dir_path):
"""Create a directory for the script output files.""" """Create a directory for the script output files."""
if not os.path.exists(dir_path): if not os.path.exists(dir_path):
...@@ -160,7 +181,7 @@ def DownloadCoverageToolsIfNeeded(): ...@@ -160,7 +181,7 @@ def DownloadCoverageToolsIfNeeded():
if (coverage_revision == clang_revision and if (coverage_revision == clang_revision and
coverage_sub_revision == clang_sub_revision): coverage_sub_revision == clang_sub_revision):
# LLVM coverage tools are up to date, bail out. # LLVM coverage tools are up to date, bail out.
return return clang_revision
package_version = '%d-%d' % (clang_revision, clang_sub_revision) package_version = '%d-%d' % (clang_revision, clang_sub_revision)
coverage_tools_file = 'llvm-code-coverage-%s.tgz' % package_version coverage_tools_file = 'llvm-code-coverage-%s.tgz' % package_version
...@@ -186,6 +207,8 @@ def DownloadCoverageToolsIfNeeded(): ...@@ -186,6 +207,8 @@ def DownloadCoverageToolsIfNeeded():
raise Exception( raise Exception(
'Failed to download coverage tools: %s.' % coverage_tools_url) 'Failed to download coverage tools: %s.' % coverage_tools_url)
return clang_revision
def ExtractAndFixFilename(data, source_dir): def ExtractAndFixFilename(data, source_dir):
"""Extract full paths to source code files and replace with relative paths.""" """Extract full paths to source code files and replace with relative paths."""
...@@ -267,13 +290,17 @@ def GenerateReport(report_data): ...@@ -267,13 +290,17 @@ def GenerateReport(report_data):
return REPORT_TEMPLATE.format(table_data=table_data) return REPORT_TEMPLATE.format(table_data=table_data)
def GenerateSources(executable_path, output_dir, source_dir, coverage_file): def GenerateSources(executable_path, output_dir, source_dir, filters,
coverage_file):
"""Generate coverage visualization for source code files.""" """Generate coverage visualization for source code files."""
llvm_cov_command = [ llvm_cov_command = [
LLVM_COV_PATH, 'show', '-format=html', executable_path, LLVM_COV_PATH, 'show', '-format=html', executable_path,
'-instr-profile=%s' % coverage_file '-instr-profile=%s' % coverage_file
] ]
for path in filters:
llvm_cov_command.append(path)
data = subprocess.check_output(llvm_cov_command) data = subprocess.check_output(llvm_cov_command)
# Extract CSS style from the data. # Extract CSS style from the data.
...@@ -324,17 +351,27 @@ def GenerateSources(executable_path, output_dir, source_dir, coverage_file): ...@@ -324,17 +351,27 @@ def GenerateSources(executable_path, output_dir, source_dir, coverage_file):
file_handle.write(file_data) file_handle.write(file_data)
def GenerateSummary(executable_path, output_dir, coverage_file): def GenerateSummary(executable_path, output_dir, filters, coverage_file,
clang_revision):
"""Generate code coverage summary report (i.e. a table with all files).""" """Generate code coverage summary report (i.e. a table with all files)."""
llvm_cov_command = [ llvm_cov_command = [
LLVM_COV_PATH, 'report', executable_path, LLVM_COV_PATH, 'report', executable_path,
'-instr-profile=%s' % coverage_file '-instr-profile=%s' % coverage_file
] ]
for path in filters:
llvm_cov_command.append(path)
data = subprocess.check_output(llvm_cov_command) data = subprocess.check_output(llvm_cov_command)
report = GenerateReport(data) report = GenerateReport(data)
with open(os.path.join(output_dir, REPORT_FILENAME), 'w') as file_handle: with open(os.path.join(output_dir, REPORT_FILENAME), 'w') as file_handle:
# TODO(mmoroz): remove this hacky warning after next clang roll.
if filters and clang_revision < 315685:
report = ('Warning: the report below contains information for all the '
'sources even though you used "--filter" option. This bug has '
'been fixed upstream. It will be fixed in Chromium after next '
'clang roll (https://reviews.llvm.org/rL315685).<br>' + report)
file_handle.write(report) file_handle.write(report)
...@@ -421,6 +458,14 @@ def main(): ...@@ -421,6 +458,14 @@ def main():
'--output', '--output',
required=True, required=True,
help='Directory where code coverage files will be written to.') help='Directory where code coverage files will be written to.')
parser.add_argument(
'--filter',
required=False,
nargs='+',
default = [],
help='(Optional) Paths to source code files/directories shown in the '
'report. By default, the report shows all the sources compiled and '
'linked into the target executable.')
if not len(sys.argv[1:]): if not len(sys.argv[1:]):
# Print help when no arguments are provided on command line. # Print help when no arguments are provided on command line.
...@@ -431,9 +476,9 @@ def main(): ...@@ -431,9 +476,9 @@ def main():
executable_path = args.command.split()[0] executable_path = args.command.split()[0]
CheckBuildInstrumentation(executable_path) CheckBinaryAndArgs(executable_path, args.filter)
DownloadCoverageToolsIfNeeded() clang_revision = DownloadCoverageToolsIfNeeded()
CreateOutputDir(args.output) CreateOutputDir(args.output)
profile_file = os.path.join(args.output, LLVM_PROFILE_FILE_NAME) profile_file = os.path.join(args.output, LLVM_PROFILE_FILE_NAME)
...@@ -442,8 +487,10 @@ def main(): ...@@ -442,8 +487,10 @@ def main():
coverage_file = os.path.join(args.output, LLVM_COVERAGE_FILE_NAME) coverage_file = os.path.join(args.output, LLVM_COVERAGE_FILE_NAME)
ProcessCoverageDump(profile_file, coverage_file) ProcessCoverageDump(profile_file, coverage_file)
GenerateSummary(executable_path, args.output, coverage_file) GenerateSummary(executable_path, args.output, args.filter, coverage_file,
GenerateSources(executable_path, args.output, args.source, coverage_file) clang_revision)
GenerateSources(executable_path, args.output, args.source, args.filter,
coverage_file)
ServeReportOnHTTP(args.output) ServeReportOnHTTP(args.output)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment