Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
generate_coverage.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2#
3# Copyright 2013 The Flutter Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7import sys
8import subprocess
9import os
10import argparse
11import errno
12import shutil
13
14
16 buildtool_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../buildtools')
17 platform_dir = ''
18 if sys.platform.startswith('linux'):
19 platform_dir = 'linux-x64'
20 elif sys.platform == 'darwin':
21 platform_dir = 'mac-x64'
22 else:
23 raise Exception('Unknown/Unsupported platform.')
24 llvm_bin_dir = os.path.abspath(os.path.join(buildtool_dir, platform_dir, 'clang/bin'))
25 if not os.path.exists(llvm_bin_dir):
26 raise Exception('LLVM directory %s double not be located.' % llvm_bin_dir)
27 return llvm_bin_dir
28
29
30def make_dirs(new_dir):
31 """A wrapper around os.makedirs() that emulates "mkdir -p"."""
32 try:
33 os.makedirs(new_dir)
34 except OSError as err:
35 if err.errno != errno.EEXIST:
36 raise
37
38
40 if os.path.isdir(path) and not os.path.islink(path):
41 shutil.rmtree(path)
42 elif os.path.exists(path):
43 os.remove(path)
44
45
47 raw_profiles = []
48 binaries = []
49
50 # Run all unit tests and collect raw profiles.
51 for test in args.tests:
52 absolute_test_path = os.path.abspath(test)
53 absolute_test_dir = os.path.dirname(absolute_test_path)
54 test_name = os.path.basename(absolute_test_path)
55
56 if not os.path.exists(absolute_test_path):
57 print('Path %s does not exist.' % absolute_test_path)
58 return -1
59
60 unstripped_test_path = os.path.join(absolute_test_dir, 'exe.unstripped', test_name)
61
62 if os.path.exists(unstripped_test_path):
63 binaries.append(unstripped_test_path)
64 else:
65 binaries.append(absolute_test_path)
66
67 raw_profile = absolute_test_path + '.rawprofile'
68
69 remove_if_exists(raw_profile)
70
71 print('Running test %s to gather profile.' % os.path.basename(absolute_test_path))
72
73 test_command = [absolute_test_path]
74
75 test_args = ' '.join(args.test_args).split()
76
77 if test_args is not None:
78 test_command += test_args
79
80 subprocess.check_call(test_command, env={'LLVM_PROFILE_FILE': raw_profile})
81
82 if not os.path.exists(raw_profile):
83 print('Could not find raw profile data for unit test run %s.' % test)
84 print('Did you build with the --coverage flag?')
85 return -1
86
87 raw_profiles.append(raw_profile)
88
89 return (binaries, raw_profiles)
90
91
92def merge_profiles(llvm_bin_dir, raw_profiles, output):
93 # Merge all raw profiles into a single profile.
94 profdata_binary = os.path.join(llvm_bin_dir, 'llvm-profdata')
95
96 print('Merging %d raw profile(s) into single profile.' % len(raw_profiles))
97 merged_profile_path = os.path.join(output, 'all.profile')
98 remove_if_exists(merged_profile_path)
99 merge_command = [profdata_binary, 'merge', '-sparse'] + raw_profiles + ['-o', merged_profile_path]
100 subprocess.check_call(merge_command)
101 print('Done.')
102 return merged_profile_path
103
104
105def main():
106 parser = argparse.ArgumentParser()
107
108 parser.add_argument(
109 '-t',
110 '--tests',
111 nargs='+',
112 dest='tests',
113 required=True,
114 help='The unit tests to run and gather coverage data on.'
115 )
116 parser.add_argument(
117 '-o',
118 '--output',
119 dest='output',
120 required=True,
121 help='The output directory for coverage results.'
122 )
123 parser.add_argument(
124 '-f',
125 '--format',
126 type=str,
127 choices=['all', 'html', 'summary', 'lcov'],
128 required=True,
129 help='The type of coverage information to be displayed.'
130 )
131 parser.add_argument(
132 '-a',
133 '--args',
134 nargs='+',
135 dest='test_args',
136 required=False,
137 help='The arguments to pass to the unit test executable being run.'
138 )
139
140 args = parser.parse_args()
141
142 output = os.path.abspath(args.output)
143
144 make_dirs(output)
145
146 generate_all_reports = args.format == 'all'
147
148 binaries, raw_profiles = collect_profiles(args)
149
150 if len(raw_profiles) == 0:
151 print('No raw profiles could be generated.')
152 return -1
153
154 binaries_flag = []
155 for binary in binaries:
156 binaries_flag.append('-object')
157 binaries_flag.append(binary)
158
159 llvm_bin_dir = get_llvm_bin_directory()
160
161 merged_profile_path = merge_profiles(llvm_bin_dir, raw_profiles, output)
162
163 if not os.path.exists(merged_profile_path):
164 print('Could not generate or find merged profile %s.' % merged_profile_path)
165 return -1
166
167 llvm_cov_binary = os.path.join(llvm_bin_dir, 'llvm-cov')
168 instr_profile_flag = '-instr-profile=%s' % merged_profile_path
169 ignore_flags = '-ignore-filename-regex=third_party|unittest|fixture'
170
171 # Generate the HTML report if specified.
172 if generate_all_reports or args.format == 'html':
173 print('Generating HTML report.')
174 subprocess.check_call([llvm_cov_binary, 'show'] + binaries_flag + [
175 instr_profile_flag,
176 '-format=html',
177 '-output-dir=%s' % output,
178 '-tab-size=2',
179 ignore_flags,
180 ])
181 print('Done.')
182
183 # Generate a report summary if specified.
184 if generate_all_reports or args.format == 'summary':
185 print('Generating a summary report.')
186 subprocess.check_call([llvm_cov_binary, 'report'] + binaries_flag + [
187 instr_profile_flag,
188 ignore_flags,
189 ])
190 print('Done.')
191
192 # Generate a lcov summary if specified.
193 if generate_all_reports or args.format == 'lcov':
194 print('Generating LCOV report.')
195 lcov_file = os.path.join(output, 'coverage.lcov')
196 remove_if_exists(lcov_file)
197 with open(lcov_file, 'w') as lcov_redirect:
198 subprocess.check_call([llvm_cov_binary, 'export'] + binaries_flag + [
199 instr_profile_flag,
200 ignore_flags,
201 '-format=lcov',
202 ],
203 stdout=lcov_redirect)
204 print('Done.')
205
206 return 0
207
208
209if __name__ == '__main__':
210 sys.exit(main())
void print(void *str)
Definition bridge.cpp:126
merge_profiles(llvm_bin_dir, raw_profiles, output)
Definition main.py:1