Skip to content

Commit 4edf35f

Browse files
committed
Support for instrumenting only selected files or functions
This change implements support for applying profile instrumentation only to selected files or functions. The implementation uses the sanitizer special case list format to select which files and functions to instrument, and relies on the new noprofile IR attribute to exclude functions from instrumentation. Differential Revision: https://reviews.llvm.org/D94820
1 parent 32cc556 commit 4edf35f

29 files changed

+403
-1
lines changed

clang/docs/ClangCommandLineReference.rst

+6
Original file line numberDiff line numberDiff line change
@@ -2045,6 +2045,12 @@ Set update method of profile counters (atomic,prefer-atomic,single)
20452045

20462046
Use instrumentation data for profile-guided optimization. If pathname is a directory, it reads from <pathname>/default.profdata. Otherwise, it reads from file <pathname>.
20472047

2048+
.. program:: clang1
2049+
.. option:: -fprofile-list=<file>
2050+
.. program:: clang
2051+
2052+
Filename defining the list of functions/files to instrument. The file uses the sanitizer special case list format.
2053+
20482054
.. option:: -freciprocal-math, -fno-reciprocal-math
20492055

20502056
Allow division operations to be reassociated

clang/docs/SourceBasedCodeCoverage.rst

+45
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,51 @@ To compile code with coverage enabled, pass ``-fprofile-instr-generate
6464
Note that linking together code with and without coverage instrumentation is
6565
supported. Uninstrumented code simply won't be accounted for in reports.
6666

67+
Instrumenting only selected files or functions
68+
----------------------------------------------
69+
70+
Sometimes it's useful to only instrument certain files or functions. For
71+
example in automated testing infrastructure, it may be desirable to only
72+
instrument files or functions that were modified by a patch to reduce the
73+
overhead of instrumenting a full system.
74+
75+
This can be done using the ``-fprofile-list=file.list`` option. The option
76+
can be specified multiple times to pass multiple files:
77+
78+
.. code-block:: console
79+
80+
$ echo "!fun:*test*" > fun.list
81+
$ echo "src:code.cc" > src.list
82+
% clang++ -O2 -fprofile-instr-generate -fcoverage-mapping -fprofile-list=fun.list -fprofile-list=code.list code.cc -o code
83+
84+
The file uses :doc:`SanitizerSpecialCaseList` format. To filter individual
85+
functions or entire source files using ``fun:<name>`` or ``src:<file>``
86+
respectively. To exclude a function or a source file, use ``!fun:<name>`` or
87+
``!src:<file>`` respectively. The format also supports wildcard expansion. The
88+
compiler generated functions are assumed to be located in the main source file.
89+
It is also possible to restrict the filter to a particular instrumentation type
90+
by using a named section. For example:
91+
92+
.. code-block:: none
93+
94+
# all functions whose name starts with foo will be instrumented.
95+
fun:foo*
96+
97+
# except for foo1 which will be excluded from instrumentation.
98+
!fun:foo1
99+
100+
# every function in path/to/foo.cc will be instrumented.
101+
src:path/to/foo.cc
102+
103+
# bar will be instrumented only when using backend instrumentation.
104+
# Recognized section names are clang, llvm and csllvm.
105+
[llvm]
106+
fun:bar
107+
108+
When the file contains only excludes, all files and functions except for the
109+
excluded ones will be instrumented. Otherwise, only the files and functions
110+
specified will be instrumented.
111+
67112
Running the instrumented program
68113
================================
69114

clang/docs/UsersManual.rst

+13
Original file line numberDiff line numberDiff line change
@@ -2268,6 +2268,17 @@ programs using the same instrumentation method as ``-fprofile-generate``.
22682268
This option currently works with ``-fprofile-arcs`` and ``-fprofile-instr-generate``,
22692269
but not with ``-fprofile-generate``.
22702270

2271+
.. option:: -fprofile-list=<pathname>
2272+
2273+
This option can be used to apply profile instrumentation only to selected
2274+
files or functions. ``pathname`` specifies a file in the sanitizer special
2275+
case list format which selects which files and functions to instrument.
2276+
2277+
.. code-block:: console
2278+
2279+
$ echo "fun:test" > fun.list
2280+
$ clang++ -O2 -fprofile-instr-generate -fprofile-list=fun.list code.cc -o code
2281+
22712282
Disabling Instrumentation
22722283
^^^^^^^^^^^^^^^^^^^^^^^^^
22732284

@@ -3740,6 +3751,8 @@ Execute ``clang-cl /?`` to see a list of supported options:
37403751
Use instrumentation data for profile-guided optimization
37413752
-fprofile-remapping-file=<file>
37423753
Use the remappings described in <file> to match the profile data against names in the program
3754+
-fprofile-list=<file>
3755+
Filename defining the list of functions/files to instrument
37433756
-fsanitize-address-field-padding=<value>
37443757
Level of field padding for AddressSanitizer
37453758
-fsanitize-address-globals-dead-stripping

