From 0d7311a78ae801848a0fccf73b16108cd2cd3449 Mon Sep 17 00:00:00 2001 From: stma247 <184293860+stma247@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:45:53 +0100 Subject: [PATCH 1/2] [llvm-cov] Fix branch counts of template functions (#111743) Added option "--unify-instantiations" to llvm-cov export to combine branch execution counts of C++ template instantiations. on-behalf-of: @e-solutions-GmbH --- .../branch-export-lcov-unify-instances.test | 38 +++++ .../tools/llvm-cov/branch-export-lcov.test | 37 ++++- llvm/tools/llvm-cov/CodeCoverage.cpp | 5 + llvm/tools/llvm-cov/CoverageExporterLcov.cpp | 130 +++++++++++++----- llvm/tools/llvm-cov/CoverageViewOptions.h | 1 + 5 files changed, 179 insertions(+), 32 deletions(-) create mode 100644 llvm/test/tools/llvm-cov/branch-export-lcov-unify-instances.test diff --git a/llvm/test/tools/llvm-cov/branch-export-lcov-unify-instances.test b/llvm/test/tools/llvm-cov/branch-export-lcov-unify-instances.test new file mode 100644 index 0000000000000..368024fbe83b8 --- /dev/null +++ b/llvm/test/tools/llvm-cov/branch-export-lcov-unify-instances.test @@ -0,0 +1,38 @@ + +// RUN: llvm-profdata merge %S/Inputs/branch-templates.proftext -o %t.profdata +// RUN: llvm-cov export --format=lcov --unify-instantiations=true %S/Inputs/branch-templates.o32l -instr-profile %t.profdata | FileCheck %s -check-prefix=UNIFY + +// UNIFY-DAG: BRDA:14,0,0,1 +// UNIFY-DAG: BRDA:14,0,1,2 +// UNIFY-DAG: BRDA:30,0,0,1 +// UNIFY-DAG: BRDA:30,0,1,0 +// UNIFY-DAG: BRDA:32,0,0,0 +// UNIFY-DAG: BRDA:32,0,1,1 +// UNIFY-DAG: BRDA:34,0,0,1 +// UNIFY-DAG: BRDA:34,0,1,0 +// UNIFY-NOT: BRDA +// UNIFY: BRF:8 +// UNIFY: BRH:4 +// UNIFY: LF:17 +// UNIFY: LH:13 + +// RUN: llvm-profdata merge %S/Inputs/branch-templates.proftext -o %t.profdata +// RUN: llvm-cov export --format=lcov --unify-instantiations=false %S/Inputs/branch-templates.o32l -instr-profile %t.profdata | FileCheck %s + +// CHECK-DAG: BRDA:14,0,0,0 +// CHECK-DAG: BRDA:14,0,1,1 +// CHECK-DAG: BRDA:14,1,2,1 +// CHECK-DAG: BRDA:14,1,3,0 +// CHECK-DAG: BRDA:14,2,4,0 +// CHECK-DAG: BRDA:14,2,5,1 +// CHECK-DAG: BRDA:30,0,0,1 +// CHECK-DAG: BRDA:30,0,1,0 +// CHECK-DAG: BRDA:32,0,0,0 +// CHECK-DAG: BRDA:32,0,1,1 +// CHECK-DAG: BRDA:34,0,0,1 +// CHECK-DAG: BRDA:34,0,1,0 +// CHECK-NOT: BRDA +// CHECK: BRF:8 +// CHECK: BRH:4 +// CHECK: LF:17 +// CHECK: LH:13 diff --git a/llvm/test/tools/llvm-cov/branch-export-lcov.test b/llvm/test/tools/llvm-cov/branch-export-lcov.test index fe43dd66de8d0..936ba2576121b 100644 --- a/llvm/test/tools/llvm-cov/branch-export-lcov.test +++ b/llvm/test/tools/llvm-cov/branch-export-lcov.test @@ -39,7 +39,7 @@ // Check recursive macro-expansions. // RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata -// RUN: llvm-cov export --format=lcov %S/Inputs/branch-macros.o32l -instr-profile %t.profdata | FileCheck %s -check-prefix=MACROS +// RUN: llvm-cov export --format=lcov --unify-instantiations=false %S/Inputs/branch-macros.o32l -instr-profile %t.profdata | FileCheck %s -check-prefix=MACROS // RUN: llvm-cov export --format=lcov --skip-branches %S/Inputs/branch-macros.o32l -instr-profile %t.profdata | FileCheck %s -check-prefix=NOBRANCH // MACROS-COUNT-4: BRDA:17 @@ -78,3 +78,38 @@ // NOBRANCH-NOT: BRF // NOBRANCH-NOT: BRH +// Check recursive macro-expansions with unify mode. +// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata +// RUN: llvm-cov export --format=lcov --unify-instantiations=true %S/Inputs/branch-macros.o32l -instr-profile %t.profdata | FileCheck %s -check-prefix=MACROS2 + +// MACROS2-COUNT-4: BRDA:17 +// MACROS2-NOT: BRDA:17 + +// MACROS2-COUNT-4: BRDA:19 +// MACROS2-NOT: BRDA:19 + +// MACROS2-COUNT-4: BRDA:21 +// MACROS2-NOT: BRDA:21 + +// MACROS2-COUNT-4: BRDA:23 +// MACROS2-NOT: BRDA:23 + +// MACROS2-COUNT-4: BRDA:25 +// MACROS2-NOT: BRDA:25 + +// MACROS2: BRDA:27,0,0,0 +// MACROS2: BRDA:27,0,1,3 +// MACROS2: BRDA:27,1,2,- +// MACROS2: BRDA:27,1,3,- +// MACROS2: BRDA:27,2,4,- +// MACROS2: BRDA:27,2,5,- +// MACROS2: BRDA:27,3,6,- +// MACROS2: BRDA:27,3,7,- +// MACROS2: BRDA:27,4,8,- +// MACROS2: BRDA:27,4,9,- + +// MACROS2-COUNT-10: BRDA:37 +// MACROS2-NOT: BRDA:37 +// MACROS2-NOT: BRDA +// MACROS2: BRF:40 +// MACROS2: BRH:24 diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index c828e25de4b02..1f2484cd4dda9 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -1283,6 +1283,10 @@ int CodeCoverageTool::doExport(int argc, const char **argv, cl::desc("Don't export branch data (LCOV)"), cl::cat(ExportCategory)); + cl::opt UnifyInstantiations("unify-instantiations", cl::Optional, + cl::desc("Unify function instantiations"), + cl::init(true), cl::cat(ExportCategory)); + auto Err = commandLineParser(argc, argv); if (Err) return Err; @@ -1290,6 +1294,7 @@ int CodeCoverageTool::doExport(int argc, const char **argv, ViewOpts.SkipExpansions = SkipExpansions; ViewOpts.SkipFunctions = SkipFunctions; ViewOpts.SkipBranches = SkipBranches; + ViewOpts.UnifyFunctionInstantiations = UnifyInstantiations; if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text && ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) { diff --git a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp index d6b9367ae4c51..a0803b64bea05 100644 --- a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp @@ -43,9 +43,26 @@ #include "CoverageReport.h" using namespace llvm; +using namespace coverage; namespace { +struct NestedCountedRegion : public coverage::CountedRegion { + // Contains the path to default and expanded branches. + // Size is 1 for default branches and greater 1 for expanded branches. + std::vector NestedPath; + // Indicates whether this item should be ignored at rendering. + bool Ignore = false; + + NestedCountedRegion(llvm::coverage::CountedRegion Region, + std::vector NestedPath) + : llvm::coverage::CountedRegion(std::move(Region)), + NestedPath(std::move(NestedPath)) {} + + // Returns the root line of the branch. + unsigned getEffectiveLine() const { return NestedPath.front().first; } +}; + void renderFunctionSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { OS << "FNF:" << Summary.FunctionCoverage.getNumFunctions() << '\n' @@ -75,58 +92,107 @@ void renderLineExecutionCounts(raw_ostream &OS, } } -std::vector +std::vector collectNestedBranches(const coverage::CoverageMapping &Coverage, ArrayRef Expansions, - int ViewDepth = 0, int SrcLine = 0) { - std::vector Branches; + std::vector &NestedPath) { + std::vector Branches; for (const auto &Expansion : Expansions) { auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); - // If we're at the top level, set the corresponding source line. - if (ViewDepth == 0) - SrcLine = Expansion.Region.LineStart; + // Track the path to the nested expansions. + NestedPath.push_back(Expansion.Region.startLoc()); // Recursively collect branches from nested expansions. auto NestedExpansions = ExpansionCoverage.getExpansions(); - auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions, - ViewDepth + 1, SrcLine); + auto NestedExBranches = + collectNestedBranches(Coverage, NestedExpansions, NestedPath); append_range(Branches, NestedExBranches); // Add branches from this level of expansion. auto ExBranches = ExpansionCoverage.getBranches(); - for (auto B : ExBranches) + for (auto &B : ExBranches) if (B.FileID == Expansion.FileID) { - B.LineStart = SrcLine; - Branches.push_back(B); + Branches.push_back(NestedCountedRegion(B, NestedPath)); } + + NestedPath.pop_back(); } return Branches; } -bool sortLine(llvm::coverage::CountedRegion I, - llvm::coverage::CountedRegion J) { - return (I.LineStart < J.LineStart) || - ((I.LineStart == J.LineStart) && (I.ColumnStart < J.ColumnStart)); +void appendNestedCountedRegions(const std::vector &Src, + std::vector &Dst) { + auto Unfolded = make_filter_range(Src, [](auto &Region) { + return !Region.TrueFolded || !Region.FalseFolded; + }); + Dst.reserve(Dst.size() + Src.size()); + std::transform(Unfolded.begin(), Unfolded.end(), std::back_inserter(Dst), + [=](auto &Region) { + return NestedCountedRegion(Region, {Region.startLoc()}); + }); +} + +void appendNestedCountedRegions(const std::vector &Src, + std::vector &Dst) { + auto Unfolded = make_filter_range(Src, [](auto &NestedRegion) { + return !NestedRegion.TrueFolded || !NestedRegion.FalseFolded; + }); + Dst.reserve(Dst.size() + Src.size()); + std::copy(Unfolded.begin(), Unfolded.end(), std::back_inserter(Dst)); +} + +bool sortNested(const NestedCountedRegion &I, const NestedCountedRegion &J) { + // This sorts each element by line and column. + // Implies that all elements are first sorted by getEffectiveLine(). + return I.NestedPath < J.NestedPath; +} + +void combineInstanceCounts(std::vector &Branches) { + auto NextBranch = Branches.begin(); + auto EndBranch = Branches.end(); + + while (NextBranch != EndBranch) { + auto SumBranch = NextBranch++; + + // Ensure that only branches with the same NestedPath are summed up. + while (NextBranch != EndBranch && + SumBranch->NestedPath == NextBranch->NestedPath) { + SumBranch->ExecutionCount += NextBranch->ExecutionCount; + SumBranch->FalseExecutionCount += NextBranch->FalseExecutionCount; + // Mark this branch as ignored. + NextBranch->Ignore = true; + + NextBranch++; + } + } } void renderBranchExecutionCounts(raw_ostream &OS, const coverage::CoverageMapping &Coverage, - const coverage::CoverageData &FileCoverage) { - std::vector Branches = - FileCoverage.getBranches(); + const coverage::CoverageData &FileCoverage, + bool UnifyInstances) { + + std::vector Branches; + + appendNestedCountedRegions(FileCoverage.getBranches(), Branches); // Recursively collect branches for all file expansions. - std::vector ExBranches = - collectNestedBranches(Coverage, FileCoverage.getExpansions()); + std::vector NestedPath; + std::vector ExBranches = + collectNestedBranches(Coverage, FileCoverage.getExpansions(), NestedPath); // Append Expansion Branches to Source Branches. - append_range(Branches, ExBranches); + appendNestedCountedRegions(ExBranches, Branches); // Sort branches based on line number to ensure branches corresponding to the // same source line are counted together. - llvm::sort(Branches, sortLine); + llvm::sort(Branches, sortNested); + + if (UnifyInstances) { + combineInstanceCounts(Branches); + } auto NextBranch = Branches.begin(); auto EndBranch = Branches.end(); @@ -134,12 +200,13 @@ void renderBranchExecutionCounts(raw_ostream &OS, // Branches with the same source line are enumerated individually // (BranchIndex) as well as based on True/False pairs (PairIndex). while (NextBranch != EndBranch) { - unsigned CurrentLine = NextBranch->LineStart; + unsigned CurrentLine = NextBranch->getEffectiveLine(); unsigned PairIndex = 0; unsigned BranchIndex = 0; - while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) { - if (!NextBranch->TrueFolded || !NextBranch->FalseFolded) { + while (NextBranch != EndBranch && + CurrentLine == NextBranch->getEffectiveLine()) { + if (!NextBranch->Ignore) { unsigned BC1 = NextBranch->ExecutionCount; unsigned BC2 = NextBranch->FalseExecutionCount; bool BranchNotExecuted = (BC1 == 0 && BC2 == 0); @@ -173,7 +240,7 @@ void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, const std::string &Filename, const FileCoverageSummary &FileReport, bool ExportSummaryOnly, - bool SkipFunctions, bool SkipBranches) { + bool SkipFunctions, bool SkipBranches, bool UnifyInstances) { OS << "SF:" << Filename << '\n'; if (!ExportSummaryOnly && !SkipFunctions) { @@ -186,7 +253,7 @@ void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, auto FileCoverage = Coverage.getCoverageForFile(Filename); renderLineExecutionCounts(OS, FileCoverage); if (!SkipBranches) - renderBranchExecutionCounts(OS, Coverage, FileCoverage); + renderBranchExecutionCounts(OS, Coverage, FileCoverage, UnifyInstances); } if (!SkipBranches) renderBranchSummary(OS, FileReport); @@ -198,11 +265,11 @@ void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage, ArrayRef SourceFiles, ArrayRef FileReports, - bool ExportSummaryOnly, bool SkipFunctions, - bool SkipBranches) { + bool ExportSummaryOnly, bool SkipFunctions, bool SkipBranches, + bool UnifyInstances) { for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly, - SkipFunctions, SkipBranches); + SkipFunctions, SkipBranches, UnifyInstances); } } // end anonymous namespace @@ -221,5 +288,6 @@ void CoverageExporterLcov::renderRoot(ArrayRef SourceFiles) { auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals, SourceFiles, Options); renderFiles(OS, Coverage, SourceFiles, FileReports, Options.ExportSummaryOnly, - Options.SkipFunctions, Options.SkipBranches); + Options.SkipFunctions, Options.SkipBranches, + Options.UnifyFunctionInstantiations); } diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h index 81e69c3814e30..1f6ad570f86f2 100644 --- a/llvm/tools/llvm-cov/CoverageViewOptions.h +++ b/llvm/tools/llvm-cov/CoverageViewOptions.h @@ -35,6 +35,7 @@ struct CoverageViewOptions { bool ShowBranchPercents; bool ShowExpandedRegions; bool ShowFunctionInstantiations; + bool UnifyFunctionInstantiations; bool ShowFullFilenames; bool ShowBranchSummary; bool ShowMCDCSummary; From 82382d3c451dcee108fea7100a37ce8bbf8f1bc2 Mon Sep 17 00:00:00 2001 From: stma247 <184293860+stma247@users.noreply.github.com> Date: Wed, 9 Apr 2025 10:38:59 +0200 Subject: [PATCH 2/2] [llvm-cov] Fix flaky test with expensive checks (#113925) Fixed flaky test if EXPENSIVE_CHECKS are enabled, caused by an ambiguous comparator. Keep orginal position in NestedCountedRegion to sort distinct. on-behalf-of: @e-solutions-GmbH --- llvm/tools/llvm-cov/CoverageExporterLcov.cpp | 32 +++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp index a0803b64bea05..96be9693a7047 100644 --- a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp @@ -51,13 +51,16 @@ struct NestedCountedRegion : public coverage::CountedRegion { // Contains the path to default and expanded branches. // Size is 1 for default branches and greater 1 for expanded branches. std::vector NestedPath; + // Contains the original index of this element used to keep the original order + // in case of equal nested path. + unsigned Position; // Indicates whether this item should be ignored at rendering. bool Ignore = false; NestedCountedRegion(llvm::coverage::CountedRegion Region, - std::vector NestedPath) + std::vector NestedPath, unsigned Position) : llvm::coverage::CountedRegion(std::move(Region)), - NestedPath(std::move(NestedPath)) {} + NestedPath(std::move(NestedPath)), Position(Position) {} // Returns the root line of the branch. unsigned getEffectiveLine() const { return NestedPath.front().first; } @@ -95,7 +98,8 @@ void renderLineExecutionCounts(raw_ostream &OS, std::vector collectNestedBranches(const coverage::CoverageMapping &Coverage, ArrayRef Expansions, - std::vector &NestedPath) { + std::vector &NestedPath, + unsigned &PositionCounter) { std::vector Branches; for (const auto &Expansion : Expansions) { auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); @@ -105,15 +109,16 @@ collectNestedBranches(const coverage::CoverageMapping &Coverage, // Recursively collect branches from nested expansions. auto NestedExpansions = ExpansionCoverage.getExpansions(); - auto NestedExBranches = - collectNestedBranches(Coverage, NestedExpansions, NestedPath); + auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions, + NestedPath, PositionCounter); append_range(Branches, NestedExBranches); // Add branches from this level of expansion. auto ExBranches = ExpansionCoverage.getBranches(); for (auto &B : ExBranches) if (B.FileID == Expansion.FileID) { - Branches.push_back(NestedCountedRegion(B, NestedPath)); + Branches.push_back( + NestedCountedRegion(B, NestedPath, PositionCounter++)); } NestedPath.pop_back(); @@ -128,9 +133,11 @@ void appendNestedCountedRegions(const std::vector &Src, return !Region.TrueFolded || !Region.FalseFolded; }); Dst.reserve(Dst.size() + Src.size()); + unsigned PositionCounter = Dst.size(); std::transform(Unfolded.begin(), Unfolded.end(), std::back_inserter(Dst), - [=](auto &Region) { - return NestedCountedRegion(Region, {Region.startLoc()}); + [=, &PositionCounter](auto &Region) { + return NestedCountedRegion(Region, {Region.startLoc()}, + PositionCounter++); }); } @@ -146,7 +153,9 @@ void appendNestedCountedRegions(const std::vector &Src, bool sortNested(const NestedCountedRegion &I, const NestedCountedRegion &J) { // This sorts each element by line and column. // Implies that all elements are first sorted by getEffectiveLine(). - return I.NestedPath < J.NestedPath; + // Use original position if NestedPath is equal. + return std::tie(I.NestedPath, I.Position) < + std::tie(J.NestedPath, J.Position); } void combineInstanceCounts(std::vector &Branches) { @@ -180,8 +189,9 @@ void renderBranchExecutionCounts(raw_ostream &OS, // Recursively collect branches for all file expansions. std::vector NestedPath; - std::vector ExBranches = - collectNestedBranches(Coverage, FileCoverage.getExpansions(), NestedPath); + unsigned PositionCounter = 0; + std::vector ExBranches = collectNestedBranches( + Coverage, FileCoverage.getExpansions(), NestedPath, PositionCounter); // Append Expansion Branches to Source Branches. appendNestedCountedRegions(ExBranches, Branches);