Index: docs/CommandGuide/llvm-cov.rst =================================================================== --- docs/CommandGuide/llvm-cov.rst +++ docs/CommandGuide/llvm-cov.rst @@ -246,6 +246,10 @@ Show code coverage only for functions that match the given regular expression. +.. option:: -ignore-source-regex= + + Skip source code files with file paths that match the given regular expression. + .. option:: -format= Use the specified output format. The supported formats are: "text", "html". @@ -351,6 +355,10 @@ Show statistics for all function instantiations. Defaults to false. +.. option:: -ignore-source-regex= + + Skip source code files with file paths that match the given regular expression. + .. program:: llvm-cov export .. _llvm-cov-export: @@ -390,3 +398,7 @@ will not export coverage information for smaller units such as individual functions or regions. The result will be the same as produced by :program: `llvm-cov report` command, but presented in JSON format rather than text. + +.. option:: -ignore-source-regex= + + Skip source code files with file paths that match the given regular expression. Index: test/tools/llvm-cov/sources-ignored.test =================================================================== --- /dev/null +++ test/tools/llvm-cov/sources-ignored.test @@ -0,0 +1,58 @@ +######################## +# Test "report" command. +######################## +# Ignore all header files. +RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ +RUN: -path-equivalence=/tmp,%S/Inputs -ignore-source-regex='.*\.h$' \ +RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: | FileCheck -check-prefix=REPORT_IGNORE_HEADERS %s + +REPORT_IGNORE_HEADERS-NOT: {{.*}}dec.h{{.*}} +REPORT_IGNORE_HEADERS-NOT: {{.*}}inc.h{{.*}} +REPORT_IGNORE_HEADERS-NOT: {{.*}}abs.h{{.*}} +REPORT_IGNORE_HEADERS: {{^}}TOTAL 1{{.*}}100.00%{{$}} + +# Ignore all files from "extra" directory. +RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ +RUN: -path-equivalence=/tmp,%S/Inputs -ignore-source-regex='.*extra[/\\].*' \ +RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: | FileCheck -check-prefix=REPORT_IGNORE_DIR %s + +REPORT_IGNORE_DIR-NOT: {{.*}}extra{{[/\\]}}dec.h{{.*}} +REPORT_IGNORE_DIR-NOT: {{.*}}extra{{[/\\]}}inc.h{{.*}} +REPORT_IGNORE_DIR: {{.*}}abs.h{{.*}} +REPORT_IGNORE_DIR: {{.*}}main.cc{{.*}} +REPORT_IGNORE_DIR: {{^}}TOTAL 5{{.*}}100.00%{{$}} + +######################## +# Test "show" command. +######################## +# Ignore all ".cc" files. +RUN: llvm-cov show -instr-profile %S/Inputs/sources_specified/main.profdata \ +RUN: -path-equivalence=/tmp,%S/Inputs -ignore-source-regex='.*\.cc$' \ +RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: | FileCheck -check-prefix=SHOW_IGNORE_CC %s + +# Order of files may differ, check that there are 3 files and not abs.h. +SHOW_IGNORE_CC-NOT: {{.*}}main.cc{{.*}} +SHOW_IGNORE_CC: {{.*}}sources_specified{{.*}} +SHOW_IGNORE_CC: {{.*}}sources_specified{{.*}} +SHOW_IGNORE_CC: {{.*}}sources_specified{{.*}} + +######################## +# Test "export" command. +######################## +# Use a temp .json file as output in a single line. Ignore headers that have +# name in a format of 3 symbols followed by ".h". +RUN: llvm-cov export -instr-profile %S/Inputs/sources_specified/main.profdata \ +RUN: -path-equivalence=/tmp,%S/Inputs -ignore-source-regex='.*...\.h$' \ +RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: > %t.export.json + +RUN: FileCheck -check-prefix=NO-EXPORT_IGNORE_3_SYMBOLS_H %s < %t.export.json +RUN: FileCheck -check-prefix=EXPORT_IGNORE_3_SYMBOLS_H %s < %t.export.json + +NO-EXPORT_IGNORE_3_SYMBOLS_H-NOT: {{"filename":"(/|\\\\)tmp(/|\\\\)sources_specified(/|\\\\)abs.h"}} +NO-EXPORT_IGNORE_3_SYMBOLS_H-NOT: {{"filename":"(/|\\\\)tmp(/|\\\\)sources_specified(/|\\\\)inc.h"}} +NO-EXPORT_IGNORE_3_SYMBOLS_H-NOT: {{"filename":"(/|\\\\)tmp(/|\\\\)sources_specified(/|\\\\)dec.h"}} +EXPORT_IGNORE_3_SYMBOLS_H: {{"filename":"(/|\\\\)tmp(/|\\\\)sources_specified(/|\\\\)main.cc"}} Index: test/tools/llvm-cov/sources-specified.test =================================================================== --- test/tools/llvm-cov/sources-specified.test +++ test/tools/llvm-cov/sources-specified.test @@ -21,7 +21,7 @@ SHOW: {{.*}}sources_specified{{.*}} -# Test "export" command. Use a temp .json file as output is a single line. +# Test "export" command. Use a temp .json file as output in a single line. RUN: llvm-cov export -instr-profile %S/Inputs/sources_specified/main.profdata \ RUN: -path-equivalence=/tmp,%S/Inputs \ RUN: %S/Inputs/sources_specified/main.covmapping \ Index: tools/llvm-cov/CMakeLists.txt =================================================================== --- tools/llvm-cov/CMakeLists.txt +++ tools/llvm-cov/CMakeLists.txt @@ -4,6 +4,7 @@ llvm-cov.cpp gcov.cpp CodeCoverage.cpp + CoverageExporter.cpp CoverageExporterJson.cpp CoverageFilters.cpp CoverageReport.cpp @@ -11,5 +12,6 @@ SourceCoverageView.cpp SourceCoverageViewHTML.cpp SourceCoverageViewText.cpp + SourceFilters.cpp TestingSupport.cpp ) Index: tools/llvm-cov/CodeCoverage.cpp =================================================================== --- tools/llvm-cov/CodeCoverage.cpp +++ tools/llvm-cov/CodeCoverage.cpp @@ -20,6 +20,7 @@ #include "CoverageViewOptions.h" #include "RenderingSupport.h" #include "SourceCoverageView.h" +#include "SourceFilters.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" @@ -95,7 +96,8 @@ std::unique_ptr createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage); - /// \brief Load the coverage mapping data. Return nullptr if an error occurred. + /// \brief Load the coverage mapping data. Return nullptr if an error + /// occurred. std::unique_ptr load(); /// \brief Create a mapping from files in the Coverage data to local copies @@ -126,6 +128,7 @@ std::vector ObjectFilenames; CoverageViewOptions ViewOpts; CoverageFiltersMatchAll Filters; + SourceRegexFilters IgnoreSourceFilters; /// The path to the indexed profile. std::string PGOFilename; @@ -158,7 +161,7 @@ /// Whitelist from -name-whitelist to be used for filtering. std::unique_ptr NameWhitelist; }; -} +} // namespace static std::string getErrorString(const Twine &Message, StringRef Whence, bool Warning) { @@ -189,7 +192,8 @@ return; } sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true); - SourceFiles.emplace_back(EffectivePath.str()); + if (!IgnoreSourceFilters.matches(EffectivePath)) + SourceFiles.emplace_back(EffectivePath.str()); } void CodeCoverageTool::collectPaths(const std::string &Path) { @@ -210,8 +214,8 @@ if (llvm::sys::fs::is_directory(Status)) { std::error_code EC; - for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E; - F != E; F.increment(EC)) { + for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E; F != E; + F.increment(EC)) { if (EC) { warning(EC.message(), F->path()); @@ -278,9 +282,9 @@ return nullptr; auto Expansions = FunctionCoverage.getExpansions(); - auto View = SourceCoverageView::create(DC.demangle(Function.Name), - SourceBuffer.get(), ViewOpts, - std::move(FunctionCoverage)); + auto View = + SourceCoverageView::create(DC.demangle(Function.Name), SourceBuffer.get(), + ViewOpts, std::move(FunctionCoverage)); attachExpansionSubViews(*View, Expansions, Coverage); return View; @@ -415,7 +419,8 @@ // Convert input files from local paths to coverage data file paths. StringMap InvRemappedFilenames; for (const auto &RemappedFilename : RemappedFilenames) - InvRemappedFilenames[RemappedFilename.getValue()] = RemappedFilename.getKey(); + InvRemappedFilenames[RemappedFilename.getValue()] = + RemappedFilename.getKey(); for (std::string &Filename : SourceFiles) { SmallString<128> NativeFilename; @@ -597,6 +602,12 @@ "regular expression"), cl::ZeroOrMore, cl::cat(FilteringCategory)); + cl::list IgnoreSourceRegexFilters( + "ignore-source-regex", cl::Optional, + cl::desc("Skip source code files with file paths that match the given " + "regular expression."), + cl::ZeroOrMore, cl::cat(FilteringCategory)); + cl::opt RegionCoverageLtFilter( "region-coverage-lt", cl::Optional, cl::desc("Show code coverage only for functions with region coverage " @@ -630,8 +641,7 @@ cl::opt RegionSummary( "show-region-summary", cl::Optional, - cl::desc("Show region statistics in summary table"), - cl::init(true)); + cl::desc("Show region statistics in summary table"), cl::init(true)); cl::opt InstantiationSummary( "show-instantiation-summary", cl::Optional, @@ -701,7 +711,7 @@ error(SpecialCaseListErr); } - // Create the function filters + // Create the function filters. if (!NameFilters.empty() || NameWhitelist || !NameRegexFilters.empty()) { auto NameFilterer = llvm::make_unique(); for (const auto &Name : NameFilters) @@ -714,6 +724,11 @@ llvm::make_unique(Regex)); Filters.push_back(std::move(NameFilterer)); } + + // Create the source files filters. + for (const auto &RE : IgnoreSourceRegexFilters) + IgnoreSourceFilters.push_back(llvm::make_unique(RE)); + if (RegionCoverageLtFilter.getNumOccurrences() || RegionCoverageGtFilter.getNumOccurrences() || LineCoverageLtFilter.getNumOccurrences() || @@ -862,8 +877,11 @@ if (SourceFiles.empty()) // Get the source files from the function coverage mapping. - for (StringRef Filename : Coverage->getUniqueSourceFiles()) - SourceFiles.push_back(Filename); + for (StringRef Filename : Coverage->getUniqueSourceFiles()) { + // Apply ignore source files filters. + if (!IgnoreSourceFilters.matches(Filename)) + SourceFiles.push_back(Filename); + } // Create an index out of the source files. if (ViewOpts.hasOutputDirectory()) { @@ -920,9 +938,8 @@ // If NumThreads is not specified, auto-detect a good default. if (NumThreads == 0) - NumThreads = - std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), - unsigned(SourceFiles.size()))); + NumThreads = std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), + unsigned(SourceFiles.size()))); if (!ViewOpts.hasOutputDirectory() || NumThreads == 1) { for (const std::string &SourceFile : SourceFiles) @@ -962,7 +979,7 @@ CoverageReport Report(ViewOpts, *Coverage.get()); if (!ShowFunctionSummaries) { if (SourceFiles.empty()) - Report.renderFileReports(llvm::outs()); + Report.renderFileReports(llvm::outs(), IgnoreSourceFilters); else Report.renderFileReports(llvm::outs(), SourceFiles); } else { @@ -998,7 +1015,7 @@ auto Exporter = CoverageExporterJson(*Coverage.get(), ViewOpts, outs()); if (SourceFiles.empty()) - Exporter.renderRoot(); + Exporter.renderRoot(IgnoreSourceFilters); else Exporter.renderRoot(SourceFiles); Index: tools/llvm-cov/CoverageExporter.h =================================================================== --- tools/llvm-cov/CoverageExporter.h +++ tools/llvm-cov/CoverageExporter.h @@ -16,6 +16,7 @@ #include "CoverageSummaryInfo.h" #include "CoverageViewOptions.h" +#include "SourceFilters.h" #include "llvm/ProfileData/Coverage/CoverageMapping.h" namespace llvm { @@ -40,7 +41,7 @@ virtual ~CoverageExporter(){}; /// \brief Render the CoverageMapping object. - virtual void renderRoot() = 0; + virtual void renderRoot(const SourceRegexFilters &IgnoreFilters); /// \brief Render the CoverageMapping object for specified source files. virtual void renderRoot(const std::vector &SourceFiles) = 0; Index: tools/llvm-cov/CoverageExporter.cpp =================================================================== --- /dev/null +++ tools/llvm-cov/CoverageExporter.cpp @@ -0,0 +1,26 @@ +//===- CoverageExporter.cpp - Code coverage exporter ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class defines a code coverage exporter interface. +// +//===----------------------------------------------------------------------===// + +#include "CoverageExporter.h" + +using namespace llvm; + +// Default implementation that does filtering of the source files. +void CoverageExporter::renderRoot(const SourceRegexFilters &IgnoreFilters) { + std::vector SourceFiles; + for (StringRef SF : Coverage.getUniqueSourceFiles()) { + if (!IgnoreFilters.matches(SF)) + SourceFiles.emplace_back(SF); + } + renderRoot(SourceFiles); +} Index: tools/llvm-cov/CoverageExporterJson.h =================================================================== --- tools/llvm-cov/CoverageExporterJson.h +++ tools/llvm-cov/CoverageExporterJson.h @@ -101,7 +101,9 @@ const CoverageViewOptions &Options, raw_ostream &OS); /// \brief Render the CoverageMapping object. - void renderRoot() override; + void renderRoot(const SourceRegexFilters &IgnoreFilters) { + CoverageExporter::renderRoot(IgnoreFilters); + }; /// \brief Render the CoverageMapping object for specified source files. void renderRoot(const std::vector &SourceFiles) override; Index: tools/llvm-cov/CoverageExporterJson.cpp =================================================================== --- tools/llvm-cov/CoverageExporterJson.cpp +++ tools/llvm-cov/CoverageExporterJson.cpp @@ -117,13 +117,6 @@ OS << "]"; } -void CoverageExporterJson::renderRoot() { - std::vector SourceFiles; - for (StringRef SF : Coverage.getUniqueSourceFiles()) - SourceFiles.emplace_back(SF); - renderRoot(SourceFiles); -} - void CoverageExporterJson::renderRoot( const std::vector &SourceFiles) { // Start Root of JSON object. @@ -218,11 +211,11 @@ void CoverageExporterJson::renderFile(const std::string &Filename, const FileCoverageSummary &FileReport) { - // Start File. + // Start File. emitDictStart(); emitDictElement("filename", Filename); - + if (!Options.ExportSummaryOnly) { // Calculate and render detailed coverage information for given file. auto FileCoverage = Coverage.getCoverageForFile(Filename); @@ -236,7 +229,6 @@ emitDictEnd(); } - void CoverageExporterJson::renderFileCoverage( const coverage::CoverageData &FileCoverage, const FileCoverageSummary &FileReport) { Index: tools/llvm-cov/CoverageReport.h =================================================================== --- tools/llvm-cov/CoverageReport.h +++ tools/llvm-cov/CoverageReport.h @@ -17,6 +17,7 @@ #include "CoverageFilters.h" #include "CoverageSummaryInfo.h" #include "CoverageViewOptions.h" +#include "SourceFilters.h" namespace llvm { @@ -44,16 +45,16 @@ const CoverageViewOptions &Options, const CoverageFilter &Filters = CoverageFiltersMatchAll()); - static void - prepareSingleFileReport(const StringRef Filename, - const coverage::CoverageMapping *Coverage, - const CoverageViewOptions &Options, - const unsigned LCP, - FileCoverageSummary *FileReport, - const CoverageFilter *Filters); + static void prepareSingleFileReport(const StringRef Filename, + const coverage::CoverageMapping *Coverage, + const CoverageViewOptions &Options, + const unsigned LCP, + FileCoverageSummary *FileReport, + const CoverageFilter *Filters); /// Render file reports for every unique file in the coverage mapping. - void renderFileReports(raw_ostream &OS) const; + void renderFileReports(raw_ostream &OS, + const SourceRegexFilters &IgnoreFilters) const; /// Render file reports for the files specified in \p Files. void renderFileReports(raw_ostream &OS, ArrayRef Files) const; Index: tools/llvm-cov/CoverageReport.cpp =================================================================== --- tools/llvm-cov/CoverageReport.cpp +++ tools/llvm-cov/CoverageReport.cpp @@ -230,9 +230,10 @@ OS << format("%*u", FileReportColumns[10], (unsigned)File.LineCoverage.getNumLines()); - Options.colored_ostream(OS, LineCoverageColor) << format( - "%*u", FileReportColumns[11], (unsigned)(File.LineCoverage.getNumLines() - - File.LineCoverage.getCovered())); + Options.colored_ostream(OS, LineCoverageColor) + << format("%*u", FileReportColumns[11], + (unsigned)(File.LineCoverage.getNumLines() - + File.LineCoverage.getCovered())); if (File.LineCoverage.getNumLines()) Options.colored_ostream(OS, LineCoverageColor) << format("%*.2f", FileReportColumns[12] - 1, @@ -244,8 +245,7 @@ } void CoverageReport::render(const FunctionCoverageSummary &Function, - const DemangleCache &DC, - raw_ostream &OS) const { + const DemangleCache &DC, raw_ostream &OS) const { auto FuncCoverageColor = determineCoveragePercentageColor(Function.RegionCoverage); auto LineCoverageColor = @@ -321,8 +321,8 @@ } } -void CoverageReport::prepareSingleFileReport(const StringRef Filename, - const coverage::CoverageMapping *Coverage, +void CoverageReport::prepareSingleFileReport( + const StringRef Filename, const coverage::CoverageMapping *Coverage, const CoverageViewOptions &Options, const unsigned LCP, FileCoverageSummary *FileReport, const CoverageFilter *Filters) { for (const auto &Group : Coverage->getInstantiationGroups(Filename)) { @@ -357,9 +357,8 @@ // If NumThreads is not specified, auto-detect a good default. if (NumThreads == 0) - NumThreads = - std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), - unsigned(Files.size()))); + NumThreads = std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), + unsigned(Files.size()))); ThreadPool Pool(NumThreads); @@ -368,8 +367,8 @@ for (StringRef Filename : Files) { FileReports.emplace_back(Filename.drop_front(LCP)); - Pool.async(&CoverageReport::prepareSingleFileReport, Filename, - &Coverage, Options, LCP, &FileReports.back(), &Filters); + Pool.async(&CoverageReport::prepareSingleFileReport, Filename, &Coverage, + Options, LCP, &FileReports.back(), &Filters); } Pool.wait(); @@ -379,15 +378,19 @@ return FileReports; } -void CoverageReport::renderFileReports(raw_ostream &OS) const { +void CoverageReport::renderFileReports( + raw_ostream &OS, const SourceRegexFilters &IgnoreFilters) const { std::vector UniqueSourceFiles; - for (StringRef SF : Coverage.getUniqueSourceFiles()) - UniqueSourceFiles.emplace_back(SF.str()); + for (StringRef SF : Coverage.getUniqueSourceFiles()) { + // Apply ignore source files filters. + if (!IgnoreFilters.matches(SF)) + UniqueSourceFiles.emplace_back(SF.str()); + } renderFileReports(OS, UniqueSourceFiles); } -void CoverageReport::renderFileReports( - raw_ostream &OS, ArrayRef Files) const { +void CoverageReport::renderFileReports(raw_ostream &OS, + ArrayRef Files) const { renderFileReports(OS, Files, CoverageFiltersMatchAll()); } Index: tools/llvm-cov/SourceFilters.h =================================================================== --- /dev/null +++ tools/llvm-cov/SourceFilters.h @@ -0,0 +1,59 @@ +//===- SourceFilters.h - Source file names 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 source file names. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_SOURCEFILTERS_H +#define LLVM_COV_SOURCEFILTERS_H + +#include "llvm/ADT/StringRef.h" +#include + +namespace llvm { + +/// \brief Matches specific filename that pass the requirement of this filter. +class SourceFilter { +public: + virtual ~SourceFilter() {} + + /// \brief Return true if the filename passes the requirements of this filter. + virtual bool matches(StringRef Filename) const = 0; +}; + +/// \brief Matches source files whose name matches a certain regular expression. +class SourceRegexFilter : public SourceFilter { + StringRef Regex; + +public: + SourceRegexFilter(StringRef Regex) : Regex(Regex) {} + + bool matches(StringRef Filename) const override; +}; + +/// \brief A collection of filters. +/// Matches source files that match any filters contained +/// in an instance of this class. +class SourceRegexFilters : public SourceFilter { +protected: + std::vector> Filters; + +public: + /// \brief Append a filter to this collection. + void push_back(std::unique_ptr Filter); + + bool empty() const { return Filters.empty(); } + + bool matches(StringRef Filename) const override; +}; + +} // namespace llvm + +#endif // LLVM_COV_SOURCEFILTERS_H Index: tools/llvm-cov/SourceFilters.cpp =================================================================== --- /dev/null +++ tools/llvm-cov/SourceFilters.cpp @@ -0,0 +1,33 @@ +//===- SourceFilters.cpp - Source file names 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 source file names. +// +//===----------------------------------------------------------------------===// + +#include "SourceFilters.h" +#include "llvm/Support/Regex.h" + +using namespace llvm; + +bool SourceRegexFilter::matches(StringRef Filename) const { + return llvm::Regex(Regex).match(Filename); +} + +void SourceRegexFilters::push_back(std::unique_ptr Filter) { + Filters.push_back(std::move(Filter)); +} + +bool SourceRegexFilters::matches(StringRef Filename) const { + for (const auto &Filter : Filters) { + if (Filter->matches(Filename)) + return true; + } + return false; +}