Browse Source

[llvm-cov] Implement -ignore-filename-regex= option for excluding source files.

Summary:
The option is helpful for large projects where it's not feasible to specify sources which
user would like to see in the report. Instead, it allows to black-list specific sources via
regular expressions (e.g. now it's possible to skip all files that have "test" in its name).

This also partially fixes https://bugs.llvm.org/show_bug.cgi?id=34277

Reviewers: vsk, morehouse, liaoyuke

Reviewed By: vsk

Subscribers: kcc, mgorny, llvm-commits

Differential Revision: https://reviews.llvm.org/D43907

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@329581 91177308-0d34-0410-b5e6-96231b3b80d8
Max Moroz 7 years ago
parent
commit
06fa683388

+ 12 - 0
docs/CommandGuide/llvm-cov.rst

@@ -246,6 +246,10 @@ OPTIONS
 
 
  Show code coverage only for functions that match the given regular expression.
  Show code coverage only for functions that match the given regular expression.
 
 
+.. option:: -ignore-filename-regex=<PATTERN>
+
+ Skip source code files with file paths that match the given regular expression.
+
 .. option:: -format=<FORMAT>
 .. option:: -format=<FORMAT>
 
 
  Use the specified output format. The supported formats are: "text", "html".
  Use the specified output format. The supported formats are: "text", "html".
@@ -351,6 +355,10 @@ OPTIONS
 
 
  Show statistics for all function instantiations. Defaults to false.
  Show statistics for all function instantiations. Defaults to false.
 
 
+.. option:: -ignore-filename-regex=<PATTERN>
+
+ Skip source code files with file paths that match the given regular expression.
+
 .. program:: llvm-cov export
 .. program:: llvm-cov export
 
 
 .. _llvm-cov-export:
 .. _llvm-cov-export:
@@ -390,3 +398,7 @@ OPTIONS
  will not export coverage information for smaller units such as individual
  will not export coverage information for smaller units such as individual
  functions or regions. The result will be the same as produced by :program:
  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.
  `llvm-cov report` command, but presented in JSON format rather than text.
+
+.. option:: -ignore-filename-regex=<PATTERN>
+
+ Skip source code files with file paths that match the given regular expression.

+ 71 - 0
test/tools/llvm-cov/ignore-filename-regex.test

@@ -0,0 +1,71 @@
+########################
+# 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-filename-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-filename-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%{{$}}
+
+# Ignore all files from "extra" directory even when SOURCES specified.
+RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \
+RUN:   -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*extra[/\\].*' \
+RUN:   %S/Inputs/sources_specified/main.covmapping \
+RUN:   %S/Inputs/sources_specified/extra %S/Inputs/sources_specified/abs.h \
+RUN:   | FileCheck -check-prefix=REPORT_IGNORE_DIR_WITH_SOURCES %s
+
+REPORT_IGNORE_DIR_WITH_SOURCES-NOT: {{.*}}extra{{[/\\]}}dec.h{{.*}}
+REPORT_IGNORE_DIR_WITH_SOURCES-NOT: {{.*}}extra{{[/\\]}}inc.h{{.*}}
+REPORT_IGNORE_DIR_WITH_SOURCES-NOT: {{.*}}main.cc{{.*}}
+REPORT_IGNORE_DIR_WITH_SOURCES: {{.*}}abs.h{{.*}}
+REPORT_IGNORE_DIR_WITH_SOURCES: {{^}}TOTAL 4{{.*}}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-filename-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-filename-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"}}

+ 22 - 5
tools/llvm-cov/CodeCoverage.cpp

@@ -126,6 +126,7 @@ private:
   std::vector<StringRef> ObjectFilenames;
   std::vector<StringRef> ObjectFilenames;
   CoverageViewOptions ViewOpts;
   CoverageViewOptions ViewOpts;
   CoverageFiltersMatchAll Filters;
   CoverageFiltersMatchAll Filters;
+  CoverageFilters IgnoreFilenameFilters;
 
 
   /// The path to the indexed profile.
   /// The path to the indexed profile.
   std::string PGOFilename;
   std::string PGOFilename;
@@ -189,7 +190,8 @@ void CodeCoverageTool::addCollectedPath(const std::string &Path) {
     return;
     return;
   }
   }
   sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true);
   sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true);
