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 "-Xdemangler", 71 "c++filt", 72 "-Xdemangler", 73 "-n", 74 ] 75 + restricted_dirs 76 ) 77 if compilation_dir: 78 invocation += ["-compilation-dir=" + compilation_dir] 79 subprocess.check_call(invocation) 80 with open(os.path.join(report_dir, "summary.txt"), "wb") as Summary: 81 subprocess.check_call( 82 [host_llvm_cov, "report"] 83 + objects 84 + ["-instr-profile", profile] 85 + restricted_dirs, 86 stdout=Summary, 87 ) 88 print("Done!") 89 90 91def prepare_html_reports( 92 host_llvm_cov, 93 profdata_path, 94 report_dir, 95 binaries, 96 unified_report, 97 restricted_dirs, 98 compilation_dir, 99): 100 if unified_report: 101 prepare_html_report( 102 host_llvm_cov, 103 profdata_path, 104 report_dir, 105 binaries, 106 restricted_dirs, 107 compilation_dir, 108 ) 109 else: 110 for binary in binaries: 111 binary_report_dir = os.path.join(report_dir, os.path.basename(binary)) 112 prepare_html_report( 113 host_llvm_cov, 114 profdata_path, 115 binary_report_dir, 116 [binary], 117 restricted_dirs, 118 compilation_dir, 119 ) 120 121 122if __name__ == "__main__": 123 parser = argparse.ArgumentParser(description=__doc__) 124 parser.add_argument("host_llvm_profdata", help="Path to llvm-profdata") 125 parser.add_argument("host_llvm_cov", help="Path to llvm-cov") 126 parser.add_argument( 127 "profile_data_dir", help="Path to the directory containing the raw profiles" 128 ) 129 parser.add_argument( 130 "report_dir", help="Path to the output directory for html reports" 131 ) 132 parser.add_argument( 133 "binaries", 134 metavar="B", 135 type=str, 136 nargs="*", 137 help="Path to an instrumented binary", 138 ) 139 parser.add_argument( 140 "--only-merge", 141 action="store_true", 142 help="Only merge raw profiles together, skip report " "generation", 143 ) 144 parser.add_argument( 145 "--preserve-profiles", help="Do not delete raw profiles", action="store_true" 146 ) 147 parser.add_argument( 148 "--use-existing-profdata", help="Specify an existing indexed profile to use" 149 ) 150 parser.add_argument( 151 "--unified-report", 152 action="store_true", 153 help="Emit a unified report for all binaries", 154 ) 155 parser.add_argument( 156 "--restrict", 157 metavar="R", 158 type=str, 159 nargs="*", 160 default=[], 161 help="Restrict the reporting to the given source paths" 162 " (must be specified after all other positional arguments)", 163 ) 164 parser.add_argument( 165 "-C", 166 "--compilation-dir", 167 type=str, 168 default="", 169 help="The compilation directory of the binary", 170 ) 171 args = parser.parse_args() 172 173 if args.use_existing_profdata and args.only_merge: 174 print("--use-existing-profdata and --only-merge are incompatible") 175 exit(1) 176 177 if args.use_existing_profdata: 178 profdata_path = args.use_existing_profdata 179 else: 180 profdata_path = merge_raw_profiles( 181 args.host_llvm_profdata, args.profile_data_dir, args.preserve_profiles 182 ) 183 184 if not len(args.binaries): 185 print("No binaries specified, no work to do!") 186 exit(1) 187 188 if not args.only_merge: 189 prepare_html_reports( 190 args.host_llvm_cov, 191 profdata_path, 192 args.report_dir, 193 args.binaries, 194 args.unified_report, 195 args.restrict, 196 args.compilation_dir, 197 ) 198