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}