-  SourceFiles.emplace_back(EffectivePath.str());
+  if (!IgnoreFilenameFilters.matchesFilename(EffectivePath))
+    SourceFiles.emplace_back(EffectivePath.str());
 }
 }
 
 
 void CodeCoverageTool::collectPaths(const std::string &Path) {
 void CodeCoverageTool::collectPaths(const std::string &Path) {
@@ -597,6 +599,12 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
                "regular expression"),
                "regular expression"),
       cl::ZeroOrMore, cl::cat(FilteringCategory));
       cl::ZeroOrMore, cl::cat(FilteringCategory));
 
 
+  cl::list<std::string> IgnoreFilenameRegexFilters(
+      "ignore-filename-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<double> RegionCoverageLtFilter(
   cl::opt<double> RegionCoverageLtFilter(
       "region-coverage-lt", cl::Optional,
       "region-coverage-lt", cl::Optional,
       cl::desc("Show code coverage only for functions with region coverage "
       cl::desc("Show code coverage only for functions with region coverage "
@@ -714,6 +722,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
             llvm::make_unique<NameRegexCoverageFilter>(Regex));
             llvm::make_unique<NameRegexCoverageFilter>(Regex));
       Filters.push_back(std::move(NameFilterer));
       Filters.push_back(std::move(NameFilterer));
     }
     }
+
     if (RegionCoverageLtFilter.getNumOccurrences() ||
     if (RegionCoverageLtFilter.getNumOccurrences() ||
         RegionCoverageGtFilter.getNumOccurrences() ||
         RegionCoverageGtFilter.getNumOccurrences() ||
         LineCoverageLtFilter.getNumOccurrences() ||
         LineCoverageLtFilter.getNumOccurrences() ||
@@ -734,6 +743,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
       Filters.push_back(std::move(StatFilterer));
       Filters.push_back(std::move(StatFilterer));
     }
     }
 
 
+    // Create the ignore filename filters.
+    for (const auto &RE : IgnoreFilenameRegexFilters)
+      IgnoreFilenameFilters.push_back(
+          llvm::make_unique<NameRegexCoverageFilter>(RE));
+
     if (!Arches.empty()) {
     if (!Arches.empty()) {
       for (const std::string &Arch : Arches) {
       for (const std::string &Arch : Arches) {
         if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
         if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
@@ -748,6 +762,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
       }
       }
     }
     }
 
 
+    // IgnoreFilenameFilters are applied even when InputSourceFiles specified.
     for (const std::string &File : InputSourceFiles)
     for (const std::string &File : InputSourceFiles)
       collectPaths(File);
       collectPaths(File);
 
 
@@ -862,8 +877,10 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
 
 
   if (SourceFiles.empty())
   if (SourceFiles.empty())
     // Get the source files from the function coverage mapping.
     // Get the source files from the function coverage mapping.
