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