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.14.0", default-features = false, features = [
61//!     "client_requests_defs"
62//! ]}
63//!
64//! [dev-dependencies]
65//! iai-callgrind = { version = "0.14.0", 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//! | `x86_64/windows+msvc` | no  | unsupported by valgrind
105//! | `s390x/linux`         | no  | unstable inline assembly
106//! | `mips32/linux`        | no  | unstable inline assembly
107//! | `mips64/linux`        | no  | unstable inline assembly
108//! | `powerpc/linux`       | no  | unstable inline assembly
109//! | `powerpc64/linux`     | no  | unstable inline assembly
110//! | `powerpc64le/linux`   | no  | unstable inline assembly
111//! | `nanomips/linux`      | no  | valgrind only
112//!
113//! All other targets you don't find in the table above are also not supported by valgrind, yet.
114//!
115//! Note this table might quickly become outdated with higher versions of valgrind, and you should
116//! not rely on it to be up-to-date. As indicated above, the bindings are created dynamically in
117//! such a way, that always all targets which are covered by valgrind are also covered by
118//! `iai-callgrind`. They just might not have been optimized, yet. If you need to know if your
119//! target is supported you should consult the `valgrind.h` header file in the [Valgrind
120//! Repository](https://sourceware.org/git/?p=valgrind.git) or have a look at the [Valgrind Release
121//! Notes](https://valgrind.org/downloads/current.html)
122//!
123//! # Sources and additional documentation
124//!
125//! A lot of the library documentation of the client requests within this module and its submodules
126//! is taken from the online manual and the valgrind header files. For more details see also [The
127//! Client Request
128//! mechanism](https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq)
129
130#![allow(clippy::inline_always)]
131
132/// Return true if a client request is defined and available in the used valgrind version
133///
134/// For internal use only!
135///
136/// We do this check to avoid incompatibilities with older valgrinds version which might not have
137/// all client requests available we're offering.
138///
139/// We're only using constant values known at compile time, which the compiler will finally optimize
140/// away, so this macro costs us nothing.
141macro_rules! is_def {
142    ($user_req:path) => {{
143        $user_req as cty::c_uint > 0x1000
144    }};
145}
146
147/// The macro which uses [`valgrind_do_client_request_stmt`] or [`valgrind_do_client_request_expr`]
148/// to execute the client request.
149///
150/// For internal use only!
151///
152/// This macro has two forms: The first takes 7 arguments `name, request, arg1, ..., arg5` and
153/// returns `()`. The expanded macro of this form calls [`valgrind_do_client_request_stmt`]. The
154/// second first has 8 arguments `name, default, request, arg1, ..., arg5` returning a `usize`. The
155/// expanded macro of this form calls [`valgrind_do_client_request_expr`].
156///
157/// Both forms will raise a [`fatal_error`] in case the [`is_def`] macro returns false.
158macro_rules! do_client_request {
159    ($name:literal, $request:path, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr) => {{
160        if is_def!($request) {
161            valgrind_do_client_request_stmt(
162                $request as cty::c_uint,
163                $arg1,
164                $arg2,
165                $arg3,
166                $arg4,
167                $arg5,
168            );
169        } else {
170            fatal_error($name);
171        }
172    }};
173    (
174        $name:literal,
175        $default:expr,
176        $request:path,
177        $arg1:expr,
178        $arg2:expr,
179        $arg3:expr,
180        $arg4:expr,
181        $arg5:expr
182    ) => {{
183        if is_def!($request) {
184            valgrind_do_client_request_expr(
185                $default,
186                $request as cty::c_uint,
187                $arg1,
188                $arg2,
189                $arg3,
190                $arg4,
191                $arg5,
192            )
193        } else {
194            fatal_error($name);
195        }
196    }};
197}
198
199/// Convenience macro to create a `\0`-byte terminated [`std::ffi::CString`] from a literal string
200///
201/// The string literal passed to this macro must not contain or end with a `\0`-byte. If you need a
202/// checked version of [`std::ffi::CString`] you can use [`std::ffi::CString::new`].
203///
204/// # Safety
205///
206/// This macro is unsafe but convenient and efficient. It is your responsibility to ensure that the
207/// input string literal does not contain any `\0` bytes.
208#[macro_export]
209macro_rules! cstring {
210    ($string:literal) => {{
211        std::ffi::CString::from_vec_with_nul_unchecked(concat!($string, "\0").as_bytes().to_vec())
212    }};
213}
214
215/// Convenience macro to create a `\0`-byte terminated [`std::ffi::CString`] from a format string
216///
217/// The format string passed to this macro must not contain or end with a `\0`-byte.
218///
219/// # Safety
220///
221/// The same safety conditions as to the [`cstring`] macro apply here
222#[macro_export]
223macro_rules! format_cstring {
224    ($($args:tt)*) => {{
225        std::ffi::CString::from_vec_with_nul_unchecked(
226            format!("{}\0", format_args!($($args)*)).into_bytes()
227        )
228    }};
229}
230
231cfg_if! {
232    if #[cfg(feature = "client_requests")] {
233        /// Allow prints to valgrind log
234        ///
235        /// This macro is a safe variant of the `VALGRIND_PRINTF` function, checking for `\0` bytes in the
236        /// formatting string. However, if you're sure there are no `\0` bytes present you can
237        /// safely use [`crate::valgrind_printf_unchecked`] which performs better compared to this
238        /// macro.
239        #[macro_export]
240        macro_rules! valgrind_printf {
241            ($($args:tt)*) => {{
242                match std::ffi::CString::from_vec_with_nul(
243                    format!("{}\0", format_args!($($args)*)).into_bytes()
244                ) {
245                    Ok(c_string) => {
246                        unsafe {
247                            $crate::client_requests::__valgrind_print(
248                                c_string.as_ptr()
249                            );
250                        }
251                        Ok(())
252                    },
253                    Err(error) => Err(
254                        $crate::client_requests::error::ClientRequestError::from(error)
255                    )
256                }
257            }};
258        }
259
260        /// Allow prints to valgrind log
261        ///
262        /// Use this macro only if you are sure there are no `\0`-bytes in the formatted string. If
263        /// unsure use the safe [`crate::valgrind_printf`] variant.
264        ///
265        /// This variant performs better than [`crate::valgrind_printf`].
266        #[macro_export]
267        macro_rules! valgrind_printf_unchecked {
268            ($($args:tt)*) => {{
269                let string = format!("{}\0", format_args!($($args)*));
270                $crate::client_requests::__valgrind_print(
271                    string.as_ptr() as *const $crate::cty::c_char
272                );
273            }};
274        }
275
276        /// Allow prints to valgrind log ending with a newline
277        ///
278        /// See also [`crate::valgrind_printf`]
279        #[macro_export]
280        macro_rules! valgrind_println {
281            () => { $crate::valgrind_printf!("\n") };
282            ($($arg:tt)*) => {{
283                match std::ffi::CString::from_vec_with_nul(
284                    format!("{}\n\0", format_args!($($arg)*)).into_bytes()
285                ) {
286                    Ok(c_string) => {
287                        unsafe {
288                            $crate::client_requests::__valgrind_print(
289                                c_string.as_ptr()
290                            );
291                        }
292                        Ok(())
293                    },
294                    Err(error) => Err(
295                        $crate::client_requests::error::ClientRequestError::from(error)
296                    )
297                }
298            }};
299        }
300
301        /// Allow prints to valgrind log ending with a newline
302        ///
303        /// See also [`crate::valgrind_printf_unchecked`]
304        #[macro_export]
305        macro_rules! valgrind_println_unchecked {
306            () => { $crate::valgrind_printf_unchecked!("\n") };
307            ($($args:tt)*) => {{
308                let string = format!("{}\n\0", format_args!($($args)*));
309                $crate::client_requests::__valgrind_print(
310                    string.as_ptr() as *const $crate::cty::c_char
311                );
312            }};
313        }
314
315        /// Allow prints to valgrind log with a backtrace
316        ///
317        /// See also [`crate::valgrind_printf`]
318        #[macro_export]
319        macro_rules! valgrind_printf_backtrace {
320            ($($arg:tt)*) => {{
321                match std::ffi::CString::from_vec_with_nul(
322                    format!("{}\0", format_args!($($arg)*)).into_bytes()
323                ) {
324                    Ok(c_string) => {
325                        unsafe {
326                            $crate::client_requests::__valgrind_print_backtrace(
327                                c_string.as_ptr()
328                            );
329                        }
330                        Ok(())
331                    },
332                    Err(error) => Err(
333                        $crate::client_requests::error::ClientRequestError::from(error)
334                    )
335                }
336            }};
337        }
338
339        /// Allow prints to valgrind log with a backtrace
340        ///
341        /// See also [`crate::valgrind_printf_unchecked`]
342        #[macro_export]
343        macro_rules! valgrind_printf_backtrace_unchecked {
344            ($($arg:tt)*) => {{
345                let string = format!("{}\0", format_args!($($arg)*));
346                $crate::client_requests::__valgrind_print_backtrace(
347                    string.as_ptr() as *const $crate::cty::c_char
348                );
349            }};
350        }
351
352        /// Allow prints to valgrind log with a backtrace ending the formatted string with a newline
353        ///
354        /// See also [`crate::valgrind_printf`]
355        #[macro_export]
356        macro_rules! valgrind_println_backtrace {
357            () => { $crate::valgrind_printf_backtrace!("\n") };
358            ($($arg:tt)*) => {{
359                match std::ffi::CString::from_vec_with_nul(
360                    format!("{}\n\0", format_args!($($arg)*)).into_bytes()
361                ) {
362                    Ok(c_string) => {
363                        unsafe {
364                            $crate::client_requests::__valgrind_print_backtrace(
365                                c_string.as_ptr()
366                            );
367                        }
368                        Ok(())
369                    },
370                    Err(error) => Err(
371                        $crate::client_requests::error::ClientRequestError::from(error)
372                    )
373                }
374            }};
375        }
376
377        /// Allow prints to valgrind log with a backtrace ending the formatted string with a newline
378        ///
379        /// See also [`crate::valgrind_printf_unchecked`]
380        #[macro_export]
381        macro_rules! valgrind_println_backtrace_unchecked {
382            () => { $crate::valgrind_printf_backtrace_unchecked!("\n") };
383            ($($arg:tt)*) => {{
384                let string = format!("{}\n\0", format_args!($($arg)*));
385                unsafe {
386                    $crate::client_requests::__valgrind_print_backtrace(
387                        string.as_ptr() as *const $crate::cty::c_char
388                    );
389                }
390            }};
391        }
392    } else {
393        /// Allow prints to valgrind log
394        ///
395        /// This macro is a safe variant of the `VALGRIND_PRINTF` function, checking for `\0` bytes in the
396        /// formatting string. However, if you're sure there are no `\0` bytes present you can
397        /// safely use [`crate::valgrind_printf_unchecked`] which performs better compared to this
398        /// macro.
399        #[macro_export]
400        macro_rules! valgrind_printf {
401            ($($arg:tt)*) => {{
402                let res: Result<(), $crate::client_requests::error::ClientRequestError> = Ok(());
403                res
404            }};
405        }
406
407        /// Allow prints to valgrind log
408        ///
409        /// Use this macro only if you are sure there are no `\0`-bytes in the formatted string. If
410        /// unsure use the safe [`crate::valgrind_printf`] variant.
411        ///
412        /// This variant performs better than [`crate::valgrind_printf`].
413        #[macro_export]
414        macro_rules! valgrind_printf_unchecked {
415            ($($arg:tt)*) => {{ $crate::client_requests::__no_op() }};
416        }
417
418        /// Allow prints to valgrind log ending with a newline
419        ///
420        /// See also [`crate::valgrind_printf`]
421        #[macro_export]
422        macro_rules! valgrind_println {
423            ($($arg:tt)*) => {{
424                let res: Result<(), $crate::client_requests::error::ClientRequestError> = Ok(());
425                res
426            }};
427        }
428
429        /// Allow prints to valgrind log ending with a newline
430        ///
431        /// See also [`crate::valgrind_printf_unchecked`]
432        #[macro_export]
433        macro_rules! valgrind_println_unchecked {
434            ($($arg:tt)*) => {{ $crate::client_requests::__no_op() }};
435        }
436
437        /// Allow prints to valgrind log with a backtrace
438        ///
439        /// See also [`crate::valgrind_printf`]
440        #[macro_export]
441        macro_rules! valgrind_printf_backtrace {
442            ($($arg:tt)*) => {{
443                let res: Result<(), $crate::client_requests::error::ClientRequestError> = Ok(());
444                res
445            }};
446        }
447
448        /// Allow prints to valgrind log with a backtrace
449        ///
450        /// See also [`crate::valgrind_printf_unchecked`]
451        #[macro_export]
452        macro_rules! valgrind_printf_backtrace_unchecked {
453            ($($arg:tt)*) => {{ $crate::client_requests::__no_op() }};
454        }
455
456        /// Allow prints to valgrind log with a backtrace ending the formatted string with a newline
457        ///
458        /// See also [`crate::valgrind_printf`]
459        #[macro_export]
460        macro_rules! valgrind_println_backtrace {
461            ($($arg:tt)*) => {{
462                let res: Result<(), $crate::client_requests::error::ClientRequestError> = Ok(());
463                res
464            }};
465        }
466
467        /// Allow prints to valgrind log with a backtrace ending the formatted string with a newline
468        ///
469        /// See also [`crate::valgrind_printf_unchecked`]
470        #[macro_export]
471        macro_rules! valgrind_println_backtrace_unchecked {
472            ($($arg:tt)*) => {{ $crate::client_requests::__no_op() }};
473        }
474    }
475}
476
477mod arch;
478mod bindings;
479pub mod cachegrind;
480pub mod callgrind;
481pub mod dhat;
482pub mod drd;
483pub mod error;
484pub mod helgrind;
485pub mod memcheck;
486mod native_bindings;
487pub mod valgrind;
488
489use arch::imp::valgrind_do_client_request_expr;
490use arch::valgrind_do_client_request_stmt;
491use cfg_if::cfg_if;
492
493/// The `ThreadId` is used by some client requests to represent the `tid` which valgrind uses or
494/// returns
495///
496/// This type has no relationship to [`std::thread::ThreadId`]!
497pub type ThreadId = usize;
498
499/// The `StackId` is used and returned by some client requests and represents an id on valgrind's
500/// stack
501pub type StackId = usize;
502
503/// The raw file descriptor number
504///
505/// This type has no relationship to the standard library type definition of `RawFd` besides they
506/// are wrapping the same type on unix systems.
507pub type RawFd = cty::c_int;
508
509/// Valgrind's version number from the `valgrind.h` file
510///
511/// Note that the version numbers were introduced at valgrind version 3.6 and so would not exist in
512/// version 3.5 or earlier. `VALGRIND_VERSION` is None is this case, else it is a tuple `(MAJOR,
513/// MINOR)`
514pub const VALGRIND_VERSION: Option<(u32, u32)> = {
515    if bindings::IC_VALGRIND_MAJOR == 0 {
516        None
517    } else {
518        Some((bindings::IC_VALGRIND_MAJOR, bindings::IC_VALGRIND_MINOR))
519    }
520};
521
522fn fatal_error(func: &str) -> ! {
523    panic!(
524        "{0}: FATAL: {0}::{func} not available! You may need update your installed valgrind \
525         version or don't use this client request. The valgrind version of the valgrind.h header \
526         file is {1}. Aborting...",
527        module_path!(),
528        if let Some((major, minor)) = VALGRIND_VERSION {
529            format!("{major}.{minor}")
530        } else {
531            "< 3.6".to_owned()
532        }
533    );
534}
535
536#[doc(hidden)]
537#[inline(always)]
538pub unsafe fn __valgrind_print(ptr: *const cty::c_char) {
539    native_bindings::valgrind_printf(ptr);
540}
541
542#[doc(hidden)]
543#[inline(always)]
544pub unsafe fn __valgrind_print_backtrace(ptr: *const cty::c_char) {
545    native_bindings::valgrind_printf_backtrace(ptr);
546}
547
548#[doc(hidden)]
549#[inline(always)]
550pub unsafe fn __no_op() {}