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 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 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 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 164 void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { 165 OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n' 166 << "LH:" << Summary.LineCoverage.getCovered() << '\n'; 167 } 168 169 void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { 170 OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n' 171 << "BFH:" << Summary.BranchCoverage.getCovered() << '\n'; 172 } 173 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 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 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 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