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 
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 
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 
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>
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     append_range(Branches, NestedExBranches);
95 
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);
102       }
103   }
104 
105   return Branches;
106 }
107 
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));
112 }
113 
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();
119 
120   // Recursively collect branches for all file expansions.
121   std::vector<llvm::coverage::CountedRegion> ExBranches =
122       collectNestedBranches(Coverage, FileCoverage.getExpansions());
123 
124   // Append Expansion Branches to Source Branches.
125   append_range(Branches, ExBranches);
126 
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);
130 
131   auto NextBranch = Branches.begin();
132   auto EndBranch = Branches.end();
133 
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;
140 
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);
146 
147         for (int I = 0; I < 2; I++, BranchIndex++) {
148           OS << "BRDA:" << CurrentLine << ',' << PairIndex << ','
149              << BranchIndex;
150           if (BranchNotExecuted)
151             OS << ',' << '-' << '\n';
152           else
153             OS << ',' << (I == 0 ? BC1 : BC2) << '\n';
154         }
155 
156         PairIndex++;
157       }
158       NextBranch++;
159     }
160   }
161 }
162 
163 void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
164   OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n'
165      << "LH:" << Summary.LineCoverage.getCovered() << '\n';
166 }
167 
168 void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
169   OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n'
170      << "BRH:" << Summary.BranchCoverage.getCovered() << '\n';
171 }
172 
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';
178 
179   if (!ExportSummaryOnly && !SkipFunctions) {
180     renderFunctions(OS, Coverage.getCoveredFunctions(Filename));
181   }
182   renderFunctionSummary(OS, FileReport);
183 
184   if (!ExportSummaryOnly) {
185     // Calculate and render detailed coverage information for given file.
186     auto FileCoverage = Coverage.getCoverageForFile(Filename);
187     renderLineExecutionCounts(OS, FileCoverage);
188     if (!SkipBranches)
189       renderBranchExecutionCounts(OS, Coverage, FileCoverage);
190   }
191   if (!SkipBranches)
192     renderBranchSummary(OS, FileReport);
193   renderLineSummary(OS, FileReport);
194 
195   OS << "end_of_record\n";
196 }
197 
198 void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
199                  ArrayRef<std::string> SourceFiles,
200                  ArrayRef<FileCoverageSummary> FileReports,
201                  bool ExportSummaryOnly, bool SkipFunctions,
202                  bool SkipBranches) {
203   for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I)
204     renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly,
205                SkipFunctions, SkipBranches);
206 }
207 
208 } // end anonymous namespace
209 
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);
215   }
216   renderRoot(SourceFiles);
217 }
218 
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);
225 }
226