From bfed824b57d14e2ba98ddbaf1a1410cf04a3e279 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Tue, 29 Oct 2019 15:47:23 -0700 Subject: [PATCH] [llvm-cov] Add option to whitelist filenames Add the `-whitelist-filename-regex` option to restrict coverage reporting to file paths that match a whitelist regex. Patch by Michael Daniels! rdar://56720320 --- llvm/docs/CommandGuide/llvm-cov.rst | 4 + .../llvm-cov/whitelist-filename-regex.test | 88 +++++++++++++++++++ llvm/tools/llvm-cov/CodeCoverage.cpp | 25 ++++-- llvm/tools/llvm-cov/CoverageExporter.h | 2 +- llvm/tools/llvm-cov/CoverageExporterJson.cpp | 5 +- llvm/tools/llvm-cov/CoverageExporterJson.h | 2 +- llvm/tools/llvm-cov/CoverageExporterLcov.cpp | 5 +- llvm/tools/llvm-cov/CoverageExporterLcov.h | 2 +- llvm/tools/llvm-cov/CoverageFilters.cpp | 17 ++++ llvm/tools/llvm-cov/CoverageFilters.h | 16 ++++ llvm/tools/llvm-cov/CoverageReport.cpp | 6 +- llvm/tools/llvm-cov/CoverageReport.h | 2 +- 12 files changed, 154 insertions(+), 20 deletions(-) create mode 100644 llvm/test/tools/llvm-cov/whitelist-filename-regex.test diff --git a/llvm/docs/CommandGuide/llvm-cov.rst b/llvm/docs/CommandGuide/llvm-cov.rst index 08123e8cb788..9bc9f7edae21 100644 --- a/llvm/docs/CommandGuide/llvm-cov.rst +++ b/llvm/docs/CommandGuide/llvm-cov.rst @@ -259,6 +259,10 @@ OPTIONS Skip source code files with file paths that match the given regular expression. +.. option:: -whitelist-filename-regex= + + Show code coverage only for files that match the given regular expression. + .. option:: -format= Use the specified output format. The supported formats are: "text", "html". diff --git a/llvm/test/tools/llvm-cov/whitelist-filename-regex.test b/llvm/test/tools/llvm-cov/whitelist-filename-regex.test new file mode 100644 index 000000000000..7039fb532f33 --- /dev/null +++ b/llvm/test/tools/llvm-cov/whitelist-filename-regex.test @@ -0,0 +1,88 @@ +######################## +# Test "report" command. +######################## +# Only source files +RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ +RUN: -path-equivalence=/tmp,%S/Inputs -whitelist-filename-regex='.*\.cc$' \ +RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: | FileCheck -check-prefix=REPORT_WHITELIST_SOURCE %s + +REPORT_WHITELIST_SOURCE-NOT: {{.*}}dec.h{{.*}} +REPORT_WHITELIST_SOURCE-NOT: {{.*}}inc.h{{.*}} +REPORT_WHITELIST_SOURCE-NOT: {{.*}}abs.h{{.*}} +REPORT_WHITELIST_SOURCE: {{.*}}main.cc{{.*}} +REPORT_WHITELIST_SOURCE: {{^}}TOTAL 1{{.*}}100.00%{{$}} + +# Whitelist all files from "extra" directory. +RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ +RUN: -path-equivalence=/tmp,%S/Inputs -whitelist-filename-regex='.*extra[/\\].*' \ +RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: | FileCheck -check-prefix=REPORT_WHITELIST_DIR %s + +REPORT_WHITELIST_DIR: {{.*}}dec.h{{.*}} +REPORT_WHITELIST_DIR: {{.*}}inc.h{{.*}} +REPORT_WHITELIST_DIR-NOT: {{.*}}abs.h{{.*}} +REPORT_WHITELIST_DIR-NOT: {{.*}}main.cc{{.*}} +REPORT_WHITELIST_DIR: {{^}}TOTAL 2{{.*}}50.00%{{$}} + +# Whitelist ignored files +RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ +RUN: -path-equivalence=/tmp,%S/Inputs \ +RUN: -ignore-filename-regex='.*' -whitelist-filename-regex='.*' \ +RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: | FileCheck -check-prefix=REPORT_WHITELIST_IGNORED %s + +REPORT_WHITELIST_IGNORED-NOT: {{.*}}dec.h{{.*}} +REPORT_WHITELIST_IGNORED-NOT: {{.*}}inc.h{{.*}} +REPORT_WHITELIST_IGNORED-NOT: {{.*}}abs.h{{.*}} +REPORT_WHITELIST_IGNORED-NOT: {{.*}}main.cc{{.*}} +REPORT_WHITELIST_IGNORED-NOT: {{^}}TOTAL 0{{.*}}0.00%{{$}} + +# Whitelist 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 -whitelist-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_WHITELIST_DIR_WITH_SOURCES %s + +REPORT_WHITELIST_DIR_WITH_SOURCES: {{.*}}dec.h{{.*}} +REPORT_WHITELIST_DIR_WITH_SOURCES: {{.*}}inc.h{{.*}} +REPORT_WHITELIST_DIR_WITH_SOURCES-NOT: {{.*}}abs.h{{.*}} +REPORT_WHITELIST_DIR_WITH_SOURCES-NOT: {{.*}}main.cc{{.*}} +REPORT_WHITELIST_DIR_WITH_SOURCES: {{^}}TOTAL 2{{.*}}50.00%{{$}} + +######################## +# Test "show" command. +######################## +# Whitelist a couple files +RUN: llvm-cov show -instr-profile %S/Inputs/sources_specified/main.profdata \ +RUN: -path-equivalence=/tmp,%S/Inputs \ +RUN: -whitelist-filename-regex='.*\.cc$' -whitelist-filename-regex='.*abs\.h$' \ +RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: | FileCheck -check-prefix=SHOW_WHITELIST_CC %s + +# Order of files may differ, check that there are 3 files and not abs.h. +SHOW_IGNORE_CC-NOT: {{.*}}main.cc{{.*}} + +SHOW_WHITELIST_CC-NOT: {{.*}}dec.h{{.*}} +SHOW_WHITELIST_CC-NOT: {{.*}}inc.h{{.*}} +SHOW_WHITELIST_CC: {{.*}}sources_specified{{.*}} +SHOW_WHITELIST_CC: {{.*}}sources_specified{{.*}} + +######################## +# Test "export" command. +######################## +# Use a temp .json file as output in a single line. Whitelist headers that have +# an 'a' follow by 2 chars followed by '.h'. +RUN: llvm-cov export -instr-profile %S/Inputs/sources_specified/main.profdata \ +RUN: -path-equivalence=/tmp,%S/Inputs -whitelist-filename-regex='.*a..\.h$' \ +RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: > %t.export.json + +RUN: FileCheck -check-prefix=NO-EXPORT_WHITELIST_3_SYMBOLS_H %s < %t.export.json +RUN: FileCheck -check-prefix=EXPORT_WHITELIST_3_SYMBOLS_H %s < %t.export.json + +NO-EXPORT_WHITELIST_3_SYMBOLS_H-NOT: {{"filename":"(/|\\\\)tmp(/|\\\\)sources_specified(/|\\\\)inc.h"}} +NO-EXPORT_WHITELIST_3_SYMBOLS_H-NOT: {{"filename":"(/|\\\\)tmp(/|\\\\)sources_specified(/|\\\\)dec.h"}} +NO-EXPORT_WHITELIST_3_SYMBOLS_H-NOT: {{"filename":"(/|\\\\)tmp(/|\\\\)sources_specified(/|\\\\)main.cc"}} +EXPORT_WHITELIST_3_SYMBOLS_H: {{"filename":"(/|\\\\)tmp(/|\\\\)sources_specified(/|\\\\)abs.h"}} diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index 7151cfb032f3..79c58450f0dd 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -126,7 +126,7 @@ private: std::vector ObjectFilenames; CoverageViewOptions ViewOpts; CoverageFiltersMatchAll Filters; - CoverageFilters IgnoreFilenameFilters; + FilenameCoverageFilters FilenameFilters; /// The path to the indexed profile. std::string PGOFilename; @@ -190,7 +190,7 @@ void CodeCoverageTool::addCollectedPath(const std::string &Path) { return; } sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true); - if (!IgnoreFilenameFilters.matchesFilename(EffectivePath)) + if (FilenameFilters.matchesFilename(EffectivePath)) SourceFiles.emplace_back(EffectivePath.str()); } @@ -595,6 +595,12 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { "regular expression"), cl::ZeroOrMore, cl::cat(FilteringCategory)); + cl::list WhitelistFilenameRegexFilters( + "whitelist-filename-regex", cl::Optional, + cl::desc("Show code coverage only for file paths that match the given " + "regular expression"), + cl::ZeroOrMore, cl::cat(FilteringCategory)); + cl::list IgnoreFilenameRegexFilters( "ignore-filename-regex", cl::Optional, cl::desc("Skip source code files with file paths that match the given " @@ -744,10 +750,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { Filters.push_back(std::move(StatFilterer)); } - // Create the ignore filename filters. + // Create the filename filters. for (const auto &RE : IgnoreFilenameRegexFilters) - IgnoreFilenameFilters.push_back( - std::make_unique(RE)); + FilenameFilters.blacklist(std::make_unique(RE)); + for (const auto &RE : WhitelistFilenameRegexFilters) + FilenameFilters.whitelist(std::make_unique(RE)); if (!Arches.empty()) { for (const std::string &Arch : Arches) { @@ -763,7 +770,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { } } - // IgnoreFilenameFilters are applied even when InputSourceFiles specified. + // FilenameFilters are applied even when InputSourceFiles specified. for (const std::string &File : InputSourceFiles) collectPaths(File); @@ -884,7 +891,7 @@ int CodeCoverageTool::doShow(int argc, const char **argv, if (SourceFiles.empty()) // Get the source files from the function coverage mapping. for (StringRef Filename : Coverage->getUniqueSourceFiles()) { - if (!IgnoreFilenameFilters.matchesFilename(Filename)) + if (FilenameFilters.matchesFilename(Filename)) SourceFiles.push_back(Filename); } @@ -988,7 +995,7 @@ int CodeCoverageTool::doReport(int argc, const char **argv, CoverageReport Report(ViewOpts, *Coverage.get()); if (!ShowFunctionSummaries) { if (SourceFiles.empty()) - Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters); + Report.renderFileReports(llvm::outs(), FilenameFilters); else Report.renderFileReports(llvm::outs(), SourceFiles); } else { @@ -1054,7 +1061,7 @@ int CodeCoverageTool::doExport(int argc, const char **argv, } if (SourceFiles.empty()) - Exporter->renderRoot(IgnoreFilenameFilters); + Exporter->renderRoot(FilenameFilters); else Exporter->renderRoot(SourceFiles); diff --git a/llvm/tools/llvm-cov/CoverageExporter.h b/llvm/tools/llvm-cov/CoverageExporter.h index 751e55dc0916..03a7447e934c 100644 --- a/llvm/tools/llvm-cov/CoverageExporter.h +++ b/llvm/tools/llvm-cov/CoverageExporter.h @@ -40,7 +40,7 @@ public: virtual ~CoverageExporter(){}; /// Render the CoverageMapping object. - virtual void renderRoot(const CoverageFilters &IgnoreFilters) = 0; + virtual void renderRoot(const FilenameCoverageFilters &FilenameFilters) = 0; /// Render the CoverageMapping object for specified source files. virtual void renderRoot(ArrayRef SourceFiles) = 0; diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp index 181d428ed9d8..204571b7ee8f 100644 --- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -190,10 +190,11 @@ json::Array renderFunctions( } // end anonymous namespace -void CoverageExporterJson::renderRoot(const CoverageFilters &IgnoreFilters) { +void CoverageExporterJson::renderRoot( + const FilenameCoverageFilters &FilenameFilters) { std::vector SourceFiles; for (StringRef SF : Coverage.getUniqueSourceFiles()) { - if (!IgnoreFilters.matchesFilename(SF)) + if (FilenameFilters.matchesFilename(SF)) SourceFiles.emplace_back(SF); } renderRoot(SourceFiles); diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.h b/llvm/tools/llvm-cov/CoverageExporterJson.h index c19475005552..c0f4f5b4beb4 100644 --- a/llvm/tools/llvm-cov/CoverageExporterJson.h +++ b/llvm/tools/llvm-cov/CoverageExporterJson.h @@ -24,7 +24,7 @@ public: : CoverageExporter(CoverageMapping, Options, OS) {} /// Render the CoverageMapping object. - void renderRoot(const CoverageFilters &IgnoreFilters) override; + void renderRoot(const FilenameCoverageFilters &FilenameFilters) override; /// Render the CoverageMapping object for specified source files. void renderRoot(ArrayRef SourceFiles) override; diff --git a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp index d9b0c3b0d7a8..bc9dce852958 100644 --- a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp @@ -106,10 +106,11 @@ void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage, } // end anonymous namespace -void CoverageExporterLcov::renderRoot(const CoverageFilters &IgnoreFilters) { +void CoverageExporterLcov::renderRoot( + const FilenameCoverageFilters &FilenameFilters) { std::vector SourceFiles; for (StringRef SF : Coverage.getUniqueSourceFiles()) { - if (!IgnoreFilters.matchesFilename(SF)) + if (FilenameFilters.matchesFilename(SF)) SourceFiles.emplace_back(SF); } renderRoot(SourceFiles); diff --git a/llvm/tools/llvm-cov/CoverageExporterLcov.h b/llvm/tools/llvm-cov/CoverageExporterLcov.h index e8a260bf4937..0ffeb1111b8b 100644 --- a/llvm/tools/llvm-cov/CoverageExporterLcov.h +++ b/llvm/tools/llvm-cov/CoverageExporterLcov.h @@ -24,7 +24,7 @@ public: : CoverageExporter(CoverageMapping, Options, OS) {} /// Render the CoverageMapping object. - void renderRoot(const CoverageFilters &IgnoreFilters) override; + void renderRoot(const FilenameCoverageFilters &FilenameFilters) override; /// Render the CoverageMapping object for specified source files. void renderRoot(ArrayRef SourceFiles) override; diff --git a/llvm/tools/llvm-cov/CoverageFilters.cpp b/llvm/tools/llvm-cov/CoverageFilters.cpp index ca241e386e87..57d8d5e5be28 100644 --- a/llvm/tools/llvm-cov/CoverageFilters.cpp +++ b/llvm/tools/llvm-cov/CoverageFilters.cpp @@ -83,3 +83,20 @@ bool CoverageFiltersMatchAll::matches( } return true; } + +bool FilenameCoverageFilters::matchesFilename(StringRef Filename) const { + if (!Whitelist.empty() && !Whitelist.matchesFilename(Filename)) { + return false; + } + return !Blacklist.matchesFilename(Filename); +} + +void FilenameCoverageFilters::whitelist( + std::unique_ptr Filter) { + Whitelist.push_back(std::move(Filter)); +} + +void FilenameCoverageFilters::blacklist( + std::unique_ptr Filter) { + Blacklist.push_back(std::move(Filter)); +} diff --git a/llvm/tools/llvm-cov/CoverageFilters.h b/llvm/tools/llvm-cov/CoverageFilters.h index ce56e1607111..26f8fcc0a0da 100644 --- a/llvm/tools/llvm-cov/CoverageFilters.h +++ b/llvm/tools/llvm-cov/CoverageFilters.h @@ -152,6 +152,22 @@ public: const coverage::FunctionRecord &Function) const override; }; +/// A collection of filters. +/// Matches filenames that are whitelisted and not blacklisted. +class FilenameCoverageFilters : public CoverageFilter { +protected: + CoverageFilters Whitelist; + CoverageFilters Blacklist; + +public: + + void whitelist(std::unique_ptr Filter); + void blacklist(std::unique_ptr Filter); + + bool matchesFilename(StringRef Filename) const override; +}; + + } // namespace llvm #endif // LLVM_COV_COVERAGEFILTERS_H diff --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp index 82259542c597..805e465f99c1 100644 --- a/llvm/tools/llvm-cov/CoverageReport.cpp +++ b/llvm/tools/llvm-cov/CoverageReport.cpp @@ -379,11 +379,11 @@ std::vector CoverageReport::prepareFileReports( } void CoverageReport::renderFileReports( - raw_ostream &OS, const CoverageFilters &IgnoreFilenameFilters) const { + raw_ostream &OS, const FilenameCoverageFilters &FilenameFilters) const { std::vector UniqueSourceFiles; for (StringRef SF : Coverage.getUniqueSourceFiles()) { - // Apply ignore source files filters. - if (!IgnoreFilenameFilters.matchesFilename(SF)) + // Apply source files filters. + if (FilenameFilters.matchesFilename(SF)) UniqueSourceFiles.emplace_back(SF.str()); } renderFileReports(OS, UniqueSourceFiles); diff --git a/llvm/tools/llvm-cov/CoverageReport.h b/llvm/tools/llvm-cov/CoverageReport.h index f9a092f510b5..7290586f0a05 100644 --- a/llvm/tools/llvm-cov/CoverageReport.h +++ b/llvm/tools/llvm-cov/CoverageReport.h @@ -53,7 +53,7 @@ public: /// Render file reports for every unique file in the coverage mapping. void renderFileReports(raw_ostream &OS, - const CoverageFilters &IgnoreFilenameFilters) const; + const FilenameCoverageFilters &FilenameFilters) const; /// Render file reports for the files specified in \p Files. void renderFileReports(raw_ostream &OS, ArrayRef Files) const;