#!/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", "-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, )