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}