[Clang] Add options -fprofile-filter-files and -fprofile-exclude-files to filter the files to instrument with gcov

Summary:
These options are taking regex separated by colons to filter files.
- if both are empty then all files are instrumented
- if -fprofile-filter-files is empty then all the filenames matching any of the regex from exclude are not instrumented
- if -fprofile-exclude-files is empty then all the filenames matching any of the regex from filter are instrumented
- if both aren't empty then all the filenames which match any of the regex in filter and which don't match all the regex in filter are instrumented
- this patch is a follow-up of https://reviews.llvm.org/D52033

Reviewers: marco-c, vsk

Reviewed By: marco-c, vsk

Subscribers: cfe-commits, sylvestre.ledru

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

llvm-svn: 346642
This commit is contained in:
Calixte Denizet 2018-11-12 09:12:27 +00:00
parent c6fabeac11
commit cedcc73d93
10 changed files with 182 additions and 0 deletions

View File

@ -64,6 +64,12 @@ Non-comprehensive list of changes in this release
New Compiler Flags
------------------
- ``-fprofile-filter-files=[regexes]`` and ``-fprofile-exclude-files=[regexes]``.
Clang has now options to filter or exclude some files when
instrumenting for gcov-based profiling.
See the :doc:`UsersManual` for details.
- ...
Deprecated Compiler Flags

View File

@ -1871,6 +1871,55 @@ using the ``llvm-cxxmap`` and ``llvm-profdata merge`` tools.
following the Itanium C++ ABI mangling scheme. This covers all C++ targets
supported by Clang other than Windows.
GCOV-based Profiling
--------------------
GCOV is a test coverage program, it helps to know how often a line of code
is executed. When instrumenting the code with ``--coverage`` option, some
counters are added for each edge linking basic blocks.
At compile time, gcno files are generated containing information about
blocks and edges between them. At runtime the counters are incremented and at
exit the counters are dumped in gcda files.
The tool ``llvm-cov gcov`` will parse gcno, gcda and source files to generate
a report ``.c.gcov``.
.. option:: -fprofile-filter-files=[regexes]
Define a list of regexes separated by a semi-colon.
If a file name matches any of the regexes then the file is instrumented.
.. code-block:: console
$ clang --coverage -fprofile-filter-files=".*\.c$" foo.c
For example, this will only instrument files finishing with ``.c``, skipping ``.h`` files.
.. option:: -fprofile-exclude-files=[regexes]
Define a list of regexes separated by a semi-colon.
If a file name doesn't match all the regexes then the file is instrumented.
.. code-block:: console
$ clang --coverage -fprofile-exclude-files="^/usr/include/.*$" foo.c
For example, this will instrument all the files except the ones in ``/usr/include``.
If both options are used then a file is instrumented if its name matches any
of the regexes from ``-fprofile-filter-list`` and doesn't match all the regexes
from ``-fprofile-exclude-list``.
.. code-block:: console
$ clang --coverage -fprofile-exclude-files="^/usr/include/.*$" \
-fprofile-filter-files="^/usr/.*$"
In that case ``/usr/foo/oof.h`` is instrumented since it matches the filter regex and
doesn't match the exclude regex, but ``/usr/include/foo.h`` doesn't since it matches
the exclude regex.
Controlling Debug Information
-----------------------------

View File

@ -768,6 +768,12 @@ def fno_profile_instr_use : Flag<["-"], "fno-profile-instr-use">,
HelpText<"Disable using instrumentation data for profile-guided optimization">;
def fno_profile_use : Flag<["-"], "fno-profile-use">,
Alias<fno_profile_instr_use>;
def fprofile_filter_files_EQ : Joined<["-"], "fprofile-filter-files=">,
Group<f_Group>, Flags<[CC1Option, CoreOption]>,
HelpText<"Instrument only functions from files where names match any regex separated by a semi-colon">;
def fprofile_exclude_files_EQ : Joined<["-"], "fprofile-exclude-files=">,
Group<f_Group>, Flags<[CC1Option, CoreOption]>,
HelpText<"Instrument only functions from files where names don't match all the regexes separated by a semi-colon">;
def faddrsig : Flag<["-"], "faddrsig">, Group<f_Group>, Flags<[CoreOption, CC1Option]>,
HelpText<"Emit an address-significance table">;

