xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-cov/CoverageExporterLcov.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
10b57cec5SDimitry Andric //===- CoverageExporterLcov.cpp - Code coverage export --------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements export of code coverage data to lcov trace file format.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric //
150b57cec5SDimitry Andric // The trace file code coverage export follows the following format (see also
160b57cec5SDimitry Andric // https://linux.die.net/man/1/geninfo). Each quoted string appears on its own
170b57cec5SDimitry Andric // line; the indentation shown here is only for documentation purposes.
180b57cec5SDimitry Andric //
190b57cec5SDimitry Andric // - for each source file:
200b57cec5SDimitry Andric //   - "SF:<absolute path to source file>"
210b57cec5SDimitry Andric //   - for each function:
220b57cec5SDimitry Andric //     - "FN:<line number of function start>,<function name>"
230b57cec5SDimitry Andric //   - for each function:
240b57cec5SDimitry Andric //     - "FNDA:<execution count>,<function name>"
250b57cec5SDimitry Andric //   - "FNF:<number of functions found>"
260b57cec5SDimitry Andric //   - "FNH:<number of functions hit>"
270b57cec5SDimitry Andric //   - for each instrumented line:
280b57cec5SDimitry Andric //     - "DA:<line number>,<execution count>[,<checksum>]
29e8d8bef9SDimitry Andric //   - for each branch:
30e8d8bef9SDimitry Andric //     - "BRDA:<line number>,<branch pair id>,<branch id>,<count>"
31e8d8bef9SDimitry Andric //   - "BRF:<number of branches found>"
32e8d8bef9SDimitry Andric //   - "BRH:<number of branches hit>"
330b57cec5SDimitry Andric //   - "LH:<number of lines with non-zero execution count>"
34e8d8bef9SDimitry Andric //   - "LF:<number of instrumented lines>"
350b57cec5SDimitry Andric //   - "end_of_record"
360b57cec5SDimitry Andric //
370b57cec5SDimitry Andric // If the user is exporting summary information only, then the FN, FNDA, and DA
380b57cec5SDimitry Andric // lines will not be present.
390b57cec5SDimitry Andric //
400b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric #include "CoverageExporterLcov.h"
430b57cec5SDimitry Andric #include "CoverageReport.h"
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric using namespace llvm;
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric namespace {
480b57cec5SDimitry Andric 
renderFunctionSummary(raw_ostream & OS,const FileCoverageSummary & Summary)490b57cec5SDimitry Andric void renderFunctionSummary(raw_ostream &OS,
500b57cec5SDimitry Andric                            const FileCoverageSummary &Summary) {
510b57cec5SDimitry Andric   OS << "FNF:" << Summary.FunctionCoverage.getNumFunctions() << '\n'
520b57cec5SDimitry Andric      << "FNH:" << Summary.FunctionCoverage.getExecuted() << '\n';
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric 
renderFunctions(raw_ostream & OS,const iterator_range<coverage::FunctionRecordIterator> & Functions)550b57cec5SDimitry Andric void renderFunctions(
560b57cec5SDimitry Andric     raw_ostream &OS,
570b57cec5SDimitry Andric     const iterator_range<coverage::FunctionRecordIterator> &Functions) {
580b57cec5SDimitry Andric   for (const auto &F : Functions) {
590b57cec5SDimitry Andric     auto StartLine = F.CountedRegions.front().LineStart;
600b57cec5SDimitry Andric     OS << "FN:" << StartLine << ',' << F.Name << '\n';
610b57cec5SDimitry Andric   }
620b57cec5SDimitry Andric   for (const auto &F : Functions)
630b57cec5SDimitry Andric     OS << "FNDA:" << F.ExecutionCount << ',' << F.Name << '\n';
640b57cec5SDimitry Andric }
650b57cec5SDimitry Andric 
renderLineExecutionCounts(raw_ostream & OS,const coverage::CoverageData & FileCoverage)660b57cec5SDimitry Andric void renderLineExecutionCounts(raw_ostream &OS,
670b57cec5SDimitry Andric                                const coverage::CoverageData &FileCoverage) {
680b57cec5SDimitry Andric   coverage::LineCoverageIterator LCI{FileCoverage, 1};
690b57cec5SDimitry Andric   coverage::LineCoverageIterator LCIEnd = LCI.getEnd();
700b57cec5SDimitry Andric   for (; LCI != LCIEnd; ++LCI) {
710b57cec5SDimitry Andric     const coverage::LineCoverageStats &LCS = *LCI;
720b57cec5SDimitry Andric     if (LCS.isMapped()) {
730b57cec5SDimitry Andric       OS << "DA:" << LCS.getLine() << ',' << LCS.getExecutionCount() << '\n';
740b57cec5SDimitry Andric     }
750b57cec5SDimitry Andric   }
760b57cec5SDimitry Andric }
770b57cec5SDimitry Andric 
78e8d8bef9SDimitry Andric std::vector<llvm::coverage::CountedRegion>
collectNestedBranches(const coverage::CoverageMapping & Coverage,ArrayRef<llvm::coverage::ExpansionRecord> Expansions,int ViewDepth=0,int SrcLine=0)79e8d8bef9SDimitry Andric collectNestedBranches(const coverage::CoverageMapping &Coverage,
80e8d8bef9SDimitry Andric                       ArrayRef<llvm::coverage::ExpansionRecord> Expansions,
81e8d8bef9SDimitry Andric                       int ViewDepth = 0, int SrcLine = 0) {
82e8d8bef9SDimitry Andric   std::vector<llvm::coverage::CountedRegion> Branches;
83e8d8bef9SDimitry Andric   for (const auto &Expansion : Expansions) {
84e8d8bef9SDimitry Andric     auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
85e8d8bef9SDimitry Andric 
86e8d8bef9SDimitry Andric     // If we're at the top level, set the corresponding source line.
87e8d8bef9SDimitry Andric     if (ViewDepth == 0)
88e8d8bef9SDimitry Andric       SrcLine = Expansion.Region.LineStart;
89e8d8bef9SDimitry Andric 
90e8d8bef9SDimitry Andric     // Recursively collect branches from nested expansions.
91e8d8bef9SDimitry Andric     auto NestedExpansions = ExpansionCoverage.getExpansions();
92e8d8bef9SDimitry Andric     auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions,
93e8d8bef9SDimitry Andric                                                   ViewDepth + 1, SrcLine);
94fe6060f1SDimitry Andric     append_range(Branches, NestedExBranches);
95e8d8bef9SDimitry Andric 
96e8d8bef9SDimitry Andric     // Add branches from this level of expansion.
97e8d8bef9SDimitry Andric     auto ExBranches = ExpansionCoverage.getBranches();
98e8d8bef9SDimitry Andric     for (auto B : ExBranches)
99e8d8bef9SDimitry Andric       if (B.FileID == Expansion.FileID) {
100e8d8bef9SDimitry Andric         B.LineStart = SrcLine;
101e8d8bef9SDimitry Andric         Branches.push_back(B);
102e8d8bef9SDimitry Andric       }
103e8d8bef9SDimitry Andric   }
104e8d8bef9SDimitry Andric 
105e8d8bef9SDimitry Andric   return Branches;
106e8d8bef9SDimitry Andric }
107e8d8bef9SDimitry Andric 
sortLine(llvm::coverage::CountedRegion I,llvm::coverage::CountedRegion J)108e8d8bef9SDimitry Andric bool sortLine(llvm::coverage::CountedRegion I,
109e8d8bef9SDimitry Andric               llvm::coverage::CountedRegion J) {
110e8d8bef9SDimitry Andric   return (I.LineStart < J.LineStart) ||
111e8d8bef9SDimitry Andric          ((I.LineStart == J.LineStart) && (I.ColumnStart < J.ColumnStart));
112e8d8bef9SDimitry Andric }
113e8d8bef9SDimitry Andric 
renderBranchExecutionCounts(raw_ostream & OS,const coverage::CoverageMapping & Coverage,const coverage::CoverageData & FileCoverage)114e8d8bef9SDimitry Andric void renderBranchExecutionCounts(raw_ostream &OS,
115e8d8bef9SDimitry Andric                                  const coverage::CoverageMapping &Coverage,
116e8d8bef9SDimitry Andric                                  const coverage::CoverageData &FileCoverage) {
117e8d8bef9SDimitry Andric   std::vector<llvm::coverage::CountedRegion> Branches =
118e8d8bef9SDimitry Andric       FileCoverage.getBranches();
119e8d8bef9SDimitry Andric 
120e8d8bef9SDimitry Andric   // Recursively collect branches for all file expansions.
121e8d8bef9SDimitry Andric   std::vector<llvm::coverage::CountedRegion> ExBranches =
122e8d8bef9SDimitry Andric       collectNestedBranches(Coverage, FileCoverage.getExpansions());
123e8d8bef9SDimitry Andric 
124e8d8bef9SDimitry Andric   // Append Expansion Branches to Source Branches.
125fe6060f1SDimitry Andric   append_range(Branches, ExBranches);
126e8d8bef9SDimitry Andric 
127e8d8bef9SDimitry Andric   // Sort branches based on line number to ensure branches corresponding to the
128e8d8bef9SDimitry Andric   // same source line are counted together.
129e8d8bef9SDimitry Andric   llvm::sort(Branches, sortLine);
130e8d8bef9SDimitry Andric 
131e8d8bef9SDimitry Andric   auto NextBranch = Branches.begin();
132e8d8bef9SDimitry Andric   auto EndBranch = Branches.end();
133e8d8bef9SDimitry Andric 
134e8d8bef9SDimitry Andric   // Branches with the same source line are enumerated individually
135e8d8bef9SDimitry Andric   // (BranchIndex) as well as based on True/False pairs (PairIndex).
136e8d8bef9SDimitry Andric   while (NextBranch != EndBranch) {
137e8d8bef9SDimitry Andric     unsigned CurrentLine = NextBranch->LineStart;
138e8d8bef9SDimitry Andric     unsigned PairIndex = 0;
139e8d8bef9SDimitry Andric     unsigned BranchIndex = 0;
140e8d8bef9SDimitry Andric 
141e8d8bef9SDimitry Andric     while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) {
142e8d8bef9SDimitry Andric       if (!NextBranch->Folded) {
143e8d8bef9SDimitry Andric         unsigned BC1 = NextBranch->ExecutionCount;
144e8d8bef9SDimitry Andric         unsigned BC2 = NextBranch->FalseExecutionCount;
145e8d8bef9SDimitry Andric         bool BranchNotExecuted = (BC1 == 0 && BC2 == 0);
146e8d8bef9SDimitry Andric 
147e8d8bef9SDimitry Andric         for (int I = 0; I < 2; I++, BranchIndex++) {
148e8d8bef9SDimitry Andric           OS << "BRDA:" << CurrentLine << ',' << PairIndex << ','
149e8d8bef9SDimitry Andric              << BranchIndex;
150e8d8bef9SDimitry Andric           if (BranchNotExecuted)
151e8d8bef9SDimitry Andric             OS << ',' << '-' << '\n';
152e8d8bef9SDimitry Andric           else
153e8d8bef9SDimitry Andric             OS << ',' << (I == 0 ? BC1 : BC2) << '\n';
154e8d8bef9SDimitry Andric         }
155e8d8bef9SDimitry Andric 
156e8d8bef9SDimitry Andric         PairIndex++;
157e8d8bef9SDimitry Andric       }
158e8d8bef9SDimitry Andric       NextBranch++;
159e8d8bef9SDimitry Andric     }
160e8d8bef9SDimitry Andric   }
161e8d8bef9SDimitry Andric }
162e8d8bef9SDimitry Andric 
renderLineSummary(raw_ostream & OS,const FileCoverageSummary & Summary)1630b57cec5SDimitry Andric void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
1640b57cec5SDimitry Andric   OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n'
1650b57cec5SDimitry Andric      << "LH:" << Summary.LineCoverage.getCovered() << '\n';
1660b57cec5SDimitry Andric }
1670b57cec5SDimitry Andric 
renderBranchSummary(raw_ostream & OS,const FileCoverageSummary & Summary)168e8d8bef9SDimitry Andric void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
169e8d8bef9SDimitry Andric   OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n'
17069ade1e0SDimitry Andric      << "BRH:" << Summary.BranchCoverage.getCovered() << '\n';
171e8d8bef9SDimitry Andric }
172e8d8bef9SDimitry Andric 
renderFile(raw_ostream & OS,const coverage::CoverageMapping & Coverage,const std::string & Filename,const FileCoverageSummary & FileReport,bool ExportSummaryOnly,bool SkipFunctions,bool SkipBranches)1730b57cec5SDimitry Andric void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
1740b57cec5SDimitry Andric                 const std::string &Filename,
1755ffd83dbSDimitry Andric                 const FileCoverageSummary &FileReport, bool ExportSummaryOnly,
176*bdd1243dSDimitry Andric                 bool SkipFunctions, bool SkipBranches) {
1770b57cec5SDimitry Andric   OS << "SF:" << Filename << '\n';
1780b57cec5SDimitry Andric 
1795ffd83dbSDimitry Andric   if (!ExportSummaryOnly && !SkipFunctions) {
1800b57cec5SDimitry Andric     renderFunctions(OS, Coverage.getCoveredFunctions(Filename));
1810b57cec5SDimitry Andric   }
1820b57cec5SDimitry Andric   renderFunctionSummary(OS, FileReport);
1830b57cec5SDimitry Andric 
1840b57cec5SDimitry Andric   if (!ExportSummaryOnly) {
1850b57cec5SDimitry Andric     // Calculate and render detailed coverage information for given file.
1860b57cec5SDimitry Andric     auto FileCoverage = Coverage.getCoverageForFile(Filename);
1870b57cec5SDimitry Andric     renderLineExecutionCounts(OS, FileCoverage);
188*bdd1243dSDimitry Andric     if (!SkipBranches)
189e8d8bef9SDimitry Andric       renderBranchExecutionCounts(OS, Coverage, FileCoverage);
1900b57cec5SDimitry Andric   }
191*bdd1243dSDimitry Andric   if (!SkipBranches)
192e8d8bef9SDimitry Andric     renderBranchSummary(OS, FileReport);
1930b57cec5SDimitry Andric   renderLineSummary(OS, FileReport);
1940b57cec5SDimitry Andric 
1950b57cec5SDimitry Andric   OS << "end_of_record\n";
1960b57cec5SDimitry Andric }
1970b57cec5SDimitry Andric 
renderFiles(raw_ostream & OS,const coverage::CoverageMapping & Coverage,ArrayRef<std::string> SourceFiles,ArrayRef<FileCoverageSummary> FileReports,bool ExportSummaryOnly,bool SkipFunctions,bool SkipBranches)1980b57cec5SDimitry Andric void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
1990b57cec5SDimitry Andric                  ArrayRef<std::string> SourceFiles,
2000b57cec5SDimitry Andric                  ArrayRef<FileCoverageSummary> FileReports,
201*bdd1243dSDimitry Andric                  bool ExportSummaryOnly, bool SkipFunctions,
202*bdd1243dSDimitry Andric                  bool SkipBranches) {
2030b57cec5SDimitry Andric   for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I)
2045ffd83dbSDimitry Andric     renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly,
205*bdd1243dSDimitry Andric                SkipFunctions, SkipBranches);
2060b57cec5SDimitry Andric }
2070b57cec5SDimitry Andric 
2080b57cec5SDimitry Andric } // end anonymous namespace
2090b57cec5SDimitry Andric 
renderRoot(const CoverageFilters & IgnoreFilters)2100b57cec5SDimitry Andric void CoverageExporterLcov::renderRoot(const CoverageFilters &IgnoreFilters) {
2110b57cec5SDimitry Andric   std::vector<std::string> SourceFiles;
2120b57cec5SDimitry Andric   for (StringRef SF : Coverage.getUniqueSourceFiles()) {
2130b57cec5SDimitry Andric     if (!IgnoreFilters.matchesFilename(SF))
2140b57cec5SDimitry Andric       SourceFiles.emplace_back(SF);
2150b57cec5SDimitry Andric   }
2160b57cec5SDimitry Andric   renderRoot(SourceFiles);
2170b57cec5SDimitry Andric }
2180b57cec5SDimitry Andric 
renderRoot(ArrayRef<std::string> SourceFiles)2190b57cec5SDimitry Andric void CoverageExporterLcov::renderRoot(ArrayRef<std::string> SourceFiles) {
2200b57cec5SDimitry Andric   FileCoverageSummary Totals = FileCoverageSummary("Totals");
2210b57cec5SDimitry Andric   auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
2220b57cec5SDimitry Andric                                                         SourceFiles, Options);
2235ffd83dbSDimitry Andric   renderFiles(OS, Coverage, SourceFiles, FileReports, Options.ExportSummaryOnly,
224*bdd1243dSDimitry Andric               Options.SkipFunctions, Options.SkipBranches);
2250b57cec5SDimitry Andric }
226