| #!/usr/bin/env python |
| |
| from __future__ import print_function |
| |
| """Prepare a code coverage artifact. |
| |
| - Collate raw profiles into one indexed profile. |
| - Generate html reports for the given binaries. |
| |
| Caution: The positional arguments to this script must be specified before any |
| optional arguments, such as --restrict. |
| """ |
| |
| import argparse |
| import glob |
| import os |
| import subprocess |
| import sys |
| |
| |
| def merge_raw_profiles(host_llvm_profdata, profile_data_dir, preserve_profiles): |
| print(":: Merging raw profiles...", end="") |
| sys.stdout.flush() |
| raw_profiles = glob.glob(os.path.join(profile_data_dir, "*.profraw")) |
| manifest_path = os.path.join(profile_data_dir, "profiles.manifest") |
| profdata_path = os.path.join(profile_data_dir, "Coverage.profdata") |
| with open(manifest_path, "w") as manifest: |
| manifest.write("\n".join(raw_profiles)) |
| subprocess.check_call( |
| [ |
| host_llvm_profdata, |
| "merge", |
| "-sparse", |
| "-f", |
| manifest_path, |
| "-o", |
| profdata_path, |
| ] |
| ) |
| if not preserve_profiles: |
| for raw_profile in raw_profiles: |
| os.remove(raw_profile) |
| os.remove(manifest_path) |
| print("Done!") |
| return profdata_path |
| |
| |
| def prepare_html_report( |
| host_llvm_cov, profile, report_dir, binaries, restricted_dirs, compilation_dir |
| ): |
| print(":: Preparing html report for {0}...".format(binaries), end="") |
| sys.stdout.flush() |
| objects = [] |
| for i, binary in enumerate(binaries): |
| if i == 0: |
| objects.append(binary) |
| else: |
| objects.extend(("-object", binary)) |
| invocation = ( |
| [host_llvm_cov, "show"] |
| + objects |
| + [ |
| "-format", |
| "html", |
| "-instr-profile", |
| profile, |
| "-o", |
| report_dir, |
| "-show-line-counts-or-regions", |
| "-show-directory-coverage", |
| "-Xdemangler", |
| "c++filt", |
| "-Xdemangler", |
| "-n", |
| ] |
| + restricted_dirs |
| ) |
| if compilation_dir: |
| invocation += ["-compilation-dir=" + compilation_dir] |
| subprocess.check_call(invocation) |
| with open(os.path.join(report_dir, "summary.txt"), "wb") as Summary: |
| subprocess.check_call( |
| [host_llvm_cov, "report"] |
| + objects |
| + ["-instr-profile", profile] |
| + restricted_dirs, |
| stdout=Summary, |
| ) |
| print("Done!") |
| |
| |
| def prepare_html_reports( |
| host_llvm_cov, |
| profdata_path, |
| report_dir, |
| binaries, |
| unified_report, |
| restricted_dirs, |
| compilation_dir, |
| ): |
| if unified_report: |
| prepare_html_report( |
| host_llvm_cov, |
| profdata_path, |
| report_dir, |
| binaries, |
| restricted_dirs, |
| compilation_dir, |
| ) |
| else: |
| for binary in binaries: |
| binary_report_dir = os.path.join(report_dir, os.path.basename(binary)) |
| prepare_html_report( |
| host_llvm_cov, |
| profdata_path, |
| binary_report_dir, |
| [binary], |
| restricted_dirs, |
| compilation_dir, |
| ) |
| |
| |
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument("host_llvm_profdata", help="Path to llvm-profdata") |
| parser.add_argument("host_llvm_cov", help="Path to llvm-cov") |
| parser.add_argument( |
| "profile_data_dir", help="Path to the directory containing the raw profiles" |
| ) |
| parser.add_argument( |
| "report_dir", help="Path to the output directory for html reports" |
| ) |
| parser.add_argument( |
| "binaries", |
| metavar="B", |
| type=str, |
| nargs="*", |
| help="Path to an instrumented binary", |
| ) |
| parser.add_argument( |
| "--only-merge", |
| action="store_true", |
| help="Only merge raw profiles together, skip report " "generation", |
| ) |
| parser.add_argument( |
| "--preserve-profiles", help="Do not delete raw profiles", action="store_true" |
| ) |
| parser.add_argument( |
| "--use-existing-profdata", help="Specify an existing indexed profile to use" |
| ) |
| parser.add_argument( |
| "--unified-report", |
| action="store_true", |
| help="Emit a unified report for all binaries", |
| ) |
| parser.add_argument( |
| "--restrict", |
| metavar="R", |
| type=str, |
| nargs="*", |
| default=[], |
| help="Restrict the reporting to the given source paths" |
| " (must be specified after all other positional arguments)", |
| ) |
| parser.add_argument( |
| "-C", |
| "--compilation-dir", |
| type=str, |
| default="", |
| help="The compilation directory of the binary", |
| ) |
| args = parser.parse_args() |
| |
| if args.use_existing_profdata and args.only_merge: |
| print("--use-existing-profdata and --only-merge are incompatible") |
| exit(1) |
| |
| if args.use_existing_profdata: |
| profdata_path = args.use_existing_profdata |
| else: |
| profdata_path = merge_raw_profiles( |
| args.host_llvm_profdata, args.profile_data_dir, args.preserve_profiles |
| ) |
| |
| if not len(args.binaries): |
| print("No binaries specified, no work to do!") |
| exit(1) |
| |
| if not args.only_merge: |
| prepare_html_reports( |
| args.host_llvm_cov, |
| profdata_path, |
| args.report_dir, |
| args.binaries, |
| args.unified_report, |
| args.restrict, |
| args.compilation_dir, |
| ) |