[clang] [compiler-rt] [llvm] [XRay] Add `-fxray-default-options` to pass build-time defined XRay options (PR #116878)

Min-Yih Hsu via cfe-commits cfe-commits at lists.llvm.org
Tue Nov 19 13:29:03 PST 2024


https://github.com/mshockwave created https://github.com/llvm/llvm-project/pull/116878

This flag specifies XRay options that will automatically be applied to the instrumented binaries during run-time even without setting `XRAY_OPTIONS`. This is useful in cases where setting the `XRAY_OPTIONS` environment variable might be difficult. Plus, it's a convenient way to populate XRay options when you always want the instrumentation to be enabled.

The implementation follows what memory profiler and PGO did for their profile file name and profile version, respectively: insert a weak linkage global variable carrying the option string, which is then intercepted by compiler-rt during xray's option parsing phase.

>From 95ee4bd09aa48d4f0c2c4f5fc1e81e20cd57c7e2 Mon Sep 17 00:00:00 2001
From: Min-Yih Hsu <min.hsu at sifive.com>
Date: Mon, 11 Nov 2024 11:40:43 -0800
Subject: [PATCH] [XRay] Add `-fxray-default-options` to pass build-time
 defined XRay options

This is useful in cases where setting the `XRAY_OPTIONS` environment
variable might be difficult. Plus, it's a convenient way to populate
XRay options when you always want the instrumentation to be enabled.

The implementation follows what memory profiler and PGO did for their
profile file name and profile version, respectively: insert a weak
linkage global variable carrying the option string, which is then
intercepted by compiler-rt during xray's option parsing phase.
---
 clang/include/clang/Basic/CodeGenOptions.h    |  4 ++
 clang/include/clang/Driver/Options.td         |  6 +++
 clang/lib/CodeGen/BackendUtil.cpp             |  8 +++
 clang/lib/CodeGen/CodeGenModule.cpp           |  8 +++
 clang/lib/Driver/XRayArgs.cpp                 |  3 ++
 clang/test/CodeGen/xray-default-options.c     |  7 +++
 compiler-rt/lib/xray/CMakeLists.txt           |  1 +
 compiler-rt/lib/xray/xray_flags.cpp           |  4 ++
 compiler-rt/lib/xray/xray_flags.h             |  6 +++
 compiler-rt/lib/xray/xray_options_var.cpp     | 24 +++++++++
 .../xray/TestCases/Posix/default-options.cpp  | 12 +++++
 llvm/docs/XRay.rst                            | 15 +++++-
 .../Instrumentation/XRayPreparation.h         | 24 +++++++++
 llvm/lib/Passes/PassBuilder.cpp               |  1 +
 llvm/lib/Passes/PassRegistry.def              |  1 +
 .../Transforms/Instrumentation/CMakeLists.txt |  1 +
 .../Instrumentation/XRayPreparation.cpp       | 49 +++++++++++++++++++
 llvm/test/CodeGen/X86/xray-default-options.ll | 10 ++++
 18 files changed, 183 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CodeGen/xray-default-options.c
 create mode 100644 compiler-rt/lib/xray/xray_options_var.cpp
 create mode 100644 compiler-rt/test/xray/TestCases/Posix/default-options.cpp
 create mode 100644 llvm/include/llvm/Transforms/Instrumentation/XRayPreparation.h
 create mode 100644 llvm/lib/Transforms/Instrumentation/XRayPreparation.cpp
 create mode 100644 llvm/test/CodeGen/X86/xray-default-options.ll

diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 2dcf98b465661e..1e9e3b87052f51 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -399,6 +399,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// Set of XRay instrumentation kinds to emit.
   XRayInstrSet XRayInstrumentationBundle;
 
+  /// Default XRay options. Will be overrided by the XRAY_OPTIONS
+  /// environment variable during run-time.
+  std::string XRayDefaultOptions;
+
   std::vector<std::string> DefaultFunctionAttrs;
 
   /// List of dynamic shared object files to be loaded as pass plugins.
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index f2f9c20c9bc264..c5ae8ec8fc528c 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2882,6 +2882,12 @@ defm xray_instrument : BoolFOption<"xray-instrument",
           "Generate XRay instrumentation sleds on function entry and exit">,
   NegFlag<SetFalse>>;
 
+def fxray_default_options_EQ :
+  Joined<["-"], "fxray-default-options=">,
+  Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
+  HelpText<"Default XRay options. Can be overwritten by XRAY_OPTIONS environment variable during run-time.">,
+  MarshallingInfoString<CodeGenOpts<"XRayDefaultOptions">>;
+
 def fxray_instruction_threshold_EQ :
   Joined<["-"], "fxray-instruction-threshold=">,
   Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index bf9b04f02e9f44..1ad204a6b29a77 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -77,6 +77,7 @@
 #include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h"
 #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
 #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
+#include "llvm/Transforms/Instrumentation/XRayPreparation.h"
 #include "llvm/Transforms/ObjCARC.h"
 #include "llvm/Transforms/Scalar/EarlyCSE.h"
 #include "llvm/Transforms/Scalar/GVN.h"
@@ -1062,6 +1063,13 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
       });
     }
 