clang/include/clang/AST/ASTContext.h

+7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "clang/Basic/Linkage.h"
3737
#include "clang/Basic/OperatorKinds.h"
3838
#include "clang/Basic/PartialDiagnostic.h"
39+
#include "clang/Basic/ProfileList.h"
3940
#include "clang/Basic/SanitizerBlacklist.h"
4041
#include "clang/Basic/SourceLocation.h"
4142
#include "clang/Basic/Specifiers.h"
@@ -566,6 +567,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
566567
/// should be imbued with the XRay "always" or "never" attributes.
567568
std::unique_ptr<XRayFunctionFilter> XRayFilter;
568569

570+
/// ProfileList object that is used by the profile instrumentation
571+
/// to decide which entities should be instrumented.
572+
std::unique_ptr<ProfileList> ProfList;
573+
569574
/// The allocator used to create AST objects.
570575
///
571576
/// AST objects are never destructed; rather, all memory associated with the
@@ -691,6 +696,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
691696
return *XRayFilter;
692697
}
693698

699+
const ProfileList &getProfileList() const { return *ProfList; }
700+
694701
DiagnosticsEngine &getDiagnostics() const;
695702

696703
FullSourceLoc getFullLoc(SourceLocation Loc) const {

clang/include/clang/Basic/LangOptions.h

+4
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,10 @@ class LangOptions : public LangOptionsBase {
285285
/// attribute(s).
286286
std::vector<std::string> XRayAttrListFiles;
287287

288+
/// Paths to special case list files specifying which entities
289+
/// (files, functions) should or should not be instrumented.
290+
std::vector<std::string> ProfileListFiles;
291+
288292
clang::ObjCRuntime ObjCRuntime;
289293

290294
CoreFoundationABI CFRuntime = CoreFoundationABI::Unspecified;
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===--- ProfileList.h - ProfileList filter ---------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// User-provided filters include/exclude profile instrumentation in certain
10+
// functions.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
#ifndef LLVM_CLANG_BASIC_INSTRPROFLIST_H
14+
#define LLVM_CLANG_BASIC_INSTRPROFLIST_H
15+
16+
#include "clang/Basic/CodeGenOptions.h"
17+
#include "clang/Basic/LLVM.h"
18+
#include "clang/Basic/SourceLocation.h"
19+
#include "llvm/ADT/ArrayRef.h"
20+
#include "llvm/ADT/Optional.h"
21+
#include "llvm/ADT/StringRef.h"
22+
#include <memory>
23+
24+
namespace llvm {
25+
class SpecialCaseList;
26+
}
27+
28+
namespace clang {
29+
30+
class ProfileSpecialCaseList;
31+
32+
class ProfileList {
33+
std::unique_ptr<ProfileSpecialCaseList> SCL;
34+
const bool Empty;
35+
const bool Default;
36+
SourceManager &SM;
37+
38+
public:
39+
ProfileList(ArrayRef<std::string> Paths, SourceManager &SM);
40+
~ProfileList();
41+
42+
bool isEmpty() const { return Empty; }
43+
bool getDefault() const { return Default; }
44+
45+
llvm::Optional<bool>
46+
isFunctionExcluded(StringRef FunctionName,
47+
CodeGenOptions::ProfileInstrKind Kind) const;
48+
llvm::Optional<bool>
49+
isLocationExcluded(SourceLocation Loc,
50+
CodeGenOptions::ProfileInstrKind Kind) const;
51+
llvm::Optional<bool>
52+
isFileExcluded(StringRef FileName,
53+
CodeGenOptions::ProfileInstrKind Kind) const;
54+
};
55+
56+
} // namespace clang
57+
58+
#endif

clang/include/clang/Driver/Options.td

+4
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,10 @@ defm pseudo_probe_for_profiling : BoolFOption<"pseudo-probe-for-profiling",
11511151
def forder_file_instrumentation : Flag<["-"], "forder-file-instrumentation">,
11521152
Group<f_Group>, Flags<[CC1Option, CoreOption]>,
11531153
HelpText<"Generate instrumented code to collect order file into default.profraw file (overridden by '=' form of option or LLVM_PROFILE_FILE env var)">;
1154+
def fprofile_list_EQ : Joined<["-"], "fprofile-list=">,
1155+
Group<f_Group>, Flags<[CC1Option, CoreOption]>,
1156+
HelpText<"Filename defining the list of functions/files to instrument">,
1157+
MarshallingInfoStringVector<LangOpts<"ProfileListFiles">>;
11541158

11551159
defm addrsig : BoolFOption<"addrsig",
11561160
CodeGenOpts<"Addrsig">, DefaultFalse,

clang/lib/AST/ASTContext.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,7 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
965965
XRayFilter(new XRayFunctionFilter(LangOpts.XRayAlwaysInstrumentFiles,
966966
LangOpts.XRayNeverInstrumentFiles,
967967
LangOpts.XRayAttrListFiles, SM)),
968+
ProfList(new ProfileList(LangOpts.ProfileListFiles, SM)),
968969
PrintingPolicy(LOpts), Idents(idents), Selectors(sels),
969970
BuiltinInfo(builtins), DeclarationNames(*this), Comments(SM),
970971
CommentCommandTraits(BumpAlloc, LOpts.CommentOpts),

clang/lib/Basic/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ add_clang_library(clangBasic
5959
OpenCLOptions.cpp
6060
OpenMPKinds.cpp
6161
OperatorPrecedence.cpp
62+
ProfileList.cpp
6263
SanitizerBlacklist.cpp
6364
SanitizerSpecialCaseList.cpp
6465
Sanitizers.cpp

clang/lib/Basic/ProfileList.cpp

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//===--- ProfileList.h - ProfileList filter ---------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// User-provided filters include/exclude profile instrumentation in certain
10+
// functions or files.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "clang/Basic/ProfileList.h"
15+
#include "clang/Basic/FileManager.h"
16+
#include "clang/Basic/SourceManager.h"
17+
#include "llvm/Support/SpecialCaseList.h"
18+
19+
#include "llvm/Support/raw_ostream.h"
20+
21+
using namespace clang;
22+
23+
namespace clang {
24+
25+
class ProfileSpecialCaseList : public llvm::SpecialCaseList {
26+
public:
27+
static std::unique_ptr<ProfileSpecialCaseList>
28+
create(const std::vector<std::string> &Paths, llvm::vfs::FileSystem &VFS,
29+
std::string &Error);
30+
31+
static std::unique_ptr<ProfileSpecialCaseList>
32+
createOrDie(const std::vector<std::string> &Paths,
33+
llvm::vfs::FileSystem &VFS);
34+
35+
bool isEmpty() const { return Sections.empty(); }
36+
37+
bool hasPrefix(StringRef Prefix) const {
38+
for (auto &SectionIter : Sections)
39+
if (SectionIter.Entries.count(Prefix) > 0)
40+
return true;
41+
return false;
42+
}
43+
};
44+
45+
std::unique_ptr<ProfileSpecialCaseList>
46+
ProfileSpecialCaseList::create(const std::vector<std::string> &Paths,
47+
llvm::vfs::FileSystem &VFS,
48+
std::string &Error) {
49+
auto PSCL = std::make_unique<ProfileSpecialCaseList>();
50+
if (PSCL->createInternal(Paths, VFS, Error))
51+
return PSCL;
52+
return nullptr;
53+
}
54+
55+
std::unique_ptr<ProfileSpecialCaseList>
56+
ProfileSpecialCaseList::createOrDie(const std::vector<std::string> &Paths,
57+
llvm::vfs::FileSystem &VFS) {
58+
std::string Error;
59+
if (auto PSCL = create(Paths, VFS, Error))
60+
return PSCL;
61+
llvm::report_fatal_error(Error);
62+
}
63+
64+
}
65+
66+
ProfileList::ProfileList(ArrayRef<std::string> Paths, SourceManager &SM)
67+
: SCL(ProfileSpecialCaseList::createOrDie(
68+
Paths, SM.getFileManager().getVirtualFileSystem())),
69+
Empty(SCL->isEmpty()),
70+
Default(SCL->hasPrefix("fun") || SCL->hasPrefix("src")), SM(SM) {}
71+
72+
ProfileList::~ProfileList() = default;
73+
74+
static StringRef getSectionName(CodeGenOptions::ProfileInstrKind Kind) {
75+
switch (Kind) {
76+
case CodeGenOptions::ProfileNone:
77+
return "";
78+
case CodeGenOptions::ProfileClangInstr:
79+
return "clang";
80+
case CodeGenOptions::ProfileIRInstr:
81+
return "llvm";
82+
case CodeGenOptions::ProfileCSIRInstr:
83+
return "csllvm";
84+
}
85+
}
86+
87+
llvm::Optional<bool>
88+
ProfileList::isFunctionExcluded(StringRef FunctionName,
89+
CodeGenOptions::ProfileInstrKind Kind) const {
90+
StringRef Section = getSectionName(Kind);
91+
if (SCL->inSection(Section, "!fun", FunctionName))
92+
return true;
93+
if (SCL->inSection(Section, "fun", FunctionName))
94+
return false;
95+
return None;
96+
}
97+
98+
llvm::Optional<bool>
99+
ProfileList::isLocationExcluded(SourceLocation Loc,
100+
CodeGenOptions::ProfileInstrKind Kind) const {
101+
return isFileExcluded(SM.getFilename(SM.getFileLoc(Loc)), Kind);
102+
}
103+
104+
llvm::Optional<bool>
105+
ProfileList::isFileExcluded(StringRef FileName,
106+
CodeGenOptions::ProfileInstrKind Kind) const {
107+
StringRef Section = getSectionName(Kind);
108+
if (SCL->inSection(Section, "!src", FileName))
109+
return true;
110+
if (SCL->inSection(Section, "src", FileName))
111+
return false;
112+
return None;
113+
}

clang/lib/CodeGen/CodeGenFunction.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,10 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
839839
}
840840
}
841841

842+
if (CGM.getCodeGenOpts().getProfileInstr() != CodeGenOptions::ProfileNone)
843+
if (CGM.isProfileInstrExcluded(Fn, Loc))
844+
Fn->addFnAttr(llvm::Attribute::NoProfile);
845+
842846
unsigned Count, Offset;
843847
if (const auto *Attr =
844848
D ? D->getAttr<PatchableFunctionEntryAttr>() : nullptr) {

clang/lib/CodeGen/CodeGenFunction.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1442,7 +1442,8 @@ class CodeGenFunction : public CodeGenTypeCache {
14421442
/// Increment the profiler's counter for the given statement by \p StepV.
14431443
/// If \p StepV is null, the default increment is 1.
14441444
void incrementProfileCounter(const Stmt *S, llvm::Value *StepV = nullptr) {
1445-
if (CGM.getCodeGenOpts().hasProfileClangInstr())
1445+
if (CGM.getCodeGenOpts().hasProfileClangInstr() &&
1446+
!CurFn->hasFnAttribute(llvm::Attribute::NoProfile))
14461447
PGO.emitCounterIncrement(Builder, S, StepV);
14471448
PGO.setCurrentStmt(S);
14481449
}

clang/lib/CodeGen/CodeGenModule.cpp

+28
Original file line numberDiff line numberDiff line change
@@ -2563,6 +2563,34 @@ bool CodeGenModule::imbueXRayAttrs(llvm::Function *Fn, SourceLocation Loc,
25632563
return true;
25642564
}
25652565

2566+
bool CodeGenModule::isProfileInstrExcluded(llvm::Function *Fn,
2567+
SourceLocation Loc) const {
2568+
const auto &ProfileList = getContext().getProfileList();
2569+
// If the profile list is empty, then instrument everything.
2570+
if (ProfileList.isEmpty())
2571+
return false;
2572+
CodeGenOptions::ProfileInstrKind Kind = getCodeGenOpts().getProfileInstr();
2573+
// First, check the function name.
2574+
Optional<bool> V = ProfileList.isFunctionExcluded(Fn->getName(), Kind);
2575+
if (V.hasValue())
2576+
return *V;
2577+
// Next, check the source location.
2578+
if (Loc.isValid()) {
2579+
Optional<bool> V = ProfileList.isLocationExcluded(Loc, Kind);
2580+
if (V.hasValue())
2581+
return *V;
2582+
}
2583+
// If location is unknown, this may be a compiler-generated function. Assume
2584+
// it's located in the main file.
2585+
auto &SM = Context.getSourceManager();
2586+
if (const auto *MainFile = SM.getFileEntryForID(SM.getMainFileID())) {
2587+
Optional<bool> V = ProfileList.isFileExcluded(MainFile->getName(), Kind);
2588+
if (V.hasValue())
2589+
return *V;
2590+
}
2591+
return ProfileList.getDefault();
2592+
}
2593+
25662594
bool CodeGenModule::MustBeEmitted(const ValueDecl *Global) {
25672595
// Never defer when EmitAllDecls is specified.
25682596
if (LangOpts.EmitAllDecls)

clang/lib/CodeGen/CodeGenModule.h

+4
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,10 @@ class CodeGenModule : public CodeGenTypeCache {
12771277
bool imbueXRayAttrs(llvm::Function *Fn, SourceLocation Loc,
12781278
StringRef Category = StringRef()) const;
12791279

1280+
/// Returns true if function at the given location should be excluded from
1281+
/// profile instrumentation.
1282+
bool isProfileInstrExcluded(llvm::Function *Fn, SourceLocation Loc) const;
1283+
12801284
SanitizerMetadata *getSanitizerMetadata() {
12811285
return SanitizerMD.get();
12821286
}

0 commit comments

Comments
 (0)