Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -Z instrument-xray flag #102963

Merged
merged 8 commits into from
Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -127,14 +128,41 @@ 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
));
}
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
}

fn nojumptables_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_error_messages/locales/en-US/session.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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 });
Expand Down
26 changes: 23 additions & 3 deletions compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize>,
/// `-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),
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2876,6 +2895,7 @@ pub(crate) mod dep_tracking {
CodeModel,
TlsModel,
InstrumentCoverage,
InstrumentXRay,
CrateType,
MergeFunctions,
PanicStrategy,
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_session/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
73 changes: 73 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -869,6 +870,68 @@ mod parse {
true
}

pub(crate) fn parse_instrument_xray(
slot: &mut Option<InstrumentXRay>,
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<NonZeroUsize>, v: Option<&str>) -> bool {
match v {
Some(s) => {
Expand Down Expand Up @@ -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<InstrumentXRay> = (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<u64> = (None, parse_opt_number, [TRACKED],
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/aarch64_linux_android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub fn target() -> Target {
| SanitizerSet::MEMTAG
| SanitizerSet::SHADOWCALLSTACK
| SanitizerSet::ADDRESS,
supports_xray: true,
..super::android_base::opts()
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub fn target() -> Target {
| SanitizerSet::MEMTAG
| SanitizerSet::THREAD
| SanitizerSet::HWADDRESS,
supports_xray: true,
..super::linux_gnu_base::opts()
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1937,6 +1940,7 @@ impl Default for TargetOptions {
supports_stack_protector: true,
entry_name: "main".into(),
entry_abi: Conv::C,
supports_xray: false,
}
}
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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());
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/x86_64_linux_android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
39 changes: 39 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/instrument-xray.md
Original file line number Diff line number Diff line change
@@ -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).
Loading