aboutsummaryrefslogtreecommitdiffstats
path: root/tools/llvm-cov
diff options
context:
space:
mode:
authorStephen Hines <srhines@google.com>2014-12-01 14:51:49 -0800
committerStephen Hines <srhines@google.com>2014-12-02 16:08:10 -0800
commit37ed9c199ca639565f6ce88105f9e39e898d82d0 (patch)
tree8fb36d3910e3ee4c4e1b7422f4f017108efc52f5 /tools/llvm-cov
parentd2327b22152ced7bc46dc629fc908959e8a52d03 (diff)
downloadexternal_llvm-37ed9c199ca639565f6ce88105f9e39e898d82d0.zip
external_llvm-37ed9c199ca639565f6ce88105f9e39e898d82d0.tar.gz
external_llvm-37ed9c199ca639565f6ce88105f9e39e898d82d0.tar.bz2
Update aosp/master LLVM for rebase to r222494.
Change-Id: Ic787f5e0124df789bd26f3f24680f45e678eef2d
Diffstat (limited to 'tools/llvm-cov')
-rw-r--r--tools/llvm-cov/Android.mk15
-rw-r--r--tools/llvm-cov/CMakeLists.txt10
-rw-r--r--tools/llvm-cov/CodeCoverage.cpp484
-rw-r--r--tools/llvm-cov/CoverageFilters.cpp59
-rw-r--r--tools/llvm-cov/CoverageFilters.h127
-rw-r--r--tools/llvm-cov/CoverageReport.cpp202
-rw-r--r--tools/llvm-cov/CoverageReport.h40
-rw-r--r--tools/llvm-cov/CoverageSummary.cpp64
-rw-r--r--tools/llvm-cov/CoverageSummary.h45
-rw-r--r--tools/llvm-cov/CoverageSummaryInfo.cpp96
-rw-r--r--tools/llvm-cov/CoverageSummaryInfo.h133
-rw-r--r--tools/llvm-cov/CoverageViewOptions.h36
-rw-r--r--tools/llvm-cov/LLVMBuild.txt2
-rw-r--r--tools/llvm-cov/Makefile2
-rw-r--r--tools/llvm-cov/RenderingSupport.h60
-rw-r--r--tools/llvm-cov/SourceCoverageView.cpp260
-rw-r--r--tools/llvm-cov/SourceCoverageView.h162
-rw-r--r--tools/llvm-cov/TestingSupport.cpp91
-rw-r--r--tools/llvm-cov/gcov.cpp153
-rw-r--r--tools/llvm-cov/llvm-cov.cpp178
20 files changed, 2090 insertions, 129 deletions
diff --git a/tools/llvm-cov/Android.mk b/tools/llvm-cov/Android.mk
index dae211f..d76c940 100644
--- a/tools/llvm-cov/Android.mk
+++ b/tools/llvm-cov/Android.mk
@@ -8,9 +8,22 @@ LLVM_ROOT_PATH := $(LOCAL_PATH)/../..
#===---------------------------------------------------------------===
llvm_cov_SRC_FILES := \
- llvm-cov.cpp
+ CodeCoverage.cpp \
+ CoverageFilters.cpp \
+ CoverageReport.cpp \
+ CoverageSummary.cpp \
+ CoverageSummaryInfo.cpp \
+ gcov.cpp \
+ llvm-cov.cpp \
+ SourceCoverageView.cpp \
+ TestingSupport.cpp
llvm_cov_STATIC_LIBRARIES := \
+ libLLVMObject \
+ libLLVMProfileData \
+ libLLVMMC \
+ libLLVMMCParser \
+ libLLVMBitReader \
libLLVMCore \
libLLVMSupport \
diff --git a/tools/llvm-cov/CMakeLists.txt b/tools/llvm-cov/CMakeLists.txt
index 67cea71..b2d2b89 100644
--- a/tools/llvm-cov/CMakeLists.txt
+++ b/tools/llvm-cov/CMakeLists.txt
@@ -1,5 +1,13 @@
-set(LLVM_LINK_COMPONENTS core support )
+set(LLVM_LINK_COMPONENTS core support object profiledata)
add_llvm_tool(llvm-cov
llvm-cov.cpp
+ gcov.cpp
+ CodeCoverage.cpp
+ CoverageFilters.cpp
+ CoverageReport.cpp
+ CoverageSummary.cpp
+ CoverageSummaryInfo.cpp
+ SourceCoverageView.cpp
+ TestingSupport.cpp
)
diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp
new file mode 100644
index 0000000..093525e
--- /dev/null
+++ b/tools/llvm-cov/CodeCoverage.cpp
@@ -0,0 +1,484 @@
+//===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// The 'CodeCoverageTool' class implements a command line tool to analyze and
+// report coverage information using the profiling instrumentation and code
+// coverage mapping.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RenderingSupport.h"
+#include "CoverageViewOptions.h"
+#include "CoverageFilters.h"
+#include "SourceCoverageView.h"
+#include "CoverageSummary.h"
+#include "CoverageReport.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ProfileData/InstrProfReader.h"
+#include "llvm/ProfileData/CoverageMapping.h"
+#include "llvm/ProfileData/CoverageMappingReader.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryObject.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include <functional>
+#include <system_error>
+
+using namespace llvm;
+using namespace coverage;
+
+namespace {
+/// \brief The implementation of the coverage tool.
+class CodeCoverageTool {
+public:
+ enum Command {
+ /// \brief The show command.
+ Show,
+ /// \brief The report command.
+ Report
+ };
+
+ /// \brief Print the error message to the error output stream.
+ void error(const Twine &Message, StringRef Whence = "");
+
+ /// \brief Return a memory buffer for the given source file.
+ ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
+
+ /// \brief Create source views for the expansions of the view.
+ void attachExpansionSubViews(SourceCoverageView &View,
+ ArrayRef<ExpansionRecord> Expansions,
+ CoverageMapping &Coverage);
+
+ /// \brief Create the source view of a particular function.
+ std::unique_ptr<SourceCoverageView>
+ createFunctionView(const FunctionRecord &Function, CoverageMapping &Coverage);
+
+ /// \brief Create the main source view of a particular source file.
+ std::unique_ptr<SourceCoverageView>
+ createSourceFileView(StringRef SourceFile, CoverageMapping &Coverage);
+
+ /// \brief Load the coverage mapping data. Return true if an error occured.
+ std::unique_ptr<CoverageMapping> load();
+
+ int run(Command Cmd, int argc, const char **argv);
+
+ typedef std::function<int(int, const char **)> CommandLineParserType;
+
+ int show(int argc, const char **argv,
+ CommandLineParserType commandLineParser);
+
+ int report(int argc, const char **argv,
+ CommandLineParserType commandLineParser);
+
+ std::string ObjectFilename;
+ CoverageViewOptions ViewOpts;
+ std::string PGOFilename;
+ CoverageFiltersMatchAll Filters;
+ std::vector<std::string> SourceFiles;
+ std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
+ LoadedSourceFiles;
+ bool CompareFilenamesOnly;
+ StringMap<std::string> RemappedFilenames;
+};
+}
+
+void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
+ errs() << "error: ";
+ if (!Whence.empty())
+ errs() << Whence << ": ";
+ errs() << Message << "\n";
+}
+
+ErrorOr<const MemoryBuffer &>
+CodeCoverageTool::getSourceFile(StringRef SourceFile) {
+ // If we've remapped filenames, look up the real location for this file.
+ if (!RemappedFilenames.empty()) {
+ auto Loc = RemappedFilenames.find(SourceFile);
+ if (Loc != RemappedFilenames.end())
+ SourceFile = Loc->second;
+ }
+ for (const auto &Files : LoadedSourceFiles)
+ if (sys::fs::equivalent(SourceFile, Files.first))
+ return *Files.second;
+ auto Buffer = MemoryBuffer::getFile(SourceFile);
+ if (auto EC = Buffer.getError()) {
+ error(EC.message(), SourceFile);
+ return EC;
+ }
+ LoadedSourceFiles.push_back(
+ std::make_pair(SourceFile, std::move(Buffer.get())));
+ return *LoadedSourceFiles.back().second;
+}
+
+void
+CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View,
+ ArrayRef<ExpansionRecord> Expansions,
+ CoverageMapping &Coverage) {
+ if (!ViewOpts.ShowExpandedRegions)
+ return;
+ for (const auto &Expansion : Expansions) {
+ auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
+ if (ExpansionCoverage.empty())
+ continue;
+ auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
+ if (!SourceBuffer)
+ continue;
+
+ auto SubViewExpansions = ExpansionCoverage.getExpansions();
+ auto SubView = llvm::make_unique<SourceCoverageView>(
+ SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage));
+ attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
+ View.addExpansion(Expansion.Region, std::move(SubView));
+ }
+}
+
+std::unique_ptr<SourceCoverageView>
+CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
+ CoverageMapping &Coverage) {
+ auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
+ if (FunctionCoverage.empty())
+ return nullptr;
+ auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
+ if (!SourceBuffer)
+ return nullptr;
+
+ auto Expansions = FunctionCoverage.getExpansions();
+ auto View = llvm::make_unique<SourceCoverageView>(
+ SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage));
+ attachExpansionSubViews(*View, Expansions, Coverage);
+
+ return View;
+}
+
+std::unique_ptr<SourceCoverageView>
+CodeCoverageTool::createSourceFileView(StringRef SourceFile,
+ CoverageMapping &Coverage) {
+ auto SourceBuffer = getSourceFile(SourceFile);
+ if (!SourceBuffer)
+ return nullptr;
+ auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
+ if (FileCoverage.empty())
+ return nullptr;
+
+ auto Expansions = FileCoverage.getExpansions();
+ auto View = llvm::make_unique<SourceCoverageView>(
+ SourceBuffer.get(), ViewOpts, std::move(FileCoverage));
+ attachExpansionSubViews(*View, Expansions, Coverage);
+
+ for (auto Function : Coverage.getInstantiations(SourceFile)) {
+ auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
+ auto SubViewExpansions = SubViewCoverage.getExpansions();
+ auto SubView = llvm::make_unique<SourceCoverageView>(
+ SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
+ attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
+
+ if (SubView) {
+ unsigned FileID = Function->CountedRegions.front().FileID;
+ unsigned Line = 0;
+ for (const auto &CR : Function->CountedRegions)
+ if (CR.FileID == FileID)
+ Line = std::max(CR.LineEnd, Line);
+ View->addInstantiation(Function->Name, Line, std::move(SubView));
+ }
+ }
+ return View;
+}
+
+std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
+ auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename);
+ if (std::error_code EC = CoverageOrErr.getError()) {
+ colored_ostream(errs(), raw_ostream::RED)
+ << "error: Failed to load coverage: " << EC.message();
+ errs() << "\n";
+ return nullptr;
+ }
+ auto Coverage = std::move(CoverageOrErr.get());
+ unsigned Mismatched = Coverage->getMismatchedCount();
+ if (Mismatched) {
+ colored_ostream(errs(), raw_ostream::RED)
+ << "warning: " << Mismatched << " functions have mismatched data. ";
+ errs() << "\n";
+ }
+
+ if (CompareFilenamesOnly) {
+ auto CoveredFiles = Coverage.get()->getUniqueSourceFiles();
+ for (auto &SF : SourceFiles) {
+ StringRef SFBase = sys::path::filename(SF);
+ for (const auto &CF : CoveredFiles)
+ if (SFBase == sys::path::filename(CF)) {
+ RemappedFilenames[CF] = SF;
+ SF = CF;
+ break;
+ }
+ }
+ }
+
+ return Coverage;
+}
+
+int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
+ // Print a stack trace if we signal out.
+ sys::PrintStackTraceOnErrorSignal();
+ PrettyStackTraceProgram X(argc, argv);
+ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+
+ cl::opt<std::string, true> ObjectFilename(
+ cl::Positional, cl::Required, cl::location(this->ObjectFilename),
+ cl::desc("Covered executable or object file."));
+
+ cl::list<std::string> InputSourceFiles(
+ cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
+
+ cl::opt<std::string, true> PGOFilename(
+ "instr-profile", cl::Required, cl::location(this->PGOFilename),
+ cl::desc(
+ "File with the profile data obtained after an instrumented run"));
+
+ cl::opt<bool> DebugDump("dump", cl::Optional,
+ cl::desc("Show internal debug dump"));
+
+ cl::opt<bool> FilenameEquivalence(
+ "filename-equivalence", cl::Optional,
+ cl::desc("Treat source files as equivalent to paths in the coverage data "
+ "when the file names match, even if the full paths do not"));
+
+ cl::OptionCategory FilteringCategory("Function filtering options");
+
+ cl::list<std::string> NameFilters(
+ "name", cl::Optional,
+ cl::desc("Show code coverage only for functions with the given name"),
+ cl::ZeroOrMore, cl::cat(FilteringCategory));
+
+ cl::list<std::string> NameRegexFilters(
+ "name-regex", cl::Optional,
+ cl::desc("Show code coverage only for functions that match the given "
+ "regular expression"),
+ cl::ZeroOrMore, cl::cat(FilteringCategory));
+
+ cl::opt<double> RegionCoverageLtFilter(
+ "region-coverage-lt", cl::Optional,
+ cl::desc("Show code coverage only for functions with region coverage "
+ "less than the given threshold"),
+ cl::cat(FilteringCategory));
+
+ cl::opt<double> RegionCoverageGtFilter(
+ "region-coverage-gt", cl::Optional,
+ cl::desc("Show code coverage only for functions with region coverage "
+ "greater than the given threshold"),
+ cl::cat(FilteringCategory));
+
+ cl::opt<double> LineCoverageLtFilter(
+ "line-coverage-lt", cl::Optional,
+ cl::desc("Show code coverage only for functions with line coverage less "
+ "than the given threshold"),
+ cl::cat(FilteringCategory));
+
+ cl::opt<double> LineCoverageGtFilter(
+ "line-coverage-gt", cl::Optional,
+ cl::desc("Show code coverage only for functions with line coverage "
+ "greater than the given threshold"),
+ cl::cat(FilteringCategory));
+
+ auto commandLineParser = [&, this](int argc, const char **argv) -> int {
+ cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
+ ViewOpts.Debug = DebugDump;
+ CompareFilenamesOnly = FilenameEquivalence;
+
+ // Create the function filters
+ if (!NameFilters.empty() || !NameRegexFilters.empty()) {
+ auto NameFilterer = new CoverageFilters;
+ for (const auto &Name : NameFilters)
+ NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name));
+ for (const auto &Regex : NameRegexFilters)
+ NameFilterer->push_back(
+ llvm::make_unique<NameRegexCoverageFilter>(Regex));
+ Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer));
+ }
+ if (RegionCoverageLtFilter.getNumOccurrences() ||
+ RegionCoverageGtFilter.getNumOccurrences() ||
+ LineCoverageLtFilter.getNumOccurrences() ||
+ LineCoverageGtFilter.getNumOccurrences()) {
+ auto StatFilterer = new CoverageFilters;
+ if (RegionCoverageLtFilter.getNumOccurrences())
+ StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
+ RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
+ if (RegionCoverageGtFilter.getNumOccurrences())
+ StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
+ RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
+ if (LineCoverageLtFilter.getNumOccurrences())
+ StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
+ LineCoverageFilter::LessThan, LineCoverageLtFilter));
+ if (LineCoverageGtFilter.getNumOccurrences())
+ StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
+ RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
+ Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer));
+ }
+
+ for (const auto &File : InputSourceFiles) {
+ SmallString<128> Path(File);
+ if (std::error_code EC = sys::fs::make_absolute(Path)) {
+ errs() << "error: " << File << ": " << EC.message();
+ return 1;
+ }
+ SourceFiles.push_back(Path.str());
+ }
+ return 0;
+ };
+
+ switch (Cmd) {
+ case Show:
+ return show(argc, argv, commandLineParser);
+ case Report:
+ return report(argc, argv, commandLineParser);
+ }
+ return 0;
+}
+
+int CodeCoverageTool::show(int argc, const char **argv,
+ CommandLineParserType commandLineParser) {
+
+ cl::OptionCategory ViewCategory("Viewing options");
+
+ cl::opt<bool> ShowLineExecutionCounts(
+ "show-line-counts", cl::Optional,
+ cl::desc("Show the execution counts for each line"), cl::init(true),
+ cl::cat(ViewCategory));
+
+ cl::opt<bool> ShowRegions(
+ "show-regions", cl::Optional,
+ cl::desc("Show the execution counts for each region"),
+ cl::cat(ViewCategory));
+
+ cl::opt<bool> ShowBestLineRegionsCounts(
+ "show-line-counts-or-regions", cl::Optional,
+ cl::desc("Show the execution counts for each line, or the execution "
+ "counts for each region on lines that have multiple regions"),
+ cl::cat(ViewCategory));
+
+ cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
+ cl::desc("Show expanded source regions"),
+ cl::cat(ViewCategory));
+
+ cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
+ cl::desc("Show function instantiations"),
+ cl::cat(ViewCategory));
+
+ cl::opt<bool> NoColors("no-colors", cl::Optional,
+ cl::desc("Don't show text colors"), cl::init(false),
+ cl::cat(ViewCategory));
+
+ auto Err = commandLineParser(argc, argv);
+ if (Err)
+ return Err;
+
+ ViewOpts.Colors = !NoColors;
+ ViewOpts.ShowLineNumbers = true;
+ ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
+ !ShowRegions || ShowBestLineRegionsCounts;
+ ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
+ ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
+ ViewOpts.ShowExpandedRegions = ShowExpansions;
+ ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
+
+ auto Coverage = load();
+ if (!Coverage)
+ return 1;
+
+ if (!Filters.empty()) {
+ // Show functions
+ for (const auto &Function : Coverage->getCoveredFunctions()) {
+ if (!Filters.matches(Function))
+ continue;
+
+ auto mainView = createFunctionView(Function, *Coverage);
+ if (!mainView) {
+ ViewOpts.colored_ostream(outs(), raw_ostream::RED)
+ << "warning: Could not read coverage for '" << Function.Name;
+ outs() << "\n";
+ continue;
+ }
+ ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << Function.Name
+ << ":";
+ outs() << "\n";
+ mainView->render(outs(), /*WholeFile=*/false);
+ outs() << "\n";
+ }
+ return 0;
+ }
+
+ // Show files
+ bool ShowFilenames = SourceFiles.size() != 1;
+
+ if (SourceFiles.empty())
+ // Get the source files from the function coverage mapping
+ for (StringRef Filename : Coverage->getUniqueSourceFiles())
+ SourceFiles.push_back(Filename);
+
+ for (const auto &SourceFile : SourceFiles) {
+ auto mainView = createSourceFileView(SourceFile, *Coverage);
+ if (!mainView) {
+ ViewOpts.colored_ostream(outs(), raw_ostream::RED)
+ << "warning: The file '" << SourceFile << "' isn't covered.";
+ outs() << "\n";
+ continue;
+ }
+
+ if (ShowFilenames) {
+ ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
+ outs() << "\n";
+ }
+ mainView->render(outs(), /*Wholefile=*/true);
+ if (SourceFiles.size() > 1)
+ outs() << "\n";
+ }
+
+ return 0;
+}
+
+int CodeCoverageTool::report(int argc, const char **argv,
+ CommandLineParserType commandLineParser) {
+ cl::opt<bool> NoColors("no-colors", cl::Optional,
+ cl::desc("Don't show text colors"), cl::init(false));
+
+ auto Err = commandLineParser(argc, argv);
+ if (Err)
+ return Err;
+
+ ViewOpts.Colors = !NoColors;
+
+ auto Coverage = load();
+ if (!Coverage)
+ return 1;
+
+ CoverageSummary Summarizer;
+ Summarizer.createSummaries(*Coverage);
+ CoverageReport Report(ViewOpts, Summarizer);
+ if (SourceFiles.empty() && Filters.empty()) {
+ Report.renderFileReports(llvm::outs());
+ return 0;
+ }
+
+ Report.renderFunctionReports(llvm::outs());
+ return 0;
+}
+
+int showMain(int argc, const char *argv[]) {
+ CodeCoverageTool Tool;
+ return Tool.run(CodeCoverageTool::Show, argc, argv);
+}
+
+int reportMain(int argc, const char *argv[]) {
+ CodeCoverageTool Tool;
+ return Tool.run(CodeCoverageTool::Report, argc, argv);
+}
diff --git a/tools/llvm-cov/CoverageFilters.cpp b/tools/llvm-cov/CoverageFilters.cpp
new file mode 100644
index 0000000..325dd72
--- /dev/null
+++ b/tools/llvm-cov/CoverageFilters.cpp
@@ -0,0 +1,59 @@
+//===- CoverageFilters.cpp - Function coverage mapping filters ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// These classes provide filtering for function coverage mapping records.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageFilters.h"
+#include "CoverageSummaryInfo.h"
+#include "llvm/Support/Regex.h"
+
+using namespace llvm;
+
+bool NameCoverageFilter::matches(const coverage::FunctionRecord &Function) {
+ StringRef FuncName = Function.Name;
+ return FuncName.find(Name) != StringRef::npos;
+}
+
+bool
+NameRegexCoverageFilter::matches(const coverage::FunctionRecord &Function) {
+ return llvm::Regex(Regex).match(Function.Name);
+}
+
+bool RegionCoverageFilter::matches(const coverage::FunctionRecord &Function) {
+ return PassesThreshold(FunctionCoverageSummary::get(Function)
+ .RegionCoverage.getPercentCovered());
+}
+
+bool LineCoverageFilter::matches(const coverage::FunctionRecord &Function) {
+ return PassesThreshold(
+ FunctionCoverageSummary::get(Function).LineCoverage.getPercentCovered());
+}
+
+void CoverageFilters::push_back(std::unique_ptr<CoverageFilter> Filter) {
+ Filters.push_back(std::move(Filter));
+}
+
+bool CoverageFilters::matches(const coverage::FunctionRecord &Function) {
+ for (const auto &Filter : Filters) {
+ if (Filter->matches(Function))
+ return true;
+ }
+ return false;
+}
+
+bool
+CoverageFiltersMatchAll::matches(const coverage::FunctionRecord &Function) {
+ for (const auto &Filter : Filters) {
+ if (!Filter->matches(Function))
+ return false;
+ }
+ return true;
+}
diff --git a/tools/llvm-cov/CoverageFilters.h b/tools/llvm-cov/CoverageFilters.h
new file mode 100644
index 0000000..e543005
--- /dev/null
+++ b/tools/llvm-cov/CoverageFilters.h
@@ -0,0 +1,127 @@
+//===- CoverageFilters.h - Function coverage mapping filters --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// These classes provide filtering for function coverage mapping records.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEFILTERS_H
+#define LLVM_COV_COVERAGEFILTERS_H
+
+#include "llvm/ProfileData/CoverageMapping.h"
+#include <vector>
+#include <memory>
+
+namespace llvm {
+
+/// \brief Matches specific functions that pass the requirement of this filter.
+class CoverageFilter {
+public:
+ virtual ~CoverageFilter() {}
+
+ /// \brief Return true if the function passes the requirements of this filter.
+ virtual bool matches(const coverage::FunctionRecord &Function) {
+ return true;
+ }
+};
+
+/// \brief Matches functions that contain a specific string in their name.
+class NameCoverageFilter : public CoverageFilter {
+ StringRef Name;
+
+public:
+ NameCoverageFilter(StringRef Name) : Name(Name) {}
+
+ bool matches(const coverage::FunctionRecord &Function) override;
+};
+
+/// \brief Matches functions whose name matches a certain regular expression.
+class NameRegexCoverageFilter : public CoverageFilter {
+ StringRef Regex;
+
+public:
+ NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {}
+
+ bool matches(const coverage::FunctionRecord &Function) override;
+};
+
+/// \brief Matches numbers that pass a certain threshold.
+template <typename T> class StatisticThresholdFilter {
+public:
+ enum Operation { LessThan, GreaterThan };
+
+protected:
+ Operation Op;
+ T Threshold;
+
+ StatisticThresholdFilter(Operation Op, T Threshold)
+ : Op(Op), Threshold(Threshold) {}
+
+ /// \brief Return true if the given number is less than
+ /// or greater than the certain threshold.
+ bool PassesThreshold(T Value) const {
+ switch (Op) {
+ case LessThan:
+ return Value < Threshold;
+ case GreaterThan:
+ return Value > Threshold;
+ }
+ return false;
+ }
+};
+
+/// \brief Matches functions whose region coverage percentage
+/// is above/below a certain percentage.
+class RegionCoverageFilter : public CoverageFilter,
+ public StatisticThresholdFilter<double> {
+public:
+ RegionCoverageFilter(Operation Op, double Threshold)
+ : StatisticThresholdFilter(Op, Threshold) {}
+
+ bool matches(const coverage::FunctionRecord &Function) override;
+};
+
+/// \brief Matches functions whose line coverage percentage
+/// is above/below a certain percentage.
+class LineCoverageFilter : public CoverageFilter,
+ public StatisticThresholdFilter<double> {
+public:
+ LineCoverageFilter(Operation Op, double Threshold)
+ : StatisticThresholdFilter(Op, Threshold) {}
+
+ bool matches(const coverage::FunctionRecord &Function) override;
+};
+
+/// \brief A collection of filters.
+/// Matches functions that match any filters contained
+/// in an instance of this class.
+class CoverageFilters : public CoverageFilter {
+protected:
+ std::vector<std::unique_ptr<CoverageFilter>> Filters;
+
+public:
+ /// \brief Append a filter to this collection.
+ void push_back(std::unique_ptr<CoverageFilter> Filter);
+
+ bool empty() const { return Filters.empty(); }
+
+ bool matches(const coverage::FunctionRecord &Function) override;
+};
+
+/// \brief A collection of filters.
+/// Matches functions that match all of the filters contained
+/// in an instance of this class.
+class CoverageFiltersMatchAll : public CoverageFilters {
+public:
+ bool matches(const coverage::FunctionRecord &Function) override;
+};
+
+} // namespace llvm
+
+#endif // LLVM_COV_COVERAGEFILTERS_H
diff --git a/tools/llvm-cov/CoverageReport.cpp b/tools/llvm-cov/CoverageReport.cpp
new file mode 100644
index 0000000..7ac9355
--- /dev/null
+++ b/tools/llvm-cov/CoverageReport.cpp
@@ -0,0 +1,202 @@
+//===- CoverageReport.cpp - Code coverage report -------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements rendering of a code coverage report.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageReport.h"
+#include "CoverageSummary.h"
+#include "RenderingSupport.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace llvm;
+namespace {
+/// \brief Helper struct which prints trimmed and aligned columns.
+struct Column {
+ enum TrimKind { NoTrim, LeftTrim, RightTrim };
+
+ enum AlignmentKind { LeftAlignment, RightAlignment };
+
+ StringRef Str;
+ unsigned Width;
+ TrimKind Trim;
+ AlignmentKind Alignment;
+
+ Column(StringRef Str, unsigned Width)
+ : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {}
+
+ Column &set(TrimKind Value) {
+ Trim = Value;
+ return *this;
+ }
+
+ Column &set(AlignmentKind Value) {
+ Alignment = Value;
+ return *this;
+ }
+
+ void render(raw_ostream &OS) const;
+};
+raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {
+ Value.render(OS);
+ return OS;
+}
+}
+
+void Column::render(raw_ostream &OS) const {
+ if (Str.size() <= Width) {
+ if (Alignment == RightAlignment) {
+ OS.indent(Width - Str.size());
+ OS << Str;
+ return;
+ }
+ OS << Str;
+ OS.indent(Width - Str.size());
+ return;
+ }
+
+ switch (Trim) {
+ case NoTrim:
+ OS << Str.substr(0, Width);
+ break;
+ case LeftTrim:
+ OS << "..." << Str.substr(Str.size() - Width + 3);
+ break;
+ case RightTrim:
+ OS << Str.substr(0, Width - 3) << "...";
+ break;
+ }
+}
+
+static Column column(StringRef Str, unsigned Width) {
+ return Column(Str, Width);
+}
+
+template <typename T>
+static Column column(StringRef Str, unsigned Width, const T &Value) {
+ return Column(Str, Width).set(Value);
+}
+
+static const unsigned FileReportColumns[] = {25, 10, 8, 8, 10, 10};
+static const unsigned FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};
+
+/// \brief Prints a horizontal divider which spans across the given columns.
+template <typename T, size_t N>
+static void renderDivider(T (&Columns)[N], raw_ostream &OS) {
+ unsigned Length = 0;
+ for (unsigned I = 0; I < N; ++I)
+ Length += Columns[I];
+ for (unsigned I = 0; I < Length; ++I)
+ OS << '-';
+}
+
+/// \brief Return the color which correponds to the coverage
+/// percentage of a certain metric.
+template <typename T>
+static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {
+ if (Info.isFullyCovered())
+ return raw_ostream::GREEN;
+ return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW
+ : raw_ostream::RED;
+}
+
+void CoverageReport::render(const FileCoverageSummary &File, raw_ostream &OS) {
+ OS << column(File.Name, FileReportColumns[0], Column::LeftTrim)
+ << format("%*u", FileReportColumns[1], (unsigned)File.RegionCoverage.NumRegions);
+ Options.colored_ostream(OS, File.RegionCoverage.isFullyCovered()
+ ? raw_ostream::GREEN
+ : raw_ostream::RED)
+ << format("%*u", FileReportColumns[2], (unsigned)File.RegionCoverage.NotCovered);
+ Options.colored_ostream(OS,
+ determineCoveragePercentageColor(File.RegionCoverage))
+ << format("%*.2f", FileReportColumns[3] - 1,
+ File.RegionCoverage.getPercentCovered()) << '%';
+ OS << format("%*u", FileReportColumns[4],
+ (unsigned)File.FunctionCoverage.NumFunctions);
+ Options.colored_ostream(
+ OS, determineCoveragePercentageColor(File.FunctionCoverage))
+ << format("%*.2f", FileReportColumns[5] - 1,
+ File.FunctionCoverage.getPercentCovered()) << '%';
+ OS << "\n";
+}
+
+void CoverageReport::render(const FunctionCoverageSummary &Function,
+ raw_ostream &OS) {
+ OS << column(Function.Name, FunctionReportColumns[0], Column::RightTrim)
+ << format("%*u", FunctionReportColumns[1],
+ (unsigned)Function.RegionCoverage.NumRegions);
+ Options.colored_ostream(OS, Function.RegionCoverage.isFullyCovered()
+ ? raw_ostream::GREEN
+ : raw_ostream::RED)
+ << format("%*u", FunctionReportColumns[2],
+ (unsigned)Function.RegionCoverage.NotCovered);
+ Options.colored_ostream(
+ OS, determineCoveragePercentageColor(Function.RegionCoverage))
+ << format("%*.2f", FunctionReportColumns[3] - 1,
+ Function.RegionCoverage.getPercentCovered()) << '%';
+ OS << format("%*u", FunctionReportColumns[4],
+ (unsigned)Function.LineCoverage.NumLines);
+ Options.colored_ostream(OS, Function.LineCoverage.isFullyCovered()
+ ? raw_ostream::GREEN
+ : raw_ostream::RED)
+ << format("%*u", FunctionReportColumns[5],
+ (unsigned)Function.LineCoverage.NotCovered);
+ Options.colored_ostream(
+ OS, determineCoveragePercentageColor(Function.LineCoverage))
+ << format("%*.2f", FunctionReportColumns[6] - 1,
+ Function.LineCoverage.getPercentCovered()) << '%';
+ OS << "\n";
+}
+
+void CoverageReport::renderFunctionReports(raw_ostream &OS) {
+ bool isFirst = true;
+ for (const auto &File : Summary.getFileSummaries()) {
+ if (isFirst)
+ isFirst = false;
+ else
+ OS << "\n";
+ OS << "File '" << File.Name << "':\n";
+ OS << column("Name", FunctionReportColumns[0])
+ << column("Regions", FunctionReportColumns[1], Column::RightAlignment)
+ << column("Miss", FunctionReportColumns[2], Column::RightAlignment)
+ << column("Cover", FunctionReportColumns[3], Column::RightAlignment)
+ << column("Lines", FunctionReportColumns[4], Column::RightAlignment)
+ << column("Miss", FunctionReportColumns[5], Column::RightAlignment)
+ << column("Cover", FunctionReportColumns[6], Column::RightAlignment);
+ OS << "\n";
+ renderDivider(FunctionReportColumns, OS);
+ OS << "\n";
+ for (const auto &Function : File.FunctionSummaries)
+ render(Function, OS);
+ renderDivider(FunctionReportColumns, OS);
+ OS << "\n";
+ render(FunctionCoverageSummary("TOTAL", /*ExecutionCount=*/0,
+ File.RegionCoverage, File.LineCoverage),
+ OS);
+ }
+}
+
+void CoverageReport::renderFileReports(raw_ostream &OS) {
+ OS << column("Filename", FileReportColumns[0])
+ << column("Regions", FileReportColumns[1], Column::RightAlignment)
+ << column("Miss", FileReportColumns[2], Column::RightAlignment)
+ << column("Cover", FileReportColumns[3], Column::RightAlignment)
+ << column("Functions", FileReportColumns[4], Column::RightAlignment)
+ << column("Executed", FileReportColumns[5], Column::RightAlignment)
+ << "\n";
+ renderDivider(FileReportColumns, OS);
+ OS << "\n";
+ for (const auto &File : Summary.getFileSummaries())
+ render(File, OS);
+ renderDivider(FileReportColumns, OS);
+ OS << "\n";
+ render(Summary.getCombinedFileSummaries(), OS);
+}
diff --git a/tools/llvm-cov/CoverageReport.h b/tools/llvm-cov/CoverageReport.h
new file mode 100644
index 0000000..e8d34f2
--- /dev/null
+++ b/tools/llvm-cov/CoverageReport.h
@@ -0,0 +1,40 @@
+//===- CoverageReport.h - Code coverage report ---------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements rendering of a code coverage report.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEREPORT_H
+#define LLVM_COV_COVERAGEREPORT_H
+
+#include "CoverageViewOptions.h"
+#include "CoverageSummary.h"
+
+namespace llvm {
+
+/// \brief Displays the code coverage report.
+class CoverageReport {
+ const CoverageViewOptions &Options;
+ CoverageSummary &Summary;
+
+ void render(const FileCoverageSummary &File, raw_ostream &OS);
+ void render(const FunctionCoverageSummary &Function, raw_ostream &OS);
+
+public:
+ CoverageReport(const CoverageViewOptions &Options, CoverageSummary &Summary)
+ : Options(Options), Summary(Summary) {}
+
+ void renderFunctionReports(raw_ostream &OS);
+
+ void renderFileReports(raw_ostream &OS);
+};
+}
+
+#endif // LLVM_COV_COVERAGEREPORT_H
diff --git a/tools/llvm-cov/CoverageSummary.cpp b/tools/llvm-cov/CoverageSummary.cpp
new file mode 100644
index 0000000..059c8c8
--- /dev/null
+++ b/tools/llvm-cov/CoverageSummary.cpp
@@ -0,0 +1,64 @@
+//===- CoverageSummary.cpp - Code coverage summary ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements data management and rendering for the code coverage
+// summaries of all files and functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageSummary.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+
+unsigned CoverageSummary::getFileID(StringRef Filename) {
+ for (unsigned I = 0, E = Filenames.size(); I < E; ++I) {
+ if (sys::fs::equivalent(Filenames[I], Filename))
+ return I;
+ }
+ Filenames.push_back(Filename);
+ return Filenames.size() - 1;
+}
+
+void
+CoverageSummary::createSummaries(const coverage::CoverageMapping &Coverage) {
+ for (StringRef Filename : Coverage.getUniqueSourceFiles()) {
+ size_t PrevSize = FunctionSummaries.size();
+ for (const auto &F : Coverage.getCoveredFunctions(Filename))
+ FunctionSummaries.push_back(FunctionCoverageSummary::get(F));
+ size_t Count = FunctionSummaries.size() - PrevSize;
+ if (Count == 0)
+ continue;
+ FileSummaries.push_back(FileCoverageSummary::get(
+ Filename, makeArrayRef(FunctionSummaries.data() + PrevSize, Count)));
+ }
+}
+
+FileCoverageSummary CoverageSummary::getCombinedFileSummaries() {
+ size_t NumRegions = 0, CoveredRegions = 0;
+ size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0;
+ size_t NumFunctionsExecuted = 0, NumFunctions = 0;
+ for (const auto &File : FileSummaries) {
+ NumRegions += File.RegionCoverage.NumRegions;
+ CoveredRegions += File.RegionCoverage.Covered;
+
+ NumLines += File.LineCoverage.NumLines;
+ NonCodeLines += File.LineCoverage.NonCodeLines;
+ CoveredLines += File.LineCoverage.Covered;
+
+ NumFunctionsExecuted += File.FunctionCoverage.Executed;
+ NumFunctions += File.FunctionCoverage.NumFunctions;
+ }
+ return FileCoverageSummary(
+ "TOTAL", RegionCoverageInfo(CoveredRegions, NumRegions),
+ LineCoverageInfo(CoveredLines, NonCodeLines, NumLines),
+ FunctionCoverageInfo(NumFunctionsExecuted, NumFunctions),
+ None);
+}
diff --git a/tools/llvm-cov/CoverageSummary.h b/tools/llvm-cov/CoverageSummary.h
new file mode 100644
index 0000000..9dbebde
--- /dev/null
+++ b/tools/llvm-cov/CoverageSummary.h
@@ -0,0 +1,45 @@
+//===- CoverageSummary.h - Code coverage summary --------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements data management and rendering for the code coverage
+// summaries of all files and functions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGESUMMARY_H
+#define LLVM_COV_COVERAGESUMMARY_H
+
+#include "CoverageSummaryInfo.h"
+#include <vector>
+
+namespace llvm {
+
+/// \brief Manager for the function and file code coverage summaries.
+class CoverageSummary {
+ std::vector<StringRef> Filenames;
+ std::vector<FunctionCoverageSummary> FunctionSummaries;
+ std::vector<std::pair<unsigned, unsigned>> FunctionSummariesFileIDs;
+ std::vector<FileCoverageSummary> FileSummaries;
+
+ unsigned getFileID(StringRef Filename);
+
+public:
+ void createSummaries(const coverage::CoverageMapping &Coverage);
+
+ ArrayRef<FileCoverageSummary> getFileSummaries() { return FileSummaries; }
+
+ FileCoverageSummary getCombinedFileSummaries();
+
+ void render(const FunctionCoverageSummary &Summary, raw_ostream &OS);
+
+ void render(raw_ostream &OS);
+};
+}
+
+#endif // LLVM_COV_COVERAGESUMMARY_H
diff --git a/tools/llvm-cov/CoverageSummaryInfo.cpp b/tools/llvm-cov/CoverageSummaryInfo.cpp
new file mode 100644
index 0000000..dd78ace
--- /dev/null
+++ b/tools/llvm-cov/CoverageSummaryInfo.cpp
@@ -0,0 +1,96 @@
+//===- CoverageSummaryInfo.cpp - Coverage summary for function/file -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// These structures are used to represent code coverage metrics
+// for functions/files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageSummaryInfo.h"
+
+using namespace llvm;
+using namespace coverage;
+
+FunctionCoverageSummary
+FunctionCoverageSummary::get(const coverage::FunctionRecord &Function) {
+ // Compute the region coverage
+ size_t NumCodeRegions = 0, CoveredRegions = 0;
+ for (auto &CR : Function.CountedRegions) {
+ if (CR.Kind != CounterMappingRegion::CodeRegion)
+ continue;
+ ++NumCodeRegions;
+ if (CR.ExecutionCount != 0)
+ ++CoveredRegions;
+ }
+
+ // Compute the line coverage
+ size_t NumLines = 0, CoveredLines = 0;
+ for (unsigned FileID = 0, E = Function.Filenames.size(); FileID < E;
+ ++FileID) {
+ // Find the line start and end of the function's source code
+ // in that particular file
+ unsigned LineStart = std::numeric_limits<unsigned>::max();
+ unsigned LineEnd = 0;
+ for (auto &CR : Function.CountedRegions) {
+ if (CR.FileID != FileID)
+ continue;
+ LineStart = std::min(LineStart, CR.LineStart);
+ LineEnd = std::max(LineEnd, CR.LineEnd);
+ }
+ unsigned LineCount = LineEnd - LineStart + 1;
+
+ // Get counters
+ llvm::SmallVector<uint64_t, 16> ExecutionCounts;
+ ExecutionCounts.resize(LineCount, 0);
+ for (auto &CR : Function.CountedRegions) {
+ if (CR.FileID != FileID)
+ continue;
+ // Ignore the lines that were skipped by the preprocessor.
+ auto ExecutionCount = CR.ExecutionCount;
+ if (CR.Kind == CounterMappingRegion::SkippedRegion) {
+ LineCount -= CR.LineEnd - CR.LineStart + 1;
+ ExecutionCount = 1;
+ }
+ for (unsigned I = CR.LineStart; I <= CR.LineEnd; ++I)
+ ExecutionCounts[I - LineStart] = ExecutionCount;
+ }
+ CoveredLines += LineCount - std::count(ExecutionCounts.begin(),
+ ExecutionCounts.end(), 0);
+ NumLines += LineCount;
+ }
+ return FunctionCoverageSummary(
+ Function.Name, Function.ExecutionCount,
+ RegionCoverageInfo(CoveredRegions, NumCodeRegions),
+ LineCoverageInfo(CoveredLines, 0, NumLines));
+}
+
+FileCoverageSummary
+FileCoverageSummary::get(StringRef Name,
+ ArrayRef<FunctionCoverageSummary> FunctionSummaries) {
+ size_t NumRegions = 0, CoveredRegions = 0;
+ size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0;
+ size_t NumFunctionsExecuted = 0;
+ for (const auto &Func : FunctionSummaries) {
+ CoveredRegions += Func.RegionCoverage.Covered;
+ NumRegions += Func.RegionCoverage.NumRegions;
+
+ CoveredLines += Func.LineCoverage.Covered;
+ NonCodeLines += Func.LineCoverage.NonCodeLines;
+ NumLines += Func.LineCoverage.NumLines;
+
+ if (Func.ExecutionCount != 0)
+ ++NumFunctionsExecuted;
+ }
+
+ return FileCoverageSummary(
+ Name, RegionCoverageInfo(CoveredRegions, NumRegions),
+ LineCoverageInfo(CoveredLines, NonCodeLines, NumLines),
+ FunctionCoverageInfo(NumFunctionsExecuted, FunctionSummaries.size()),
+ FunctionSummaries);
+}
diff --git a/tools/llvm-cov/CoverageSummaryInfo.h b/tools/llvm-cov/CoverageSummaryInfo.h
new file mode 100644
index 0000000..0036032
--- /dev/null
+++ b/tools/llvm-cov/CoverageSummaryInfo.h
@@ -0,0 +1,133 @@
+//===- CoverageSummaryInfo.h - Coverage summary for function/file ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// These structures are used to represent code coverage metrics
+// for functions/files.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGESUMMARYINFO_H
+#define LLVM_COV_COVERAGESUMMARYINFO_H
+
+#include "llvm/ProfileData/CoverageMapping.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+
+/// \brief Provides information about region coverage for a function/file.
+struct RegionCoverageInfo {
+ /// \brief The number of regions that were executed at least once.
+ size_t Covered;
+
+ /// \brief The number of regions that weren't executed.
+ size_t NotCovered;
+
+ /// \brief The total number of regions in a function/file.
+ size_t NumRegions;
+
+ RegionCoverageInfo(size_t Covered, size_t NumRegions)
+ : Covered(Covered), NotCovered(NumRegions - Covered),
+ NumRegions(NumRegions) {}
+
+ bool isFullyCovered() const { return Covered == NumRegions; }
+
+ double getPercentCovered() const {
+ return double(Covered) / double(NumRegions) * 100.0;
+ }
+};
+
+/// \brief Provides information about line coverage for a function/file.
+struct LineCoverageInfo {
+ /// \brief The number of lines that were executed at least once.
+ size_t Covered;
+
+ /// \brief The number of lines that weren't executed.
+ size_t NotCovered;
+
+ /// \brief The number of lines that aren't code.
+ size_t NonCodeLines;
+
+ /// \brief The total number of lines in a function/file.
+ size_t NumLines;
+
+ LineCoverageInfo(size_t Covered, size_t NumNonCodeLines, size_t NumLines)
+ : Covered(Covered), NotCovered(NumLines - NumNonCodeLines - Covered),
+ NonCodeLines(NumNonCodeLines), NumLines(NumLines) {}
+
+ bool isFullyCovered() const { return Covered == (NumLines - NonCodeLines); }
+
+ double getPercentCovered() const {
+ return double(Covered) / double(NumLines - NonCodeLines) * 100.0;
+ }
+};
+
+/// \brief Provides information about function coverage for a file.
+struct FunctionCoverageInfo {
+ /// \brief The number of functions that were executed.
+ size_t Executed;
+
+ /// \brief The total number of functions in this file.
+ size_t NumFunctions;
+
+ FunctionCoverageInfo(size_t Executed, size_t NumFunctions)
+ : Executed(Executed), NumFunctions(NumFunctions) {}
+
+ bool isFullyCovered() const { return Executed == NumFunctions; }
+
+ double getPercentCovered() const {
+ return double(Executed) / double(NumFunctions) * 100.0;
+ }
+};
+
+/// \brief A summary of function's code coverage.
+struct FunctionCoverageSummary {
+ StringRef Name;
+ uint64_t ExecutionCount;
+ RegionCoverageInfo RegionCoverage;
+ LineCoverageInfo LineCoverage;
+
+ FunctionCoverageSummary(StringRef Name, uint64_t ExecutionCount,
+ const RegionCoverageInfo &RegionCoverage,
+ const LineCoverageInfo &LineCoverage)
+ : Name(Name), ExecutionCount(ExecutionCount),
+ RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {
+ }
+
+ /// \brief Compute the code coverage summary for the given function coverage
+ /// mapping record.
+ static FunctionCoverageSummary
+ get(const coverage::FunctionRecord &Function);
+};
+
+/// \brief A summary of file's code coverage.
+struct FileCoverageSummary {
+ StringRef Name;
+ RegionCoverageInfo RegionCoverage;
+ LineCoverageInfo LineCoverage;
+ FunctionCoverageInfo FunctionCoverage;
+ /// \brief The summary of every function
+ /// in this file.
+ ArrayRef<FunctionCoverageSummary> FunctionSummaries;
+
+ FileCoverageSummary(StringRef Name, const RegionCoverageInfo &RegionCoverage,
+ const LineCoverageInfo &LineCoverage,
+ const FunctionCoverageInfo &FunctionCoverage,
+ ArrayRef<FunctionCoverageSummary> FunctionSummaries)
+ : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),
+ FunctionCoverage(FunctionCoverage),
+ FunctionSummaries(FunctionSummaries) {}
+
+ /// \brief Compute the code coverage summary for a file.
+ static FileCoverageSummary
+ get(StringRef Name, ArrayRef<FunctionCoverageSummary> FunctionSummaries);
+};
+
+} // namespace llvm
+
+#endif // LLVM_COV_COVERAGESUMMARYINFO_H
diff --git a/tools/llvm-cov/CoverageViewOptions.h b/tools/llvm-cov/CoverageViewOptions.h
new file mode 100644
index 0000000..94b55fe
--- /dev/null
+++ b/tools/llvm-cov/CoverageViewOptions.h
@@ -0,0 +1,36 @@
+//===- CoverageViewOptions.h - Code coverage display options -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEVIEWOPTIONS_H
+#define LLVM_COV_COVERAGEVIEWOPTIONS_H
+
+#include "RenderingSupport.h"
+
+namespace llvm {
+
+/// \brief The options for displaying the code coverage information.
+struct CoverageViewOptions {
+ bool Debug;
+ bool Colors;
+ bool ShowLineNumbers;
+ bool ShowLineStats;
+ bool ShowRegionMarkers;
+ bool ShowLineStatsOrRegionMarkers;
+ bool ShowExpandedRegions;
+ bool ShowFunctionInstantiations;
+
+ /// \brief Change the output's stream color if the colors are enabled.
+ ColoredRawOstream colored_ostream(raw_ostream &OS,
+ raw_ostream::Colors Color) const {
+ return llvm::colored_ostream(OS, Color, Colors);
+ }
+};
+}
+
+#endif // LLVM_COV_COVERAGEVIEWOPTIONS_H
diff --git a/tools/llvm-cov/LLVMBuild.txt b/tools/llvm-cov/LLVMBuild.txt
index 87e00d1..d6eb74d 100644
--- a/tools/llvm-cov/LLVMBuild.txt
+++ b/tools/llvm-cov/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Tool
name = llvm-cov
parent = Tools
-required_libraries = Instrumentation
+required_libraries = ProfileData Support Instrumentation
diff --git a/tools/llvm-cov/Makefile b/tools/llvm-cov/Makefile
index efed6cc..6e32b4d 100644
--- a/tools/llvm-cov/Makefile
+++ b/tools/llvm-cov/Makefile
@@ -9,7 +9,7 @@
LEVEL := ../..
TOOLNAME := llvm-cov
-LINK_COMPONENTS := core support
+LINK_COMPONENTS := core support profiledata object
# This tool has no plugins, optimize startup time.
TOOL_NO_EXPORTS := 1
diff --git a/tools/llvm-cov/RenderingSupport.h b/tools/llvm-cov/RenderingSupport.h
new file mode 100644
index 0000000..0271329
--- /dev/null
+++ b/tools/llvm-cov/RenderingSupport.h
@@ -0,0 +1,60 @@
+//===- RenderingSupport.h - output stream rendering support functions ----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_RENDERINGSUPPORT_H
+#define LLVM_COV_RENDERINGSUPPORT_H
+
+#include "llvm/Support/raw_ostream.h"
+#include <utility>
+
+namespace llvm {
+
+/// \brief A helper class that resets the output stream's color if needed
+/// when destroyed.
+class ColoredRawOstream {
+ ColoredRawOstream(const ColoredRawOstream &OS) LLVM_DELETED_FUNCTION;
+
+public:
+ raw_ostream &OS;
+ bool IsColorUsed;
+
+ ColoredRawOstream(raw_ostream &OS, bool IsColorUsed)
+ : OS(OS), IsColorUsed(IsColorUsed) {}
+
+ ColoredRawOstream(ColoredRawOstream &&Other)
+ : OS(Other.OS), IsColorUsed(Other.IsColorUsed) {
+ // Reset the other IsColorUsed so that the other object won't reset the
+ // color when destroyed.
+ Other.IsColorUsed = false;
+ }
+
+ ~ColoredRawOstream() {
+ if (IsColorUsed)
+ OS.resetColor();
+ }
+};
+
+template <typename T>
+inline raw_ostream &operator<<(const ColoredRawOstream &OS, T &&Value) {
+ return OS.OS << std::forward<T>(Value);
+}
+
+/// \brief Change the color of the output stream if the `IsColorUsed` flag
+/// is true. Returns an object that resets the color when destroyed.
+inline ColoredRawOstream colored_ostream(raw_ostream &OS,
+ raw_ostream::Colors Color,
+ bool IsColorUsed = true,
+ bool Bold = false, bool BG = false) {
+ if (IsColorUsed)
+ OS.changeColor(Color, Bold, BG);
+ return ColoredRawOstream(OS, IsColorUsed);
+}
+}
+
+#endif // LLVM_COV_RENDERINGSUPPORT_H
diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp
new file mode 100644
index 0000000..015099c
--- /dev/null
+++ b/tools/llvm-cov/SourceCoverageView.cpp
@@ -0,0 +1,260 @@
+//===- SourceCoverageView.cpp - Code coverage view for source code --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements rendering for code coverage of source code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SourceCoverageView.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/LineIterator.h"
+
+using namespace llvm;
+
+void SourceCoverageView::renderLine(
+ raw_ostream &OS, StringRef Line, int64_t LineNumber,
+ const coverage::CoverageSegment *WrappedSegment,
+ ArrayRef<const coverage::CoverageSegment *> Segments,
+ unsigned ExpansionCol) {
+ Optional<raw_ostream::Colors> Highlight;
+ SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
+
+ // The first segment overlaps from a previous line, so we treat it specially.
+ if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0)
+ Highlight = raw_ostream::RED;
+
+ // Output each segment of the line, possibly highlighted.
+ unsigned Col = 1;
+ for (const auto *S : Segments) {
+ unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
+ colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
+ Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true)
+ << Line.substr(Col - 1, End - Col);
+ if (Options.Debug && Highlight)
+ HighlightedRanges.push_back(std::make_pair(Col, End));
+ Col = End;
+ if (Col == ExpansionCol)
+ Highlight = raw_ostream::CYAN;
+ else if (S->HasCount && S->Count == 0)
+ Highlight = raw_ostream::RED;
+ else
+ Highlight = None;
+ }
+
+ // Show the rest of the line
+ colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
+ Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true)
+ << Line.substr(Col - 1, Line.size() - Col + 1);
+ OS << "\n";
+
+ if (Options.Debug) {
+ for (const auto &Range : HighlightedRanges)
+ errs() << "Highlighted line " << LineNumber << ", " << Range.first
+ << " -> " << Range.second << "\n";
+ if (Highlight)
+ errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
+ }
+}
+
+void SourceCoverageView::renderIndent(raw_ostream &OS, unsigned Level) {
+ for (unsigned I = 0; I < Level; ++I)
+ OS << " |";
+}
+
+void SourceCoverageView::renderViewDivider(unsigned Level, unsigned Length,
+ raw_ostream &OS) {
+ assert(Level != 0 && "Cannot render divider at top level");
+ renderIndent(OS, Level - 1);
+ OS.indent(2);
+ for (unsigned I = 0; I < Length; ++I)
+ OS << "-";
+}
+
+void
+SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
+ const LineCoverageInfo &Line) {
+ if (!Line.isMapped()) {
+ OS.indent(LineCoverageColumnWidth) << '|';
+ return;
+ }
+ SmallString<32> Buffer;
+ raw_svector_ostream BufferOS(Buffer);
+ BufferOS << Line.ExecutionCount;
+ auto Str = BufferOS.str();
+ // Trim
+ Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));
+ // Align to the right
+ OS.indent(LineCoverageColumnWidth - Str.size());
+ colored_ostream(OS, raw_ostream::MAGENTA,
+ Line.hasMultipleRegions() && Options.Colors)
+ << Str;
+ OS << '|';
+}
+
+void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
+ unsigned LineNo) {
+ SmallString<32> Buffer;
+ raw_svector_ostream BufferOS(Buffer);
+ BufferOS << LineNo;
+ auto Str = BufferOS.str();
+ // Trim and align to the right
+ Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
+ OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
+}
+
+void SourceCoverageView::renderRegionMarkers(
+ raw_ostream &OS, ArrayRef<const coverage::CoverageSegment *> Segments) {
+ SmallString<32> Buffer;
+ raw_svector_ostream BufferOS(Buffer);
+
+ unsigned PrevColumn = 1;
+ for (const auto *S : Segments) {
+ if (!S->IsRegionEntry)
+ continue;
+ // Skip to the new region
+ if (S->Col > PrevColumn)
+ OS.indent(S->Col - PrevColumn);
+ PrevColumn = S->Col + 1;
+ BufferOS << S->Count;
+ StringRef Str = BufferOS.str();
+ // Trim the execution count
+ Str = Str.substr(0, std::min(Str.size(), (size_t)7));
+ PrevColumn += Str.size();
+ OS << '^' << Str;
+ Buffer.clear();
+ }
+ OS << "\n";
+
+ if (Options.Debug)
+ for (const auto *S : Segments)
+ errs() << "Marker at " << S->Line << ":" << S->Col << " = " << S->Count
+ << (S->IsRegionEntry ? "\n" : " (pop)\n");
+}
+
+void SourceCoverageView::render(raw_ostream &OS, bool WholeFile,
+ unsigned IndentLevel) {
+ // The width of the leading columns
+ unsigned CombinedColumnWidth =
+ (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
+ (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
+ // The width of the line that is used to divide between the view and the
+ // subviews.
+ unsigned DividerWidth = CombinedColumnWidth + 4;
+
+ // We need the expansions and instantiations sorted so we can go through them
+ // while we iterate lines.
+ std::sort(ExpansionSubViews.begin(), ExpansionSubViews.end());
+ std::sort(InstantiationSubViews.begin(), InstantiationSubViews.end());
+ auto NextESV = ExpansionSubViews.begin();
+ auto EndESV = ExpansionSubViews.end();
+ auto NextISV = InstantiationSubViews.begin();
+ auto EndISV = InstantiationSubViews.end();
+
+ // Get the coverage information for the file.
+ auto NextSegment = CoverageInfo.begin();
+ auto EndSegment = CoverageInfo.end();
+
+ unsigned FirstLine = NextSegment != EndSegment ? NextSegment->Line : 0;
+ const coverage::CoverageSegment *WrappedSegment = nullptr;
+ SmallVector<const coverage::CoverageSegment *, 8> LineSegments;
+ for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) {
+ // If we aren't rendering the whole file, we need to filter out the prologue
+ // and epilogue.
+ if (!WholeFile) {
+ if (NextSegment == EndSegment)
+ break;
+ else if (LI.line_number() < FirstLine)
+ continue;
+ }
+
+ // Collect the coverage information relevant to this line.
+ if (LineSegments.size())
+ WrappedSegment = LineSegments.back();
+ LineSegments.clear();
+ while (NextSegment != EndSegment && NextSegment->Line == LI.line_number())
+ LineSegments.push_back(&*NextSegment++);
+
+ // Calculate a count to be for the line as a whole.
+ LineCoverageInfo LineCount;
+ if (WrappedSegment && WrappedSegment->HasCount)
+ LineCount.addRegionCount(WrappedSegment->Count);
+ for (const auto *S : LineSegments)
+ if (S->HasCount && S->IsRegionEntry)
+ LineCount.addRegionStartCount(S->Count);
+
+ // Render the line prefix.
+ renderIndent(OS, IndentLevel);
+ if (Options.ShowLineStats)
+ renderLineCoverageColumn(OS, LineCount);
+ if (Options.ShowLineNumbers)
+ renderLineNumberColumn(OS, LI.line_number());
+
+ // If there are expansion subviews, we want to highlight the first one.
+ unsigned ExpansionColumn = 0;
+ if (NextESV != EndESV && NextESV->getLine() == LI.line_number() &&
+ Options.Colors)
+ ExpansionColumn = NextESV->getStartCol();
+
+ // Display the source code for the current line.
+ renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments,
+ ExpansionColumn);
+
+ // Show the region markers.
+ if (Options.ShowRegionMarkers && (!Options.ShowLineStatsOrRegionMarkers ||
+ LineCount.hasMultipleRegions()) &&
+ !LineSegments.empty()) {
+ renderIndent(OS, IndentLevel);
+ OS.indent(CombinedColumnWidth);
+ renderRegionMarkers(OS, LineSegments);
+ }
+
+ // Show the expansions and instantiations for this line.
+ unsigned NestedIndent = IndentLevel + 1;
+ bool RenderedSubView = false;
+ for (; NextESV != EndESV && NextESV->getLine() == LI.line_number();
+ ++NextESV) {
+ renderViewDivider(NestedIndent, DividerWidth, OS);
+ OS << "\n";
+ if (RenderedSubView) {
+ // Re-render the current line and highlight the expansion range for
+ // this subview.
+ ExpansionColumn = NextESV->getStartCol();
+ renderIndent(OS, IndentLevel);
+ OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1));
+ renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments,
+ ExpansionColumn);
+ renderViewDivider(NestedIndent, DividerWidth, OS);
+ OS << "\n";
+ }
+ // Render the child subview
+ if (Options.Debug)
+ errs() << "Expansion at line " << NextESV->getLine() << ", "
+ << NextESV->getStartCol() << " -> " << NextESV->getEndCol()
+ << "\n";
+ NextESV->View->render(OS, false, NestedIndent);
+ RenderedSubView = true;
+ }
+ for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) {
+ renderViewDivider(NestedIndent, DividerWidth, OS);
+ OS << "\n";
+ renderIndent(OS, NestedIndent);
+ OS << ' ';
+ Options.colored_ostream(OS, raw_ostream::CYAN) << NextISV->FunctionName
+ << ":";
+ OS << "\n";
+ NextISV->View->render(OS, false, NestedIndent);
+ RenderedSubView = true;
+ }
+ if (RenderedSubView) {
+ renderViewDivider(NestedIndent, DividerWidth, OS);
+ OS << "\n";
+ }
+ }
+}
diff --git a/tools/llvm-cov/SourceCoverageView.h b/tools/llvm-cov/SourceCoverageView.h
new file mode 100644
index 0000000..d92a748
--- /dev/null
+++ b/tools/llvm-cov/SourceCoverageView.h
@@ -0,0 +1,162 @@
+//===- SourceCoverageView.h - Code coverage view for source code ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements rendering for code coverage of source code.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_SOURCECOVERAGEVIEW_H
+#define LLVM_COV_SOURCECOVERAGEVIEW_H
+
+#include "CoverageViewOptions.h"
+#include "llvm/ProfileData/CoverageMapping.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <vector>
+
+namespace llvm {
+
+class SourceCoverageView;
+
+/// \brief A view that represents a macro or include expansion
+struct ExpansionView {
+ coverage::CounterMappingRegion Region;
+ std::unique_ptr<SourceCoverageView> View;
+
+ ExpansionView(const coverage::CounterMappingRegion &Region,
+ std::unique_ptr<SourceCoverageView> View)
+ : Region(Region), View(std::move(View)) {}
+ ExpansionView(ExpansionView &&RHS)
+ : Region(std::move(RHS.Region)), View(std::move(RHS.View)) {}
+ ExpansionView &operator=(ExpansionView &&RHS) {
+ Region = std::move(RHS.Region);
+ View = std::move(RHS.View);
+ return *this;
+ }
+
+ unsigned getLine() const { return Region.LineStart; }
+ unsigned getStartCol() const { return Region.ColumnStart; }
+ unsigned getEndCol() const { return Region.ColumnEnd; }
+
+ friend bool operator<(const ExpansionView &LHS, const ExpansionView &RHS) {
+ return LHS.Region.startLoc() < RHS.Region.startLoc();
+ }
+};
+
+/// \brief A view that represents a function instantiation
+struct InstantiationView {
+ StringRef FunctionName;
+ unsigned Line;
+ std::unique_ptr<SourceCoverageView> View;
+
+ InstantiationView(StringRef FunctionName, unsigned Line,
+ std::unique_ptr<SourceCoverageView> View)
+ : FunctionName(FunctionName), Line(Line), View(std::move(View)) {}
+ InstantiationView(InstantiationView &&RHS)
+ : FunctionName(std::move(RHS.FunctionName)), Line(std::move(RHS.Line)),
+ View(std::move(RHS.View)) {}
+ InstantiationView &operator=(InstantiationView &&RHS) {
+ FunctionName = std::move(RHS.FunctionName);
+ Line = std::move(RHS.Line);
+ View = std::move(RHS.View);
+ return *this;
+ }
+
+ friend bool operator<(const InstantiationView &LHS,
+ const InstantiationView &RHS) {
+ return LHS.Line < RHS.Line;
+ }
+};
+
+/// \brief A code coverage view of a specific source file.
+/// It can have embedded coverage views.
+class SourceCoverageView {
+private:
+ /// \brief Coverage information for a single line.
+ struct LineCoverageInfo {
+ uint64_t ExecutionCount;
+ unsigned RegionCount;
+ bool Mapped;
+
+ LineCoverageInfo() : ExecutionCount(0), RegionCount(0), Mapped(false) {}
+
+ bool isMapped() const { return Mapped; }
+
+ bool hasMultipleRegions() const { return RegionCount > 1; }
+
+ void addRegionStartCount(uint64_t Count) {
+ Mapped = true;
+ ExecutionCount = Count;
+ ++RegionCount;
+ }
+
+ void addRegionCount(uint64_t Count) {
+ Mapped = true;
+ if (!RegionCount)
+ ExecutionCount = Count;
+ }
+ };
+
+ const MemoryBuffer &File;
+ const CoverageViewOptions &Options;
+ coverage::CoverageData CoverageInfo;
+ std::vector<ExpansionView> ExpansionSubViews;
+ std::vector<InstantiationView> InstantiationSubViews;
+
+ /// \brief Render a source line with highlighting.
+ void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber,
+ const coverage::CoverageSegment *WrappedSegment,
+ ArrayRef<const coverage::CoverageSegment *> Segments,
+ unsigned ExpansionCol);
+
+ void renderIndent(raw_ostream &OS, unsigned Level);
+
+ void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS);
+
+ /// \brief Render the line's execution count column.
+ void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line);
+
+ /// \brief Render the line number column.
+ void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo);
+
+ /// \brief Render all the region's execution counts on a line.
+ void
+ renderRegionMarkers(raw_ostream &OS,
+ ArrayRef<const coverage::CoverageSegment *> Segments);
+
+ static const unsigned LineCoverageColumnWidth = 7;
+ static const unsigned LineNumberColumnWidth = 5;
+
+public:
+ SourceCoverageView(const MemoryBuffer &File,
+ const CoverageViewOptions &Options,
+ coverage::CoverageData &&CoverageInfo)
+ : File(File), Options(Options), CoverageInfo(std::move(CoverageInfo)) {}
+
+ const CoverageViewOptions &getOptions() const { return Options; }
+
+ /// \brief Add an expansion subview to this view.
+ void addExpansion(const coverage::CounterMappingRegion &Region,
+ std::unique_ptr<SourceCoverageView> View) {
+ ExpansionSubViews.emplace_back(Region, std::move(View));
+ }
+
+ /// \brief Add a function instantiation subview to this view.
+ void addInstantiation(StringRef FunctionName, unsigned Line,
+ std::unique_ptr<SourceCoverageView> View) {
+ InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View));
+ }
+
+ /// \brief Print the code coverage information for a specific
+ /// portion of a source file to the output stream.
+ void render(raw_ostream &OS, bool WholeFile, unsigned IndentLevel = 0);
+};
+
+} // namespace llvm
+
+#endif // LLVM_COV_SOURCECOVERAGEVIEW_H
diff --git a/tools/llvm-cov/TestingSupport.cpp b/tools/llvm-cov/TestingSupport.cpp
new file mode 100644
index 0000000..537f133
--- /dev/null
+++ b/tools/llvm-cov/TestingSupport.cpp
@@ -0,0 +1,91 @@
+//===- TestingSupport.cpp - Convert objects files into test files --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryObject.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include <system_error>
+#include <functional>
+
+using namespace llvm;
+using namespace object;
+
+int convertForTestingMain(int argc, const char *argv[]) {
+ sys::PrintStackTraceOnErrorSignal();
+ PrettyStackTraceProgram X(argc, argv);
+ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+
+ cl::opt<std::string> InputSourceFile(cl::Positional, cl::Required,
+ cl::desc("<Source file>"));
+
+ cl::opt<std::string> OutputFilename(
+ "o", cl::Required,
+ cl::desc(
+ "File with the profile data obtained after an instrumented run"));
+
+ cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
+
+ auto ObjErr = llvm::object::ObjectFile::createObjectFile(InputSourceFile);
+ if (auto Err = ObjErr.getError()) {
+ errs() << "error: " << Err.message() << "\n";
+ return 1;
+ }
+ ObjectFile *OF = ObjErr.get().getBinary();
+ auto BytesInAddress = OF->getBytesInAddress();
+ if (BytesInAddress != 8) {
+ errs() << "error: 64 bit binary expected\n";
+ return 1;
+ }
+
+ // Look for the sections that we are interested in.
+ int FoundSectionCount = 0;
+ SectionRef ProfileNames, CoverageMapping;
+ for (const auto &Section : OF->sections()) {
+ StringRef Name;
+ if (Section.getName(Name))
+ return 1;
+ if (Name == "__llvm_prf_names") {
+ ProfileNames = Section;
+ } else if (Name == "__llvm_covmap") {
+ CoverageMapping = Section;
+ } else
+ continue;
+ ++FoundSectionCount;
+ }
+ if (FoundSectionCount != 2)
+ return 1;
+
+ // Get the contents of the given sections.
+ uint64_t ProfileNamesAddress = ProfileNames.getAddress();
+ StringRef CoverageMappingData;
+ StringRef ProfileNamesData;
+ if (CoverageMapping.getContents(CoverageMappingData) ||
+ ProfileNames.getContents(ProfileNamesData))
+ return 1;
+
+ int FD;
+ if (auto Err =
+ sys::fs::openFileForWrite(OutputFilename, FD, sys::fs::F_None)) {
+ errs() << "error: " << Err.message() << "\n";
+ return 1;
+ }
+
+ raw_fd_ostream OS(FD, true);
+ OS << "llvmcovmtestdata";
+ encodeULEB128(ProfileNamesData.size(), OS);
+ encodeULEB128(ProfileNamesAddress, OS);
+ OS << ProfileNamesData << CoverageMappingData;
+
+ return 0;
+}
diff --git a/tools/llvm-cov/gcov.cpp b/tools/llvm-cov/gcov.cpp
new file mode 100644
index 0000000..4c9195a
--- /dev/null
+++ b/tools/llvm-cov/gcov.cpp
@@ -0,0 +1,153 @@
+//===- gcov.cpp - GCOV compatible LLVM coverage tool ----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// llvm-cov is a command line tools to analyze and report coverage information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/GCOV.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryObject.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include <system_error>
+using namespace llvm;
+
+void reportCoverage(StringRef SourceFile, StringRef ObjectDir,
+ const std::string &InputGCNO, const std::string &InputGCDA,
+ bool DumpGCOV, const GCOVOptions &Options) {
+ SmallString<128> CoverageFileStem(ObjectDir);
+ if (CoverageFileStem.empty()) {
+ // If no directory was specified with -o, look next to the source file.
+ CoverageFileStem = sys::path::parent_path(SourceFile);
+ sys::path::append(CoverageFileStem, sys::path::stem(SourceFile));
+ } else if (sys::fs::is_directory(ObjectDir))
+ // A directory name was given. Use it and the source file name.
+ sys::path::append(CoverageFileStem, sys::path::stem(SourceFile));
+ else
+ // A file was given. Ignore the source file and look next to this file.
+ sys::path::replace_extension(CoverageFileStem, "");
+
+ std::string GCNO = InputGCNO.empty()
+ ? std::string(CoverageFileStem.str()) + ".gcno"
+ : InputGCNO;
+ std::string GCDA = InputGCDA.empty()
+ ? std::string(CoverageFileStem.str()) + ".gcda"
+ : InputGCDA;
+ GCOVFile GF;
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff =
+ MemoryBuffer::getFileOrSTDIN(GCNO);
+ if (std::error_code EC = GCNO_Buff.getError()) {
+ errs() << GCNO << ": " << EC.message() << "\n";
+ return;
+ }
+ GCOVBuffer GCNO_GB(GCNO_Buff.get().get());
+ if (!GF.readGCNO(GCNO_GB)) {
+ errs() << "Invalid .gcno File!\n";
+ return;
+ }
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff =
+ MemoryBuffer::getFileOrSTDIN(GCDA);
+ if (std::error_code EC = GCDA_Buff.getError()) {
+ if (EC != errc::no_such_file_or_directory) {
+ errs() << GCDA << ": " << EC.message() << "\n";
+ return;
+ }
+ // Clear the filename to make it clear we didn't read anything.
+ GCDA = "-";
+ } else {
+ GCOVBuffer GCDA_GB(GCDA_Buff.get().get());
+ if (!GF.readGCDA(GCDA_GB)) {
+ errs() << "Invalid .gcda File!\n";
+ return;
+ }
+ }
+
+ if (DumpGCOV)
+ GF.dump();
+
+ FileInfo FI(Options);
+ GF.collectLineCounts(FI);
+ FI.print(SourceFile, GCNO, GCDA);
+}
+
+int gcovMain(int argc, const char *argv[]) {
+ // Print a stack trace if we signal out.
+ sys::PrintStackTraceOnErrorSignal();
+ PrettyStackTraceProgram X(argc, argv);
+ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+
+ cl::list<std::string> SourceFiles(cl::Positional, cl::OneOrMore,
+ cl::desc("SOURCEFILE"));
+
+ cl::opt<bool> AllBlocks("a", cl::Grouping, cl::init(false),
+ cl::desc("Display all basic blocks"));
+ cl::alias AllBlocksA("all-blocks", cl::aliasopt(AllBlocks));
+
+ cl::opt<bool> BranchProb("b", cl::Grouping, cl::init(false),
+ cl::desc("Display branch probabilities"));
+ cl::alias BranchProbA("branch-probabilities", cl::aliasopt(BranchProb));
+
+ cl::opt<bool> BranchCount("c", cl::Grouping, cl::init(false),
+ cl::desc("Display branch counts instead "
+ "of percentages (requires -b)"));
+ cl::alias BranchCountA("branch-counts", cl::aliasopt(BranchCount));
+
+ cl::opt<bool> LongNames("l", cl::Grouping, cl::init(false),
+ cl::desc("Prefix filenames with the main file"));
+ cl::alias LongNamesA("long-file-names", cl::aliasopt(LongNames));
+
+ cl::opt<bool> FuncSummary("f", cl::Grouping, cl::init(false),
+ cl::desc("Show coverage for each function"));
+ cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary));
+
+ 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));
+
+ cl::opt<std::string> ObjectDir(
+ "o", cl::value_desc("DIR|FILE"), cl::init(""),
+ cl::desc("Find objects in DIR or based on FILE's path"));
+ cl::alias ObjectDirA("object-directory", cl::aliasopt(ObjectDir));
+ cl::alias ObjectDirB("object-file", cl::aliasopt(ObjectDir));
+
+ cl::opt<bool> PreservePaths("p", cl::Grouping, cl::init(false),
+ cl::desc("Preserve path components"));
+ cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths));
+
+ cl::opt<bool> UncondBranch("u", cl::Grouping, cl::init(false),
+ cl::desc("Display unconditional branch info "
+ "(requires -b)"));
+ cl::alias UncondBranchA("unconditional-branches", cl::aliasopt(UncondBranch));
+
+ cl::OptionCategory DebugCat("Internal and debugging options");
+ cl::opt<bool> DumpGCOV("dump", cl::init(false), cl::cat(DebugCat),
+ cl::desc("Dump the gcov file to stderr"));
+ cl::opt<std::string> InputGCNO("gcno", cl::cat(DebugCat), cl::init(""),
+ cl::desc("Override inferred gcno file"));
+ cl::opt<std::string> InputGCDA("gcda", cl::cat(DebugCat), cl::init(""),
+ cl::desc("Override inferred gcda file"));
+
+ cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
+
+ GCOVOptions Options(AllBlocks, BranchProb, BranchCount, FuncSummary,
+ PreservePaths, UncondBranch, LongNames, NoOutput);
+
+ for (const auto &SourceFile : SourceFiles)
+ reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV,
+ Options);
+ return 0;
+}
diff --git a/tools/llvm-cov/llvm-cov.cpp b/tools/llvm-cov/llvm-cov.cpp
index 18cc1b1..a67859e 100644
--- a/tools/llvm-cov/llvm-cov.cpp
+++ b/tools/llvm-cov/llvm-cov.cpp
@@ -11,140 +11,68 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/ADT/SmallString.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Errc.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/GCOV.h"
-#include "llvm/Support/ManagedStatic.h"
-#include "llvm/Support/MemoryObject.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
-#include <system_error>
-using namespace llvm;
-
-static cl::list<std::string> SourceFiles(cl::Positional, cl::OneOrMore,
- cl::desc("SOURCEFILE"));
-
-static cl::opt<bool> AllBlocks("a", cl::Grouping, cl::init(false),
- cl::desc("Display all basic blocks"));
-static cl::alias AllBlocksA("all-blocks", cl::aliasopt(AllBlocks));
-
-static cl::opt<bool> BranchProb("b", cl::Grouping, cl::init(false),
- cl::desc("Display branch probabilities"));
-static cl::alias BranchProbA("branch-probabilities", cl::aliasopt(BranchProb));
-
-static cl::opt<bool> BranchCount("c", cl::Grouping, cl::init(false),
- cl::desc("Display branch counts instead "
- "of percentages (requires -b)"));
-static cl::alias BranchCountA("branch-counts", cl::aliasopt(BranchCount));
-
-static cl::opt<bool> LongNames("l", cl::Grouping, cl::init(false),
- cl::desc("Prefix filenames with the main file"));
-static cl::alias LongNamesA("long-file-names", cl::aliasopt(LongNames));
+#include <string>
-static cl::opt<bool> FuncSummary("f", cl::Grouping, cl::init(false),
- cl::desc("Show coverage for each function"));
-static cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary));
-
-static cl::opt<bool> NoOutput("n", cl::Grouping, cl::init(false),
- cl::desc("Do not output any .gcov files"));
-static cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput));
-
-static cl::opt<std::string>
-ObjectDir("o", cl::value_desc("DIR|FILE"), cl::init(""),
- cl::desc("Find objects in DIR or based on FILE's path"));
-static cl::alias ObjectDirA("object-directory", cl::aliasopt(ObjectDir));
-static cl::alias ObjectDirB("object-file", cl::aliasopt(ObjectDir));
-
-static cl::opt<bool> PreservePaths("p", cl::Grouping, cl::init(false),
- cl::desc("Preserve path components"));
-static cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths));
+using namespace llvm;
-static cl::opt<bool> UncondBranch("u", cl::Grouping, cl::init(false),
- cl::desc("Display unconditional branch info "
- "(requires -b)"));
-static cl::alias UncondBranchA("unconditional-branches",
- cl::aliasopt(UncondBranch));
+/// \brief The main entry point for the 'show' subcommand.
+int showMain(int argc, const char *argv[]);
-static cl::OptionCategory DebugCat("Internal and debugging options");
-static cl::opt<bool> DumpGCOV("dump", cl::init(false), cl::cat(DebugCat),
- cl::desc("Dump the gcov file to stderr"));
-static cl::opt<std::string> InputGCNO("gcno", cl::cat(DebugCat), cl::init(""),
- cl::desc("Override inferred gcno file"));
-static cl::opt<std::string> InputGCDA("gcda", cl::cat(DebugCat), cl::init(""),
- cl::desc("Override inferred gcda file"));
+/// \brief The main entry point for the 'report' subcommand.
+int reportMain(int argc, const char *argv[]);
-void reportCoverage(StringRef SourceFile) {
- SmallString<128> CoverageFileStem(ObjectDir);
- if (CoverageFileStem.empty()) {
- // If no directory was specified with -o, look next to the source file.
- CoverageFileStem = sys::path::parent_path(SourceFile);
- sys::path::append(CoverageFileStem, sys::path::stem(SourceFile));
- } else if (sys::fs::is_directory(ObjectDir))
- // A directory name was given. Use it and the source file name.
- sys::path::append(CoverageFileStem, sys::path::stem(SourceFile));
- else
- // A file was given. Ignore the source file and look next to this file.
- sys::path::replace_extension(CoverageFileStem, "");
+/// \brief The main entry point for the 'convert-for-testing' subcommand.
+int convertForTestingMain(int argc, const char *argv[]);
- std::string GCNO = InputGCNO.empty()
- ? std::string(CoverageFileStem.str()) + ".gcno"
- : InputGCNO;
- std::string GCDA = InputGCDA.empty()
- ? std::string(CoverageFileStem.str()) + ".gcda"
- : InputGCDA;
- GCOVFile GF;
+/// \brief The main entry point for the gcov compatible coverage tool.
+int gcovMain(int argc, const char *argv[]);
- ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff =
- MemoryBuffer::getFileOrSTDIN(GCNO);
- if (std::error_code EC = GCNO_Buff.getError()) {
- errs() << GCNO << ": " << EC.message() << "\n";
- return;
- }
- GCOVBuffer GCNO_GB(GCNO_Buff.get().get());
- if (!GF.readGCNO(GCNO_GB)) {
- errs() << "Invalid .gcno File!\n";
- return;
- }
+/// \brief Top level help.
+int helpMain(int argc, const char *argv[]) {
+ errs() << "OVERVIEW: LLVM code coverage tool\n\n"
+ << "USAGE: llvm-cov {gcov|report|show}\n";
+ return 0;
+}
- ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff =
- MemoryBuffer::getFileOrSTDIN(GCDA);
- if (std::error_code EC = GCDA_Buff.getError()) {
- if (EC != errc::no_such_file_or_directory) {
- errs() << GCDA << ": " << EC.message() << "\n";
- return;
- }
- // Clear the filename to make it clear we didn't read anything.
- GCDA = "-";
- } else {
- GCOVBuffer GCDA_GB(GCDA_Buff.get().get());
- if (!GF.readGCDA(GCDA_GB)) {
- errs() << "Invalid .gcda File!\n";
- return;
+int main(int argc, const char **argv) {
+ // If argv[0] is or ends with 'gcov', always be gcov compatible
+ if (sys::path::stem(argv[0]).endswith_lower("gcov"))
+ return gcovMain(argc, argv);
+
+ // Check if we are invoking a specific tool command.
+ if (argc > 1) {
+ typedef int (*MainFunction)(int, const char *[]);
+ MainFunction Func = StringSwitch<MainFunction>(argv[1])
+ .Case("convert-for-testing", convertForTestingMain)
+ .Case("gcov", gcovMain)
+ .Case("report", reportMain)
+ .Case("show", showMain)
+ .Cases("-h", "-help", "--help", helpMain)
+ .Default(nullptr);
+
+ if (Func) {
+ std::string Invocation = std::string(argv[0]) + " " + argv[1];
+ argv[1] = Invocation.c_str();
+ return Func(argc - 1, argv + 1);
}
}
- if (DumpGCOV)
- GF.dump();
-
- GCOVOptions Options(AllBlocks, BranchProb, BranchCount, FuncSummary,
- PreservePaths, UncondBranch, LongNames, NoOutput);
- FileInfo FI(Options);
- GF.collectLineCounts(FI);
- FI.print(SourceFile, GCNO, GCDA);
-}
-
-int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
-
- cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
-
- for (const auto &SourceFile : SourceFiles)
- reportCoverage(SourceFile);
- return 0;
+ // Give a warning and fall back to gcov
+ errs().changeColor(raw_ostream::RED);
+ errs() << "warning:";
+ // Assume that argv[1] wasn't a command when it stats with a '-' or is a
+ // filename (i.e. contains a '.')
+ if (argc > 1 && !StringRef(argv[1]).startswith("-") &&
+ StringRef(argv[1]).find(".") == StringRef::npos)
+ errs() << " Unrecognized command '" << argv[1] << "'.";
+ errs() << " Using the gcov compatible mode "
+ "(this behaviour may be dropped in the future).";
+ errs().resetColor();
+ errs() << "\n";
+
+ return gcovMain(argc, argv);
}