xref: /openbsd-src/gnu/llvm/llvm/tools/llvm-cov/CoverageExporterLcov.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
109467b48Spatrick //===- CoverageExporterLcov.cpp - Code coverage export --------------------===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // This file implements export of code coverage data to lcov trace file format.
1009467b48Spatrick //
1109467b48Spatrick //===----------------------------------------------------------------------===//
1209467b48Spatrick 
1309467b48Spatrick //===----------------------------------------------------------------------===//
1409467b48Spatrick //
1509467b48Spatrick // The trace file code coverage export follows the following format (see also
1609467b48Spatrick // https://linux.die.net/man/1/geninfo). Each quoted string appears on its own
1709467b48Spatrick // line; the indentation shown here is only for documentation purposes.
1809467b48Spatrick //
1909467b48Spatrick // - for each source file:
2009467b48Spatrick //   - "SF:<absolute path to source file>"
2109467b48Spatrick //   - for each function:
2209467b48Spatrick //     - "FN:<line number of function start>,<function name>"
2309467b48Spatrick //   - for each function:
2409467b48Spatrick //     - "FNDA:<execution count>,<function name>"
2509467b48Spatrick //   - "FNF:<number of functions found>"
2609467b48Spatrick //   - "FNH:<number of functions hit>"
2709467b48Spatrick //   - for each instrumented line:
2809467b48Spatrick //     - "DA:<line number>,<execution count>[,<checksum>]
2973471bf0Spatrick //   - for each branch:
3073471bf0Spatrick //     - "BRDA:<line number>,<branch pair id>,<branch id>,<count>"
3173471bf0Spatrick //   - "BRF:<number of branches found>"
3273471bf0Spatrick //   - "BRH:<number of branches hit>"
3309467b48Spatrick //   - "LH:<number of lines with non-zero execution count>"
3473471bf0Spatrick //   - "LF:<number of instrumented lines>"
3509467b48Spatrick //   - "end_of_record"
3609467b48Spatrick //
3709467b48Spatrick // If the user is exporting summary information only, then the FN, FNDA, and DA
3809467b48Spatrick // lines will not be present.
3909467b48Spatrick //
4009467b48Spatrick //===----------------------------------------------------------------------===//
4109467b48Spatrick 
4209467b48Spatrick #include "CoverageExporterLcov.h"
4309467b48Spatrick #include "CoverageReport.h"
4409467b48Spatrick 
4509467b48Spatrick using namespace llvm;
4609467b48Spatrick 
4709467b48Spatrick namespace {
4809467b48Spatrick 
renderFunctionSummary(raw_ostream & OS,const FileCoverageSummary & Summary)4909467b48Spatrick void renderFunctionSummary(raw_ostream &OS,
5009467b48Spatrick                            const FileCoverageSummary &Summary) {
5109467b48Spatrick   OS << "FNF:" << Summary.FunctionCoverage.getNumFunctions() << '\n'
5209467b48Spatrick      << "FNH:" << Summary.FunctionCoverage.getExecuted() << '\n';
5309467b48Spatrick }
5409467b48Spatrick 
renderFunctions(raw_ostream & OS,const iterator_range<coverage::FunctionRecordIterator> & Functions)5509467b48Spatrick void renderFunctions(
5609467b48Spatrick     raw_ostream &OS,
5709467b48Spatrick     const iterator_range<coverage::FunctionRecordIterator> &Functions) {
5809467b48Spatrick   for (const auto &F : Functions) {
5909467b48Spatrick     auto StartLine = F.CountedRegions.front().LineStart;
6009467b48Spatrick     OS << "FN:" << StartLine << ',' << F.Name << '\n';
6109467b48Spatrick   }
6209467b48Spatrick   for (const auto &F : Functions)
6309467b48Spatrick     OS << "FNDA:" << F.ExecutionCount << ',' << F.Name << '\n';
6409467b48Spatrick }
6509467b48Spatrick 
renderLineExecutionCounts(raw_ostream & OS,const coverage::CoverageData & FileCoverage)6609467b48Spatrick void renderLineExecutionCounts(raw_ostream &OS,
6709467b48Spatrick                                const coverage::CoverageData &FileCoverage) {
6809467b48Spatrick   coverage::LineCoverageIterator LCI{FileCoverage, 1};
6909467b48Spatrick   coverage::LineCoverageIterator LCIEnd = LCI.getEnd();
7009467b48Spatrick   for (; LCI != LCIEnd; ++LCI) {
7109467b48Spatrick     const coverage::LineCoverageStats &LCS = *LCI;
7209467b48Spatrick     if (LCS.isMapped()) {
7309467b48Spatrick       OS << "DA:" << LCS.getLine() << ',' << LCS.getExecutionCount() << '\n';
7409467b48Spatrick     }
7509467b48Spatrick   }
7609467b48Spatrick }
7709467b48Spatrick 
7873471bf0Spatrick std::vector<llvm::coverage::CountedRegion>
collectNestedBranches(const coverage::CoverageMapping & Coverage,ArrayRef<llvm::coverage::ExpansionRecord> Expansions,int ViewDepth=0,int SrcLine=0)7973471bf0Spatrick collectNestedBranches(const coverage::CoverageMapping &Coverage,
8073471bf0Spatrick                       ArrayRef<llvm::coverage::ExpansionRecord> Expansions,
8173471bf0Spatrick                       int ViewDepth = 0, int SrcLine = 0) {
8273471bf0Spatrick   std::vector<llvm::coverage::CountedRegion> Branches;
8373471bf0Spatrick   for (const auto &Expansion : Expansions) {
8473471bf0Spatrick     auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
8573471bf0Spatrick 
8673471bf0Spatrick     // If we're at the top level, set the corresponding source line.
8773471bf0Spatrick     if (ViewDepth == 0)
8873471bf0Spatrick       SrcLine = Expansion.Region.LineStart;
8973471bf0Spatrick 
9073471bf0Spatrick     // Recursively collect branches from nested expansions.
9173471bf0Spatrick     auto NestedExpansions = ExpansionCoverage.getExpansions();
9273471bf0Spatrick     auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions,
9373471bf0Spatrick                                                   ViewDepth + 1, SrcLine);
9473471bf0Spatrick     append_range(Branches, NestedExBranches);
9573471bf0Spatrick 
9673471bf0Spatrick     // Add branches from this level of expansion.
9773471bf0Spatrick     auto ExBranches = ExpansionCoverage.getBranches();
9873471bf0Spatrick     for (auto B : ExBranches)
9973471bf0Spatrick       if (B.FileID == Expansion.FileID) {
10073471bf0Spatrick         B.LineStart = SrcLine;
10173471bf0Spatrick         Branches.push_back(B);
10273471bf0Spatrick       }
10373471bf0Spatrick   }
10473471bf0Spatrick 
10573471bf0Spatrick   return Branches;
10673471bf0Spatrick }
10773471bf0Spatrick 
sortLine(llvm::coverage::CountedRegion I,llvm::coverage::CountedRegion J)10873471bf0Spatrick bool sortLine(llvm::coverage::CountedRegion I,
10973471bf0Spatrick               llvm::coverage::CountedRegion J) {
11073471bf0Spatrick   return (I.LineStart < J.LineStart) ||
11173471bf0Spatrick          ((I.LineStart == J.LineStart) && (I.ColumnStart < J.ColumnStart));
11273471bf0Spatrick }
11373471bf0Spatrick 
renderBranchExecutionCounts(raw_ostream & OS,const coverage::CoverageMapping & Coverage,const coverage::CoverageData & FileCoverage)11473471bf0Spatrick void renderBranchExecutionCounts(raw_ostream &OS,
11573471bf0Spatrick                                  const coverage::CoverageMapping &Coverage,
11673471bf0Spatrick                                  const coverage::CoverageData &FileCoverage) {
11773471bf0Spatrick   std::vector<llvm::coverage::CountedRegion> Branches =
11873471bf0Spatrick       FileCoverage.getBranches();
11973471bf0Spatrick 
12073471bf0Spatrick   // Recursively collect branches for all file expansions.
12173471bf0Spatrick   std::vector<llvm::coverage::CountedRegion> ExBranches =
12273471bf0Spatrick       collectNestedBranches(Coverage, FileCoverage.getExpansions());
12373471bf0Spatrick 
12473471bf0Spatrick   // Append Expansion Branches to Source Branches.
12573471bf0Spatrick   append_range(Branches, ExBranches);
12673471bf0Spatrick 
12773471bf0Spatrick   // Sort branches based on line number to ensure branches corresponding to the
12873471bf0Spatrick   // same source line are counted together.
12973471bf0Spatrick   llvm::sort(Branches, sortLine);
13073471bf0Spatrick 
13173471bf0Spatrick   auto NextBranch = Branches.begin();
13273471bf0Spatrick   auto EndBranch = Branches.end();
13373471bf0Spatrick 
13473471bf0Spatrick   // Branches with the same source line are enumerated individually
13573471bf0Spatrick   // (BranchIndex) as well as based on True/False pairs (PairIndex).
13673471bf0Spatrick   while (NextBranch != EndBranch) {
13773471bf0Spatrick     unsigned CurrentLine = NextBranch->LineStart;
13873471bf0Spatrick     unsigned PairIndex = 0;
13973471bf0Spatrick     unsigned BranchIndex = 0;
14073471bf0Spatrick 
14173471bf0Spatrick     while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) {
14273471bf0Spatrick       if (!NextBranch->Folded) {
14373471bf0Spatrick         unsigned BC1 = NextBranch->ExecutionCount;
14473471bf0Spatrick         unsigned BC2 = NextBranch->FalseExecutionCount;
14573471bf0Spatrick         bool BranchNotExecuted = (BC1 == 0 && BC2 == 0);
14673471bf0Spatrick 
14773471bf0Spatrick         for (int I = 0; I < 2; I++, BranchIndex++) {
14873471bf0Spatrick           OS << "BRDA:" << CurrentLine << ',' << PairIndex << ','
14973471bf0Spatrick              << BranchIndex;
15073471bf0Spatrick           if (BranchNotExecuted)
15173471bf0Spatrick             OS << ',' << '-' << '\n';
15273471bf0Spatrick           else
15373471bf0Spatrick             OS << ',' << (I == 0 ? BC1 : BC2) << '\n';
15473471bf0Spatrick         }
15573471bf0Spatrick 
15673471bf0Spatrick         PairIndex++;
15773471bf0Spatrick       }
15873471bf0Spatrick       NextBranch++;
15973471bf0Spatrick     }
16073471bf0Spatrick   }
16173471bf0Spatrick }
16273471bf0Spatrick 
renderLineSummary(raw_ostream & OS,const FileCoverageSummary & Summary)16309467b48Spatrick void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
16409467b48Spatrick   OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n'
16509467b48Spatrick      << "LH:" << Summary.LineCoverage.getCovered() << '\n';
16609467b48Spatrick }
16709467b48Spatrick 
renderBranchSummary(raw_ostream & OS,const FileCoverageSummary & Summary)16873471bf0Spatrick void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
16973471bf0Spatrick   OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n'
17073471bf0Spatrick      << "BRH:" << Summary.BranchCoverage.getCovered() << '\n';
17173471bf0Spatrick }
17273471bf0Spatrick 
renderFile(raw_ostream & OS,const coverage::CoverageMapping & Coverage,const std::string & Filename,const FileCoverageSummary & FileReport,bool ExportSummaryOnly,bool SkipFunctions,bool SkipBranches)17309467b48Spatrick void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
17409467b48Spatrick                 const std::string &Filename,
175097a140dSpatrick                 const FileCoverageSummary &FileReport, bool ExportSummaryOnly,
176*d415bd75Srobert                 bool SkipFunctions, bool SkipBranches) {
17709467b48Spatrick   OS << "SF:" << Filename << '\n';
17809467b48Spatrick 
179097a140dSpatrick   if (!ExportSummaryOnly && !SkipFunctions) {
18009467b48Spatrick     renderFunctions(OS, Coverage.getCoveredFunctions(Filename));
18109467b48Spatrick   }
18209467b48Spatrick   renderFunctionSummary(OS, FileReport);
18309467b48Spatrick 
18409467b48Spatrick   if (!ExportSummaryOnly) {
18509467b48Spatrick     // Calculate and render detailed coverage information for given file.
18609467b48Spatrick     auto FileCoverage = Coverage.getCoverageForFile(Filename);
18709467b48Spatrick     renderLineExecutionCounts(OS, FileCoverage);
188*d415bd75Srobert     if (!SkipBranches)
18973471bf0Spatrick       renderBranchExecutionCounts(OS, Coverage, FileCoverage);
19009467b48Spatrick   }
191*d415bd75Srobert   if (!SkipBranches)
19273471bf0Spatrick     renderBranchSummary(OS, FileReport);
19309467b48Spatrick   renderLineSummary(OS, FileReport);
19409467b48Spatrick 
19509467b48Spatrick   OS << "end_of_record\n";
19609467b48Spatrick }
19709467b48Spatrick 
renderFiles(raw_ostream & OS,const coverage::CoverageMapping & Coverage,ArrayRef<std::string> SourceFiles,ArrayRef<FileCoverageSummary> FileReports,bool ExportSummaryOnly,bool SkipFunctions,bool SkipBranches)19809467b48Spatrick void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
19909467b48Spatrick                  ArrayRef<std::string> SourceFiles,
20009467b48Spatrick                  ArrayRef<FileCoverageSummary> FileReports,
201*d415bd75Srobert                  bool ExportSummaryOnly, bool SkipFunctions,
202*d415bd75Srobert                  bool SkipBranches) {
20309467b48Spatrick   for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I)
204097a140dSpatrick     renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly,
205*d415bd75Srobert                SkipFunctions, SkipBranches);
20609467b48Spatrick }
20709467b48Spatrick 
20809467b48Spatrick } // end anonymous namespace
20909467b48Spatrick 
renderRoot(const CoverageFilters & IgnoreFilters)21009467b48Spatrick void CoverageExporterLcov::renderRoot(const CoverageFilters &IgnoreFilters) {
21109467b48Spatrick   std::vector<std::string> SourceFiles;
21209467b48Spatrick   for (StringRef SF : Coverage.getUniqueSourceFiles()) {
21309467b48Spatrick     if (!IgnoreFilters.matchesFilename(SF))
21409467b48Spatrick       SourceFiles.emplace_back(SF);
21509467b48Spatrick   }
21609467b48Spatrick   renderRoot(SourceFiles);
21709467b48Spatrick }
21809467b48Spatrick 
renderRoot(ArrayRef<std::string> SourceFiles)21909467b48Spatrick void CoverageExporterLcov::renderRoot(ArrayRef<std::string> SourceFiles) {
22009467b48Spatrick   FileCoverageSummary Totals = FileCoverageSummary("Totals");
22109467b48Spatrick   auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
22209467b48Spatrick                                                         SourceFiles, Options);
223097a140dSpatrick   renderFiles(OS, Coverage, SourceFiles, FileReports, Options.ExportSummaryOnly,
224*d415bd75Srobert               Options.SkipFunctions, Options.SkipBranches);
22509467b48Spatrick }
226