iai_callgrind::client_requests::valgrind

Function malloclike_block

source
pub fn malloclike_block(
    addr: *const (),
    size: usize,
    redzone: usize,
    is_zeroed: bool,
)
Available on crate feature client_requests_defs only.
Expand description

Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing when heap blocks are allocated in order to give accurate results.

Ignored if addr == 0.

The following description is taken almost untouched from the docs in the valgrind.h header file.

This happens automatically for the standard allocator functions such as malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, delete[], etc.

But if your program uses a custom allocator, this doesn’t automatically happen, and Valgrind will not do as well. For example, if you allocate superblocks with mmap() and then allocates chunks of the superblocks, all Valgrind’s observations will be at the mmap() level, and it won’t know that the chunks should be considered separate entities. In Memcheck’s case, that means you probably won’t get heap block overrun detection (because there won’t be redzones marked as unaddressable) and you definitely won’t get any leak detection.

The following client requests allow a custom allocator to be annotated so that it can be handled accurately by Valgrind.

malloclike_block marks a region of memory as having been allocated by a malloc()-like function. For Memcheck (an illustrative case), this does two things:

  • It records that the block has been allocated. This means any addresses within the block mentioned in error messages will be identified as belonging to the block. It also means that if the block isn’t freed it will be detected by the leak checker.
  • It marks the block as being addressable and undefined (if is_zeroed is not set), or addressable and defined (if is_zeroed is set). This controls how accesses to the block by the program are handled.

addr is the start of the usable block (ie. after any redzone), size is its size. redzone is the redzone size if the allocator can apply redzones – these are blocks of padding at the start and end of each block. Adding redzones is recommended as it makes it much more likely Valgrind will spot block overruns. is_zeroed indicates if the memory is zeroed (or filled with another predictable value), as is the case for calloc().

malloclike_block should be put immediately after the point where a heap block – that will be used by the client program – is allocated. It’s best to put it at the outermost level of the allocator if possible; for example, if you have a function my_alloc() which calls internal_alloc(), and the client request is put inside internal_alloc(), stack traces relating to the heap block will contain entries for both my_alloc() and internal_alloc(), which is probably not what you want.

For Memcheck users: if you use malloclike_block to carve out custom blocks from within a heap block, B, that has been allocated with malloc/calloc/new/etc, then block B will be ignored during leak-checking – the custom blocks will take precedence.

In many cases, these three client requests (malloclike_block, resizeinplace_block, freelike_block) will not be enough to get your allocator working well with Memcheck. More specifically, if your allocator writes to freed blocks in any way then a super::memcheck::make_mem_undefined call will be necessary to mark the memory as addressable just before the zeroing occurs, otherwise you’ll get a lot of invalid write errors. For example, you’ll need to do this if your allocator recycles freed blocks, but it zeroes them before handing them back out (via malloclike_block). Alternatively, if your allocator reuses freed blocks for allocator-internal data structures, super::memcheck::make_mem_undefined calls will also be necessary.

Really, what’s happening is a blurring of the lines between the client program and the allocator… after freelike_block is called, the memory should be considered unaddressable to the client program, but the allocator knows more than the rest of the client program and so may be able to safely access it. Extra client requests are necessary for Valgrind to understand the distinction between the allocator and the rest of the program.

See also Memory Pools: describing and working with custom allocators and Memcheck: Client requests