1 //===- CoverageExporterLcov.cpp - Code coverage export --------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements export of code coverage data to lcov trace file format.
10 //
11 //===----------------------------------------------------------------------===//
12
13 //===----------------------------------------------------------------------===//
14 //
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.
18 //
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>]
29 // - for each branch:
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>"
35 // - "end_of_record"
36 //
37 // If the user is exporting summary information only, then the FN, FNDA, and DA
38 // lines will not be present.
39 //
40 //===----------------------------------------------------------------------===//
41
42 #include "CoverageExporterLcov.h"
43 #include "CoverageReport.h"
44
45 using namespace llvm;
46
47 namespace {
48
renderFunctionSummary(raw_ostream & OS,const FileCoverageSummary & Summary)49 void renderFunctionSummary(raw_ostream &OS,
50 const FileCoverageSummary &Summary) {
51 OS << "FNF:" << Summary.FunctionCoverage.getNumFunctions() << '\n'
52 << "FNH:" << Summary.FunctionCoverage.getExecuted() << '\n';
53 }
54
renderFunctions(raw_ostream & OS,const iterator_range<coverage::FunctionRecordIterator> & Functions)55 void renderFunctions(
56 raw_ostream &OS,
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';
61 }
62 for (const auto &F : Functions)
63 OS << "FNDA:" << F.ExecutionCount << ',' << F.Name << '\n';
64 }
65
renderLineExecutionCounts(raw_ostream & OS,const coverage::CoverageData & FileCoverage)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;
72 if (LCS.isMapped()) {
73 OS << "DA:" << LCS.getLine() << ',' << LCS.getExecutionCount() << '\n';
74 }
75 }
76 }
77
78 std::vector<llvm::coverage::CountedRegion>
collectNestedBranches(const coverage::CoverageMapping & Coverage,ArrayRef<llvm::coverage::ExpansionRecord> Expansions,int ViewDepth=0,int SrcLine=0)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);
85
86 // If we're at the top level, set the corresponding source line.
87 if (ViewDepth == 0)
88 SrcLine = Expansion.Region.LineStart;
89
90 // Recursively collect branches from nested expansions.
91 auto NestedExpansions = ExpansionCoverage.getExpansions();
92 auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions,
93 ViewDepth + 1, SrcLine);
94 Branches.insert(Branches.end(), NestedExBranches.begin(),
95 NestedExBranches.end());
96
97 // Add branches from this level of expansion.
98 auto ExBranches = ExpansionCoverage.getBranches();
99 for (auto B : ExBranches)
100 if (B.FileID == Expansion.FileID) {
101 B.LineStart = SrcLine;
102 Branches.push_back(B);
103 }
104 }
105
106 return Branches;
107 }
108
sortLine(llvm::coverage::CountedRegion I,llvm::coverage::CountedRegion J)109 bool sortLine(llvm::coverage::CountedRegion I,
110 llvm::coverage::CountedRegion J) {
111 return (I.LineStart < J.LineStart) ||
112 ((I.LineStart == J.LineStart) && (I.ColumnStart < J.ColumnStart));
113 }
114
renderBranchExecutionCounts(raw_ostream & OS,const coverage::CoverageMapping & Coverage,const coverage::CoverageData & FileCoverage)115 void renderBranchExecutionCounts(raw_ostream &OS,
116 const coverage::CoverageMapping &Coverage,
117 const coverage::CoverageData &FileCoverage) {
118 std::vector<llvm::coverage::CountedRegion> Branches =
119 FileCoverage.getBranches();
120
121 // Recursively collect branches for all file expansions.
122 std::vector<llvm::coverage::CountedRegion> ExBranches =
123 collectNestedBranches(Coverage, FileCoverage.getExpansions());
124
125 // Append Expansion Branches to Source Branches.
126 Branches.insert(Branches.end(), ExBranches.begin(), ExBranches.end());
127
128 // Sort branches based on line number to ensure branches corresponding to the
129 // same source line are counted together.
130 llvm::sort(Branches, sortLine);
131
132 auto NextBranch = Branches.begin();
133 auto EndBranch = Branches.end();
134
135 // Branches with the same source line are enumerated individually
136 // (BranchIndex) as well as based on True/False pairs (PairIndex).
137 while (NextBranch != EndBranch) {
138 unsigned CurrentLine = NextBranch->LineStart;
139 unsigned PairIndex = 0;
140 unsigned BranchIndex = 0;
141
142 while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) {
143 if (!NextBranch->Folded) {
144 unsigned BC1 = NextBranch->ExecutionCount;
145 unsigned BC2 = NextBranch->FalseExecutionCount;
146 bool BranchNotExecuted = (BC1 == 0 && BC2 == 0);
147
148 for (int I = 0; I < 2; I++, BranchIndex++) {
149 OS << "BRDA:" << CurrentLine << ',' << PairIndex << ','
150 << BranchIndex;
151 if (BranchNotExecuted)
152 OS << ',' << '-' << '\n';
153 else
154 OS << ',' << (I == 0 ? BC1 : BC2) << '\n';
155 }
156
157 PairIndex++;
158 }
159 NextBranch++;
160 }
161 }
162 }
163
renderLineSummary(raw_ostream & OS,const FileCoverageSummary & Summary)164 void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
165 OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n'
166 << "LH:" << Summary.LineCoverage.getCovered() << '\n';
167 }
168
renderBranchSummary(raw_ostream & OS,const FileCoverageSummary & Summary)169 void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
170 OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n'
171 << "BFH:" << Summary.BranchCoverage.getCovered() << '\n';
172 }
173
renderFile(raw_ostream & OS,const coverage::CoverageMapping & Coverage,const std::string & Filename,const FileCoverageSummary & FileReport,bool ExportSummaryOnly,bool SkipFunctions)174 void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
175 const std::string &Filename,
176 const FileCoverageSummary &FileReport, bool ExportSummaryOnly,
177 bool SkipFunctions) {
178 OS << "SF:" << Filename << '\n';
179
180 if (!ExportSummaryOnly && !SkipFunctions) {
181 renderFunctions(OS, Coverage.getCoveredFunctions(Filename));
182 }
183 renderFunctionSummary(OS, FileReport);
184
185 if (!ExportSummaryOnly) {
186 // Calculate and render detailed coverage information for given file.
187 auto FileCoverage = Coverage.getCoverageForFile(Filename);
188 renderLineExecutionCounts(OS, FileCoverage);
189 renderBranchExecutionCounts(OS, Coverage, FileCoverage);
190 }
191 renderBranchSummary(OS, FileReport);
192 renderLineSummary(OS, FileReport);
193
194 OS << "end_of_record\n";
195 }
196
renderFiles(raw_ostream & OS,const coverage::CoverageMapping & Coverage,ArrayRef<std::string> SourceFiles,ArrayRef<FileCoverageSummary> FileReports,bool ExportSummaryOnly,bool SkipFunctions)197 void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
198 ArrayRef<std::string> SourceFiles,
199 ArrayRef<FileCoverageSummary> FileReports,
200 bool ExportSummaryOnly, bool SkipFunctions) {
201 for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I)
202 renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly,
203 SkipFunctions);
204 }
205
206 } // end anonymous namespace
207
renderRoot(const CoverageFilters & IgnoreFilters)208 void CoverageExporterLcov::renderRoot(const CoverageFilters &IgnoreFilters) {
209 std::vector<std::string> SourceFiles;
210 for (StringRef SF : Coverage.getUniqueSourceFiles()) {
211 if (!IgnoreFilters.matchesFilename(SF))
212 SourceFiles.emplace_back(SF);
213 }
214 renderRoot(SourceFiles);
215 }
216
renderRoot(ArrayRef<std::string> SourceFiles)217 void CoverageExporterLcov::renderRoot(ArrayRef<std::string> SourceFiles) {
218 FileCoverageSummary Totals = FileCoverageSummary("Totals");
219 auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
220 SourceFiles, Options);
221 renderFiles(OS, Coverage, SourceFiles, FileReports, Options.ExportSummaryOnly,
222 Options.SkipFunctions);
223 }
224