diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 4c7351879877..04a1d939d2e5 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -301,6 +301,15 @@ environment variable. We first document the most relevant and most commonly used * `-Zmiri-disable-isolation` disables host isolation. As a consequence, the program has access to host resources such as environment variables, file systems, and randomness. +* `-Zmiri-disable-leak-backtraces` disables backtraces reports for memory leaks. By default, a + backtrace is captured for every allocation when it is created, just in case it leaks. This incurs + some memory overhead to store data that is almost never used. This flag is implied by + `-Zmiri-ignore-leaks`. +* `-Zmiri-env-forward=` forwards the `var` environment variable to the interpreted program. Can + be used multiple times to forward several variables. Execution will still be deterministic if the + value of forwarded variables stays the same. Has no effect if `-Zmiri-disable-isolation` is set. +* `-Zmiri-ignore-leaks` disables the memory leak checker, and also allows some + remaining threads to exist when the main thread exits. * `-Zmiri-isolation-error=` configures Miri's response to operations requiring host access while isolation is enabled. `abort`, `hide`, `warn`, and `warn-nobacktrace` are the supported actions. The default is to `abort`, @@ -308,11 +317,6 @@ environment variable. We first document the most relevant and most commonly used execution with a "permission denied" error being returned to the program. `warn` prints a full backtrace when that happens; `warn-nobacktrace` is less verbose. `hide` hides the warning entirely. -* `-Zmiri-env-forward=` forwards the `var` environment variable to the interpreted program. Can - be used multiple times to forward several variables. Execution will still be deterministic if the - value of forwarded variables stays the same. Has no effect if `-Zmiri-disable-isolation` is set. -* `-Zmiri-ignore-leaks` disables the memory leak checker, and also allows some - remaining threads to exist when the main thread exits. * `-Zmiri-num-cpus` states the number of available CPUs to be reported by miri. By default, the number of available CPUs is `1`. Note that this flag does not affect how miri handles threads in any way. diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 26a7ead2407c..3aa71bb7e3c8 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -359,6 +359,8 @@ fn main() { isolation_enabled = Some(false); } miri_config.isolated_op = miri::IsolatedOp::Allow; + } else if arg == "-Zmiri-disable-leak-backtraces" { + miri_config.collect_leak_backtraces = false; } else if arg == "-Zmiri-disable-weak-memory-emulation" { miri_config.weak_memory_emulation = false; } else if arg == "-Zmiri-track-weak-memory-loads" { @@ -385,6 +387,7 @@ fn main() { }; } else if arg == "-Zmiri-ignore-leaks" { miri_config.ignore_leaks = true; + miri_config.collect_leak_backtraces = false; } else if arg == "-Zmiri-panic-on-unsupported" { miri_config.panic_on_unsupported = true; } else if arg == "-Zmiri-tag-raw-pointers" { diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 60533687bed5..defd37c37757 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -146,6 +146,8 @@ pub struct MiriConfig { pub num_cpus: u32, /// Requires Miri to emulate pages of a certain size pub page_size: Option, + /// Whether to collect a backtrace when each allocation is created, just in case it leaks. + pub collect_leak_backtraces: bool, } impl Default for MiriConfig { @@ -180,6 +182,7 @@ impl Default for MiriConfig { gc_interval: 10_000, num_cpus: 1, page_size: None, + collect_leak_backtraces: true, } } } @@ -461,9 +464,14 @@ pub fn eval_entry<'tcx>( let leaks = ecx.find_leaked_allocations(&ecx.machine.static_roots); if !leaks.is_empty() { report_leaks(&ecx, leaks); - tcx.sess.note_without_error( - "the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check", - ); + let leak_message = "the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check"; + if ecx.machine.collect_leak_backtraces { + // If we are collecting leak backtraces, each leak is a distinct error diagnostic. + tcx.sess.note_without_error(leak_message); + } else { + // If we do not have backtraces, we just report an error without any span. + tcx.sess.err(leak_message); + }; // Ignore the provided return code - let the reported error // determine the return code. return None; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index a9c1357fcf29..37f54a4a5bd4 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -471,12 +471,17 @@ pub struct MiriMachine<'mir, 'tcx> { pub(crate) gc_interval: u32, /// The number of blocks that passed since the last BorTag GC pass. pub(crate) since_gc: u32, + /// The number of CPUs to be reported by miri. pub(crate) num_cpus: u32, + /// Determines Miri's page size and associated values pub(crate) page_size: u64, pub(crate) stack_addr: u64, pub(crate) stack_size: u64, + + /// Whether to collect a backtrace when each allocation is created, just in case it leaks. + pub(crate) collect_leak_backtraces: bool, } impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { @@ -585,6 +590,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { page_size, stack_addr, stack_size, + collect_leak_backtraces: config.collect_leak_backtraces, } } @@ -732,6 +738,7 @@ impl VisitTags for MiriMachine<'_, '_> { page_size: _, stack_addr: _, stack_size: _, + collect_leak_backtraces: _, } = self; threads.visit_tags(visit); @@ -975,7 +982,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { // If an allocation is leaked, we want to report a backtrace to indicate where it was // allocated. We don't need to record a backtrace for allocations which are allowed to // leak. - let backtrace = if kind.may_leak() { None } else { Some(ecx.generate_stacktrace()) }; + let backtrace = if kind.may_leak() || !ecx.machine.collect_leak_backtraces { + None + } else { + Some(ecx.generate_stacktrace()) + }; let alloc: Allocation = alloc.adjust_from_tcx( &ecx.tcx, diff --git a/src/tools/miri/tests/fail/memleak.stderr b/src/tools/miri/tests/fail/memleak.stderr index 9b23a71f5abb..6d9b664c8f48 100644 --- a/src/tools/miri/tests/fail/memleak.stderr +++ b/src/tools/miri/tests/fail/memleak.stderr @@ -15,6 +15,8 @@ note: inside `main` LL | std::mem::forget(Box::new(42)); | ^^^^^^^^^^^^ +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check error: aborting due to previous error diff --git a/src/tools/miri/tests/fail/memleak_no_backtrace.rs b/src/tools/miri/tests/fail/memleak_no_backtrace.rs new file mode 100644 index 000000000000..24d4a02df712 --- /dev/null +++ b/src/tools/miri/tests/fail/memleak_no_backtrace.rs @@ -0,0 +1,7 @@ +//@compile-flags: -Zmiri-disable-leak-backtraces +//@error-pattern: the evaluated program leaked memory +//@normalize-stderr-test: ".*│.*" -> "$$stripped$$" + +fn main() { + std::mem::forget(Box::new(42)); +} diff --git a/src/tools/miri/tests/fail/memleak_no_backtrace.stderr b/src/tools/miri/tests/fail/memleak_no_backtrace.stderr new file mode 100644 index 000000000000..f44e6ce07977 --- /dev/null +++ b/src/tools/miri/tests/fail/memleak_no_backtrace.stderr @@ -0,0 +1,4 @@ +error: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/memleak_rc.32bit.stderr b/src/tools/miri/tests/fail/memleak_rc.32bit.stderr index 0eaf84048e0a..0e1146cf4ad9 100644 --- a/src/tools/miri/tests/fail/memleak_rc.32bit.stderr +++ b/src/tools/miri/tests/fail/memleak_rc.32bit.stderr @@ -16,6 +16,8 @@ note: inside `main` LL | let x = Dummy(Rc::new(RefCell::new(None))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check error: aborting due to previous error diff --git a/src/tools/miri/tests/fail/memleak_rc.64bit.stderr b/src/tools/miri/tests/fail/memleak_rc.64bit.stderr index 9b7adc00ef5c..4979588f370f 100644 --- a/src/tools/miri/tests/fail/memleak_rc.64bit.stderr +++ b/src/tools/miri/tests/fail/memleak_rc.64bit.stderr @@ -16,6 +16,8 @@ note: inside `main` LL | let x = Dummy(Rc::new(RefCell::new(None))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check error: aborting due to previous error