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