-    for (StringRef Filename : Coverage->getUniqueSourceFiles())
-      SourceFiles.push_back(Filename);
+    for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
+      if (!IgnoreFilenameFilters.matchesFilename(Filename))
+        SourceFiles.push_back(Filename);
+    }
 
 
   // Create an index out of the source files.
   // Create an index out of the source files.
   if (ViewOpts.hasOutputDirectory()) {
   if (ViewOpts.hasOutputDirectory()) {
@@ -962,7 +979,7 @@ int CodeCoverageTool::doReport(int argc, const char **argv,
   CoverageReport Report(ViewOpts, *Coverage.get());
   CoverageReport Report(ViewOpts, *Coverage.get());
   if (!ShowFunctionSummaries) {
   if (!ShowFunctionSummaries) {
     if (SourceFiles.empty())
     if (SourceFiles.empty())
-      Report.renderFileReports(llvm::outs());
+      Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters);
     else
     else
       Report.renderFileReports(llvm::outs(), SourceFiles);
       Report.renderFileReports(llvm::outs(), SourceFiles);
   } else {
   } else {
@@ -998,7 +1015,7 @@ int CodeCoverageTool::doExport(int argc, const char **argv,
   auto Exporter = CoverageExporterJson(*Coverage.get(), ViewOpts, outs());
   auto Exporter = CoverageExporterJson(*Coverage.get(), ViewOpts, outs());
 
 
   if (SourceFiles.empty())
   if (SourceFiles.empty())
-    Exporter.renderRoot();
+    Exporter.renderRoot(IgnoreFilenameFilters);
   else
   else
     Exporter.renderRoot(SourceFiles);
     Exporter.renderRoot(SourceFiles);
 
 

+ 2 - 1
tools/llvm-cov/CoverageExporter.h

@@ -14,6 +14,7 @@
 #ifndef LLVM_COV_COVERAGEEXPORTER_H
 #ifndef LLVM_COV_COVERAGEEXPORTER_H
 #define LLVM_COV_COVERAGEEXPORTER_H
 #define LLVM_COV_COVERAGEEXPORTER_H
 
 
+#include "CoverageFilters.h"
 #include "CoverageSummaryInfo.h"
 #include "CoverageSummaryInfo.h"
 #include "CoverageViewOptions.h"
 #include "CoverageViewOptions.h"
 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
@@ -40,7 +41,7 @@ public:
   virtual ~CoverageExporter(){};
   virtual ~CoverageExporter(){};
 
 
   /// \brief Render the CoverageMapping object.
   /// \brief Render the CoverageMapping object.
-  virtual void renderRoot() = 0;
+  virtual void renderRoot(const CoverageFilters &IgnoreFilenameFilters) = 0;
 
 
   /// \brief Render the CoverageMapping object for specified source files.
   /// \brief Render the CoverageMapping object for specified source files.
   virtual void renderRoot(const std::vector<std::string> &SourceFiles) = 0;
   virtual void renderRoot(const std::vector<std::string> &SourceFiles) = 0;

+ 8 - 5
tools/llvm-cov/CoverageExporterJson.cpp

@@ -117,10 +117,13 @@ void CoverageExporterJson::emitArrayEnd() {
   OS << "]";
   OS << "]";
 }
 }
 
 
-void CoverageExporterJson::renderRoot() {
+void CoverageExporterJson::renderRoot(
+    const CoverageFilters &IgnoreFilenameFilters) {
   std::vector<std::string> SourceFiles;
   std::vector<std::string> SourceFiles;
-  for (StringRef SF : Coverage.getUniqueSourceFiles())
-    SourceFiles.emplace_back(SF);
+  for (StringRef SF : Coverage.getUniqueSourceFiles()) {
+    if (!IgnoreFilenameFilters.matchesFilename(SF))
+      SourceFiles.emplace_back(SF);
+  }
   renderRoot(SourceFiles);
   renderRoot(SourceFiles);
 }
 }
 
 
@@ -218,11 +221,11 @@ void CoverageExporterJson::renderFiles(
 
 
 void CoverageExporterJson::renderFile(const std::string &Filename,
 void CoverageExporterJson::renderFile(const std::string &Filename,
                                       const FileCoverageSummary &FileReport) {
                                       const FileCoverageSummary &FileReport) {
-   // Start File.
+  // Start File.
   emitDictStart();
   emitDictStart();
 
 
   emitDictElement("filename", Filename);
   emitDictElement("filename", Filename);
-  
+
   if (!Options.ExportSummaryOnly) {
   if (!Options.ExportSummaryOnly) {
     // Calculate and render detailed coverage information for given file.
     // Calculate and render detailed coverage information for given file.
     auto FileCoverage = Coverage.getCoverageForFile(Filename);
     auto FileCoverage = Coverage.getCoverageForFile(Filename);

+ 1 - 1
tools/llvm-cov/CoverageExporterJson.h

@@ -101,7 +101,7 @@ public:
                        const CoverageViewOptions &Options, raw_ostream &OS);
                        const CoverageViewOptions &Options, raw_ostream &OS);
 
 
   /// \brief Render the CoverageMapping object.
   /// \brief Render the CoverageMapping object.
-  void renderRoot() override;
+  void renderRoot(const CoverageFilters &IgnoreFilenameFilters) override;
 
 
   /// \brief Render the CoverageMapping object for specified source files.
   /// \brief Render the CoverageMapping object for specified source files.
   void renderRoot(const std::vector<std::string> &SourceFiles) override;
   void renderRoot(const std::vector<std::string> &SourceFiles) override;

+ 12 - 0
tools/llvm-cov/CoverageFilters.cpp

@@ -30,6 +30,10 @@ bool NameRegexCoverageFilter::matches(
   return llvm::Regex(Regex).match(Function.Name);
   return llvm::Regex(Regex).match(Function.Name);
 }
 }
 
 
+bool NameRegexCoverageFilter::matchesFilename(StringRef Filename) const {
+  return llvm::Regex(Regex).match(Filename);
+}
+
 bool NameWhitelistCoverageFilter::matches(
 bool NameWhitelistCoverageFilter::matches(
     const coverage::CoverageMapping &,
     const coverage::CoverageMapping &,
     const coverage::FunctionRecord &Function) const {
     const coverage::FunctionRecord &Function) const {
@@ -63,6 +67,14 @@ bool CoverageFilters::matches(const coverage::CoverageMapping &CM,
   return false;
   return false;
 }
 }
 
 
+bool CoverageFilters::matchesFilename(StringRef Filename) const {
+  for (const auto &Filter : Filters) {
+    if (Filter->matchesFilename(Filename))
+      return true;
+  }
+  return false;
+}
+
 bool CoverageFiltersMatchAll::matches(
 bool CoverageFiltersMatchAll::matches(
     const coverage::CoverageMapping &CM,
     const coverage::CoverageMapping &CM,
     const coverage::FunctionRecord &Function) const {
     const coverage::FunctionRecord &Function) const {

+ 9 - 0
tools/llvm-cov/CoverageFilters.h

@@ -32,6 +32,11 @@ public:
                        const coverage::FunctionRecord &Function) const {
                        const coverage::FunctionRecord &Function) const {
     return true;
     return true;
   }
   }
+
+  /// \brief Return true if the filename passes the requirements of this filter.
+  virtual bool matchesFilename(StringRef Filename) const {
+    return true;
+  }
 };
 };
 
 
 /// \brief Matches functions that contain a specific string in their name.
 /// \brief Matches functions that contain a specific string in their name.
@@ -54,6 +59,8 @@ public:
 
 
   bool matches(const coverage::CoverageMapping &CM,
   bool matches(const coverage::CoverageMapping &CM,
                const coverage::FunctionRecord &Function) const override;
                const coverage::FunctionRecord &Function) const override;
+
+  bool matchesFilename(StringRef Filename) const override;
 };
 };
 
 
 /// \brief Matches functions whose name appears in a SpecialCaseList in the
 /// \brief Matches functions whose name appears in a SpecialCaseList in the
@@ -133,6 +140,8 @@ public:
 
 
   bool matches(const coverage::CoverageMapping &CM,
   bool matches(const coverage::CoverageMapping &CM,
                const coverage::FunctionRecord &Function) const override;
                const coverage::FunctionRecord &Function) const override;
+
+  bool matchesFilename(StringRef Filename) const override;
 };
 };
 
 
 /// \brief A collection of filters.
 /// \brief A collection of filters.

+ 7 - 3
tools/llvm-cov/CoverageReport.cpp

@@ -379,10 +379,14 @@ std::vector<FileCoverageSummary> CoverageReport::prepareFileReports(
   return FileReports;
   return FileReports;
 }
 }
 
 
