| #!/usr/bin/env python3 | |
| """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, | |
| ) |