diff options
Diffstat (limited to 'tools/llvm-cov')
-rw-r--r-- | tools/llvm-cov/Android.mk | 15 | ||||
-rw-r--r-- | tools/llvm-cov/CMakeLists.txt | 10 | ||||
-rw-r--r-- | tools/llvm-cov/CodeCoverage.cpp | 484 | ||||
-rw-r--r-- | tools/llvm-cov/CoverageFilters.cpp | 59 | ||||
-rw-r--r-- | tools/llvm-cov/CoverageFilters.h | 127 | ||||
-rw-r--r-- | tools/llvm-cov/CoverageReport.cpp | 202 | ||||
-rw-r--r-- | tools/llvm-cov/CoverageReport.h | 40 | ||||
-rw-r--r-- | tools/llvm-cov/CoverageSummary.cpp | 64 | ||||
-rw-r--r-- | tools/llvm-cov/CoverageSummary.h | 45 | ||||
-rw-r--r-- | tools/llvm-cov/CoverageSummaryInfo.cpp | 96 | ||||
-rw-r--r-- | tools/llvm-cov/CoverageSummaryInfo.h | 133 | ||||
-rw-r--r-- | tools/llvm-cov/CoverageViewOptions.h | 36 | ||||
-rw-r--r-- | tools/llvm-cov/LLVMBuild.txt | 2 | ||||
-rw-r--r-- | tools/llvm-cov/Makefile | 2 | ||||
-rw-r--r-- | tools/llvm-cov/RenderingSupport.h | 60 | ||||
-rw-r--r-- | tools/llvm-cov/SourceCoverageView.cpp | 260 | ||||
-rw-r--r-- | tools/llvm-cov/SourceCoverageView.h | 162 | ||||
-rw-r--r-- | tools/llvm-cov/TestingSupport.cpp | 91 | ||||
-rw-r--r-- | tools/llvm-cov/gcov.cpp | 153 | ||||
-rw-r--r-- | tools/llvm-cov/llvm-cov.cpp | 178 |
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); } |