From 0e60df9ed1439cb9d7bcc1a09bf2fc87d03393b1 Mon Sep 17 00:00:00 2001 From: Oleksii Lozovskyi Date: Sat, 24 Sep 2022 20:02:44 +0900 Subject: [PATCH 1/8] Parse "-Z instrument-xray" codegen option Recognize all bells and whistles that LLVM's XRay pass is capable of. The always/never settings are a bit dumb without attributes but they're still there. The default instruction count is chosen by the compiler, not LLVM pass. We'll do it later. --- compiler/rustc_interface/src/tests.rs | 2 + compiler/rustc_session/src/config.rs | 26 ++++++- compiler/rustc_session/src/options.rs | 73 +++++++++++++++++++ .../src/compiler-flags/instrument-xray.md | 39 ++++++++++ tests/rustdoc-ui/z-help.stdout | 9 +++ 5 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 src/doc/unstable-book/src/compiler-flags/instrument-xray.md diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 52a4e0e74181f..5daefadabba02 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -5,6 +5,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; use rustc_session::config::rustc_optgroups; use rustc_session::config::Input; +use rustc_session::config::InstrumentXRay; use rustc_session::config::TraitSolver; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; use rustc_session::config::{ @@ -755,6 +756,7 @@ fn test_unstable_options_tracking_hash() { tracked!(inline_mir_threshold, Some(123)); tracked!(instrument_coverage, Some(InstrumentCoverage::All)); tracked!(instrument_mcount, true); + tracked!(instrument_xray, Some(InstrumentXRay::default())); tracked!(link_only, true); tracked!(llvm_plugins, vec![String::from("plugin_name")]); tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 973d860118ef1..7d2fdf94baa36 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -174,6 +174,25 @@ pub enum InstrumentCoverage { Off, } +/// Settings for `-Z instrument-xray` flag. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +pub struct InstrumentXRay { + /// `-Z instrument-xray=always`, force instrumentation + pub always: bool, + /// `-Z instrument-xray=never`, disable instrumentation + pub never: bool, + /// `-Z instrument-xray=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 + /// for instrumentation, or `None` to use compiler's default + pub instruction_threshold: Option, + /// `-Z instrument-xray=skip-entry`, do not instrument function entry + pub skip_entry: bool, + /// `-Z instrument-xray=skip-exit`, do not instrument function exit + pub skip_exit: bool, +} + #[derive(Clone, PartialEq, Hash, Debug)] pub enum LinkerPluginLto { LinkerPlugin(PathBuf), @@ -2805,9 +2824,9 @@ impl PpMode { pub(crate) mod dep_tracking { use super::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, - InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OomStrategy, OptLevel, - OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SplitDwarfKind, - SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths, + InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, + OomStrategy, OptLevel, OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, + SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths, }; use crate::lint; use crate::options::WasiExecModel; @@ -2876,6 +2895,7 @@ pub(crate) mod dep_tracking { CodeModel, TlsModel, InstrumentCoverage, + InstrumentXRay, CrateType, MergeFunctions, PanicStrategy, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 0db4d85ff4b67..a0a8a42575ef3 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -380,6 +380,7 @@ mod desc { pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub const parse_instrument_coverage: &str = "`all` (default), `except-unused-generics`, `except-unused-functions`, or `off`"; + pub 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 const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0"; pub const parse_trait_solver: &str = @@ -869,6 +870,68 @@ mod parse { true } + pub(crate) fn parse_instrument_xray( + slot: &mut Option, + 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 mut options = slot.get_or_insert_default(); + let mut seen_always = false; + let mut seen_never = false; + let mut seen_ignore_loops = false; + let mut seen_instruction_threshold = false; + let mut seen_skip_entry = false; + let mut seen_skip_exit = false; + for option in v.into_iter().map(|v| v.split(',')).flatten() { + match option { + "always" if !seen_always && !seen_never => { + options.always = true; + options.never = false; + seen_always = true; + } + "never" if !seen_never && !seen_always => { + options.never = true; + options.always = false; + seen_never = true; + } + "ignore-loops" if !seen_ignore_loops => { + options.ignore_loops = true; + seen_ignore_loops = true; + } + option + if option.starts_with("instruction-threshold") + && !seen_instruction_threshold => + { + let Some(("instruction-threshold", n)) = option.split_once('=') else { + return false; + }; + match n.parse() { + Ok(n) => options.instruction_threshold = Some(n), + Err(_) => return false, + } + seen_instruction_threshold = true; + } + "skip-entry" if !seen_skip_entry => { + options.skip_entry = true; + seen_skip_entry = true; + } + "skip-exit" if !seen_skip_exit => { + options.skip_exit = true; + seen_skip_exit = true; + } + _ => return false, + } + } + true + } + pub(crate) fn parse_treat_err_as_bug(slot: &mut Option, v: Option<&str>) -> bool { match v { Some(s) => { @@ -1397,6 +1460,16 @@ options! { `=off` (default)"), 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], + "insert function instrument code for XRay-based tracing (default: no) + Optional extra settings: + `=always` + `=never` + `=ignore-loops` + `=instruction-threshold=N` + `=skip-entry` + `=skip-exit` + Multiple options can be combined with commas."), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], "keep hygiene data after analysis (default: no)"), layout_seed: Option = (None, parse_opt_number, [TRACKED], diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md new file mode 100644 index 0000000000000..7fb33cd68b4a3 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md @@ -0,0 +1,39 @@ +# `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-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` – + 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-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/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout index 4f07fca82d1eb..2169b89c9291e 100644 --- a/tests/rustdoc-ui/z-help.stdout +++ b/tests/rustdoc-ui/z-help.stdout @@ -70,6 +70,15 @@ `=except-unused-functions` `=off` (default) -Z instrument-mcount=val -- insert function instrument code for mcount-based tracing (default: no) + -Z instrument-xray=val -- insert function instrument code for XRay-based tracing (default: no) + Optional extra settings: + `=always` + `=never` + `=ignore-loops` + `=instruction-threshold=N` + `=skip-entry` + `=skip-exit` + Multiple options can be combined with commas. -Z keep-hygiene-data=val -- keep hygiene data after analysis (default: no) -Z layout-seed=val -- seed layout randomization -Z link-native-libraries=val -- link native libraries in the linker invocation (default: yes) From d748f085473a12fc6fdde3874a8f0f276c1657c6 Mon Sep 17 00:00:00 2001 From: Oleksii Lozovskyi Date: Sat, 24 Sep 2022 20:02:44 +0900 Subject: [PATCH 2/8] UI tests for -Z instrument-xray I'm tired of testing it manually, just codify my expectations in tests. They're pretty low-maintenance. --- src/tools/tidy/src/ui_tests.rs | 2 +- tests/ui/instrument-xray/flags-always-never-1.rs | 6 ++++++ tests/ui/instrument-xray/flags-always-never-1.stderr | 2 ++ tests/ui/instrument-xray/flags-always-never-2.rs | 8 ++++++++ tests/ui/instrument-xray/flags-basic.rs | 8 ++++++++ tests/ui/instrument-xray/flags-dupe-always.rs | 6 ++++++ tests/ui/instrument-xray/flags-dupe-always.stderr | 2 ++ tests/ui/instrument-xray/flags-dupe-ignore-loops.rs | 6 ++++++ tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr | 2 ++ 9 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tests/ui/instrument-xray/flags-always-never-1.rs create mode 100644 tests/ui/instrument-xray/flags-always-never-1.stderr create mode 100644 tests/ui/instrument-xray/flags-always-never-2.rs create mode 100644 tests/ui/instrument-xray/flags-basic.rs create mode 100644 tests/ui/instrument-xray/flags-dupe-always.rs create mode 100644 tests/ui/instrument-xray/flags-dupe-always.stderr create mode 100644 tests/ui/instrument-xray/flags-dupe-ignore-loops.rs create mode 100644 tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 83551a1d820ab..ef3abb9514f24 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -9,7 +9,7 @@ use std::path::Path; const ENTRY_LIMIT: usize = 1000; // FIXME: The following limits should be reduced eventually. -const ROOT_ENTRY_LIMIT: usize = 939; +const ROOT_ENTRY_LIMIT: usize = 940; const ISSUES_ENTRY_LIMIT: usize = 2001; fn check_entries(path: &Path, bad: &mut bool) { diff --git a/tests/ui/instrument-xray/flags-always-never-1.rs b/tests/ui/instrument-xray/flags-always-never-1.rs new file mode 100644 index 0000000000000..03274dedd0830 --- /dev/null +++ b/tests/ui/instrument-xray/flags-always-never-1.rs @@ -0,0 +1,6 @@ +// Checks that `-Z instrument-xray` does not allow `always` and `never` simultaneously. +// +// compile-flags: -Z instrument-xray=always,never +// error-pattern: incorrect value `always,never` for unstable option `instrument-xray` + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-always-never-1.stderr b/tests/ui/instrument-xray/flags-always-never-1.stderr new file mode 100644 index 0000000000000..e211c6f602546 --- /dev/null +++ b/tests/ui/instrument-xray/flags-always-never-1.stderr @@ -0,0 +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 + diff --git a/tests/ui/instrument-xray/flags-always-never-2.rs b/tests/ui/instrument-xray/flags-always-never-2.rs new file mode 100644 index 0000000000000..e752890b47ad0 --- /dev/null +++ b/tests/ui/instrument-xray/flags-always-never-2.rs @@ -0,0 +1,8 @@ +// Checks that `-Z instrument-xray` allows `always` and `never` sequentially. +// (The last specified setting wins, like `-Z instrument-xray=no` as well.) +// +// compile-flags: -Z instrument-xray=always +// compile-flags: -Z instrument-xray=never +// check-pass + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-basic.rs b/tests/ui/instrument-xray/flags-basic.rs new file mode 100644 index 0000000000000..5889a20f670c8 --- /dev/null +++ b/tests/ui/instrument-xray/flags-basic.rs @@ -0,0 +1,8 @@ +// Verifies basic `-Z instrument-xray` flags. +// +// compile-flags: -Z instrument-xray +// compile-flags: -Z instrument-xray=skip-exit +// compile-flags: -Z instrument-xray=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 new file mode 100644 index 0000000000000..36dda4bbd03f4 --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-always.rs @@ -0,0 +1,6 @@ +// Checks that `-Z instrument-xray` does not allow duplicates. +// +// compile-flags: -Z instrument-xray=always,always +// error-pattern: incorrect value `always,always` for unstable option `instrument-xray` + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-dupe-always.stderr b/tests/ui/instrument-xray/flags-dupe-always.stderr new file mode 100644 index 0000000000000..d1ac113fa4384 --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-always.stderr @@ -0,0 +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 + diff --git a/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs new file mode 100644 index 0000000000000..227f8557f42ad --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs @@ -0,0 +1,6 @@ +// Checks that `-Z instrument-xray` does not allow duplicates. +// +// compile-flags: -Z instrument-xray=ignore-loops,ignore-loops +// error-pattern: incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray` + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr b/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr new file mode 100644 index 0000000000000..52f6b33075bc1 --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr @@ -0,0 +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 + From b3cadd2dcfe37836cdbfd6c5871bd8398748ac3f Mon Sep 17 00:00:00 2001 From: Oleksii Lozovskyi Date: Sat, 24 Sep 2022 21:54:47 +0900 Subject: [PATCH 3/8] Allow multiple instrumentation attributes Four because that's the new reasonable maximum for XRay instrumentation attributes in the following commit. --- compiler/rustc_codegen_llvm/src/attributes.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 54ac7a46cf2f3..0494bd7a19be9 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -118,7 +118,8 @@ pub fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attr /// Tell LLVM what instrument function to insert. #[inline] -fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { +fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 4]> { + let mut attrs = SmallVec::new(); if cx.sess().opts.unstable_opts.instrument_mcount { // Similar to `clang -pg` behavior. Handled by the // `post-inline-ee-instrument` LLVM pass. @@ -127,14 +128,13 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribu // See test/CodeGen/mcount.c in clang. let mcount_name = cx.sess().target.mcount.as_ref(); - Some(llvm::CreateAttrStringValue( + attrs.push(llvm::CreateAttrStringValue( cx.llcx, "instrument-function-entry-inlined", &mcount_name, - )) - } else { - None + )); } + attrs } fn nojumptables_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { From bac15db1d0f3ccdb9b9f61ba808cd25fbf400e88 Mon Sep 17 00:00:00 2001 From: Oleksii Lozovskyi Date: Sat, 24 Sep 2022 22:05:25 +0900 Subject: [PATCH 4/8] Emit basic XRay instrumentation attributes Add the attributes to functions according to the settings. "xray-always" overrides "xray-never", and they both override "xray-ignore-loops" and "xray-instruction-threshold", but we'll let lints deal with warnings about silly attribute combinations. --- compiler/rustc_codegen_llvm/src/attributes.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 0494bd7a19be9..7a4ec494c8e7f 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -134,6 +134,34 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attr &mcount_name, )); } + if let Some(options) = &cx.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")); + } + if options.never { + attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-never")); + } + 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")); + } + } attrs } From 0fef658ffeb0fdc858d6c111a992b570ed73c951 Mon Sep 17 00:00:00 2001 From: Oleksii Lozovskyi Date: Sun, 2 Oct 2022 10:25:49 +0900 Subject: [PATCH 5/8] Codegen tests for -Z instrument-xray Let's add at least some tests to verify that this option is accepted and produces expected LLVM attributes. More tests can be added later with attribute support. --- tests/codegen/instrument-xray/basic.rs | 8 ++++++++ tests/codegen/instrument-xray/options-combine.rs | 11 +++++++++++ tests/codegen/instrument-xray/options-override.rs | 10 ++++++++++ 3 files changed, 29 insertions(+) create mode 100644 tests/codegen/instrument-xray/basic.rs create mode 100644 tests/codegen/instrument-xray/options-combine.rs create mode 100644 tests/codegen/instrument-xray/options-override.rs diff --git a/tests/codegen/instrument-xray/basic.rs b/tests/codegen/instrument-xray/basic.rs new file mode 100644 index 0000000000000..9c128767747e7 --- /dev/null +++ b/tests/codegen/instrument-xray/basic.rs @@ -0,0 +1,8 @@ +// Checks that `-Z instrument-xray` produces expected instrumentation. +// +// compile-flags: -Z instrument-xray=always + +#![crate_type = "lib"] + +// CHECK: attributes #{{.*}} "function-instrument"="xray-always" +pub fn function() {} diff --git a/tests/codegen/instrument-xray/options-combine.rs b/tests/codegen/instrument-xray/options-combine.rs new file mode 100644 index 0000000000000..0c1992318f576 --- /dev/null +++ b/tests/codegen/instrument-xray/options-combine.rs @@ -0,0 +1,11 @@ +// Checks that `-Z instrument-xray` options can be specified multiple times. +// +// compile-flags: -Z instrument-xray=skip-exit +// compile-flags: -Z instrument-xray=instruction-threshold=123 +// compile-flags: -Z instrument-xray=instruction-threshold=456 + +#![crate_type = "lib"] + +// CHECK: attributes #{{.*}} "xray-instruction-threshold"="456" "xray-skip-exit" +// CHECK-NOT: attributes #{{.*}} "xray-instruction-threshold"="123" +pub fn function() {} diff --git a/tests/codegen/instrument-xray/options-override.rs b/tests/codegen/instrument-xray/options-override.rs new file mode 100644 index 0000000000000..3a7c37f9006be --- /dev/null +++ b/tests/codegen/instrument-xray/options-override.rs @@ -0,0 +1,10 @@ +// Checks that the last `-Z instrument-xray` option wins. +// +// compile-flags: -Z instrument-xray=always +// compile-flags: -Z instrument-xray=never + +#![crate_type = "lib"] + +// CHECK: attributes #{{.*}} "function-instrument"="xray-never" +// CHECK-NOT: attributes #{{.*}} "function-instrument"="xray-always" +pub fn function() {} From 8e49c847400b0ec47d501a72e3c0ad95f81b5fe9 Mon Sep 17 00:00:00 2001 From: Oleksii Lozovskyi Date: Sun, 2 Oct 2022 10:45:54 +0900 Subject: [PATCH 6/8] XRay support flag in TargetOptions Specify where XRay is supported. I only test ARM64 and x86_64, but hey those others should work too, right? LLVM documentation says that MIPS and PPC are also supported, but I don't have the hardware, so I won't pretend. Naturally, more targets can be added later with more testing. --- compiler/rustc_target/src/spec/aarch64_linux_android.rs | 1 + compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs | 1 + .../rustc_target/src/spec/aarch64_unknown_linux_musl.rs | 1 + compiler/rustc_target/src/spec/mod.rs | 6 ++++++ compiler/rustc_target/src/spec/x86_64_linux_android.rs | 1 + compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs | 1 + compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs | 1 + compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs | 1 + compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs | 1 + compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs | 1 + 10 files changed, 15 insertions(+) diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs index c85f7f62a4239..daa946ccd519c 100644 --- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { | SanitizerSet::MEMTAG | SanitizerSet::SHADOWCALLSTACK | SanitizerSet::ADDRESS, + supports_xray: true, ..super::android_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs index 3006044d54a6e..36d54f1d7cc5c 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs @@ -17,6 +17,7 @@ pub fn target() -> Target { | SanitizerSet::MEMTAG | SanitizerSet::THREAD | SanitizerSet::HWADDRESS, + supports_xray: true, ..super::linux_gnu_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs index 002d0dac2a668..9c299fed6be16 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs @@ -3,6 +3,7 @@ use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.max_atomic_width = Some(128); + base.supports_xray = true; Target { llvm_target: "aarch64-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index a094c2c545269..bc1920e34249a 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1718,6 +1718,9 @@ pub struct TargetOptions { /// The ABI of entry function. /// Default value is `Conv::C`, i.e. C call convention pub entry_abi: Conv, + + /// Whether the target supports XRay instrumentation. + pub supports_xray: bool, } /// Add arguments for the given flavor and also for its "twin" flavors @@ -1937,6 +1940,7 @@ impl Default for TargetOptions { supports_stack_protector: true, entry_name: "main".into(), entry_abi: Conv::C, + supports_xray: false, } } } @@ -2592,6 +2596,7 @@ impl Target { key!(supports_stack_protector, bool); key!(entry_name); key!(entry_abi, Conv)?; + key!(supports_xray, bool); if base.is_builtin { // This can cause unfortunate ICEs later down the line. @@ -2845,6 +2850,7 @@ impl ToJson for Target { target_option_val!(supports_stack_protector); target_option_val!(entry_name); target_option_val!(entry_abi); + target_option_val!(supports_xray); if let Some(abi) = self.default_adjusted_cabi { d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json()); diff --git a/compiler/rustc_target/src/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs index 9c9137848550f..a3bdb5f5465b0 100644 --- a/compiler/rustc_target/src/spec/x86_64_linux_android.rs +++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs @@ -8,6 +8,7 @@ pub fn target() -> Target { base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.stack_probes = StackProbeType::X86; + base.supports_xray = true; Target { llvm_target: "x86_64-linux-android".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs index 98988ab359542..b41e5842aad13 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs @@ -8,6 +8,7 @@ pub fn target() -> Target { base.stack_probes = StackProbeType::X86; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-freebsd".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs index a91ab365b668a..9af1049b87026 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs @@ -12,6 +12,7 @@ pub fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs index 9087dc3df6007..bf4cf7d7becad 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs @@ -12,6 +12,7 @@ pub fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs index 64ae425d8c0b7..74c434935ba88 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs @@ -11,6 +11,7 @@ pub fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-netbsd".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs index 66b8e20226f19..8e4d42a0acaf3 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs @@ -6,6 +6,7 @@ pub fn target() -> Target { base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.stack_probes = StackProbeType::X86; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-openbsd".into(), From 3561dc948c1a2a24c2b49992099954a96be3c8ee Mon Sep 17 00:00:00 2001 From: Oleksii Lozovskyi Date: Sun, 2 Oct 2022 10:55:35 +0900 Subject: [PATCH 7/8] Emit an error if -Z instrument-xray is not supported This is somewhat important because LLVM enables the pass based on target architecture, but support by the target OS also matters. For example, XRay attributes are processed by codegen for macOS targets, but Apple linker fails to process relocations in XRay data sections, so the feature as a whole is not supported there for the time being. --- compiler/rustc_error_messages/locales/en-US/session.ftl | 2 ++ compiler/rustc_session/src/errors.rs | 6 ++++++ compiler/rustc_session/src/session.rs | 4 ++++ tests/ui/instrument-xray/target-not-supported.rs | 9 +++++++++ tests/ui/instrument-xray/target-not-supported.stderr | 4 ++++ 5 files changed, 25 insertions(+) create mode 100644 tests/ui/instrument-xray/target-not-supported.rs create mode 100644 tests/ui/instrument-xray/target-not-supported.stderr diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl index 5984c201af0d0..fe553edab4276 100644 --- a/compiler/rustc_error_messages/locales/en-US/session.ftl +++ b/compiler/rustc_error_messages/locales/en-US/session.ftl @@ -25,6 +25,8 @@ session_profile_sample_use_file_does_not_exist = file `{$path}` passed to `-C pr session_target_requires_unwind_tables = target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no` +session_instrumentation_not_supported = {$us} instrumentation is not supported for this target + session_sanitizer_not_supported = {$us} sanitizer is not supported for this target session_sanitizers_not_supported = {$us} sanitizers are not supported for this target diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 8e8fba5e236f5..c851145440b86 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -71,6 +71,12 @@ pub struct ProfileSampleUseFileDoesNotExist<'a> { #[diag(session_target_requires_unwind_tables)] pub struct TargetRequiresUnwindTables; +#[derive(Diagnostic)] +#[diag(session_instrumentation_not_supported)] +pub struct InstrumentationNotSupported { + pub us: String, +} + #[derive(Diagnostic)] #[diag(session_sanitizer_not_supported)] pub struct SanitizerNotSupported { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 8a0176f639174..fe6ac80fde6eb 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1589,6 +1589,10 @@ fn validate_commandline_args_with_session_available(sess: &Session) { { sess.emit_err(errors::SplitDebugInfoUnstablePlatform { debuginfo: sess.split_debuginfo() }); } + + if sess.opts.unstable_opts.instrument_xray.is_some() && !sess.target.options.supports_xray { + sess.emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() }); + } } /// Holds data on the current incremental compilation session, if there is one. diff --git a/tests/ui/instrument-xray/target-not-supported.rs b/tests/ui/instrument-xray/target-not-supported.rs new file mode 100644 index 0000000000000..e6bdd23e8fc3a --- /dev/null +++ b/tests/ui/instrument-xray/target-not-supported.rs @@ -0,0 +1,9 @@ +// Verifies that `-Z instrument-xray` cannot be used with unsupported targets, +// +// needs-llvm-components: x86 +// compile-flags: -Z instrument-xray --target x86_64-apple-darwin +// error-pattern: error: XRay instrumentation is not supported for this target + +#![feature(no_core)] +#![no_core] +#![no_main] diff --git a/tests/ui/instrument-xray/target-not-supported.stderr b/tests/ui/instrument-xray/target-not-supported.stderr new file mode 100644 index 0000000000000..6e3b0c8a380b8 --- /dev/null +++ b/tests/ui/instrument-xray/target-not-supported.stderr @@ -0,0 +1,4 @@ +error: XRay instrumentation is not supported for this target + +error: aborting due to previous error + From 54b26f49e6d30aefcbf206ee5cfcf6122503553c Mon Sep 17 00:00:00 2001 From: Oleksii Lozovskyi Date: Sun, 2 Oct 2022 12:58:05 +0900 Subject: [PATCH 8/8] Test XRay only for supported targets Now that the compiler accepts "-Z instrument-xray" option only when targeting one of the supported targets, make sure to not run the codegen tests where the compiler will fail. Like with other compiletests, we don't have access to internals, so simply hardcode a list of supported architectures here. --- src/tools/compiletest/src/header.rs | 2 ++ src/tools/compiletest/src/util.rs | 13 +++++++++++++ tests/codegen/instrument-xray/basic.rs | 1 + tests/codegen/instrument-xray/options-combine.rs | 1 + tests/codegen/instrument-xray/options-override.rs | 1 + tests/ui/instrument-xray/flags-always-never-1.rs | 1 + tests/ui/instrument-xray/flags-always-never-2.rs | 1 + tests/ui/instrument-xray/flags-basic.rs | 1 + tests/ui/instrument-xray/flags-dupe-always.rs | 1 + tests/ui/instrument-xray/flags-dupe-ignore-loops.rs | 1 + 10 files changed, 23 insertions(+) diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 45fd87bea9bb5..e11ebca6ea9af 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -941,6 +941,7 @@ pub fn make_test_description( let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_memtag = util::MEMTAG_SUPPORTED_TARGETS.contains(&&*config.target); let has_shadow_call_stack = util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(&&*config.target); + let has_xray = util::XRAY_SUPPORTED_TARGETS.contains(&&*config.target); // For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find // whether `rust-lld` is present in the compiler under test. @@ -1019,6 +1020,7 @@ pub fn make_test_description( && config.parse_name_directive(ln, "needs-sanitizer-shadow-call-stack") ); reason!(!config.can_unwind() && config.parse_name_directive(ln, "needs-unwind")); + reason!(!has_xray && config.parse_name_directive(ln, "needs-xray")); reason!( config.target == "wasm32-unknown-unknown" && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS) diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index ff7e8df987816..67f49bb6397c2 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -78,6 +78,19 @@ pub const MEMTAG_SUPPORTED_TARGETS: &[&str] = pub const SHADOWCALLSTACK_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-android"]; +pub const XRAY_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-linux-android", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", + "x86_64-linux-android", + "x86_64-unknown-freebsd", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", + "x86_64-unknown-netbsd", + "x86_64-unknown-none-linuxkernel", + "x86_64-unknown-openbsd", +]; + pub fn make_new_path(path: &str) -> String { assert!(cfg!(windows)); // Windows just uses PATH as the library search path, so we have to diff --git a/tests/codegen/instrument-xray/basic.rs b/tests/codegen/instrument-xray/basic.rs index 9c128767747e7..d3e49d5317442 100644 --- a/tests/codegen/instrument-xray/basic.rs +++ b/tests/codegen/instrument-xray/basic.rs @@ -1,5 +1,6 @@ // Checks that `-Z instrument-xray` produces expected instrumentation. // +// needs-xray // compile-flags: -Z instrument-xray=always #![crate_type = "lib"] diff --git a/tests/codegen/instrument-xray/options-combine.rs b/tests/codegen/instrument-xray/options-combine.rs index 0c1992318f576..f7e500b65f62d 100644 --- a/tests/codegen/instrument-xray/options-combine.rs +++ b/tests/codegen/instrument-xray/options-combine.rs @@ -1,5 +1,6 @@ // Checks that `-Z instrument-xray` options can be specified multiple times. // +// needs-xray // compile-flags: -Z instrument-xray=skip-exit // compile-flags: -Z instrument-xray=instruction-threshold=123 // compile-flags: -Z instrument-xray=instruction-threshold=456 diff --git a/tests/codegen/instrument-xray/options-override.rs b/tests/codegen/instrument-xray/options-override.rs index 3a7c37f9006be..00f81837902dd 100644 --- a/tests/codegen/instrument-xray/options-override.rs +++ b/tests/codegen/instrument-xray/options-override.rs @@ -1,5 +1,6 @@ // Checks that the last `-Z instrument-xray` option wins. // +// needs-xray // compile-flags: -Z instrument-xray=always // compile-flags: -Z instrument-xray=never diff --git a/tests/ui/instrument-xray/flags-always-never-1.rs b/tests/ui/instrument-xray/flags-always-never-1.rs index 03274dedd0830..4dd43439eb7c2 100644 --- a/tests/ui/instrument-xray/flags-always-never-1.rs +++ b/tests/ui/instrument-xray/flags-always-never-1.rs @@ -1,5 +1,6 @@ // Checks that `-Z instrument-xray` does not allow `always` and `never` simultaneously. // +// needs-xray // compile-flags: -Z instrument-xray=always,never // error-pattern: incorrect value `always,never` for unstable option `instrument-xray` diff --git a/tests/ui/instrument-xray/flags-always-never-2.rs b/tests/ui/instrument-xray/flags-always-never-2.rs index e752890b47ad0..7310aa0a0d288 100644 --- a/tests/ui/instrument-xray/flags-always-never-2.rs +++ b/tests/ui/instrument-xray/flags-always-never-2.rs @@ -1,6 +1,7 @@ // Checks that `-Z instrument-xray` allows `always` and `never` sequentially. // (The last specified setting wins, like `-Z instrument-xray=no` as well.) // +// needs-xray // compile-flags: -Z instrument-xray=always // compile-flags: -Z instrument-xray=never // check-pass diff --git a/tests/ui/instrument-xray/flags-basic.rs b/tests/ui/instrument-xray/flags-basic.rs index 5889a20f670c8..b97f0dd8a072c 100644 --- a/tests/ui/instrument-xray/flags-basic.rs +++ b/tests/ui/instrument-xray/flags-basic.rs @@ -1,5 +1,6 @@ // 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 diff --git a/tests/ui/instrument-xray/flags-dupe-always.rs b/tests/ui/instrument-xray/flags-dupe-always.rs index 36dda4bbd03f4..407f3e2aa5da8 100644 --- a/tests/ui/instrument-xray/flags-dupe-always.rs +++ b/tests/ui/instrument-xray/flags-dupe-always.rs @@ -1,5 +1,6 @@ // Checks that `-Z instrument-xray` does not allow duplicates. // +// needs-xray // compile-flags: -Z instrument-xray=always,always // error-pattern: incorrect value `always,always` for unstable option `instrument-xray` diff --git a/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs index 227f8557f42ad..75b210a6547ec 100644 --- a/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs +++ b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs @@ -1,5 +1,6 @@ // Checks that `-Z instrument-xray` does not allow duplicates. // +// needs-xray // compile-flags: -Z instrument-xray=ignore-loops,ignore-loops // error-pattern: incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray`