iai_callgrind/
common.rs

1//! Common structs for `bin_bench` and `lib_bench`
2
3use std::vec::Vec;
4
5use derive_more::AsRef;
6use iai_callgrind_macros::IntoInner;
7
8use super::{
9    CachegrindMetric, CachegrindMetrics, CallgrindMetrics, DhatMetric, DhatMetrics, Direction,
10    ErrorMetric, EventKind, FlamegraphKind, Limit, ValgrindTool, __internal,
11};
12use crate::EntryPoint;
13
14/// The configuration for the experimental bbv
15///
16/// Can be specified in [`crate::LibraryBenchmarkConfig::tool`] or
17/// [`crate::BinaryBenchmarkConfig::tool`].
18///
19/// # Example
20///
21/// ```rust
22/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
23/// # #[library_benchmark]
24/// # fn some_func() {}
25/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
26/// use iai_callgrind::{LibraryBenchmarkConfig, main, Bbv};
27///
28/// # fn main() {
29/// main!(
30///     config = LibraryBenchmarkConfig::default()
31///         .tool(Bbv::default());
32///     library_benchmark_groups = some_group
33/// );
34/// # }
35/// ```
36#[derive(Debug, Clone, IntoInner, AsRef)]
37pub struct Bbv(__internal::InternalTool);
38
39/// The configuration for cachegrind
40///
41/// Can be specified in [`crate::LibraryBenchmarkConfig::tool`] or
42/// [`crate::BinaryBenchmarkConfig::tool`].
43///
44/// # Example
45///
46/// ```rust
47/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
48/// # #[library_benchmark]
49/// # fn some_func() {}
50/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
51/// use iai_callgrind::{LibraryBenchmarkConfig, main, Cachegrind};
52///
53/// # fn main() {
54/// main!(
55///     config = LibraryBenchmarkConfig::default()
56///         .tool(Cachegrind::default());
57///     library_benchmark_groups = some_group
58/// );
59/// # }
60/// ```
61#[derive(Debug, Clone, IntoInner, AsRef)]
62pub struct Cachegrind(__internal::InternalTool);
63
64/// The configuration for Callgrind
65///
66/// Can be specified in [`crate::LibraryBenchmarkConfig::tool`] or
67/// [`crate::BinaryBenchmarkConfig::tool`].
68///
69/// # Example
70///
71/// ```rust
72/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
73/// # #[library_benchmark]
74/// # fn some_func() {}
75/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
76/// use iai_callgrind::{LibraryBenchmarkConfig, main, Callgrind};
77///
78/// # fn main() {
79/// main!(
80///     config = LibraryBenchmarkConfig::default()
81///         .tool(Callgrind::default());
82///     library_benchmark_groups = some_group
83/// );
84/// # }
85/// ```
86#[derive(Debug, Clone, IntoInner, AsRef)]
87pub struct Callgrind(__internal::InternalTool);
88
89/// The configuration for Dhat
90///
91/// Can be specified in [`crate::LibraryBenchmarkConfig::tool`] or
92/// [`crate::BinaryBenchmarkConfig::tool`].
93///
94/// # Example
95///
96/// ```rust
97/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
98/// # #[library_benchmark]
99/// # fn some_func() {}
100/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
101/// use iai_callgrind::{LibraryBenchmarkConfig, main, Dhat};
102///
103/// # fn main() {
104/// main!(
105///     config = LibraryBenchmarkConfig::default()
106///         .tool(Dhat::default());
107///     library_benchmark_groups = some_group
108/// );
109/// # }
110/// ```
111#[derive(Debug, Clone, IntoInner, AsRef)]
112pub struct Dhat(__internal::InternalTool);
113
114/// The configuration for DRD
115///
116/// Can be specified in [`crate::LibraryBenchmarkConfig::tool`] or
117/// [`crate::BinaryBenchmarkConfig::tool`].
118///
119/// # Example
120///
121/// ```rust
122/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
123/// # #[library_benchmark]
124/// # fn some_func() {}
125/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
126/// use iai_callgrind::{LibraryBenchmarkConfig, main, Drd};
127///
128/// # fn main() {
129/// main!(
130///     config = LibraryBenchmarkConfig::default()
131///         .tool(Drd::default());
132///     library_benchmark_groups = some_group
133/// );
134/// # }
135/// ```
136#[derive(Debug, Clone, IntoInner, AsRef)]
137pub struct Drd(__internal::InternalTool);
138
139/// The `FlamegraphConfig` which allows the customization of the created flamegraphs
140///
141/// Callgrind flamegraphs are very similar to `callgrind_annotate` output. In contrast to
142/// `callgrind_annotate` text based output, the produced flamegraphs are svg files (located in the
143/// `target/iai` directory) which can be viewed in a browser.
144///
145/// # Experimental
146///
147/// Note the following considerations only affect flamegraphs of multi-threaded/multi-process
148/// benchmarks and benchmarks which produce multiple parts with a total over all sub-metrics.
149///
150/// Currently, Iai-Callgrind creates the flamegraphs only for the total over all threads/parts and
151/// subprocesses. This leads to complications since the call graph is not be fully recovered just by
152/// examining each thread/subprocess separately. So, the total metrics in the flamegraphs might not
153/// be the same as the total metrics shown in the terminal output. If in doubt, the terminal output
154/// shows the the correct metrics.
155///
156/// # Examples
157///
158/// ```rust
159/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
160/// use iai_callgrind::{LibraryBenchmarkConfig, FlamegraphConfig, main, Callgrind};
161/// # #[library_benchmark]
162/// # fn some_func() {}
163/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
164/// # fn main() {
165/// main!(
166///     config = LibraryBenchmarkConfig::default()
167///                 .tool(Callgrind::default()
168///                     .flamegraph(FlamegraphConfig::default())
169///                 );
170///     library_benchmark_groups = some_group
171/// );
172/// # }
173/// ```
174#[derive(Debug, Clone, Default, IntoInner, AsRef)]
175pub struct FlamegraphConfig(__internal::InternalFlamegraphConfig);
176
177/// The configuration for Helgrind
178///
179/// Can be specified in [`crate::LibraryBenchmarkConfig::tool`] or
180/// [`crate::BinaryBenchmarkConfig::tool`].
181///
182/// # Example
183///
184/// ```rust
185/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
186/// # #[library_benchmark]
187/// # fn some_func() {}
188/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
189/// use iai_callgrind::{LibraryBenchmarkConfig, main, Helgrind};
190///
191/// # fn main() {
192/// main!(
193///     config = LibraryBenchmarkConfig::default()
194///         .tool(Helgrind::default());
195///     library_benchmark_groups = some_group
196/// );
197/// # }
198/// ```
199#[derive(Debug, Clone, IntoInner, AsRef)]
200pub struct Helgrind(__internal::InternalTool);
201
202/// The configuration for Massif
203///
204/// Can be specified in [`crate::LibraryBenchmarkConfig::tool`] or
205/// [`crate::BinaryBenchmarkConfig::tool`].
206///
207/// # Example
208///
209/// ```rust
210/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
211/// # #[library_benchmark]
212/// # fn some_func() {}
213/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
214/// use iai_callgrind::{LibraryBenchmarkConfig, main, Massif};
215///
216/// # fn main() {
217/// main!(
218///     config = LibraryBenchmarkConfig::default()
219///         .tool(Massif::default());
220///     library_benchmark_groups = some_group
221/// );
222/// # }
223/// ```
224#[derive(Debug, Clone, IntoInner, AsRef)]
225pub struct Massif(__internal::InternalTool);
226
227/// The configuration for Memcheck
228///
229/// Can be specified in [`crate::LibraryBenchmarkConfig::tool`] or
230/// [`crate::BinaryBenchmarkConfig::tool`].
231///
232/// # Example
233///
234/// ```rust
235/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
236/// # #[library_benchmark]
237/// # fn some_func() {}
238/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
239/// use iai_callgrind::{LibraryBenchmarkConfig, main, Memcheck};
240///
241/// # fn main() {
242/// main!(
243///     config = LibraryBenchmarkConfig::default()
244///         .tool(Memcheck::default());
245///     library_benchmark_groups = some_group
246/// );
247/// # }
248/// ```
249#[derive(Debug, Clone, IntoInner, AsRef)]
250pub struct Memcheck(__internal::InternalTool);
251
252/// Configure the default output format of the terminal output of Iai-Callgrind
253///
254/// This configuration is only applied to the default output format (`--output-format=default`) and
255/// not to any of the json output formats like (`--output-format=json`).
256///
257/// # Examples
258///
259/// For example configure the truncation length of the description to `200` for all library
260/// benchmarks in the same file with [`OutputFormat::truncate_description`]:
261///
262/// ```rust
263/// use iai_callgrind::{main, LibraryBenchmarkConfig, OutputFormat};
264/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
265/// # #[library_benchmark]
266/// # fn some_func() {}
267/// # library_benchmark_group!(
268/// #    name = some_group;
269/// #    benchmarks = some_func
270/// # );
271/// # fn main() {
272/// main!(
273///     config = LibraryBenchmarkConfig::default()
274///         .output_format(OutputFormat::default()
275///             .truncate_description(Some(200))
276///         );
277///     library_benchmark_groups = some_group
278/// );
279/// # }
280#[derive(Debug, Clone, Default, IntoInner, AsRef)]
281pub struct OutputFormat(__internal::InternalOutputFormat);
282
283impl Bbv {
284    /// Create a new `BBV` configuration with initial command-line arguments
285    ///
286    /// See also [`Callgrind::args`] and [`Bbv::args`]
287    ///
288    /// # Examples
289    ///
290    /// ```rust
291    /// use iai_callgrind::Bbv;
292    ///
293    /// let config = Bbv::with_args(["interval-size=10000"]);
294    /// ```
295    pub fn with_args<I, T>(args: T) -> Self
296    where
297        I: AsRef<str>,
298        T: IntoIterator<Item = I>,
299    {
300        Self(__internal::InternalTool::with_args(ValgrindTool::BBV, args))
301    }
302
303    /// Add command-line arguments to the `BBV` configuration
304    ///
305    /// Valid arguments
306    /// are <https://valgrind.org/docs/manual/bbv-manual.html#bbv-manual.usage> and the core
307    /// valgrind command-line arguments
308    /// <https://valgrind.org/docs/manual/manual-core.html#manual-core.options>.
309    ///
310    /// See also [`Callgrind::args`]
311    ///
312    /// # Examples
313    ///
314    /// ```rust
315    /// use iai_callgrind::Bbv;
316    ///
317    /// let config = Bbv::default().args(["interval-size=10000"]);
318    /// ```
319    pub fn args<I, T>(&mut self, args: T) -> &mut Self
320    where
321        I: AsRef<str>,
322        T: IntoIterator<Item = I>,
323    {
324        self.0.raw_args.extend_ignore_flag(args);
325        self
326    }
327
328    /// Enable this tool. This is the default.
329    ///
330    /// See also [`Callgrind::enable`]
331    ///
332    /// ```rust
333    /// use iai_callgrind::Bbv;
334    ///
335    /// let config = Bbv::default().enable(false);
336    /// ```
337    pub fn enable(&mut self, value: bool) -> &mut Self {
338        self.0.enable = Some(value);
339        self
340    }
341}
342
343impl Default for Bbv {
344    fn default() -> Self {
345        Self(__internal::InternalTool::new(ValgrindTool::BBV))
346    }
347}
348
349impl Cachegrind {
350    /// Create a new `Cachegrind` configuration with initial command-line arguments
351    ///
352    /// See also [`Callgrind::args`] and [`Cachegrind::args`]
353    ///
354    /// # Examples
355    ///
356    /// ```rust
357    /// use iai_callgrind::Cachegrind;
358    ///
359    /// let config = Cachegrind::with_args(["intr-at-start=no"]);
360    /// ```
361    pub fn with_args<I, T>(args: T) -> Self
362    where
363        I: AsRef<str>,
364        T: IntoIterator<Item = I>,
365    {
366        Self(__internal::InternalTool::with_args(
367            ValgrindTool::Cachegrind,
368            args,
369        ))
370    }
371
372    /// Add command-line arguments to the `Cachegrind` configuration
373    ///
374    /// Valid arguments
375    /// are <https://valgrind.org/docs/manual/cg-manual.html#cg-manual.cgopts> and the core
376    /// valgrind command-line arguments
377    /// <https://valgrind.org/docs/manual/manual-core.html#manual-core.options>.
378    ///
379    /// See also [`Callgrind::args`]
380    ///
381    /// # Examples
382    ///
383    /// ```rust
384    /// use iai_callgrind::Cachegrind;
385    ///
386    /// let config = Cachegrind::default().args(["intr-at-start=no"]);
387    /// ```
388    pub fn args<I, T>(&mut self, args: T) -> &mut Self
389    where
390        I: AsRef<str>,
391        T: IntoIterator<Item = I>,
392    {
393        self.0.raw_args.extend_ignore_flag(args);
394        self
395    }
396
397    /// Enable this tool. This is the default.
398    ///
399    /// See also [`Callgrind::enable`]
400    ///
401    /// # Examples
402    ///
403    /// ```rust
404    /// use iai_callgrind::Cachegrind;
405    ///
406    /// let config = Cachegrind::default().enable(false);
407    /// ```
408    pub fn enable(&mut self, value: bool) -> &mut Self {
409        self.0.enable = Some(value);
410        self
411    }
412
413    /// Customize the format of the cachegrind output
414    ///
415    /// See also [`Callgrind::format`] for more details and [`crate::CachegrindMetrics`] for valid
416    /// metrics.
417    ///
418    /// # Examples
419    ///
420    /// ```rust
421    /// use iai_callgrind::{Cachegrind, CachegrindMetric, CachegrindMetrics};
422    ///
423    /// let config =
424    ///     Cachegrind::default().format([CachegrindMetric::Ir.into(), CachegrindMetrics::CacheSim]);
425    /// ```
426    pub fn format<I, T>(&mut self, cachegrind_metrics: T) -> &mut Self
427    where
428        I: Into<CachegrindMetrics>,
429        T: IntoIterator<Item = I>,
430    {
431        let format = self
432            .0
433            .output_format
434            .get_or_insert_with(|| __internal::InternalToolOutputFormat::Cachegrind(Vec::new()));
435
436        if let __internal::InternalToolOutputFormat::Cachegrind(items) = format {
437            items.extend(cachegrind_metrics.into_iter().map(Into::into));
438        }
439
440        self
441    }
442
443    /// Configure the limits percentages over/below which a performance regression can be assumed
444    ///
445    /// DEPRECATED: Please use [`Cachegrind::soft_limits`] instead.
446    #[deprecated = "Please use Cachegrind::soft_limits instead"]
447    pub fn limits<T>(&mut self, limits: T) -> &mut Self
448    where
449        T: IntoIterator<Item = (CachegrindMetric, f64)>,
450    {
451        self.soft_limits(limits)
452    }
453
454    /// Configure the soft limits over/below which a performance regression can be assumed
455    ///
456    /// Same as [`Callgrind::soft_limits`] but for [`CachegrindMetric`].
457    ///
458    /// # Examples
459    ///
460    /// ```
461    /// use iai_callgrind::{Cachegrind, CachegrindMetric};
462    ///
463    /// let config = Cachegrind::default().soft_limits([(CachegrindMetric::Ir, 5f64)]);
464    /// ```
465    ///
466    /// or for a group of metrics but with a special value for `Ir`:
467    ///
468    /// ```
469    /// use iai_callgrind::{Cachegrind, CachegrindMetric, CachegrindMetrics};
470    ///
471    /// let config = Cachegrind::default().soft_limits([
472    ///     (CachegrindMetrics::All, 10f64),
473    ///     (CachegrindMetric::Ir.into(), 5f64),
474    /// ]);
475    /// ```
476    pub fn soft_limits<K, T>(&mut self, soft_limits: T) -> &mut Self
477    where
478        K: Into<CachegrindMetrics>,
479        T: IntoIterator<Item = (K, f64)>,
480    {
481        let iter = soft_limits.into_iter().map(|(k, l)| (k.into(), l));
482
483        if let Some(__internal::InternalToolRegressionConfig::Cachegrind(config)) =
484            &mut self.0.regression_config
485        {
486            config.soft_limits.extend(iter);
487        } else {
488            self.0.regression_config = Some(__internal::InternalToolRegressionConfig::Cachegrind(
489                __internal::InternalCachegrindRegressionConfig {
490                    soft_limits: iter.collect(),
491                    hard_limits: Vec::default(),
492                    fail_fast: None,
493                },
494            ));
495        }
496        self
497    }
498
499    /// Set hard limits above which a performance regression can be assumed
500    ///
501    /// Same as [`Callgrind::hard_limits`] but for [`CachegrindMetrics`].
502    ///
503    /// # Examples
504    ///
505    /// ```
506    /// use iai_callgrind::{Cachegrind, CachegrindMetric};
507    ///
508    /// let config = Cachegrind::default().hard_limits([(CachegrindMetric::Ir, 10_000)]);
509    /// ```
510    ///
511    /// or for a group of metrics but with a special value for `Ir`:
512    ///
513    /// ```
514    /// use iai_callgrind::{Cachegrind, CachegrindMetric, CachegrindMetrics};
515    ///
516    /// let config = Cachegrind::default().hard_limits([
517    ///     (CachegrindMetrics::Default, 10_000),
518    ///     (CachegrindMetric::Ir.into(), 5_000),
519    /// ]);
520    /// ```
521    pub fn hard_limits<K, L, T>(&mut self, hard_limits: T) -> &mut Self
522    where
523        K: Into<CachegrindMetrics>,
524        L: Into<Limit>,
525        T: IntoIterator<Item = (K, L)>,
526    {
527        let iter = hard_limits.into_iter().map(|(k, l)| (k.into(), l.into()));
528
529        if let Some(__internal::InternalToolRegressionConfig::Cachegrind(config)) =
530            &mut self.0.regression_config
531        {
532            config.hard_limits.extend(iter);
533        } else {
534            self.0.regression_config = Some(__internal::InternalToolRegressionConfig::Cachegrind(
535                __internal::InternalCachegrindRegressionConfig {
536                    soft_limits: Vec::default(),
537                    hard_limits: iter.collect(),
538                    fail_fast: None,
539                },
540            ));
541        }
542        self
543    }
544
545    /// If set to true, then the benchmarks fail on the first encountered regression
546    ///
547    /// The default is `false` and the whole benchmark run fails with a regression error after all
548    /// benchmarks have been run.
549    ///
550    /// # Examples
551    ///
552    /// ```
553    /// use iai_callgrind::Cachegrind;
554    ///
555    /// let config = Cachegrind::default().fail_fast(true);
556    /// ```
557    pub fn fail_fast(&mut self, value: bool) -> &mut Self {
558        if let Some(__internal::InternalToolRegressionConfig::Cachegrind(config)) =
559            &mut self.0.regression_config
560        {
561            config.fail_fast = Some(value);
562        } else {
563            self.0.regression_config = Some(__internal::InternalToolRegressionConfig::Cachegrind(
564                __internal::InternalCachegrindRegressionConfig {
565                    soft_limits: Vec::default(),
566                    hard_limits: Vec::default(),
567                    fail_fast: Some(value),
568                },
569            ));
570        }
571        self
572    }
573}
574
575impl Default for Cachegrind {
576    fn default() -> Self {
577        Self(__internal::InternalTool::new(ValgrindTool::Cachegrind))
578    }
579}
580
581impl Callgrind {
582    /// Create a new `Callgrind` configuration with initial command-line arguments
583    ///
584    /// See also [`Callgrind::args`]
585    ///
586    /// # Examples
587    ///
588    /// ```rust
589    /// use iai_callgrind::Callgrind;
590    ///
591    /// let config = Callgrind::with_args(["collect-bus=yes"]);
592    /// ```
593    pub fn with_args<I, T>(args: T) -> Self
594    where
595        I: AsRef<str>,
596        T: IntoIterator<Item = I>,
597    {
598        Self(__internal::InternalTool::with_args(
599            ValgrindTool::Callgrind,
600            args,
601        ))
602    }
603
604    /// Add command-line arguments to the `Callgrind` configuration
605    ///
606    /// The command-line arguments are passed directly to the callgrind invocation. Valid arguments
607    /// are <https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options> and the core
608    /// valgrind command-line arguments
609    /// <https://valgrind.org/docs/manual/manual-core.html#manual-core.options>. Note that not all
610    /// command-line arguments are supported especially the ones which change output paths.
611    /// Unsupported arguments will be ignored printing a warning.
612    ///
613    /// The flags can be omitted ("collect-bus" instead of "--collect-bus").
614    ///
615    /// # Examples
616    ///
617    /// ```rust
618    /// use iai_callgrind::Callgrind;
619    ///
620    /// let config = Callgrind::default().args(["collect-bus=yes"]);
621    /// ```
622    pub fn args<I, T>(&mut self, args: T) -> &mut Self
623    where
624        I: AsRef<str>,
625        T: IntoIterator<Item = I>,
626    {
627        self.0.raw_args.extend_ignore_flag(args);
628        self
629    }
630
631    /// Enable this tool. This is the default.
632    ///
633    /// This is mostly useful to disable a tool which has been enabled in a
634    /// [`crate::LibraryBenchmarkConfig`] (or [`crate::BinaryBenchmarkConfig`]) at a higher-level.
635    /// However, the default tool (usually callgrind) cannot be disabled.
636    ///
637    /// ```rust
638    /// use iai_callgrind::Callgrind;
639    ///
640    /// let config = Callgrind::default().enable(false);
641    /// ```
642    pub fn enable(&mut self, value: bool) -> &mut Self {
643        self.0.enable = Some(value);
644        self
645    }
646
647    /// Set or unset the entry point for a benchmark
648    ///
649    /// Iai-Callgrind sets the [`--toggle-collect`] argument of callgrind to the benchmark function
650    /// which we call [`EntryPoint::Default`]. Specifying a `--toggle-collect` argument, sets
651    /// automatically `--collect-at-start=no`. This ensures that only the metrics from the benchmark
652    /// itself are collected and not the `setup` or `teardown` or anything before/after the
653    /// benchmark function.
654    ///
655    /// However, there are cases when the default toggle is not enough [`EntryPoint::Custom`] or in
656    /// the way [`EntryPoint::None`].
657    ///
658    /// Setting [`EntryPoint::Custom`] is convenience for disabling the entry point with
659    /// [`EntryPoint::None`] and setting `--toggle-collect=CUSTOM_ENTRY_POINT` in
660    /// [`Callgrind::args`]. [`EntryPoint::Custom`] can be useful if you
661    /// want to benchmark a private function and only need the function in the benchmark function as
662    /// access point. [`EntryPoint::Custom`] accepts glob patterns the same way as
663    /// [`--toggle-collect`] does.
664    ///
665    /// # Examples
666    ///
667    /// If you're using callgrind client requests either in the benchmark function itself or in your
668    /// library, then using [`EntryPoint::None`] is presumably be required. Consider the following
669    /// example (`DEFAULT_ENTRY_POINT` marks the default entry point):
670    #[cfg_attr(not(feature = "client_requests_defs"), doc = "```rust,ignore")]
671    #[cfg_attr(feature = "client_requests_defs", doc = "```rust")]
672    /// use iai_callgrind::{
673    ///     main, LibraryBenchmarkConfig,library_benchmark, library_benchmark_group
674    /// };
675    /// use std::hint::black_box;
676    ///
677    /// fn to_be_benchmarked() -> u64 {
678    ///     println!("Some info output");
679    ///     iai_callgrind::client_requests::callgrind::start_instrumentation();
680    ///     let result = {
681    ///         // some heavy calculations
682    /// #       10
683    ///     };
684    ///     iai_callgrind::client_requests::callgrind::stop_instrumentation();
685    ///
686    ///     result
687    /// }
688    ///
689    /// #[library_benchmark]
690    /// fn some_bench() -> u64 { // <-- DEFAULT ENTRY POINT
691    ///     black_box(to_be_benchmarked())
692    /// }
693    ///
694    /// library_benchmark_group!(name = some_group; benchmarks = some_bench);
695    /// # fn main() {
696    /// main!(library_benchmark_groups = some_group);
697    /// # }
698    /// ```
699    /// In the example above [`EntryPoint::Default`] is active, so the counting of events starts
700    /// when the `some_bench` function is entered. In `to_be_benchmarked`, the client request
701    /// `start_instrumentation` does effectively nothing and `stop_instrumentation` will stop the
702    /// event counting as requested. This is most likely not what you intended. The event counting
703    /// should start with `start_instrumentation`. To achieve this, you can set [`EntryPoint::None`]
704    /// which removes the default toggle, but also `--collect-at-start=no`. So, you need to specify
705    /// `--collect-at-start=no` in [`Callgrind::args`]. The example would then look like this:
706    /// ```rust
707    /// use std::hint::black_box;
708    ///
709    /// use iai_callgrind::{library_benchmark, EntryPoint, LibraryBenchmarkConfig, Callgrind};
710    /// # use iai_callgrind::{library_benchmark_group, main};
711    /// # fn to_be_benchmarked() -> u64 { 10 }
712    ///
713    /// // ...
714    ///
715    /// #[library_benchmark(
716    ///     config = LibraryBenchmarkConfig::default()
717    ///         .tool(Callgrind::with_args(["--collect-at-start=no"])
718    ///             .entry_point(EntryPoint::None)
719    ///         )
720    /// )]
721    /// fn some_bench() -> u64 {
722    ///     black_box(to_be_benchmarked())
723    /// }
724    ///
725    /// // ...
726    ///
727    /// # library_benchmark_group!(name = some_group; benchmarks = some_bench);
728    /// # fn main() {
729    /// # main!(library_benchmark_groups = some_group);
730    /// # }
731    /// ```
732    /// [`--toggle-collect`]: https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options
733    pub fn entry_point(&mut self, entry_point: EntryPoint) -> &mut Self {
734        self.0.entry_point = Some(entry_point);
735        self
736    }
737
738    /// Configure the limits percentages over/below which a performance regression can be assumed
739    ///
740    /// DEPRECATED: Use [`Callgrind::soft_limits`] instead.
741    #[deprecated = "Please use Callgrind::soft_limits instead"]
742    pub fn limits<T>(&mut self, limits: T) -> &mut Self
743    where
744        T: IntoIterator<Item = (EventKind, f64)>,
745    {
746        self.soft_limits(limits)
747    }
748
749    /// Configure the soft limits over/below which a performance regression can be assumed
750    ///
751    /// A soft limit consists of an [`EventKind`] and a percentage over which a regression is
752    /// assumed. If the limit is negative, then a regression is assumed to be below this limit.
753    ///
754    /// # Examples
755    ///
756    /// ```
757    /// use iai_callgrind::{Callgrind, EventKind};
758    ///
759    /// let config = Callgrind::default().soft_limits([(EventKind::Ir, 5f64)]);
760    /// ```
761    ///
762    /// or for a whole group of metrics but a special value for `Ir`:
763    ///
764    /// ```
765    /// use iai_callgrind::{Callgrind, CallgrindMetrics, EventKind};
766    ///
767    /// let config = Callgrind::default()
768    ///     .soft_limits([(CallgrindMetrics::All, 10f64), (EventKind::Ir.into(), 5f64)]);
769    /// ```
770    pub fn soft_limits<K, T>(&mut self, soft_limits: T) -> &mut Self
771    where
772        K: Into<CallgrindMetrics>,
773        T: IntoIterator<Item = (K, f64)>,
774    {
775        let iter = soft_limits.into_iter().map(|(k, l)| (k.into(), l));
776
777        if let Some(__internal::InternalToolRegressionConfig::Callgrind(config)) =
778            &mut self.0.regression_config
779        {
780            config.soft_limits.extend(iter);
781        } else {
782            self.0.regression_config = Some(__internal::InternalToolRegressionConfig::Callgrind(
783                __internal::InternalCallgrindRegressionConfig {
784                    soft_limits: iter.collect(),
785                    hard_limits: Vec::default(),
786                    fail_fast: None,
787                },
788            ));
789        }
790        self
791    }
792
793    /// Set hard limits above which a performance regression can be assumed
794    ///
795    /// In contrast to [`Callgrind::soft_limits`], hard limits restrict an [`EventKind`] in absolute
796    /// numbers instead of a percentage. A hard limit only affects the `new` benchmark run.
797    ///
798    /// # Errors
799    ///
800    /// Specifying limits with [`Limit::Float`] for metric groups which contain mixed metrics of
801    /// [`Limit::Float`] and [`Limit::Int`] type is an error because [`Limit::Float`] can't be
802    /// converted to [`Limit::Int`]. Use [`Limit::Int`] instead and overwrite the float metrics of
803    /// this group with [`Limit::Float`] if required.
804    ///
805    /// ```
806    /// use iai_callgrind::{Callgrind, CallgrindMetrics, Limit};
807    ///
808    /// // This is an error
809    /// let config = Callgrind::default().hard_limits([(CallgrindMetrics::All, 10_000.0)]);
810    ///
811    /// // This is ok
812    /// let config = Callgrind::default().hard_limits([(CallgrindMetrics::All, 10_000)]);
813    ///
814    /// // Overwriting metrics is fine too
815    /// let config = Callgrind::default().hard_limits([
816    ///     (CallgrindMetrics::All, Limit::Int(10_000)),
817    ///     (CallgrindMetrics::CacheMissRates, Limit::Float(5f64)),
818    ///     (CallgrindMetrics::CacheHitRates, Limit::Float(100f64)),
819    /// ]);
820    /// ```
821    ///
822    /// # Examples
823    ///
824    /// If in a benchmark configured like below, there are more than `10_000` instruction fetches, a
825    /// performance regression is registered failing the benchmark run.
826    ///
827    /// ```
828    /// use iai_callgrind::{Callgrind, EventKind};
829    ///
830    /// let config = Callgrind::default().hard_limits([(EventKind::Ir, 10_000)]);
831    /// ```
832    ///
833    /// or for a group of metrics but with a special value for `Ir`:
834    ///
835    /// ```
836    /// use iai_callgrind::{Callgrind, CallgrindMetrics, EventKind};
837    ///
838    /// let config = Callgrind::default().hard_limits([
839    ///     (CallgrindMetrics::Default, 10_000),
840    ///     (EventKind::Ir.into(), 5_000),
841    /// ]);
842    /// ```
843    pub fn hard_limits<K, L, T>(&mut self, hard_limits: T) -> &mut Self
844    where
845        K: Into<CallgrindMetrics>,
846        L: Into<Limit>,
847        T: IntoIterator<Item = (K, L)>,
848    {
849        let iter = hard_limits.into_iter().map(|(k, l)| (k.into(), l.into()));
850
851        if let Some(__internal::InternalToolRegressionConfig::Callgrind(config)) =
852            &mut self.0.regression_config
853        {
854            config.hard_limits.extend(iter);
855        } else {
856            self.0.regression_config = Some(__internal::InternalToolRegressionConfig::Callgrind(
857                __internal::InternalCallgrindRegressionConfig {
858                    soft_limits: Vec::default(),
859                    hard_limits: iter.collect(),
860                    fail_fast: None,
861                },
862            ));
863        }
864        self
865    }
866
867    /// If set to true, then the benchmarks fail on the first encountered regression
868    ///
869    /// The default is `false` and the whole benchmark run fails with a regression error after all
870    /// benchmarks have been run.
871    ///
872    /// # Examples
873    ///
874    /// ```
875    /// use iai_callgrind::Callgrind;
876    ///
877    /// let config = Callgrind::default().fail_fast(true);
878    /// ```
879    pub fn fail_fast(&mut self, value: bool) -> &mut Self {
880        if let Some(__internal::InternalToolRegressionConfig::Callgrind(config)) =
881            &mut self.0.regression_config
882        {
883            config.fail_fast = Some(value);
884        } else {
885            self.0.regression_config = Some(__internal::InternalToolRegressionConfig::Callgrind(
886                __internal::InternalCallgrindRegressionConfig {
887                    soft_limits: Vec::default(),
888                    hard_limits: Vec::default(),
889                    fail_fast: Some(value),
890                },
891            ));
892        }
893        self
894    }
895
896    /// Option to produce flamegraphs from callgrind output with a [`crate::FlamegraphConfig`]
897    ///
898    /// The flamegraphs are usable but still in an experimental stage. Callgrind lacks the tool like
899    /// `cg_diff` for cachegrind to compare two different profiles. Flamegraphs on the other hand
900    /// can bridge the gap and be [`FlamegraphKind::Differential`] to compare two benchmark runs.
901    ///
902    /// # Examples
903    ///
904    /// ```rust
905    /// # use iai_callgrind::{library_benchmark, library_benchmark_group};
906    /// # #[library_benchmark]
907    /// # fn some_func() {}
908    /// # library_benchmark_group!(name = some_group; benchmarks = some_func);
909    /// use iai_callgrind::{
910    ///     LibraryBenchmarkConfig, main, FlamegraphConfig, FlamegraphKind, Callgrind
911    /// };
912    ///
913    /// # fn main() {
914    /// main!(
915    ///     config = LibraryBenchmarkConfig::default()
916    ///         .tool(Callgrind::default()
917    ///             .flamegraph(FlamegraphConfig::default()
918    ///                 .kind(FlamegraphKind::Differential)
919    ///             )
920    ///         );
921    ///     library_benchmark_groups = some_group
922    /// );
923    /// # }
924    /// ```
925    pub fn flamegraph<T>(&mut self, flamegraph: T) -> &mut Self
926    where
927        T: Into<__internal::InternalFlamegraphConfig>,
928    {
929        self.0.flamegraph_config = Some(__internal::InternalToolFlamegraphConfig::Callgrind(
930            flamegraph.into(),
931        ));
932        self
933    }
934
935    /// Customize the format of the callgrind output
936    ///
937    /// This option allows customizing the output format of callgrind metrics. It does not set any
938    /// flags for the callgrind execution (i.e. `--branch-sim=yes`) which actually enable the
939    /// collection of these metrics. Consult the docs of [`EventKind`] and [`CallgrindMetrics`] to
940    /// see which flag is necessary to enable the collection of a specific metric. The rules:
941    ///
942    /// 1. A metric is only printed if specified here
943    /// 2. A metric is not printed if not collected by callgrind
944    /// 3. The order matters
945    /// 4. In case of duplicate specifications of the same metric the first one wins.
946    ///
947    /// Callgrind offers a lot of metrics, so the [`CallgrindMetrics`] enum contains groups of
948    /// [`EventKind`]s, to avoid having to specify all [`EventKind`]s one-by-one (although still
949    /// possible with [`CallgrindMetrics::SingleEvent`]).
950    ///
951    /// All command-line arguments of callgrind and which metric they collect are described in full
952    /// detail in the [callgrind
953    /// documentation](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options).
954    ///
955    /// # Examples
956    ///
957    /// To enable printing all callgrind metrics specify [`CallgrindMetrics::All`]. `All` callgrind
958    /// metrics include the cache misses ([`EventKind::I1mr`], ...). For example in a library
959    /// benchmark:
960    ///
961    /// ```rust
962    /// # use iai_callgrind::{library_benchmark, library_benchmark_group};
963    /// use iai_callgrind::{main, LibraryBenchmarkConfig, OutputFormat, CallgrindMetrics, Callgrind};
964    /// # #[library_benchmark]
965    /// # fn some_func() {}
966    /// # library_benchmark_group!(name = some_group; benchmarks = some_func);
967    /// # fn main() {
968    /// main!(
969    ///     config = LibraryBenchmarkConfig::default()
970    ///                  .tool(Callgrind::default()
971    ///                      .format([CallgrindMetrics::All]));
972    ///     library_benchmark_groups = some_group
973    /// );
974    /// # }
975    /// ```
976    ///
977    /// The benchmark is executed with the callgrind arguments set by iai-callgrind which don't
978    /// collect any other metrics than cache misses (`--cache-sim=yes`), so the output will look
979    /// like this:
980    ///
981    /// ```text
982    /// file::some_group::printing cache_misses:
983    ///   Instructions:                        1353|1353                 (No change)
984    ///   Dr:                                   255|255                  (No change)
985    ///   Dw:                                   233|233                  (No change)
986    ///   I1mr:                                  54|54                   (No change)
987    ///   D1mr:                                  12|12                   (No change)
988    ///   D1mw:                                   0|0                    (No change)
989    ///   ILmr:                                  53|53                   (No change)
990    ///   DLmr:                                   3|3                    (No change)
991    ///   DLmw:                                   0|0                    (No change)
992    ///   L1 Hits:                             1775|1775                 (No change)
993    ///   LL Hits:                               10|10                   (No change)
994    ///   RAM Hits:                              56|56                   (No change)
995    ///   Total read+write:                    1841|1841                 (No change)
996    ///   Estimated Cycles:                    3785|3785                 (No change)
997    /// ```
998    pub fn format<I, T>(&mut self, callgrind_metrics: T) -> &mut Self
999    where
1000        I: Into<CallgrindMetrics>,
1001        T: IntoIterator<Item = I>,
1002    {
1003        let format = self
1004            .0
1005            .output_format
1006            .get_or_insert_with(|| __internal::InternalToolOutputFormat::Callgrind(Vec::new()));
1007
1008        if let __internal::InternalToolOutputFormat::Callgrind(items) = format {
1009            items.extend(callgrind_metrics.into_iter().map(Into::into));
1010        }
1011
1012        self
1013    }
1014}
1015
1016impl Default for Callgrind {
1017    fn default() -> Self {
1018        Self(__internal::InternalTool::new(ValgrindTool::Callgrind))
1019    }
1020}
1021
1022impl Dhat {
1023    /// Create a new `Callgrind` configuration with initial command-line arguments
1024    ///
1025    /// See also [`Callgrind::args`] and [`Dhat::args`]
1026    ///
1027    /// # Examples
1028    ///
1029    /// ```rust
1030    /// use iai_callgrind::Dhat;
1031    ///
1032    /// let config = Dhat::with_args(["mode=ad-hoc"]);
1033    /// ```
1034    pub fn with_args<I, T>(args: T) -> Self
1035    where
1036        I: AsRef<str>,
1037        T: IntoIterator<Item = I>,
1038    {
1039        Self(__internal::InternalTool::with_args(
1040            ValgrindTool::DHAT,
1041            args,
1042        ))
1043    }
1044
1045    /// Add command-line arguments to the `Dhat` configuration
1046    ///
1047    /// Valid arguments
1048    /// are <https://valgrind.org/docs/manual/dh-manual.html#dh-manual.options> and the core
1049    /// valgrind command-line arguments
1050    /// <https://valgrind.org/docs/manual/manual-core.html#manual-core.options>.
1051    ///
1052    /// See also [`Callgrind::args`]
1053    ///
1054    /// # Examples
1055    ///
1056    /// ```rust
1057    /// use iai_callgrind::Dhat;
1058    ///
1059    /// let config = Dhat::default().args(["interval-size=10000"]);
1060    /// ```
1061    pub fn args<I, T>(&mut self, args: T) -> &mut Self
1062    where
1063        I: AsRef<str>,
1064        T: IntoIterator<Item = I>,
1065    {
1066        self.0.raw_args.extend_ignore_flag(args);
1067        self
1068    }
1069
1070    /// Enable this tool. This is the default.
1071    ///
1072    /// See also [`Callgrind::enable`]
1073    ///
1074    /// ```rust
1075    /// use iai_callgrind::Dhat;
1076    ///
1077    /// let config = Dhat::default().enable(false);
1078    /// ```
1079    pub fn enable(&mut self, value: bool) -> &mut Self {
1080        self.0.enable = Some(value);
1081        self
1082    }
1083
1084    /// Customize the format of the dhat output
1085    ///
1086    /// See also [`Callgrind::format`] for more details and [`DhatMetric`] for valid metrics.
1087    ///
1088    /// # Examples
1089    ///
1090    /// ```rust
1091    /// use iai_callgrind::{Dhat, DhatMetric};
1092    ///
1093    /// let config = Dhat::default().format([DhatMetric::TotalBytes, DhatMetric::AtTGmaxBytes]);
1094    /// ```
1095    pub fn format<I, T>(&mut self, kinds: T) -> &mut Self
1096    where
1097        I: Into<DhatMetric>,
1098        T: IntoIterator<Item = I>,
1099    {
1100        let format = self
1101            .0
1102            .output_format
1103            .get_or_insert_with(|| __internal::InternalToolOutputFormat::DHAT(Vec::new()));
1104
1105        if let __internal::InternalToolOutputFormat::DHAT(items) = format {
1106            items.extend(kinds.into_iter().map(Into::into));
1107        }
1108
1109        self
1110    }
1111
1112    /// Set or unset the entry point for DHAT
1113    ///
1114    /// The basic concept of this [`EntryPoint`] is almost the same as for
1115    /// [`Callgrind::entry_point`] and for additional details see there. For library benchmarks the
1116    /// default entry point is [`EntryPoint::Default`] and for binary benchmarks it's
1117    /// [`EntryPoint::None`].
1118    ///
1119    /// Note that the default entry point tries to match the benchmark function, so it doesn't make
1120    /// much sense to use [`EntryPoint::Default`] in binary benchmarks. The result of an incorrect
1121    /// entry point is usually that all metrics are `0`, which is an indicator that something has
1122    /// gone wrong.
1123    ///
1124    /// # Details
1125    ///
1126    /// There are subtle differences to the entry point in callgrind and the calculation of the
1127    /// final metrics shown in the DHAT output can only be done on a best-effort basis. As opposed
1128    /// to callgrind, the default entry point [`EntryPoint::Default`] is applied after the benchmark
1129    /// run based on the output files because DHAT does not have a command line argument like
1130    /// `--toggle-collect`. The DHAT output files however, can't be used to reliably exclude the
1131    /// `setup` and `teardown` of the benchmark function. As a consequence, allocations and
1132    /// deallocations in the `setup` and `teardown` function are included in the final metrics. All
1133    /// other (de-)allocations in the benchmark file (around `2000` - `2500` bytes) to prepare the
1134    /// benchmark run are not included what stabilizes the metrics enough to be able to specify
1135    /// limits with [`Dhat::limits`] for regression checks and focus the metrics on the benchmark
1136    /// function.
1137    ///
1138    /// Since there is no `--toggle-collect` argument, it's possible to define additional `frames`
1139    /// (the Iai-Callgrind specific DHAT equivalent of callgrind toggles) in the [`Dhat::frames`]
1140    /// method.
1141    ///
1142    /// The [`EntryPoint::Default`] matches the benchmark function and a [`EntryPoint::Custom`] is
1143    /// convenience for specifying [`EntryPoint::None`] and a frame in [`Dhat::frames`].
1144    ///
1145    /// # Examples
1146    ///
1147    /// Specifying no entry point in library benchmarks is the same as specifying
1148    /// [`EntryPoint::Default`]. It is used here nonetheless for demonstration purposes:
1149    ///
1150    /// ```rust
1151    /// # mod my_lib { pub fn to_be_benchmarked() -> Vec<i32> { vec![0] } }
1152    /// use iai_callgrind::{
1153    ///     main, LibraryBenchmarkConfig, library_benchmark, library_benchmark_group, Dhat,
1154    ///     EntryPoint
1155    /// };
1156    /// use std::hint::black_box;
1157    /// use my_lib::to_be_benchmarked;
1158    ///
1159    /// #[library_benchmark(
1160    ///     config = LibraryBenchmarkConfig::default()
1161    ///         .tool(Dhat::default().entry_point(EntryPoint::Default))
1162    /// )]
1163    /// fn some_bench() -> Vec<i32> { // <-- DEFAULT ENTRY POINT
1164    ///     black_box(to_be_benchmarked())
1165    /// }
1166    ///
1167    /// library_benchmark_group!(name = some_group; benchmarks = some_bench);
1168    /// # fn main() {
1169    /// main!(library_benchmark_groups = some_group);
1170    /// # }
1171    /// ```
1172    ///
1173    /// You most likely want to disable the entry point with [`EntryPoint::None`] if you're using
1174    /// DHAT ad-hoc profiling.
1175    #[cfg_attr(not(feature = "client_requests_defs"), doc = "```rust,ignore")]
1176    #[cfg_attr(feature = "client_requests_defs", doc = "```rust")]
1177    /// use iai_callgrind::{
1178    ///     main, LibraryBenchmarkConfig, library_benchmark, library_benchmark_group,
1179    ///     EntryPoint, Dhat
1180    /// };
1181    /// use std::hint::black_box;
1182    ///
1183    /// fn to_be_benchmarked() -> Vec<i32> {
1184    ///     iai_callgrind::client_requests::dhat::ad_hoc_event(20);
1185    ///     // allocations worth a weight of `20`
1186    /// #   vec![1, 2, 3, 4, 5]
1187    /// }
1188    ///
1189    /// #[library_benchmark(
1190    ///     config = LibraryBenchmarkConfig::default()
1191    ///         .tool(Dhat::with_args(["--mode=ad-hoc"])
1192    ///             .entry_point(EntryPoint::None)
1193    ///         )
1194    /// )]
1195    /// fn some_bench() -> Vec<i32> {
1196    ///     black_box(to_be_benchmarked())
1197    /// }
1198    ///
1199    /// library_benchmark_group!(name = some_group; benchmarks = some_bench);
1200    /// # fn main() {
1201    /// main!(library_benchmark_groups = some_group);
1202    /// # }
1203    /// ```
1204    pub fn entry_point(&mut self, entry_point: EntryPoint) -> &mut Self {
1205        self.0.entry_point = Some(entry_point);
1206        self
1207    }
1208
1209    /// Add one or multiple `frames` which will be included in the benchmark metrics
1210    ///
1211    /// `Frames` are special to Iai-Callgrind and the DHAT equivalent to callgrind toggles
1212    /// (`--toggle-collect`) and like `--toggle-collect` this method accepts simple glob patterns
1213    /// with `*` and `?` wildcards. A `Frame` describes an entry in the call stack (See the
1214    /// example). Sometimes the [`Dhat::entry_point`] is not enough and it is required to specify
1215    /// additional frames. This is especially true in multi-threaded/multi-process applications.
1216    /// Like in callgrind, each thread/subprocess in DHAT is treated as a separate unit and thus
1217    /// requires `frames` in addition to the default entry point to include the interesting ones in
1218    /// the measurements.
1219    ///
1220    /// # Example
1221    ///
1222    /// To demonstrate a general workflow, below is a sanitized example output of `dh_view.html` of
1223    /// a benchmark of a multi-threaded program. Most of the program points, including the default
1224    /// entry point, are not shown here to safe some space. The spawned thread
1225    /// (`std::sys::pal::unix::thread::Thread::new::thread_start`) with the function call
1226    /// `benchmark_tests::find_primes` is the interesting one.
1227    ///
1228    /// ```text
1229    /// â–¼ PP 1/1 (3 children) {
1230    ///     Total:     156,372 bytes (100%, 14,948.32/Minstr) in 76 blocks (100%, 7.27/Minstr), avg size 2,057.53 bytes, avg lifetime 2,907,942.57 instrs (27.8% of program duration)
1231    ///     At t-gmax: 52,351 bytes (100%) in 20 blocks (100%), avg size 2,617.55 bytes
1232    ///     At t-end:  0 bytes (0%) in 0 blocks (0%), avg size 0 bytes
1233    ///     Reads:     117,583 bytes (100%, 11,240.3/Minstr), 0.75/byte
1234    ///     Writes:    135,680 bytes (100%, 12,970.28/Minstr), 0.87/byte
1235    ///     Allocated at {
1236    ///       #0: [root]
1237    ///     }
1238    ///   }
1239    ///   ├─▼ PP 1.1/3 (12 children) {
1240    ///   │     Total:     154,468 bytes (98.78%, 14,766.31/Minstr) in 57 blocks (75%, 5.45/Minstr), avg size 2,709.96 bytes, avg lifetime 2,937,398.7 instrs (28.08% of program duration)
1241    ///   │     At t-gmax: 51,375 bytes (98.14%) in 15 blocks (75%), avg size 3,425 bytes
1242    ///   │     At t-end:  0 bytes (0%) in 0 blocks (0%), avg size 0 bytes
1243    ///   │     Reads:     116,367 bytes (98.97%, 11,124.06/Minstr), 0.75/byte
1244    ///   │     Writes:    134,872 bytes (99.4%, 12,893.03/Minstr), 0.87/byte
1245    ///   │     Allocated at {
1246    ///   │       #1: 0x48CC7A8: malloc (in /usr/lib/valgrind/vgpreload_dhat-amd64-linux.so)
1247    ///   │     }
1248    ///   │   }
1249    ///   │   ├── PP 1.1.1/12 {
1250    ///   │   │     Total:     81,824 bytes (52.33%, 7,821.93/Minstr) in 29 blocks (38.16%, 2.77/Minstr), avg size 2,821.52 bytes, avg lifetime 785,423.83 instrs (7.51% of program duration)
1251    ///   │   │     Max:       40,960 bytes in 3 blocks, avg size 13,653.33 bytes
1252    ///   │   │     At t-gmax: 40,960 bytes (78.24%) in 3 blocks (15%), avg size 13,653.33 bytes
1253    ///   │   │     At t-end:  0 bytes (0%) in 0 blocks (0%), avg size 0 bytes
1254    ///   │   │     Reads:     66,824 bytes (56.83%, 6,388.01/Minstr), 0.82/byte
1255    ///   │   │     Writes:    66,824 bytes (49.25%, 6,388.01/Minstr), 0.82/byte
1256    ///   │   │     Allocated at {
1257    ///   │   │       ^1: 0x48CC7A8: malloc (in /usr/lib/valgrind/vgpreload_dhat-amd64-linux.so)
1258    ///   │   │       #2: 0x40197C7: UnknownInlinedFun (alloc.rs:93)
1259    ///   │   │       #3: 0x40197C7: UnknownInlinedFun (alloc.rs:188)
1260    ///   │   │       #4: 0x40197C7: UnknownInlinedFun (alloc.rs:249)
1261    ///   │   │       #5: 0x40197C7: UnknownInlinedFun (mod.rs:476)
1262    ///   │   │       #6: 0x40197C7: with_capacity_in<alloc::alloc::Global> (mod.rs:422)
1263    ///   │   │       #7: 0x40197C7: with_capacity_in<u64, alloc::alloc::Global> (mod.rs:190)
1264    ///   │   │       #8: 0x40197C7: with_capacity_in<u64, alloc::alloc::Global> (mod.rs:815)
1265    ///   │   │       #9: 0x40197C7: with_capacity<u64> (mod.rs:495)
1266    ///   │   │       #10: 0x40197C7: from_iter<u64, core::iter::adapters::filter::Filter<core::ops::range::RangeInclusive<u64>, benchmark_tests::find_primes::{closure_env#0}>> (spec_from_iter_nested.rs:31)
1267    ///   │   │       #11: 0x40197C7: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (spec_from_iter.rs:34)
1268    ///   │   │       #12: 0x4016B97: from_iter<u64, core::iter::adapters::filter::Filter<core::ops::range::RangeInclusive<u64>, benchmark_tests::find_primes::{closure_env#0}>> (mod.rs:3438)
1269    ///   │   │       #13: 0x4016B97: collect<core::iter::adapters::filter::Filter<core::ops::range::RangeInclusive<u64>, benchmark_tests::find_primes::{closure_env#0}>, alloc::vec::Vec<u64, alloc::alloc::Global>> (iterator.rs:2001)
1270    ///   │   │       #14: 0x4016B97: benchmark_tests::find_primes (lib.rs:25)
1271    ///   │   │       #15: 0x4019DA0: {closure#0} (lib.rs:32)
1272    ///   │   │       #16: 0x4019DA0: std::sys::backtrace::__rust_begin_short_backtrace (backtrace.rs:152)
1273    ///   │   │       #17: 0x4018BB4: {closure#0}<benchmark_tests::find_primes_multi_thread::{closure_env#0}, alloc::vec::Vec<u64, alloc::alloc::Global>> (mod.rs:559)
1274    ///   │   │       #18: 0x4018BB4: call_once<alloc::vec::Vec<u64, alloc::alloc::Global>, std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<benchmark_tests::find_primes_multi_thread::{closure_env#0}, alloc::vec::Vec<u64, alloc::alloc::Global>>> (unwind_safe.rs:272)
1275    ///   │   │       #19: 0x4018BB4: do_call<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<benchmark_tests::find_primes_multi_thread::{closure_env#0}, alloc::vec::Vec<u64, alloc::alloc::Global>>>, alloc::vec::Vec<u64, alloc::alloc::Global>> (panicking.rs:589)
1276    ///   │   │       #20: 0x4018BB4: try<alloc::vec::Vec<u64, alloc::alloc::Global>, core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<benchmark_tests::find_primes_multi_thread::{closure_env#0}, alloc::vec::Vec<u64, alloc::alloc::Global>>>> (panicking.rs:552)
1277    ///   │   │       #21: 0x4018BB4: catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<benchmark_tests::find_primes_multi_thread::{closure_env#0}, alloc::vec::Vec<u64, alloc::alloc::Global>>>, alloc::vec::Vec<u64, alloc::alloc::Global>> (panic.rs:359)
1278    ///   │   │       #22: 0x4018BB4: {closure#1}<benchmark_tests::find_primes_multi_thread::{closure_env#0}, alloc::vec::Vec<u64, alloc::alloc::Global>> (mod.rs:557)
1279    ///   │   │       #23: 0x4018BB4: core::ops::function::FnOnce::call_once{{vtable.shim}} (function.rs:250)
1280    ///   │   │       #24: 0x404A2BA: call_once<(), dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global> (boxed.rs:1966)
1281    ///   │   │       #25: 0x404A2BA: call_once<(), alloc::boxed::Box<dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global>, alloc::alloc::Global> (boxed.rs:1966)
1282    ///   │   │       #26: 0x404A2BA: std::sys::pal::unix::thread::Thread::new::thread_start (thread.rs:97)
1283    ///   │   │       #27: 0x49C27EA: ??? (in /usr/lib/libc.so.6)
1284    ///   │   │       #28: 0x4A45FB3: clone (in /usr/lib/libc.so.6)
1285    ///   │   │     }
1286    ///   │   │   }
1287    ///
1288    ///   ...
1289    /// ```
1290    ///
1291    /// As can be seen, the call stack of the program point `PP 1.1.1/12` does not include a main
1292    /// function, benchmark function, and so forth because a thread is a completely separate unit.
1293    /// This enables us to exclude uninteresting threads by simply not specifying them here and
1294    /// include the interesting ones for example with:
1295    ///
1296    /// ```rust
1297    /// use iai_callgrind::Dhat;
1298    ///
1299    /// Dhat::default().frames(["benchmark_tests::find_primes"]);
1300    /// ```
1301    pub fn frames<I, T>(&mut self, frames: T) -> &mut Self
1302    where
1303        I: Into<String>,
1304        T: IntoIterator<Item = I>,
1305    {
1306        let this = self.0.frames.get_or_insert_with(Vec::new);
1307        this.extend(frames.into_iter().map(Into::into));
1308
1309        self
1310    }
1311
1312    /// Configure the limits percentages over/below which a performance regression can be assumed
1313    ///
1314    /// Same as [`Callgrind::soft_limits`] but for [`DhatMetric`]s.
1315    ///
1316    /// # Examples
1317    ///
1318    /// ```
1319    /// use iai_callgrind::{Dhat, DhatMetric};
1320    ///
1321    /// let config = Dhat::default().soft_limits([(DhatMetric::TotalBytes, 5f64)]);
1322    /// ```
1323    pub fn soft_limits<K, T>(&mut self, soft_limits: T) -> &mut Self
1324    where
1325        K: Into<DhatMetrics>,
1326        T: IntoIterator<Item = (K, f64)>,
1327    {
1328        let iter = soft_limits.into_iter().map(|(k, l)| (k.into(), l));
1329
1330        if let Some(__internal::InternalToolRegressionConfig::Dhat(config)) =
1331            &mut self.0.regression_config
1332        {
1333            config.soft_limits.extend(iter);
1334        } else {
1335            self.0.regression_config = Some(__internal::InternalToolRegressionConfig::Dhat(
1336                __internal::InternalDhatRegressionConfig {
1337                    soft_limits: iter.collect(),
1338                    hard_limits: Vec::default(),
1339                    fail_fast: None,
1340                },
1341            ));
1342        }
1343        self
1344    }
1345
1346    /// Set hard limits above which a performance regression can be assumed
1347    ///
1348    /// Same as [`Callgrind::hard_limits`] but for [`DhatMetric`]s.
1349    ///
1350    /// # Examples
1351    ///
1352    /// If in a benchmark configured like below, there are more than a total of `10_000` bytes
1353    /// allocated, a performance regression is registered failing the benchmark run.
1354    ///
1355    /// ```
1356    /// use iai_callgrind::{Dhat, DhatMetric};
1357    ///
1358    /// let config = Dhat::default().hard_limits([(DhatMetric::TotalBytes, 10_000)]);
1359    /// ```
1360    ///
1361    /// or for a group of metrics but with a special value for `TotalBytes`:
1362    ///
1363    /// ```
1364    /// use iai_callgrind::{Dhat, DhatMetric, DhatMetrics};
1365    ///
1366    /// let config = Dhat::default().hard_limits([
1367    ///     (DhatMetrics::Default, 10_000),
1368    ///     (DhatMetric::TotalBytes.into(), 5_000),
1369    /// ]);
1370    /// ```
1371    pub fn hard_limits<K, L, T>(&mut self, hard_limits: T) -> &mut Self
1372    where
1373        K: Into<DhatMetrics>,
1374        L: Into<Limit>,
1375        T: IntoIterator<Item = (K, L)>,
1376    {
1377        let iter = hard_limits.into_iter().map(|(k, l)| (k.into(), l.into()));
1378
1379        if let Some(__internal::InternalToolRegressionConfig::Dhat(config)) =
1380            &mut self.0.regression_config
1381        {
1382            config.hard_limits.extend(iter);
1383        } else {
1384            self.0.regression_config = Some(__internal::InternalToolRegressionConfig::Dhat(
1385                __internal::InternalDhatRegressionConfig {
1386                    soft_limits: Vec::default(),
1387                    hard_limits: iter.collect(),
1388                    fail_fast: None,
1389                },
1390            ));
1391        }
1392        self
1393    }
1394
1395    /// If set to true, then the benchmarks fail on the first encountered regression
1396    ///
1397    /// The default is `false` and the whole benchmark run fails with a regression error after all
1398    /// benchmarks have been run.
1399    ///
1400    /// # Examples
1401    ///
1402    /// ```
1403    /// use iai_callgrind::Dhat;
1404    ///
1405    /// let config = Dhat::default().fail_fast(true);
1406    /// ```
1407    pub fn fail_fast(&mut self, value: bool) -> &mut Self {
1408        if let Some(__internal::InternalToolRegressionConfig::Dhat(config)) =
1409            &mut self.0.regression_config
1410        {
1411            config.fail_fast = Some(value);
1412        } else {
1413            self.0.regression_config = Some(__internal::InternalToolRegressionConfig::Dhat(
1414                __internal::InternalDhatRegressionConfig {
1415                    soft_limits: Vec::default(),
1416                    hard_limits: Vec::default(),
1417                    fail_fast: Some(value),
1418                },
1419            ));
1420        }
1421        self
1422    }
1423}
1424
1425impl Default for Dhat {
1426    fn default() -> Self {
1427        Self(__internal::InternalTool::new(ValgrindTool::DHAT))
1428    }
1429}
1430
1431impl Drd {
1432    /// Create a new `Drd` configuration with initial command-line arguments
1433    ///
1434    /// See also [`Callgrind::args`] and [`Drd::args`]
1435    ///
1436    /// # Examples
1437    ///
1438    /// ```rust
1439    /// use iai_callgrind::Drd;
1440    ///
1441    /// let config = Drd::with_args(["exclusive-threshold=100"]);
1442    /// ```
1443    pub fn with_args<I, T>(args: T) -> Self
1444    where
1445        I: AsRef<str>,
1446        T: IntoIterator<Item = I>,
1447    {
1448        Self(__internal::InternalTool::with_args(ValgrindTool::DRD, args))
1449    }
1450
1451    /// Add command-line arguments to the `Drd` configuration
1452    ///
1453    /// Valid arguments are <https://valgrind.org/docs/manual/drd-manual.html#drd-manual.options>
1454    /// and the core valgrind command-line arguments
1455    /// <https://valgrind.org/docs/manual/manual-core.html#manual-core.options>.
1456    ///
1457    /// See also [`Callgrind::args`]
1458    ///
1459    /// # Examples
1460    ///
1461    /// ```rust
1462    /// use iai_callgrind::Drd;
1463    ///
1464    /// let config = Drd::default().args(["exclusive-threshold=100"]);
1465    /// ```
1466    pub fn args<I, T>(&mut self, args: T) -> &mut Self
1467    where
1468        I: AsRef<str>,
1469        T: IntoIterator<Item = I>,
1470    {
1471        self.0.raw_args.extend_ignore_flag(args);
1472        self
1473    }
1474
1475    /// Enable this tool. This is the default.
1476    ///
1477    /// See also [`Callgrind::enable`]
1478    ///
1479    /// # Examples
1480    ///
1481    /// ```rust
1482    /// use iai_callgrind::Drd;
1483    ///
1484    /// let config = Drd::default().enable(false);
1485    /// ```
1486    pub fn enable(&mut self, value: bool) -> &mut Self {
1487        self.0.enable = Some(value);
1488        self
1489    }
1490
1491    /// Customize the format of the `DRD` output
1492    ///
1493    /// See also [`Callgrind::format`] for more details and [`ErrorMetric`] for valid metrics.
1494    ///
1495    /// # Examples
1496    ///
1497    /// ```rust
1498    /// use iai_callgrind::{Drd, ErrorMetric};
1499    ///
1500    /// let config = Drd::default().format([ErrorMetric::Errors, ErrorMetric::SuppressedErrors]);
1501    /// ```
1502    pub fn format<I, T>(&mut self, kinds: T) -> &mut Self
1503    where
1504        I: Into<ErrorMetric>,
1505        T: IntoIterator<Item = I>,
1506    {
1507        let format = self
1508            .0
1509            .output_format
1510            .get_or_insert_with(|| __internal::InternalToolOutputFormat::DRD(Vec::new()));
1511
1512        if let __internal::InternalToolOutputFormat::DRD(items) = format {
1513            items.extend(kinds.into_iter().map(Into::into));
1514        }
1515
1516        self
1517    }
1518}
1519
1520impl Default for Drd {
1521    fn default() -> Self {
1522        Self(__internal::InternalTool::new(ValgrindTool::DRD))
1523    }
1524}
1525
1526impl FlamegraphConfig {
1527    /// Option to change the [`FlamegraphKind`]
1528    ///
1529    /// The default is [`FlamegraphKind::All`].
1530    ///
1531    /// # Examples
1532    ///
1533    /// For example, to only create a differential flamegraph:
1534    ///
1535    /// ```
1536    /// use iai_callgrind::{FlamegraphConfig, FlamegraphKind};
1537    ///
1538    /// let config = FlamegraphConfig::default().kind(FlamegraphKind::Differential);
1539    /// ```
1540    pub fn kind(&mut self, kind: FlamegraphKind) -> &mut Self {
1541        self.0.kind = Some(kind);
1542        self
1543    }
1544
1545    /// Negate the differential flamegraph [`FlamegraphKind::Differential`]
1546    ///
1547    /// The default is `false`.
1548    ///
1549    /// Instead of showing the differential flamegraph from the viewing angle of what has happened
1550    /// the negated differential flamegraph shows what will happen. Especially, this allows to see
1551    /// vanished event lines (in blue) for example because the underlying code has improved and
1552    /// removed an unnecessary function call.
1553    ///
1554    /// See also [Differential Flame
1555    /// Graphs](https://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs.html) from
1556    /// Brendan Gregg's Blog.
1557    ///
1558    /// # Examples
1559    ///
1560    /// ```
1561    /// use iai_callgrind::{FlamegraphConfig, FlamegraphKind};
1562    ///
1563    /// let config = FlamegraphConfig::default().negate_differential(true);
1564    /// ```
1565    pub fn negate_differential(&mut self, negate_differential: bool) -> &mut Self {
1566        self.0.negate_differential = Some(negate_differential);
1567        self
1568    }
1569
1570    /// Normalize the differential flamegraph
1571    ///
1572    /// This'll make the first profile event count to match the second. This'll help in situations
1573    /// when everything looks read (or blue) to get a balanced profile with the full red/blue
1574    /// spectrum
1575    ///
1576    /// # Examples
1577    ///
1578    /// ```
1579    /// use iai_callgrind::{FlamegraphConfig, FlamegraphKind};
1580    ///
1581    /// let config = FlamegraphConfig::default().normalize_differential(true);
1582    /// ```
1583    pub fn normalize_differential(&mut self, normalize_differential: bool) -> &mut Self {
1584        self.0.normalize_differential = Some(normalize_differential);
1585        self
1586    }
1587
1588    /// One or multiple [`EventKind`] for which a flamegraph is going to be created.
1589    ///
1590    /// The default is [`EventKind::Ir`]
1591    ///
1592    /// Currently, flamegraph creation is limited to one flamegraph for each [`EventKind`] and
1593    /// there's no way to merge all event kinds into a single flamegraph.
1594    ///
1595    /// Note it is an error to specify a [`EventKind`] which isn't recorded by callgrind. See the
1596    /// docs of the variants of [`EventKind`] which callgrind option is needed to create a record
1597    /// for it. See also the [Callgrind
1598    /// Documentation](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options). The
1599    /// [`EventKind`]s recorded by callgrind which are available as long as the cache simulation is
1600    /// turned on with `--cache-sim=yes` (which is the default):
1601    ///
1602    /// * [`EventKind::Ir`]
1603    /// * [`EventKind::Dr`]
1604    /// * [`EventKind::Dw`]
1605    /// * [`EventKind::I1mr`]
1606    /// * [`EventKind::ILmr`]
1607    /// * [`EventKind::D1mr`]
1608    /// * [`EventKind::DLmr`]
1609    /// * [`EventKind::D1mw`]
1610    /// * [`EventKind::DLmw`]
1611    ///
1612    /// If the cache simulation is turned on, the following derived `EventKinds` are also available:
1613    ///
1614    /// * [`EventKind::L1hits`]
1615    /// * [`EventKind::LLhits`]
1616    /// * [`EventKind::RamHits`]
1617    /// * [`EventKind::TotalRW`]
1618    /// * [`EventKind::EstimatedCycles`]
1619    ///
1620    /// # Examples
1621    ///
1622    /// ```
1623    /// use iai_callgrind::{EventKind, FlamegraphConfig};
1624    ///
1625    /// let config =
1626    ///     FlamegraphConfig::default().event_kinds([EventKind::EstimatedCycles, EventKind::Ir]);
1627    /// ```
1628    pub fn event_kinds<T>(&mut self, event_kinds: T) -> &mut Self
1629    where
1630        T: IntoIterator<Item = EventKind>,
1631    {
1632        self.0.event_kinds = Some(event_kinds.into_iter().collect());
1633        self
1634    }
1635
1636    /// Set the [`Direction`] in which the flamegraph should grow.
1637    ///
1638    /// The default is [`Direction::TopToBottom`].
1639    ///
1640    /// # Examples
1641    ///
1642    /// For example to change the default
1643    ///
1644    /// ```
1645    /// use iai_callgrind::{Direction, FlamegraphConfig};
1646    ///
1647    /// let config = FlamegraphConfig::default().direction(Direction::BottomToTop);
1648    /// ```
1649    pub fn direction(&mut self, direction: Direction) -> &mut Self {
1650        self.0.direction = Some(direction);
1651        self
1652    }
1653
1654    /// Overwrite the default title of the final flamegraph
1655    ///
1656    /// # Examples
1657    ///
1658    /// ```
1659    /// use iai_callgrind::{Direction, FlamegraphConfig};
1660    ///
1661    /// let config = FlamegraphConfig::default().title("My flamegraph title".to_owned());
1662    /// ```
1663    pub fn title(&mut self, title: String) -> &mut Self {
1664        self.0.title = Some(title);
1665        self
1666    }
1667
1668    /// Overwrite the default subtitle of the final flamegraph
1669    ///
1670    /// # Examples
1671    ///
1672    /// ```
1673    /// use iai_callgrind::FlamegraphConfig;
1674    ///
1675    /// let config = FlamegraphConfig::default().subtitle("My flamegraph subtitle".to_owned());
1676    /// ```
1677    pub fn subtitle(&mut self, subtitle: String) -> &mut Self {
1678        self.0.subtitle = Some(subtitle);
1679        self
1680    }
1681
1682    /// Set the minimum width (in pixels) for which event lines are going to be shown.
1683    ///
1684    /// The default is `0.1`
1685    ///
1686    /// To show all events, set the `min_width` to `0f64`.
1687    ///
1688    /// # Examples
1689    ///
1690    /// ```
1691    /// use iai_callgrind::FlamegraphConfig;
1692    ///
1693    /// let config = FlamegraphConfig::default().min_width(0f64);
1694    /// ```
1695    pub fn min_width(&mut self, min_width: f64) -> &mut Self {
1696        self.0.min_width = Some(min_width);
1697        self
1698    }
1699}
1700
1701impl Helgrind {
1702    /// Create a new `Helgrind` configuration with initial command-line arguments
1703    ///
1704    /// See also [`Callgrind::args`] and [`Helgrind::args`]
1705    ///
1706    /// # Examples
1707    ///
1708    /// ```rust
1709    /// use iai_callgrind::Helgrind;
1710    ///
1711    /// let config = Helgrind::with_args(["free-is-write=yes"]);
1712    /// ```
1713    pub fn with_args<I, T>(args: T) -> Self
1714    where
1715        I: AsRef<str>,
1716        T: IntoIterator<Item = I>,
1717    {
1718        Self(__internal::InternalTool::with_args(
1719            ValgrindTool::Helgrind,
1720            args,
1721        ))
1722    }
1723
1724    /// Add command-line arguments to the `Helgrind` configuration
1725    ///
1726    /// Valid arguments
1727    /// are <https://valgrind.org/docs/manual/hg-manual.html#hg-manual.options> and the core
1728    /// valgrind command-line arguments
1729    /// <https://valgrind.org/docs/manual/manual-core.html#manual-core.options>.
1730    ///
1731    /// See also [`Callgrind::args`]
1732    ///
1733    /// # Examples
1734    ///
1735    /// ```rust
1736    /// use iai_callgrind::Helgrind;
1737    ///
1738    /// let config = Helgrind::default().args(["free-is-write=yes"]);
1739    /// ```
1740    pub fn args<I, T>(&mut self, args: T) -> &mut Self
1741    where
1742        I: AsRef<str>,
1743        T: IntoIterator<Item = I>,
1744    {
1745        self.0.raw_args.extend_ignore_flag(args);
1746        self
1747    }
1748
1749    /// Enable this tool. This is the default.
1750    ///
1751    /// See also [`Callgrind::enable`]
1752    ///
1753    /// # Examples
1754    ///
1755    /// ```rust
1756    /// use iai_callgrind::Helgrind;
1757    ///
1758    /// let config = Helgrind::default().enable(false);
1759    /// ```
1760    pub fn enable(&mut self, value: bool) -> &mut Self {
1761        self.0.enable = Some(value);
1762        self
1763    }
1764
1765    /// Customize the format of the `Helgrind` output
1766    ///
1767    /// See also [`Callgrind::format`] for more details and [`ErrorMetric`] for valid metrics.
1768    ///
1769    /// # Examples
1770    ///
1771    /// ```rust
1772    /// use iai_callgrind::{ErrorMetric, Helgrind};
1773    ///
1774    /// let config = Helgrind::default().format([ErrorMetric::Errors, ErrorMetric::SuppressedErrors]);
1775    /// ```
1776    pub fn format<I, T>(&mut self, kinds: T) -> &mut Self
1777    where
1778        I: Into<ErrorMetric>,
1779        T: IntoIterator<Item = I>,
1780    {
1781        let format = self
1782            .0
1783            .output_format
1784            .get_or_insert_with(|| __internal::InternalToolOutputFormat::Helgrind(Vec::new()));
1785
1786        if let __internal::InternalToolOutputFormat::Helgrind(items) = format {
1787            items.extend(kinds.into_iter().map(Into::into));
1788        }
1789
1790        self
1791    }
1792}
1793
1794impl Default for Helgrind {
1795    fn default() -> Self {
1796        Self(__internal::InternalTool::new(ValgrindTool::Helgrind))
1797    }
1798}
1799
1800impl Massif {
1801    /// Create a new `Massif` configuration with initial command-line arguments
1802    ///
1803    /// See also [`Callgrind::args`] and [`Massif::args`]
1804    ///
1805    /// # Examples
1806    ///
1807    /// ```rust
1808    /// use iai_callgrind::Massif;
1809    ///
1810    /// let config = Massif::with_args(["threshold=2.0"]);
1811    /// ```
1812    pub fn with_args<I, T>(args: T) -> Self
1813    where
1814        I: AsRef<str>,
1815        T: IntoIterator<Item = I>,
1816    {
1817        Self(__internal::InternalTool::with_args(
1818            ValgrindTool::Massif,
1819            args,
1820        ))
1821    }
1822
1823    /// Add command-line arguments to the `Massif` configuration
1824    ///
1825    /// Valid arguments
1826    /// are <https://valgrind.org/docs/manual/ms-manual.html#ms-manual.options> and the core
1827    /// valgrind command-line arguments
1828    /// <https://valgrind.org/docs/manual/manual-core.html#manual-core.options>.
1829    ///
1830    /// See also [`Callgrind::args`]
1831    ///
1832    /// # Examples
1833    ///
1834    /// ```rust
1835    /// use iai_callgrind::Massif;
1836    ///
1837    /// let config = Massif::default().args(["threshold=2.0"]);
1838    /// ```
1839    pub fn args<I, T>(&mut self, args: T) -> &mut Self
1840    where
1841        I: AsRef<str>,
1842        T: IntoIterator<Item = I>,
1843    {
1844        self.0.raw_args.extend_ignore_flag(args);
1845        self
1846    }
1847
1848    /// Enable this tool. This is the default.
1849    ///
1850    /// See also [`Callgrind::enable`]
1851    ///
1852    /// # Examples
1853    ///
1854    /// ```rust
1855    /// use iai_callgrind::Massif;
1856    ///
1857    /// let config = Massif::default().enable(false);
1858    /// ```
1859    pub fn enable(&mut self, value: bool) -> &mut Self {
1860        self.0.enable = Some(value);
1861        self
1862    }
1863}
1864
1865impl Default for Massif {
1866    fn default() -> Self {
1867        Self(__internal::InternalTool::new(ValgrindTool::Massif))
1868    }
1869}
1870
1871impl Memcheck {
1872    /// Create a new `Memcheck` configuration with initial command-line arguments
1873    ///
1874    /// See also [`Callgrind::args`] and [`Memcheck::args`]
1875    ///
1876    /// # Examples
1877    ///
1878    /// ```rust
1879    /// use iai_callgrind::Memcheck;
1880    ///
1881    /// let config = Memcheck::with_args(["free-is-write=yes"]);
1882    /// ```
1883    pub fn with_args<I, T>(args: T) -> Self
1884    where
1885        I: AsRef<str>,
1886        T: IntoIterator<Item = I>,
1887    {
1888        Self(__internal::InternalTool::with_args(
1889            ValgrindTool::Memcheck,
1890            args,
1891        ))
1892    }
1893
1894    /// Add command-line arguments to the `Memcheck` configuration
1895    ///
1896    /// Valid arguments
1897    /// are <https://valgrind.org/docs/manual/mc-manual.html#mc-manual.options> and the core
1898    /// valgrind command-line arguments
1899    /// <https://valgrind.org/docs/manual/manual-core.html#manual-core.options>.
1900    ///
1901    /// See also [`Callgrind::args`]
1902    ///
1903    /// # Examples
1904    ///
1905    /// ```rust
1906    /// use iai_callgrind::Memcheck;
1907    ///
1908    /// let config = Memcheck::default().args(["show-leak-kinds=all"]);
1909    /// ```
1910    pub fn args<I, T>(&mut self, args: T) -> &mut Self
1911    where
1912        I: AsRef<str>,
1913        T: IntoIterator<Item = I>,
1914    {
1915        self.0.raw_args.extend_ignore_flag(args);
1916        self
1917    }
1918
1919    /// Enable this tool. This is the default.
1920    ///
1921    /// See also [`Callgrind::enable`]
1922    ///
1923    /// # Examples
1924    ///
1925    /// ```rust
1926    /// use iai_callgrind::Memcheck;
1927    ///
1928    /// let config = Memcheck::default().enable(false);
1929    /// ```
1930    pub fn enable(&mut self, value: bool) -> &mut Self {
1931        self.0.enable = Some(value);
1932        self
1933    }
1934
1935    /// Customize the format of the `Memcheck` output
1936    ///
1937    /// See also [`Callgrind::format`] for more details and [`ErrorMetric`] for valid metrics.
1938    ///
1939    /// # Examples
1940    ///
1941    /// ```rust
1942    /// use iai_callgrind::{ErrorMetric, Memcheck};
1943    ///
1944    /// let config = Memcheck::default().format([ErrorMetric::Errors, ErrorMetric::SuppressedErrors]);
1945    /// ```
1946    pub fn format<I, T>(&mut self, kinds: T) -> &mut Self
1947    where
1948        I: Into<ErrorMetric>,
1949        T: IntoIterator<Item = I>,
1950    {
1951        let format = self
1952            .0
1953            .output_format
1954            .get_or_insert_with(|| __internal::InternalToolOutputFormat::Memcheck(Vec::new()));
1955
1956        if let __internal::InternalToolOutputFormat::Memcheck(items) = format {
1957            items.extend(kinds.into_iter().map(Into::into));
1958        }
1959
1960        self
1961    }
1962}
1963
1964impl Default for Memcheck {
1965    fn default() -> Self {
1966        Self(__internal::InternalTool::new(ValgrindTool::Memcheck))
1967    }
1968}
1969
1970impl OutputFormat {
1971    /// Adjust, enable or disable the truncation of the description in the iai-callgrind output
1972    ///
1973    /// The default is to truncate the description to the size of 50 ascii characters. A `None`
1974    /// value disables the truncation entirely and a `Some` value will truncate the description to
1975    /// the given amount of characters excluding the ellipsis.
1976    ///
1977    /// To clearify which part of the output is meant by `DESCRIPTION`:
1978    ///
1979    /// ```text
1980    /// benchmark_file::group_name::function_name id:DESCRIPTION
1981    ///   Instructions:              352135|352135          (No change)
1982    ///   L1 Hits:                   470117|470117          (No change)
1983    ///   LL Hits:                      748|748             (No change)
1984    ///   RAM Hits:                    4112|4112            (No change)
1985    ///   Total read+write:          474977|474977          (No change)
1986    ///   Estimated Cycles:          617777|617777          (No change)
1987    /// ```
1988    ///
1989    /// # Examples
1990    ///
1991    /// For example, specifying this option with a `None` value in the `main!` macro disables the
1992    /// truncation of the description for all benchmarks.
1993    ///
1994    /// ```rust
1995    /// use iai_callgrind::{main, LibraryBenchmarkConfig, OutputFormat};
1996    /// # use iai_callgrind::{library_benchmark, library_benchmark_group};
1997    /// # #[library_benchmark]
1998    /// # fn some_func() {}
1999    /// # library_benchmark_group!(
2000    /// #    name = some_group;
2001    /// #    benchmarks = some_func
2002    /// # );
2003    /// # fn main() {
2004    /// main!(
2005    ///     config = LibraryBenchmarkConfig::default()
2006    ///         .output_format(OutputFormat::default()
2007    ///             .truncate_description(None)
2008    ///         );
2009    ///     library_benchmark_groups = some_group
2010    /// );
2011    /// # }
2012    /// ```
2013    pub fn truncate_description(&mut self, value: Option<usize>) -> &mut Self {
2014        self.0.truncate_description = Some(value);
2015        self
2016    }
2017
2018    /// Show intermediate metrics from parts, subprocesses, threads, ... (Default: false)
2019    ///
2020    /// In callgrind, threads are treated as separate units (similar to subprocesses) and the
2021    /// metrics for them are dumped into an own file. Other valgrind tools usually separate the
2022    /// output files only by subprocesses. To also show the metrics of any intermediate fragments
2023    /// and not just the total over all of them, set the value of this method to `true`.
2024    ///
2025    /// Temporarily setting `show_intermediate` to `true` can help to find misconfigurations in
2026    /// multi-thread/multi-process benchmarks.
2027    ///
2028    /// # Examples
2029    ///
2030    /// As opposed to valgrind/callgrind, `--trace-children=yes`, `--separate-threads=yes` and
2031    /// `--fair-sched=try` are the defaults in Iai-Callgrind, so in the following example it's not
2032    /// necessary to specify `--separate-threads` to track the metrics of the spawned thread.
2033    /// However, it is necessary to specify an additional toggle or else the metrics of the thread
2034    /// are all zero. We also set the [`super::EntryPoint`] to `None` to disable the default entry
2035    /// point (toggle) which is the benchmark function. So, with this setup we collect only the
2036    /// metrics of the method `my_lib::heavy_calculation` in the spawned thread and nothing else.
2037    ///
2038    /// ```rust
2039    /// use iai_callgrind::{
2040    ///     main, LibraryBenchmarkConfig, OutputFormat, EntryPoint, library_benchmark,
2041    ///     library_benchmark_group, Callgrind
2042    /// };
2043    /// # mod my_lib { pub fn heavy_calculation() -> u64 { 42 }}
2044    ///
2045    /// #[library_benchmark(
2046    ///     config = LibraryBenchmarkConfig::default()
2047    ///         .tool(Callgrind::with_args(["--toggle-collect=my_lib::heavy_calculation"])
2048    ///             .entry_point(EntryPoint::None)
2049    ///         )
2050    ///         .output_format(OutputFormat::default().show_intermediate(true))
2051    /// )]
2052    /// fn bench_thread() -> u64 {
2053    ///     let handle = std::thread::spawn(|| my_lib::heavy_calculation());
2054    ///     handle.join().unwrap()
2055    /// }
2056    ///
2057    /// library_benchmark_group!(name = some_group; benchmarks = bench_thread);
2058    /// # fn main() {
2059    /// main!(library_benchmark_groups = some_group);
2060    /// # }
2061    /// ```
2062    ///
2063    /// Running the above benchmark the first time will print something like the below (The exact
2064    /// metric counts are made up for demonstration purposes):
2065    ///
2066    /// ```text
2067    /// my_benchmark::some_group::bench_thread
2068    ///   ## pid: 633247 part: 1 thread: 1   |N/A
2069    ///   Command:            target/release/deps/my_benchmark-08fe8356975cd1af
2070    ///   Instructions:                     0|N/A             (*********)
2071    ///   L1 Hits:                          0|N/A             (*********)
2072    ///   LL Hits:                          0|N/A             (*********)
2073    ///   RAM Hits:                         0|N/A             (*********)
2074    ///   Total read+write:                 0|N/A             (*********)
2075    ///   Estimated Cycles:                 0|N/A             (*********)
2076    ///   ## pid: 633247 part: 1 thread: 2   |N/A
2077    ///   Command:            target/release/deps/my_benchmark-08fe8356975cd1af
2078    ///   Instructions:                  3905|N/A             (*********)
2079    ///   L1 Hits:                       4992|N/A             (*********)
2080    ///   LL Hits:                          0|N/A             (*********)
2081    ///   RAM Hits:                       464|N/A             (*********)
2082    ///   Total read+write:              5456|N/A             (*********)
2083    ///   Estimated Cycles:             21232|N/A             (*********)
2084    ///   ## Total
2085    ///   Instructions:                  3905|N/A             (*********)
2086    ///   L1 Hits:                       4992|N/A             (*********)
2087    ///   LL Hits:                          0|N/A             (*********)
2088    ///   RAM Hits:                       464|N/A             (*********)
2089    ///   Total read+write:              5456|N/A             (*********)
2090    ///   Estimated Cycles:             21232|N/A             (*********)
2091    /// ```
2092    ///
2093    /// With `show_intermediate` set to `false` (the default), only the total is shown:
2094    ///
2095    /// ```text
2096    /// my_benchmark::some_group::bench_thread
2097    ///   Instructions:                  3905|N/A             (*********)
2098    ///   L1 Hits:                       4992|N/A             (*********)
2099    ///   LL Hits:                          0|N/A             (*********)
2100    ///   RAM Hits:                       464|N/A             (*********)
2101    ///   Total read+write:              5456|N/A             (*********)
2102    ///   Estimated Cycles:             21232|N/A             (*********)
2103    /// ```
2104    pub fn show_intermediate(&mut self, value: bool) -> &mut Self {
2105        self.0.show_intermediate = Some(value);
2106        self
2107    }
2108
2109    /// Show an ascii grid in the benchmark terminal output
2110    ///
2111    /// This option adds guiding lines which can help reading the benchmark output when running
2112    /// multiple tools with multiple threads/subprocesses.
2113    ///
2114    /// # Examples
2115    ///
2116    /// ```rust
2117    /// use iai_callgrind::OutputFormat;
2118    ///
2119    /// let output_format = OutputFormat::default().show_grid(true);
2120    /// ```
2121    ///
2122    /// Below is the output of a Iai-Callgrind run with DHAT as additional tool benchmarking a
2123    /// function that executes a subprocess which itself starts multiple threads. For the benchmark
2124    /// run below [`OutputFormat::show_intermediate`] was also active to show the threads and
2125    /// subprocesses.
2126    ///
2127    /// ```text
2128    /// test_lib_bench_threads::bench_group::bench_thread_in_subprocess three:3
2129    /// |======== CALLGRIND ===================================================================
2130    /// |-## pid: 3186352 part: 1 thread: 1       |pid: 2721318 part: 1 thread: 1
2131    /// | Command:            target/release/deps/test_lib_bench_threads-b0b85adec9a45de1
2132    /// | Instructions:                       4697|4697                 (No change)
2133    /// | L1 Hits:                            6420|6420                 (No change)
2134    /// | LL Hits:                              17|17                   (No change)
2135    /// | RAM Hits:                            202|202                  (No change)
2136    /// | Total read+write:                   6639|6639                 (No change)
2137    /// | Estimated Cycles:                  13575|13575                (No change)
2138    /// |-## pid: 3186468 part: 1 thread: 1       |pid: 2721319 part: 1 thread: 1
2139    /// | Command:            target/release/thread 3
2140    /// | Instructions:                      35452|35452                (No change)
2141    /// | L1 Hits:                           77367|77367                (No change)
2142    /// | LL Hits:                             610|610                  (No change)
2143    /// | RAM Hits:                            784|784                  (No change)
2144    /// | Total read+write:                  78761|78761                (No change)
2145    /// | Estimated Cycles:                 107857|107857               (No change)
2146    /// |-## pid: 3186468 part: 1 thread: 2       |pid: 2721319 part: 1 thread: 2
2147    /// | Command:            target/release/thread 3
2148    /// | Instructions:                    2460507|2460507              (No change)
2149    /// | L1 Hits:                         2534939|2534939              (No change)
2150    /// | LL Hits:                              17|17                   (No change)
2151    /// | RAM Hits:                            186|186                  (No change)
2152    /// | Total read+write:                2535142|2535142              (No change)
2153    /// | Estimated Cycles:                2541534|2541534              (No change)
2154    /// |-## pid: 3186468 part: 1 thread: 3       |pid: 2721319 part: 1 thread: 3
2155    /// | Command:            target/release/thread 3
2156    /// | Instructions:                    3650414|3650414              (No change)
2157    /// | L1 Hits:                         3724275|3724275              (No change)
2158    /// | LL Hits:                              21|21                   (No change)
2159    /// | RAM Hits:                            130|130                  (No change)
2160    /// | Total read+write:                3724426|3724426              (No change)
2161    /// | Estimated Cycles:                3728930|3728930              (No change)
2162    /// |-## pid: 3186468 part: 1 thread: 4       |pid: 2721319 part: 1 thread: 4
2163    /// | Command:            target/release/thread 3
2164    /// | Instructions:                    4349846|4349846              (No change)
2165    /// | L1 Hits:                         4423438|4423438              (No change)
2166    /// | LL Hits:                              24|24                   (No change)
2167    /// | RAM Hits:                            125|125                  (No change)
2168    /// | Total read+write:                4423587|4423587              (No change)
2169    /// | Estimated Cycles:                4427933|4427933              (No change)
2170    /// |-## Total
2171    /// | Instructions:                   10500916|10500916             (No change)
2172    /// | L1 Hits:                        10766439|10766439             (No change)
2173    /// | LL Hits:                             689|689                  (No change)
2174    /// | RAM Hits:                           1427|1427                 (No change)
2175    /// | Total read+write:               10768555|10768555             (No change)
2176    /// | Estimated Cycles:               10819829|10819829             (No change)
2177    /// |======== DHAT ========================================================================
2178    /// |-## pid: 3186472 ppid: 3185288           |pid: 2721323 ppid: 2720196
2179    /// | Command:            target/release/deps/test_lib_bench_threads-b0b85adec9a45de1
2180    /// | Total bytes:                        2774|2774                 (No change)
2181    /// | Total blocks:                         24|24                   (No change)
2182    /// | At t-gmax bytes:                    1736|1736                 (No change)
2183    /// | At t-gmax blocks:                      3|3                    (No change)
2184    /// | At t-end bytes:                        0|0                    (No change)
2185    /// | At t-end blocks:                       0|0                    (No change)
2186    /// | Reads bytes:                       21054|21054                (No change)
2187    /// | Writes bytes:                      13165|13165                (No change)
2188    /// |-## pid: 3186473 ppid: 3186472           |pid: 2721324 ppid: 2721323
2189    /// | Command:            target/release/thread 3
2190    /// | Total bytes:                      156158|156158               (No change)
2191    /// | Total blocks:                         73|73                   (No change)
2192    /// | At t-gmax bytes:                   52225|52225                (No change)
2193    /// | At t-gmax blocks:                     19|19                   (No change)
2194    /// | At t-end bytes:                        0|0                    (No change)
2195    /// | At t-end blocks:                       0|0                    (No change)
2196    /// | Reads bytes:                      118403|118403               (No change)
2197    /// | Writes bytes:                     135926|135926               (No change)
2198    /// |-## Total
2199    /// | Total bytes:                      158932|158932               (No change)
2200    /// | Total blocks:                         97|97                   (No change)
2201    /// | At t-gmax bytes:                   53961|53961                (No change)
2202    /// | At t-gmax blocks:                     22|22                   (No change)
2203    /// | At t-end bytes:                        0|0                    (No change)
2204    /// | At t-end blocks:                       0|0                    (No change)
2205    /// | Reads bytes:                      139457|139457               (No change)
2206    /// | Writes bytes:                     149091|149091               (No change)
2207    /// |-Comparison with bench_find_primes_multi_thread three:3
2208    /// | Instructions:                   10494117|10500916             (-0.06475%) [-1.00065x]
2209    /// | L1 Hits:                        10757259|10766439             (-0.08526%) [-1.00085x]
2210    /// | LL Hits:                             601|689                  (-12.7721%) [-1.14642x]
2211    /// | RAM Hits:                           1189|1427                 (-16.6783%) [-1.20017x]
2212    /// | Total read+write:               10759049|10768555             (-0.08828%) [-1.00088x]
2213    /// | Estimated Cycles:               10801879|10819829             (-0.16590%) [-1.00166x]
2214    pub fn show_grid(&mut self, value: bool) -> &mut Self {
2215        self.0.show_grid = Some(value);
2216        self
2217    }
2218
2219    /// Shows changes only when they are above the `tolerance` level
2220    ///
2221    /// Changes whose percentage is below the specified tolerance are not marked as changes.
2222    /// Negative tolerance values are converted to their absolute value.
2223    ///
2224    /// # Examples
2225    ///
2226    /// ```rust
2227    /// use iai_callgrind::OutputFormat;
2228    ///
2229    /// let output_format = OutputFormat::default().tolerance(1.5);
2230    /// ```
2231    ///
2232    /// Below is the output of an Iai-Callgrind run with the tolerance set.
2233    ///
2234    /// ```text
2235    /// my_benchmark::some_group::bench_with_tolerance_margin
2236    ///   Instructions:                     9975976|9976136              (Tolerance)
2237    ///   L1 Hits:                         10183337|10183517             (Tolerance)
2238    ///   LL Hits:                              641|654                  (-1.98777%) [-1.02028x]
2239    ///   RAM Hits:                            1211|1216                 (Tolerance)
2240    ///   Total read+write:                10185189|10185387             (Tolerance)
2241    ///   Estimated Cycles:                10228927|10229347             (Tolerance)
2242    /// ```
2243    pub fn tolerance(&mut self, value: f64) -> &mut Self {
2244        self.0.tolerance = Some(value);
2245        self
2246    }
2247}