Skip to main content

valgrind_requests/
memcheck.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 `memcheck.h` header file with some small
35// adjustments, so above is the original license from `memcheck.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 `memcheck.h` header file
42//!
43//! See also [Memcheck Client
44//! Requests](https://valgrind.org/docs/manual/mc-manual.html#mc-manual.clientreqs)
45
46use core::ffi::CStr;
47
48use super::{
49    bindings, fatal_error, valgrind_do_client_request_expr, valgrind_do_client_request_stmt,
50};
51
52/// The [`BlockHandle`] type as returned by [`create_block`]
53///
54/// You can pass this [`BlockHandle`] to [`discard`]
55pub type BlockHandle = usize;
56
57/// The `LeakCounts` as returned by [`count_leaks`] and [`count_leak_blocks`]
58///
59/// These client request fills in the four fields of [`LeakCounts`] with the number of bytes of
60/// memory found by the previous leak check to be leaked (i.e. the sum of direct leaks and indirect
61/// leaks), dubious, reachable and suppressed.
62#[derive(Debug, Default, PartialEq, Eq, Clone, Hash)]
63pub struct LeakCounts {
64    /// The number of bytes of memory of dubious leaks
65    pub dubious: cty::c_ulong,
66    /// The number of bytes of memory of direct and indirect leaks
67    pub leaked: cty::c_ulong,
68    /// The number of bytes of memory of reachable leaks
69    pub reachable: cty::c_ulong,
70    /// The number of bytes of memory of suppressed leaks
71    pub suppressed: cty::c_ulong,
72}
73
74/// Mark memory `addr` as unaddressable for `len` bytes
75#[inline(always)]
76pub fn make_mem_noaccess(addr: *const (), len: usize) -> usize {
77    do_client_request!(
78        "memcheck::make_mem_noaccess",
79        0,
80        bindings::VR_MemcheckClientRequest::VR_MAKE_MEM_NOACCESS,
81        addr as usize,
82        len,
83        0,
84        0,
85        0
86    )
87}
88
89/// Mark memory at `addr` as addressable but undefined for `len` bytes
90#[inline(always)]
91pub fn make_mem_undefined(addr: *const (), len: usize) -> usize {
92    do_client_request!(
93        "memcheck::make_mem_undefined",
94        0,
95        bindings::VR_MemcheckClientRequest::VR_MAKE_MEM_UNDEFINED,
96        addr as usize,
97        len,
98        0,
99        0,
100        0
101    )
102}
103
104/// Mark memory at `addr` as addressable and defined for `len` bytes.
105#[inline(always)]
106pub fn make_mem_defined(addr: *const (), len: usize) -> usize {
107    do_client_request!(
108        "memcheck::make_mem_defined",
109        0,
110        bindings::VR_MemcheckClientRequest::VR_MAKE_MEM_DEFINED,
111        addr as usize,
112        len,
113        0,
114        0,
115        0
116    )
117}
118
119/// Similar to [`make_mem_defined`] except that addressability is not altered
120///
121/// Bytes which are addressable are marked as defined, but those which are not addressable are left
122/// unchanged.
123#[inline(always)]
124pub fn make_mem_defined_if_addressable(addr: *const (), len: usize) -> usize {
125    do_client_request!(
126        "memcheck::make_mem_defined_if_addressable",
127        0,
128        bindings::VR_MemcheckClientRequest::VR_MAKE_MEM_DEFINED_IF_ADDRESSABLE,
129        addr as usize,
130        len,
131        0,
132        0,
133        0
134    )
135}
136
137/// Create a [`BlockHandle`].
138///
139/// The `desc` is a C string which is included in any messages pertaining to addresses within the
140/// specified memory range. This client request has no other effect on the properties of the memory
141/// range.
142///
143/// The specified address range is associated with the `desc` string. When Memcheck reports
144/// invalid access to an address in the range, it will describe it in terms of this block rather
145/// than in terms of any other block it knows about. Note that the use of this macro does not
146/// actually change the state of memory in any way -- it merely gives a name for the range. At some
147/// point you may want Memcheck to stop reporting errors in terms of the block named by
148/// `create_block`. To make this possible, `create_block` returns a [`BlockHandle`]. You can pass
149/// this [`BlockHandle`] to [`discard`]. After doing so, Valgrind will no longer relate addressing
150/// errors in the specified range to the block.
151#[inline(always)]
152pub fn create_block<T>(addr: *const (), len: usize, desc: T) -> BlockHandle
153where
154    T: AsRef<CStr>,
155{
156    do_client_request!(
157        "memcheck::create_block",
158        0,
159        bindings::VR_MemcheckClientRequest::VR_CREATE_BLOCK,
160        addr as usize,
161        len,
162        desc.as_ref().as_ptr() as usize,
163        0,
164        0
165    )
166}
167
168/// Discard a [`BlockHandle`] previously acquired with [`create_block`]
169///
170/// Returns 1 for an invalid handle, 0 for a valid handle. Passing invalid handles to [`discard`] is
171/// harmless.
172///
173/// See also [`create_block`]
174#[inline(always)]
175pub fn discard(handle: BlockHandle) -> usize {
176    do_client_request!(
177        "memcheck::discard",
178        0,
179        bindings::VR_MemcheckClientRequest::VR_DISCARD,
180        0,
181        handle,
182        0,
183        0,
184        0
185    )
186}
187
188/// Checks that memory at `addr` is addressable for `len` bytes.
189///
190/// If suitable addressibility is not established, Valgrind prints an error message and returns the
191/// address of the first offending byte. Otherwise, it returns zero.
192#[inline(always)]
193pub fn check_mem_is_addressable(addr: *const (), len: usize) -> usize {
194    do_client_request!(
195        "memcheck::check_mem_is_addressable",
196        0,
197        bindings::VR_MemcheckClientRequest::VR_CHECK_MEM_IS_ADDRESSABLE,
198        addr as usize,
199        len,
200        0,
201        0,
202        0
203    )
204}
205
206/// Check that memory at `addr` is addressable and defined for `len` bytes.
207///
208/// If suitable addressibility and definedness are not established, Valgrind prints an error message
209/// and returns the address of the first offending byte. Otherwise, it returns zero.
210#[inline(always)]
211pub fn check_mem_is_defined(addr: *const (), len: usize) -> usize {
212    do_client_request!(
213        "memcheck::check_mem_is_defined",
214        0,
215        bindings::VR_MemcheckClientRequest::VR_CHECK_MEM_IS_DEFINED,
216        addr as usize,
217        len,
218        0,
219        0,
220        0
221    )
222}
223
224/// Use this macro to force the definedness and addressibility of a `value` to be checked.
225///
226/// If suitable addressibility and definedness are not established, Valgrind prints an error message
227/// and returns the address of the first offending byte. Otherwise, it returns zero.
228#[inline(always)]
229pub fn check_value_is_defined<T>(value: &T) -> usize {
230    do_client_request!(
231        "memcheck::check_value_is_defined",
232        0,
233        bindings::VR_MemcheckClientRequest::VR_CHECK_MEM_IS_DEFINED,
234        core::ptr::from_ref::<T>(value) as usize,
235        core::mem::size_of::<T>(),
236        0,
237        0,
238        0
239    )
240}
241
242/// Do a full memory leak check (like `--leak-check=full`) mid-execution
243///
244/// This is useful for incrementally checking for leaks between arbitrary places in the program's
245/// execution.
246#[inline(always)]
247pub fn do_leak_check() {
248    do_client_request!(
249        "memcheck::do_leak_check",
250        bindings::VR_MemcheckClientRequest::VR_DO_LEAK_CHECK,
251        0,
252        0,
253        0,
254        0,
255        0
256    );
257}
258
259/// Same as [`do_leak_check`] but only showing the entries for which there was an increase in leaked
260/// bytes or leaked nr of blocks since the previous leak search.
261#[inline(always)]
262pub fn do_added_leak_check() {
263    do_client_request!(
264        "memcheck::do_added_leak_check",
265        bindings::VR_MemcheckClientRequest::VR_DO_LEAK_CHECK,
266        0,
267        1,
268        0,
269        0,
270        0
271    );
272}
273
274/// Same as [`do_added_leak_check`] but showing entries with increased or decreased leaked
275/// bytes/blocks since previous leak search.
276#[inline(always)]
277pub fn do_changed_leak_check() {
278    do_client_request!(
279        "memcheck::do_changed_leak_check",
280        bindings::VR_MemcheckClientRequest::VR_DO_LEAK_CHECK,
281        0,
282        2,
283        0,
284        0,
285        0
286    );
287}
288
289/// Same as [`do_leak_check`] but only showing new entries i.e. loss records that were not there in
290/// the previous leak search.
291#[inline(always)]
292pub fn do_new_leak_check() {
293    do_client_request!(
294        "memcheck::do_new_leak_check",
295        bindings::VR_MemcheckClientRequest::VR_DO_LEAK_CHECK,
296        0,
297        3,
298        0,
299        0,
300        0
301    );
302}
303
304/// Do a summary memory leak check (like `--leak-check=summary`) mid-execution
305#[inline(always)]
306pub fn do_quick_leak_check() {
307    do_client_request!(
308        "memcheck::do_quick_leak_check",
309        bindings::VR_MemcheckClientRequest::VR_DO_LEAK_CHECK,
310        1,
311        0,
312        0,
313        0,
314        0
315    );
316}
317
318/// Return [`LeakCounts`] found by all previous leak checks
319///
320/// This client request fills in the four fields of [`LeakCounts`] with the number of bytes of
321/// memory found by the previous leak check to be leaked (i.e. the sum of direct leaks and indirect
322/// leaks), dubious, reachable and suppressed.
323///
324/// This is useful in test harness code, after calling [`do_leak_check`] or [`do_quick_leak_check`]
325#[inline(always)]
326pub fn count_leaks() -> LeakCounts {
327    let leaks = LeakCounts::default();
328    do_client_request!(
329        "memcheck::count_leaks",
330        bindings::VR_MemcheckClientRequest::VR_COUNT_LEAKS,
331        core::ptr::addr_of!(leaks.leaked) as usize,
332        core::ptr::addr_of!(leaks.dubious) as usize,
333        core::ptr::addr_of!(leaks.reachable) as usize,
334        core::ptr::addr_of!(leaks.suppressed) as usize,
335        0
336    );
337    leaks
338}
339
340/// Identical to [`count_leaks`] except that it returns the number of blocks rather than the number
341/// of bytes in each category.
342#[inline(always)]
343pub fn count_leak_blocks() -> LeakCounts {
344    let leaks = LeakCounts::default();
345    do_client_request!(
346        "memcheck::count_leak_blocks",
347        bindings::VR_MemcheckClientRequest::VR_COUNT_LEAK_BLOCKS,
348        core::ptr::addr_of!(leaks.leaked) as usize,
349        core::ptr::addr_of!(leaks.dubious) as usize,
350        core::ptr::addr_of!(leaks.reachable) as usize,
351        core::ptr::addr_of!(leaks.suppressed) as usize,
352        0
353    );
354    leaks
355}
356
357/// Allow you to get the V (validity) bits for an address range `[addr...addr+len-1]`
358///
359/// The validity data is copied into the provided `bits` slice.
360///
361/// Return values:
362/// * 0   if not running on valgrind
363/// * 1   success
364/// * 2   [previously indicated unaligned arrays; these are now allowed]
365/// * 3   if any parts of `addr`/`bits` are not addressable.
366///
367/// The metadata is not copied in cases 0, 2 or 3, so it should be impossible to segfault your
368/// system by using this call.
369///
370/// You should probably only set V bits with [`set_vbits`] that you have got with this client
371/// request.
372///
373/// Only for those who really know what they are doing.
374#[inline(always)]
375pub fn get_vbits(addr: *const (), bits: &mut [u8], len: usize) -> usize {
376    do_client_request!(
377        "memcheck::get_vbits",
378        0,
379        bindings::VR_MemcheckClientRequest::VR_GET_VBITS,
380        addr as usize,
381        bits.as_ptr() as usize,
382        len,
383        0,
384        0
385    )
386}
387
388/// Allow you to set the V (validity) bits for an address range `[addr...addr+len-1]`
389///
390/// The validity data is copied from the provided `bits` slice.
391///
392/// Return values:
393/// * 0   if not running on valgrind
394/// * 1   success
395/// * 2   [previously indicated unaligned arrays;  these are now allowed]
396/// * 3   if any parts of `addr`/`bits` are not addressable.
397///
398/// The metadata is not copied in cases 0, 2 or 3, so it should be impossible to segfault your
399/// system by using this call.
400///
401/// You should probably only set V bits with `set_vbits` that you have got with [`get_vbits`].
402///
403/// Only for those who really know what they are doing.
404#[inline(always)]
405pub fn set_vbits(addr: *const (), bits: &[u8], len: usize) -> usize {
406    do_client_request!(
407        "memcheck::set_vbits",
408        0,
409        bindings::VR_MemcheckClientRequest::VR_SET_VBITS,
410        addr as usize,
411        bits.as_ptr() as usize,
412        len,
413        0,
414        0
415    )
416}
417
418/// Disables reporting of addressing errors in the specified address range.
419#[inline(always)]
420pub fn disable_addr_error_reporting_in_range(addr: *const (), len: usize) -> usize {
421    do_client_request!(
422        "memcheck::disable_addr_error_reporting_in_range",
423        0,
424        bindings::VR_MemcheckClientRequest::VR_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE,
425        addr as usize,
426        len,
427        0,
428        0,
429        0
430    )
431}
432
433/// Enables reporting of addressing errors in the specified address range.
434#[inline(always)]
435pub fn enable_addr_error_reporting_in_range(addr: *const (), len: usize) -> usize {
436    do_client_request!(
437        "memcheck::enable_addr_error_reporting_in_range",
438        0,
439        bindings::VR_MemcheckClientRequest::VR_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE,
440        addr as usize,
441        len,
442        0,
443        0,
444        0
445    )
446}