checkers 0.4.1

A sanity checker for global allocations.

checkers

Documentation Crates Actions Status

Checkers is a simple allocation sanitizer for Rust. It plugs in through the global allocator and can sanity check your unsafe Rust during integration testing. Since it plugs in through the global allocator it doesn't require any additional dependencies and works for all platforms - but it is more limited in what it can verify.

It can check for the following things:

  • Double-frees.
  • Freeing regions which are not allocated.
  • Freeing only part of regions which are allocated.
  • Freeing a region with a mismatching layout.
  • The underlying allocator produces regions adhering to the requested layout. Namely size and alignment.
  • Detailed information on memory usage.
  • Other user-defined conditions (see test).

What it can't do:

  • Test multithreaded code. Since the allocator is global, it is difficult to scope the state for each test case.
  • Detect out-of-bounds accesses.

Examples

It is recommended that you use checkers for integration tests, which by default lives in the ./tests directory. Each file in this directory will be compiled as a separate program, so the use of the global allocator can be more isolated.

We then use checkers by installing checkers::Allocator as the global allocator, after this we can make use of #[checkers::test] attribute macro or the checkers::with function in our tests.

#[global_allocator]
static ALLOCATOR: checkers::Allocator = checkers::Allocator;

#[checkers::test]
fn test_allocations() {
    let _ = Box::into_raw(Box::new(42));
}

The above would result in the following test output:

dangling region: 0x226e5784f30-0x226e5784f40 (size: 16, align: 8).
thread 'test_leak_box' panicked at 'allocation checks failed', tests\leaky_tests.rs:4:1

With checkers::with, we can perform more detailed diagnostics:

#[global_allocator]
static ALLOCATOR: checkers::Allocator = checkers::Allocator;

#[test]
fn test_event_inspection() {
    let snapshot = checkers::with(|| {
        let _ = vec![1, 2, 3, 4];
    });

    assert_eq!(2, snapshot.events.len());
    assert!(snapshot.events[0].is_allocation_with(|r| r.size >= 16));
    assert!(snapshot.events[1].is_deallocation_with(|a| a.size >= 16));
    assert_eq!(1, snapshot.events.allocations());
    assert_eq!(1, snapshot.events.deallocations());
    assert!(snapshot.events.max_memory_used().unwrap() >= 16);
}