1"""This script helps to generate source based code coverage report. 2 3Usage: 4 python genReport.py --objects [OBJECTS] --format [FORMAT] 5 6""" 7# Copyright (C) 2023 The Android Open Source Project 8# 9# Licensed under the Apache License, Version 2.0 (the "License"); 10# you may not use this file except in compliance with the License. 11# You may obtain a copy of the License at 12# 13# http://www.apache.org/licenses/LICENSE-2.0 14# 15# Unless required by applicable law or agreed to in writing, software 16# distributed under the License is distributed on an "AS IS" BASIS, 17# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18# See the License for the specific language governing permissions and 19# limitations under the License. 20 21import argparse 22import os 23import subprocess 24import sys 25 26def genProfdata(files, llvmDir): 27 llvmProfdataBin = os.path.join(llvmDir, 'llvm-profdata') 28 29 # To prevent args characters from exceeding the limitation, 30 # we split the files into several batches and merge them one by one. 31 batch_size = 10 32 profraws_length = len(files) 33 profdata_list = [] 34 for offset in range(0, profraws_length, batch_size): 35 profdata_filename = f'{offset}.profdata' 36 profdata_list.append(profdata_filename) 37 38 subprocess_cmd = [llvmProfdataBin, 'merge'] 39 subprocess_cmd.extend(files[offset:min(offset+batch_size, profraws_length)]) 40 subprocess_cmd.extend([f'-o={profdata_filename}']) 41 42 subprocess.call(subprocess_cmd) 43 44 subprocess_cmd = [llvmProfdataBin, 'merge'] 45 subprocess_cmd.extend(profdata_list) 46 subprocess_cmd.extend(['-o=out.profdata']) 47 subprocess.call(subprocess_cmd) 48 49 for profdata in profdata_list: 50 os.remove(profdata) 51 52def genHtml(llvmDir, objects, out): 53 llvmCovBin = os.path.join(llvmDir, 'llvm-cov') 54 subprocess_cmd = [ 55 llvmCovBin, 56 'show', 57 '-instr-profile=out.profdata', 58 ] 59 subprocess_cmd.extend(objects) 60 subprocess_cmd.extend(['-use-color', '--format=html']) 61 with(open(out+'.html', 'w', encoding='utf-8')) as f: 62 subprocess.call(subprocess_cmd, stdout=f) 63 64def genJson(llvmDir, objects, out, summary_only=True): 65 llvmCovBin = os.path.join(llvmDir, 'llvm-cov') 66 subprocess_cmd = [ 67 llvmCovBin, 68 'export', 69 '-instr-profile=out.profdata', 70 ] 71 subprocess_cmd.extend(objects) 72 if summary_only: 73 subprocess_cmd.extend(['-summary-only']) 74 75 with(open(out+'.json', 'w', encoding='utf-8')) as f: 76 subprocess.call(subprocess_cmd, stdout=f) 77 78def genLcov(llvmDir, objects, out, summary_only=True): 79 llvmCovBin = os.path.join(llvmDir, 'llvm-cov') 80 subprocess_cmd = [ 81 llvmCovBin, 82 'export', 83 '-format=lcov', 84 '-instr-profile=out.profdata', 85 ] 86 subprocess_cmd.extend(objects) 87 if summary_only: 88 subprocess_cmd.extend(['-summary-only']) 89 90 with(open(out+'.lcov', 'w', encoding='utf-8')) as f: 91 subprocess.call(subprocess_cmd, stdout=f) 92 93 94def main(): 95 arg_parser = argparse.ArgumentParser() 96 97 arg_parser.add_argument( 98 '--objects', 99 type=str, 100 required=True, 101 nargs='+', 102 help='List the elf files which are included in the test') 103 104 arg_parser.add_argument( 105 '--format', 106 type=str, 107 default='html', 108 help='Output format of the "llvm-cov show/export" command. The ' 109 'supported formats are "text", "html" and "lcov".') 110 111 arg_parser.add_argument( 112 '--llvm-dir', 113 type=str, 114 default='prebuilts/clang/host/linux-x86/llvm-binutils-stable/', 115 help='Provide path to LLVM binary directory to override the default ' 116 'one') 117 118 arg_parser.add_argument( 119 '--profraw-dir', 120 type=str, 121 default='tmp/', 122 help='Provide path to directory containing .profraw files') 123 124 arg_parser.add_argument( 125 '--output', 126 type=str, 127 default='out', 128 help='Provide output filename(without extension)') 129 130 arg_parser.add_argument( 131 '--summary-only', 132 default=True, 133 action=argparse.BooleanOptionalAction, 134 help='Flag of whether to enable summary only') 135 136 args = arg_parser.parse_args() 137 138 if not os.path.isdir(args.llvm_dir): 139 print('Provide path to LLVM binary directory') 140 return 141 142 if not os.path.isdir(args.profraw_dir): 143 print('Provide path to directory containing .profraw files') 144 return 145 146 profrawFiles = [ 147 os.path.join(args.profraw_dir, f) 148 for f in os.listdir(args.profraw_dir) if f.endswith('.profraw')] 149 if len(profrawFiles) == 0: 150 print('No profraw files found in directory ' + args.profraw_dir) 151 152 genProfdata(profrawFiles, args.llvm_dir) 153 objects = [] 154 for obj in args.objects: 155 objects.extend(['-object', obj]) 156 157 if args.format == 'html': 158 genHtml(args.llvm_dir, objects, args.output) 159 elif args.format == 'json': 160 genJson(args.llvm_dir, objects, args.output, args.summary_only) 161 elif args.format == 'lcov': 162 genLcov(args.llvm_dir, objects, args.output, args.summary_only) 163 else: 164 print('Only json / html / lcov supported') 165 return 166 167if __name__ == '__main__': 168 sys.exit(main()) 169