diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2021-02-16 20:13:02 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2021-02-16 20:13:02 +0000 |
commit | b60736ec1405bb0a8dd40989f67ef4c93da068ab (patch) | |
tree | 5c43fbb7c9fc45f0f87e0e6795a86267dbd12f9d /llvm/tools/llvm-cov | |
parent | cfca06d7963fa0909f90483b42a6d7d194d01e08 (diff) | |
download | src-b60736ec1405bb0a8dd40989f67ef4c93da068ab.tar.gz src-b60736ec1405bb0a8dd40989f67ef4c93da068ab.zip |
Vendor import of llvm-project main 8e464dd76bef, the last commit beforevendor/llvm-project/llvmorg-12-init-17869-g8e464dd76bef
the upstream release/12.x branch was created.
Diffstat (limited to 'llvm/tools/llvm-cov')
-rw-r--r-- | llvm/tools/llvm-cov/CodeCoverage.cpp | 96 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/CoverageExporterJson.cpp | 100 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/CoverageExporterLcov.cpp | 99 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/CoverageReport.cpp | 49 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/CoverageSummaryInfo.cpp | 42 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/CoverageSummaryInfo.h | 49 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/CoverageViewOptions.h | 6 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageView.cpp | 23 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageView.h | 28 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp | 76 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageViewHTML.h | 3 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageViewText.cpp | 50 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/SourceCoverageViewText.h | 3 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/gcov.cpp | 26 |
14 files changed, 601 insertions, 49 deletions
diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index b3c895b44a6d..baa968820b63 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -88,6 +88,12 @@ private: ArrayRef<ExpansionRecord> Expansions, const CoverageMapping &Coverage); + /// Create source views for the branches of the view. + void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName, + ArrayRef<CountedRegion> Branches, + const MemoryBuffer &File, + CoverageData &CoverageInfo); + /// Create the source view of a particular function. std::unique_ptr<SourceCoverageView> createFunctionView(const FunctionRecord &Function, @@ -130,6 +136,9 @@ private: CoverageFiltersMatchAll Filters; CoverageFilters IgnoreFilenameFilters; + /// True if InputSourceFiles are provided. + bool HadSourceFiles = false; + /// The path to the indexed profile. std::string PGOFilename; @@ -194,6 +203,7 @@ void CodeCoverageTool::addCollectedPath(const std::string &Path) { sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true); if (!IgnoreFilenameFilters.matchesFilename(EffectivePath)) SourceFiles.emplace_back(EffectivePath.str()); + HadSourceFiles = !SourceFiles.empty(); } void CodeCoverageTool::collectPaths(const std::string &Path) { @@ -264,15 +274,45 @@ void CodeCoverageTool::attachExpansionSubViews( if (!SourceBuffer) continue; + auto SubViewBranches = ExpansionCoverage.getBranches(); auto SubViewExpansions = ExpansionCoverage.getExpansions(); auto SubView = SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage)); attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); + attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches, + SourceBuffer.get(), ExpansionCoverage); View.addExpansion(Expansion.Region, std::move(SubView)); } } +void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View, + StringRef SourceName, + ArrayRef<CountedRegion> Branches, + const MemoryBuffer &File, + CoverageData &CoverageInfo) { + if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents) + return; + + const auto *NextBranch = Branches.begin(); + const auto *EndBranch = Branches.end(); + + // Group branches that have the same line number into the same subview. + while (NextBranch != EndBranch) { + std::vector<CountedRegion> ViewBranches; + unsigned CurrentLine = NextBranch->LineStart; + + while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) + ViewBranches.push_back(*NextBranch++); + + if (!ViewBranches.empty()) { + auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts, + std::move(CoverageInfo)); + View.addBranch(CurrentLine, ViewBranches, std::move(SubView)); + } + } +} + std::unique_ptr<SourceCoverageView> CodeCoverageTool::createFunctionView(const FunctionRecord &Function, const CoverageMapping &Coverage) { @@ -283,11 +323,14 @@ CodeCoverageTool::createFunctionView(const FunctionRecord &Function, if (!SourceBuffer) return nullptr; + auto Branches = FunctionCoverage.getBranches(); auto Expansions = FunctionCoverage.getExpansions(); auto View = SourceCoverageView::create(DC.demangle(Function.Name), SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage)); attachExpansionSubViews(*View, Expansions, Coverage); + attachBranchSubViews(*View, DC.demangle(Function.Name), Branches, + SourceBuffer.get(), FunctionCoverage); return View; } @@ -302,10 +345,13 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile, if (FileCoverage.empty()) return nullptr; + auto Branches = FileCoverage.getBranches(); auto Expansions = FileCoverage.getExpansions(); auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), ViewOpts, std::move(FileCoverage)); attachExpansionSubViews(*View, Expansions, Coverage); + attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(), + FileCoverage); if (!ViewOpts.ShowFunctionInstantiations) return View; @@ -322,9 +368,12 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile, if (Function->ExecutionCount > 0) { auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); auto SubViewExpansions = SubViewCoverage.getExpansions(); + auto SubViewBranches = SubViewCoverage.getBranches(); SubView = SourceCoverageView::create( Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); + attachBranchSubViews(*SubView, SourceFile, SubViewBranches, + SourceBuffer.get(), SubViewCoverage); } unsigned FileID = Function->CountedRegions.front().FileID; @@ -395,6 +444,7 @@ void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { return ""; SmallString<128> NativePath; sys::path::native(Path, NativePath); + sys::path::remove_dots(NativePath, true); if (!sys::path::is_separator(NativePath.back())) NativePath += sys::path::get_separator(); return NativePath.c_str(); @@ -406,6 +456,7 @@ void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { for (StringRef Filename : Coverage.getUniqueSourceFiles()) { SmallString<128> NativeFilename; sys::path::native(Filename, NativeFilename); + sys::path::remove_dots(NativeFilename, true); if (NativeFilename.startswith(RemapFrom)) { RemappedFilenames[Filename] = RemapTo + NativeFilename.substr(RemapFrom.size()).str(); @@ -430,16 +481,11 @@ void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) { std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles(); - auto UncoveredFilesIt = SourceFiles.end(); // The user may have specified source files which aren't in the coverage // mapping. Filter these files away. - UncoveredFilesIt = std::remove_if( - SourceFiles.begin(), SourceFiles.end(), [&](const std::string &SF) { - return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), - SF); - }); - - SourceFiles.erase(UncoveredFilesIt, SourceFiles.end()); + llvm::erase_if(SourceFiles, [&](const std::string &SF) { + return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF); + }); } void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { @@ -544,8 +590,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { cl::Positional, cl::desc("Covered executable or object file.")); cl::list<std::string> CovFilenames( - "object", cl::desc("Coverage executable or object file"), cl::ZeroOrMore, - cl::CommaSeparated); + "object", cl::desc("Coverage executable or object file"), cl::ZeroOrMore); + + cl::opt<bool> DebugDumpCollectedObjects( + "dump-collected-objects", cl::Optional, cl::Hidden, + cl::desc("Show the collected coverage object files")); cl::list<std::string> InputSourceFiles( cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); @@ -641,6 +690,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { cl::desc("Show region statistics in summary table"), cl::init(true)); + cl::opt<bool> BranchSummary( + "show-branch-summary", cl::Optional, + cl::desc("Show branch condition statistics in summary table"), + cl::init(true)); + cl::opt<bool> InstantiationSummary( "show-instantiation-summary", cl::Optional, cl::desc("Show instantiation statistics in summary table")); @@ -668,6 +722,12 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { ::exit(1); } + if (DebugDumpCollectedObjects) { + for (StringRef OF : ObjectFilenames) + outs() << OF << '\n'; + ::exit(0); + } + ViewOpts.Format = Format; switch (ViewOpts.Format) { case CoverageViewOptions::OutputFormat::Text: @@ -778,6 +838,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { ::exit(0); } + ViewOpts.ShowBranchSummary = BranchSummary; ViewOpts.ShowRegionSummary = RegionSummary; ViewOpts.ShowInstantiationSummary = InstantiationSummary; ViewOpts.ExportSummaryOnly = SummaryOnly; @@ -812,6 +873,15 @@ int CodeCoverageTool::doShow(int argc, const char **argv, cl::desc("Show the execution counts for each region"), cl::cat(ViewCategory)); + cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches( + "show-branches", cl::Optional, + cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory), + cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count, + "count", "Show True/False counts"), + clEnumValN(CoverageViewOptions::BranchOutputType::Percent, + "percent", "Show True/False percent")), + cl::init(CoverageViewOptions::BranchOutputType::Off)); + cl::opt<bool> ShowBestLineRegionsCounts( "show-line-counts-or-regions", cl::Optional, cl::desc("Show the execution counts for each line, or the execution " @@ -855,6 +925,10 @@ int CodeCoverageTool::doShow(int argc, const char **argv, !ShowRegions || ShowBestLineRegionsCounts; ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; ViewOpts.ShowExpandedRegions = ShowExpansions; + ViewOpts.ShowBranchCounts = + ShowBranches == CoverageViewOptions::BranchOutputType::Count; + ViewOpts.ShowBranchPercents = + ShowBranches == CoverageViewOptions::BranchOutputType::Percent; ViewOpts.ShowFunctionInstantiations = ShowInstantiations; ViewOpts.ShowOutputDirectory = ShowOutputDirectory; ViewOpts.TabSize = TabSize; @@ -886,7 +960,7 @@ int CodeCoverageTool::doShow(int argc, const char **argv, auto Printer = CoveragePrinter::create(ViewOpts); - if (SourceFiles.empty()) + if (SourceFiles.empty() && !HadSourceFiles) // Get the source files from the function coverage mapping. for (StringRef Filename : Coverage->getUniqueSourceFiles()) { if (!IgnoreFilenameFilters.matchesFilename(Filename)) diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp index c8bb1aa5b6ea..d1446f379f00 100644 --- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -18,6 +18,8 @@ // -- Export: dict => Json representation of one CoverageMapping // -- Files: array => List of objects describing coverage for files // -- File: dict => Coverage for a single file +// -- Branches: array => List of Branches in the file +// -- Branch: dict => Describes a branch of the file with counters // -- Segments: array => List of Segments contained in the file // -- Segment: dict => Describes a segment of the file with a counter // -- Expansions: array => List of expansion records @@ -25,10 +27,13 @@ // -- CountedRegion: dict => The region to be expanded // -- TargetRegions: array => List of Regions in the expansion // -- CountedRegion: dict => Single Region in the expansion +// -- Branches: array => List of Branches in the expansion +// -- Branch: dict => Describes a branch in expansion and counters // -- Summary: dict => Object summarizing the coverage for this file // -- LineCoverage: dict => Object summarizing line coverage // -- FunctionCoverage: dict => Object summarizing function coverage // -- RegionCoverage: dict => Object summarizing region coverage +// -- BranchCoverage: dict => Object summarizing branch coverage // -- Functions: array => List of objects describing coverage for functions // -- Function: dict => Coverage info for a single function // -- Filenames: array => List of filenames that the function relates to @@ -37,6 +42,7 @@ // -- FunctionCoverage: dict => Object summarizing function coverage // -- InstantiationCoverage: dict => Object summarizing inst. coverage // -- RegionCoverage: dict => Object summarizing region coverage +// -- BranchCoverage: dict => Object summarizing branch coverage // //===----------------------------------------------------------------------===// @@ -84,6 +90,14 @@ json::Array renderRegion(const coverage::CountedRegion &Region) { int64_t(Region.Kind)}); } +json::Array renderBranch(const coverage::CountedRegion &Region) { + return json::Array( + {Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd, + clamp_uint64_to_int64(Region.ExecutionCount), + clamp_uint64_to_int64(Region.FalseExecutionCount), Region.FileID, + Region.ExpandedFileID, int64_t(Region.Kind)}); +} + json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) { json::Array RegionArray; for (const auto &Region : Regions) @@ -91,13 +105,49 @@ json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) { return RegionArray; } -json::Object renderExpansion(const coverage::ExpansionRecord &Expansion) { +json::Array renderBranchRegions(ArrayRef<coverage::CountedRegion> Regions) { + json::Array RegionArray; + for (const auto &Region : Regions) + if (!Region.Folded) + RegionArray.push_back(renderBranch(Region)); + return RegionArray; +} + +std::vector<llvm::coverage::CountedRegion> +collectNestedBranches(const coverage::CoverageMapping &Coverage, + ArrayRef<llvm::coverage::ExpansionRecord> Expansions) { + std::vector<llvm::coverage::CountedRegion> Branches; + for (const auto &Expansion : Expansions) { + auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); + + // Recursively collect branches from nested expansions. + auto NestedExpansions = ExpansionCoverage.getExpansions(); + auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions); + Branches.insert(Branches.end(), NestedExBranches.begin(), + NestedExBranches.end()); + + // Add branches from this level of expansion. + auto ExBranches = ExpansionCoverage.getBranches(); + for (auto B : ExBranches) + if (B.FileID == Expansion.FileID) + Branches.push_back(B); + } + + return Branches; +} + +json::Object renderExpansion(const coverage::CoverageMapping &Coverage, + const coverage::ExpansionRecord &Expansion) { + std::vector<llvm::coverage::ExpansionRecord> Expansions = {Expansion}; return json::Object( {{"filenames", json::Array(Expansion.Function.Filenames)}, // Mark the beginning and end of this expansion in the source file. {"source_region", renderRegion(Expansion.Region)}, // Enumerate the coverage information for the expansion. - {"target_regions", renderRegions(Expansion.Function.CountedRegions)}}); + {"target_regions", renderRegions(Expansion.Function.CountedRegions)}, + // Enumerate the branch coverage information for the expansion. + {"branches", + renderBranchRegions(collectNestedBranches(Coverage, Expansions))}}); } json::Object renderSummary(const FileCoverageSummary &Summary) { @@ -123,14 +173,22 @@ json::Object renderSummary(const FileCoverageSummary &Summary) { {"covered", int64_t(Summary.RegionCoverage.getCovered())}, {"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() - Summary.RegionCoverage.getCovered())}, - {"percent", Summary.RegionCoverage.getPercentCovered()}})}}); + {"percent", Summary.RegionCoverage.getPercentCovered()}})}, + {"branches", + json::Object( + {{"count", int64_t(Summary.BranchCoverage.getNumBranches())}, + {"covered", int64_t(Summary.BranchCoverage.getCovered())}, + {"notcovered", int64_t(Summary.BranchCoverage.getNumBranches() - + Summary.BranchCoverage.getCovered())}, + {"percent", Summary.BranchCoverage.getPercentCovered()}})}}); } -json::Array renderFileExpansions(const coverage::CoverageData &FileCoverage, +json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage, + const coverage::CoverageData &FileCoverage, const FileCoverageSummary &FileReport) { json::Array ExpansionArray; for (const auto &Expansion : FileCoverage.getExpansions()) - ExpansionArray.push_back(renderExpansion(Expansion)); + ExpansionArray.push_back(renderExpansion(Coverage, Expansion)); return ExpansionArray; } @@ -142,6 +200,14 @@ json::Array renderFileSegments(const coverage::CoverageData &FileCoverage, return SegmentArray; } +json::Array renderFileBranches(const coverage::CoverageData &FileCoverage, + const FileCoverageSummary &FileReport) { + json::Array BranchArray; + for (const auto &Branch : FileCoverage.getBranches()) + BranchArray.push_back(renderBranch(Branch)); + return BranchArray; +} + json::Object renderFile(const coverage::CoverageMapping &Coverage, const std::string &Filename, const FileCoverageSummary &FileReport, @@ -151,8 +217,10 @@ json::Object renderFile(const coverage::CoverageMapping &Coverage, // Calculate and render detailed coverage information for given file. auto FileCoverage = Coverage.getCoverageForFile(Filename); File["segments"] = renderFileSegments(FileCoverage, FileReport); + File["branches"] = renderFileBranches(FileCoverage, FileReport); if (!Options.SkipExpansions) { - File["expansions"] = renderFileExpansions(FileCoverage, FileReport); + File["expansions"] = + renderFileExpansions(Coverage, FileCoverage, FileReport); } } File["summary"] = renderSummary(FileReport); @@ -197,6 +265,7 @@ json::Array renderFunctions( json::Object({{"name", F.Name}, {"count", clamp_uint64_to_int64(F.ExecutionCount)}, {"regions", renderRegions(F.CountedRegions)}, + {"branches", renderBranchRegions(F.CountedBranchRegions)}, {"filenames", json::Array(F.Filenames)}})); return FunctionArray; } @@ -218,16 +287,15 @@ void CoverageExporterJson::renderRoot(ArrayRef<std::string> SourceFiles) { SourceFiles, Options); auto Files = renderFiles(Coverage, SourceFiles, FileReports, Options); // Sort files in order of their names. - std::sort(Files.begin(), Files.end(), - [](const json::Value &A, const json::Value &B) { - const json::Object *ObjA = A.getAsObject(); - const json::Object *ObjB = B.getAsObject(); - assert(ObjA != nullptr && "Value A was not an Object"); - assert(ObjB != nullptr && "Value B was not an Object"); - const StringRef FilenameA = ObjA->getString("filename").getValue(); - const StringRef FilenameB = ObjB->getString("filename").getValue(); - return FilenameA.compare(FilenameB) < 0; - }); + llvm::sort(Files, [](const json::Value &A, const json::Value &B) { + const json::Object *ObjA = A.getAsObject(); + const json::Object *ObjB = B.getAsObject(); + assert(ObjA != nullptr && "Value A was not an Object"); + assert(ObjB != nullptr && "Value B was not an Object"); + const StringRef FilenameA = ObjA->getString("filename").getValue(); + const StringRef FilenameB = ObjB->getString("filename").getValue(); + return FilenameA.compare(FilenameB) < 0; + }); auto Export = json::Object( {{"files", std::move(Files)}, {"totals", renderSummary(Totals)}}); // Skip functions-level information if necessary. diff --git a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp index a6b3c6607030..99ca037e7b5e 100644 --- a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp @@ -26,8 +26,12 @@ // - "FNH:<number of functions hit>" // - for each instrumented line: // - "DA:<line number>,<execution count>[,<checksum>] +// - for each branch: +// - "BRDA:<line number>,<branch pair id>,<branch id>,<count>" +// - "BRF:<number of branches found>" +// - "BRH:<number of branches hit>" // - "LH:<number of lines with non-zero execution count>" -// - "LF:<nubmer of instrumented lines>" +// - "LF:<number of instrumented lines>" // - "end_of_record" // // If the user is exporting summary information only, then the FN, FNDA, and DA @@ -71,11 +75,102 @@ void renderLineExecutionCounts(raw_ostream &OS, } } +std::vector<llvm::coverage::CountedRegion> +collectNestedBranches(const coverage::CoverageMapping &Coverage, + ArrayRef<llvm::coverage::ExpansionRecord> Expansions, + int ViewDepth = 0, int SrcLine = 0) { + std::vector<llvm::coverage::CountedRegion> 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; + + // Recursively collect branches from nested expansions. + auto NestedExpansions = ExpansionCoverage.getExpansions(); + auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions, + ViewDepth + 1, SrcLine); + Branches.insert(Branches.end(), NestedExBranches.begin(), + NestedExBranches.end()); + + // Add branches from this level of expansion. + auto ExBranches = ExpansionCoverage.getBranches(); + for (auto B : ExBranches) + if (B.FileID == Expansion.FileID) { + B.LineStart = SrcLine; + Branches.push_back(B); + } + } + + 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 renderBranchExecutionCounts(raw_ostream &OS, + const coverage::CoverageMapping &Coverage, + const coverage::CoverageData &FileCoverage) { + std::vector<llvm::coverage::CountedRegion> Branches = + FileCoverage.getBranches(); + + // Recursively collect branches for all file expansions. + std::vector<llvm::coverage::CountedRegion> ExBranches = + collectNestedBranches(Coverage, FileCoverage.getExpansions()); + + // Append Expansion Branches to Source Branches. + Branches.insert(Branches.end(), ExBranches.begin(), ExBranches.end()); + + // Sort branches based on line number to ensure branches corresponding to the + // same source line are counted together. + llvm::sort(Branches, sortLine); + + auto NextBranch = Branches.begin(); + auto EndBranch = Branches.end(); + + // 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 PairIndex = 0; + unsigned BranchIndex = 0; + + while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) { + if (!NextBranch->Folded) { + unsigned BC1 = NextBranch->ExecutionCount; + unsigned BC2 = NextBranch->FalseExecutionCount; + bool BranchNotExecuted = (BC1 == 0 && BC2 == 0); + + for (int I = 0; I < 2; I++, BranchIndex++) { + OS << "BRDA:" << CurrentLine << ',' << PairIndex << ',' + << BranchIndex; + if (BranchNotExecuted) + OS << ',' << '-' << '\n'; + else + OS << ',' << (I == 0 ? BC1 : BC2) << '\n'; + } + + PairIndex++; + } + NextBranch++; + } + } +} + void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n' << "LH:" << Summary.LineCoverage.getCovered() << '\n'; } +void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { + OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n' + << "BFH:" << Summary.BranchCoverage.getCovered() << '\n'; +} + void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, const std::string &Filename, const FileCoverageSummary &FileReport, bool ExportSummaryOnly, @@ -91,7 +186,9 @@ void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, // Calculate and render detailed coverage information for given file. auto FileCoverage = Coverage.getCoverageForFile(Filename); renderLineExecutionCounts(OS, FileCoverage); + renderBranchExecutionCounts(OS, Coverage, FileCoverage); } + renderBranchSummary(OS, FileReport); renderLineSummary(OS, FileReport); OS << "end_of_record\n"; diff --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp index 8509710032d1..2c08f5309494 100644 --- a/llvm/tools/llvm-cov/CoverageReport.cpp +++ b/llvm/tools/llvm-cov/CoverageReport.cpp @@ -86,9 +86,9 @@ Column column(StringRef Str, unsigned Width, const T &Value) { } // Specify the default column widths. -size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, - 16, 16, 10, 12, 18, 10}; -size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8}; +size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, 16, + 16, 10, 12, 18, 10, 12, 18, 10}; +size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8}; /// Adjust column widths to fit long file paths and function names. void adjustColumnWidths(ArrayRef<StringRef> Files, @@ -239,6 +239,23 @@ void CoverageReport::render(const FileCoverageSummary &File, << '%'; else OS << column("-", FileReportColumns[12], Column::RightAlignment); + + if (Options.ShowBranchSummary) { + OS << format("%*u", FileReportColumns[13], + (unsigned)File.BranchCoverage.getNumBranches()); + Options.colored_ostream(OS, LineCoverageColor) + << format("%*u", FileReportColumns[14], + (unsigned)(File.BranchCoverage.getNumBranches() - + File.BranchCoverage.getCovered())); + if (File.BranchCoverage.getNumBranches()) + Options.colored_ostream(OS, LineCoverageColor) + << format("%*.2f", FileReportColumns[15] - 1, + File.BranchCoverage.getPercentCovered()) + << '%'; + else + OS << column("-", FileReportColumns[15], Column::RightAlignment); + } + OS << "\n"; } @@ -273,6 +290,19 @@ void CoverageReport::render(const FunctionCoverageSummary &Function, << format("%*.2f", FunctionReportColumns[6] - 1, Function.LineCoverage.getPercentCovered()) << '%'; + if (Options.ShowBranchSummary) { + OS << format("%*u", FunctionReportColumns[7], + (unsigned)Function.BranchCoverage.getNumBranches()); + Options.colored_ostream(OS, LineCoverageColor) + << format("%*u", FunctionReportColumns[8], + (unsigned)(Function.BranchCoverage.getNumBranches() - + Function.BranchCoverage.getCovered())); + Options.colored_ostream( + OS, determineCoveragePercentageColor(Function.BranchCoverage)) + << format("%*.2f", FunctionReportColumns[9] - 1, + Function.BranchCoverage.getPercentCovered()) + << '%'; + } OS << "\n"; } @@ -301,6 +331,10 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files, << column("Lines", FunctionReportColumns[4], Column::RightAlignment) << column("Miss", FunctionReportColumns[5], Column::RightAlignment) << column("Cover", FunctionReportColumns[6], Column::RightAlignment); + if (Options.ShowBranchSummary) + OS << column("Branches", FunctionReportColumns[7], Column::RightAlignment) + << column("Miss", FunctionReportColumns[8], Column::RightAlignment) + << column("Cover", FunctionReportColumns[9], Column::RightAlignment); OS << "\n"; renderDivider(FunctionReportColumns, OS); OS << "\n"; @@ -310,6 +344,7 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files, ++Totals.ExecutionCount; Totals.RegionCoverage += Function.RegionCoverage; Totals.LineCoverage += Function.LineCoverage; + Totals.BranchCoverage += Function.BranchCoverage; render(Function, DC, OS); } if (Totals.ExecutionCount) { @@ -420,7 +455,13 @@ void CoverageReport::renderFileReports( << column("Executed", FileReportColumns[9], Column::RightAlignment); OS << column("Lines", FileReportColumns[10], Column::RightAlignment) << column("Missed Lines", FileReportColumns[11], Column::RightAlignment) - << column("Cover", FileReportColumns[12], Column::RightAlignment) << "\n"; + << column("Cover", FileReportColumns[12], Column::RightAlignment); + if (Options.ShowBranchSummary) + OS << column("Branches", FileReportColumns[13], Column::RightAlignment) + << column("Missed Branches", FileReportColumns[14], + Column::RightAlignment) + << column("Cover", FileReportColumns[15], Column::RightAlignment); + OS << "\n"; renderDivider(FileReportColumns, OS); OS << "\n"; diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp index 929529c27b6e..4a0a86168908 100644 --- a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp +++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp @@ -16,6 +16,34 @@ using namespace llvm; using namespace coverage; +static void sumBranches(size_t &NumBranches, size_t &CoveredBranches, + const ArrayRef<CountedRegion> &Branches) { + for (const auto &BR : Branches) { + // Skip folded branches. + if (BR.Folded) + continue; + + // "True" Condition Branches. + ++NumBranches; + if (BR.ExecutionCount > 0) + ++CoveredBranches; + // "False" Condition Branches. + ++NumBranches; + if (BR.FalseExecutionCount > 0) + ++CoveredBranches; + } +} + +static void sumBranchExpansions(size_t &NumBranches, size_t &CoveredBranches, + const CoverageMapping &CM, + ArrayRef<ExpansionRecord> Expansions) { + for (const auto &Expansion : Expansions) { + auto CE = CM.getCoverageForExpansion(Expansion); + sumBranches(NumBranches, CoveredBranches, CE.getBranches()); + sumBranchExpansions(NumBranches, CoveredBranches, CM, CE.getExpansions()); + } +} + FunctionCoverageSummary FunctionCoverageSummary::get(const CoverageMapping &CM, const coverage::FunctionRecord &Function) { @@ -40,10 +68,16 @@ FunctionCoverageSummary::get(const CoverageMapping &CM, ++CoveredLines; } + // Compute the branch coverage, including branches from expansions. + size_t NumBranches = 0, CoveredBranches = 0; + sumBranches(NumBranches, CoveredBranches, CD.getBranches()); + sumBranchExpansions(NumBranches, CoveredBranches, CM, CD.getExpansions()); + return FunctionCoverageSummary( Function.Name, Function.ExecutionCount, RegionCoverageInfo(CoveredRegions, NumCodeRegions), - LineCoverageInfo(CoveredLines, NumLines)); + LineCoverageInfo(CoveredLines, NumLines), + BranchCoverageInfo(CoveredBranches, NumBranches)); } FunctionCoverageSummary @@ -62,9 +96,15 @@ FunctionCoverageSummary::get(const InstantiationGroup &Group, Summary.ExecutionCount = Group.getTotalExecutionCount(); Summary.RegionCoverage = Summaries[0].RegionCoverage; Summary.LineCoverage = Summaries[0].LineCoverage; + Summary.BranchCoverage = Summaries[0].BranchCoverage; for (const auto &FCS : Summaries.drop_front()) { Summary.RegionCoverage.merge(FCS.RegionCoverage); Summary.LineCoverage.merge(FCS.LineCoverage); + + // Sum branch coverage across instantiation groups for the summary rather + // than "merge" the maximum count. This is a clearer view into whether all + // created branches are covered. + Summary.BranchCoverage += FCS.BranchCoverage; } return Summary; } diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.h b/llvm/tools/llvm-cov/CoverageSummaryInfo.h index 97beacb26d07..4bc1c24a079f 100644 --- a/llvm/tools/llvm-cov/CoverageSummaryInfo.h +++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.h @@ -101,6 +101,42 @@ public: } }; +/// Provides information about branches coverage for a function/file. +class BranchCoverageInfo { + /// The number of branches that were executed at least once. + size_t Covered; + + /// The total number of branches in a function/file. + size_t NumBranches; + +public: + BranchCoverageInfo() : Covered(0), NumBranches(0) {} + + BranchCoverageInfo(size_t Covered, size_t NumBranches) + : Covered(Covered), NumBranches(NumBranches) { + assert(Covered <= NumBranches && "Covered branches over-counted"); + } + + BranchCoverageInfo &operator+=(const BranchCoverageInfo &RHS) { + Covered += RHS.Covered; + NumBranches += RHS.NumBranches; + return *this; + } + + size_t getCovered() const { return Covered; } + + size_t getNumBranches() const { return NumBranches; } + + bool isFullyCovered() const { return Covered == NumBranches; } + + double getPercentCovered() const { + assert(Covered <= NumBranches && "Covered branches over-counted"); + if (NumBranches == 0) + return 0.0; + return double(Covered) / double(NumBranches) * 100.0; + } +}; + /// Provides information about function coverage for a file. class FunctionCoverageInfo { /// The number of functions that were executed. @@ -147,15 +183,19 @@ struct FunctionCoverageSummary { uint64_t ExecutionCount; RegionCoverageInfo RegionCoverage; LineCoverageInfo LineCoverage; + BranchCoverageInfo BranchCoverage; FunctionCoverageSummary(const std::string &Name) - : Name(Name), ExecutionCount(0), RegionCoverage(), LineCoverage() {} + : Name(Name), ExecutionCount(0), RegionCoverage(), LineCoverage(), + BranchCoverage() {} FunctionCoverageSummary(const std::string &Name, uint64_t ExecutionCount, const RegionCoverageInfo &RegionCoverage, - const LineCoverageInfo &LineCoverage) + const LineCoverageInfo &LineCoverage, + const BranchCoverageInfo &BranchCoverage) : Name(Name), ExecutionCount(ExecutionCount), - RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {} + RegionCoverage(RegionCoverage), LineCoverage(LineCoverage), + BranchCoverage(BranchCoverage) {} /// Compute the code coverage summary for the given function coverage /// mapping record. @@ -174,6 +214,7 @@ struct FileCoverageSummary { StringRef Name; RegionCoverageInfo RegionCoverage; LineCoverageInfo LineCoverage; + BranchCoverageInfo BranchCoverage; FunctionCoverageInfo FunctionCoverage; FunctionCoverageInfo InstantiationCoverage; @@ -185,6 +226,7 @@ struct FileCoverageSummary { RegionCoverage += RHS.RegionCoverage; LineCoverage += RHS.LineCoverage; FunctionCoverage += RHS.FunctionCoverage; + BranchCoverage += RHS.BranchCoverage; InstantiationCoverage += RHS.InstantiationCoverage; return *this; } @@ -192,6 +234,7 @@ struct FileCoverageSummary { void addFunction(const FunctionCoverageSummary &Function) { RegionCoverage += Function.RegionCoverage; LineCoverage += Function.LineCoverage; + BranchCoverage += Function.BranchCoverage; FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0); } diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h index dde0c692ab05..eee4ba74e165 100644 --- a/llvm/tools/llvm-cov/CoverageViewOptions.h +++ b/llvm/tools/llvm-cov/CoverageViewOptions.h @@ -23,20 +23,26 @@ struct CoverageViewOptions { Lcov }; + enum class BranchOutputType { Count, Percent, Off }; + bool Debug; bool Colors; bool ShowLineNumbers; bool ShowLineStats; bool ShowRegionMarkers; + bool ShowBranchCounts; + bool ShowBranchPercents; bool ShowExpandedRegions; bool ShowFunctionInstantiations; bool ShowFullFilenames; + bool ShowBranchSummary; bool ShowRegionSummary; bool ShowInstantiationSummary; bool ExportSummaryOnly; bool SkipExpansions; bool SkipFunctions; OutputFormat Format; + BranchOutputType ShowBranches; std::string ShowOutputDirectory; std::vector<std::string> DemanglerOpts; uint32_t TabSize; diff --git a/llvm/tools/llvm-cov/SourceCoverageView.cpp b/llvm/tools/llvm-cov/SourceCoverageView.cpp index cd7395a1a87d..aca58a07f250 100644 --- a/llvm/tools/llvm-cov/SourceCoverageView.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageView.cpp @@ -132,7 +132,8 @@ bool SourceCoverageView::shouldRenderRegionMarkers( } bool SourceCoverageView::hasSubViews() const { - return !ExpansionSubViews.empty() || !InstantiationSubViews.empty(); + return !ExpansionSubViews.empty() || !InstantiationSubViews.empty() || + !BranchSubViews.empty(); } std::unique_ptr<SourceCoverageView> @@ -167,6 +168,12 @@ void SourceCoverageView::addExpansion( ExpansionSubViews.emplace_back(Region, std::move(View)); } +void SourceCoverageView::addBranch(unsigned Line, + ArrayRef<CountedRegion> Regions, + std::unique_ptr<SourceCoverageView> View) { + BranchSubViews.emplace_back(Line, Regions, std::move(View)); +} + void SourceCoverageView::addInstantiation( StringRef FunctionName, unsigned Line, std::unique_ptr<SourceCoverageView> View) { @@ -187,14 +194,17 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, renderTableHeader(OS, (ViewDepth > 0) ? 0 : getFirstUncoveredLineNo(), ViewDepth); - // We need the expansions and instantiations sorted so we can go through them - // while we iterate lines. + // We need the expansions, instantiations, and branches sorted so we can go + // through them while we iterate lines. llvm::stable_sort(ExpansionSubViews); llvm::stable_sort(InstantiationSubViews); + llvm::stable_sort(BranchSubViews); auto NextESV = ExpansionSubViews.begin(); auto EndESV = ExpansionSubViews.end(); auto NextISV = InstantiationSubViews.begin(); auto EndISV = InstantiationSubViews.end(); + auto NextBRV = BranchSubViews.begin(); + auto EndBRV = BranchSubViews.end(); // Get the coverage information for the file. auto StartSegment = CoverageInfo.begin(); @@ -234,7 +244,7 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, if (shouldRenderRegionMarkers(*LCI)) renderRegionMarkers(OS, *LCI, ViewDepth); - // Show the expansions and instantiations for this line. + // Show the expansions, instantiations, and branches for this line. bool RenderedSubView = false; for (; NextESV != EndESV && NextESV->getLine() == LI.line_number(); ++NextESV) { @@ -257,6 +267,11 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, renderInstantiationView(OS, *NextISV, ViewDepth + 1); RenderedSubView = true; } + for (; NextBRV != EndBRV && NextBRV->Line == LI.line_number(); ++NextBRV) { + renderViewDivider(OS, ViewDepth + 1); + renderBranchView(OS, *NextBRV, ViewDepth + 1); + RenderedSubView = true; + } if (RenderedSubView) renderViewDivider(OS, ViewDepth + 1); renderLineSuffix(OS, ViewDepth); diff --git a/llvm/tools/llvm-cov/SourceCoverageView.h b/llvm/tools/llvm-cov/SourceCoverageView.h index 9ae928443651..5a9fcdd15761 100644 --- a/llvm/tools/llvm-cov/SourceCoverageView.h +++ b/llvm/tools/llvm-cov/SourceCoverageView.h @@ -67,6 +67,23 @@ struct InstantiationView { } }; +/// A view that represents one or more branch regions on a given source line. +struct BranchView { + std::vector<CountedRegion> Regions; + std::unique_ptr<SourceCoverageView> View; + unsigned Line; + + BranchView(unsigned Line, ArrayRef<CountedRegion> Regions, + std::unique_ptr<SourceCoverageView> View) + : Regions(Regions), View(std::move(View)), Line(Line) {} + + unsigned getLine() const { return Line; } + + friend bool operator<(const BranchView &LHS, const BranchView &RHS) { + return LHS.Line < RHS.Line; + } +}; + /// A file manager that handles format-aware file creation. class CoveragePrinter { public: @@ -140,6 +157,9 @@ class SourceCoverageView { /// A container for all expansions (e.g macros) in the source on display. std::vector<ExpansionView> ExpansionSubViews; + /// A container for all branches in the source on display. + std::vector<BranchView> BranchSubViews; + /// A container for all instantiations (e.g template functions) in the source /// on display. std::vector<InstantiationView> InstantiationSubViews; @@ -209,6 +229,10 @@ protected: virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) = 0; + /// Render a branch view and any nested views. + virtual void renderBranchView(raw_ostream &OS, BranchView &BRV, + unsigned ViewDepth) = 0; + /// Render \p Title, a project title if one is available, and the /// created time. virtual void renderTitle(raw_ostream &OS, StringRef CellText) = 0; @@ -255,6 +279,10 @@ public: void addInstantiation(StringRef FunctionName, unsigned Line, std::unique_ptr<SourceCoverageView> View); + /// Add a branch subview to this view. + void addBranch(unsigned Line, ArrayRef<CountedRegion> Regions, + std::unique_ptr<SourceCoverageView> View); + /// Print the code coverage information for a specific portion of a /// source file to the output stream. void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName, diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp index 9d10def0a211..7ab1d6608ccf 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -248,7 +248,7 @@ const char *ReportTitleTag = "h2"; const char *CreatedTimeTag = "h4"; std::string getPathToStyle(StringRef ViewPath) { - std::string PathToStyle = ""; + std::string PathToStyle; std::string PathSep = std::string(sys::path::get_separator()); unsigned NumSeps = ViewPath.count(PathSep); for (unsigned I = 0, E = NumSeps; I < E; ++I) @@ -313,6 +313,8 @@ static void emitColumnLabelsForIndex(raw_ostream &OS, Columns.emplace_back(tag("td", "Line Coverage", "column-entry-bold")); if (Opts.ShowRegionSummary) Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold")); + if (Opts.ShowBranchSummary) + Columns.emplace_back(tag("td", "Branch Coverage", "column-entry-bold")); OS << tag("tr", join(Columns.begin(), Columns.end(), "")); } @@ -378,6 +380,10 @@ void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF, AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(), FCS.RegionCoverage.getNumRegions(), FCS.RegionCoverage.getPercentCovered()); + if (Opts.ShowBranchSummary) + AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(), + FCS.BranchCoverage.getNumBranches(), + FCS.BranchCoverage.getPercentCovered()); if (IsTotals) OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold"); @@ -611,7 +617,7 @@ void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L, void SourceCoverageViewHTML::renderLineCoverageColumn( raw_ostream &OS, const LineCoverageStats &Line) { - std::string Count = ""; + std::string Count; if (Line.isMapped()) Count = tag("pre", formatCount(Line.getExecutionCount())); std::string CoverageClass = @@ -650,6 +656,72 @@ void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS, OS << EndExpansionDiv; } +void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV, + unsigned ViewDepth) { + // Render the child subview. + if (getOptions().Debug) + errs() << "Branch at line " << BRV.getLine() << '\n'; + + OS << BeginExpansionDiv; + OS << BeginPre; + for (const auto &R : BRV.Regions) { + // Calculate TruePercent and False Percent. + double TruePercent = 0.0; + double FalsePercent = 0.0; + unsigned Total = R.ExecutionCount + R.FalseExecutionCount; + + if (!getOptions().ShowBranchCounts && Total != 0) { + TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0; + FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0; + } + + // Display Line + Column. + std::string LineNoStr = utostr(uint64_t(R.LineStart)); + std::string ColNoStr = utostr(uint64_t(R.ColumnStart)); + std::string TargetName = "L" + LineNoStr; + + OS << " Branch ("; + OS << tag("span", + a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr), + TargetName), + "line-number") + + "): ["; + + if (R.Folded) { + OS << "Folded - Ignored]\n"; + continue; + } + + // Display TrueCount or TruePercent. + std::string TrueColor = R.ExecutionCount ? "None" : "red"; + std::string TrueCovClass = + (R.ExecutionCount > 0) ? "covered-line" : "uncovered-line"; + + OS << tag("span", "True", TrueColor); + OS << ": "; + if (getOptions().ShowBranchCounts) + OS << tag("span", formatCount(R.ExecutionCount), TrueCovClass) << ", "; + else + OS << format("%0.2f", TruePercent) << "%, "; + + // Display FalseCount or FalsePercent. + std::string FalseColor = R.FalseExecutionCount ? "None" : "red"; + std::string FalseCovClass = + (R.FalseExecutionCount > 0) ? "covered-line" : "uncovered-line"; + + OS << tag("span", "False", FalseColor); + OS << ": "; + if (getOptions().ShowBranchCounts) + OS << tag("span", formatCount(R.FalseExecutionCount), FalseCovClass); + else + OS << format("%0.2f", FalsePercent) << "%"; + + OS << "]\n"; + } + OS << EndPre; + OS << EndExpansionDiv; +} + void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) { diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.h b/llvm/tools/llvm-cov/SourceCoverageViewHTML.h index 9834040008a6..7d94675f4b0b 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.h +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.h @@ -68,6 +68,9 @@ class SourceCoverageViewHTML : public SourceCoverageView { void renderExpansionView(raw_ostream &OS, ExpansionView &ESV, unsigned ViewDepth) override; + void renderBranchView(raw_ostream &OS, BranchView &BRV, + unsigned ViewDepth) override; + void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) override; diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp index fcabee2ee69d..948414a4f995 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp @@ -10,11 +10,12 @@ /// //===----------------------------------------------------------------------===// -#include "CoverageReport.h" #include "SourceCoverageViewText.h" +#include "CoverageReport.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Format.h" using namespace llvm; @@ -222,6 +223,53 @@ void SourceCoverageViewText::renderExpansionView(raw_ostream &OS, /*ShowTitle=*/false, ViewDepth + 1); } +void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV, + unsigned ViewDepth) { + // Render the child subview. + if (getOptions().Debug) + errs() << "Branch at line " << BRV.getLine() << '\n'; + + for (const auto &R : BRV.Regions) { + double TruePercent = 0.0; + double FalsePercent = 0.0; + unsigned Total = R.ExecutionCount + R.FalseExecutionCount; + + if (!getOptions().ShowBranchCounts && Total != 0) { + TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0; + FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0; + } + + renderLinePrefix(OS, ViewDepth); + OS << " Branch (" << R.LineStart << ":" << R.ColumnStart << "): ["; + + if (R.Folded) { + OS << "Folded - Ignored]\n"; + continue; + } + + colored_ostream(OS, raw_ostream::RED, + getOptions().Colors && !R.ExecutionCount, + /*Bold=*/false, /*BG=*/true) + << "True"; + + if (getOptions().ShowBranchCounts) + OS << ": " << formatCount(R.ExecutionCount) << ", "; + else + OS << ": " << format("%0.2f", TruePercent) << "%, "; + + colored_ostream(OS, raw_ostream::RED, + getOptions().Colors && !R.FalseExecutionCount, + /*Bold=*/false, /*BG=*/true) + << "False"; + + if (getOptions().ShowBranchCounts) + OS << ": " << formatCount(R.FalseExecutionCount); + else + OS << ": " << format("%0.2f", FalsePercent) << "%"; + OS << "]\n"; + } +} + void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) { diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.h b/llvm/tools/llvm-cov/SourceCoverageViewText.h index c8c4632c3b9d..b2be06039f95 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewText.h +++ b/llvm/tools/llvm-cov/SourceCoverageViewText.h @@ -59,6 +59,9 @@ class SourceCoverageViewText : public SourceCoverageView { void renderExpansionView(raw_ostream &OS, ExpansionView &ESV, unsigned ViewDepth) override; + void renderBranchView(raw_ostream &OS, BranchView &BRV, + unsigned ViewDepth) override; + void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) override; diff --git a/llvm/tools/llvm-cov/gcov.cpp b/llvm/tools/llvm-cov/gcov.cpp index 7a1dbbfe9338..d42e7cd3b551 100644 --- a/llvm/tools/llvm-cov/gcov.cpp +++ b/llvm/tools/llvm-cov/gcov.cpp @@ -43,8 +43,10 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, : InputGCDA; GCOVFile GF; + // Open .gcda and .gcda without requiring a NUL terminator. The concurrent + // modification may nullify the NUL terminator condition. ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff = - MemoryBuffer::getFileOrSTDIN(GCNO); + MemoryBuffer::getFileOrSTDIN(GCNO, -1, /*RequiresNullTerminator=*/false); if (std::error_code EC = GCNO_Buff.getError()) { errs() << GCNO << ": " << EC.message() << "\n"; return; @@ -56,7 +58,7 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, } ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff = - MemoryBuffer::getFileOrSTDIN(GCDA); + MemoryBuffer::getFileOrSTDIN(GCDA, -1, /*RequiresNullTerminator=*/false); if (std::error_code EC = GCDA_Buff.getError()) { if (EC != errc::no_such_file_or_directory) { errs() << GCDA << ": " << EC.message() << "\n"; @@ -75,9 +77,7 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, if (DumpGCOV) GF.print(errs()); - FileInfo FI(Options); - GF.collectLineCounts(FI); - FI.print(llvm::outs(), SourceFile, GCNO, GCDA, GF); + gcovOneInput(Options, SourceFile, GCNO, GCDA, GF); } int gcovMain(int argc, const char *argv[]) { @@ -115,6 +115,11 @@ int gcovMain(int argc, const char *argv[]) { cl::Grouping, cl::NotHidden, cl::aliasopt(Intermediate)); + cl::opt<bool> Demangle("demangled-names", cl::init(false), + cl::desc("Demangle function names")); + cl::alias DemangleA("m", cl::desc("Alias for --demangled-names"), + cl::Grouping, cl::NotHidden, cl::aliasopt(Demangle)); + cl::opt<bool> NoOutput("n", cl::Grouping, cl::init(false), cl::desc("Do not output any .gcov files")); cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput)); @@ -129,6 +134,14 @@ int gcovMain(int argc, const char *argv[]) { cl::desc("Preserve path components")); cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths)); + cl::opt<bool> RelativeOnly( + "r", cl::Grouping, + cl::desc("Only dump files with relative paths or absolute paths with the " + "prefix specified by -s")); + cl::alias RelativeOnlyA("relative-only", cl::aliasopt(RelativeOnly)); + cl::opt<std::string> SourcePrefix("s", cl::desc("Source prefix to elide")); + cl::alias SourcePrefixA("source-prefix", cl::aliasopt(SourcePrefix)); + cl::opt<bool> UseStdout("t", cl::Grouping, cl::init(false), cl::desc("Print to stdout")); cl::alias UseStdoutA("stdout", cl::aliasopt(UseStdout)); @@ -155,7 +168,8 @@ int gcovMain(int argc, const char *argv[]) { GCOV::Options Options(AllBlocks, BranchProb, BranchCount, FuncSummary, PreservePaths, UncondBranch, Intermediate, LongNames, - NoOutput, UseStdout, HashFilenames); + Demangle, NoOutput, RelativeOnly, UseStdout, + HashFilenames, SourcePrefix); for (const auto &SourceFile : SourceFiles) reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV, |