diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -120,6 +120,12 @@ ///< 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) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1339,6 +1339,17 @@ 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, Flags<[CC1Option]>, + HelpText<"Only instrument 1 of N groups">; + +def fxray_selected_function_group : + Joined<["-"], "fxray-selected-function-group=">, + 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, Flags<[CC1Option]>, HelpText<"Use separate accesses for consecutive bitfield runs with legal widths and alignments.">; diff --git a/clang/include/clang/Driver/XRayArgs.h b/clang/include/clang/Driver/XRayArgs.h --- a/clang/include/clang/Driver/XRayArgs.h +++ b/clang/include/clang/Driver/XRayArgs.h @@ -32,6 +32,8 @@ bool XRayRT = true; bool XRayIgnoreLoops = false; bool XRayFunctionIndex; + int XRayFunctionGroups = 1; + int XRaySelectedFunctionGroup; public: /// Parses the XRay arguments from an argument list. diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -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 @@ 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() : 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()) @@ -804,6 +809,16 @@ if (!CGM.getCodeGenOpts().XRayInstrumentationBundle.has( XRayInstrKind::FunctionEntry)) Fn->addFnAttr("xray-skip-entry"); + + auto FuncGroups = CGM.getCodeGenOpts().XRayTotalFunctionGroups; + if (FuncGroups > 1) { + auto FuncName = llvm::makeArrayRef( + 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; diff --git a/clang/lib/Driver/XRayArgs.cpp b/clang/lib/Driver/XRayArgs.cpp --- a/clang/lib/Driver/XRayArgs.cpp +++ b/clang/lib/Driver/XRayArgs.cpp @@ -186,6 +186,21 @@ 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 @@ 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))); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1130,6 +1130,10 @@ 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); diff --git a/clang/test/CodeGen/xray-function-groups.cpp b/clang/test/CodeGen/xray-function-groups.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/xray-function-groups.cpp @@ -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" {{.*}} diff --git a/llvm/docs/XRay.rst b/llvm/docs/XRay.rst --- a/llvm/docs/XRay.rst +++ b/llvm/docs/XRay.rst @@ -62,17 +62,18 @@ 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 @@ 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 ===========