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() {}