[clang] 6f7fbdd - [xray] Function coverage groups
Ian Levesque via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 24 19:10:06 PDT 2020
Author: Ian Levesque
Date: 2020-09-24T22:09:53-04:00
New Revision: 6f7fbdd2857fc8a7280afbb26fd4e1a6450069e4
URL: https://github.com/llvm/llvm-project/commit/6f7fbdd2857fc8a7280afbb26fd4e1a6450069e4
DIFF: https://github.com/llvm/llvm-project/commit/6f7fbdd2857fc8a7280afbb26fd4e1a6450069e4.diff
LOG: [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
Added:
clang/test/CodeGen/xray-function-groups.cpp
Modified:
clang/include/clang/Basic/CodeGenOptions.def
clang/include/clang/Driver/Options.td
clang/include/clang/Driver/XRayArgs.h
clang/lib/CodeGen/CodeGenFunction.cpp
clang/lib/Driver/XRayArgs.cpp
clang/lib/Frontend/CompilerInvocation.cpp
llvm/docs/XRay.rst
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index b5da2a9cde1a..a259218b29c6 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -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)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index d7c2496b8a5d..a1f3d7a4316f 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -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.">;
diff --git a/clang/include/clang/Driver/XRayArgs.h b/clang/include/clang/Driver/XRayArgs.h
index 2f055e5c6d7d..4c18fecd2763 100644
--- a/clang/include/clang/Driver/XRayArgs.h
+++ b/clang/include/clang/Driver/XRayArgs.h
@@ -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.
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 016c7105b52d..47ef5c830723 100644
--- 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 @@ 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;
diff --git a/clang/lib/Driver/XRayArgs.cpp b/clang/lib/Driver/XRayArgs.cpp
index f00c3906df97..b44509ad3b88 100644
--- a/clang/lib/Driver/XRayArgs.cpp
+++ b/clang/lib/Driver/XRayArgs.cpp
@@ -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)));
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index a88a91182307..2d008d8a3fbe 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -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);
diff --git a/clang/test/CodeGen/xray-function-groups.cpp b/clang/test/CodeGen/xray-function-groups.cpp
new file mode 100644
index 000000000000..b50c90b09c6e
--- /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
index 8616088b1062..72768fa8410a 100644
--- a/llvm/docs/XRay.rst
+++ b/llvm/docs/XRay.rst
@@ -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
diff erent 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
+
diff erent 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
diff erent 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
===========
More information about the cfe-commits
mailing list