aboutsummaryrefslogtreecommitdiff
path: root/llvm/tools/llvm-cov
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/llvm-cov')
-rw-r--r--llvm/tools/llvm-cov/CodeCoverage.cpp96
-rw-r--r--llvm/tools/llvm-cov/CoverageExporterJson.cpp100
-rw-r--r--llvm/tools/llvm-cov/CoverageExporterLcov.cpp99
-rw-r--r--llvm/tools/llvm-cov/CoverageReport.cpp49
-rw-r--r--llvm/tools/llvm-cov/CoverageSummaryInfo.cpp42
-rw-r--r--llvm/tools/llvm-cov/CoverageSummaryInfo.h49
-rw-r--r--llvm/tools/llvm-cov/CoverageViewOptions.h6
-rw-r--r--llvm/tools/llvm-cov/SourceCoverageView.cpp23
-rw-r--r--llvm/tools/llvm-cov/SourceCoverageView.h28
-rw-r--r--llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp76
-rw-r--r--llvm/tools/llvm-cov/SourceCoverageViewHTML.h3
-rw-r--r--llvm/tools/llvm-cov/SourceCoverageViewText.cpp50
-rw-r--r--llvm/tools/llvm-cov/SourceCoverageViewText.h3
-rw-r--r--llvm/tools/llvm-cov/gcov.cpp26
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,