1 //===- CoverageExporterLcov.cpp - Code coverage export --------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements export of code coverage data to lcov trace file format.
11 //===----------------------------------------------------------------------===//
13 //===----------------------------------------------------------------------===//
15 // The trace file code coverage export follows the following format (see also
16 // https://linux.die.net/man/1/geninfo). Each quoted string appears on its own
17 // line; the indentation shown here is only for documentation purposes.
19 // - for each source file:
20 // - "SF:<absolute path to source file>"
21 // - for each function:
22 // - "FN:<line number of function start>,<function name>"
23 // - for each function:
24 // - "FNDA:<execution count>,<function name>"
25 // - "FNF:<number of functions found>"
26 // - "FNH:<number of functions hit>"
27 // - for each instrumented line:
28 // - "DA:<line number>,<execution count>[,<checksum>]
30 // - "BRDA:<line number>,<branch pair id>,<branch id>,<count>"
31 // - "BRF:<number of branches found>"
32 // - "BRH:<number of branches hit>"
33 // - "LH:<number of lines with non-zero execution count>"
34 // - "LF:<number of instrumented lines>"
37 // If the user is exporting summary information only, then the FN, FNDA, and DA
38 // lines will not be present.
40 //===----------------------------------------------------------------------===//
42 #include "CoverageExporterLcov.h"
43 #include "CoverageReport.h"
49 void renderFunctionSummary(raw_ostream
&OS
,
50 const FileCoverageSummary
&Summary
) {
51 OS
<< "FNF:" << Summary
.FunctionCoverage
.getNumFunctions() << '\n'
52 << "FNH:" << Summary
.FunctionCoverage
.getExecuted() << '\n';
57 const iterator_range
<coverage::FunctionRecordIterator
> &Functions
) {
58 for (const auto &F
: Functions
) {
59 auto StartLine
= F
.CountedRegions
.front().LineStart
;
60 OS
<< "FN:" << StartLine
<< ',' << F
.Name
<< '\n';
62 for (const auto &F
: Functions
)
63 OS
<< "FNDA:" << F
.ExecutionCount
<< ',' << F
.Name
<< '\n';
66 void renderLineExecutionCounts(raw_ostream
&OS
,
67 const coverage::CoverageData
&FileCoverage
) {
68 coverage::LineCoverageIterator LCI
{FileCoverage
, 1};
69 coverage::LineCoverageIterator LCIEnd
= LCI
.getEnd();
70 for (; LCI
!= LCIEnd
; ++LCI
) {
71 const coverage::LineCoverageStats
&LCS
= *LCI
;
73 OS
<< "DA:" << LCS
.getLine() << ',' << LCS
.getExecutionCount() << '\n';
78 std::vector
<llvm::coverage::CountedRegion
>
79 collectNestedBranches(const coverage::CoverageMapping
&Coverage
,
80 ArrayRef
<llvm::coverage::ExpansionRecord
> Expansions
,
81 int ViewDepth
= 0, int SrcLine
= 0) {
82 std::vector
<llvm::coverage::CountedRegion
> Branches
;
83 for (const auto &Expansion
: Expansions
) {
84 auto ExpansionCoverage
= Coverage
.getCoverageForExpansion(Expansion
);
86 // If we're at the top level, set the corresponding source line.
88 SrcLine
= Expansion
.Region
.LineStart
;
90 // Recursively collect branches from nested expansions.
91 auto NestedExpansions
= ExpansionCoverage
.getExpansions();
92 auto NestedExBranches
= collectNestedBranches(Coverage
, NestedExpansions
,
93 ViewDepth
+ 1, SrcLine
);
94 append_range(Branches
, NestedExBranches
);
96 // Add branches from this level of expansion.
97 auto ExBranches
= ExpansionCoverage
.getBranches();
98 for (auto B
: ExBranches
)
99 if (B
.FileID
== Expansion
.FileID
) {
100 B
.LineStart
= SrcLine
;
101 Branches
.push_back(B
);
108 bool sortLine(llvm::coverage::CountedRegion I
,
109 llvm::coverage::CountedRegion J
) {
110 return (I
.LineStart
< J
.LineStart
) ||
111 ((I
.LineStart
== J
.LineStart
) && (I
.ColumnStart
< J
.ColumnStart
));
114 void renderBranchExecutionCounts(raw_ostream
&OS
,
115 const coverage::CoverageMapping
&Coverage
,
116 const coverage::CoverageData
&FileCoverage
) {
117 std::vector
<llvm::coverage::CountedRegion
> Branches
=
118 FileCoverage
.getBranches();
120 // Recursively collect branches for all file expansions.
121 std::vector
<llvm::coverage::CountedRegion
> ExBranches
=
122 collectNestedBranches(Coverage
, FileCoverage
.getExpansions());
124 // Append Expansion Branches to Source Branches.
125 append_range(Branches
, ExBranches
);
127 // Sort branches based on line number to ensure branches corresponding to the
128 // same source line are counted together.
129 llvm::sort(Branches
, sortLine
);
131 auto NextBranch
= Branches
.begin();
132 auto EndBranch
= Branches
.end();
134 // Branches with the same source line are enumerated individually
135 // (BranchIndex) as well as based on True/False pairs (PairIndex).
136 while (NextBranch
!= EndBranch
) {
137 unsigned CurrentLine
= NextBranch
->LineStart
;
138 unsigned PairIndex
= 0;
139 unsigned BranchIndex
= 0;
141 while (NextBranch
!= EndBranch
&& CurrentLine
== NextBranch
->LineStart
) {
142 if (!NextBranch
->Folded
) {
143 unsigned BC1
= NextBranch
->ExecutionCount
;
144 unsigned BC2
= NextBranch
->FalseExecutionCount
;
145 bool BranchNotExecuted
= (BC1
== 0 && BC2
== 0);
147 for (int I
= 0; I
< 2; I
++, BranchIndex
++) {
148 OS
<< "BRDA:" << CurrentLine
<< ',' << PairIndex
<< ','
150 if (BranchNotExecuted
)
151 OS
<< ',' << '-' << '\n';
153 OS
<< ',' << (I
== 0 ? BC1
: BC2
) << '\n';
163 void renderLineSummary(raw_ostream
&OS
, const FileCoverageSummary
&Summary
) {
164 OS
<< "LF:" << Summary
.LineCoverage
.getNumLines() << '\n'
165 << "LH:" << Summary
.LineCoverage
.getCovered() << '\n';
168 void renderBranchSummary(raw_ostream
&OS
, const FileCoverageSummary
&Summary
) {
169 OS
<< "BRF:" << Summary
.BranchCoverage
.getNumBranches() << '\n'
170 << "BRH:" << Summary
.BranchCoverage
.getCovered() << '\n';
173 void renderFile(raw_ostream
&OS
, const coverage::CoverageMapping
&Coverage
,
174 const std::string
&Filename
,
175 const FileCoverageSummary
&FileReport
, bool ExportSummaryOnly
,
176 bool SkipFunctions
, bool SkipBranches
) {
177 OS
<< "SF:" << Filename
<< '\n';
179 if (!ExportSummaryOnly
&& !SkipFunctions
) {
180 renderFunctions(OS
, Coverage
.getCoveredFunctions(Filename
));
182 renderFunctionSummary(OS
, FileReport
);
184 if (!ExportSummaryOnly
) {
185 // Calculate and render detailed coverage information for given file.
186 auto FileCoverage
= Coverage
.getCoverageForFile(Filename
);
187 renderLineExecutionCounts(OS
, FileCoverage
);
189 renderBranchExecutionCounts(OS
, Coverage
, FileCoverage
);
192 renderBranchSummary(OS
, FileReport
);
193 renderLineSummary(OS
, FileReport
);
195 OS
<< "end_of_record\n";
198 void renderFiles(raw_ostream
&OS
, const coverage::CoverageMapping
&Coverage
,
199 ArrayRef
<std::string
> SourceFiles
,
200 ArrayRef
<FileCoverageSummary
> FileReports
,
201 bool ExportSummaryOnly
, bool SkipFunctions
,
203 for (unsigned I
= 0, E
= SourceFiles
.size(); I
< E
; ++I
)
204 renderFile(OS
, Coverage
, SourceFiles
[I
], FileReports
[I
], ExportSummaryOnly
,
205 SkipFunctions
, SkipBranches
);
208 } // end anonymous namespace
210 void CoverageExporterLcov::renderRoot(const CoverageFilters
&IgnoreFilters
) {
211 std::vector
<std::string
> SourceFiles
;
212 for (StringRef SF
: Coverage
.getUniqueSourceFiles()) {
213 if (!IgnoreFilters
.matchesFilename(SF
))
214 SourceFiles
.emplace_back(SF
);
216 renderRoot(SourceFiles
);
219 void CoverageExporterLcov::renderRoot(ArrayRef
<std::string
> SourceFiles
) {
220 FileCoverageSummary Totals
= FileCoverageSummary("Totals");
221 auto FileReports
= CoverageReport::prepareFileReports(Coverage
, Totals
,
222 SourceFiles
, Options
);
223 renderFiles(OS
, Coverage
, SourceFiles
, FileReports
, Options
.ExportSummaryOnly
,
224 Options
.SkipFunctions
, Options
.SkipBranches
);