xref: /llvm-project/llvm/utils/prepare-code-coverage-artifact.py (revision b71edfaa4ec3c998aadb35255ce2f60bba2940b0)
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