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}