OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
2 # Copyright (c) 2015 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 | |
7 """Run the given command through LLVM's coverage tools.""" | |
8 | |
9 | |
10 import argparse | |
11 import json | |
12 import os | |
13 import shlex | |
14 import subprocess | |
15 import sys | |
16 | |
17 | |
18 BUILDTYPE = 'Coverage' | |
19 OUT_DIR = os.path.realpath(os.path.join('out', BUILDTYPE)) | |
20 PROFILE_DATA = 'default.profraw' | |
21 PROFILE_DATA_MERGED = 'prof_merged' | |
22 | |
23 | |
24 def _fix_filename(filename): | |
25 """Return a filename which we can use to identify the file. | |
26 | |
27 The file paths printed by llvm-cov take the form: | |
28 | |
29 /path/to/repo/out/dir/../../src/filename.cpp | |
30 | |
31 And then they're truncated to 22 characters with leading ellipses: | |
32 | |
33 ...../../src/filename.cpp | |
34 | |
35 This makes it really tough to determine whether the file actually belongs in | |
36 the Skia repo. This function strips out the leading junk so that, if the file | |
37 exists in the repo, the returned string matches the end of some relative path | |
38 in the repo. This doesn't guarantee correctness, but it's about as close as | |
39 we can get. | |
40 """ | |
41 return filename.split('..')[-1].lstrip('./') | |
42 | |
43 | |
44 def _filter_results(results): | |
45 """Filter out any results for files not in the Skia repo. | |
46 | |
47 We run through the list of checked-in files and determine whether each file | |
48 belongs in the repo. Unfortunately, llvm-cov leaves us with fragments of the | |
49 file paths, so we can't guarantee accuracy. See the docstring for | |
50 _fix_filename for more details. | |
51 """ | |
52 all_files = subprocess.check_output(['git', 'ls-files']).splitlines() | |
53 filtered = [] | |
54 for percent, filename in results: | |
55 new_file = _fix_filename(filename) | |
56 matched = [] | |
57 for f in all_files: | |
58 if f.endswith(new_file): | |
59 matched.append(f) | |
60 if len(matched) == 1: | |
61 filtered.append((percent, matched[0])) | |
62 elif len(matched) > 1: | |
63 print >> sys.stderr, ('WARNING: multiple matches for %s; skipping:\n\t%s' | |
64 % (new_file, '\n\t'.join(matched))) | |
65 print 'Filtered out %d files.' % (len(results) - len(filtered)) | |
66 return filtered | |
borenet
2015/07/01 20:06:51
The right solution here is to submit a change to L
mtklein
2015/07/01 20:17:37
I wonder how basename.startswith('Sk') works as ou
borenet
2015/07/06 12:06:35
Maybe. The biggest problem with that is we'll mis
| |
67 | |
68 | |
69 def run_coverage(cmd): | |
70 """Run the given command and return per-file coverage data. | |
71 | |
72 Assumes that the binary has been built using llvm_coverage_build and that | |
73 LLVM 3.6 or newer is installed. | |
74 """ | |
75 binary_path = os.path.join(OUT_DIR, cmd[0]) | |
76 subprocess.call([binary_path] + cmd[1:]) | |
77 try: | |
78 subprocess.check_call( | |
79 ['llvm-profdata', 'merge', PROFILE_DATA, | |
80 '-output=%s' % PROFILE_DATA_MERGED]) | |
81 finally: | |
82 os.remove(PROFILE_DATA) | |
83 try: | |
84 report = subprocess.check_output( | |
85 ['llvm-cov', 'report', '-instr-profile', PROFILE_DATA_MERGED, | |
86 binary_path]) | |
87 finally: | |
88 os.remove(PROFILE_DATA_MERGED) | |
89 results = [] | |
90 for line in report.splitlines()[2:-2]: | |
91 filename, _, _, cover, _, _ = shlex.split(line) | |
92 percent = float(cover.split('%')[0]) | |
93 results.append((percent, filename)) | |
94 results = _filter_results(results) | |
95 results.sort() | |
96 return results | |
97 | |
98 | |
99 def main(): | |
100 res = run_coverage(sys.argv[1:]) | |
101 print '% Covered\tFilename' | |
102 for percent, f in res: | |
mtklein
2015/07/01 20:17:37
Can we get a line-by-line breakdown or some sort o
borenet
2015/07/06 12:06:35
`llvm-cov show` generates a giant file which shows
| |
103 print '%f\t%s' % (percent, f) | |
104 | |
105 | |
106 if __name__ == '__main__': | |
107 main() | |
OLD | NEW |