+    if (CodeGenOpts.XRayInstrumentFunctions &&
+        !CodeGenOpts.XRayDefaultOptions.empty()) {
+      PB.registerOptimizerLastEPCallback(
+          [](ModulePassManager &MPM, OptimizationLevel Level,
+             ThinOrFullLTOPhase) { MPM.addPass(XRayPreparationPass()); });
+    }
+
     if (CodeGenOpts.FatLTO) {
       MPM.addPass(PB.buildFatLTODefaultPipeline(
           Level, PrepareForThinLTO,
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index b854eeb62a80ce..803668e3dece38 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -448,6 +448,14 @@ CodeGenModule::CodeGenModule(ASTContext &C,
   if (Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86)
     getModule().addModuleFlag(llvm::Module::Error, "NumRegisterParameters",
                               CodeGenOpts.NumRegisterParameters);
+
+  // Insert XRay default options if it hasn't been done.
+  if (CodeGenOpts.XRayInstrumentFunctions &&
+      !CodeGenOpts.XRayDefaultOptions.empty() &&
+      !getModule().getModuleFlag("xray-default-opts"))
+    getModule().addModuleFlag(
+        llvm::Module::Error, "xray-default-opts",
+        llvm::MDString::get(LLVMContext, CodeGenOpts.XRayDefaultOptions));
 }
 
 CodeGenModule::~CodeGenModule() {}
diff --git a/clang/lib/Driver/XRayArgs.cpp b/clang/lib/Driver/XRayArgs.cpp
index de5c38ebc3abbd..cabd6e48db7c8b 100644
--- a/clang/lib/Driver/XRayArgs.cpp
+++ b/clang/lib/Driver/XRayArgs.cpp
@@ -200,6 +200,9 @@ void XRayArgs::addArgs(const ToolChain &TC, const ArgList &Args,
     Args.addOptInFlag(CmdArgs, options::OPT_fxray_shared,
                       options::OPT_fno_xray_shared);
 
+  if (const Arg *A = Args.getLastArg(options::OPT_fxray_default_options_EQ))
+    A->render(Args, CmdArgs);
+
   if (const Arg *A =
           Args.getLastArg(options::OPT_fxray_instruction_threshold_EQ)) {
     int Value;
diff --git a/clang/test/CodeGen/xray-default-options.c b/clang/test/CodeGen/xray-default-options.c
new file mode 100644
index 00000000000000..92c52d1d30e326
--- /dev/null
+++ b/clang/test/CodeGen/xray-default-options.c
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -fxray-instrument -fxray-default-options='patch_premain=true,xray_mode=xray-basic' \
+// RUN:     -std=c11 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s
+
+void justAFunction() {
+}
+
+// CHECK: !{{[0-9]+}} = !{i32 1, !"xray-default-opts", !"patch_premain=true,xray_mode=xray-basic"}
diff --git a/compiler-rt/lib/xray/CMakeLists.txt b/compiler-rt/lib/xray/CMakeLists.txt
index 7e3f1a0aa616e5..ee22611b75f25c 100644
--- a/compiler-rt/lib/xray/CMakeLists.txt
+++ b/compiler-rt/lib/xray/CMakeLists.txt
@@ -7,6 +7,7 @@ set(XRAY_SOURCES
   xray_flags.cpp
   xray_interface.cpp
   xray_log_interface.cpp
+  xray_options_var.cpp
   xray_utils.cpp
   )
 
diff --git a/compiler-rt/lib/xray/xray_flags.cpp b/compiler-rt/lib/xray/xray_flags.cpp
index e4c6906dc44345..8a10da5e3665ee 100644
--- a/compiler-rt/lib/xray/xray_flags.cpp
+++ b/compiler-rt/lib/xray/xray_flags.cpp
@@ -67,6 +67,10 @@ void initializeFlags() XRAY_NEVER_INSTRUMENT {
   const char *XRayCompileFlags = useCompilerDefinedFlags();
   XRayParser.ParseString(XRayCompileFlags);
 
+  // Override from compile-time options.
+  if (XRAY_OPTIONS_VAR[0] != 0)
+    XRayParser.ParseString(XRAY_OPTIONS_VAR);
+
   // Override from environment variables.
   XRayParser.ParseStringFromEnv("XRAY_OPTIONS");
 
diff --git a/compiler-rt/lib/xray/xray_flags.h b/compiler-rt/lib/xray/xray_flags.h
index cce6fe9d62f9f6..228e70176d025b 100644
--- a/compiler-rt/lib/xray/xray_flags.h
+++ b/compiler-rt/lib/xray/xray_flags.h
@@ -17,6 +17,12 @@
 #include "sanitizer_common/sanitizer_flag_parser.h"
 #include "sanitizer_common/sanitizer_internal_defs.h"
 
+#define XRAY_OPTIONS_VAR __llvm_xray_options
+
+extern "C" {
+extern char XRAY_OPTIONS_VAR[1];
+}
+
 namespace __xray {
 
 struct Flags {
diff --git a/compiler-rt/lib/xray/xray_options_var.cpp b/compiler-rt/lib/xray/xray_options_var.cpp
new file mode 100644
index 00000000000000..832ca57b253aa4
--- /dev/null
+++ b/compiler-rt/lib/xray/xray_options_var.cpp
@@ -0,0 +1,24 @@
+/*===----- xray_options_var.cpp - XRay option variable setup  -------------===*\
+|*
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+|* See https://llvm.org/LICENSE.txt for license information.
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+|*
+\*===----------------------------------------------------------------------===*/
+
+#include "xray_flags.h"
+
+// FIXME: Generalize these. See lib/profile/InstrProfilingPort.h and
+// include/profile/InstrProfData.inc
+#define COMPILER_RT_VISIBILITY __attribute__((visibility("hidden")))
+#define COMPILER_RT_WEAK __attribute__((weak))
+
+extern "C" {
+/* char __llvm_xray_options[1]
+ *
+ * The runtime should only provide its own definition of this symbol when the
+ * user has not specified one. Set this up by moving the runtime's copy of this
+ * symbol to an object file within the archive.
+ */
+COMPILER_RT_WEAK COMPILER_RT_VISIBILITY char XRAY_OPTIONS_VAR[1] = {0};
+}
diff --git a/compiler-rt/test/xray/TestCases/Posix/default-options.cpp b/compiler-rt/test/xray/TestCases/Posix/default-options.cpp
new file mode 100644
index 00000000000000..f5cfea3d82379e
--- /dev/null
+++ b/compiler-rt/test/xray/TestCases/Posix/default-options.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_xray -g -fxray-default-options='patch_premain=true:verbosity=1:xray_mode=xray-basic' -o %t %s
+// RUN: rm -f xray-log.default-options.*
+// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: rm -f xray-log.default-options.*
+//
+// REQUIRES: target={{(aarch64|loongarch64|x86_64)-.*}}
+// REQUIRES: built-in-llvm-tree
+__attribute__((xray_always_instrument)) void always() {}
+
+int main() { always(); }
+
+// CHECK: =={{[0-9].*}}==XRay: Log file in '{{.*}}'
diff --git a/llvm/docs/XRay.rst b/llvm/docs/XRay.rst
index acfb83c5374eda..fccfed83957a45 100644
--- a/llvm/docs/XRay.rst
+++ b/llvm/docs/XRay.rst
@@ -157,7 +157,8 @@ Also by default the filename of the XRay trace is ``xray-log.XXXXXX`` where the
 ``XXXXXX`` part is randomly generated.
 
 These options can be controlled through the ``XRAY_OPTIONS`` environment
-variable, where we list down the options and their defaults below.
+variable during program run-time, where we list down the options and their
+defaults below.
 
 +-------------------+-----------------+---------------+------------------------+
 | Option            | Type            | Default       | Description            |
@@ -177,6 +178,18 @@ variable, where we list down the options and their defaults below.
 |                   |                 |               | level.                 |
 +-------------------+-----------------+---------------+------------------------+
 
+In addition to environment variable, you can also use ``-fxray-default-options``
+to specify default XRay options during program build time.
+
+For example, programs built with the following flags automatically use
+'patch_premain=true,xray_mode=basic' by default even without setting ``XRAY_OPTIONS``.
+
+::
+
+  clang -fxray-instrument -fxray-default-options='patch_premain=true,xray_mode=basic' ...
+
+Note that you still can override options designated by ``-fxray-default-options``
+using ``XRAY_OPTIONS`` during run-time.
 
 If you choose to not use the default logging implementation that comes with the
 XRay runtime and/or control when/how the XRay instrumentation runs, you may use
diff --git a/llvm/include/llvm/Transforms/Instrumentation/XRayPreparation.h b/llvm/include/llvm/Transforms/Instrumentation/XRayPreparation.h
new file mode 100644
index 00000000000000..93aec841def862
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Instrumentation/XRayPreparation.h
@@ -0,0 +1,24 @@
+//===- XRayPreparation.h - Preparation for XRay instrumentation -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This Pass does some IR-level preparations (e.g. inserting global variable
+// that carries default options, if there is any) for XRay instrumentation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_XRAYPREPARATION_H
+#define LLVM_TRANSFORMS_INSTRUMENTATION_XRAYPREPARATION_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+struct XRayPreparationPass : public PassInfoMixin<XRayPreparationPass> {
+  PreservedAnalyses run(Module &, ModuleAnalysisManager &);
+};
+} // namespace llvm
+#endif
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index bc6b449d22abe8..0edde8b6d3ff2b 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -221,6 +221,7 @@
 #include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h"
 #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
 #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
+#include "llvm/Transforms/Instrumentation/XRayPreparation.h"
 #include "llvm/Transforms/ObjCARC.h"
 #include "llvm/Transforms/Scalar/ADCE.h"
 #include "llvm/Transforms/Scalar/AlignmentFromAssumptions.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 7c3798f6462a46..3e3113815def36 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -157,6 +157,7 @@ MODULE_PASS("tsan-module", ModuleThreadSanitizerPass())
 MODULE_PASS("verify", VerifierPass())
 MODULE_PASS("view-callgraph", CallGraphViewerPass())
 MODULE_PASS("wholeprogramdevirt", WholeProgramDevirtPass())
+MODULE_PASS("xray-preparation", XRayPreparationPass())
 #undef MODULE_PASS
 
 #ifndef MODULE_PASS_WITH_PARAMS
diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
index 3e3c3eced4bb9c..9979ce0bee3a69 100644
--- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
+++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
@@ -26,6 +26,7 @@ add_llvm_component_library(LLVMInstrumentation
   ThreadSanitizer.cpp
   HWAddressSanitizer.cpp
   RealtimeSanitizer.cpp
+  XRayPreparation.cpp
 
   ADDITIONAL_HEADER_DIRS
   ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms
diff --git a/llvm/lib/Transforms/Instrumentation/XRayPreparation.cpp b/llvm/lib/Transforms/Instrumentation/XRayPreparation.cpp
new file mode 100644
index 00000000000000..f3a688184b49b0
--- /dev/null
+++ b/llvm/lib/Transforms/Instrumentation/XRayPreparation.cpp
@@ -0,0 +1,49 @@
+//===- XRayPreparation.cpp - Preparation for XRay instrumentation -------- ===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This Pass does some IR-level preparations (e.g. inserting global variable
+// that carries default options, if there is any) for XRay instrumentation.
+//
+//===---------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Instrumentation/XRayPreparation.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Module.h"
+#include "llvm/TargetParser/Triple.h"
+
+using namespace llvm;
+
+static void createXRayDefaultOptionsVar(Module &M, StringRef DefaultOptions) {
+  Constant *DefaultOptionsConst =
+      ConstantDataArray::getString(M.getContext(), DefaultOptions, true);
+  // This global variable will be passed into XRay's compiler-rt and used as
+  // the initial set of XRay options.
+  GlobalVariable *DefaultOptsVar = new GlobalVariable(
+      M, DefaultOptionsConst->getType(), true, GlobalValue::WeakAnyLinkage,
+      DefaultOptionsConst, "__llvm_xray_options");
+  DefaultOptsVar->setVisibility(GlobalValue::HiddenVisibility);
+  Triple TT(M.getTargetTriple());
+  if (TT.supportsCOMDAT()) {
+    DefaultOptsVar->setLinkage(GlobalValue::ExternalLinkage);
+    DefaultOptsVar->setComdat(M.getOrInsertComdat("__llvm_xray_options"));
+  }
+}
+
+PreservedAnalyses XRayPreparationPass::run(Module &M,
+                                           ModuleAnalysisManager &MAM) {
+  // XRay default options.
+  if (const auto *DefaultOpts =
+          dyn_cast_or_null<MDString>(M.getModuleFlag("xray-default-opts"))) {
+    createXRayDefaultOptionsVar(M, DefaultOpts->getString());
+    PreservedAnalyses PA;
+    PA.preserveSet<CFGAnalyses>();
+    return PA;
+  }
+
+  return PreservedAnalyses::all();
+}
diff --git a/llvm/test/CodeGen/X86/xray-default-options.ll b/llvm/test/CodeGen/X86/xray-default-options.ll
new file mode 100644
index 00000000000000..9ae40b23525ef0
--- /dev/null
+++ b/llvm/test/CodeGen/X86/xray-default-options.ll
@@ -0,0 +1,10 @@
+; RUN: opt -S -p xray-preparation < %s | FileCheck %s
+
+target triple = "x86_64-unknown-linux-gnu"
+
+; CHECK: $__llvm_xray_options = comdat any
+; CHECK: @__llvm_xray_options = hidden constant [40 x i8] c"patch_premain=true,xray_mode=xray-basic\00", comdat
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 1, !"xray-default-opts", !"patch_premain=true,xray_mode=xray-basic"}



More information about the cfe-commits mailing list