[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
This commit is contained in:
Ian Levesque 2020-09-18 14:45:51 -04:00
parent 8c98c88034
commit 6f7fbdd285
8 changed files with 156 additions and 6 deletions

View File

@ -120,6 +120,12 @@ CODEGENOPT(XRayOmitFunctionIndex , 1, 0)
///< XRay instrumentation.
VALUE_CODEGENOPT(XRayInstructionThreshold , 32, 200)
///< Only instrument 1 in N functions, by dividing functions into N total groups and
///< instrumenting only the specified group at a time. Group numbers start at 0
///< and end at N-1.
VALUE_CODEGENOPT(XRayTotalFunctionGroups, 32, 1)
VALUE_CODEGENOPT(XRaySelectedFunctionGroup, 32, 0)
VALUE_CODEGENOPT(PatchableFunctionEntryCount , 32, 0) ///< Number of NOPs at function entry
VALUE_CODEGENOPT(PatchableFunctionEntryOffset , 32, 0)

View File

@ -1339,6 +1339,17 @@ def fxray_instrumentation_bundle :
Group<f_Group>, Flags<[CC1Option]>,
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'.">;
def fxray_function_groups :
Joined<["-"], "fxray-function-groups=">,
Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Only instrument 1 of N groups">;
def fxray_selected_function_group :
Joined<["-"], "fxray-selected-function-group=">,
Group<f_Group>, Flags<[CC1Option]>,
HelpText<"When using -fxray-function-groups, select which group of functions to instrument. Valid range is 0 to fxray-function-groups - 1">;
def ffine_grained_bitfield_accesses : Flag<["-"],
"ffine-grained-bitfield-accesses">, Group<f_clang_Group>, Flags<[CC1Option]>,
HelpText<"Use separate accesses for consecutive bitfield runs with legal widths and alignments.">;

View File

@ -32,6 +32,8 @@ class XRayArgs {
bool XRayRT = true;
bool XRayIgnoreLoops = false;
bool XRayFunctionIndex;
int XRayFunctionGroups = 1;
int XRaySelectedFunctionGroup;
public:
/// Parses the XRay arguments from an argument list.

View File

@ -32,6 +32,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/CodeGen/CGFunctionInfo.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Dominators.h"
@ -40,6 +41,7 @@
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Operator.h"
#include "llvm/Support/CRC.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
using namespace clang;
using namespace CodeGen;
@ -772,13 +774,16 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
SanOpts.Mask &= ~SanitizerKind::Null;
// Apply xray attributes to the function (as a string, for now)
bool AlwaysXRayAttr = false;
if (const auto *XRayAttr = D ? D->getAttr<XRayInstrumentAttr>() : nullptr) {
if (CGM.getCodeGenOpts().XRayInstrumentationBundle.has(
XRayInstrKind::FunctionEntry) ||
CGM.getCodeGenOpts().XRayInstrumentationBundle.has(
XRayInstrKind::FunctionExit)) {
if (XRayAttr->alwaysXRayInstrument() && ShouldXRayInstrumentFunction())
if (XRayAttr->alwaysXRayInstrument() && ShouldXRayInstrumentFunction()) {
Fn->addFnAttr("function-instrument", "xray-always");
AlwaysXRayAttr = true;
}
if (XRayAttr->neverXRayInstrument())
Fn->addFnAttr("function-instrument", "xray-never");
if (const auto *LogArgs = D->getAttr<XRayLogArgsAttr>())
@ -804,6 +809,16 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
if (!CGM.getCodeGenOpts().XRayInstrumentationBundle.has(
XRayInstrKind::FunctionEntry))
Fn->addFnAttr("xray-skip-entry");
auto FuncGroups = CGM.getCodeGenOpts().XRayTotalFunctionGroups;
if (FuncGroups > 1) {
auto FuncName = llvm::makeArrayRef<uint8_t>(
CurFn->getName().bytes_begin(), CurFn->getName().bytes_end());
auto Group = crc32(FuncName) % FuncGroups;
if (Group != CGM.getCodeGenOpts().XRaySelectedFunctionGroup &&
!AlwaysXRayAttr)
Fn->addFnAttr("function-instrument", "xray-never");
}
}
unsigned Count, Offset;

View File

@ -186,6 +186,21 @@ XRayArgs::XRayArgs(const ToolChain &TC, const ArgList &Args) {
Modes.push_back(std::string(M));
}
if (const Arg *A = Args.getLastArg(options::OPT_fxray_function_groups)) {
StringRef S = A->getValue();
if (S.getAsInteger(0, XRayFunctionGroups) || XRayFunctionGroups < 1)
D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
}
if (const Arg *A =
Args.getLastArg(options::OPT_fxray_selected_function_group)) {
StringRef S = A->getValue();
if (S.getAsInteger(0, XRaySelectedFunctionGroup) ||
XRaySelectedFunctionGroup < 0 ||
XRaySelectedFunctionGroup >= XRayFunctionGroups)
D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
}
// Then we want to sort and unique the modes we've collected.
llvm::sort(Modes);
Modes.erase(std::unique(Modes.begin(), Modes.end()), Modes.end());
@ -210,6 +225,17 @@ void XRayArgs::addArgs(const ToolChain &TC, const ArgList &Args,
if (!XRayFunctionIndex)
CmdArgs.push_back("-fno-xray-function-index");
if (XRayFunctionGroups > 1) {
CmdArgs.push_back(Args.MakeArgString(Twine("-fxray-function-groups=") +
Twine(XRayFunctionGroups)));
}
if (XRaySelectedFunctionGroup != 0) {
CmdArgs.push_back(
Args.MakeArgString(Twine("-fxray-selected-function-group=") +
Twine(XRaySelectedFunctionGroup)));
}
CmdArgs.push_back(Args.MakeArgString(Twine(XRayInstructionThresholdOption) +
Twine(InstructionThreshold)));

View File

@ -1130,6 +1130,10 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
getLastArgIntValue(Args, OPT_fxray_instruction_threshold_EQ, 200, Diags);
Opts.XRayIgnoreLoops = Args.hasArg(OPT_fxray_ignore_loops);
Opts.XRayOmitFunctionIndex = Args.hasArg(OPT_fno_xray_function_index);
Opts.XRayTotalFunctionGroups =
getLastArgIntValue(Args, OPT_fxray_function_groups, 1, Diags);
Opts.XRaySelectedFunctionGroup =
getLastArgIntValue(Args, OPT_fxray_selected_function_group, 0, Diags);
auto XRayInstrBundles =
Args.getAllArgValues(OPT_fxray_instrumentation_bundle);

View File

@ -0,0 +1,56 @@
// RUN: %clang_cc1 -fxray-instrument -fxray-instruction-threshold=1 -fxray-function-groups=3 -fxray-selected-function-group=0 \
// RUN: -emit-llvm -o - %s -triple x86_64-unknown-linux-gnu | FileCheck --check-prefix=GROUP0 %s
// RUN: %clang_cc1 -fxray-instrument -fxray-instruction-threshold=1 -fxray-function-groups=3 -fxray-selected-function-group=1 \
// RUN: -emit-llvm -o - %s -triple x86_64-unknown-linux-gnu | FileCheck --check-prefix=GROUP1 %s
// RUN: %clang_cc1 -fxray-instrument -fxray-instruction-threshold=1 -fxray-function-groups=3 -fxray-selected-function-group=2 \
// RUN: -emit-llvm -o - %s -triple x86_64-unknown-linux-gnu | FileCheck --check-prefix=GROUP2 %s
static int foo() { // part of group 0
return 1;
}
int bar() { // part of group 2
return 1;
}
int yarr() { // part of group 1
foo();
return 1;
}
[[clang::xray_always_instrument]] int always() { // part of group 0
return 1;
}
[[clang::xray_never_instrument]] int never() { // part of group 1
return 1;
}
// GROUP0: define{{.*}} i32 @_Z3barv() #[[ATTRS_BAR:[0-9]+]] {
// GROUP0: define{{.*}} i32 @_Z4yarrv() #[[ATTRS_BAR]] {
// GROUP0: define{{.*}} i32 @_ZL3foov() #[[ATTRS_FOO:[0-9]+]] {
// GROUP0: define{{.*}} i32 @_Z6alwaysv() #[[ATTRS_ALWAYS:[0-9]+]] {
// GROUP0: define{{.*}} i32 @_Z5neverv() #[[ATTRS_NEVER:[0-9]+]] {
// GROUP0-DAG: attributes #[[ATTRS_BAR]] = {{.*}} "function-instrument"="xray-never" {{.*}}
// GROUP0-DAG: attributes #[[ATTRS_ALWAYS]] = {{.*}} "function-instrument"="xray-always" {{.*}}
// GROUP0-DAG: attributes #[[ATTRS_NEVER]] = {{.*}} "function-instrument"="xray-never" {{.*}}
// GROUP1: define{{.*}} i32 @_Z3barv() #[[ATTRS_BAR:[0-9]+]] {
// GROUP1: define{{.*}} i32 @_Z4yarrv() #[[ATTRS_YARR:[0-9]+]] {
// GROUP1: define{{.*}} i32 @_ZL3foov() #[[ATTRS_BAR]] {
// GROUP1: define{{.*}} i32 @_Z6alwaysv() #[[ATTRS_ALWAYS:[0-9]+]] {
// GROUP1: define{{.*}} i32 @_Z5neverv() #[[ATTRS_NEVER:[0-9]+]] {
// GROUP1-DAG: attributes #[[ATTRS_BAR]] = {{.*}} "function-instrument"="xray-never" {{.*}}
// GROUP1-DAG: attributes #[[ATTRS_ALWAYS]] = {{.*}} "function-instrument"="xray-always" {{.*}}
// GROUP1-DAG: attributes #[[ATTRS_NEVER]] = {{.*}} "function-instrument"="xray-never" {{.*}}
// GROUP2: define{{.*}} i32 @_Z3barv() #[[ATTRS_BAR:[0-9]+]] {
// GROUP2: define{{.*}} i32 @_Z4yarrv() #[[ATTRS_YARR:[0-9]+]] {
// GROUP2: define{{.*}} i32 @_ZL3foov() #[[ATTRS_YARR]] {
// GROUP2: define{{.*}} i32 @_Z6alwaysv() #[[ATTRS_ALWAYS:[0-9]+]] {
// GROUP2: define{{.*}} i32 @_Z5neverv() #[[ATTRS_NEVER:[0-9]+]] {
// GROUP2-DAG: attributes #[[ATTRS_YARR]] = {{.*}} "function-instrument"="xray-never" {{.*}}
// GROUP2-DAG: attributes #[[ATTRS_ALWAYS]] = {{.*}} "function-instrument"="xray-always" {{.*}}
// GROUP2-DAG: attributes #[[ATTRS_NEVER]] = {{.*}} "function-instrument"="xray-never" {{.*}}

View File

@ -62,17 +62,18 @@ For example:
clang -fxray-instrument ...
By default, functions that have at least 200 instructions will get XRay
instrumentation points. You can tweak that number through the
By default, functions that have at least 200 instructions (or contain a loop) will
get XRay instrumentation points. You can tweak that number through the
``-fxray-instruction-threshold=`` flag:
::
clang -fxray-instrument -fxray-instruction-threshold=1 ...
You can also specifically instrument functions in your binary to either always
or never be instrumented using source-level attributes. You can do it using the
GCC-style attributes or C++11-style attributes.
The loop detection can be disabled with ``-fxray-ignore-loops`` to use only the
instruction threshold. You can also specifically instrument functions in your
binary to either always or never be instrumented using source-level attributes.
You can do it using the GCC-style attributes or C++11-style attributes.
.. code-block:: c++
@ -309,6 +310,35 @@ libraries, distributed with the LLVM distribution. These are:
instrumentation map in XRay-instrumented object files and binaries. The
``extract`` and ``stack`` subcommands uses this particular library.
Minimizing Binary Size
----------------------
XRay supports several different instrumentation points including ``function-entry``,
``function-exit``, ``custom``, and ``typed`` points. These can be enabled individually
using the ``-fxray-instrumentaton-bundle=`` flag. For example if you only wanted to
instrument function entry and custom points you could specify:
::
clang -fxray-instrument -fxray-instrumentation-bundle=function-entry,custom ...
This will omit the other sled types entirely, reducing the binary size. You can also
instrument just a sampled subset of functions using instrumentation groups.
For example, to instrument only a quarter of available functions invoke:
::
clang -fxray-instrument -fxray-function-groups=4
A subset will be chosen arbitrarily based on a hash of the function name. To sample a
different subset you can specify ``-fxray-selected-function-group=`` with a group number
in the range of 0 to ``xray-function-groups`` - 1. Together these options could be used
to produce multiple binaries with different instrumented subsets. If all you need is
runtime control over which functions are being traced at any given time it is better
to selectively patch and unpatch the individual functions you need using the XRay
Runtime Library's ``__xray_patch_function()`` method.
Future Work
===========