Skip to content

Commit 6f7fbdd

Browse files
committed
[xray] Function coverage groups
Add the ability to selectively instrument a subset of functions by dividing the functions into N logical groups and then selecting a group to cover. By selecting different groups over time you could cover the entire application incrementally with lower overhead than instrumenting the entire application at once. Differential Revision: https://reviews.llvm.org/D87953
1 parent 8c98c88 commit 6f7fbdd

File tree

8 files changed

+156
-6
lines changed

8 files changed

+156
-6
lines changed

clang/include/clang/Basic/CodeGenOptions.def

+6
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ CODEGENOPT(XRayOmitFunctionIndex , 1, 0)
120120
///< XRay instrumentation.
121121
VALUE_CODEGENOPT(XRayInstructionThreshold , 32, 200)
122122

123+
///< Only instrument 1 in N functions, by dividing functions into N total groups and
124+
///< instrumenting only the specified group at a time. Group numbers start at 0
125+
///< and end at N-1.
126+
VALUE_CODEGENOPT(XRayTotalFunctionGroups, 32, 1)
127+
VALUE_CODEGENOPT(XRaySelectedFunctionGroup, 32, 0)
128+
123129
VALUE_CODEGENOPT(PatchableFunctionEntryCount , 32, 0) ///< Number of NOPs at function entry
124130
VALUE_CODEGENOPT(PatchableFunctionEntryOffset , 32, 0)
125131

clang/include/clang/Driver/Options.td

+11
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,17 @@ def fxray_instrumentation_bundle :
13391339
Group<f_Group>, Flags<[CC1Option]>,
13401340
HelpText<"Select which XRay instrumentation points to emit. Options: all, none, function-entry, function-exit, function, custom. Default is 'all'. 'function' includes both 'function-entry' and 'function-exit'.">;
13411341

1342+
def fxray_function_groups :
1343+
Joined<["-"], "fxray-function-groups=">,
1344+
Group<f_Group>, Flags<[CC1Option]>,
1345+
HelpText<"Only instrument 1 of N groups">;
1346+
1347+
def fxray_selected_function_group :
1348+
Joined<["-"], "fxray-selected-function-group=">,
1349+
Group<f_Group>, Flags<[CC1Option]>,
1350+
HelpText<"When using -fxray-function-groups, select which group of functions to instrument. Valid range is 0 to fxray-function-groups - 1">;
1351+
1352+
13421353
def ffine_grained_bitfield_accesses : Flag<["-"],
13431354
"ffine-grained-bitfield-accesses">, Group<f_clang_Group>, Flags<[CC1Option]>,
13441355
HelpText<"Use separate accesses for consecutive bitfield runs with legal widths and alignments.">;

