From a07a5175be26292026bd30d22d4baf6f889ec622 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Mon, 9 Feb 2026 10:32:34 -0600 Subject: [PATCH 1/7] Add instrument_fn attribute This attribute enables or disables function instrumentation when using `-Zinstrument-xray` or `-Zinstrument-mcount`. It supports the following usage: `#[instrument_fn = "on|off"]` For XRay, "on" is equivalent to always instrument, and "off" is equivalent to never instrumenting. For mcount, "on" has no effect. "off" disables instrumentation of the function. --- .../src/attributes/codegen_attrs.rs | 40 +++++++++ compiler/rustc_attr_parsing/src/context.rs | 1 + compiler/rustc_codegen_llvm/src/attributes.rs | 53 +++++++---- .../rustc_codegen_ssa/src/codegen_attrs.rs | 3 + compiler/rustc_feature/src/builtin_attrs.rs | 4 + compiler/rustc_feature/src/unstable.rs | 2 + .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../src/middle/codegen_fn_attrs.rs | 3 + compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + tests/codegen-llvm/instrument_fn.rs | 47 ++++++++++ tests/ui/attributes/instrument_fn.rs | 43 +++++++++ tests/ui/attributes/instrument_fn.stderr | 89 +++++++++++++++++++ .../feature-gate-instrument-fn.rs | 13 +++ .../feature-gate-instrument-fn.stderr | 23 +++++ 16 files changed, 312 insertions(+), 15 deletions(-) create mode 100644 tests/codegen-llvm/instrument_fn.rs create mode 100644 tests/ui/attributes/instrument_fn.rs create mode 100644 tests/ui/attributes/instrument_fn.stderr create mode 100644 tests/ui/feature-gates/feature-gate-instrument-fn.rs create mode 100644 tests/ui/feature-gates/feature-gate-instrument-fn.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 4909e0d35173c..ac534243dba44 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -574,6 +574,46 @@ impl CombineAttributeParser for ForceTargetFeatureParser { } } +pub(crate) struct InstrumentFnParser; + +impl SingleAttributeParser for InstrumentFnParser { + const PATH: &[Symbol] = &[sym::instrument_fn]; + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "on|off"); + + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let mut instrument = None; + match args { + ArgParser::NameValue(nv) => { + if let Some(option) = nv.value_as_str() + && (option == sym::off || option == sym::on) + { + instrument = Some(option == sym::on); + } else { + cx.expected_specific_argument(nv.value_span, &[sym::on, sym::off]); + } + } + ArgParser::List(_) => { + cx.expected_single_argument(cx.attr_span); + } + ArgParser::NoArgs => { + cx.expected_specific_argument(cx.attr_span, &[sym::on, sym::off]); + } + } + Some(AttributeKind::InstrumentFn(instrument)) + } +} + pub(crate) struct SanitizeParser; impl SingleAttributeParser for SanitizeParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 190568bed508d..34e6beefde667 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -187,6 +187,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 546fa87ff5612..0ad3ffbfe7965 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -199,35 +199,57 @@ fn function_return_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll fn instrument_function_attr<'ll>( cx: &SimpleCx<'ll>, sess: &Session, + instrument_fn: &Option, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); if sess.opts.unstable_opts.instrument_mcount { // Similar to `clang -pg` behavior. Handled by the // `post-inline-ee-instrument` LLVM pass. - // The function name varies on platforms. - // See test/CodeGen/mcount.c in clang. - let mcount_name = match &sess.target.llvm_mcount_intrinsic { - Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(), - None => sess.target.mcount.as_ref(), - }; + // #[instrument_fn], the default is on. + let instrument_entry = instrument_fn.unwrap_or_else(|| true); - attrs.push(llvm::CreateAttrStringValue( - cx.llcx, - "instrument-function-entry-inlined", - mcount_name, - )); + if instrument_entry { + // The function name varies on platforms. + // See test/CodeGen/mcount.c in clang. + let mcount_name = match &sess.target.llvm_mcount_intrinsic { + Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(), + None => sess.target.mcount.as_ref(), + }; + + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "instrument-function-entry-inlined", + mcount_name, + )); + } } if let Some(options) = &sess.opts.unstable_opts.instrument_xray { // XRay instrumentation is similar to __cyg_profile_func_{enter,exit}. // Function prologue and epilogue are instrumented with NOP sleds, // a runtime library later replaces them with detours into tracing code. - if options.always { - attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-always")); + + let mut never = options.never; + let mut always = options.always; + + // Apply optional #[instrument_fn] override. + match instrument_fn { + Some(true) => { + always = true; + } + Some(false) => { + never = true; + } + None => {} } - if options.never { + + if never { attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-never")); } + if always { + attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-always")); + } + if options.ignore_loops { attrs.push(llvm::CreateAttrString(cx.llcx, "xray-ignore-loops")); } @@ -239,6 +261,7 @@ fn instrument_function_attr<'ll>( "xray-instruction-threshold", &threshold.to_string(), )); + if options.skip_entry { attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-entry")); } @@ -397,7 +420,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( // FIXME: none of these functions interact with source level attributes. to_add.extend(frame_pointer_type_attr(cx, sess)); to_add.extend(function_return_attr(cx, sess)); - to_add.extend(instrument_function_attr(cx, sess)); + to_add.extend(instrument_function_attr(cx, sess, &codegen_fn_attrs.instrument_fn)); to_add.extend(nojumptables_attr(cx, sess)); to_add.extend(probestack_attr(cx, tcx)); to_add.extend(stackprotector_attr(cx, sess)); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index b8a8bb3ad419d..9aa5520596999 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -292,6 +292,9 @@ fn process_builtin_attrs( codegen_fn_attrs.patchable_function_entry = Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); } + AttributeKind::InstrumentFn(instrument_fn) => { + codegen_fn_attrs.instrument_fn = *instrument_fn; + } _ => {} } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 3a2f548902d11..b6c4484e042d4 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -768,6 +768,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(List: &["set"], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute"), ErrorPreceding, EncodeCrossCrate::No ), + gated!( + instrument_fn, Normal, template!(List: &[r#"entry = "on|off""#]), ErrorPreceding, + EncodeCrossCrate::No, instrument_fn, experimental!(instrument_fn), + ), gated!( unsafe force_target_feature, Normal, template!(List: &[r#"enable = "name""#]), DuplicatesOk, EncodeCrossCrate::No, effective_target_features, experimental!(force_target_feature) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 39e886227d946..86a472cd98135 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -544,6 +544,8 @@ declare_features! ( (unstable, import_trait_associated_functions, "1.86.0", Some(134691)), /// Allows associated types in inherent impls. (incomplete, inherent_associated_types, "1.52.0", Some(8995)), + /// Enable #[instrument_fn] on function (todo: tracking issue) + (unstable, instrument_fn, "CURRENT_RUSTC_VERSION", None), /// Allows using `pointer` and `reference` in intra-doc links (unstable, intra_doc_pointers, "1.51.0", Some(80896)), /// lahfsahf target feature on x86. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index e8476c3d8c73b..0b6772c409e30 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1070,6 +1070,9 @@ pub enum AttributeKind { /// Represents `#[instruction_set]` InstructionSet(InstructionSetAttr), + /// Represents `#[instrument_fn]` + InstrumentFn(Option), + /// Represents `#[lang]` Lang(LangItem, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 27128f6996370..e7e94b64a026b 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -50,6 +50,7 @@ impl AttributeKind { Ignore { .. } => No, Inline(..) => No, InstructionSet(..) => No, + InstrumentFn(..) => No, Lang(..) => Yes, Link(..) => No, LinkName { .. } => Yes, // Needed for rustdoc diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 4f600af0cbfce..fbcf79b6976ea 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -109,6 +109,8 @@ pub struct CodegenFnAttrs { pub objc_class: Option, /// The `#[rustc_objc_selector = "..."]` attribute. pub objc_selector: Option, + /// The `#[instrument_fn]` attribute. + pub instrument_fn: Option, } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)] @@ -236,6 +238,7 @@ impl CodegenFnAttrs { patchable_function_entry: None, objc_class: None, objc_selector: None, + instrument_fn: None, } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bec6ab7e83551..307f56a037358 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -259,6 +259,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Fundamental | AttributeKind::Ignore { .. } | AttributeKind::InstructionSet(..) + | AttributeKind::InstrumentFn(..) | AttributeKind::Lang(..) | AttributeKind::LinkName { .. } | AttributeKind::LinkOrdinal { .. } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 48cdf46b821f9..fa162c3117d9f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1087,6 +1087,7 @@ symbols! { inout, inputs, instruction_set, + instrument_fn, integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below integral, internal, diff --git a/tests/codegen-llvm/instrument_fn.rs b/tests/codegen-llvm/instrument_fn.rs new file mode 100644 index 0000000000000..75de5b66a7b2b --- /dev/null +++ b/tests/codegen-llvm/instrument_fn.rs @@ -0,0 +1,47 @@ +// Verify the #[instrument_fn] applies the correct LLVM IR function attributes. +// +//@ revisions:XRAY MCOUNT +//@ add-minicore +//@ [XRAY] compile-flags: -Zinstrument-xray -Copt-level=0 +//@ [MCOUNT] compile-flags: -Zinstrument-mcount -Copt-level=0 + +#![feature(no_core)] +#![crate_type = "lib"] +#![feature(instrument_fn)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[no_mangle] +// CHECK: define void @instrument_default() {{.*}} [[DFLT_ATTR:#[0-9]+]] +fn instrument_default() {} + +#[no_mangle] +#[instrument_fn = "off"] +// CHECK: define void @instrument_off() {{.*}} [[OFF_ATTR:#[0-9]+]] +fn instrument_off() {} + +#[no_mangle] +#[instrument_fn = "on"] +// MCOUNT: define void @instrument_on() {{.*}} [[DFLT_ATTR]] +// XRAY: define void @instrument_on() {{.*}} [[ON_ATTR:#[0-9]+]] +fn instrument_on() {} + +// MCOUNT: attributes [[DFLT_ATTR]] {{.*}} "instrument-function-entry-inlined"= +// MCOUNT-NOT: attributes [[OFF_ATTR]] {{.*}} "instrument-function-entry-inlined"= + +// XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "function-instrument"="xray-always" +// XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "function-instrument"="xray-never" +// XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "xray-skip-exit" +// XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "xray-skip-entry" + +// XRAY-NOT: attributes [[OFF_ATTR]] {{.*}} "function-instrument"="xray-always" +// XRAY: attributes [[OFF_ATTR]] {{.*}} "function-instrument"="xray-never" +// XRAY-NOT: attributes [[OFF_ATTR]] {{.*}} "xray-skip-exit" +// XRAY-NOT: attributes [[OFF_ATTR]] {{.*}} "xray-skip-entry" + +// XRAY: attributes [[ON_ATTR]] {{.*}} "function-instrument"="xray-always" +// XRAY-NOT: attributes [[ON_ATTR]] {{.*}} "function-instrument"="xray-never" +// XRAY-NOT: attributes [[ON_ATTR]] {{.*}} "xray-skip-exit" +// XRAY-NOT: attributes [[ON_ATTR]] {{.*}} "xray-skip-entry" diff --git a/tests/ui/attributes/instrument_fn.rs b/tests/ui/attributes/instrument_fn.rs new file mode 100644 index 0000000000000..ca87bbb646a8f --- /dev/null +++ b/tests/ui/attributes/instrument_fn.rs @@ -0,0 +1,43 @@ +#![feature(instrument_fn)] + +#[instrument_fn = "on"] //~ ERROR attribute cannot be used on +struct F; + +#[instrument_fn = "on"] //~ ERROR attribute cannot be used on +mod module {} + +#[instrument_fn = "on"] //~ ERROR attribute cannot be used on +impl F { + #[instrument_fn = "off"] + fn no_instrument_fn(self, x: u32) -> u32 { + #[instrument_fn = "off"] //~ ERROR attribute cannot be used on + //~^ ERROR attributes on expressions are experimental + x * 2 + } +} + +#[instrument_fn = "off"] //~ ERROR attribute cannot be used on +trait Foo { + #[instrument_fn = "off"] //~ ERROR attribute cannot be used on + fn bar(); + + #[instrument_fn = "off"] + fn baz() {} +} + +impl Foo for F { + #[instrument_fn = "off"] + fn bar() {} +} + +#[instrument_fn = "on"] +fn main() {} + +#[instrument_fn(entry = "on")] //~ ERROR malformed +fn instrument_fn_list() {} + +#[instrument_fn] //~ ERROR malformed +fn instrument_fn_noarg() {} + +#[instrument_fn = 1] //~ ERROR malformed +fn instrument_fn_invalid_opt() {} diff --git a/tests/ui/attributes/instrument_fn.stderr b/tests/ui/attributes/instrument_fn.stderr new file mode 100644 index 0000000000000..5e889b9fe6e2c --- /dev/null +++ b/tests/ui/attributes/instrument_fn.stderr @@ -0,0 +1,89 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/instrument_fn.rs:13:9 + | +LL | #[instrument_fn = "off"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: `#[instrument_fn]` attribute cannot be used on structs + --> $DIR/instrument_fn.rs:3:1 + | +LL | #[instrument_fn = "on"] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[instrument_fn]` can only be applied to functions + +error: `#[instrument_fn]` attribute cannot be used on modules + --> $DIR/instrument_fn.rs:6:1 + | +LL | #[instrument_fn = "on"] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[instrument_fn]` can only be applied to functions + +error: `#[instrument_fn]` attribute cannot be used on inherent impl blocks + --> $DIR/instrument_fn.rs:9:1 + | +LL | #[instrument_fn = "on"] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[instrument_fn]` can only be applied to functions + +error: `#[instrument_fn]` attribute cannot be used on expressions + --> $DIR/instrument_fn.rs:13:9 + | +LL | #[instrument_fn = "off"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[instrument_fn]` can only be applied to functions + +error: `#[instrument_fn]` attribute cannot be used on traits + --> $DIR/instrument_fn.rs:19:1 + | +LL | #[instrument_fn = "off"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[instrument_fn]` can only be applied to functions + +error: `#[instrument_fn]` attribute cannot be used on required trait methods + --> $DIR/instrument_fn.rs:21:5 + | +LL | #[instrument_fn = "off"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[instrument_fn]` can be applied to functions, inherent methods, provided trait methods, and trait methods in impl blocks + +error[E0805]: malformed `instrument_fn` attribute input + --> $DIR/instrument_fn.rs:36:1 + | +LL | #[instrument_fn(entry = "on")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected a single argument here + | help: must be of the form: `#[instrument_fn = "on|off"]` + +error[E0539]: malformed `instrument_fn` attribute input + --> $DIR/instrument_fn.rs:39:1 + | +LL | #[instrument_fn] + | ^^^^^^^^^^^^^^^^ + | | + | valid arguments are `on` or `off` + | help: must be of the form: `#[instrument_fn = "on|off"]` + +error[E0539]: malformed `instrument_fn` attribute input + --> $DIR/instrument_fn.rs:42:1 + | +LL | #[instrument_fn = 1] + | ^^^^^^^^^^^^^^^^^^-^ + | | | + | | valid arguments are `on` or `off` + | help: must be of the form: `#[instrument_fn = "on|off"]` + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0539, E0658, E0805. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/feature-gates/feature-gate-instrument-fn.rs b/tests/ui/feature-gates/feature-gate-instrument-fn.rs new file mode 100644 index 0000000000000..7ec84c978b8b2 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-instrument-fn.rs @@ -0,0 +1,13 @@ +//@ add-minicore +//@ compile-flags: --crate-type=rlib +#![no_core] +#![feature(no_core)] + +extern crate minicore; +use minicore::*; + +#[instrument_fn = "on"] //~ ERROR attribute is an experimental feature +fn instrumented_fn() {} + +#[instrument_fn = "off"] //~ ERROR attribute is an experimental feature +fn not_instrumented_fn() {} diff --git a/tests/ui/feature-gates/feature-gate-instrument-fn.stderr b/tests/ui/feature-gates/feature-gate-instrument-fn.stderr new file mode 100644 index 0000000000000..07869b1ea7b69 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-instrument-fn.stderr @@ -0,0 +1,23 @@ +error[E0658]: the `#[instrument_fn]` attribute is an experimental feature + --> $DIR/feature-gate-instrument-fn.rs:9:1 + | +LL | #[instrument_fn = "on"] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #99999 for more information + = help: add `#![feature(instrument_fn)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `#[instrument_fn]` attribute is an experimental feature + --> $DIR/feature-gate-instrument-fn.rs:12:1 + | +LL | #[instrument_fn = "off"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #99999 for more information + = help: add `#![feature(instrument_fn)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. From 6e9fa585757265a5394f127d8ac880f4c39eed76 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Mon, 16 Feb 2026 16:19:57 -0600 Subject: [PATCH 2/7] Add `-Zinstrument-fentry` The behavior is very similar to `-Zinstrument-mcount`, except this option has no frame-pointer requirements, and is called before the call frame is set up. This only works on x86, x86-64, and s390x targets. --- compiler/rustc_codegen_llvm/src/attributes.rs | 8 ++++-- compiler/rustc_session/src/options.rs | 2 ++ compiler/rustc_session/src/session.rs | 4 +++ compiler/rustc_target/src/spec/json.rs | 3 +++ compiler/rustc_target/src/spec/mod.rs | 4 +++ .../spec/targets/i686_unknown_linux_gnu.rs | 1 + .../spec/targets/i686_unknown_linux_musl.rs | 1 + .../spec/targets/s390x_unknown_linux_gnu.rs | 1 + .../spec/targets/s390x_unknown_linux_musl.rs | 1 + .../targets/s390x_unknown_none_softfloat.rs | 1 + .../spec/targets/x86_64_unknown_linux_gnu.rs | 1 + .../spec/targets/x86_64_unknown_linux_musl.rs | 1 + .../spec/targets/x86_64_unknown_linux_none.rs | 1 + tests/assembly-llvm/x86_64-fentry.rs | 19 ++++++++++++++ tests/codegen-llvm/instrument-fentry.rs | 25 +++++++++++++++++++ tests/codegen-llvm/instrument_fn.rs | 10 +++++--- 16 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 tests/assembly-llvm/x86_64-fentry.rs create mode 100644 tests/codegen-llvm/instrument-fentry.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 0ad3ffbfe7965..cfbd4e56b30fa 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -202,14 +202,14 @@ fn instrument_function_attr<'ll>( instrument_fn: &Option, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - if sess.opts.unstable_opts.instrument_mcount { + if sess.opts.unstable_opts.instrument_mcount || sess.opts.unstable_opts.instrument_fentry { // Similar to `clang -pg` behavior. Handled by the // `post-inline-ee-instrument` LLVM pass. // #[instrument_fn], the default is on. let instrument_entry = instrument_fn.unwrap_or_else(|| true); - if instrument_entry { + if instrument_entry && sess.opts.unstable_opts.instrument_mcount { // The function name varies on platforms. // See test/CodeGen/mcount.c in clang. let mcount_name = match &sess.target.llvm_mcount_intrinsic { @@ -223,6 +223,10 @@ fn instrument_function_attr<'ll>( mcount_name, )); } + + if instrument_entry && sess.opts.unstable_opts.instrument_fentry { + attrs.push(llvm::CreateAttrStringValue(cx.llcx, "fentry-call", "true")); + } } if let Some(options) = &sess.opts.unstable_opts.instrument_xray { // XRay instrumentation is similar to __cyg_profile_func_{enter,exit}. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 74b3aa11d0d80..06ff9e7d182fb 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2400,6 +2400,8 @@ options! { "a default MIR inlining threshold (default: 50)"), input_stats: bool = (false, parse_bool, [UNTRACKED], "print some statistics about AST and HIR (default: no)"), + instrument_fentry: bool = (false, parse_bool, [TRACKED], + "insert function instrument code for fentry-based tracing (default: no)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), instrument_xray: Option = (None, parse_instrument_xray, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 0548380331bef..6440e51ac650c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1300,6 +1300,10 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } + if sess.opts.unstable_opts.instrument_fentry && !sess.target.options.supports_fentry { + sess.dcx().emit_err(errors::InstrumentationNotSupported { us: "fentry".to_string() }); + } + if sess.opts.unstable_opts.instrument_xray.is_some() && !sess.target.options.supports_xray { sess.dcx().emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() }); } diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 20fbb687b3080..4ca7396dfee77 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -225,6 +225,7 @@ impl Target { forward!(supports_stack_protector); forward!(small_data_threshold_support); forward!(entry_name); + forward!(supports_fentry); forward!(supports_xray); // we're going to run `update_from_cli`, but that won't change the target's AbiMap @@ -409,6 +410,7 @@ impl ToJson for Target { target_option_val!(small_data_threshold_support); target_option_val!(entry_name); target_option_val!(entry_abi); + target_option_val!(supports_fentry); target_option_val!(supports_xray); // Serializing `-Clink-self-contained` needs a dynamic key to support the @@ -630,6 +632,7 @@ struct TargetSpecJson { supports_stack_protector: Option, small_data_threshold_support: Option, entry_name: Option>, + supports_fentry: Option, supports_xray: Option, entry_abi: Option, } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 77cc6dd1036c8..56a78078887b5 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2612,6 +2612,9 @@ pub struct TargetOptions { /// Default value is `CanonAbi::C` pub entry_abi: CanonAbi, + /// Whether the target supports fentry instrumentation. + pub supports_fentry: bool, + /// Whether the target supports XRay instrumentation. pub supports_xray: bool, @@ -2854,6 +2857,7 @@ impl Default for TargetOptions { supports_stack_protector: true, entry_name: "main".into(), entry_abi: CanonAbi::C, + supports_fentry: false, supports_xray: false, default_address_space: rustc_abi::AddressSpace::ZERO, small_data_threshold_support: SmallDataThresholdSupport::DefaultForArch, diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs index 932f6034433e5..7bc48b1998fac 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs @@ -22,6 +22,7 @@ pub(crate) fn target() -> Target { base.supported_sanitizers = SanitizerSet::ADDRESS; base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); base.stack_probes = StackProbeType::Inline; + base.supports_fentry = true; Target { llvm_target: "i686-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs index 5c85a3e13820c..212c2b8db56da 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs @@ -12,6 +12,7 @@ pub(crate) fn target() -> Target { base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32", "-Wl,-melf_i386"]); base.stack_probes = StackProbeType::Inline; + base.supports_fentry = true; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs index 8859e0d650a2b..e6b58bff22848 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs @@ -12,6 +12,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_fentry = true; Target { llvm_target: "s390x-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index 21e705ebbec5d..5ff9f025ba1d9 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -13,6 +13,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_fentry = true; Target { llvm_target: "s390x-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_none_softfloat.rs index 7d42c1fd92440..3f7498672af34 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_none_softfloat.rs @@ -20,6 +20,7 @@ pub(crate) fn target() -> Target { rustc_abi: Some(RustcAbi::Softfloat), stack_probes: StackProbeType::Inline, supported_sanitizers: SanitizerSet::KERNELADDRESS, + supports_fentry: true, ..Default::default() }; diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs index defa9f146d798..0d87a7b760c61 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs @@ -19,6 +19,7 @@ pub(crate) fn target() -> Target { | SanitizerSet::SAFESTACK | SanitizerSet::THREAD | SanitizerSet::REALTIME; + base.supports_fentry = true; base.supports_xray = true; Target { diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs index ee883532ad4ac..a664f22479913 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs @@ -15,6 +15,7 @@ pub(crate) fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_fentry = true; base.supports_xray = true; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs index 768b1a1ba1128..c0d65818d325b 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_none.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.linker_flavor = LinkerFlavor::Gnu(Cc::No, Lld::Yes); base.linker = Some("rust-lld".into()); base.panic_strategy = PanicStrategy::Abort; + base.supports_fentry = true; Target { llvm_target: "x86_64-unknown-linux-none".into(), diff --git a/tests/assembly-llvm/x86_64-fentry.rs b/tests/assembly-llvm/x86_64-fentry.rs new file mode 100644 index 0000000000000..008df25f75a9f --- /dev/null +++ b/tests/assembly-llvm/x86_64-fentry.rs @@ -0,0 +1,19 @@ +//@ assembly-output: emit-asm +//@ compile-flags: -Zinstrument-fentry=y -Cllvm-args=-x86-asm-syntax=intel + +//@ revisions: x86_64-linux +//@[x86_64-linux] compile-flags: --target=x86_64-unknown-linux-gnu +//@[x86_64-linux] needs-llvm-components: x86 +//@[x86_64-linux] only-x86_64-unknown-linux-gnu + +#![crate_type = "lib"] + +// CHECK-LABEL: mcount_func: +#[no_mangle] +pub fn mcount_func() { + // CHECK: call __fentry__ + + std::hint::black_box(()); + + // CHECK: ret +} diff --git a/tests/codegen-llvm/instrument-fentry.rs b/tests/codegen-llvm/instrument-fentry.rs new file mode 100644 index 0000000000000..933f8ac60168d --- /dev/null +++ b/tests/codegen-llvm/instrument-fentry.rs @@ -0,0 +1,25 @@ +//@ add-minicore +//@ compile-flags: -Z instrument-fentry -Copt-level=0 +// +//@ revisions: x86_64-linux +//@[x86_64-linux] compile-flags: --target=x86_64-unknown-linux-gnu +//@[x86_64-linux] needs-llvm-components: x86 +// +//@ revisions: x86-linux +//@[x86-linux] compile-flags: --target=i686-unknown-linux-gnu +//@[x86-linux] needs-llvm-components: x86 +// +//@ revisions: s390x-linux +//@[s390x-linux] compile-flags: --target=s390x-unknown-linux-gnu +//@[s390x-linux] needs-llvm-components: systemz + +#![feature(no_core)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +// CHECK: attributes #{{.*}} "fentry-call"="true" +pub fn foo() {} diff --git a/tests/codegen-llvm/instrument_fn.rs b/tests/codegen-llvm/instrument_fn.rs index 75de5b66a7b2b..fb4f72411a82e 100644 --- a/tests/codegen-llvm/instrument_fn.rs +++ b/tests/codegen-llvm/instrument_fn.rs @@ -1,9 +1,11 @@ // Verify the #[instrument_fn] applies the correct LLVM IR function attributes. // -//@ revisions:XRAY MCOUNT +//@ revisions:XRAY MCOUNT FENTRY //@ add-minicore //@ [XRAY] compile-flags: -Zinstrument-xray -Copt-level=0 //@ [MCOUNT] compile-flags: -Zinstrument-mcount -Copt-level=0 +//@ [FENTRY] compile-flags: -Zinstrument-fentry -Copt-level=0 --target=x86_64-unknown-linux-gnu +//@ [FENTRY] needs-llvm-components: x86 #![feature(no_core)] #![crate_type = "lib"] @@ -25,14 +27,16 @@ fn instrument_off() {} #[no_mangle] #[instrument_fn = "on"] // MCOUNT: define void @instrument_on() {{.*}} [[DFLT_ATTR]] +// FENTRY: define void @instrument_on() {{.*}} [[DFLT_ATTR]] // XRAY: define void @instrument_on() {{.*}} [[ON_ATTR:#[0-9]+]] fn instrument_on() {} // MCOUNT: attributes [[DFLT_ATTR]] {{.*}} "instrument-function-entry-inlined"= // MCOUNT-NOT: attributes [[OFF_ATTR]] {{.*}} "instrument-function-entry-inlined"= -// XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "function-instrument"="xray-always" -// XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "function-instrument"="xray-never" +// FENTRY: attributes [[DFLT_ATTR]] {{.*}} "fentry-call"="true" +// FENTRY-NOT: attributes [[OFF_ATTR]] {{.*}} "fentry-call"="true" + // XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "xray-skip-exit" // XRAY-NOT: attributes [[DFLT_ATTR]] {{.*}} "xray-skip-entry" From 85e4b04bc420fb0c7496d0bf95556913884979fa Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Thu, 5 Mar 2026 14:48:14 -0600 Subject: [PATCH 3/7] Add -Zinstrument-mcount-opts This facilitates support for inserting nop's and/or recording the location of each mcount call in a special section named `__mcount_loc`. --- compiler/rustc_codegen_llvm/src/attributes.rs | 7 ++++ compiler/rustc_session/src/config.rs | 19 ++++++++--- compiler/rustc_session/src/options.rs | 34 +++++++++++++++++++ tests/codegen-llvm/instrument-mcount-opts.rs | 31 +++++++++++++++++ 4 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 tests/codegen-llvm/instrument-mcount-opts.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index cfbd4e56b30fa..25bed443853e5 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -227,6 +227,13 @@ fn instrument_function_attr<'ll>( if instrument_entry && sess.opts.unstable_opts.instrument_fentry { attrs.push(llvm::CreateAttrStringValue(cx.llcx, "fentry-call", "true")); } + + if sess.opts.unstable_opts.instrument_mcount_opts.no_call { + attrs.push(llvm::CreateAttrString(cx.llcx, "mnop-mcount")); + } + if sess.opts.unstable_opts.instrument_mcount_opts.record { + attrs.push(llvm::CreateAttrString(cx.llcx, "mrecord-mcount")); + } } if let Some(options) = &sess.opts.unstable_opts.instrument_xray { // XRay instrumentation is similar to __cyg_profile_func_{enter,exit}. diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 0677ca0fddbaa..775b6ea1efaec 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -246,6 +246,15 @@ pub enum AnnotateMoves { Enabled(Option), } +/// Settings for `-Z instrument-mcount-opts` flag. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +pub struct InstrumentMcountOpts { + // Insert a nop which could be replaced by an mcount call. + pub no_call: bool, + // Record the location of the call instrument in a special linker section. + pub record: bool, +} + /// Settings for `-Z instrument-xray` flag. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] pub struct InstrumentXRay { @@ -3074,10 +3083,11 @@ pub(crate) mod dep_tracking { use super::{ AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, - InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, - LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, OutFileName, OutputType, - OutputTypes, PatchableFunctionEntry, Polonius, ResolveDocLinks, SourceFileHashAlgorithm, - SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, + InliningThreshold, InstrumentCoverage, InstrumentMcountOpts, InstrumentXRay, + LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, + OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, + ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, + SymbolManglingVersion, WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -3139,6 +3149,7 @@ pub(crate) mod dep_tracking { TlsModel, InstrumentCoverage, CoverageOptions, + InstrumentMcountOpts, InstrumentXRay, CrateType, MergeFunctions, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 06ff9e7d182fb..559350f745226 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -825,6 +825,8 @@ mod desc { pub(crate) const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub(crate) const parse_instrument_coverage: &str = parse_bool; pub(crate) const parse_coverage_options: &str = "`block` | `branch` | `condition`"; + pub(crate) const parse_instrument_mcount_opts: &str = + "a comma separated list of options: `call`,`no-call`,`record`,`no-record`."; pub(crate) const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub(crate) const parse_unpretty: &str = "`string` or `string=string`"; pub(crate) const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; @@ -1567,6 +1569,32 @@ pub mod parse { true } + pub(crate) fn parse_instrument_mcount_opts( + slot: &mut InstrumentMcountOpts, + v: Option<&str>, + ) -> bool { + for option in v.into_iter().flat_map(|v| v.split(',')) { + match option { + "no-call" => { + slot.no_call = true; + } + "call" => { + slot.no_call = false; + } + "no-record" => { + slot.record = false; + } + "record" => { + slot.record = true; + } + _ => { + return false; + } + } + } + v.is_some() + } + pub(crate) fn parse_instrument_xray( slot: &mut Option, v: Option<&str>, @@ -2404,6 +2432,12 @@ options! { "insert function instrument code for fentry-based tracing (default: no)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), + instrument_mcount_opts: InstrumentMcountOpts = (InstrumentMcountOpts::default(), parse_instrument_mcount_opts, [TRACKED], + "mcount instrumentation configuration options (default: `call,no-record`). + Optional extra settings: + `=call` or `=no-call` + `=no-record` or `=record` + Multiple options can be combined with commas."), instrument_xray: Option = (None, parse_instrument_xray, [TRACKED], "insert function instrument code for XRay-based tracing (default: no) Optional extra settings: diff --git a/tests/codegen-llvm/instrument-mcount-opts.rs b/tests/codegen-llvm/instrument-mcount-opts.rs new file mode 100644 index 0000000000000..0c23f714d5ac4 --- /dev/null +++ b/tests/codegen-llvm/instrument-mcount-opts.rs @@ -0,0 +1,31 @@ +//@ revisions: dflt ncyr ycnr ncnr ycyr +//@ add-minicore +//@ needs-llvm-components: systemz +//@ compile-flags: -Zinstrument-fentry=y -Copt-level=0 --target=s390x-unknown-linux-gnu +//@[ncyr] compile-flags: -Zinstrument-mcount-opts=no-call,record +//@[ncnr] compile-flags: -Zinstrument-mcount-opts=no-call,no-record +//@[ycnr] compile-flags: -Zinstrument-mcount-opts=call,no-record +//@[ycyr] compile-flags: -Zinstrument-mcount-opts=call,record +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +// dflt: attributes #{{.*}} {{.*}} "fentry-call"="true" +// dflt-NOT: attributes #{{.*}} {{.*}} "mnop-mcount" +// dflt-NOT: attributes #{{.*}} {{.*}} "mrecord-mcount" +// +// ncyr: attributes #{{.*}} {{.*}} "fentry-call"="true" "mnop-mcount" "mrecord-mcount" +// +// ncnr: attributes #{{.*}} {{.*}} "fentry-call"="true" "mnop-mcount" +// ncnr-NOT: attributes #{{.*}} {{.*}} "mrecord-mcount" +// +// ycnr: attributes #{{.*}} {{.*}} "fentry-call"="true" +// ycnr-NOT: attributes #{{.*}} {{.*}} "mnop-mcount" +// ycnr-NOT: attributes #{{.*}} {{.*}} "mrecord-mcount" +// +// ycyr: attributes #{{.*}} {{.*}} "fentry-call"="true" "mrecord-mcount" +// ycyr-NOT: attributes #{{.*}} {{.*}} "mnop-mcount" +pub fn foo() {} From e59dc8e7691f6fcd1ab52639073371f88ff550d1 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Wed, 11 Mar 2026 09:06:35 -0500 Subject: [PATCH 4/7] Refactor -Zinstrument-mcount, -Zinstrument-fentry, -Zinstrument-xray Convert these into `-Zinstrument-function={none|fentry|xray}`. `-Zinstrument-xray` is split into `-Zinstrument-function=xray` and `-Zinstrument-xray-opts=...`. Also, rename `-Zinstrument-mcount-opts` to `-Zinstrument-fentry-opts` to reflect it is only usable with fentry. In theory, it could work with both (as it does on gcc), in practice the llvm support is limited to s390x/fentry as of llvm 22. --- compiler/rustc_codegen_llvm/src/attributes.rs | 156 ++++++++++-------- compiler/rustc_interface/src/tests.rs | 20 ++- compiler/rustc_session/src/config.rs | 42 +++-- compiler/rustc_session/src/options.rs | 61 +++---- compiler/rustc_session/src/session.rs | 23 ++- .../src/compiler-flags/instrument-xray.md | 10 +- tests/assembly-llvm/aarch64-xray.rs | 2 +- tests/assembly-llvm/x86_64-fentry.rs | 2 +- tests/assembly-llvm/x86_64-mcount.rs | 2 +- tests/assembly-llvm/x86_64-xray.rs | 2 +- tests/codegen-llvm/instrument-fentry.rs | 2 +- tests/codegen-llvm/instrument-mcount-opts.rs | 10 +- tests/codegen-llvm/instrument-mcount.rs | 2 +- tests/codegen-llvm/instrument-xray/basic.rs | 5 +- .../instrument-xray/options-combine.rs | 8 +- .../instrument-xray/options-override.rs | 6 +- tests/codegen-llvm/instrument_fn.rs | 7 +- tests/ui/README.md | 4 +- .../instrument-xray/flags-always-never-1.rs | 6 +- .../flags-always-never-1.stderr | 2 +- .../instrument-xray/flags-always-never-2.rs | 8 +- tests/ui/instrument-xray/flags-basic.rs | 6 +- tests/ui/instrument-xray/flags-dupe-always.rs | 6 +- .../instrument-xray/flags-dupe-always.stderr | 2 +- .../flags-dupe-ignore-loops.rs | 6 +- .../flags-dupe-ignore-loops.stderr | 2 +- tests/ui/instrument-xray/platform-support.rs | 8 +- 27 files changed, 232 insertions(+), 178 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 25bed443853e5..cf06639b8d58d 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -6,7 +6,9 @@ use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs, }; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet}; +use rustc_session::config::{ + BranchProtection, FunctionReturn, InstrumentFunction, OptLevel, PAuthKey, PacRet, +}; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{Arch, FramePointer, SanitizerSet, StackProbeType, StackProtector}; use smallvec::SmallVec; @@ -173,7 +175,7 @@ pub(crate) fn frame_pointer_type_attr<'ll>( let opts = &sess.opts; // "mcount" function relies on stack pointer. // See . - if opts.unstable_opts.instrument_mcount { + if opts.unstable_opts.instrument_function == InstrumentFunction::Mcount { fp.ratchet(FramePointer::Always); } fp.ratchet(opts.cg.force_frame_pointers); @@ -202,83 +204,101 @@ fn instrument_function_attr<'ll>( instrument_fn: &Option, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - if sess.opts.unstable_opts.instrument_mcount || sess.opts.unstable_opts.instrument_fentry { - // Similar to `clang -pg` behavior. Handled by the - // `post-inline-ee-instrument` LLVM pass. - - // #[instrument_fn], the default is on. - let instrument_entry = instrument_fn.unwrap_or_else(|| true); - - if instrument_entry && sess.opts.unstable_opts.instrument_mcount { - // The function name varies on platforms. - // See test/CodeGen/mcount.c in clang. - let mcount_name = match &sess.target.llvm_mcount_intrinsic { - Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(), - None => sess.target.mcount.as_ref(), - }; + match sess.opts.unstable_opts.instrument_function { + InstrumentFunction::Fentry => { + // Similar to `clang -pg -mfentry` behavior. - attrs.push(llvm::CreateAttrStringValue( - cx.llcx, - "instrument-function-entry-inlined", - mcount_name, - )); - } + // #[instrument_fn], the default is on. + let instrument_entry = instrument_fn.unwrap_or_else(|| true); - if instrument_entry && sess.opts.unstable_opts.instrument_fentry { - attrs.push(llvm::CreateAttrStringValue(cx.llcx, "fentry-call", "true")); - } + if instrument_entry { + attrs.push(llvm::CreateAttrStringValue(cx.llcx, "fentry-call", "true")); - if sess.opts.unstable_opts.instrument_mcount_opts.no_call { - attrs.push(llvm::CreateAttrString(cx.llcx, "mnop-mcount")); + if sess.opts.unstable_opts.instrument_fentry_opts.no_call { + attrs.push(llvm::CreateAttrString(cx.llcx, "mnop-mcount")); + } + if sess.opts.unstable_opts.instrument_fentry_opts.record { + attrs.push(llvm::CreateAttrString(cx.llcx, "mrecord-mcount")); + } + } } - if sess.opts.unstable_opts.instrument_mcount_opts.record { - attrs.push(llvm::CreateAttrString(cx.llcx, "mrecord-mcount")); + InstrumentFunction::Mcount => { + // Similar to `clang -pg` behavior. Handled by the + // `post-inline-ee-instrument` LLVM pass. + + // #[instrument_fn], the default is on. + let instrument_entry = instrument_fn.unwrap_or_else(|| true); + + if instrument_entry { + // The function name varies on platforms. + // See test/CodeGen/mcount.c in clang. + let mcount_name = match &sess.target.llvm_mcount_intrinsic { + Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(), + None => sess.target.mcount.as_ref(), + }; + + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "instrument-function-entry-inlined", + mcount_name, + )); + } } - } - if let Some(options) = &sess.opts.unstable_opts.instrument_xray { - // XRay instrumentation is similar to __cyg_profile_func_{enter,exit}. - // Function prologue and epilogue are instrumented with NOP sleds, - // a runtime library later replaces them with detours into tracing code. - - let mut never = options.never; - let mut always = options.always; + InstrumentFunction::XRay => { + // XRay instrumentation is similar to __cyg_profile_func_{enter,exit}. + // Function prologue and epilogue are instrumented with NOP sleds, + // a runtime library later replaces them with detours into tracing code. + let options = &sess.opts.unstable_opts.instrument_xray_opts; + + let mut never = options.never; + let mut always = options.always; + + // Apply optional #[instrument_fn] override. + match instrument_fn { + Some(true) => { + always = true; + } + Some(false) => { + never = true; + } + None => {} + } - // Apply optional #[instrument_fn] override. - match instrument_fn { - Some(true) => { - always = true; + if never { + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "function-instrument", + "xray-never", + )); } - Some(false) => { - never = true; + if always { + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "function-instrument", + "xray-always", + )); } - None => {} - } - if never { - attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-never")); - } - if always { - attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-always")); - } - - if options.ignore_loops { - attrs.push(llvm::CreateAttrString(cx.llcx, "xray-ignore-loops")); - } - // LLVM will not choose the default for us, but rather requires specific - // threshold in absence of "xray-always". Use the same default as Clang. - let threshold = options.instruction_threshold.unwrap_or(200); - attrs.push(llvm::CreateAttrStringValue( - cx.llcx, - "xray-instruction-threshold", - &threshold.to_string(), - )); + if options.ignore_loops { + attrs.push(llvm::CreateAttrString(cx.llcx, "xray-ignore-loops")); + } + // LLVM will not choose the default for us, but rather requires specific + // threshold in absence of "xray-always". Use the same default as Clang. + let threshold = options.instruction_threshold.unwrap_or(200); + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "xray-instruction-threshold", + &threshold.to_string(), + )); - if options.skip_entry { - attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-entry")); - } - if options.skip_exit { - attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-exit")); + if options.skip_entry { + attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-entry")); + } + if options.skip_exit { + attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-exit")); + } } + InstrumentFunction::No => {} } attrs } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 88056a0db966d..a5ee969e1a922 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -12,12 +12,12 @@ use rustc_hir::attrs::{CollapseMacroDebuginfo, NativeLibKind}; use rustc_session::config::{ AnnotateMoves, AutoDiff, BranchProtection, CFGuard, Cfg, CoverageLevel, CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs, - FmtDebug, FunctionReturn, InliningThreshold, Input, InstrumentCoverage, InstrumentXRay, - LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, - Offload, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, - PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, - SymbolManglingVersion, WasiExecModel, build_configuration, build_session_options, - rustc_optgroups, + FmtDebug, FunctionReturn, InliningThreshold, Input, InstrumentCoverage, InstrumentFunction, + InstrumentMcountOpts, InstrumentXRayOpts, LinkSelfContained, LinkerPluginLto, LocationDetail, + LtoCli, MirIncludeSpans, NextSolverConfig, Offload, Options, OutFileName, OutputType, + OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, Polonius, + ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, + build_configuration, build_session_options, rustc_optgroups, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -811,8 +811,12 @@ fn test_unstable_options_tracking_hash() { tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); - tracked!(instrument_mcount, true); - tracked!(instrument_xray, Some(InstrumentXRay::default())); + tracked!(instrument_fentry_opts, InstrumentMcountOpts { no_call: true, record: true }); + tracked!(instrument_function, InstrumentFunction::Fentry); + tracked!( + instrument_xray_opts, + InstrumentXRayOpts { always: true, ..InstrumentXRayOpts::default() } + ); tracked!(link_directives, false); tracked!(link_only, true); tracked!(lint_llvm_ir, true); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 775b6ea1efaec..9d8c1b19894c4 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -150,6 +150,19 @@ pub enum InstrumentCoverage { Yes, } +/// The different settings that the `-Z instrument-function` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum InstrumentFunction { + /// No instrumentation requested, or `-Z instrument-function=none` + No, + /// `-Z instrument-function=fentry` + Fentry, + /// `-Z instrument-function=mcount` + Mcount, + /// `-Z instrument-function=xray` + XRay, +} + /// Individual flag values controlled by `-Zcoverage-options`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] pub struct CoverageOptions { @@ -255,22 +268,22 @@ pub struct InstrumentMcountOpts { pub record: bool, } -/// Settings for `-Z instrument-xray` flag. +/// Settings for `-Z instrument-xray-opts` flag. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] -pub struct InstrumentXRay { - /// `-Z instrument-xray=always`, force instrumentation +pub struct InstrumentXRayOpts { + /// `-Z instrument-xray-opts=always`, force instrumentation pub always: bool, - /// `-Z instrument-xray=never`, disable instrumentation + /// `-Z instrument-xray-opts=never`, disable instrumentation pub never: bool, - /// `-Z instrument-xray=ignore-loops`, ignore presence of loops, + /// `-Z instrument-xray-opts=ignore-loops`, ignore presence of loops, /// instrument functions based only on instruction count pub ignore_loops: bool, - /// `-Z instrument-xray=instruction-threshold=N`, explicitly set instruction threshold + /// `-Z instrument-xray-opts=instruction-threshold=N`, explicitly set instruction threshold /// for instrumentation, or `None` to use compiler's default pub instruction_threshold: Option, - /// `-Z instrument-xray=skip-entry`, do not instrument function entry + /// `-Z instrument-xray-opts=skip-entry`, do not instrument function entry pub skip_entry: bool, - /// `-Z instrument-xray=skip-exit`, do not instrument function exit + /// `-Z instrument-xray-opts=skip-exit`, do not instrument function exit pub skip_exit: bool, } @@ -3083,11 +3096,11 @@ pub(crate) mod dep_tracking { use super::{ AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, - InliningThreshold, InstrumentCoverage, InstrumentMcountOpts, InstrumentXRay, - LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, - OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, - ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, - SymbolManglingVersion, WasiExecModel, + InliningThreshold, InstrumentCoverage, InstrumentFunction, InstrumentMcountOpts, + InstrumentXRayOpts, LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, + NextSolverConfig, Offload, OptLevel, OutFileName, OutputType, OutputTypes, + PatchableFunctionEntry, Polonius, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, + SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -3148,9 +3161,10 @@ pub(crate) mod dep_tracking { CodeModel, TlsModel, InstrumentCoverage, + InstrumentFunction, CoverageOptions, InstrumentMcountOpts, - InstrumentXRay, + InstrumentXRayOpts, CrateType, MergeFunctions, OnBrokenPipe, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 559350f745226..4c8e8e76de53f 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -825,9 +825,10 @@ mod desc { pub(crate) const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub(crate) const parse_instrument_coverage: &str = parse_bool; pub(crate) const parse_coverage_options: &str = "`block` | `branch` | `condition`"; + pub(crate) const parse_instrument_function: &str = "`fentry` | `mcount` | `xray`"; pub(crate) const parse_instrument_mcount_opts: &str = "a comma separated list of options: `call`,`no-call`,`record`,`no-record`."; - pub(crate) const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; + pub(crate) const parse_instrument_xray_opts: &str = "a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub(crate) const parse_unpretty: &str = "`string` or `string=string`"; pub(crate) const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; pub(crate) const parse_next_solver_config: &str = @@ -1569,6 +1570,21 @@ pub mod parse { true } + pub(crate) fn parse_instrument_function( + slot: &mut InstrumentFunction, + v: Option<&str>, + ) -> bool { + let Some(v) = v else { return false }; + match v { + "fentry" => *slot = InstrumentFunction::Fentry, + "mcount" => *slot = InstrumentFunction::Mcount, + "xray" => *slot = InstrumentFunction::XRay, + "none" => *slot = InstrumentFunction::No, + _ => return false, + } + true + } + pub(crate) fn parse_instrument_mcount_opts( slot: &mut InstrumentMcountOpts, v: Option<&str>, @@ -1595,19 +1611,10 @@ pub mod parse { v.is_some() } - pub(crate) fn parse_instrument_xray( - slot: &mut Option, + pub(crate) fn parse_instrument_xray_opts( + slot: &mut InstrumentXRayOpts, v: Option<&str>, ) -> bool { - if v.is_some() { - let mut bool_arg = None; - if parse_opt_bool(&mut bool_arg, v) { - *slot = if bool_arg.unwrap() { Some(InstrumentXRay::default()) } else { None }; - return true; - } - } - - let options = slot.get_or_insert_default(); let mut seen_always = false; let mut seen_never = false; let mut seen_ignore_loops = false; @@ -1617,17 +1624,17 @@ pub mod parse { for option in v.into_iter().flat_map(|v| v.split(',')) { match option { "always" if !seen_always && !seen_never => { - options.always = true; - options.never = false; + slot.always = true; + slot.never = false; seen_always = true; } "never" if !seen_never && !seen_always => { - options.never = true; - options.always = false; + slot.never = true; + slot.always = false; seen_never = true; } "ignore-loops" if !seen_ignore_loops => { - options.ignore_loops = true; + slot.ignore_loops = true; seen_ignore_loops = true; } option @@ -1638,17 +1645,17 @@ pub mod parse { return false; }; match n.parse() { - Ok(n) => options.instruction_threshold = Some(n), + Ok(n) => slot.instruction_threshold = Some(n), Err(_) => return false, } seen_instruction_threshold = true; } "skip-entry" if !seen_skip_entry => { - options.skip_entry = true; + slot.skip_entry = true; seen_skip_entry = true; } "skip-exit" if !seen_skip_exit => { - options.skip_exit = true; + slot.skip_exit = true; seen_skip_exit = true; } _ => return false, @@ -2428,18 +2435,16 @@ options! { "a default MIR inlining threshold (default: 50)"), input_stats: bool = (false, parse_bool, [UNTRACKED], "print some statistics about AST and HIR (default: no)"), - instrument_fentry: bool = (false, parse_bool, [TRACKED], - "insert function instrument code for fentry-based tracing (default: no)"), - instrument_mcount: bool = (false, parse_bool, [TRACKED], - "insert function instrument code for mcount-based tracing (default: no)"), - instrument_mcount_opts: InstrumentMcountOpts = (InstrumentMcountOpts::default(), parse_instrument_mcount_opts, [TRACKED], - "mcount instrumentation configuration options (default: `call,no-record`). + instrument_fentry_opts: InstrumentMcountOpts = (InstrumentMcountOpts::default(), parse_instrument_mcount_opts, [TRACKED], + "fentry instrumentation configuration options (default: `call,no-record`). Optional extra settings: `=call` or `=no-call` `=no-record` or `=record` Multiple options can be combined with commas."), - instrument_xray: Option = (None, parse_instrument_xray, [TRACKED], - "insert function instrument code for XRay-based tracing (default: no) + instrument_function: InstrumentFunction = (InstrumentFunction::No, parse_instrument_function, [TRACKED], + "insert function instrument code for mcount-based tracing (default: none)"), + instrument_xray_opts: InstrumentXRayOpts = (InstrumentXRayOpts::default(), parse_instrument_xray_opts, [TRACKED], + "configuration options for XRay-based tracing (default is ``). Optional extra settings: `=always` `=never` diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 6440e51ac650c..47af7e84f33ee 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -37,7 +37,8 @@ use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use crate::config::{ self, CoverageLevel, CoverageOptions, CrateType, DebugInfo, ErrorOutputType, FunctionReturn, - Input, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath, + Input, InstrumentCoverage, InstrumentFunction, OptLevel, OutFileName, OutputType, + SwitchWithOptPath, }; use crate::filesearch::FileSearch; use crate::lint::LintId; @@ -1300,12 +1301,20 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } - if sess.opts.unstable_opts.instrument_fentry && !sess.target.options.supports_fentry { - sess.dcx().emit_err(errors::InstrumentationNotSupported { us: "fentry".to_string() }); - } - - if sess.opts.unstable_opts.instrument_xray.is_some() && !sess.target.options.supports_xray { - sess.dcx().emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() }); + match sess.opts.unstable_opts.instrument_function { + InstrumentFunction::No => {} + InstrumentFunction::Mcount => {} + InstrumentFunction::Fentry => { + if !sess.target.options.supports_fentry { + sess.dcx() + .emit_err(errors::InstrumentationNotSupported { us: "fentry".to_string() }); + } + } + InstrumentFunction::XRay => { + if !sess.target.options.supports_xray { + sess.dcx().emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() }); + } + } } if let Some(flavor) = sess.opts.cg.linker_flavor diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md index 7fb33cd68b4a3..846c364028753 100644 --- a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md +++ b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md @@ -9,11 +9,11 @@ For more information on XRay, read [LLVM documentation](https://llvm.org/docs/XRay.html), and/or the [XRay whitepaper](http://research.google.com/pubs/pub45287.html). -Set the `-Z instrument-xray` compiler flag in order to enable XRay instrumentation. +Set the `-Z instrument-function=xray` compiler flag in order to enable XRay instrumentation. - - `-Z instrument-xray` – use the default settings - - `-Z instrument-xray=skip-exit` – configure a custom setting - - `-Z instrument-xray=ignore-loops,instruction-threshold=300` – + - `-Z instrument-function=xray` – use the default settings + - `-Z instrument-function=xray -Z instrument-xray-opts=skip-exit` – configure a custom setting + - `-Z instrument-function=xray -Z instrument-xray-opts=ignore-loops,instruction-threshold=300` – multiple settings separated by commas Supported options: @@ -32,7 +32,7 @@ The default settings are: - instrument functions with at least 200 instructions, or containing a non-trivial loop -Note that `-Z instrument-xray` only enables generation of NOP sleds +Note that `-Z instrument-function=xray` only enables generation of NOP sleds which on their own don't do anything useful. In order to actually trace the functions, you will need to link a separate runtime library of your choice, diff --git a/tests/assembly-llvm/aarch64-xray.rs b/tests/assembly-llvm/aarch64-xray.rs index d5ee011184394..50f90cda3ea3c 100644 --- a/tests/assembly-llvm/aarch64-xray.rs +++ b/tests/assembly-llvm/aarch64-xray.rs @@ -1,5 +1,5 @@ //@ assembly-output: emit-asm -//@ compile-flags: -Zinstrument-xray=always +//@ compile-flags: -Zinstrument-function=xray -Zinstrument-xray-opts=always //@ revisions: aarch64-linux //@[aarch64-linux] compile-flags: --target=aarch64-unknown-linux-gnu diff --git a/tests/assembly-llvm/x86_64-fentry.rs b/tests/assembly-llvm/x86_64-fentry.rs index 008df25f75a9f..54605e7a61720 100644 --- a/tests/assembly-llvm/x86_64-fentry.rs +++ b/tests/assembly-llvm/x86_64-fentry.rs @@ -1,5 +1,5 @@ //@ assembly-output: emit-asm -//@ compile-flags: -Zinstrument-fentry=y -Cllvm-args=-x86-asm-syntax=intel +//@ compile-flags: -Zinstrument-function=fentry -Cllvm-args=-x86-asm-syntax=intel //@ revisions: x86_64-linux //@[x86_64-linux] compile-flags: --target=x86_64-unknown-linux-gnu diff --git a/tests/assembly-llvm/x86_64-mcount.rs b/tests/assembly-llvm/x86_64-mcount.rs index 0428272cfc598..88d1e2c4db649 100644 --- a/tests/assembly-llvm/x86_64-mcount.rs +++ b/tests/assembly-llvm/x86_64-mcount.rs @@ -1,5 +1,5 @@ //@ assembly-output: emit-asm -//@ compile-flags: -Zinstrument-mcount=y -Cllvm-args=-x86-asm-syntax=intel +//@ compile-flags: -Zinstrument-function=mcount -Cllvm-args=-x86-asm-syntax=intel //@ revisions: x86_64-linux //@[x86_64-linux] compile-flags: --target=x86_64-unknown-linux-gnu diff --git a/tests/assembly-llvm/x86_64-xray.rs b/tests/assembly-llvm/x86_64-xray.rs index 4cf3e8cda13c0..8bb2b18567201 100644 --- a/tests/assembly-llvm/x86_64-xray.rs +++ b/tests/assembly-llvm/x86_64-xray.rs @@ -1,5 +1,5 @@ //@ assembly-output: emit-asm -//@ compile-flags: -Zinstrument-xray=always -Cllvm-args=-x86-asm-syntax=intel +//@ compile-flags: -Zinstrument-function=xray -Zinstrument-xray-opts=always -Cllvm-args=-x86-asm-syntax=intel //@ revisions: x86_64-linux //@[x86_64-linux] compile-flags: --target=x86_64-unknown-linux-gnu diff --git a/tests/codegen-llvm/instrument-fentry.rs b/tests/codegen-llvm/instrument-fentry.rs index 933f8ac60168d..11b9027959a8f 100644 --- a/tests/codegen-llvm/instrument-fentry.rs +++ b/tests/codegen-llvm/instrument-fentry.rs @@ -1,5 +1,5 @@ //@ add-minicore -//@ compile-flags: -Z instrument-fentry -Copt-level=0 +//@ compile-flags: -Z instrument-function=fentry -Copt-level=0 // //@ revisions: x86_64-linux //@[x86_64-linux] compile-flags: --target=x86_64-unknown-linux-gnu diff --git a/tests/codegen-llvm/instrument-mcount-opts.rs b/tests/codegen-llvm/instrument-mcount-opts.rs index 0c23f714d5ac4..cc007983a76e6 100644 --- a/tests/codegen-llvm/instrument-mcount-opts.rs +++ b/tests/codegen-llvm/instrument-mcount-opts.rs @@ -1,11 +1,11 @@ //@ revisions: dflt ncyr ycnr ncnr ycyr //@ add-minicore //@ needs-llvm-components: systemz -//@ compile-flags: -Zinstrument-fentry=y -Copt-level=0 --target=s390x-unknown-linux-gnu -//@[ncyr] compile-flags: -Zinstrument-mcount-opts=no-call,record -//@[ncnr] compile-flags: -Zinstrument-mcount-opts=no-call,no-record -//@[ycnr] compile-flags: -Zinstrument-mcount-opts=call,no-record -//@[ycyr] compile-flags: -Zinstrument-mcount-opts=call,record +//@ compile-flags: -Zinstrument-function=fentry -Copt-level=0 --target=s390x-unknown-linux-gnu +//@[ncyr] compile-flags: -Zinstrument-fentry-opts=no-call,record +//@[ncnr] compile-flags: -Zinstrument-fentry-opts=no-call,no-record +//@[ycnr] compile-flags: -Zinstrument-fentry-opts=call,no-record +//@[ycyr] compile-flags: -Zinstrument-fentry-opts=call,record #![feature(no_core)] #![crate_type = "rlib"] #![no_core] diff --git a/tests/codegen-llvm/instrument-mcount.rs b/tests/codegen-llvm/instrument-mcount.rs index 8c97535d4a82c..695d5ca30486d 100644 --- a/tests/codegen-llvm/instrument-mcount.rs +++ b/tests/codegen-llvm/instrument-mcount.rs @@ -1,5 +1,5 @@ // -//@ compile-flags: -Z instrument-mcount -Copt-level=0 +//@ compile-flags: -Z instrument-function=mcount -Copt-level=0 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/instrument-xray/basic.rs b/tests/codegen-llvm/instrument-xray/basic.rs index 7aaebf41e36ed..68c77b86d01a3 100644 --- a/tests/codegen-llvm/instrument-xray/basic.rs +++ b/tests/codegen-llvm/instrument-xray/basic.rs @@ -1,7 +1,8 @@ -// Checks that `-Z instrument-xray` produces expected instrumentation. +// Checks that `-Z instrument-function=xray -Z instrument-xray-opts` +// produces expected instrumentation. // //@ needs-xray -//@ compile-flags: -Z instrument-xray=always -Copt-level=0 +//@ compile-flags: -Z instrument-function=xray -Z instrument-xray-opts=always -Copt-level=0 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/instrument-xray/options-combine.rs b/tests/codegen-llvm/instrument-xray/options-combine.rs index d1e3b78e6b240..a2125d7ddae1a 100644 --- a/tests/codegen-llvm/instrument-xray/options-combine.rs +++ b/tests/codegen-llvm/instrument-xray/options-combine.rs @@ -1,9 +1,9 @@ -// Checks that `-Z instrument-xray` options can be specified multiple times. +// Checks that `-Z instrument-xray-opts` options can be specified multiple times. // //@ needs-xray -//@ compile-flags: -Z instrument-xray=skip-exit -Copt-level=0 -//@ compile-flags: -Z instrument-xray=instruction-threshold=123 -Copt-level=0 -//@ compile-flags: -Z instrument-xray=instruction-threshold=456 -Copt-level=0 +//@ compile-flags: -Z instrument-function=xray -Z instrument-xray-opts=skip-exit -Copt-level=0 +//@ compile-flags: -Z instrument-function=xray -Z instrument-xray-opts=instruction-threshold=123 -Copt-level=0 +//@ compile-flags: -Z instrument-function=xray -Z instrument-xray-opts=instruction-threshold=456 -Copt-level=0 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/instrument-xray/options-override.rs b/tests/codegen-llvm/instrument-xray/options-override.rs index 428fb723edb15..dbde1fe8b25a0 100644 --- a/tests/codegen-llvm/instrument-xray/options-override.rs +++ b/tests/codegen-llvm/instrument-xray/options-override.rs @@ -1,8 +1,8 @@ -// Checks that the last `-Z instrument-xray` option wins. +// Checks that the last `-Z instrument-xray-opts` option wins. // //@ needs-xray -//@ compile-flags: -Z instrument-xray=always -Copt-level=0 -//@ compile-flags: -Z instrument-xray=never -Copt-level=0 +//@ compile-flags: -Z instrument-function=xray -Z instrument-xray-opts=always -Copt-level=0 +//@ compile-flags: -Z instrument-function=xray -Z instrument-xray-opts=never -Copt-level=0 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/instrument_fn.rs b/tests/codegen-llvm/instrument_fn.rs index fb4f72411a82e..63076ab2cf4a7 100644 --- a/tests/codegen-llvm/instrument_fn.rs +++ b/tests/codegen-llvm/instrument_fn.rs @@ -2,9 +2,10 @@ // //@ revisions:XRAY MCOUNT FENTRY //@ add-minicore -//@ [XRAY] compile-flags: -Zinstrument-xray -Copt-level=0 -//@ [MCOUNT] compile-flags: -Zinstrument-mcount -Copt-level=0 -//@ [FENTRY] compile-flags: -Zinstrument-fentry -Copt-level=0 --target=x86_64-unknown-linux-gnu +//@ compile-flags: -Copt-level=0 +//@ [XRAY] compile-flags: -Zinstrument-function=xray +//@ [MCOUNT] compile-flags: -Zinstrument-function=mcount +//@ [FENTRY] compile-flags: -Zinstrument-function=fentry --target=x86_64-unknown-linux-gnu //@ [FENTRY] needs-llvm-components: x86 #![feature(no_core)] diff --git a/tests/ui/README.md b/tests/ui/README.md index 248f1208d8d9e..b83faffa8a8b4 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -744,9 +744,9 @@ These tests revolve around the inline `const` block that forces the compiler to See [Instrument coverage | The rustc book](https://doc.rust-lang.org/rustc/instrument-coverage.html). -## `tests/ui/instrument-xray/`: `-Z instrument-xray` +## `tests/ui/instrument-xray/`: `-Z instrument-function=xray` -See [Tracking issue for `-Z instrument-xray` #102921](https://github.com/rust-lang/rust/issues/102921). +See [Tracking issue for `-Z instrument-function=xray` #102921](https://github.com/rust-lang/rust/issues/102921). ## `tests/ui/internal/` diff --git a/tests/ui/instrument-xray/flags-always-never-1.rs b/tests/ui/instrument-xray/flags-always-never-1.rs index 97f03fca10278..6bb250ac4dd3f 100644 --- a/tests/ui/instrument-xray/flags-always-never-1.rs +++ b/tests/ui/instrument-xray/flags-always-never-1.rs @@ -1,8 +1,8 @@ -// Checks that `-Z instrument-xray` does not allow `always` and `never` simultaneously. +// Checks that `-Z instrument-xray-opts` does not allow `always` and `never` simultaneously. // //@ needs-xray -//@ compile-flags: -Z instrument-xray=always,never +//@ compile-flags: -Z instrument-function=xray -Z instrument-xray-opts=always,never fn main() {} -//~? ERROR incorrect value `always,never` for unstable option `instrument-xray` +//~? ERROR incorrect value `always,never` for unstable option `instrument-xray-opts` diff --git a/tests/ui/instrument-xray/flags-always-never-1.stderr b/tests/ui/instrument-xray/flags-always-never-1.stderr index e211c6f602546..71ce3468ffad2 100644 --- a/tests/ui/instrument-xray/flags-always-never-1.stderr +++ b/tests/ui/instrument-xray/flags-always-never-1.stderr @@ -1,2 +1,2 @@ -error: incorrect value `always,never` for unstable option `instrument-xray` - either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected +error: incorrect value `always,never` for unstable option `instrument-xray-opts` - a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected diff --git a/tests/ui/instrument-xray/flags-always-never-2.rs b/tests/ui/instrument-xray/flags-always-never-2.rs index dd4deaad326b1..9ad2b575983ce 100644 --- a/tests/ui/instrument-xray/flags-always-never-2.rs +++ b/tests/ui/instrument-xray/flags-always-never-2.rs @@ -1,9 +1,9 @@ -// Checks that `-Z instrument-xray` allows `always` and `never` sequentially. -// (The last specified setting wins, like `-Z instrument-xray=no` as well.) +// Checks that `-Z instrument-xray-opts` allows `always` and `never` sequentially. +// (The last specified setting wins) // //@ needs-xray -//@ compile-flags: -Z instrument-xray=always -//@ compile-flags: -Z instrument-xray=never +//@ compile-flags: -Z instrument-xray-opts=always +//@ compile-flags: -Z instrument-xray-opts=never //@ check-pass fn main() {} diff --git a/tests/ui/instrument-xray/flags-basic.rs b/tests/ui/instrument-xray/flags-basic.rs index ab9b7666430f9..d5eed8c008663 100644 --- a/tests/ui/instrument-xray/flags-basic.rs +++ b/tests/ui/instrument-xray/flags-basic.rs @@ -1,9 +1,9 @@ // Verifies basic `-Z instrument-xray` flags. // //@ needs-xray -//@ compile-flags: -Z instrument-xray -//@ compile-flags: -Z instrument-xray=skip-exit -//@ compile-flags: -Z instrument-xray=ignore-loops,instruction-threshold=300 +//@ compile-flags: -Z instrument-function=xray +//@ compile-flags: -Z instrument-function=xray -Z instrument-xray-opts=skip-exit +//@ compile-flags: -Z instrument-function=xray -Z instrument-xray-opts=ignore-loops,instruction-threshold=300 //@ check-pass fn main() {} diff --git a/tests/ui/instrument-xray/flags-dupe-always.rs b/tests/ui/instrument-xray/flags-dupe-always.rs index e3a5b3b1c3e25..7d79aff815c03 100644 --- a/tests/ui/instrument-xray/flags-dupe-always.rs +++ b/tests/ui/instrument-xray/flags-dupe-always.rs @@ -1,8 +1,8 @@ -// Checks that `-Z instrument-xray` does not allow duplicates. +// Checks that `-Z instrument-xray-opts` does not allow duplicates. // //@ needs-xray -//@ compile-flags: -Z instrument-xray=always,always +//@ compile-flags: -Z instrument-xray-opts=always,always fn main() {} -//~? ERROR incorrect value `always,always` for unstable option `instrument-xray` +//~? ERROR incorrect value `always,always` for unstable option `instrument-xray-opts` diff --git a/tests/ui/instrument-xray/flags-dupe-always.stderr b/tests/ui/instrument-xray/flags-dupe-always.stderr index d1ac113fa4384..e673b451be9b5 100644 --- a/tests/ui/instrument-xray/flags-dupe-always.stderr +++ b/tests/ui/instrument-xray/flags-dupe-always.stderr @@ -1,2 +1,2 @@ -error: incorrect value `always,always` for unstable option `instrument-xray` - either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected +error: incorrect value `always,always` for unstable option `instrument-xray-opts` - a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected diff --git a/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs index 648bd14463263..cf0dc06b2c066 100644 --- a/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs +++ b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs @@ -1,8 +1,8 @@ -// Checks that `-Z instrument-xray` does not allow duplicates. +// Checks that `-Z instrument-xray-opts` does not allow duplicates. // //@ needs-xray -//@ compile-flags: -Z instrument-xray=ignore-loops,ignore-loops +//@ compile-flags: -Z instrument-xray-opts=ignore-loops,ignore-loops fn main() {} -//~? ERROR incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray` +//~? ERROR incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray-opts` diff --git a/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr b/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr index 52f6b33075bc1..854a48ebefb02 100644 --- a/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr +++ b/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr @@ -1,2 +1,2 @@ -error: incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray` - either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected +error: incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray-opts` - a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected diff --git a/tests/ui/instrument-xray/platform-support.rs b/tests/ui/instrument-xray/platform-support.rs index 68b0e4f5c9821..fa15be77a2206 100644 --- a/tests/ui/instrument-xray/platform-support.rs +++ b/tests/ui/instrument-xray/platform-support.rs @@ -3,21 +3,21 @@ //@ revisions: unsupported //@[unsupported] needs-llvm-components: x86 -//@[unsupported] compile-flags: -Z instrument-xray --target=x86_64-pc-windows-msvc +//@[unsupported] compile-flags: -Z instrument-function=xray --target=x86_64-pc-windows-msvc //@ revisions: x86_64-linux //@[x86_64-linux] needs-llvm-components: x86 -//@[x86_64-linux] compile-flags: -Z instrument-xray --target=x86_64-unknown-linux-gnu +//@[x86_64-linux] compile-flags: -Z instrument-function=xray --target=x86_64-unknown-linux-gnu //@[x86_64-linux] check-pass //@ revisions: x86_64-darwin //@[x86_64-darwin] needs-llvm-components: x86 -//@[x86_64-darwin] compile-flags: -Z instrument-xray --target=x86_64-apple-darwin +//@[x86_64-darwin] compile-flags: -Z instrument-function=xray --target=x86_64-apple-darwin //@[x86_64-darwin] check-pass //@ revisions: aarch64-darwin //@[aarch64-darwin] needs-llvm-components: aarch64 -//@[aarch64-darwin] compile-flags: -Z instrument-xray --target=aarch64-apple-darwin +//@[aarch64-darwin] compile-flags: -Z instrument-function=xray --target=aarch64-apple-darwin //@[aarch64-darwin] check-pass //@ ignore-backends: gcc From d869e0de40646c5d3e5fdb6b102ec0f0935b2cb2 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Tue, 10 Feb 2026 14:18:56 -0600 Subject: [PATCH 5/7] Document -Zinstrument-function in unstable book --- .../src/compiler-flags/instrument-function.md | 116 ++++++++++++++++++ .../src/compiler-flags/instrument-xray.md | 39 ------ 2 files changed, 116 insertions(+), 39 deletions(-) create mode 100644 src/doc/unstable-book/src/compiler-flags/instrument-function.md delete mode 100644 src/doc/unstable-book/src/compiler-flags/instrument-xray.md diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-function.md b/src/doc/unstable-book/src/compiler-flags/instrument-function.md new file mode 100644 index 0000000000000..e08b239924a99 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/instrument-function.md @@ -0,0 +1,116 @@ +# `instrument-function` + +Rust exposes several mechanisms to instrument functions which work with +existing platform tooling: + +* xray +* mcount (prof/gprof style profiling) +* fentry (prof/gprof style profiling, though primarily used by the linux kernel). + +These options are mutually exclusive. Only one can be enabled when compiling a +crate. These do not alter ABI, but may require implicit symbols to exist at +link time. + +These are exposed via the `-Z instrument-function={mcount|fentry|xray|none}` +option. + +A builtin attribute `instrument_fn` can be used to enable (as may be needed +for xray), or disable instrumentation of the function. E.g.: + +```rust,no_run +#![feature(instrument_fn)] +#[instrument_fn = "off"] +fn non_instrumented_function() { +} +``` + +# `-Z instrument-function=mcount` + +mcount instrumentation is a mechanism to insert a counting function call into +the prologue of functions. This is traditionally used to perform profiling of +a binary. In more recent years, mcount has been to implement features beyond +profiling, such as logging, function patching, and tracing (see linux's +[ftrace](https://docs.kernel.org/trace/ftrace.html)). This option is similiar +to the `-pg` option provided by clang and gcc. This option also requires +a frame-pointer to be enabled. + +This option only applies to the crate being compiled, and + +This feature is enabled via `-Zinstrument-function=mcount`, and an attribute +is provided to prevent instrumentation of functions. + +On linux, an example might look like: +```rust,no_run +fn main() { + println!("Hello world!"); +} +``` + +And compiling and running for gprof a fedora 44 x86-64 host: + +```shell +$ rustc main.rs foo.rs -Zinstrument-function=mcount -C link-args=/usr/lib64/gcrt1.o -C link-self-contained +$ ./main +$ gprof main gmon.out +``` + +gprof replaces parts of the C runtime implicitly linked into a binary. The above example is not +suitable for most cases as rustc does not yet know how to substitude crt\*.o for gcrt\*.o when +linking a binary. + +# `-Z instrument-function=fentry` + +On some targets, a more specialized form of mcount is available, named `fentry`. Unlike +mcount, a frame-pointer is not required, and this is guaranteed to be called at function +entry (hence the name). + +This support is restricted to fewer targets. Today, only x86 and s390x support this, and +furthermore, only s390x supports advanced usage described below. + +## Advanced usage + +On supported targets, thee behavior of instrumentation can be further configured with the +`-Zinstrument-mcount-opts` flag. It supports the following options: + +* `=record`: record the location of each call (or nop placeholder) into a section named + `__mcount_loc`. This can be used to toggle the counting function at runtime. + +* `=no-call`: insert nop's which can be replaced by a call to a counting function. + +# `-Z instrument-function=xray` + +The tracking issue for the xray feature is: [#102921](https://github.com/rust-lang/rust/issues/102921). + +Enable generation of NOP sleds for XRay function tracing instrumentation. +For more information on XRay, +read [LLVM documentation](https://llvm.org/docs/XRay.html), +and/or the [XRay whitepaper](http://research.google.com/pubs/pub45287.html). + +Set the `-Z instrument-function=xray` compiler flag in order to enable XRay instrumentation. + + - `-Z instrument-function=xray` – use the default settings + - `-Z instrument-function=xray -Z instrument-xray-opts=skip-exit` – configure a custom setting + - `-Z instrument-function=xray -Z instrument-xray-opts=ignore-loops,instruction-threshold=300` – + multiple settings separated by commas + +Supported options: + + - `always` – force instrumentation of all functions + - `never` – do no instrument any functions + - `ignore-loops` – ignore presence of loops, + instrument functions based only on instruction count + - `instruction-threshold=10` – set a different instruction threshold for instrumentation + - `skip-entry` – do no instrument function entry + - `skip-exit` – do no instrument function exit + +The default settings are: + + - instrument both entry & exit from functions + - instrument functions with at least 200 instructions, + or containing a non-trivial loop + +Note that `-Z instrument-function=xray` only enables generation of NOP sleds +which on their own don't do anything useful. +In order to actually trace the functions, +you will need to link a separate runtime library of your choice, +such as Clang's [XRay Runtime Library](https://www.llvm.org/docs/XRay.html#xray-runtime-library). diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md deleted file mode 100644 index 846c364028753..0000000000000 --- a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md +++ /dev/null @@ -1,39 +0,0 @@ -# `instrument-xray` - -The tracking issue for this feature is: [#102921](https://github.com/rust-lang/rust/issues/102921). - ------------------------- - -Enable generation of NOP sleds for XRay function tracing instrumentation. -For more information on XRay, -read [LLVM documentation](https://llvm.org/docs/XRay.html), -and/or the [XRay whitepaper](http://research.google.com/pubs/pub45287.html). - -Set the `-Z instrument-function=xray` compiler flag in order to enable XRay instrumentation. - - - `-Z instrument-function=xray` – use the default settings - - `-Z instrument-function=xray -Z instrument-xray-opts=skip-exit` – configure a custom setting - - `-Z instrument-function=xray -Z instrument-xray-opts=ignore-loops,instruction-threshold=300` – - multiple settings separated by commas - -Supported options: - - - `always` – force instrumentation of all functions - - `never` – do no instrument any functions - - `ignore-loops` – ignore presence of loops, - instrument functions based only on instruction count - - `instruction-threshold=10` – set a different instruction threshold for instrumentation - - `skip-entry` – do no instrument function entry - - `skip-exit` – do no instrument function exit - -The default settings are: - - - instrument both entry & exit from functions - - instrument functions with at least 200 instructions, - or containing a non-trivial loop - -Note that `-Z instrument-function=xray` only enables generation of NOP sleds -which on their own don't do anything useful. -In order to actually trace the functions, -you will need to link a separate runtime library of your choice, -such as Clang's [XRay Runtime Library](https://www.llvm.org/docs/XRay.html#xray-runtime-library). From d89e1157f6af6dd9abede5f239e42975de9b2200 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Wed, 25 Feb 2026 09:30:45 -0600 Subject: [PATCH 6/7] TODO: make tracking issue This is maybe gated by some sort of agreement on RFC 3917? --- compiler/rustc_feature/src/unstable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 86a472cd98135..eaafa0e99d837 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -545,7 +545,7 @@ declare_features! ( /// Allows associated types in inherent impls. (incomplete, inherent_associated_types, "1.52.0", Some(8995)), /// Enable #[instrument_fn] on function (todo: tracking issue) - (unstable, instrument_fn, "CURRENT_RUSTC_VERSION", None), + (unstable, instrument_fn, "CURRENT_RUSTC_VERSION", Some(99999)), /// Allows using `pointer` and `reference` in intra-doc links (unstable, intra_doc_pointers, "1.51.0", Some(80896)), /// lahfsahf target feature on x86. From c890f21e943fa28a5ce5c4a56dff8d326d511fa5 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Thu, 12 Mar 2026 17:44:53 -0500 Subject: [PATCH 7/7] Limit instrument_fn codegen-llvm test to x86_64 --- tests/codegen-llvm/instrument_fn.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/codegen-llvm/instrument_fn.rs b/tests/codegen-llvm/instrument_fn.rs index 63076ab2cf4a7..6979dc7d54f7b 100644 --- a/tests/codegen-llvm/instrument_fn.rs +++ b/tests/codegen-llvm/instrument_fn.rs @@ -1,12 +1,14 @@ // Verify the #[instrument_fn] applies the correct LLVM IR function attributes. // +// Limit to x86_64 as it supports all instrumentation options. +// //@ revisions:XRAY MCOUNT FENTRY //@ add-minicore -//@ compile-flags: -Copt-level=0 +//@ compile-flags: -Copt-level=0 --target=x86_64-unknown-linux-gnu +//@ needs-llvm-components: x86 //@ [XRAY] compile-flags: -Zinstrument-function=xray //@ [MCOUNT] compile-flags: -Zinstrument-function=mcount -//@ [FENTRY] compile-flags: -Zinstrument-function=fentry --target=x86_64-unknown-linux-gnu -//@ [FENTRY] needs-llvm-components: x86 +//@ [FENTRY] compile-flags: -Zinstrument-function=fentry #![feature(no_core)] #![crate_type = "lib"]