Miri

An interpreter for Rust’s Mid-level Intermediate Representation (MIR). Runs your program on an abstract machine and detects Undefined Behaviour that a normal compiler would happily emit.

What Miri catches:

  • Out-of-bounds pointer arithmetic and derefs.
  • Use-after-free.
  • Violations of Rust’s aliasing model (Stacked Borrows / Tree Borrows): the exact rules &mut T is supposed to guarantee.
  • Invalid values (e.g. a bool that’s not 0 or 1, a &T that’s null).
  • Data races.
  • Uninitialized reads.

Use:

rustup +nightly component add miri
cargo +nightly miri test

Runs your test suite under the interpreter. It’s slow (expect 10–100×), so you don’t use it on full integration tests, but on unit tests for unsafe code it’s invaluable.

Especially important when writing unsafe. The borrow checker steps aside in unsafe blocks; Miri enforces the rules dynamically instead. If your unsafe passes Miri with a good test suite, you’ve caught most of the ways it could be wrong.

From ECE459 L28

Shares a name with the 1960s Star Trek episode. In this course the most common unsafe use is launching a GPU kernel, so Miri’s utility is limited there.

Profiling with Miri [Bra22]

Miri can also produce flamegraph-style output via measuremecrox → Chrome dev tools:

MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-measureme=crox" cargo +nightly miri run

Caveat from the article’s comments: Miri runs the debug/interpreter version, so what it flags as hot may not be hot in a release build; optimizing for Miri can actually regress real performance. Useful as an idea generator in the change-run-evaluate loop, not as ground truth.

War story: running Miri on the producer-consumer-opt code required dropping items-per-thread to 10 to finish in reasonable time. The resulting graph showed that initializing the RNG dominated thread 1’s early time; subsequent uses were quick.