Skip to content

trace: add barebones ptrace setup #4401

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

Merged
merged 1 commit into from
Jun 18, 2025
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
178 changes: 171 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ libc = "0.2"
libffi = "4.0.0"
libloading = "0.8"

[target.'cfg(target_os = "linux")'.dependencies]
nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"] }
ipc-channel = "0.19.0"
serde = { version = "1.0.219", features = ["derive"] }

[dev-dependencies]
ui_test = "0.29.1"
colored = "2"
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,11 @@ to Miri failing to detect cases of undefined behavior in a program.
Finally, the flag is **unsound** in the sense that Miri stops tracking details such as
initialization and provenance on memory shared with native code, so it is easily possible to write
code that has UB which is missed by Miri.
* `-Zmiri-force-old-native-lib-mode` disables the WIP improved native code access tracking. If for
whatever reason enabling native calls leads to odd behaviours or causes Miri to panic, disabling
the tracer *might* fix this. This will likely be removed once the tracer has been adequately
battle-tested. Note that this flag is only meaningful on Linux systems; other Unixes (currently)
exclusively use the old native-lib code.
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
This can be used to find which parts of your program are executing slowly under Miri.
The profile is written out to a file inside a directory called `<name>`, and can be processed
Expand Down
12 changes: 12 additions & 0 deletions src/alloc/isolated_alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,18 @@ impl IsolatedAlloc {
alloc::dealloc(ptr, layout);
}
}

/// Returns a vector of page addresses managed by the allocator.
pub fn pages(&self) -> Vec<usize> {
let mut pages: Vec<_> =
self.page_ptrs.clone().into_iter().map(|p| p.expose_provenance()).collect();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allocates the buffer for these pages twice... that seems rather unnecessary.

for (ptr, size) in &self.huge_ptrs {
for i in 0..size / self.page_size {
pages.push(ptr.expose_provenance().strict_add(i * self.page_size));
}
}
pages
}
}

#[cfg(test)]
Expand Down
15 changes: 14 additions & 1 deletion src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,11 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
} else {
let return_code = miri::eval_entry(tcx, entry_def_id, entry_type, &config, None)
.unwrap_or_else(|| {
#[cfg(target_os = "linux")]
miri::register_retcode_sv(rustc_driver::EXIT_FAILURE);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this done here but not all the other places where we exit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to have something to do with the way abort_if_errors() exits not properly getting us a return code. I'm unsure of what the root cause is - I spent a lot of time trying to debug that but it seems to be the only thing that causes this behaviour, so I special-cased it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abort_if_errors doesn't exit, it unwinds. catch_with_exit_code then turns that into an exit.

I think you should be able to remove the random register_retcode here, and then once #4406 lands add this inside the new fn exit there.

Or does ptrace halt execution on an unwind? It should just resume execution then to let the program run its normal course.

tcx.dcx().abort_if_errors();
rustc_driver::EXIT_FAILURE
});

std::process::exit(return_code);
}

Expand Down Expand Up @@ -722,6 +723,8 @@ fn main() {
} else {
show_error!("-Zmiri-native-lib `{}` does not exist", filename);
}
} else if arg == "-Zmiri-force-old-native-lib-mode" {
miri_config.force_old_native_lib = true;
} else if let Some(param) = arg.strip_prefix("-Zmiri-num-cpus=") {
let num_cpus = param
.parse::<u32>()
Expand Down Expand Up @@ -792,6 +795,16 @@ fn main() {

debug!("rustc arguments: {:?}", rustc_args);
debug!("crate arguments: {:?}", miri_config.args);
#[cfg(target_os = "linux")]
if !miri_config.native_lib.is_empty() && !miri_config.force_old_native_lib {
// FIXME: This should display a diagnostic / warning on error
// SAFETY: If any other threads exist at this point (namely for the ctrlc
// handler), they will not interact with anything on the main rustc/Miri
// thread in an async-signal-unsafe way such as by accessing shared
// semaphores, etc.; the handler only calls `sleep()` and `exit()`, which
// are async-signal-safe, as is accessing atomics
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is sounds like this relying on the ctrlc internal implementation details that could change any time...?

I also have no clue why we are talking about threads here, but that may be because init_sv doesn't have a self-contained safety comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it somewhat hard to imagine how the ctrlc handler could realistically be changed to act in ways that may break the safety invariants for signal safety, but for clarity I'll add a better safety comment for init_sv.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main point is that I don't think it even matters what the ctrlc handler does. I think you misunderstood the safety requirements of fork.

let _ = unsafe { miri::init_sv() };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we just ignore errors here...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it errored and the sv process failed to init, then we catch that when calling poll() on it. I would like this to emit a diagnostic on error, though; it just didn't seem necessary to include that as part of this PR

Copy link
Member

@RalfJung RalfJung Jul 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you saying we dynamically fall back to calling native code without fine-grained tracing if the setup fails?

That makes sense, but having a let _ then does not. Also, there should be a comment.

emit a diagnostic on error, though; it just didn't seem necessary to include that as part of this PR

If you have confusing things like let _ instead you need to explain those, or at least add FIXME(ptrace): emit a warning here or so.

EDIT: There actually is a FIXME, fair. I think I'd have rather seen this staged a bit differently but that's getting into very subjective territory. ;)

}
run_compiler_and_exit(
&rustc_args,
&mut MiriCompilerCalls::new(miri_config, many_seeds, genmc_config),
Expand Down
Loading