View File

@ -127,6 +127,12 @@ public:
/// The filename with path we use for coverage notes files.
std::string CoverageNotesFile;
/// Regexes separated by a semi-colon to filter the files to instrument.
std::string ProfileFilterFiles;
/// Regexes separated by a semi-colon to filter the files to not instrument.
std::string ProfileExcludeFiles;
/// The version string to put into coverage files.
char CoverageVersion[4];

View File

@ -503,6 +503,8 @@ static Optional<GCOVOptions> getGCOVOptions(const CodeGenOptions &CodeGenOpts) {
Options.UseCfgChecksum = CodeGenOpts.CoverageExtraChecksum;
Options.NoRedZone = CodeGenOpts.DisableRedZone;
Options.FunctionNamesInData = !CodeGenOpts.CoverageNoFunctionNamesInData;
Options.Filter = CodeGenOpts.ProfileFilterFiles;
Options.Exclude = CodeGenOpts.ProfileExcludeFiles;
Options.ExitBlockBeforeBody = CodeGenOpts.CoverageExitBlockBeforeBody;
return Options;
}

View File

@ -802,6 +802,29 @@ static void addPGOAndCoverageFlags(Compilation &C, const Driver &D,
CmdArgs.push_back("-fcoverage-mapping");
}
if (Args.hasArg(options::OPT_fprofile_exclude_files_EQ)) {
auto *Arg = Args.getLastArg(options::OPT_fprofile_exclude_files_EQ);
if (!Args.hasArg(options::OPT_coverage))
D.Diag(clang::diag::err_drv_argument_only_allowed_with)
<< "-fprofile-exclude-files="
<< "--coverage";
StringRef v = Arg->getValue();
CmdArgs.push_back(
Args.MakeArgString(Twine("-fprofile-exclude-files=" + v)));
}
if (Args.hasArg(options::OPT_fprofile_filter_files_EQ)) {
auto *Arg = Args.getLastArg(options::OPT_fprofile_exclude_files_EQ);
if (!Args.hasArg(options::OPT_coverage))
D.Diag(clang::diag::err_drv_argument_only_allowed_with)
<< "-fprofile-filter-files="
<< "--coverage";
StringRef v = Arg->getValue();
CmdArgs.push_back(Args.MakeArgString(Twine("-fprofile-filter-files=" + v)));
}
if (C.getArgs().hasArg(options::OPT_c) ||
C.getArgs().hasArg(options::OPT_S)) {
if (Output.isFilename()) {

View File

@ -811,6 +811,10 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
Opts.CoverageExtraChecksum = Args.hasArg(OPT_coverage_cfg_checksum);
Opts.CoverageNoFunctionNamesInData =
Args.hasArg(OPT_coverage_no_function_names_in_data);
Opts.ProfileFilterFiles =
Args.getLastArgValue(OPT_fprofile_filter_files_EQ);
Opts.ProfileExcludeFiles =
Args.getLastArgValue(OPT_fprofile_exclude_files_EQ);
Opts.CoverageExitBlockBeforeBody =
Args.hasArg(OPT_coverage_exit_block_before_body);
if (Args.hasArg(OPT_coverage_version_EQ)) {

View File

@ -0,0 +1 @@
void test1() {}

View File

@ -0,0 +1 @@
void test2() {}

View File

@ -0,0 +1,84 @@
// RUN: %clang_cc1 -emit-llvm -femit-coverage-data -femit-coverage-notes %s -o - \
// RUN: | FileCheck -check-prefix=ALL %s
// RUN: %clang_cc1 -emit-llvm -femit-coverage-data -femit-coverage-notes -fprofile-exclude-files=".*\.h$" %s -o - \
// RUN: | FileCheck -check-prefix=NO-HEADER %s
// RUN: %clang_cc1 -emit-llvm -femit-coverage-data -femit-coverage-notes -fprofile-filter-files=".*\.c$" %s -o - \
// RUN: | FileCheck -check-prefix=NO-HEADER %s
// RUN: %clang_cc1 -emit-llvm -femit-coverage-data -femit-coverage-notes -fprofile-filter-files=".*\.c$;.*1\.h$" %s -o - \
// RUN: | FileCheck -check-prefix=NO-HEADER2 %s
// RUN: %clang_cc1 -emit-llvm -femit-coverage-data -femit-coverage-notes -fprofile-exclude-files=".*2\.h$;.*1\.h$" %s -o - \
// RUN: | FileCheck -check-prefix=JUST-C %s
// RUN: %clang_cc1 -emit-llvm -femit-coverage-data -femit-coverage-notes -fprofile-exclude-files=".*code\-coverage\-filter\.c$" %s -o - \
// RUN: | FileCheck -check-prefix=HEADER %s
// RUN: %clang_cc1 -emit-llvm -femit-coverage-data -femit-coverage-notes -fprofile-filter-files=".*\.c$" -fprofile-exclude-files=".*\.c$" %s -o - \
// RUN: | FileCheck -check-prefix=NONE %s
// RUN: %clang_cc1 -emit-llvm -femit-coverage-data -femit-coverage-notes -fprofile-filter-files=".*\.c$" -fprofile-exclude-files=".*\.h$" %s -o - \
// RUN: | FileCheck -check-prefix=JUST-C %s
#include "Inputs/code-coverage-filter1.h"
#include "Inputs/code-coverage-filter2.h"
void test() {
test1();
test2();
}
// ALL: define void @test1() #0 {{.*}}
// ALL: {{.*}}__llvm_gcov_ctr{{.*}}
// ALL: ret void
// ALL: define void @test2() #0 {{.*}}
// ALL: {{.*}}__llvm_gcov_ctr{{.*}}
// ALL: ret void
// ALL: define void @test() #0 {{.*}}
// ALL: {{.*}}__llvm_gcov_ctr{{.*}}
// ALL: ret void
// NO-HEADER: define void @test1() #0 {{.*}}
// NO-HEADER-NOT: {{.*}}__llvm_gcov_ctr{{.*}}
// NO-HEADER: ret void
// NO-HEADER: define void @test2() #0 {{.*}}
// NO-HEADER-NOT: {{.*}}__llvm_gcov_ctr{{.*}}
// NO-HEADER: ret void
// NO-HEADER: define void @test() #0 {{.*}}
// NO-HEADER: {{.*}}__llvm_gcov_ctr{{.*}}
// NO-HEADER: ret void
// NO-HEADER2: define void @test1() #0 {{.*}}
// NO-HEADER2: {{.*}}__llvm_gcov_ctr{{.*}}
// NO-HEADER2: ret void
// NO-HEADER2: define void @test2() #0 {{.*}}
// NO-HEADER2-NOT: {{.*}}__llvm_gcov_ctr{{.*}}
// NO-HEADER2: ret void
// NO-HEADER2: define void @test() #0 {{.*}}
// NO-HEADER2: {{.*}}__llvm_gcov_ctr{{.*}}
// NO-HEADER2: ret void
// JUST-C: define void @test1() #0 {{.*}}
// JUST-C-NOT: {{.*}}__llvm_gcov_ctr{{.*}}
// JUST-C: ret void
// JUST-C: define void @test2() #0 {{.*}}
// JUST-C-NOT: {{.*}}__llvm_gcov_ctr{{.*}}
// JUST-C: ret void
// JUST-C: define void @test() #0 {{.*}}
// JUST-C: {{.*}}__llvm_gcov_ctr{{.*}}
// JUST-C: ret void
// HEADER: define void @test1() #0 {{.*}}
// HEADER: {{.*}}__llvm_gcov_ctr{{.*}}
// HEADER: ret void
// HEADER: define void @test2() #0 {{.*}}
// HEADER: {{.*}}__llvm_gcov_ctr{{.*}}
// HEADER: ret void
// HEADER: define void @test() #0 {{.*}}
// HEADER-NOT: {{.*}}__llvm_gcov_ctr{{.*}}
// HEADER: ret void
// NONE: define void @test1() #0 {{.*}}
// NONE-NOT: {{.*}}__llvm_gcov_ctr{{.*}}
// NONE: ret void
// NONE: define void @test2() #0 {{.*}}
// NONE-NOT: {{.*}}__llvm_gcov_ctr{{.*}}
// NONE: ret void
// NONE: define void @test() #0 {{.*}}
// NONE-NOT: {{.*}}__llvm_gcov_ctr{{.*}}
// NONE: ret void