valgrind_requests/lib.rs
1//! Idiomatic Rust bindings for [Valgrind's Client Request Mechanism][client-requests] with
2//! zero-indirection execution and zero-cost fallback.
3//!
4//!
5//! Valgrind has a trapdoor mechanism via which the client program can pass all manner of requests
6//! and queries to Valgrind and the current tool. The so-called client requests are provided to
7//! allow you to tell Valgrind facts about the behavior of your program, and also to make queries.
8//! In particular, your program can tell Valgrind about things that it otherwise would not know,
9//! leading to better results.
10//!
11//! # Installation/Building
12//!
13//! ```toml
14//! [dependencies]
15//! valgrind-requests = "1.0"
16//! ```
17//!
18//! or
19//!
20//! ```shell
21//! cargo add valgrind-requests
22//! ```
23//!
24//! `valgrind-requests` does not depend on any specific version of Valgrind. However, not all client
25//! requests are available in all Valgrind versions and this crate will abort the execution
26//! producing a panic message if a client request is used but not available in the header
27//! file.
28//!
29//! The client requests need to be built with the Valgrind header files. Usually, these header files
30//! are installed by your distribution's package manager with the Valgrind package into a global
31//! include path, and you don't need to do anything. Note that the used headers need to match the
32//! used Valgrind version.
33//!
34//! If you encounter problems because the Valgrind header files cannot be found, first ensure you
35//! have installed Valgrind and your package manager's package includes the header files. If not or
36//! you use a custom build of Valgrind, you can point the `VALGRIND_REQUESTS_VALGRIND_INCLUDE` or
37//! the `VALGRIND_REQUESTS_<triple>_VALGRIND_INCLUDE` environment variables to the include path
38//! where the Valgrind headers can be found. For example, if the Valgrind header files reside in
39//! `/home/foo/repo/valgrind/{valgrind.h, callgrind.h, ...}`, then the environment variable has to
40//! point to `VALGRIND_REQUESTS_VALGRIND_INCLUDE=/home/foo/repo` and not
41//! `VALGRIND_REQUESTS_VALGRIND_INCLUDE=/home/foo/repo/valgrind`.
42//!
43//! # Module Organization
44//!
45//! The client requests are organized into modules representing the source header file. So, if you
46//! search for a client request originating from the `valgrind.h` header file, the client request
47//! can be found in the [`valgrind`] module. `valgrind-requests` is a complete implementation of all
48//! client requests which can be found in the original header files.
49//!
50//! | Module | Header | Description |
51//! | ------ | ------ | ----------- |
52//! | [`valgrind`] | `valgrind.h` | Core client requests ([The Client request mechanism][client-requests]) |
53//! | [`memcheck`] | `memcheck.h` | [Memcheck: a memory error detector][memcheck-docs] |
54//! | [`callgrind`] | `callgrind.h` | [Callgrind: a call-graph generating cache and branch prediction profiler][callgrind-docs] |
55//! | [`cachegrind`] | `cachegrind.h` | [Cachegrind: a high-precision tracing profiler][cachegrind-docs] |
56//! | [`helgrind`] | `helgrind.h` | [Helgrind: a thread error detector][helgrind-docs] |
57//! | [`drd`] | `drd.h` | [DRD: a thread error detector][drd-docs] |
58//! | [`dhat`] | `dhat.h` | [DHAT: a dynamic heap analysis tool][dhat-docs] |
59//!
60//! Instead of using macros like in Valgrind we're using functions and small letter names, stripping
61//! the prefix if it is equal to the enclosing module. For example the client request
62//! `RUNNING_ON_VALGRIND` from the `valgrind.h` file equals [`valgrind::running_on_valgrind`]
63//! and `VALGRIND_COUNT_ERRORS` from the same `valgrind.h` header file equals
64//! [`valgrind::count_errors`].
65//!
66//! The only exception to this rule are the [`valgrind_printf`] macro and its descendants like
67//! [`valgrind_printf_unchecked`] which can be found in the crate root.
68//!
69//! # Features
70//!
71//! Core client request APIs are `no_std` compatible. The `std` feature which implies the `alloc`
72//! feature are enabled by default.
73//!
74//! This crate provides two execution feature levels:
75//!
76//! - **`act`** *(default)*: Enables actual execution of client requests when running under
77//! Valgrind. Implies `stubs`.
78//! - **`stubs`**: Enables the same public API surface and build-time code generation, but all
79//! client requests compile to no-ops that return default values. The compiler will optimize them
80//! away entirely, making this a zero-cost option suitable for production code.
81//!
82//! Formatting convenience macros such as [`valgrind_printf`] and [`valgrind_println`] require the
83//! **`alloc`** feature because they allocate owned C strings. In allocation-free builds, you can
84//! use [`valgrind_print`] or [`valgrind_print_backtrace`] instead.
85//!
86//! To use the zero-cost fallback, for example if you want to use the client requests for tests or
87//! benchmarks and need to make annotations in production code:
88//!
89//! ```toml
90//! [dependencies]
91//! valgrind-requests = { version = "1.0", default-features = false, features = ["stubs"] }
92//!
93//! [dev-dependencies]
94//! valgrind-requests = { version = "1.0" }
95//! ```
96//!
97//! The stubs compile down to nothing and your production code is as performant as without any
98//! annotations. If your production code uses the formatting convenience macros, enable both `stubs`
99//! and `alloc` with `features = ["stubs", "alloc"]`.
100//!
101//! # Performance and implementation details
102//!
103//! If possible, client requests execute with zero indirection and the same overhead as the original
104//! Valgrind C macros usable [even in high performance code][client-requests]. On
105//! Valgrind-supported platforms for which zero-indirection isn't implemented by us, a native C FFI
106//! binding is used which introduces at least an additional frame on the stack and the costs for the
107//! function call. That means all targets covered by Valgrind are also covered by
108//! `valgrind-requests`. Targets not supported by Valgrind produce a compile error.
109//!
110//! | Target | Zero-indirection | Notes |
111//! | -------------------- | ---------------- | --------------------------------------------- |
112//! | `x86_64/linux` | yes | - |
113//! | `x86_64/android` | yes | except the x32 ABI |
114//! | `x86_64/freebsd` | yes | - |
115//! | `x86_64/macos` | yes | the versions supported by Valgrind |
116//! | `x86_64/windows+gnu` | yes | - |
117//! | `x86_64/solaris` | yes | - |
118//! | `x86/linux` | yes | - |
119//! | `x86/android` | yes | - |
120//! | `x86/freebsd` | yes | - |
121//! | `x86/macos` | yes | the versions supported by Valgrind |
122//! | `x86/windows+gnu` | yes | - |
123//! | `x86/solaris` | yes | - |
124//! | `arm/linux` | yes | - |
125//! | `arm/android` | yes | - |
126//! | `aarch64/linux` | yes | - |
127//! | `aarch64/android` | yes | - |
128//! | `aarch64/freebsd` | yes | - |
129//! | `aarch64/macos` | yes | [LouisBrunner/valgrind-macos][valgrind-macos] |
130//! | `riscv64/linux` | yes | - |
131//! | `s390x/linux` | yes | - |
132//! | `powerpc/linux` | yes | rust >= 1.95.0 |
133//! | `powerpc64/linux` | yes | rust >= 1.95.0 |
134//! | `powerpc64le/linux` | yes | rust >= 1.95.0 |
135//! | `mips32/linux` | no | no rust inline assembly available |
136//! | `mips64/linux` | no | no rust inline assembly available |
137//! | `nanomips/linux` | no | no zero-indirection planned |
138//! | `x86/windows+msvc` | no | no zero-indirection planned |
139//!
140//! To disable the native C FFI binding as fallback you can set the environment variable
141//! `VALGRIND_REQUESTS_STRATEGY=strict` (possible values are: `strict`, `fallback`).
142//!
143//! # Sources and additional documentation
144//!
145//! A lot of the library documentation of the client requests within this module and its submodules
146//! is taken from the online manual and the Valgrind header files. For more details see also [The
147//! Client Request mechanism][client-requests]
148//!
149//! [callgrind-docs]:
150//! https://valgrind.org/docs/manual/cl-manual.html#cl-manual.clientrequests
151//! [cachegrind-docs]:
152//! https://valgrind.org/docs/manual/cg-manual.html#cg-manual.clientrequests
153//! [client-requests]:
154//! https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
155//! [dhat-docs]: https://valgrind.org/docs/manual/dh-manual.html
156//! [drd-docs]:
157//! https://valgrind.org/docs/manual/drd-manual.html#drd-manual.clientreqs
158//! [helgrind-docs]:
159//! https://valgrind.org/docs/manual/hg-manual.html#hg-manual.client-requests
160//! [memcheck-docs]:
161//! https://valgrind.org/docs/manual/mc-manual.html#mc-manual.clientreqs
162//! [valgrind-macos]: https://github.com/LouisBrunner/valgrind-macos
163
164#![cfg_attr(docsrs, feature(doc_cfg))]
165#![doc(test(attr(warn(unused))))]
166#![doc(test(attr(allow(unused_extern_crates))))]
167#![cfg_attr(not(feature = "std"), no_std)]
168#![expect(clippy::arbitrary_source_item_ordering)]
169
170#[cfg(feature = "alloc")]
171#[doc(hidden)]
172pub extern crate alloc as __alloc;
173#[cfg(feature = "std")]
174extern crate std;
175
176#[cfg(not(feature = "stubs"))]
177compile_error!("valgrind-requests requires either the `stubs` or `act` feature");
178
179/// Returns `true` if a client request is defined and available in the used Valgrind version.
180///
181/// For internal use only!
182///
183/// We do this check to avoid incompatibilities with older Valgrind versions which might not have
184/// all client requests available we're offering.
185///
186/// We're only using constant values known at compile time, which the compiler will finally optimize
187/// away, so this macro costs us nothing.
188macro_rules! is_def {
189 ($user_req:path) => {{ $user_req as cty::c_uint > 0x1000 }};
190}
191
192/// The macro which uses [`valgrind_do_client_request_stmt`] or [`valgrind_do_client_request_expr`]
193/// to execute the client request.
194///
195/// For internal use only!
196///
197/// This macro has two forms: The first takes 7 arguments `name, request, arg1, ..., arg5` and
198/// returns `()`. The expanded macro of this form calls [`valgrind_do_client_request_stmt`]. The
199/// second first has 8 arguments `name, default, request, arg1, ..., arg5` returning a `usize`. The
200/// expanded macro of this form calls [`valgrind_do_client_request_expr`].
201///
202/// Both forms will raise a [`fatal_error`] in case the [`is_def`] macro returns false.
203macro_rules! do_client_request {
204 ($name:literal, $request:path, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr) => {{
205 if is_def!($request) {
206 valgrind_do_client_request_stmt(
207 $request as cty::c_uint,
208 $arg1,
209 $arg2,
210 $arg3,
211 $arg4,
212 $arg5,
213 );
214 } else {
215 fatal_error($name);
216 }
217 }};
218 (
219 $name:literal,
220 $default:expr,
221 $request:path,
222 $arg1:expr,
223 $arg2:expr,
224 $arg3:expr,
225 $arg4:expr,
226 $arg5:expr
227 ) => {{
228 if is_def!($request) {
229 valgrind_do_client_request_expr(
230 $default,
231 $request as cty::c_uint,
232 $arg1,
233 $arg2,
234 $arg3,
235 $arg4,
236 $arg5,
237 )
238 } else {
239 fatal_error($name);
240 }
241 }};
242}
243
244/// Convenience macro to create a `\0`-byte terminated `alloc::ffi::CString` from a literal string
245///
246/// This macro requires the `alloc` feature. In allocation-free builds, use [`valgrind_print`]
247/// instead.
248///
249/// The string literal passed to this macro must not contain or end with a `\0`-byte. If you need a
250/// checked version of `alloc::ffi::CString` you can use `alloc::ffi::CString::new`.
251///
252/// # Safety
253///
254/// This macro is unsafe but convenient and efficient. It is your responsibility to ensure that the
255/// input string literal does not contain any `\0` bytes.
256#[cfg(feature = "alloc")]
257#[macro_export]
258macro_rules! cstring {
259 ($string:literal) => {{
260 $crate::__alloc::ffi::CString::from_vec_with_nul_unchecked(
261 concat!($string, "\0").as_bytes().to_vec(),
262 )
263 }};
264}
265
266/// Convenience macro to create a `\0`-byte terminated `alloc::ffi::CString` from a format string
267///
268/// This macro requires the `alloc` feature. In allocation-free builds, use [`valgrind_print`]
269/// instead.
270///
271/// The format string passed to this macro must not contain or end with a `\0`-byte.
272///
273/// # Safety
274///
275/// The same safety conditions as to the [`cstring`] macro apply here
276#[cfg(feature = "alloc")]
277#[macro_export]
278macro_rules! format_cstring {
279 ($($args:tt)*) => {{
280 $crate::__alloc::ffi::CString::from_vec_with_nul_unchecked(
281 $crate::__alloc::format!("{}\0", format_args!($($args)*)).into_bytes()
282 )
283 }};
284}
285
286cfg_if! {
287 if #[cfg(feature = "act")] {
288 /// Prints to the Valgrind log.
289 ///
290 /// This macro requires the `alloc` feature. In allocation-free builds, use
291 /// [`valgrind_print`] instead.
292 ///
293 /// This macro is a safe variant of the `VALGRIND_PRINTF` function, checking for `\0` bytes
294 /// in the formatting string. However, if you're sure there are no `\0` bytes present you
295 /// can safely use [`crate::valgrind_printf_unchecked`] which performs better compared to
296 /// this macro.
297 #[cfg(feature = "alloc")]
298 #[macro_export]
299 macro_rules! valgrind_printf {
300 ($($args:tt)*) => {{
301 match $crate::__alloc::ffi::CString::from_vec_with_nul(
302 $crate::__alloc::format!("{}\0", format_args!($($args)*)).into_bytes()
303 ) {
304 Ok(c_string) => {
305 unsafe {
306 $crate::__valgrind_print(
307 c_string.as_ptr()
308 );
309 }
310 Ok(())
311 },
312 Err(error) => Err(
313 $crate::error::ClientRequestError::from(error)
314 )
315 }
316 }};
317 }
318
319 /// Prints to the Valgrind log.
320 ///
321 /// This macro requires the `alloc` feature. In allocation-free builds, use
322 /// [`valgrind_print`] instead.
323 ///
324 /// Use this macro only if you are sure there are no `\0`-bytes in the formatted string. If
325 /// unsure use the safe [`crate::valgrind_printf`] variant.
326 ///
327 /// This variant performs better than [`crate::valgrind_printf`].
328 #[cfg(feature = "alloc")]
329 #[macro_export]
330 macro_rules! valgrind_printf_unchecked {
331 ($($args:tt)*) => {{
332 let string = $crate::__alloc::format!("{}\0", format_args!($($args)*));
333 $crate::__valgrind_print(
334 string.as_ptr() as *const $crate::__cty::c_char
335 );
336 }};
337 }
338
339 /// Prints to the Valgrind log ending with a newline.
340 ///
341 /// This macro requires the `alloc` feature. In allocation-free builds, use
342 /// [`valgrind_print`] instead.
343 ///
344 /// See also [`crate::valgrind_printf`]
345 #[cfg(feature = "alloc")]
346 #[macro_export]
347 macro_rules! valgrind_println {
348 () => { $crate::valgrind_printf!("\n") };
349 ($($arg:tt)*) => {{
350 match $crate::__alloc::ffi::CString::from_vec_with_nul(
351 $crate::__alloc::format!("{}\n\0", format_args!($($arg)*)).into_bytes()
352 ) {
353 Ok(c_string) => {
354 unsafe {
355 $crate::__valgrind_print(
356 c_string.as_ptr()
357 );
358 }
359 Ok(())
360 },
361 Err(error) => Err(
362 $crate::error::ClientRequestError::from(error)
363 )
364 }
365 }};
366 }
367
368 /// Prints to the Valgrind log ending with a newline.
369 ///
370 /// This macro requires the `alloc` feature. In allocation-free builds, use
371 /// [`valgrind_print`] instead.
372 ///
373 /// See also [`crate::valgrind_printf_unchecked`]
374 #[cfg(feature = "alloc")]
375 #[macro_export]
376 macro_rules! valgrind_println_unchecked {
377 () => { $crate::valgrind_printf_unchecked!("\n") };
378 ($($args:tt)*) => {{
379 let string = $crate::__alloc::format!("{}\n\0", format_args!($($args)*));
380 $crate::__valgrind_print(
381 string.as_ptr() as *const $crate::__cty::c_char
382 );
383 }};
384 }
385
386 /// Prints to the Valgrind log with a backtrace.
387 ///
388 /// This macro requires the `alloc` feature. In allocation-free builds, use
389 /// [`valgrind_print_backtrace`] instead.
390 ///
391 /// See also [`crate::valgrind_printf`]
392 #[cfg(feature = "alloc")]
393 #[macro_export]
394 macro_rules! valgrind_printf_backtrace {
395 ($($arg:tt)*) => {{
396 match $crate::__alloc::ffi::CString::from_vec_with_nul(
397 $crate::__alloc::format!("{}\0", format_args!($($arg)*)).into_bytes()
398 ) {
399 Ok(c_string) => {
400 unsafe {
401 $crate::__valgrind_print_backtrace(
402 c_string.as_ptr()
403 );
404 }
405 Ok(())
406 },
407 Err(error) => Err(
408 $crate::error::ClientRequestError::from(error)
409 )
410 }
411 }};
412 }
413
414 /// Prints to the Valgrind log with a backtrace.
415 ///
416 /// This macro requires the `alloc` feature. In allocation-free builds, use
417 /// [`valgrind_print_backtrace`] instead.
418 ///
419 /// See also [`crate::valgrind_printf_unchecked`]
420 #[cfg(feature = "alloc")]
421 #[macro_export]
422 macro_rules! valgrind_printf_backtrace_unchecked {
423 ($($arg:tt)*) => {{
424 let string = $crate::__alloc::format!("{}\0", format_args!($($arg)*));
425 $crate::__valgrind_print_backtrace(
426 string.as_ptr() as *const $crate::__cty::c_char
427 );
428 }};
429 }
430
431 /// Prints to the Valgrind log with a backtrace, ending the formatted string with a newline.
432 ///
433 /// This macro requires the `alloc` feature. In allocation-free builds, use
434 /// [`valgrind_print_backtrace`] instead.
435 ///
436 /// See also [`crate::valgrind_printf`]
437 #[cfg(feature = "alloc")]
438 #[macro_export]
439 macro_rules! valgrind_println_backtrace {
440 () => { $crate::valgrind_printf_backtrace!("\n") };
441 ($($arg:tt)*) => {{
442 match $crate::__alloc::ffi::CString::from_vec_with_nul(
443 $crate::__alloc::format!("{}\n\0", format_args!($($arg)*)).into_bytes()
444 ) {
445 Ok(c_string) => {
446 unsafe {
447 $crate::__valgrind_print_backtrace(
448 c_string.as_ptr()
449 );
450 }
451 Ok(())
452 },
453 Err(error) => Err(
454 $crate::error::ClientRequestError::from(error)
455 )
456 }
457 }};
458 }
459
460 /// Prints to the Valgrind log with a backtrace, ending the formatted string with a newline.
461 ///
462 /// This macro requires the `alloc` feature. In allocation-free builds, use
463 /// [`valgrind_print_backtrace`] instead.
464 ///
465 /// See also [`crate::valgrind_printf_unchecked`]
466 #[cfg(feature = "alloc")]
467 #[macro_export]
468 macro_rules! valgrind_println_backtrace_unchecked {
469 () => { $crate::valgrind_printf_backtrace_unchecked!("\n") };
470 ($($arg:tt)*) => {{
471 let string = $crate::__alloc::format!("{}\n\0", format_args!($($arg)*));
472 unsafe {
473 $crate::__valgrind_print_backtrace(
474 string.as_ptr() as *const $crate::__cty::c_char
475 );
476 }
477 }};
478 }
479 } else {
480 /// No-op variant of [`valgrind_printf`] for stub builds.
481 ///
482 /// This macro requires the `alloc` feature to preserve the same fallible API as the active
483 /// formatting macro. In allocation-free builds, use [`valgrind_print`] instead.
484 ///
485 /// This macro is a safe variant of the `VALGRIND_PRINTF` function, checking for `\0` bytes
486 /// in the formatting string. However, if you're sure there are no `\0` bytes present you
487 /// can safely use [`crate::valgrind_printf_unchecked`] which performs better compared to
488 /// this macro.
489 #[cfg(feature = "alloc")]
490 #[macro_export]
491 macro_rules! valgrind_printf {
492 ($($arg:tt)*) => {{
493 let res: Result<(), $crate::error::ClientRequestError> = Ok(());
494 res
495 }};
496 }
497
498 /// No-op variant of [`valgrind_printf_unchecked`] for stub builds.
499 ///
500 /// This macro requires the `alloc` feature. In allocation-free builds, use
501 /// [`valgrind_print`] instead.
502 ///
503 /// Use this macro only if you are sure there are no `\0`-bytes in the formatted string. If
504 /// unsure use the safe [`crate::valgrind_printf`] variant.
505 ///
506 /// This variant performs better than [`crate::valgrind_printf`].
507 #[cfg(feature = "alloc")]
508 #[macro_export]
509 macro_rules! valgrind_printf_unchecked {
510 ($($arg:tt)*) => {{ $crate::__no_op() }};
511 }
512
513 /// No-op variant of [`valgrind_println`] for stub builds.
514 ///
515 /// This macro requires the `alloc` feature. In allocation-free builds, use
516 /// [`valgrind_print`] instead.
517 ///
518 /// See also [`crate::valgrind_printf`]
519 #[cfg(feature = "alloc")]
520 #[macro_export]
521 macro_rules! valgrind_println {
522 ($($arg:tt)*) => {{
523 let res: Result<(), $crate::error::ClientRequestError> = Ok(());
524 res
525 }};
526 }
527
528 /// No-op variant of [`valgrind_println_unchecked`] for stub builds.
529 ///
530 /// This macro requires the `alloc` feature. In allocation-free builds, use
531 /// [`valgrind_print`] instead.
532 ///
533 /// See also [`crate::valgrind_printf_unchecked`]
534 #[cfg(feature = "alloc")]
535 #[macro_export]
536 macro_rules! valgrind_println_unchecked {
537 ($($arg:tt)*) => {{ $crate::__no_op() }};
538 }
539
540 /// No-op variant of [`valgrind_printf_backtrace`] for stub builds.
541 ///
542 /// This macro requires the `alloc` feature. In allocation-free builds, use
543 /// [`valgrind_print_backtrace`] instead.
544 ///
545 /// See also [`crate::valgrind_printf`]
546 #[cfg(feature = "alloc")]
547 #[macro_export]
548 macro_rules! valgrind_printf_backtrace {
549 ($($arg:tt)*) => {{
550 let res: Result<(), $crate::error::ClientRequestError> = Ok(());
551 res
552 }};
553 }
554
555 /// No-op variant of [`valgrind_printf_backtrace_unchecked`] for stub builds.
556 ///
557 /// This macro requires the `alloc` feature. In allocation-free builds, use
558 /// [`valgrind_print_backtrace`] instead.
559 ///
560 /// See also [`crate::valgrind_printf_unchecked`]
561 #[cfg(feature = "alloc")]
562 #[macro_export]
563 macro_rules! valgrind_printf_backtrace_unchecked {
564 ($($arg:tt)*) => {{ $crate::__no_op() }};
565 }
566
567 /// No-op variant of [`valgrind_println_backtrace`] for stub builds.
568 ///
569 /// This macro requires the `alloc` feature. In allocation-free builds, use
570 /// [`valgrind_print_backtrace`] instead.
571 ///
572 /// See also [`crate::valgrind_printf`]
573 #[cfg(feature = "alloc")]
574 #[macro_export]
575 macro_rules! valgrind_println_backtrace {
576 ($($arg:tt)*) => {{
577 let res: Result<(), $crate::error::ClientRequestError> = Ok(());
578 res
579 }};
580 }
581
582 /// No-op variant of [`valgrind_println_backtrace_unchecked`] for stub builds.
583 ///
584 /// This macro requires the `alloc` feature. In allocation-free builds, use
585 /// [`valgrind_print_backtrace`] instead.
586 ///
587 /// See also [`crate::valgrind_printf_unchecked`]
588 #[cfg(feature = "alloc")]
589 #[macro_export]
590 macro_rules! valgrind_println_backtrace_unchecked {
591 ($($arg:tt)*) => {{ $crate::__no_op() }};
592 }
593 }
594}
595
596mod arch;
597mod bindings;
598pub mod cachegrind;
599pub mod callgrind;
600pub mod dhat;
601pub mod drd;
602#[cfg(feature = "alloc")]
603pub mod error;
604pub mod helgrind;
605pub mod memcheck;
606#[cfg(feature = "act")]
607mod native_bindings;
608pub mod valgrind;
609use core::ffi::CStr;
610
611use arch::imp::valgrind_do_client_request_expr;
612use arch::valgrind_do_client_request_stmt;
613use cfg_if::cfg_if;
614#[doc(hidden)]
615pub use cty as __cty;
616
617/// The `ThreadId` is used by some client requests to represent the `tid` which Valgrind uses or
618/// returns
619///
620/// This type has no relationship to `std::thread::ThreadId`!
621pub type ThreadId = usize;
622
623/// The `StackId` is used and returned by some client requests and represents an id on Valgrind's
624/// stack
625pub type StackId = usize;
626
627/// The raw file descriptor number
628///
629/// This type has no relationship to the standard library type definition of `RawFd` besides they
630/// are wrapping the same type on unix systems.
631pub type RawFd = cty::c_int;
632
633/// Valgrind's version number from the `valgrind.h` file
634///
635/// Note that the version numbers were introduced at Valgrind version 3.6 and so would not exist in
636/// version 3.5 or earlier. `VALGRIND_VERSION` is None is this case, else it is a tuple `(MAJOR,
637/// MINOR)`
638pub const VALGRIND_VERSION: Option<(u32, u32)> = {
639 if bindings::VR_VALGRIND_MAJOR == 0 {
640 None
641 } else {
642 Some((bindings::VR_VALGRIND_MAJOR, bindings::VR_VALGRIND_MINOR))
643 }
644};
645
646fn fatal_error(func: &str) -> ! {
647 if let Some((major, minor)) = VALGRIND_VERSION {
648 panic!(
649 "{0}: FATAL: {0}::{func} not available! To be able to use this client request, a \
650 newer Valgrind version is required. The detected Valgrind version of the \
651 `valgrind.h` header file is {major}.{minor}. Aborting...",
652 module_path!(),
653 );
654 } else {
655 panic!(
656 "{0}: FATAL: {0}::{func} not available! The Valgrind headers could not be found or \
657 the Valgrind version is too old. Check your Valgrind installation or set \
658 VALGRIND_REQUESTS_VALGRIND_INCLUDE to the Valgrind include path. Aborting...",
659 module_path!(),
660 );
661 }
662}
663
664cfg_if! {
665 if #[cfg(feature = "act")] {
666 /// Prints a C string to the Valgrind log.
667 ///
668 /// This function is the allocation-free equivalent of [`valgrind_printf_unchecked`]. It
669 /// accepts any value that can be borrowed as a [`CStr`], so it can be used in `no_std`
670 /// builds without the `alloc` feature. The stub implementation of this function is a no-op
671 /// and compiles away.
672 ///
673 /// The provided string must be NUL-terminated and must not contain interior NUL bytes, as
674 /// required by [`CStr`].
675 ///
676 /// # Examples
677 ///
678 /// ```rust
679 /// valgrind_requests::valgrind_print(c"hello from Valgrind\n");
680 /// ```
681 #[inline]
682 pub fn valgrind_print<T>(c_string: T)
683 where
684 T: AsRef<CStr>,
685 {
686 // SAFETY: `CStr` guarantees a valid NUL-terminated byte sequence for the duration of
687 // the call.
688 unsafe { __valgrind_print(c_string.as_ref().as_ptr()) }
689 }
690
691 /// Prints a C string with a backtrace to the Valgrind log.
692 ///
693 /// This function is the allocation-free equivalent of
694 /// [`valgrind_printf_backtrace_unchecked`]. It accepts any value that can be borrowed as a
695 /// [`CStr`], so it can be used in `no_std` builds without the `alloc` feature. The stub
696 /// implementation of this function is a no-op and compiles away.
697 ///
698 /// The provided string must be NUL-terminated and must not contain interior NUL bytes, as
699 /// required by [`CStr`].
700 ///
701 /// # Examples
702 ///
703 /// ```rust
704 /// valgrind_requests::valgrind_print_backtrace(c"important checkpoint\n");
705 /// ```
706 #[inline]
707 pub fn valgrind_print_backtrace<T>(c_string: T)
708 where
709 T: AsRef<CStr>,
710 {
711 // SAFETY: `CStr` guarantees a valid NUL-terminated byte sequence for the duration of
712 // the call.
713 unsafe { __valgrind_print_backtrace(c_string.as_ref().as_ptr()) }
714 }
715 } else {
716 /// No-op variant of [`valgrind_print`] for stub builds.
717 ///
718 /// This function preserves the same allocation-free API surface as active builds and
719 /// compiles away entirely.
720 #[inline]
721 pub fn valgrind_print<T>(_c_string: T)
722 where
723 T: AsRef<CStr>,
724 {}
725
726 /// No-op variant of [`valgrind_print_backtrace`] for stub builds.
727 ///
728 /// This function preserves the same allocation-free API surface as active builds and
729 /// compiles away entirely.
730 #[inline]
731 pub fn valgrind_print_backtrace<T>(_c_string: T)
732 where
733 T: AsRef<CStr>,
734 {}
735 }
736}
737
738#[cfg(feature = "act")]
739#[doc(hidden)]
740#[inline(always)]
741pub unsafe fn __valgrind_print(ptr: *const cty::c_char) {
742 // SAFETY: The safety of this function must be ensured by the caller
743 unsafe {
744 native_bindings::valgrind_printf(ptr);
745 }
746}
747
748#[cfg(feature = "act")]
749#[doc(hidden)]
750#[inline(always)]
751pub unsafe fn __valgrind_print_backtrace(ptr: *const cty::c_char) {
752 // SAFETY: The safety of this function must be ensured by the caller
753 unsafe {
754 native_bindings::valgrind_printf_backtrace(ptr);
755 }
756}
757
758#[doc(hidden)]
759#[inline(always)]
760pub unsafe fn __no_op() {}