1#!/usr/bin/env python 2 3from __future__ import print_function 4 5"""Prepare a code coverage artifact. 6 7- Collate raw profiles into one indexed profile. 8- Generate html reports for the given binaries. 9 10Caution: The positional arguments to this script must be specified before any 11optional arguments, such as --restrict. 12""" 13 14import argparse 15import glob 16import os 17import subprocess 18import sys 19 20 21def merge_raw_profiles(host_llvm_profdata, profile_data_dir, preserve_profiles): 22 print(":: Merging raw profiles...", end="") 23 sys.stdout.flush() 24 raw_profiles = glob.glob(os.path.join(profile_data_dir, "*.profraw")) 25 manifest_path = os.path.join(profile_data_dir, "profiles.manifest") 26 profdata_path = os.path.join(profile_data_dir, "Coverage.profdata") 27 with open(manifest_path, "w") as manifest: 28 manifest.write("\n".join(raw_profiles)) 29 subprocess.check_call( 30 [ 31 host_llvm_profdata, 32 "merge", 33 "-sparse", 34 "-f", 35 manifest_path, 36 "-o", 37 profdata_path, 38 ] 39 ) 40 if not preserve_profiles: 41 for raw_profile in raw_profiles: 42 os.remove(raw_profile) 43 os.remove(manifest_path) 44 print("Done!") 45 return profdata_path 46 47 48def prepare_html_report( 49 host_llvm_cov, profile, report_dir, binaries, restricted_dirs, compilation_dir 50): 51 print(":: Preparing html report for {0}...".format(binaries), end="") 52 sys.stdout.flush() 53 objects = [] 54 for i, binary in enumerate(binaries): 55 if i == 0: 56 objects.append(binary) 57 else: 58 objects.extend(("-object", binary)) 59 invocation = ( 60 [host_llvm_cov, "show"] 61 + objects 62 + [ 63 "-format", 64 "html", 65 "-instr-profile", 66 profile, 67 "-o", 68 report_dir, 69 "-show-line-counts-or-regions", 70 "-show-directory-coverage", 71 "-Xdemangler", 72 "c++filt", 73 "-Xdemangler", 74 "-n", 75 ] 76 + restricted_dirs 77 ) 78 if compilation_dir: 79 invocation += ["-compilation-dir=" + compilation_dir] 80 subprocess.check_call(invocation) 81 with open(os.path.join(report_dir, "summary.txt"), "wb") as Summary: 82 subprocess.check_call( 83 [host_llvm_cov, "report"] 84 + objects 85 + ["-instr-profile", profile] 86 + restricted_dirs, 87 stdout=Summary, 88 ) 89 print("Done!") 90 91 92def prepare_html_reports( 93 host_llvm_cov, 94 profdata_path, 95 report_dir, 96 binaries, 97 unified_report, 98 restricted_dirs, 99 compilation_dir, 100): 101 if unified_report: 102 prepare_html_report( 103 host_llvm_cov, 104 profdata_path, 105 report_dir, 106 binaries, 107 restricted_dirs, 108 compilation_dir, 109 ) 110 else: 111 for binary in binaries: 112 binary_report_dir = os.path.join(report_dir, os.path.basename(binary)) 113 prepare_html_report( 114 host_llvm_cov, 115 profdata_path, 116 binary_report_dir, 117 [binary], 118 restricted_dirs, 119 compilation_dir, 120 ) 121 122 123if __name__ == "__main__": 124 parser = argparse.ArgumentParser(description=__doc__) 125 parser.add_argument("host_llvm_profdata", help="Path to llvm-profdata") 126 parser.add_argument("host_llvm_cov", help="Path to llvm-cov") 127 parser.add_argument( 128 "profile_data_dir", help="Path to the directory containing the raw profiles" 129 ) 130 parser.add_argument( 131 "report_dir", help="Path to the output directory for html reports" 132 ) 133 parser.add_argument( 134 "binaries", 135 metavar="B", 136 type=str, 137 nargs="*", 138 help="Path to an instrumented binary", 139 ) 140 parser.add_argument( 141 "--only-merge", 142 action="store_true", 143 help="Only merge raw profiles together, skip report " "generation", 144 ) 145 parser.add_argument( 146 "--preserve-profiles", help="Do not delete raw profiles", action="store_true" 147 ) 148 parser.add_argument( 149 "--use-existing-profdata", help="Specify an existing indexed profile to use" 150 ) 151 parser.add_argument( 152 "--unified-report", 153 action="store_true", 154 help="Emit a unified report for all binaries", 155 ) 156 parser.add_argument( 157 "--restrict", 158 metavar="R", 159 type=str, 160 nargs="*", 161 default=[], 162 help="Restrict the reporting to the given source paths" 163 " (must be specified after all other positional arguments)", 164 ) 165 parser.add_argument( 166 "-C", 167 "--compilation-dir", 168 type=str, 169 default="", 170 help="The compilation directory of the binary", 171 ) 172 args = parser.parse_args() 173 174 if args.use_existing_profdata and args.only_merge: 175 print("--use-existing-profdata and --only-merge are incompatible") 176 exit(1) 177 178 if args.use_existing_profdata: 179 profdata_path = args.use_existing_profdata 180 else: 181 profdata_path = merge_raw_profiles( 182 args.host_llvm_profdata, args.profile_data_dir, args.preserve_profiles 183 ) 184 185 if not len(args.binaries): 186 print("No binaries specified, no work to do!") 187 exit(1) 188 189 if not args.only_merge: 190 prepare_html_reports( 191 args.host_llvm_cov, 192 profdata_path, 193 args.report_dir, 194 args.binaries, 195 args.unified_report, 196 args.restrict, 197 args.compilation_dir, 198 ) 199