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}