iai_callgrind/client_requests/
mod.rs

1//! The `iai-callgrind` rustified interface to [Valgrind's Client Request
2//! Mechanism](https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq)
3//!
4//! You can use these methods to manipulate and query Valgrind's execution inside `iai-callgrind`
5//! benchmarks or your own programs.
6//!
7//! Valgrind has a trapdoor mechanism via which the client program can pass all manner of requests
8//! and queries to Valgrind and the current tool. The so-called client requests are provided to
9//! allow you to tell Valgrind facts about the behavior of your program, and also to make queries.
10//! In particular, your program can tell Valgrind about things that it otherwise would not know,
11//! leading to better results.
12//!
13//! # Building
14//!
15//! The client requests need to be built with the valgrind header files. Usually, these header files
16//! are installed by your distribution's package manager with the valgrind package into a global
17//! include path, and you don't need to do anything but activating the `client_requests` feature
18//! (see below) of the `iai-callgrind` dependency.
19//!
20//! If you encounter problems because the valgrind header files cannot be found, first ensure you
21//! have installed valgrind and your package manager's package includes the header files. If not or
22//! you use a custom build of valgrind, you can point the `IAI_CALLGRIND_VALGRIND_INCLUDE` or the
23//! `IAI_CALLGRIND_<triple>_VALGRIND_INCLUDE` environment variables to the include path where the
24//! valgrind headers can be found. The include directive used by `iai-callgrind` is `#include
25//! "valgrind/valgrind.h"` and is prefixed with `valgrind`. For example, if the valgrind header
26//! files reside in `/home/foo/repo/valgrind/{valgrind.h, callgrind.h, ...}`, then the environment
27//! variable has to point to `IAI_CALLGRIND_VALGRIND_INCLUDE=/home/foo/repo` and not
28//! `IAI_CALLGRIND_VALGRIND_INCLUDE=/home/foo/repo/valgrind`.
29//!
30//! Also, worth to consider is that the build of `iai-callgrind` with client requests takes longer
31//! than the build without them.
32//!
33//! # Module Organization
34//!
35//! The client requests are organized into modules representing the source header file. So, if you
36//! search for a client request originating from the `valgrind.h` header file, the client request
37//! can be found in the [`crate::client_requests::valgrind`] module. Instead of using macros like in
38//! valgrind we're using functions and small letter names, stripping the prefix if it is equal to
39//! the enclosing module. For example the client request `RUNNING_ON_VALGRIND` from the `valgrind.h`
40//! file equals [`crate::client_requests::valgrind::running_on_valgrind`] and
41//! `VALGRIND_COUNT_ERRORS` from the same `valgrind.h` header file equals
42//! [`crate::client_requests::valgrind::count_errors`].
43//!
44//! The only exception to this rule are the [`crate::valgrind_printf`] macro and its descendents
45//! like [`crate::valgrind_printf_unchecked`] which can be found in the root of `iai-callgrind`.
46//!
47//! # Features
48//!
49//! The client requests are organized into two separate features. The `client_requests_defs` feature
50//! enables the definitions but doesn't run any code yet. To actually run the client requests you
51//! need to enable the `client_requests` feature. The `client_requests` feature implies
52//! `client_requests_defs`. For example, if you need to include the client requests into your
53//! production code, you usually don't want them to run if not running under valgrind in the
54//! `iai-callgrind` benchmarks. In order to achieve this, the `client_requests_defs` can be safely
55//! included in the production code since the compiler will optimize them completely away. So, in
56//! your `Cargo.toml` file, you can do
57//!
58//! ```toml
59//! [dependencies]
60//! iai-callgrind = { version = "0.16.1", default-features = false, features = [
61//!     "client_requests_defs"
62//! ]}
63//!
64//! [dev-dependencies]
65//! iai-callgrind = { version = "0.16.1", features = ["client_requests"] }
66//! ```
67//!
68//! If you would only need the client requests in `iai-callgrind` benchmarks, you only need to add
69//! `iai-callgrind` with the `client_requests` feature to your `dev-dependencies`.
70//!
71//! # Performance and implementation details
72//!
73//! Depending on the target, the client requests are optimized to run with the same overhead as
74//! the original valgrind client requests in C code. The optimizations are based on inline assembly
75//! with the `asm!` macro and depend on the availability of it on a specific target. Since inline
76//! assembly is not stable on all targets which are supported by valgrind, we cannot provide
77//! optimized client requests for them. But, you can still use the non-optimized version on all
78//! platforms which would be natively supported by valgrind. In the end, all targets which are
79//! covered by valgrind are also covered by `iai-callgrind`.
80//!
81//! The non-optimized version add overhead because we need to wrap the macro from the header file in
82//! a function call. This additional function call equals the additional overhead compared to the
83//! original valgrind implementation. Although this is usually not much, we try hard to avoid any
84//! overhead to not slow down the benchmarks.
85//!
86//! Here's a short overview on which targets the optimized client requests are available and why
87//! not (Valgrind version = `3.22`)
88//!
89//! | Target                | Optimized | Reason  |
90//! | --------------------- | --------- | ------- |
91//! | `x86_64/linux`        | yes | -
92//! | `x86_64/freebsd`      | yes | -
93//! | `x86_64/apple+darwin` | yes | -
94//! | `x86_64/windows+gnu`  | yes | -
95//! | `x86_64/solaris`      | yes | -
96//! | `x86/linux`           | yes | -
97//! | `x86/freebsd`         | yes | -
98//! | `x86/apple+darwin`    | yes | -
99//! | `x86/windows+gnu`     | yes | -
100//! | `x86/solaris`         | yes | -
101//! | `x86/windows+msvc`    | no  | TBD
102//! | `arm/linux`           | yes | -
103//! | `aarch64/linux`       | yes | -
104//! | `riscv64/linux`       | no  | TBD
105//! | `x86_64/windows+msvc` | no  | unsupported by valgrind
106//! | `s390x/linux`         | no  | unstable inline assembly
107//! | `mips32/linux`        | no  | unstable inline assembly
108//! | `mips64/linux`        | no  | unstable inline assembly
109//! | `powerpc/linux`       | no  | unstable inline assembly
110//! | `powerpc64/linux`     | no  | unstable inline assembly
111//! | `powerpc64le/linux`   | no  | unstable inline assembly
112//! | `nanomips/linux`      | no  | valgrind only
113//!
114//! All other targets you don't find in the table above are also not supported by valgrind, yet.
115//!
116//! Note this table might quickly become outdated with higher versions of valgrind, and you should
117//! not rely on it to be up-to-date. As indicated above, the bindings are created dynamically in
118//! such a way, that always all targets which are covered by valgrind are also covered by
119//! `iai-callgrind`. They just might not have been optimized, yet. If you need to know if your
120//! target is supported you should consult the `valgrind.h` header file in the [Valgrind
121//! Repository](https://sourceware.org/git/?p=valgrind.git) or have a look at the [Valgrind Release
122//! Notes](https://valgrind.org/downloads/current.html)
123//!
124//! # Sources and additional documentation
125//!
126//! A lot of the library documentation of the client requests within this module and its submodules
127//! is taken from the online manual and the valgrind header files. For more details see also [The
128//! Client Request
129//! mechanism](https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq)
130
131#![allow(clippy::inline_always)]
132
133/// Return true if a client request is defined and available in the used valgrind version
134///
135/// For internal use only!
136///
137/// We do this check to avoid incompatibilities with older valgrinds version which might not have
138/// all client requests available we're offering.
139///
140/// We're only using constant values known at compile time, which the compiler will finally optimize
141/// away, so this macro costs us nothing.
142macro_rules! is_def {
143    ($user_req:path) => {{
144        $user_req as cty::c_uint > 0x1000
145    }};
146}
147
148/// The macro which uses [`valgrind_do_client_request_stmt`] or [`valgrind_do_client_request_expr`]
149/// to execute the client request.
150///
151/// For internal use only!
152///
153/// This macro has two forms: The first takes 7 arguments `name, request, arg1, ..., arg5` and
154/// returns `()`. The expanded macro of this form calls [`valgrind_do_client_request_stmt`]. The
155/// second first has 8 arguments `name, default, request, arg1, ..., arg5` returning a `usize`. The
156/// expanded macro of this form calls [`valgrind_do_client_request_expr`].
157///
158/// Both forms will raise a [`fatal_error`] in case the [`is_def`] macro returns false.
159macro_rules! do_client_request {
160    ($name:literal, $request:path, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr) => {{
161        if is_def!($request) {
162            valgrind_do_client_request_stmt(
163                $request as cty::c_uint,
164                $arg1,
165                $arg2,
166                $arg3,
167                $arg4,
168                $arg5,
169            );
170        } else {
171            fatal_error($name);
172        }
173    }};
174    (
175        $name:literal,
176        $default:expr,
177        $request:path,
178        $arg1:expr,
179        $arg2:expr,
180        $arg3:expr,
181        $arg4:expr,
182        $arg5:expr
183    ) => {{
184        if is_def!($request) {
185            valgrind_do_client_request_expr(
186                $default,
187                $request as cty::c_uint,
188                $arg1,
189                $arg2,
190                $arg3,
191                $arg4,
192                $arg5,
193            )
194        } else {
195            fatal_error($name);
196        }
197    }};
198}
199
200/// Convenience macro to create a `\0`-byte terminated [`std::ffi::CString`] from a literal string
201///
202/// The string literal passed to this macro must not contain or end with a `\0`-byte. If you need a
203/// checked version of [`std::ffi::CString`] you can use [`std::ffi::CString::new`].
204///
205/// # Safety
206///
207/// This macro is unsafe but convenient and efficient. It is your responsibility to ensure that the
208/// input string literal does not contain any `\0` bytes.
209#[macro_export]
210macro_rules! cstring {
211    ($string:literal) => {{
212        std::ffi::CString::from_vec_with_nul_unchecked(concat!($string, "\0").as_bytes().to_vec())
213    }};
214}
215
216/// Convenience macro to create a `\0`-byte terminated [`std::ffi::CString`] from a format string
217///
218/// The format string passed to this macro must not contain or end with a `\0`-byte.
219///
220/// # Safety
221///
222/// The same safety conditions as to the [`cstring`] macro apply here
223#[macro_export]
224macro_rules! format_cstring {
225    ($($args:tt)*) => {{
226        std::ffi::CString::from_vec_with_nul_unchecked(
227            format!("{}\0", format_args!($($args)*)).into_bytes()
228        )
229    }};
230}
231
232cfg_if! {
233    if #[cfg(feature = "client_requests")] {
234        /// Allow prints to valgrind log
235        ///
236        /// This macro is a safe variant of the `VALGRIND_PRINTF` function, checking for `\0` bytes in the
237        /// formatting string. However, if you're sure there are no `\0` bytes present you can
238        /// safely use [`crate::valgrind_printf_unchecked`] which performs better compared to this
239        /// macro.
240        #[macro_export]
241        macro_rules! valgrind_printf {
242            ($($args:tt)*) => {{
243                match std::ffi::CString::from_vec_with_nul(
244                    format!("{}\0", format_args!($($args)*)).into_bytes()
245                ) {
246                    Ok(c_string) => {
247                        unsafe {
248                            $crate::client_requests::__valgrind_print(
249                                c_string.as_ptr()
250                            );
251                        }
252                        Ok(())
253                    },
254                    Err(error) => Err(
255                        $crate::client_requests::error::ClientRequestError::from(error)
256                    )
257                }
258            }};
259        }
260
261        /// Allow prints to valgrind log
262        ///
263        /// Use this macro only if you are sure there are no `\0`-bytes in the formatted string. If
264        /// unsure use the safe [`crate::valgrind_printf`] variant.
265        ///
266        /// This variant performs better than [`crate::valgrind_printf`].
267        #[macro_export]
268        macro_rules! valgrind_printf_unchecked {
269            ($($args:tt)*) => {{
270                let string = format!("{}\0", format_args!($($args)*));
271                $crate::client_requests::__valgrind_print(
272                    string.as_ptr() as *const $crate::cty::c_char
273                );
274            }};
275        }
276
277        /// Allow prints to valgrind log ending with a newline
278        ///
279        /// See also [`crate::valgrind_printf`]
280        #[macro_export]
281        macro_rules! valgrind_println {
282            () => { $crate::valgrind_printf!("\n") };
283            ($($arg:tt)*) => {{
284                match std::ffi::CString::from_vec_with_nul(
285                    format!("{}\n\0", format_args!($($arg)*)).into_bytes()
286                ) {
287                    Ok(c_string) => {
288                        unsafe {
289                            $crate::client_requests::__valgrind_print(
290                                c_string.as_ptr()
291                            );
292                        }
293                        Ok(())
294                    },
295                    Err(error) => Err(
296                        $crate::client_requests::error::ClientRequestError::from(error)
297                    )
298                }
299            }};
300        }
301
302        /// Allow prints to valgrind log ending with a newline
303        ///
304        /// See also [`crate::valgrind_printf_unchecked`]
305        #[macro_export]
306        macro_rules! valgrind_println_unchecked {
307            () => { $crate::valgrind_printf_unchecked!("\n") };
308            ($($args:tt)*) => {{
309                let string = format!("{}\n\0", format_args!($($args)*));
310                $crate::client_requests::__valgrind_print(
311                    string.as_ptr() as *const $crate::cty::c_char
312                );
313            }};
314        }
315
316        /// Allow prints to valgrind log with a backtrace
317        ///
318        /// See also [`crate::valgrind_printf`]
319        #[macro_export]
320        macro_rules! valgrind_printf_backtrace {
321            ($($arg:tt)*) => {{
322                match std::ffi::CString::from_vec_with_nul(
323                    format!("{}\0", format_args!($($arg)*)).into_bytes()
324                ) {
325                    Ok(c_string) => {
326                        unsafe {
327                            $crate::client_requests::__valgrind_print_backtrace(
328                                c_string.as_ptr()
329                            );
330                        }
331                        Ok(())
332                    },
333                    Err(error) => Err(
334                        $crate::client_requests::error::ClientRequestError::from(error)
335                    )
336                }
337            }};
338        }
339
340        /// Allow prints to valgrind log with a backtrace
341        ///
342        /// See also [`crate::valgrind_printf_unchecked`]
343        #[macro_export]
344        macro_rules! valgrind_printf_backtrace_unchecked {
345            ($($arg:tt)*) => {{
346                let string = format!("{}\0", format_args!($($arg)*));
347                $crate::client_requests::__valgrind_print_backtrace(
348                    string.as_ptr() as *const $crate::cty::c_char
349                );
350            }};
351        }
352
353        /// Allow prints to valgrind log with a backtrace ending the formatted string with a newline
354        ///
355        /// See also [`crate::valgrind_printf`]
356        #[macro_export]
357        macro_rules! valgrind_println_backtrace {
358            () => { $crate::valgrind_printf_backtrace!("\n") };
359            ($($arg:tt)*) => {{
360                match std::ffi::CString::from_vec_with_nul(
361                    format!("{}\n\0", format_args!($($arg)*)).into_bytes()
362                ) {
363                    Ok(c_string) => {
364                        unsafe {
365                            $crate::client_requests::__valgrind_print_backtrace(
366                                c_string.as_ptr()
367                            );
368                        }
369                        Ok(())
370                    },
371                    Err(error) => Err(
372                        $crate::client_requests::error::ClientRequestError::from(error)
373                    )
374                }
375            }};
376        }
377
378        /// Allow prints to valgrind log with a backtrace ending the formatted string with a newline
379        ///
380        /// See also [`crate::valgrind_printf_unchecked`]
381        #[macro_export]
382        macro_rules! valgrind_println_backtrace_unchecked {
383            () => { $crate::valgrind_printf_backtrace_unchecked!("\n") };
384            ($($arg:tt)*) => {{
385                let string = format!("{}\n\0", format_args!($($arg)*));
386                unsafe {
387                    $crate::client_requests::__valgrind_print_backtrace(
388                        string.as_ptr() as *const $crate::cty::c_char
389                    );
390                }
391            }};
392        }
393    } else {
394        /// Allow prints to valgrind log
395        ///
396        /// This macro is a safe variant of the `VALGRIND_PRINTF` function, checking for `\0` bytes in the
397        /// formatting string. However, if you're sure there are no `\0` bytes present you can
398        /// safely use [`crate::valgrind_printf_unchecked`] which performs better compared to this
399        /// macro.
400        #[macro_export]
401        macro_rules! valgrind_printf {
402            ($($arg:tt)*) => {{
403                let res: Result<(), $crate::client_requests::error::ClientRequestError> = Ok(());
404                res
405            }};
406        }
407
408        /// Allow prints to valgrind log
409        ///
410        /// Use this macro only if you are sure there are no `\0`-bytes in the formatted string. If
411        /// unsure use the safe [`crate::valgrind_printf`] variant.
412        ///
413        /// This variant performs better than [`crate::valgrind_printf`].
414        #[macro_export]
415        macro_rules! valgrind_printf_unchecked {
416            ($($arg:tt)*) => {{ $crate::client_requests::__no_op() }};
417        }
418
419        /// Allow prints to valgrind log ending with a newline
420        ///
421        /// See also [`crate::valgrind_printf`]
422        #[macro_export]
423        macro_rules! valgrind_println {
424            ($($arg:tt)*) => {{
425                let res: Result<(), $crate::client_requests::error::ClientRequestError> = Ok(());
426                res
427            }};
428        }
429
430        /// Allow prints to valgrind log ending with a newline
431        ///
432        /// See also [`crate::valgrind_printf_unchecked`]
433        #[macro_export]
434        macro_rules! valgrind_println_unchecked {
435            ($($arg:tt)*) => {{ $crate::client_requests::__no_op() }};
436        }
437
438        /// Allow prints to valgrind log with a backtrace
439        ///
440        /// See also [`crate::valgrind_printf`]
441        #[macro_export]
442        macro_rules! valgrind_printf_backtrace {
443            ($($arg:tt)*) => {{
444                let res: Result<(), $crate::client_requests::error::ClientRequestError> = Ok(());
445                res
446            }};
447        }
448
449        /// Allow prints to valgrind log with a backtrace
450        ///
451        /// See also [`crate::valgrind_printf_unchecked`]
452        #[macro_export]
453        macro_rules! valgrind_printf_backtrace_unchecked {
454            ($($arg:tt)*) => {{ $crate::client_requests::__no_op() }};
455        }
456
457        /// Allow prints to valgrind log with a backtrace ending the formatted string with a newline
458        ///
459        /// See also [`crate::valgrind_printf`]
460        #[macro_export]
461        macro_rules! valgrind_println_backtrace {
462            ($($arg:tt)*) => {{
463                let res: Result<(), $crate::client_requests::error::ClientRequestError> = Ok(());
464                res
465            }};
466        }
467
468        /// Allow prints to valgrind log with a backtrace ending the formatted string with a newline
469        ///
470        /// See also [`crate::valgrind_printf_unchecked`]
471        #[macro_export]
472        macro_rules! valgrind_println_backtrace_unchecked {
473            ($($arg:tt)*) => {{ $crate::client_requests::__no_op() }};
474        }
475    }
476}
477
478mod arch;
479mod bindings;
480pub mod cachegrind;
481pub mod callgrind;
482pub mod dhat;
483pub mod drd;
484pub mod error;
485pub mod helgrind;
486pub mod memcheck;
487mod native_bindings;
488pub mod valgrind;
489
490use arch::imp::valgrind_do_client_request_expr;
491use arch::valgrind_do_client_request_stmt;
492use cfg_if::cfg_if;
493
494/// The `ThreadId` is used by some client requests to represent the `tid` which valgrind uses or
495/// returns
496///
497/// This type has no relationship to [`std::thread::ThreadId`]!
498pub type ThreadId = usize;
499
500/// The `StackId` is used and returned by some client requests and represents an id on valgrind's
501/// stack
502pub type StackId = usize;
503
504/// The raw file descriptor number
505///
506/// This type has no relationship to the standard library type definition of `RawFd` besides they
507/// are wrapping the same type on unix systems.
508pub type RawFd = cty::c_int;
509
510/// Valgrind's version number from the `valgrind.h` file
511///
512/// Note that the version numbers were introduced at valgrind version 3.6 and so would not exist in
513/// version 3.5 or earlier. `VALGRIND_VERSION` is None is this case, else it is a tuple `(MAJOR,
514/// MINOR)`
515pub const VALGRIND_VERSION: Option<(u32, u32)> = {
516    if bindings::IC_VALGRIND_MAJOR == 0 {
517        None
518    } else {
519        Some((bindings::IC_VALGRIND_MAJOR, bindings::IC_VALGRIND_MINOR))
520    }
521};
522
523fn fatal_error(func: &str) -> ! {
524    panic!(
525        "{0}: FATAL: {0}::{func} not available! You may need update your installed valgrind \
526         version or don't use this client request. The valgrind version of the valgrind.h header \
527         file is {1}. Aborting...",
528        module_path!(),
529        if let Some((major, minor)) = VALGRIND_VERSION {
530            format!("{major}.{minor}")
531        } else {
532            "< 3.6".to_owned()
533        }
534    );
535}
536
537#[doc(hidden)]
538#[inline(always)]
539pub unsafe fn __valgrind_print(ptr: *const cty::c_char) {
540    native_bindings::valgrind_printf(ptr);
541}
542
543#[doc(hidden)]
544#[inline(always)]
545pub unsafe fn __valgrind_print_backtrace(ptr: *const cty::c_char) {
546    native_bindings::valgrind_printf_backtrace(ptr);
547}
548
549#[doc(hidden)]
550#[inline(always)]
551pub unsafe fn __no_op() {}