clang/include/clang/Driver/XRayArgs.h

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class XRayArgs {
3232
bool XRayRT = true;
3333
bool XRayIgnoreLoops = false;
3434
bool XRayFunctionIndex;
35+
int XRayFunctionGroups = 1;
36+
int XRaySelectedFunctionGroup;
3537

3638
public:
3739
/// Parses the XRay arguments from an argument list.

clang/lib/CodeGen/CodeGenFunction.cpp

+16-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "clang/Basic/TargetInfo.h"
3333
#include "clang/CodeGen/CGFunctionInfo.h"
3434
#include "clang/Frontend/FrontendDiagnostic.h"
35+
#include "llvm/ADT/ArrayRef.h"
3536
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
3637
#include "llvm/IR/DataLayout.h"
3738
#include "llvm/IR/Dominators.h"
@@ -40,6 +41,7 @@
4041
#include "llvm/IR/Intrinsics.h"
4142
#include "llvm/IR/MDBuilder.h"
4243
#include "llvm/IR/Operator.h"
44+
#include "llvm/Support/CRC.h"
4345
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
4446
using namespace clang;
4547
using namespace CodeGen;
@@ -772,13 +774,16 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
772774
SanOpts.Mask &= ~SanitizerKind::Null;
773775

774776
// Apply xray attributes to the function (as a string, for now)
777+
bool AlwaysXRayAttr = false;
775778
if (const auto *XRayAttr = D ? D->getAttr<XRayInstrumentAttr>() : nullptr) {
776779
if (CGM.getCodeGenOpts().XRayInstrumentationBundle.has(
777780
XRayInstrKind::FunctionEntry) ||
778781
CGM.getCodeGenOpts().XRayInstrumentationBundle.has(
779782
XRayInstrKind::FunctionExit)) {
780-
if (XRayAttr->alwaysXRayInstrument() && ShouldXRayInstrumentFunction())
783+
if (XRayAttr->alwaysXRayInstrument() && ShouldXRayInstrumentFunction()) {
781784
Fn->addFnAttr("function-instrument", "xray-always");
785+
AlwaysXRayAttr = true;
786+
}
782787
if (XRayAttr->neverXRayInstrument())
783788
Fn->addFnAttr("function-instrument", "xray-never");
784789
if (const auto *LogArgs = D->getAttr<XRayLogArgsAttr>())
@@ -804,6 +809,16 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
804809
if (!CGM.getCodeGenOpts().XRayInstrumentationBundle.has(
805810
XRayInstrKind::FunctionEntry))
806811
Fn->addFnAttr("xray-skip-entry");
812+
813+
auto FuncGroups = CGM.getCodeGenOpts().XRayTotalFunctionGroups;
814+
if (FuncGroups > 1) {
815+
auto FuncName = llvm::makeArrayRef<uint8_t>(
816+
CurFn->getName().bytes_begin(), CurFn->getName().bytes_end());
817+
auto Group = crc32(FuncName) % FuncGroups;
818+
if (Group != CGM.getCodeGenOpts().XRaySelectedFunctionGroup &&
819+
!AlwaysXRayAttr)
820+
Fn->addFnAttr("function-instrument", "xray-never");
821+
}
807822
}
808823

809824
unsigned Count, Offset;

clang/lib/Driver/XRayArgs.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,21 @@ XRayArgs::XRayArgs(const ToolChain &TC, const ArgList &Args) {
186186
Modes.push_back(std::string(M));
187187
}
188188

189+
if (const Arg *A = Args.getLastArg(options::OPT_fxray_function_groups)) {
190+
StringRef S = A->getValue();
191+
if (S.getAsInteger(0, XRayFunctionGroups) || XRayFunctionGroups < 1)
192+
D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
193+
}
194+
195+
if (const Arg *A =
196+
Args.getLastArg(options::OPT_fxray_selected_function_group)) {
197+
StringRef S = A->getValue();
198+
if (S.getAsInteger(0, XRaySelectedFunctionGroup) ||
199+
XRaySelectedFunctionGroup < 0 ||
200+
XRaySelectedFunctionGroup >= XRayFunctionGroups)
201+
D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
202+
}
203+
189204
// Then we want to sort and unique the modes we've collected.
190205
llvm::sort(Modes);
191206
Modes.erase(std::unique(Modes.begin(), Modes.end()), Modes.end());
@@ -210,6 +225,17 @@ void XRayArgs::addArgs(const ToolChain &TC, const ArgList &Args,
210225
if (!XRayFunctionIndex)
211226
CmdArgs.push_back("-fno-xray-function-index");
212227

228+
if (XRayFunctionGroups > 1) {
229+
CmdArgs.push_back(Args.MakeArgString(Twine("-fxray-function-groups=") +
230+
Twine(XRayFunctionGroups)));
231+
}
232+
233+
if (XRaySelectedFunctionGroup != 0) {
234+
CmdArgs.push_back(
235+
Args.MakeArgString(Twine("-fxray-selected-function-group=") +
236+
Twine(XRaySelectedFunctionGroup)));
237+
}
238+
213239
CmdArgs.push_back(Args.MakeArgString(Twine(XRayInstructionThresholdOption) +
214240
Twine(InstructionThreshold)));
215241

clang/lib/Frontend/CompilerInvocation.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,10 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
11301130
getLastArgIntValue(Args, OPT_fxray_instruction_threshold_EQ, 200, Diags);
11311131
Opts.XRayIgnoreLoops = Args.hasArg(OPT_fxray_ignore_loops);
11321132
Opts.XRayOmitFunctionIndex = Args.hasArg(OPT_fno_xray_function_index);
1133+
Opts.XRayTotalFunctionGroups =
1134+
getLastArgIntValue(Args, OPT_fxray_function_groups, 1, Diags);
1135+
Opts.XRaySelectedFunctionGroup =
1136+
getLastArgIntValue(Args, OPT_fxray_selected_function_group, 0, Diags);
11331137

11341138
auto XRayInstrBundles =
11351139
Args.getAllArgValues(OPT_fxray_instrumentation_bundle);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %clang_cc1 -fxray-instrument -fxray-instruction-threshold=1 -fxray-function-groups=3 -fxray-selected-function-group=0 \
2+
// RUN: -emit-llvm -o - %s -triple x86_64-unknown-linux-gnu | FileCheck --check-prefix=GROUP0 %s
3+
4+
// RUN: %clang_cc1 -fxray-instrument -fxray-instruction-threshold=1 -fxray-function-groups=3 -fxray-selected-function-group=1 \
5+
// RUN: -emit-llvm -o - %s -triple x86_64-unknown-linux-gnu | FileCheck --check-prefix=GROUP1 %s
6+
7+
// RUN: %clang_cc1 -fxray-instrument -fxray-instruction-threshold=1 -fxray-function-groups=3 -fxray-selected-function-group=2 \
8+
// RUN: -emit-llvm -o - %s -triple x86_64-unknown-linux-gnu | FileCheck --check-prefix=GROUP2 %s
9+
10+
static int foo() { // part of group 0
11+
return 1;
12+
}
13+
14+
int bar() { // part of group 2
15+
return 1;
16+
}
17+
18+
int yarr() { // part of group 1
19+
foo();
20+
return 1;
21+
}
22+
23+
[[clang::xray_always_instrument]] int always() { // part of group 0
24+
return 1;
25+
}
26+
27+
[[clang::xray_never_instrument]] int never() { // part of group 1
28+
return 1;
29+
}
30+
31+
// GROUP0: define{{.*}} i32 @_Z3barv() #[[ATTRS_BAR:[0-9]+]] {
32+
// GROUP0: define{{.*}} i32 @_Z4yarrv() #[[ATTRS_BAR]] {
33+
// GROUP0: define{{.*}} i32 @_ZL3foov() #[[ATTRS_FOO:[0-9]+]] {
34+
// GROUP0: define{{.*}} i32 @_Z6alwaysv() #[[ATTRS_ALWAYS:[0-9]+]] {
35+
// GROUP0: define{{.*}} i32 @_Z5neverv() #[[ATTRS_NEVER:[0-9]+]] {
36+
// GROUP0-DAG: attributes #[[ATTRS_BAR]] = {{.*}} "function-instrument"="xray-never" {{.*}}
37+
// GROUP0-DAG: attributes #[[ATTRS_ALWAYS]] = {{.*}} "function-instrument"="xray-always" {{.*}}
38+
// GROUP0-DAG: attributes #[[ATTRS_NEVER]] = {{.*}} "function-instrument"="xray-never" {{.*}}
39+
40+
// GROUP1: define{{.*}} i32 @_Z3barv() #[[ATTRS_BAR:[0-9]+]] {
41+
// GROUP1: define{{.*}} i32 @_Z4yarrv() #[[ATTRS_YARR:[0-9]+]] {
42+
// GROUP1: define{{.*}} i32 @_ZL3foov() #[[ATTRS_BAR]] {
43+
// GROUP1: define{{.*}} i32 @_Z6alwaysv() #[[ATTRS_ALWAYS:[0-9]+]] {
44+
// GROUP1: define{{.*}} i32 @_Z5neverv() #[[ATTRS_NEVER:[0-9]+]] {
45+
// GROUP1-DAG: attributes #[[ATTRS_BAR]] = {{.*}} "function-instrument"="xray-never" {{.*}}
46+
// GROUP1-DAG: attributes #[[ATTRS_ALWAYS]] = {{.*}} "function-instrument"="xray-always" {{.*}}
47+
// GROUP1-DAG: attributes #[[ATTRS_NEVER]] = {{.*}} "function-instrument"="xray-never" {{.*}}
48+
49+
// GROUP2: define{{.*}} i32 @_Z3barv() #[[ATTRS_BAR:[0-9]+]] {
50+
// GROUP2: define{{.*}} i32 @_Z4yarrv() #[[ATTRS_YARR:[0-9]+]] {
51+
// GROUP2: define{{.*}} i32 @_ZL3foov() #[[ATTRS_YARR]] {
52+
// GROUP2: define{{.*}} i32 @_Z6alwaysv() #[[ATTRS_ALWAYS:[0-9]+]] {
53+
// GROUP2: define{{.*}} i32 @_Z5neverv() #[[ATTRS_NEVER:[0-9]+]] {
54+
// GROUP2-DAG: attributes #[[ATTRS_YARR]] = {{.*}} "function-instrument"="xray-never" {{.*}}
55+
// GROUP2-DAG: attributes #[[ATTRS_ALWAYS]] = {{.*}} "function-instrument"="xray-always" {{.*}}
56+
// GROUP2-DAG: attributes #[[ATTRS_NEVER]] = {{.*}} "function-instrument"="xray-never" {{.*}}

llvm/docs/XRay.rst

+35-5
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,18 @@ For example:
6262

6363
clang -fxray-instrument ...
6464

65-
By default, functions that have at least 200 instructions will get XRay
66-
instrumentation points. You can tweak that number through the
65+
By default, functions that have at least 200 instructions (or contain a loop) will
66+
get XRay instrumentation points. You can tweak that number through the
6767
``-fxray-instruction-threshold=`` flag:
6868

6969
::
7070

7171
clang -fxray-instrument -fxray-instruction-threshold=1 ...
7272

73-
You can also specifically instrument functions in your binary to either always
74-
or never be instrumented using source-level attributes. You can do it using the
75-
GCC-style attributes or C++11-style attributes.
73+
The loop detection can be disabled with ``-fxray-ignore-loops`` to use only the
74+
instruction threshold. You can also specifically instrument functions in your
75+
binary to either always or never be instrumented using source-level attributes.
76+
You can do it using the GCC-style attributes or C++11-style attributes.
7677

7778
.. code-block:: c++
7879

@@ -309,6 +310,35 @@ libraries, distributed with the LLVM distribution. These are:
309310
instrumentation map in XRay-instrumented object files and binaries. The
310311
``extract`` and ``stack`` subcommands uses this particular library.
311312

313+
314+
Minimizing Binary Size
315+
----------------------
316+
317+
XRay supports several different instrumentation points including ``function-entry``,
318+
``function-exit``, ``custom``, and ``typed`` points. These can be enabled individually
319+
using the ``-fxray-instrumentaton-bundle=`` flag. For example if you only wanted to
320+
instrument function entry and custom points you could specify:
321+
322+
::
323+
324+
clang -fxray-instrument -fxray-instrumentation-bundle=function-entry,custom ...
325+
326+
This will omit the other sled types entirely, reducing the binary size. You can also
327+
instrument just a sampled subset of functions using instrumentation groups.
328+
For example, to instrument only a quarter of available functions invoke:
329+
330+
::
331+
332+
clang -fxray-instrument -fxray-function-groups=4
333+
334+
A subset will be chosen arbitrarily based on a hash of the function name. To sample a
335+
different subset you can specify ``-fxray-selected-function-group=`` with a group number
336+
in the range of 0 to ``xray-function-groups`` - 1. Together these options could be used
337+
to produce multiple binaries with different instrumented subsets. If all you need is
338+
runtime control over which functions are being traced at any given time it is better
339+
to selectively patch and unpatch the individual functions you need using the XRay
340+
Runtime Library's ``__xray_patch_function()`` method.
341+
312342
Future Work
313343
===========
314344

0 commit comments

Comments
 (0)