Skip to main content

valgrind_requests/
valgrind.rs

1// Copyright (C) 2000-2017 Julian Seward.  All rights reserved.
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions
5// are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice, this list of conditions
8//    and the following disclaimer.
9//
10// 2. The origin of this software must not be misrepresented; you must not claim that you wrote the
11//    original software.  If you use this software in a product, an acknowledgment in the product
12//    documentation would be appreciated but is not required.
13//
14// 3. Altered source versions must be plainly marked as such, and must not be misrepresented as
15//    being the original software.
16//
17// 4. The name of the author may not be used to endorse or promote products derived from this
18//    software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
21// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
26// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31//
32// ----------------------------------------------------------------
33//
34// We're using a lot of the original documentation from the `valgrind.h` header file with some small
35// adjustments, so above is the original license from `valgrind.h` file.
36//
37// This file is distributed under the same License as the rest of `valgrind-requests`.
38//
39// ----------------------------------------------------------------
40//
41//! All public client requests from the `valgrind.h` header file
42//!
43//! See also [The client request
44//! mechanism](https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq)
45
46/// The `MempoolFlags` usable in [`create_mempool_ext`] as `flags`.
47#[expect(non_snake_case)]
48pub mod MempoolFlags {
49    /// When `MempoolFlags` is `DEFAULT`, the behavior is identical to [`super::create_mempool`].
50    pub const DEFAULT: u8 = 0;
51
52    /// A [`METAPOOL`] can also be marked as an `auto free` pool
53    ///
54    /// This flag, which must be OR-ed together with the [`METAPOOL`].
55    ///
56    /// For an `auto free` pool, [`super::mempool_free`] will automatically free the second level
57    /// blocks that are contained inside the first level block freed with [`super::mempool_free`].
58    /// In other words, calling [`super::mempool_free`] will cause implicit calls to
59    /// [`super::freelike_block`] for all the second level blocks included in the first level block.
60    ///
61    /// Note: it is an error to use this flag without the [`METAPOOL`] flag.
62    pub const AUTOFREE: u8 = 1;
63
64    /// The flag [`super::MempoolFlags::METAPOOL`]
65    ///
66    /// The flag [`super::MempoolFlags::METAPOOL`] specifies that the pieces of memory associated
67    /// with the pool using [`super::mempool_alloc`] will be used by the application as superblocks
68    /// to dole out [`super::malloclike_block`] blocks using [`super::malloclike_block`].
69    ///
70    /// In other words, a meta pool is a "2 levels" pool : first level is the blocks described by
71    /// [`super::mempool_alloc`] The second level blocks are described using
72    /// [`super::malloclike_block`]. Note that the association between the pool and the second level
73    /// blocks is implicit : second level blocks will be located inside first level blocks. It is
74    /// necessary to use the `METAPOOL` flag for such 2 levels pools, as otherwise Valgrind will
75    /// detect overlapping memory blocks, and will abort execution (e.g. during leak search).
76    pub const METAPOOL: u8 = 2;
77}
78
79use core::ffi::CStr;
80
81use super::{
82    RawFd, StackId, ThreadId, bindings, fatal_error, valgrind_do_client_request_expr,
83    valgrind_do_client_request_stmt,
84};
85
86/// Returns the number of Valgrinds this code is running under
87///
88/// That is, 0 if running natively, 1 if running under Valgrind, 2 if running under Valgrind which
89/// is running under another Valgrind, etc.
90#[inline(always)]
91pub fn running_on_valgrind() -> usize {
92    do_client_request!(
93        "valgrind::running_on_valgrind",
94        0,
95        bindings::VR_ValgrindClientRequest::VR_RUNNING_ON_VALGRIND,
96        0,
97        0,
98        0,
99        0,
100        0
101    )
102}
103
104/// Discard translation of code in the range [addr ... addr + len - 1].
105///
106/// Useful if you are debugging a `JITter` or some such, since it provides a way to make sure
107/// Valgrind will retranslate the invalidated area.
108#[inline(always)]
109pub fn discard_translations(addr: *const (), len: usize) {
110    do_client_request!(
111        "valgrind::discard_translations",
112        bindings::VR_ValgrindClientRequest::VR_DISCARD_TRANSLATIONS,
113        addr as usize,
114        len,
115        0,
116        0,
117        0
118    );
119}
120
121/// Returns `true` if the tool replaces malloc (e.g., Memcheck).
122///
123/// Returns `false` if the tool does not replace malloc (e.g., Cachegrind and Callgrind) or if the
124/// executable is not running under Valgrind.
125#[inline(always)]
126pub fn replaces_malloc() -> bool {
127    do_client_request!(
128        "valgrind::replaces_malloc",
129        0,
130        bindings::VR_ValgrindClientRequest::VR_VALGRIND_REPLACES_MALLOC,
131        0,
132        0,
133        0,
134        0,
135        0
136    ) != 0
137}
138
139/// Get the running tool name as a string.
140///
141/// Returns the required length (including terminating nul) for the input `buffer`. Returns `0` and
142/// the contents of `buffer` are not modified if not running under Valgrind. `buffer` may be
143/// [`core::ptr::null_mut`], which can be used to query the required buffer length before making a
144/// real request for the tool name.
145///
146/// `len` should be the length of `buffer`. The returned string in `buffer` will be nul terminated.
147/// If `buffer` is too small to contain the tool name then the name will be truncated.
148#[inline(always)]
149pub fn get_toolname(buffer: *mut u8, len: usize) -> usize {
150    do_client_request!(
151        "valgrind::get_toolname",
152        0,
153        bindings::VR_ValgrindClientRequest::VR_VALGRIND_GET_TOOLNAME,
154        buffer as usize,
155        len,
156        0,
157        0,
158        0
159    )
160}
161
162/// Allow control to move from the simulated CPU to the real CPU, calling an arbitrary function.
163///
164/// Note that the current [`ThreadId`] is inserted as the first argument.
165/// So this call:
166///
167/// `non_simd_call0(func)`
168///
169/// requires func to have this signature:
170///
171/// `usize func(ThreadId tid)`
172///
173/// Note that these client requests are not entirely reliable. For example, if you call a function
174/// with them that subsequently calls `printf()`, there's a high chance Valgrind will crash.
175/// Generally, your prospects of these working are made higher if the called function does not refer
176/// to any global variables, and does not refer to other functions (print! et al.).
177#[expect(clippy::fn_to_numeric_cast_any)]
178#[inline(always)]
179pub fn non_simd_call0(func: fn(ThreadId) -> usize) -> usize {
180    do_client_request!(
181        "valgrind::non_simd_call0",
182        0,
183        bindings::VR_ValgrindClientRequest::VR_CLIENT_CALL0,
184        func as *const () as usize,
185        0,
186        0,
187        0,
188        0
189    )
190}
191
192/// Allow control to move from the simulated CPU to the real CPU, calling an arbitrary function.
193///
194/// See also [`non_simd_call0`]
195///
196/// # Examples
197///
198/// ```rust,no_run
199/// let num = 42i32;
200/// let res = valgrind_requests::valgrind::non_simd_call1(
201///     |_tid, a| unsafe { ((a as *const i32).as_ref().unwrap() + 2) as usize },
202///     (&num) as *const i32 as usize,
203/// );
204/// assert_eq!(res, 44);
205/// ```
206#[expect(clippy::fn_to_numeric_cast_any)]
207#[inline(always)]
208pub fn non_simd_call1(func: fn(ThreadId, usize) -> usize, arg1: usize) -> usize {
209    do_client_request!(
210        "valgrind::non_simd_call1",
211        0,
212        bindings::VR_ValgrindClientRequest::VR_CLIENT_CALL1,
213        func as *const () as usize,
214        arg1,
215        0,
216        0,
217        0
218    )
219}
220
221/// Allow control to move from the simulated CPU to the real CPU, calling an arbitrary function.
222///
223/// See also [`non_simd_call0`] and [`non_simd_call1`]
224#[expect(clippy::fn_to_numeric_cast_any)]
225#[inline(always)]
226pub fn non_simd_call2(
227    func: fn(ThreadId, usize, usize) -> usize,
228    arg1: usize,
229    arg2: usize,
230) -> usize {
231    do_client_request!(
232        "valgrind::non_simd_call2",
233        0,
234        bindings::VR_ValgrindClientRequest::VR_CLIENT_CALL2,
235        func as *const () as usize,
236        arg1,
237        arg2,
238        0,
239        0
240    )
241}
242
243/// Allow control to move from the simulated CPU to the real CPU, calling an arbitrary function.
244///
245/// See also [`non_simd_call0`] and [`non_simd_call1`]
246#[expect(clippy::fn_to_numeric_cast_any)]
247#[inline(always)]
248pub fn non_simd_call3(
249    func: fn(ThreadId, usize, usize, usize) -> usize,
250    arg1: usize,
251    arg2: usize,
252    arg3: usize,
253) -> usize {
254    do_client_request!(
255        "valgrind::non_simd_call3",
256        0,
257        bindings::VR_ValgrindClientRequest::VR_CLIENT_CALL3,
258        func as *const () as usize,
259        arg1,
260        arg2,
261        arg3,
262        0
263    )
264}
265
266/// Counts the number of errors that have been recorded by a tool.
267///
268/// Can be useful to e.g. can send output to /dev/null and still count errors.
269///
270/// The tool must record the errors with `VG_(maybe_record_error)()` or `VG_(unique_error)()` for
271/// them to be counted. These are to my best knowledge (as of Valgrind 3.22) `Memcheck`, `DRD` and
272/// `Helgrind`.
273#[inline(always)]
274pub fn count_errors() -> usize {
275    do_client_request!(
276        "valgrind::count_errors",
277        0,
278        bindings::VR_ValgrindClientRequest::VR_COUNT_ERRORS,
279        0,
280        0,
281        0,
282        0,
283        0
284    )
285}
286
287/// Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing when heap blocks are
288/// allocated to give accurate results.
289///
290/// Ignored if addr == 0.
291///
292/// The following description is taken almost untouched from the docs in the `valgrind.h` header
293/// file.
294///
295/// This happens automatically for the standard allocator functions such as `malloc()`, `calloc()`,
296/// `realloc()`, `memalign()`, `new`, `new[]`, `free()`, `delete`, `delete[]`, etc.
297///
298/// But if your program uses a custom allocator, this doesn't automatically happen, and Valgrind
299/// will not do as well. For example, if you allocate superblocks with `mmap()` and then allocates
300/// chunks of the superblocks, all Valgrind's observations will be at the `mmap()` level, and it
301/// won't know that the chunks should be considered separate entities.  In Memcheck's case, that
302/// means you probably won't get heap block overrun detection (because there won't be redzones
303/// marked as unaddressable) and you definitely won't get any leak detection.
304///
305/// The following client requests allow a custom allocator to be annotated so that it can be handled
306/// accurately by Valgrind.
307///
308/// [`malloclike_block`] marks a region of memory as having been allocated by a malloc()-like
309/// function. For Memcheck (an illustrative case), this does two things:
310///
311/// - It records that the block has been allocated.  This means any addresses within the block
312///   mentioned in error messages will be identified as belonging to the block.  It also means that
313///   if the block isn't freed it will be detected by the leak checker.
314/// - It marks the block as being addressable and undefined (if `is_zeroed` is not set), or
315///   addressable and defined (if `is_zeroed` is set). This controls how accesses to the block by
316///   the program are handled.
317///
318/// `addr` is the start of the usable block (ie. after any redzone), `size` is its size. `redzone`
319/// is the redzone size if the allocator can apply redzones -- these are blocks of padding at the
320/// start and end of each block. Adding redzones is recommended as it makes it much more likely
321/// Valgrind will spot block overruns. `is_zeroed` indicates if the memory is zeroed (or filled
322/// with another predictable value), as is the case for `calloc()`.
323///
324/// [`malloclike_block`] should be put immediately after the point where a heap block -- that will
325/// be used by the client program -- is allocated. It's best to put it at the outermost level of the
326/// allocator if possible; for example, if you have a function `my_alloc()` which calls
327/// `internal_alloc()`, and the client request is put inside `internal_alloc()`, stack traces
328/// relating to the heap block will contain entries for both `my_alloc()` and `internal_alloc()`,
329/// which is probably not what you want.
330///
331/// For Memcheck users: if you use [`malloclike_block`] to carve out custom blocks from within a
332/// heap block, B, that has been allocated with malloc/calloc/new/etc, then block B will be
333/// *ignored* during leak-checking -- the custom blocks will take precedence.
334///
335/// In many cases, these three client requests (`malloclike_block`, [`resizeinplace_block`],
336/// [`freelike_block`]) will not be enough to get your allocator working well with Memcheck. More
337/// specifically, if your allocator writes to freed blocks in any way then a
338/// [`super::memcheck::make_mem_undefined`] call will be necessary to mark the memory as addressable
339/// just before the zeroing occurs, otherwise you'll get a lot of invalid write errors.  For
340/// example, you'll need to do this if your allocator recycles freed blocks, but it zeroes them
341/// before handing them back out (via `malloclike_block`). Alternatively, if your allocator reuses
342/// freed blocks for allocator-internal data structures, [`super::memcheck::make_mem_undefined`]
343/// calls will also be necessary.
344///
345/// Really, what's happening is a blurring of the lines between the client program and the
346/// allocator... after [`freelike_block`] is called, the memory should be considered unaddressable
347/// to the client program, but the allocator knows more than the rest of the client program and so
348/// may be able to safely access it. Extra client requests are necessary for Valgrind to understand
349/// the distinction between the allocator and the rest of the program.
350///
351/// See also [Memory Pools: describing and working with custom
352/// allocators](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools) and [Memcheck:
353/// Client requests](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.clientreqs)
354#[inline(always)]
355pub fn malloclike_block(addr: *const (), size: usize, redzone: usize, is_zeroed: bool) {
356    do_client_request!(
357        "valgrind::malloclike_block",
358        bindings::VR_ValgrindClientRequest::VR_MALLOCLIKE_BLOCK,
359        addr as usize,
360        size,
361        redzone,
362        usize::from(is_zeroed),
363        0
364    );
365}
366
367/// `resizeinplace_block` informs a tool about reallocation.
368///
369/// The following description is taken almost untouched from the docs in the `valgrind.h` header
370/// file.
371///
372/// For Memcheck, it does four things:
373///
374/// - It records that the size of a block has been changed. This assumes that the block was
375///   annotated as having been allocated via [`malloclike_block`]. Otherwise, an error will be
376///   issued.
377/// - If the block shrunk, it marks the freed memory as being unaddressable.
378/// - If the block grew, it marks the new area as undefined and defines a red zone past the end of
379///   the new block.
380/// - The V-bits of the overlap between the old and the new block are preserved.
381///
382/// `resizeinplace_block` should be put after allocation of the new block and before deallocation of
383/// the old block.
384///
385/// See also [`malloclike_block`] for more details
386#[inline(always)]
387pub fn resizeinplace_block(addr: *const (), old_size: usize, new_size: usize, redzone: usize) {
388    do_client_request!(
389        "valgrind::resizeinplace_block",
390        bindings::VR_ValgrindClientRequest::VR_RESIZEINPLACE_BLOCK,
391        addr as usize,
392        old_size,
393        new_size,
394        redzone,
395        0
396    );
397}
398
399/// `freelike_block` is the partner to [`malloclike_block`]. For Memcheck, it does two things:
400///
401/// The following description is taken almost untouched from the docs in the `valgrind.h` header
402/// file.
403///
404/// - It records that the block has been deallocated. This assumes that the block was annotated as
405///   having been allocated via [`malloclike_block`]. Otherwise, an error will be issued.
406/// - It marks the block as being unaddressable.
407///
408/// `freelike_block` should be put immediately after the point where a heap block is deallocated.
409///
410/// See also [`malloclike_block`] for more details
411#[inline(always)]
412pub fn freelike_block(addr: *const (), redzone: usize) {
413    do_client_request!(
414        "valgrind::freelike_block",
415        bindings::VR_ValgrindClientRequest::VR_FREELIKE_BLOCK,
416        addr as usize,
417        redzone,
418        0,
419        0,
420        0
421    );
422}
423
424/// Create a memory pool
425///
426/// This request registers the address `pool` as the anchor address for a memory pool. It also
427/// provides a size `redzone`, specifying how large the redzones placed around chunks allocated from
428/// the pool should be. Finally, it provides an `is_zeroed` argument that specifies whether the
429/// pool's chunks are zeroed (more precisely: defined) when allocated. Upon completion of this
430/// request, no chunks are associated with the pool. The request simply tells Memcheck that the pool
431/// exists, so that subsequent calls can refer to it as a pool.
432///
433/// See also [Memory Pools: describing and working with custom
434/// allocators](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools)
435#[inline(always)]
436pub fn create_mempool(pool: *const (), redzone: usize, is_zeroed: bool) {
437    do_client_request!(
438        "valgrind::create_mempool",
439        bindings::VR_ValgrindClientRequest::VR_CREATE_MEMPOOL,
440        pool as usize,
441        redzone,
442        usize::from(is_zeroed),
443        0,
444        0
445    );
446}
447
448/// Create a memory pool like [`create_mempool`] with some [`MempoolFlags`] specifying extended
449/// behavior.
450///
451/// See also [`create_mempool`], [`MempoolFlags`] and [Memory Pools: describing and working with
452/// custom allocators](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools)
453#[inline(always)]
454pub fn create_mempool_ext(pool: *const (), redzone: usize, is_zeroed: bool, flags: u8) {
455    do_client_request!(
456        "valgrind::create_mempool_ext",
457        bindings::VR_ValgrindClientRequest::VR_CREATE_MEMPOOL,
458        pool as usize,
459        redzone,
460        usize::from(is_zeroed),
461        flags as usize,
462        0
463    );
464}
465
466/// Destroy a memory pool
467///
468/// This request tells Memcheck that a pool is being torn down. Memcheck then removes all records of
469/// chunks associated with the pool, as well as its record of the pool's existence. While destroying
470/// its records of a mempool, Memcheck resets the redzones of any live chunks in the pool to
471/// `NOACCESS`.
472///
473/// See also [Memory Pools: describing and working with custom
474/// allocators](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools)
475#[inline(always)]
476pub fn destroy_mempool(pool: *const ()) {
477    do_client_request!(
478        "valgrind::destroy_mempool",
479        bindings::VR_ValgrindClientRequest::VR_DESTROY_MEMPOOL,
480        pool as usize,
481        0,
482        0,
483        0,
484        0
485    );
486}
487
488/// Associate a piece of memory with a memory `pool`
489///
490/// This request informs Memcheck that a size-byte chunk has been allocated at `addr`, and
491/// associates the chunk with the specified `pool`. If the `pool` was created with nonzero redzones,
492/// Memcheck will mark the bytes before and after the chunk as `NOACCESS`. If the pool was created
493/// with the `is_zeroed` argument set, Memcheck will mark the chunk as `DEFINED`, otherwise Memcheck
494/// will mark the chunk as `UNDEFINED`.
495///
496/// See also [Memory Pools: describing and working with custom
497/// allocators](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools)
498#[inline(always)]
499pub fn mempool_alloc(pool: *const (), addr: *const (), size: usize) {
500    do_client_request!(
501        "valgrind::mempool_alloc",
502        bindings::VR_ValgrindClientRequest::VR_MEMPOOL_ALLOC,
503        pool as usize,
504        addr as usize,
505        size,
506        0,
507        0
508    );
509}
510
511/// Disassociate a piece of memory from a memory `pool`
512///
513/// This request informs Memcheck that the chunk at `addr` should no longer be considered allocated.
514/// Memcheck will mark the chunk associated with `addr` as `NOACCESS`, and delete its record of the
515/// chunk's existence.
516///
517/// See also [Memory Pools: describing and working with custom
518/// allocators](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools)
519#[inline(always)]
520pub fn mempool_free(pool: *const (), addr: *const ()) {
521    do_client_request!(
522        "valgrind::mempool_free",
523        bindings::VR_ValgrindClientRequest::VR_MEMPOOL_FREE,
524        pool as usize,
525        addr as usize,
526        0,
527        0,
528        0
529    );
530}
531
532/// Disassociate any pieces outside a particular range
533///
534/// This request trims the chunks associated with pool. The request only operates on chunks
535/// associated with pool. Trimming is formally defined as:
536///
537/// All chunks entirely inside the range `addr..(addr+size-1)` are preserved.
538///
539/// All chunks entirely outside the range `addr..(addr+size-1)` are discarded, as though
540/// [`mempool_free`] was called on them.
541///
542/// All other chunks must intersect with the range `addr..(addr+size-1)`; areas outside the
543/// intersection are marked as `NOACCESS`, as though they had been independently freed with
544/// [`mempool_free`].
545///
546/// This is a somewhat rare request, but can be useful in implementing the type of mass-free
547/// operations common in custom LIFO allocators.
548///
549/// See also [Memory Pools: describing and working with custom
550/// allocators](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools)
551#[inline(always)]
552pub fn mempool_trim(pool: *const (), addr: *const (), size: usize) {
553    do_client_request!(
554        "valgrind::mempool_trim",
555        bindings::VR_ValgrindClientRequest::VR_MEMPOOL_TRIM,
556        pool as usize,
557        addr as usize,
558        size,
559        0,
560        0
561    );
562}
563
564/// Resize and/or move a piece associated with a memory pool
565///
566/// This request informs Memcheck that the pool previously anchored at address `pool_a` has moved to
567/// anchor address `pool_b`. This is a rare request, typically only needed if you realloc the header
568/// of a mempool.
569///
570/// No memory-status bits are altered by this request.
571///
572/// See also [Memory Pools: describing and working with custom
573/// allocators](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools)
574#[inline(always)]
575pub fn move_mempool(pool_a: *const (), pool_b: *const ()) {
576    do_client_request!(
577        "valgrind::move_mempool",
578        bindings::VR_ValgrindClientRequest::VR_MOVE_MEMPOOL,
579        pool_a as usize,
580        pool_b as usize,
581        0,
582        0,
583        0
584    );
585}
586
587/// Resize and/or move a piece associated with a memory pool
588///
589/// This request informs Memcheck that the chunk previously allocated at address `addr_a` within
590/// pool has been moved and/or resized, and should be changed to cover the region
591/// `addr_b..(addr_b+size-1)`. This is a rare request, typically only needed if you realloc a
592/// superblock or wish to extend a chunk without changing its memory-status bits.
593///
594/// No memory-status bits are altered by this request.
595///
596/// See also [Memory Pools: describing and working with custom
597/// allocators](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools)
598#[inline(always)]
599pub fn mempool_change(pool: *const (), addr_a: *const (), addr_b: *const (), size: usize) {
600    do_client_request!(
601        "valgrind::mempool_change",
602        bindings::VR_ValgrindClientRequest::VR_MEMPOOL_CHANGE,
603        pool as usize,
604        addr_a as usize,
605        addr_b as usize,
606        size,
607        0
608    );
609}
610
611/// Returns `true` if a mempool exists, else false.
612///
613/// This request informs the caller whether or not Memcheck is currently tracking a mempool at
614/// anchor address pool. It evaluates to `true` when there is a mempool associated with that
615/// address, `false` otherwise. This is a rare request, only useful in circumstances when client
616/// code might have lost track of the set of active mempools.
617///
618/// See also [Memory Pools: describing and working with custom
619/// allocators](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools)
620#[inline(always)]
621pub fn mempool_exists(pool: *const ()) -> bool {
622    do_client_request!(
623        "valgrind::mempool_exists",
624        0,
625        bindings::VR_ValgrindClientRequest::VR_MEMPOOL_EXISTS,
626        pool as usize,
627        0,
628        0,
629        0,
630        0
631    ) != 0
632}
633
634/// Mark a piece of memory as being a stack. Returns a [`super::StackId`]
635///
636/// `start` is the lowest addressable stack byte, `end` is the highest addressable stack byte.
637///
638/// Registers a new stack. Informs Valgrind that the memory range between `start` and `end` is a
639/// unique stack. Returns a stack identifier that can be used with the other [`stack_change`] and
640/// [`stack_deregister`] client requests. Valgrind will use this information to determine if a
641/// change to the stack pointer is an item pushed onto the stack or a change over to a new stack.
642/// Use this if you're using a user-level thread package and are noticing crashes in stack trace
643/// recording or spurious errors from Valgrind about uninitialized memory reads.
644///
645/// Warning: Unfortunately, this client request is unreliable and best avoided.
646#[inline(always)]
647pub fn stack_register(start: usize, end: usize) -> StackId {
648    do_client_request!(
649        "valgrind::stack_register",
650        0,
651        bindings::VR_ValgrindClientRequest::VR_STACK_REGISTER,
652        start,
653        end,
654        0,
655        0,
656        0
657    )
658}
659
660/// Unmark the piece of memory associated with a [`StackId`] as being a stack
661///
662/// Deregisters a previously registered stack. Informs Valgrind that previously registered memory
663/// range with [`StackId`] id is no longer a stack.
664///
665/// Warning: Unfortunately, this client request is unreliable and best avoided.
666#[inline(always)]
667pub fn stack_deregister(stack_id: StackId) {
668    do_client_request!(
669        "valgrind::stack_deregister",
670        bindings::VR_ValgrindClientRequest::VR_STACK_DEREGISTER,
671        stack_id,
672        0,
673        0,
674        0,
675        0
676    );
677}
678
679/// Change the `start` and `end` address of the [`StackId`]
680///
681/// `start` is the new lowest addressable stack byte, `end` is the new highest addressable stack
682/// byte.
683///
684/// Changes a previously registered stack. Informs Valgrind that the previously registered stack
685/// with [`StackId`] has changed its `start` and `end` values. Use this if your user-level thread
686/// package implements stack growth.
687///
688/// Warning: Unfortunately, this client request is unreliable and best avoided.
689#[inline(always)]
690pub fn stack_change(stack_id: StackId, start: usize, end: usize) {
691    do_client_request!(
692        "valgrind::stack_change",
693        bindings::VR_ValgrindClientRequest::VR_STACK_CHANGE,
694        stack_id,
695        start,
696        end,
697        0,
698        0
699    );
700}
701
702/// Loads PDB debug info for `Wine PE image_map`.
703///
704/// # Panics
705///
706/// When the raw file descriptor `fd` is smaller than 0
707#[inline(always)]
708pub fn load_pdb_debuginfo(fd: RawFd, ptr: *const (), total_size: usize, delta: usize) {
709    do_client_request!(
710        "valgrind::load_pdb_debuginfo",
711        bindings::VR_ValgrindClientRequest::VR_LOAD_PDB_DEBUGINFO,
712        fd.try_into().expect("A file descriptor should be >= 0"),
713        ptr as usize,
714        total_size,
715        delta,
716        0
717    );
718}
719
720/// Map a code address to a source file name and line number
721///
722/// `buf64` must point to a 64-byte buffer in the caller's address space. The result will be dumped
723/// in there and is guaranteed to be zero terminated. If no info is found, the first byte is set to
724/// zero.
725#[inline(always)]
726pub fn map_ip_to_srcloc(addr: *const (), buf64: *const ()) -> usize {
727    do_client_request!(
728        "valgrind::map_ip_to_srcloc",
729        0,
730        bindings::VR_ValgrindClientRequest::VR_MAP_IP_TO_SRCLOC,
731        addr as usize,
732        buf64 as usize,
733        0,
734        0,
735        0
736    )
737}
738
739/// Disable error reporting for this thread.
740///
741/// Behaves in a stack like way, so you can safely call this multiple times provided that
742/// [`enable_error_reporting`] is called the same number of times to re-enable reporting. The
743/// first call of this macro disables reporting. Subsequent calls have no effect except to increase
744/// the number of [`enable_error_reporting`] calls needed to re-enable reporting. Child
745/// threads do not inherit this setting from their parents -- they are always created with reporting
746/// enabled.
747#[inline(always)]
748pub fn disable_error_reporting() {
749    do_client_request!(
750        "valgrind::disable_error_reporting",
751        bindings::VR_ValgrindClientRequest::VR_CHANGE_ERR_DISABLEMENT,
752        1,
753        0,
754        0,
755        0,
756        0
757    );
758}
759
760/// Re-enable error reporting
761///
762/// See also [`disable_error_reporting`]
763#[inline(always)]
764pub fn enable_error_reporting() {
765    do_client_request!(
766        "valgrind::enable_error_reporting",
767        bindings::VR_ValgrindClientRequest::VR_CHANGE_ERR_DISABLEMENT,
768        usize::MAX, // The original code in `valgrind.h` used `-1` as value
769        0,
770        0,
771        0,
772        0
773    );
774}
775
776/// Execute a monitor command from the client program
777///
778/// If a connection is opened with GDB, the output will be sent according to the output mode set for
779/// vgdb. If no connection is opened, output will go to the log output. Returns `false` if command
780/// not recognized, `true` otherwise. Note the return value deviates from the original in
781/// `valgrind.h` which returns 1 if the command was not recognized and 0 otherwise.
782///
783/// See also [Valgrind monitor commands][valgrind-monitor]
784///
785/// [valgrind-monitor]:
786/// https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.valgrind-monitor-commands
787#[inline(always)]
788pub fn monitor_command<T>(command: T) -> bool
789where
790    T: AsRef<CStr>,
791{
792    do_client_request!(
793        "valgrind::monitor_command",
794        0,
795        bindings::VR_ValgrindClientRequest::VR_GDB_MONITOR_COMMAND,
796        command.as_ref().as_ptr() as usize,
797        0,
798        0,
799        0,
800        0
801    ) != 1
802}
803
804/// Change the value of a dynamic command line option
805///
806/// The value of some command line options can be changed dynamically while your program is running
807/// under Valgrind. The dynamically changeable options of the Valgrind core and a given tool can be
808/// listed using option --help-dyn-options,
809///
810/// Note that unknown or not dynamically changeable options will cause a warning message to be
811/// output.
812///
813/// See also [Dynamically changing
814/// options](https://valgrind.org/docs/manual/manual-core.html#manual-core.dynopts)
815#[inline(always)]
816pub fn clo_change<T>(option: T)
817where
818    T: AsRef<CStr>,
819{
820    do_client_request!(
821        "valgrind::clo_change",
822        bindings::VR_ValgrindClientRequest::VR_CLO_CHANGE,
823        option.as_ref().as_ptr() as usize,
824        0,
825        0,
826        0,
827        0
828    );
829}