diff options
-rw-r--r-- | clang/docs/ReleaseNotes.rst | 6 | ||||
-rw-r--r-- | clang/docs/UsersManual.rst | 49 | ||||
-rw-r--r-- | clang/include/clang/Driver/Options.td | 6 | ||||
-rw-r--r-- | clang/include/clang/Frontend/CodeGenOptions.h | 6 | ||||
-rw-r--r-- | clang/lib/CodeGen/BackendUtil.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Driver/ToolChains/Clang.cpp | 23 | ||||
-rw-r--r-- | clang/lib/Frontend/CompilerInvocation.cpp | 4 | ||||
-rw-r--r-- | clang/test/CodeGen/Inputs/code-coverage-filter1.h | 1 | ||||
-rw-r--r-- | clang/test/CodeGen/Inputs/code-coverage-filter2.h | 1 | ||||
-rw-r--r-- | clang/test/CodeGen/code-coverage-filter.c | 84 |
10 files changed, 182 insertions, 0 deletions
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 09f656815d43..72b043f28d03 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -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 diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index a7f2f8145e87..5c28517bebb0 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -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 ----------------------------- diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index cc9f21e2ad84..cb070f06b2a0 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -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">; diff --git a/clang/include/clang/Frontend/CodeGenOptions.h b/clang/include/clang/Frontend/CodeGenOptions.h index 6cedfdbb17df..15a6b7ad9760 100644 --- a/clang/include/clang/Frontend/CodeGenOptions.h +++ b/clang/include/clang/Frontend/CodeGenOptions.h @@ -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]; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index fd9a95b71998..484a070e5eb9 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -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; } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index c35f7a94053c..13036c18652d 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -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()) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 7be183f9701c..d43965ce5dfc 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -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)) { diff --git a/clang/test/CodeGen/Inputs/code-coverage-filter1.h b/clang/test/CodeGen/Inputs/code-coverage-filter1.h new file mode 100644 index 000000000000..4c0de6c5e569 --- /dev/null +++ b/clang/test/CodeGen/Inputs/code-coverage-filter1.h @@ -0,0 +1 @@ +void test1() {} diff --git a/clang/test/CodeGen/Inputs/code-coverage-filter2.h b/clang/test/CodeGen/Inputs/code-coverage-filter2.h new file mode 100644 index 000000000000..91e68ccae978 --- /dev/null +++ b/clang/test/CodeGen/Inputs/code-coverage-filter2.h @@ -0,0 +1 @@ +void test2() {} diff --git a/clang/test/CodeGen/code-coverage-filter.c b/clang/test/CodeGen/code-coverage-filter.c new file mode 100644 index 000000000000..392d928deff6 --- /dev/null +++ b/clang/test/CodeGen/code-coverage-filter.c @@ -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 |