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