Path: blob/main/contrib/llvm-project/llvm/tools/llvm-cov/CoverageExporterLcov.cpp
35231 views
//===- CoverageExporterLcov.cpp - Code coverage export --------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements export of code coverage data to lcov trace file format.9//10//===----------------------------------------------------------------------===//1112//===----------------------------------------------------------------------===//13//14// The trace file code coverage export follows the following format (see also15// https://linux.die.net/man/1/geninfo). Each quoted string appears on its own16// line; the indentation shown here is only for documentation purposes.17//18// - for each source file:19// - "SF:<absolute path to source file>"20// - for each function:21// - "FN:<line number of function start>,<function name>"22// - for each function:23// - "FNDA:<execution count>,<function name>"24// - "FNF:<number of functions found>"25// - "FNH:<number of functions hit>"26// - for each instrumented line:27// - "DA:<line number>,<execution count>[,<checksum>]28// - for each branch:29// - "BRDA:<line number>,<branch pair id>,<branch id>,<count>"30// - "BRF:<number of branches found>"31// - "BRH:<number of branches hit>"32// - "LH:<number of lines with non-zero execution count>"33// - "LF:<number of instrumented lines>"34// - "end_of_record"35//36// If the user is exporting summary information only, then the FN, FNDA, and DA37// lines will not be present.38//39//===----------------------------------------------------------------------===//4041#include "CoverageExporterLcov.h"42#include "CoverageReport.h"4344using namespace llvm;4546namespace {4748void renderFunctionSummary(raw_ostream &OS,49const FileCoverageSummary &Summary) {50OS << "FNF:" << Summary.FunctionCoverage.getNumFunctions() << '\n'51<< "FNH:" << Summary.FunctionCoverage.getExecuted() << '\n';52}5354void renderFunctions(55raw_ostream &OS,56const iterator_range<coverage::FunctionRecordIterator> &Functions) {57for (const auto &F : Functions) {58auto StartLine = F.CountedRegions.front().LineStart;59OS << "FN:" << StartLine << ',' << F.Name << '\n';60}61for (const auto &F : Functions)62OS << "FNDA:" << F.ExecutionCount << ',' << F.Name << '\n';63}6465void renderLineExecutionCounts(raw_ostream &OS,66const coverage::CoverageData &FileCoverage) {67coverage::LineCoverageIterator LCI{FileCoverage, 1};68coverage::LineCoverageIterator LCIEnd = LCI.getEnd();69for (; LCI != LCIEnd; ++LCI) {70const coverage::LineCoverageStats &LCS = *LCI;71if (LCS.isMapped()) {72OS << "DA:" << LCS.getLine() << ',' << LCS.getExecutionCount() << '\n';73}74}75}7677std::vector<llvm::coverage::CountedRegion>78collectNestedBranches(const coverage::CoverageMapping &Coverage,79ArrayRef<llvm::coverage::ExpansionRecord> Expansions,80int ViewDepth = 0, int SrcLine = 0) {81std::vector<llvm::coverage::CountedRegion> Branches;82for (const auto &Expansion : Expansions) {83auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);8485// If we're at the top level, set the corresponding source line.86if (ViewDepth == 0)87SrcLine = Expansion.Region.LineStart;8889// Recursively collect branches from nested expansions.90auto NestedExpansions = ExpansionCoverage.getExpansions();91auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions,92ViewDepth + 1, SrcLine);93append_range(Branches, NestedExBranches);9495// Add branches from this level of expansion.96auto ExBranches = ExpansionCoverage.getBranches();97for (auto B : ExBranches)98if (B.FileID == Expansion.FileID) {99B.LineStart = SrcLine;100Branches.push_back(B);101}102}103104return Branches;105}106107bool sortLine(llvm::coverage::CountedRegion I,108llvm::coverage::CountedRegion J) {109return (I.LineStart < J.LineStart) ||110((I.LineStart == J.LineStart) && (I.ColumnStart < J.ColumnStart));111}112113void renderBranchExecutionCounts(raw_ostream &OS,114const coverage::CoverageMapping &Coverage,115const coverage::CoverageData &FileCoverage) {116std::vector<llvm::coverage::CountedRegion> Branches =117FileCoverage.getBranches();118119// Recursively collect branches for all file expansions.120std::vector<llvm::coverage::CountedRegion> ExBranches =121collectNestedBranches(Coverage, FileCoverage.getExpansions());122123// Append Expansion Branches to Source Branches.124append_range(Branches, ExBranches);125126// Sort branches based on line number to ensure branches corresponding to the127// same source line are counted together.128llvm::sort(Branches, sortLine);129130auto NextBranch = Branches.begin();131auto EndBranch = Branches.end();132133// Branches with the same source line are enumerated individually134// (BranchIndex) as well as based on True/False pairs (PairIndex).135while (NextBranch != EndBranch) {136unsigned CurrentLine = NextBranch->LineStart;137unsigned PairIndex = 0;138unsigned BranchIndex = 0;139140while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) {141if (!NextBranch->Folded) {142unsigned BC1 = NextBranch->ExecutionCount;143unsigned BC2 = NextBranch->FalseExecutionCount;144bool BranchNotExecuted = (BC1 == 0 && BC2 == 0);145146for (int I = 0; I < 2; I++, BranchIndex++) {147OS << "BRDA:" << CurrentLine << ',' << PairIndex << ','148<< BranchIndex;149if (BranchNotExecuted)150OS << ',' << '-' << '\n';151else152OS << ',' << (I == 0 ? BC1 : BC2) << '\n';153}154155PairIndex++;156}157NextBranch++;158}159}160}161162void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {163OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n'164<< "LH:" << Summary.LineCoverage.getCovered() << '\n';165}166167void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {168OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n'169<< "BRH:" << Summary.BranchCoverage.getCovered() << '\n';170}171172void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,173const std::string &Filename,174const FileCoverageSummary &FileReport, bool ExportSummaryOnly,175bool SkipFunctions, bool SkipBranches) {176OS << "SF:" << Filename << '\n';177178if (!ExportSummaryOnly && !SkipFunctions) {179renderFunctions(OS, Coverage.getCoveredFunctions(Filename));180}181renderFunctionSummary(OS, FileReport);182183if (!ExportSummaryOnly) {184// Calculate and render detailed coverage information for given file.185auto FileCoverage = Coverage.getCoverageForFile(Filename);186renderLineExecutionCounts(OS, FileCoverage);187if (!SkipBranches)188renderBranchExecutionCounts(OS, Coverage, FileCoverage);189}190if (!SkipBranches)191renderBranchSummary(OS, FileReport);192renderLineSummary(OS, FileReport);193194OS << "end_of_record\n";195}196197void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage,198ArrayRef<std::string> SourceFiles,199ArrayRef<FileCoverageSummary> FileReports,200bool ExportSummaryOnly, bool SkipFunctions,201bool SkipBranches) {202for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I)203renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly,204SkipFunctions, SkipBranches);205}206207} // end anonymous namespace208209void CoverageExporterLcov::renderRoot(const CoverageFilters &IgnoreFilters) {210std::vector<std::string> SourceFiles;211for (StringRef SF : Coverage.getUniqueSourceFiles()) {212if (!IgnoreFilters.matchesFilename(SF))213SourceFiles.emplace_back(SF);214}215renderRoot(SourceFiles);216}217218void CoverageExporterLcov::renderRoot(ArrayRef<std::string> SourceFiles) {219FileCoverageSummary Totals = FileCoverageSummary("Totals");220auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,221SourceFiles, Options);222renderFiles(OS, Coverage, SourceFiles, FileReports, Options.ExportSummaryOnly,223Options.SkipFunctions, Options.SkipBranches);224}225226227