Antithesis logomark
DOCS

Rust instrumentation

This page covers how to enable coverage instrumentation for a Rust binary you plan to run in Antithesis. You’ll add a crate, reference it once in your code, and pass a handful of build flags. Find the crate here.

If you enabled rust instrumentation before May 22, 2026, you’re probably using the legacy method. We recommend switching to this method when you can, for now the other method is still supported.

Step 1: Add the dependency to Cargo.toml

[dependencies]
antithesis-instrumentation = "0.1"

Step 2: Include a reference to the library in your code

Somewhere in your Rust code, add this line at least once:

use antithesis_instrumentation as _;

Without it, Cargo treats the crate as unused and the linker drops the runtime shim. You can include this line in any Rust file in your project, it doesn’t matter which. You may also want to put this under a #[cfg].

Step 3: Adjust your build flags

There are two ways to do this: either at the command line or via a .toml file. You can pick either; you don’t need to do both.

Option A: Build flags at the command line

You’ll need to specify the —config options below. Your full command line will look something like this:

cargo build --bin main \
--config 'build.target = "x86_64-unknown-linux-gnu"' \
--config 'target.x86_64-unknown-linux-gnu.rustflags = [
"-Ccodegen-units=1",
"-Cpasses=sancov-module",
"-Cllvm-args=-sanitizer-coverage-level=3",
"-Cllvm-args=-sanitizer-coverage-trace-pc-guard",
"-Cllvm-args=-sanitizer-coverage-pc-table",
"-Clink-args=-Wl,--build-id",
]'

Option B: Build flags in a .toml file

You can put the flags into .cargo/config.toml file instead like this:

[build]
target = "x86_64-unknown-linux-gnu"
[target.x86_64-unknown-linux-gnu]
rustflags = [
"-Ccodegen-units=1",
"-Cpasses=sancov-module",
"-Cllvm-args=-sanitizer-coverage-level=3",
"-Cllvm-args=-sanitizer-coverage-trace-pc-guard",
"-Cllvm-args=-sanitizer-coverage-pc-table",
"-Clink-args=-Wl,--build-id",
]

The flags above instrument each crate as it is compiled. That is also why they set -Ccodegen-units=1: with more than one codegen unit, an optimized build runs a local ThinLTO pass over the crate after it has been instrumented. If you want link-time optimization proper, for instance lto = "fat" or lto = "thin" in your release profile, don’t combine it with the flags above. The optimizer would inline, merge, and tail-call the instrumented code in ways Antithesis cannot symbolize.

Instead, hand link-time optimization to the linker and let the linker’s optimization pipeline instrument the fully optimized program. Keep lto = "fat" (or "thin") in your profile, and replace the rustflags above with:

[build]
target = "x86_64-unknown-linux-gnu"
[target.x86_64-unknown-linux-gnu]
rustflags = [
"-Clinker-plugin-lto",
"-Clink-arg=-Xlinker",
"-Clink-arg=--lto-newpm-passes=lto<O3>,sancov-module",
"-Clink-arg=-Wl,-mllvm,-sanitizer-coverage-level=3",
"-Clink-arg=-Wl,-mllvm,-sanitizer-coverage-trace-pc-guard",
"-Clink-arg=-Wl,-mllvm,-sanitizer-coverage-pc-table",
"-Clink-args=-Wl,--build-id",
]

The same list works as --config options on the command line. Match the optimization level in lto<O3> to your profile’s opt-level (the release default is 3). With lto = "thin", use thinlto<O3> in place of lto<O3>.

To confirm that instrumentation ran after optimization, check that the coverage hook is only ever called, never jumped to:

$ llvm-objdump -d target/x86_64-unknown-linux-gnu/release/main | grep -c 'jmp.*__sanitizer_cov_trace_pc_guard'
0

Validation

To confirm that the instrumentation process was successful, you’ll check two things:

  1. You included the instrumentation crate in your code.
  2. Your binaries are actually instrumented.

To check if you’ve included the instrumentation crate, run the command nm to list all the symbols in a binary, and then grep the string “antithesis_load_libvoidstar”.

$ nm client_binary | grep "antithesis_load_libvoidstar"
T antithesis_load_libvoidstar

Notice the “T” character in the previous code block: it means the symbol is in the text (code) section.

To check that you’ve correctly built the binaries with the right flags. Run the command readelf and grep the string “sancov”.

$ readelf -S target/x86_64-unknown-linux-gnu/debug/main | grep sancov
[30] __sancov_guards PROGBITS 000000000007a628 077628 001750 00 WA 0 0 4
[31] __sancov_pcs PROGBITS 000000000007bd78 078d78 005d40 00 WA 0 0 8

If you built your binaries correctly, you’ll see something similar, but the addresses may be different.

Symbolization

The LLVM compiler infrastructure will output debug symbols in the DWARF format. Antithesis needs either the original unstripped binary, or the binary together with its debug info (as produced by objcopy --only-keep-debug and --add-gnu-debuglink). Symlink (or move) these files into a directory named /symbols in the root of the appropriate container image.

Antithesis labels the coverage it reports for each binary with the binary’s file name.

Help

If you need help with this, write to us at support@antithesis.com or talk to your forward deployed engineer.