-void CoverageReport::renderFileReports(raw_ostream &OS) const {
+void CoverageReport::renderFileReports(
+    raw_ostream &OS, const CoverageFilters &IgnoreFilenameFilters) const {
   std::vector<std::string> UniqueSourceFiles;
   std::vector<std::string> UniqueSourceFiles;
-  for (StringRef SF : Coverage.getUniqueSourceFiles())
-    UniqueSourceFiles.emplace_back(SF.str());
+  for (StringRef SF : Coverage.getUniqueSourceFiles()) {
+    // Apply ignore source files filters.
+    if (!IgnoreFilenameFilters.matchesFilename(SF))
+      UniqueSourceFiles.emplace_back(SF.str());
+  }
   renderFileReports(OS, UniqueSourceFiles);
   renderFileReports(OS, UniqueSourceFiles);
 }
 }
 
 

+ 2 - 1
tools/llvm-cov/CoverageReport.h

@@ -53,7 +53,8 @@ public:
                           const CoverageFilter *Filters);
                           const CoverageFilter *Filters);
 
 
   /// Render file reports for every unique file in the coverage mapping.
   /// Render file reports for every unique file in the coverage mapping.
-  void renderFileReports(raw_ostream &OS) const;
+  void renderFileReports(raw_ostream &OS,
+                         const CoverageFilters &IgnoreFilenameFilters) const;
 
 
   /// Render file reports for the files specified in \p Files.
   /// Render file reports for the files specified in \p Files.
   void renderFileReports(raw_ostream &OS, ArrayRef<std::string> Files) const;
   void renderFileReports(raw_ostream &OS, ArrayRef<std::string> Files) const;