Expand description
Valgrind Client Request interface for Rust
crabgrind
wraps various Valgrind macros in C functions, compiles and links against
the resulting binary, and exposes an unsafe interface to allow Rust programs running under Valgrind to
interact with the tools and environment.
This library is indeed a wrapper, the only thing it adds are the type conversions and some structure, all the real things are happening inside Valgrind.
Valgrind 3 API coverage
- Supported tool-specific client request interface: valgrind, callgrind, memcheck, helgrind, massif,
- Monitor commands interface
Quickstart
crabgrind
doesn’t links against Valgrind, but reads it’s header files, so they must be accessible
to build the project.
If headers resides at the /usr/include/valgrind
, cc
, the build
tool crabgrind
uses, will find them.
If you have installed Vallgrind manually, you can set DEP_VALGRIND
environment variable to the appropriate path,
its value, if specified, will be directly passed to cc::Build::include
.
env DEP_VALGRIND=/usr/include cargo build
Add the following to your Cargo.toml
file:
[dependencies]
crabgrind = "~0.1"
Next, use some of the Valgrind’s API
use crabgrind as cg;
fn main() {
if matches!(cg::run_mode(), cg::RunMode::Native) {
println!("run me under Valgrind");
} else {
cg::println!("Hey, Valgrind!");
}
}
And run your application under Valgrind, either with handy cargo-valgrind
cargo valgrind run
or manually
cargo build
valgrind ./target/debug/appname
Examples
Print current function stack-trace to the Valgrind log
Valgrind provides VALGRIND_PRINTF_BACKTRACE
macro to print the message with the stack-trace attached,
crabgrind::print_stacktrace
is it’s crabbed wrapper.
use crabgrind as cg;
#[inline(never)]
fn print_trace(){
let mode = cg::run_mode();
cg::print_stacktrace!("current mode: {mode:?}");
}
print_trace();
Exclude expensive initialization code from the measurements
One way to do this would be to turn off stats collection at stratup with the
--collect-atstart=no
callgrind command-line attribute, and enable/disable it from the code with callgrind::toggle_collect
use crabgrind as cg;
// ... some expensive initialization
cg::callgrind::toggle_collect();
// code of interest
cg::callgrind::toggle_collect();
// ... some deinitialization
Run a closure on the real CPU while running under Valgrind
We can run on the real CPU instead of the virtual one using valgrind::non_simd_call
,
refer to valgrind.h
for details on limitations and various ways to crash.
use crabgrind as cg;
let mut state = 0;
cg::valgrind::non_simd_call(|tid| {
// uncomment following line to see "the 'impossible' happened"
// println!("tid: {tid}");
state = tid;
});
println!("tid: {state}");
Save current memory usage snapshot to a file
We’ll use Massif
tool and the monitor command
interface to run the corresponding Massif command.
use crabgrind as cg;
let heap = String::from("alloca");
if cg::monitor_command("snapshot mem.snapshot").is_ok(){
println!("snapshot is saved to \"mem.snapshot\"");
}
Dump Callgrind counters on a function basis
use crabgrind as cg;
fn factorial1(num: u128) -> u128 {
match num {
0 => 1,
1 => 1,
_ => factorial1(num - 1) * num,
}
}
fn factorial2(num: u128) -> u128 {
(1..=num).product()
}
cg::callgrind::zero_stats();
let a = factorial1(20);
cg::callgrind::dump_stats("factorial1");
let b = factorial2(20);
cg::callgrind::dump_stats("factorial2");
assert_eq!(a,b);
cg::callgrind::dump_stats(None);
Overhead
from Valgrind docs
The code added to your binary has negligible performance impact: on x86, amd64, ppc32, ppc64 and ARM, the overhead is 6 simple integer instructions and is probably undetectable except in tight loops.
… the code does nothing when not run on Valgrind, so you are not forced to run your program under Valgrind just because you use the macros in this file.
however,
- wrapping each macros in a function implies function call overhead regardless of the run mode
- functions that returns
std::result::Result
involve branching - functions that takes strings as a parameters internally converts them to
std::ffi::CString
If you wish to compile out all (crab)Valgrind from the binary, you can wrap crabgrind
calls with
the feature-gate.
Tests
Tests must be run under Valgrind, as of now cargo-valgrind
fits nicely, it allows to compile and run tests under Valgrind in one command
cargo valgrind test
Modules
Macros
- Prints to the Valgrind’s log.
- Prints to the Valgrind’s log, with the current stacktrace attached.
- Prints to the Valgrind’s log, with a newline.
Enums
- Current run mode
Functions
- Change the value of a dynamic command line option.
- Returns the number of errors found so far by Valgrind
- Disable error reporting for this thread
- Re-enable error reporting for this thread
- Execute arbitrary Valgrind Monitor command
- Returns the
RunMode
app running in