iai_callgrind_runner/
api.rs

1//! The api contains all elements which the `runner` can understand
2
3use std::ffi::OsString;
4use std::fmt::Display;
5#[cfg(feature = "runner")]
6use std::fs::File;
7use std::net::SocketAddr;
8use std::path::{Path, PathBuf};
9#[cfg(feature = "runner")]
10use std::process::{Child, Command as StdCommand, Stdio as StdStdio};
11#[cfg(feature = "runner")]
12use std::str::FromStr;
13use std::time::Duration;
14
15#[cfg(feature = "runner")]
16use anyhow::anyhow;
17#[cfg(feature = "runner")]
18use indexmap::indexset;
19#[cfg(feature = "runner")]
20use indexmap::IndexSet;
21#[cfg(feature = "schema")]
22use schemars::JsonSchema;
23use serde::{Deserialize, Serialize};
24#[cfg(feature = "runner")]
25use strum::{EnumIter, IntoEnumIterator};
26
27#[cfg(feature = "runner")]
28use crate::runner;
29#[cfg(feature = "runner")]
30use crate::runner::metrics::Summarize;
31#[cfg(feature = "runner")]
32use crate::runner::metrics::TypeChecker;
33
34/// All metrics which cachegrind produces and additionally some derived events
35///
36/// Depending on the options passed to Cachegrind, these are the events that Cachegrind can produce.
37/// See the [Cachegrind
38/// documentation](https://valgrind.org/docs/manual/cg-manual.html#cg-manual.cgopts) for details.
39#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
40#[cfg_attr(feature = "schema", derive(JsonSchema))]
41#[cfg_attr(feature = "runner", derive(EnumIter))]
42pub enum CachegrindMetric {
43    /// The default event. I cache reads (which equals the number of instructions executed)
44    Ir,
45    /// D Cache reads (which equals the number of memory reads) (--cache-sim=yes)
46    Dr,
47    /// D Cache writes (which equals the number of memory writes) (--cache-sim=yes)
48    Dw,
49    /// I1 cache read misses (--cache-sim=yes)
50    I1mr,
51    /// D1 cache read misses (--cache-sim=yes)
52    D1mr,
53    /// D1 cache write misses (--cache-sim=yes)
54    D1mw,
55    /// LL cache instruction read misses (--cache-sim=yes)
56    ILmr,
57    /// LL cache data read misses (--cache-sim=yes)
58    DLmr,
59    /// LL cache data write misses (--cache-sim=yes)
60    DLmw,
61    /// I1 cache miss rate (--cache-sim=yes)
62    I1MissRate,
63    /// LL/L2 instructions cache miss rate (--cache-sim=yes)
64    LLiMissRate,
65    /// D1 cache miss rate (--cache-sim=yes)
66    D1MissRate,
67    /// LL/L2 data cache miss rate (--cache-sim=yes)
68    LLdMissRate,
69    /// LL/L2 cache miss rate (--cache-sim=yes)
70    LLMissRate,
71    /// Derived event showing the L1 hits (--cache-sim=yes)
72    L1hits,
73    /// Derived event showing the LL hits (--cache-sim=yes)
74    LLhits,
75    /// Derived event showing the RAM hits (--cache-sim=yes)
76    RamHits,
77    /// L1 cache hit rate (--cache-sim=yes)
78    L1HitRate,
79    /// LL/L2 cache hit rate (--cache-sim=yes)
80    LLHitRate,
81    /// RAM hit rate (--cache-sim=yes)
82    RamHitRate,
83    /// Derived event showing the total amount of cache reads and writes (--cache-sim=yes)
84    TotalRW,
85    /// Derived event showing estimated CPU cycles (--cache-sim=yes)
86    EstimatedCycles,
87    /// Conditional branches executed (--branch-sim=yes)
88    Bc,
89    /// Conditional branches mispredicted (--branch-sim=yes)
90    Bcm,
91    /// Indirect branches executed (--branch-sim=yes)
92    Bi,
93    /// Indirect branches mispredicted (--branch-sim=yes)
94    Bim,
95}
96
97/// A collection of groups of [`CachegrindMetric`]s
98///
99/// The members of each group are fully documented in the docs of each variant of this enum
100#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
101#[non_exhaustive]
102pub enum CachegrindMetrics {
103    /// The default group contains all metrics except the [`CachegrindMetrics::CacheMisses`],
104    /// [`CachegrindMetrics::CacheMissRates`], [`CachegrindMetrics::CacheHitRates`] and
105    /// [`EventKind::Dr`], [`EventKind::Dw`]. More specifically, the following event kinds and
106    /// groups in this order:
107    ///
108    /// ```rust
109    /// # pub mod iai_callgrind {
110    /// # pub use iai_callgrind_runner::api::{CachegrindMetrics, CachegrindMetric};
111    /// # }
112    /// use iai_callgrind::{CachegrindMetric, CachegrindMetrics};
113    ///
114    /// let metrics: Vec<CachegrindMetrics> = vec![
115    ///     CachegrindMetric::Ir.into(),
116    ///     CachegrindMetrics::CacheHits,
117    ///     CachegrindMetric::TotalRW.into(),
118    ///     CachegrindMetric::EstimatedCycles.into(),
119    ///     CachegrindMetrics::BranchSim,
120    /// ];
121    /// ```
122    #[default]
123    Default,
124
125    /// The `CacheMisses` produced by `--cache-sim=yes` contain the following [`CachegrindMetric`]s
126    /// in this order:
127    ///
128    /// ```rust
129    /// # pub mod iai_callgrind {
130    /// # pub use iai_callgrind_runner::api::{CachegrindMetric, CachegrindMetrics};
131    /// # }
132    /// use iai_callgrind::{CachegrindMetric, CachegrindMetrics};
133    ///
134    /// let metrics: Vec<CachegrindMetrics> = vec![
135    ///     CachegrindMetric::I1mr.into(),
136    ///     CachegrindMetric::D1mr.into(),
137    ///     CachegrindMetric::D1mw.into(),
138    ///     CachegrindMetric::ILmr.into(),
139    ///     CachegrindMetric::DLmr.into(),
140    ///     CachegrindMetric::DLmw.into(),
141    /// ];
142    /// ```
143    CacheMisses,
144
145    /// The cache miss rates calculated from the [`CallgrindMetrics::CacheMisses`] produced by
146    /// `--cache-sim`:
147    ///
148    /// ```rust
149    /// # pub mod iai_callgrind {
150    /// # pub use iai_callgrind_runner::api::{CachegrindMetric, CachegrindMetrics};
151    /// # }
152    /// use iai_callgrind::{CachegrindMetric, CachegrindMetrics};
153    ///
154    /// let metrics: Vec<CachegrindMetrics> = vec![
155    ///     CachegrindMetric::I1MissRate.into(),
156    ///     CachegrindMetric::LLiMissRate.into(),
157    ///     CachegrindMetric::D1MissRate.into(),
158    ///     CachegrindMetric::LLdMissRate.into(),
159    ///     CachegrindMetric::LLMissRate.into(),
160    /// ];
161    /// ```
162    CacheMissRates,
163
164    /// `CacheHits` are iai-callgrind specific and calculated from the metrics produced by
165    /// `--cache-sim=yes` in this order:
166    ///
167    /// ```
168    /// # pub mod iai_callgrind {
169    /// # pub use iai_callgrind_runner::api::{CachegrindMetric, CachegrindMetrics};
170    /// # }
171    /// use iai_callgrind::{CachegrindMetric, CachegrindMetrics};
172    ///
173    /// let metrics: Vec<CachegrindMetrics> = vec![
174    ///     CachegrindMetric::L1hits.into(),
175    ///     CachegrindMetric::LLhits.into(),
176    ///     CachegrindMetric::RamHits.into(),
177    /// ];
178    /// ```
179    CacheHits,
180
181    /// The cache hit rates calculated from the [`CachegrindMetrics::CacheHits`]:
182    ///
183    /// ```
184    /// # pub mod iai_callgrind {
185    /// # pub use iai_callgrind_runner::api::{CachegrindMetric, CachegrindMetrics};
186    /// # }
187    /// use iai_callgrind::{CachegrindMetric, CachegrindMetrics};
188    ///
189    /// let metrics: Vec<CachegrindMetrics> = vec![
190    ///     CachegrindMetric::L1HitRate.into(),
191    ///     CachegrindMetric::LLHitRate.into(),
192    ///     CachegrindMetric::RamHitRate.into(),
193    /// ];
194    /// ```
195    CacheHitRates,
196
197    /// All metrics produced by `--cache-sim=yes` including the iai-callgrind specific metrics
198    /// [`CachegrindMetric::L1hits`], [`CachegrindMetric::LLhits`], [`CachegrindMetric::RamHits`],
199    /// [`CachegrindMetric::TotalRW`], [`CachegrindMetric::EstimatedCycles`],
200    /// [`CachegrindMetrics::CacheMissRates`] and [`CachegrindMetrics::CacheHitRates`] in this
201    /// order:
202    ///
203    /// ```rust
204    /// # pub mod iai_callgrind {
205    /// # pub use iai_callgrind_runner::api::{CachegrindMetric, CachegrindMetrics};
206    /// # }
207    /// use iai_callgrind::{CachegrindMetric, CachegrindMetrics};
208    ///
209    /// let metrics: Vec<CachegrindMetrics> = vec![
210    ///     CachegrindMetric::Dr.into(),
211    ///     CachegrindMetric::Dw.into(),
212    ///     CachegrindMetrics::CacheMisses,
213    ///     CachegrindMetrics::CacheMissRates,
214    ///     CachegrindMetrics::CacheHits,
215    ///     CachegrindMetrics::CacheHitRates,
216    ///     CachegrindMetric::TotalRW.into(),
217    ///     CachegrindMetric::EstimatedCycles.into(),
218    /// ];
219    /// ```
220    CacheSim,
221
222    /// The metrics produced by `--branch-sim=yes` in this order:
223    ///
224    /// ```rust
225    /// # pub mod iai_callgrind {
226    /// # pub use iai_callgrind_runner::api::{CachegrindMetric, CachegrindMetrics};
227    /// # }
228    /// use iai_callgrind::{CachegrindMetric, CachegrindMetrics};
229    ///
230    /// let metrics: Vec<CachegrindMetrics> = vec![
231    ///     CachegrindMetric::Bc.into(),
232    ///     CachegrindMetric::Bcm.into(),
233    ///     CachegrindMetric::Bi.into(),
234    ///     CachegrindMetric::Bim.into(),
235    /// ];
236    /// ```
237    BranchSim,
238
239    /// All possible [`CachegrindMetric`]s in this order:
240    ///
241    /// ```rust
242    /// # pub mod iai_callgrind {
243    /// # pub use iai_callgrind_runner::api::{CachegrindMetric, CachegrindMetrics};
244    /// # }
245    /// use iai_callgrind::{CachegrindMetric, CachegrindMetrics};
246    ///
247    /// let metrics: Vec<CachegrindMetrics> = vec![
248    ///     CachegrindMetric::Ir.into(),
249    ///     CachegrindMetrics::CacheSim,
250    ///     CachegrindMetrics::BranchSim,
251    /// ];
252    /// ```
253    All,
254
255    /// Selection of no [`CachegrindMetric`] at all
256    None,
257
258    /// Specify a single [`CachegrindMetric`].
259    ///
260    /// Note that [`CachegrindMetric`] implements the necessary traits to convert to the
261    /// `CachegrindMetrics::SingleEvent` variant.
262    ///
263    /// # Examples
264    ///
265    /// ```rust
266    /// # pub mod iai_callgrind {
267    /// # pub use iai_callgrind_runner::api::{CachegrindMetric, CachegrindMetrics};
268    /// # }
269    /// use iai_callgrind::{CachegrindMetric, CachegrindMetrics};
270    ///
271    /// assert_eq!(
272    ///     CachegrindMetrics::SingleEvent(CachegrindMetric::Ir),
273    ///     CachegrindMetric::Ir.into()
274    /// );
275    /// ```
276    SingleEvent(CachegrindMetric),
277}
278
279/// A collection of groups of [`EventKind`]s
280///
281/// `Callgrind` supports a large amount of metrics and their collection can be enabled with various
282/// command-line flags. [`CallgrindMetrics`] groups these metrics to make it less cumbersome to
283/// specify multiple [`EventKind`]s at once if necessary.
284#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord)]
285#[non_exhaustive]
286pub enum CallgrindMetrics {
287    /// The default group contains all event kinds except the [`CallgrindMetrics::CacheMisses`],
288    /// [`CallgrindMetrics::CacheMissRates`], [`CallgrindMetrics::CacheHitRates`] and
289    /// [`EventKind::Dr`], [`EventKind::Dw`]. More specifically, the following event kinds and
290    /// groups in this order:
291    ///
292    /// ```rust
293    /// # pub mod iai_callgrind {
294    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
295    /// # }
296    /// use iai_callgrind::{CallgrindMetrics, EventKind};
297    ///
298    /// let metrics: Vec<CallgrindMetrics> = vec![
299    ///     EventKind::Ir.into(),
300    ///     CallgrindMetrics::CacheHits,
301    ///     EventKind::TotalRW.into(),
302    ///     EventKind::EstimatedCycles.into(),
303    ///     CallgrindMetrics::SystemCalls,
304    ///     EventKind::Ge.into(),
305    ///     CallgrindMetrics::BranchSim,
306    ///     CallgrindMetrics::WriteBackBehaviour,
307    ///     CallgrindMetrics::CacheUse,
308    /// ];
309    /// ```
310    #[default]
311    Default,
312
313    /// The `CacheMisses` produced by `--cache-sim=yes` contain the following [`EventKind`]s in
314    /// this order:
315    ///
316    /// ```rust
317    /// # pub mod iai_callgrind {
318    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
319    /// # }
320    /// use iai_callgrind::{CallgrindMetrics, EventKind};
321    ///
322    /// let metrics: Vec<CallgrindMetrics> = vec![
323    ///     EventKind::I1mr.into(),
324    ///     EventKind::D1mr.into(),
325    ///     EventKind::D1mw.into(),
326    ///     EventKind::ILmr.into(),
327    ///     EventKind::DLmr.into(),
328    ///     EventKind::DLmw.into(),
329    /// ];
330    /// ```
331    CacheMisses,
332
333    /// The cache miss rates calculated from the [`CallgrindMetrics::CacheMisses`] produced by
334    /// `--cache-sim`:
335    ///
336    /// ```rust
337    /// # pub mod iai_callgrind {
338    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
339    /// # }
340    /// use iai_callgrind::{CallgrindMetrics, EventKind};
341    ///
342    /// let metrics: Vec<CallgrindMetrics> = vec![
343    ///     EventKind::I1MissRate.into(),
344    ///     EventKind::D1MissRate.into(),
345    ///     EventKind::LLiMissRate.into(),
346    ///     EventKind::LLdMissRate.into(),
347    ///     EventKind::LLMissRate.into(),
348    /// ];
349    /// ```
350    CacheMissRates,
351
352    /// `CacheHits` are iai-callgrind specific and calculated from the metrics produced by
353    /// `--cache-sim=yes` in this order:
354    ///
355    /// ```
356    /// # pub mod iai_callgrind {
357    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
358    /// # }
359    /// use iai_callgrind::{CallgrindMetrics, EventKind};
360    ///
361    /// let metrics: Vec<CallgrindMetrics> = vec![
362    ///     EventKind::L1hits.into(),
363    ///     EventKind::LLhits.into(),
364    ///     EventKind::RamHits.into(),
365    /// ];
366    /// ```
367    CacheHits,
368
369    /// The cache hit rates calculated from the [`CallgrindMetrics::CacheHits`]:
370    ///
371    /// ```
372    /// # pub mod iai_callgrind {
373    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
374    /// # }
375    /// use iai_callgrind::{CallgrindMetrics, EventKind};
376    ///
377    /// let metrics: Vec<CallgrindMetrics> = vec![
378    ///     EventKind::L1HitRate.into(),
379    ///     EventKind::LLHitRate.into(),
380    ///     EventKind::RamHitRate.into(),
381    /// ];
382    /// ```
383    CacheHitRates,
384
385    /// All metrics produced by `--cache-sim=yes` including the iai-callgrind specific metrics
386    /// [`EventKind::L1hits`], [`EventKind::LLhits`], [`EventKind::RamHits`],
387    /// [`EventKind::TotalRW`], [`EventKind::EstimatedCycles`] and miss/hit rates in this order:
388    ///
389    /// ```rust
390    /// # pub mod iai_callgrind {
391    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
392    /// # }
393    /// use iai_callgrind::{CallgrindMetrics, EventKind};
394    ///
395    /// let metrics: Vec<CallgrindMetrics> = vec![
396    ///     EventKind::Dr.into(),
397    ///     EventKind::Dw.into(),
398    ///     CallgrindMetrics::CacheMisses,
399    ///     CallgrindMetrics::CacheMissRates,
400    ///     CallgrindMetrics::CacheHits,
401    ///     EventKind::TotalRW.into(),
402    ///     CallgrindMetrics::CacheHitRates,
403    ///     EventKind::EstimatedCycles.into(),
404    /// ];
405    /// ```
406    CacheSim,
407
408    /// The metrics produced by `--cacheuse=yes` in this order:
409    ///
410    /// ```rust
411    /// # pub mod iai_callgrind {
412    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
413    /// # }
414    /// use iai_callgrind::{CallgrindMetrics, EventKind};
415    ///
416    /// let metrics: Vec<CallgrindMetrics> = vec![
417    ///     EventKind::AcCost1.into(),
418    ///     EventKind::AcCost2.into(),
419    ///     EventKind::SpLoss1.into(),
420    ///     EventKind::SpLoss2.into(),
421    /// ];
422    /// ```
423    CacheUse,
424
425    /// `SystemCalls` are events of the `--collect-systime=yes` option in this order:
426    ///
427    /// ```rust
428    /// # pub mod iai_callgrind {
429    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
430    /// # }
431    /// use iai_callgrind::{CallgrindMetrics, EventKind};
432    ///
433    /// let metrics: Vec<CallgrindMetrics> = vec![
434    ///     EventKind::SysCount.into(),
435    ///     EventKind::SysTime.into(),
436    ///     EventKind::SysCpuTime.into(),
437    /// ];
438    /// ```
439    SystemCalls,
440
441    /// The metrics produced by `--branch-sim=yes` in this order:
442    ///
443    /// ```rust
444    /// # pub mod iai_callgrind {
445    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
446    /// # }
447    /// use iai_callgrind::{CallgrindMetrics, EventKind};
448    ///
449    /// let metrics: Vec<CallgrindMetrics> = vec![
450    ///     EventKind::Bc.into(),
451    ///     EventKind::Bcm.into(),
452    ///     EventKind::Bi.into(),
453    ///     EventKind::Bim.into(),
454    /// ];
455    /// ```
456    BranchSim,
457
458    /// All metrics of `--simulate-wb=yes` in this order:
459    ///
460    /// ```rust
461    /// # pub mod iai_callgrind {
462    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
463    /// # }
464    /// use iai_callgrind::{CallgrindMetrics, EventKind};
465    ///
466    /// let metrics: Vec<CallgrindMetrics> = vec![
467    ///     EventKind::ILdmr.into(),
468    ///     EventKind::DLdmr.into(),
469    ///     EventKind::DLdmw.into(),
470    /// ];
471    /// ```
472    WriteBackBehaviour,
473
474    /// All possible [`EventKind`]s in this order:
475    ///
476    /// ```rust
477    /// # pub mod iai_callgrind {
478    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
479    /// # }
480    /// use iai_callgrind::{CallgrindMetrics, EventKind};
481    ///
482    /// let metrics: Vec<CallgrindMetrics> = vec![
483    ///     EventKind::Ir.into(),
484    ///     CallgrindMetrics::CacheSim,
485    ///     CallgrindMetrics::SystemCalls,
486    ///     EventKind::Ge.into(),
487    ///     CallgrindMetrics::BranchSim,
488    ///     CallgrindMetrics::WriteBackBehaviour,
489    ///     CallgrindMetrics::CacheUse,
490    /// ];
491    /// ```
492    All,
493
494    /// Selection of no [`EventKind`] at all
495    None,
496
497    /// Specify a single [`EventKind`].
498    ///
499    /// Note that [`EventKind`] implements the necessary traits to convert to the
500    /// `CallgrindMetrics::SingleEvent` variant which is shorter to write.
501    ///
502    /// # Examples
503    ///
504    /// ```rust
505    /// # pub mod iai_callgrind {
506    /// # pub use iai_callgrind_runner::api::{CallgrindMetrics, EventKind};
507    /// # }
508    /// use iai_callgrind::{CallgrindMetrics, EventKind};
509    ///
510    /// assert_eq!(
511    ///     CallgrindMetrics::SingleEvent(EventKind::Ir),
512    ///     EventKind::Ir.into()
513    /// );
514    /// ```
515    SingleEvent(EventKind),
516}
517
518/// The kind of `Delay`
519#[non_exhaustive]
520#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
521pub enum DelayKind {
522    /// Delay the `Command` for a fixed [`Duration`]
523    DurationElapse(Duration),
524    /// Delay the `Command` until a successful tcp connection can be established
525    TcpConnect(SocketAddr),
526    /// Delay the `Command` until a successful udp response was received
527    UdpResponse(SocketAddr, Vec<u8>),
528    /// Delay the `Command` until the specified path exists
529    PathExists(PathBuf),
530}
531
532/// The metrics collected by DHAT
533#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
534#[cfg_attr(feature = "schema", derive(JsonSchema))]
535#[cfg_attr(feature = "runner", derive(EnumIter))]
536pub enum DhatMetric {
537    /// In ad-hoc mode, Total units measured over the entire execution
538    TotalUnits,
539    /// Total ad-hoc events over the entire execution
540    TotalEvents,
541    /// Total bytes allocated over the entire execution
542    TotalBytes,
543    /// Total heap blocks allocated over the entire execution
544    TotalBlocks,
545    /// The bytes alive at t-gmax, the time when the heap size reached its global maximum
546    AtTGmaxBytes,
547    /// The blocks alive at t-gmax
548    AtTGmaxBlocks,
549    /// The amount of bytes at the end of the execution.
550    ///
551    /// This is the amount of bytes which were not explicitly freed.
552    AtTEndBytes,
553    /// The amount of blocks at the end of the execution.
554    ///
555    /// This is the amount of heap blocks which were not explicitly freed.
556    AtTEndBlocks,
557    /// The amount of bytes read during the entire execution
558    ReadsBytes,
559    /// The amount of bytes written during the entire execution
560    WritesBytes,
561    /// The total lifetimes of all heap blocks allocated
562    TotalLifetimes,
563    /// The maximum amount of bytes
564    MaximumBytes,
565    /// The maximum amount of heap blocks
566    MaximumBlocks,
567}
568
569/// A collection of groups of [`DhatMetric`]s
570///
571/// The members of each group are fully documented in the docs of each variant of this enum
572#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
573pub enum DhatMetrics {
574    /// The default group in this order
575    ///
576    /// ```rust
577    /// # pub mod iai_callgrind {
578    /// # pub use iai_callgrind_runner::api::{DhatMetrics, DhatMetric};
579    /// # }
580    /// use iai_callgrind::{DhatMetric, DhatMetrics};
581    ///
582    /// let metrics: Vec<DhatMetrics> = vec![
583    ///     DhatMetric::TotalUnits.into(),
584    ///     DhatMetric::TotalEvents.into(),
585    ///     DhatMetric::TotalBytes.into(),
586    ///     DhatMetric::TotalBlocks.into(),
587    ///     DhatMetric::AtTGmaxBytes.into(),
588    ///     DhatMetric::AtTGmaxBlocks.into(),
589    ///     DhatMetric::AtTEndBytes.into(),
590    ///     DhatMetric::AtTEndBlocks.into(),
591    ///     DhatMetric::ReadsBytes.into(),
592    ///     DhatMetric::WritesBytes.into(),
593    /// ];
594    /// ```
595    #[default]
596    Default,
597
598    /// All [`DhatMetric`]s in this order
599    ///
600    /// ```rust
601    /// # pub mod iai_callgrind {
602    /// # pub use iai_callgrind_runner::api::{DhatMetrics, DhatMetric};
603    /// # }
604    /// use iai_callgrind::{DhatMetric, DhatMetrics};
605    ///
606    /// let metrics: Vec<DhatMetrics> = vec![
607    ///     DhatMetrics::Default,
608    ///     DhatMetric::TotalLifetimes.into(),
609    ///     DhatMetric::MaximumBytes.into(),
610    ///     DhatMetric::MaximumBlocks.into(),
611    /// ];
612    /// ```
613    All,
614
615    /// A single [`DhatMetric`]
616    ///
617    /// ```rust
618    /// # pub mod iai_callgrind {
619    /// # pub use iai_callgrind_runner::api::{DhatMetrics, DhatMetric};
620    /// # }
621    /// use iai_callgrind::{DhatMetric, DhatMetrics};
622    ///
623    /// assert_eq!(
624    ///     DhatMetrics::SingleMetric(DhatMetric::TotalBytes),
625    ///     DhatMetric::TotalBytes.into()
626    /// );
627    /// ```
628    SingleMetric(DhatMetric),
629}
630
631/// The `Direction` in which the flamegraph should grow.
632///
633/// The default is `TopToBottom`.
634#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
635pub enum Direction {
636    /// Grow from top to bottom with the highest event costs at the top
637    TopToBottom,
638    /// Grow from bottom to top with the highest event costs at the bottom
639    BottomToTop,
640}
641
642/// The `EntryPoint` of a benchmark
643#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
644pub enum EntryPoint {
645    /// Disable the entry point
646    None,
647    /// The default entry point is the benchmark function
648    #[default]
649    Default,
650    /// A custom entry point. The argument allows the same glob patterns as the
651    /// [`--toggle-collect`](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options)
652    /// argument of callgrind. These are the wildcards `*` (match any amount of arbitrary
653    /// characters) and `?` (match a single arbitrary character)
654    Custom(String),
655}
656
657/// The error metrics from a tool which reports errors
658///
659/// The tools which report only errors are `helgrind`, `drd` and `memcheck`. The order in which the
660/// variants are defined in this enum determines the order of the metrics in the benchmark terminal
661/// output.
662#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
663#[cfg_attr(feature = "schema", derive(JsonSchema))]
664#[cfg_attr(feature = "runner", derive(EnumIter))]
665pub enum ErrorMetric {
666    /// The amount of detected unsuppressed errors
667    Errors,
668    /// The amount of detected unsuppressed error contexts
669    Contexts,
670    /// The amount of suppressed errors
671    SuppressedErrors,
672    /// The amount of suppressed error contexts
673    SuppressedContexts,
674}
675
676/// All `EventKind`s callgrind produces and additionally some derived events
677///
678/// Depending on the options passed to Callgrind, these are the events that Callgrind can produce.
679/// See the [Callgrind
680/// documentation](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options) for details.
681#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
682#[cfg_attr(feature = "schema", derive(JsonSchema))]
683#[cfg_attr(feature = "runner", derive(EnumIter))]
684pub enum EventKind {
685    /// The default event. I cache reads (which equals the number of instructions executed)
686    Ir,
687    /// D Cache reads (which equals the number of memory reads) (--cache-sim=yes)
688    Dr,
689    /// D Cache writes (which equals the number of memory writes) (--cache-sim=yes)
690    Dw,
691    /// I1 cache read misses (--cache-sim=yes)
692    I1mr,
693    /// D1 cache read misses (--cache-sim=yes)
694    D1mr,
695    /// D1 cache write misses (--cache-sim=yes)
696    D1mw,
697    /// LL cache instruction read misses (--cache-sim=yes)
698    ILmr,
699    /// LL cache data read misses (--cache-sim=yes)
700    DLmr,
701    /// LL cache data write misses (--cache-sim=yes)
702    DLmw,
703    /// I1 cache miss rate (--cache-sim=yes)
704    I1MissRate,
705    /// LL/L2 instructions cache miss rate (--cache-sim=yes)
706    LLiMissRate,
707    /// D1 cache miss rate (--cache-sim=yes)
708    D1MissRate,
709    /// LL/L2 data cache miss rate (--cache-sim=yes)
710    LLdMissRate,
711    /// LL/L2 cache miss rate (--cache-sim=yes)
712    LLMissRate,
713    /// Derived event showing the L1 hits (--cache-sim=yes)
714    L1hits,
715    /// Derived event showing the LL hits (--cache-sim=yes)
716    LLhits,
717    /// Derived event showing the RAM hits (--cache-sim=yes)
718    RamHits,
719    /// L1 cache hit rate (--cache-sim=yes)
720    L1HitRate,
721    /// LL/L2 cache hit rate (--cache-sim=yes)
722    LLHitRate,
723    /// RAM hit rate (--cache-sim=yes)
724    RamHitRate,
725    /// Derived event showing the total amount of cache reads and writes (--cache-sim=yes)
726    TotalRW,
727    /// Derived event showing estimated CPU cycles (--cache-sim=yes)
728    EstimatedCycles,
729    /// The number of system calls done (--collect-systime=yes)
730    SysCount,
731    /// The elapsed time spent in system calls (--collect-systime=yes)
732    SysTime,
733    /// The cpu time spent during system calls (--collect-systime=nsec)
734    SysCpuTime,
735    /// The number of global bus events (--collect-bus=yes)
736    Ge,
737    /// Conditional branches executed (--branch-sim=yes)
738    Bc,
739    /// Conditional branches mispredicted (--branch-sim=yes)
740    Bcm,
741    /// Indirect branches executed (--branch-sim=yes)
742    Bi,
743    /// Indirect branches mispredicted (--branch-sim=yes)
744    Bim,
745    /// Dirty miss because of instruction read (--simulate-wb=yes)
746    ILdmr,
747    /// Dirty miss because of data read (--simulate-wb=yes)
748    DLdmr,
749    /// Dirty miss because of data write (--simulate-wb=yes)
750    DLdmw,
751    /// Counter showing bad temporal locality for L1 caches (--cachuse=yes)
752    AcCost1,
753    /// Counter showing bad temporal locality for LL caches (--cachuse=yes)
754    AcCost2,
755    /// Counter showing bad spatial locality for L1 caches (--cachuse=yes)
756    SpLoss1,
757    /// Counter showing bad spatial locality for LL caches (--cachuse=yes)
758    SpLoss2,
759}
760
761/// Set the expected exit status of a benchmarked binary
762#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
763pub enum ExitWith {
764    /// Exit with success is similar to `ExitCode(0)`
765    Success,
766    /// Exit with failure is similar to setting the `ExitCode` to something different from `0`
767    /// without having to rely on a specific exit code
768    Failure,
769    /// The exact `ExitCode` of the benchmark run
770    Code(i32),
771}
772
773/// The kind of `Flamegraph` which is going to be constructed
774#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
775pub enum FlamegraphKind {
776    /// The regular flamegraph for the new callgrind run
777    Regular,
778    /// A differential flamegraph showing the differences between the new and old callgrind run
779    Differential,
780    /// All flamegraph kinds that can be constructed (`Regular` and `Differential`). This
781    /// is the default.
782    All,
783    /// Do not produce any flamegraphs
784    None,
785}
786
787/// A `Limit` which can be either an integer or a float
788///
789/// Depending on the metric the type of the hard limit is a float or an integer. For example
790/// [`EventKind::Ir`] is an integer and [`EventKind::L1HitRate`] is a percentage and therefore a
791/// float.
792///
793/// The type of the metric can be seen in the terminal output of Iai-Callgrind: Floats always
794/// contain a `.` and integers do not.
795#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
796pub enum Limit {
797    /// An integer `Limit`. For example [`EventKind::Ir`]
798    Int(u64),
799    /// A float `Limit`. For example [`EventKind::L1HitRate`] or [`EventKind::I1MissRate`]
800    Float(f64),
801}
802
803/// Configure the `Stream` which should be used as pipe in [`Stdin::Setup`]
804///
805/// The default is [`Pipe::Stdout`]
806#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
807pub enum Pipe {
808    /// The `Stdout` default `Stream`
809    #[default]
810    Stdout,
811    /// The `Stderr` error `Stream`
812    Stderr,
813}
814
815/// This is a special `Stdio` for the stdin method of [`Command`]
816///
817/// Contains all the standard [`Stdio`] options and the [`Stdin::Setup`] option
818#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
819pub enum Stdin {
820    /// Using this in [`Command::stdin`] pipes the stream specified with [`Pipe`] of the `setup`
821    /// function into the `Stdin` of the [`Command`]. In this case the `setup` and [`Command`] are
822    /// executed in parallel instead of sequentially. See [`Command::stdin`] for more details.
823    Setup(Pipe),
824    #[default]
825    /// See [`Stdio::Inherit`]
826    Inherit,
827    /// See [`Stdio::Null`]
828    Null,
829    /// See [`Stdio::File`]
830    File(PathBuf),
831    /// See [`Stdio::Pipe`]
832    Pipe,
833}
834
835/// Configure the `Stdio` of `Stdin`, `Stdout` and `Stderr`
836///
837/// Describes what to do with a standard I/O stream for the [`Command`] when passed to the stdin,
838/// stdout, and stderr methods of [`Command`].
839#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
840pub enum Stdio {
841    /// The [`Command`]'s `Stream` inherits from the benchmark runner.
842    #[default]
843    Inherit,
844    /// This stream will be ignored. This is the equivalent of attaching the stream to `/dev/null`
845    Null,
846    /// Redirect the content of a file into this `Stream`. This is equivalent to a redirection in a
847    /// shell for example for the `Stdout` of `my-command`: `my-command > some_file`
848    File(PathBuf),
849    /// A new pipe should be arranged to connect the benchmark runner and the [`Command`]
850    Pipe,
851}
852
853/// We use this enum only internally in the benchmark runner
854#[cfg(feature = "runner")]
855#[derive(Debug, Clone, Copy, PartialEq, Eq)]
856pub(crate) enum Stream {
857    Stdin,
858    Stderr,
859    Stdout,
860}
861
862/// The tool specific flamegraph configuration
863#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
864pub enum ToolFlamegraphConfig {
865    /// The callgrind configuration
866    Callgrind(FlamegraphConfig),
867    /// The option for tools which can't create flamegraphs
868    None,
869}
870
871/// The tool specific metrics to show in the terminal output
872#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
873pub enum ToolOutputFormat {
874    /// The Callgrind configuration
875    Callgrind(Vec<CallgrindMetrics>),
876    /// The Cachegrind configuration
877    Cachegrind(Vec<CachegrindMetrics>),
878    /// The DHAT configuration
879    DHAT(Vec<DhatMetric>),
880    /// The Memcheck configuration
881    Memcheck(Vec<ErrorMetric>),
882    /// The Helgrind configuration
883    Helgrind(Vec<ErrorMetric>),
884    /// The DRD configuration
885    DRD(Vec<ErrorMetric>),
886    /// If there is no configuration
887    None,
888}
889
890/// The tool specific regression check configuration
891#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
892pub enum ToolRegressionConfig {
893    /// The cachegrind configuration
894    Cachegrind(CachegrindRegressionConfig),
895    /// The callgrind configuration
896    Callgrind(CallgrindRegressionConfig),
897    /// The dhat configuration
898    Dhat(DhatRegressionConfig),
899    /// The option for tools which don't perform regression checks
900    None,
901}
902
903/// The valgrind tools which can be run
904///
905/// Note the default changes from `Callgrind` to `Cachegrind` if the `cachegrind` feature is
906/// selected.
907#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
908#[cfg_attr(feature = "schema", derive(JsonSchema))]
909pub enum ValgrindTool {
910    /// [Callgrind: a call-graph generating cache and branch prediction profiler](https://valgrind.org/docs/manual/cl-manual.html)
911    Callgrind,
912    /// [Cachegrind: a high-precision tracing profiler](https://valgrind.org/docs/manual/cg-manual.html)
913    Cachegrind,
914    /// [DHAT: a dynamic heap analysis tool](https://valgrind.org/docs/manual/dh-manual.html)
915    DHAT,
916    /// [Memcheck: a memory error detector](https://valgrind.org/docs/manual/mc-manual.html)
917    Memcheck,
918    /// [Helgrind: a thread error detector](https://valgrind.org/docs/manual/hg-manual.html)
919    Helgrind,
920    /// [DRD: a thread error detector](https://valgrind.org/docs/manual/drd-manual.html)
921    DRD,
922    /// [Massif: a heap profiler](https://valgrind.org/docs/manual/ms-manual.html)
923    Massif,
924    /// [BBV: an experimental basic block vector generation tool](https://valgrind.org/docs/manual/bbv-manual.html)
925    BBV,
926}
927
928/// The model for the `#[binary_benchmark]` attribute or the equivalent from the low level api
929///
930/// For internal use only
931#[derive(Debug, Clone, Default, Serialize, Deserialize)]
932pub struct BinaryBenchmark {
933    /// The extracted binary benchmarks
934    pub benches: Vec<BinaryBenchmarkBench>,
935    /// The configuration at `#[binary_benchmark]` level
936    pub config: Option<BinaryBenchmarkConfig>,
937}
938
939/// The model for the `#[bench]` attribute or the low level equivalent
940///
941/// For internal use only
942#[derive(Debug, Clone, Default, Serialize, Deserialize)]
943pub struct BinaryBenchmarkBench {
944    /// The arguments to the function
945    pub args: Option<String>,
946    /// The returned [`Command`]
947    pub command: Command,
948    /// The configuration at `#[bench]` or `#[benches]` level
949    pub config: Option<BinaryBenchmarkConfig>,
950    /// The name of the annotated function
951    pub function_name: String,
952    /// True if there is a `setup` function
953    pub has_setup: bool,
954    /// True if there is a `teardown` function
955    pub has_teardown: bool,
956    /// The `id` of the benchmark as in `#[bench::id]`
957    pub id: Option<String>,
958}
959
960/// The model for the configuration in binary benchmarks
961///
962/// This is the configuration which is built from the configuration of the UI and for internal use
963/// only.
964#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
965pub struct BinaryBenchmarkConfig {
966    /// If some, set the the working directory of the benchmarked binary to this path
967    pub current_dir: Option<PathBuf>,
968    /// The valgrind tool to run instead of the default callgrind
969    pub default_tool: Option<ValgrindTool>,
970    /// True if the environment variables should be cleared
971    pub env_clear: Option<bool>,
972    /// The environment variables to set or pass through to the binary
973    pub envs: Vec<(OsString, Option<OsString>)>,
974    /// The [`ExitWith`] to set the expected exit code/signal of the benchmarked binary
975    pub exit_with: Option<ExitWith>,
976    /// The configuration of the output format
977    pub output_format: Option<OutputFormat>,
978    /// Run the benchmarked binary in a [`Sandbox`] or not
979    pub sandbox: Option<Sandbox>,
980    /// Run the `setup` function parallel to the benchmarked binary
981    pub setup_parallel: Option<bool>,
982    /// The valgrind tools to run in addition to the default tool
983    pub tools: Tools,
984    /// The tool override at this configuration level
985    pub tools_override: Option<Tools>,
986    /// The arguments to pass to all tools
987    pub valgrind_args: RawArgs,
988}
989
990/// The model for the `binary_benchmark_group` macro
991#[derive(Debug, Clone, Default, Serialize, Deserialize)]
992pub struct BinaryBenchmarkGroup {
993    /// The actual data and the benchmarks of this group
994    pub binary_benchmarks: Vec<BinaryBenchmark>,
995    /// If true compare the benchmarks in this group
996    pub compare_by_id: Option<bool>,
997    /// The configuration at this level
998    pub config: Option<BinaryBenchmarkConfig>,
999    /// True if there is a `setup` function
1000    pub has_setup: bool,
1001    /// True if there is a `teardown` function
1002    pub has_teardown: bool,
1003    /// The name or id of the `binary_benchmark_group!`
1004    pub id: String,
1005}
1006
1007/// The model for the main! macro
1008#[derive(Debug, Clone, Serialize, Deserialize)]
1009pub struct BinaryBenchmarkGroups {
1010    /// The command line arguments as we receive them from `cargo bench`
1011    pub command_line_args: Vec<String>,
1012    /// The configuration of this level
1013    pub config: BinaryBenchmarkConfig,
1014    /// The default tool changed by the `cachegrind` feature
1015    pub default_tool: ValgrindTool,
1016    /// All groups of this benchmark
1017    pub groups: Vec<BinaryBenchmarkGroup>,
1018    /// True if there is a `setup` function
1019    pub has_setup: bool,
1020    /// True if there is a `teardown` function
1021    pub has_teardown: bool,
1022}
1023
1024/// The model for the regression check configuration of Cachegrind
1025#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1026pub struct CachegrindRegressionConfig {
1027    /// True if the benchmarks should fail on the first occurrence of a regression
1028    pub fail_fast: Option<bool>,
1029    /// The hard limits
1030    pub hard_limits: Vec<(CachegrindMetrics, Limit)>,
1031    /// The soft limits
1032    pub soft_limits: Vec<(CachegrindMetrics, f64)>,
1033}
1034
1035/// The model for the regression check configuration of Callgrind
1036#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1037pub struct CallgrindRegressionConfig {
1038    /// True if the benchmarks should fail on the first occurrence of a regression
1039    pub fail_fast: Option<bool>,
1040    /// The hard limits
1041    pub hard_limits: Vec<(CallgrindMetrics, Limit)>,
1042    /// The soft limits
1043    pub soft_limits: Vec<(CallgrindMetrics, f64)>,
1044}
1045
1046/// The model for the command returned by the binary benchmark function
1047#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1048pub struct Command {
1049    /// The arguments for the executable
1050    pub args: Vec<OsString>,
1051    /// The configuration at this level
1052    pub config: BinaryBenchmarkConfig,
1053    /// If present the command is delayed as configured in [`Delay`]
1054    pub delay: Option<Delay>,
1055    /// The path to the executable
1056    pub path: PathBuf,
1057    /// The command's stderr
1058    pub stderr: Option<Stdio>,
1059    /// The command's stdin
1060    pub stdin: Option<Stdin>,
1061    /// The command's stdout
1062    pub stdout: Option<Stdio>,
1063}
1064
1065/// The delay of the [`Command`]
1066#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
1067pub struct Delay {
1068    /// The kind of delay
1069    pub kind: DelayKind,
1070    /// The polling time to check the delay condition
1071    pub poll: Option<Duration>,
1072    /// The timeout for the delay
1073    pub timeout: Option<Duration>,
1074}
1075
1076/// The model for the regression check configuration of DHAT
1077#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1078pub struct DhatRegressionConfig {
1079    /// True if the benchmarks should fail on the first occurrence of a regression
1080    pub fail_fast: Option<bool>,
1081    /// The hard limits
1082    pub hard_limits: Vec<(DhatMetrics, Limit)>,
1083    /// The soft limits
1084    pub soft_limits: Vec<(DhatMetrics, f64)>,
1085}
1086
1087/// The fixtures to copy into the [`Sandbox`]
1088#[derive(Debug, Clone, Serialize, Deserialize)]
1089pub struct Fixtures {
1090    /// If true, follow symlinks
1091    pub follow_symlinks: bool,
1092    /// The path to the fixtures
1093    pub path: PathBuf,
1094}
1095
1096/// The model for the configuration of flamegraphs
1097#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1098pub struct FlamegraphConfig {
1099    /// The direction of the flamegraph. Top to bottom or vice versa
1100    pub direction: Option<Direction>,
1101    /// The event kinds for which a flamegraph should be generated
1102    pub event_kinds: Option<Vec<EventKind>>,
1103    /// The flamegraph kind
1104    pub kind: Option<FlamegraphKind>,
1105    /// The minimum width which should be displayed
1106    pub min_width: Option<f64>,
1107    /// If true, negate a differential flamegraph
1108    pub negate_differential: Option<bool>,
1109    /// If true, normalize a differential flamegraph
1110    pub normalize_differential: Option<bool>,
1111    /// The subtitle to use for the flamegraphs
1112    pub subtitle: Option<String>,
1113    /// The title to use for the flamegraphs
1114    pub title: Option<String>,
1115}
1116
1117/// The model for the `#[library_benchmark]` attribute
1118#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1119pub struct LibraryBenchmark {
1120    /// The extracted benchmarks of the annotated function
1121    pub benches: Vec<LibraryBenchmarkBench>,
1122    /// The configuration at this level
1123    pub config: Option<LibraryBenchmarkConfig>,
1124}
1125
1126/// The model for the `#[bench]` attribute in a `#[library_benchmark]`
1127#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1128pub struct LibraryBenchmarkBench {
1129    /// The arguments for the function
1130    pub args: Option<String>,
1131    /// The configuration at this level
1132    pub config: Option<LibraryBenchmarkConfig>,
1133    /// The name of the function
1134    pub function_name: String,
1135    /// The id of the attribute as in `#[bench::id]`
1136    pub id: Option<String>,
1137}
1138
1139/// The model for the configuration in library benchmarks
1140///
1141/// This is the configuration which is built from the configuration of the UI and for internal use
1142/// only.
1143#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1144pub struct LibraryBenchmarkConfig {
1145    /// The valgrind tool to run instead of the default callgrind
1146    pub default_tool: Option<ValgrindTool>,
1147    /// True if the environment variables should be cleared
1148    pub env_clear: Option<bool>,
1149    /// The environment variables to set or pass through to the binary
1150    pub envs: Vec<(OsString, Option<OsString>)>,
1151    /// The configuration of the output format
1152    pub output_format: Option<OutputFormat>,
1153    /// The valgrind tools to run in addition to the default tool
1154    pub tools: Tools,
1155    /// The tool override at this configuration level
1156    pub tools_override: Option<Tools>,
1157    /// The arguments to pass to all tools
1158    pub valgrind_args: RawArgs,
1159}
1160
1161/// The model for the `library_benchmark_group` macro
1162#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1163pub struct LibraryBenchmarkGroup {
1164    /// If true compare the benchmarks in this group
1165    pub compare_by_id: Option<bool>,
1166    /// The configuration at this level
1167    pub config: Option<LibraryBenchmarkConfig>,
1168    /// True if there is a `setup` function
1169    pub has_setup: bool,
1170    /// True if there is a `teardown` function
1171    pub has_teardown: bool,
1172    /// The name or id of the `library_benchmark_group!`
1173    pub id: String,
1174    /// The actual data and the benchmarks of this group
1175    pub library_benchmarks: Vec<LibraryBenchmark>,
1176}
1177
1178/// The model for the `main` macro
1179#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1180pub struct LibraryBenchmarkGroups {
1181    /// The command line args as we receive them from `cargo bench`
1182    pub command_line_args: Vec<String>,
1183    /// The configuration of this level
1184    pub config: LibraryBenchmarkConfig,
1185    /// The default tool changed by the `cachegrind` feature
1186    pub default_tool: ValgrindTool,
1187    /// All groups of this benchmark
1188    pub groups: Vec<LibraryBenchmarkGroup>,
1189    /// True if there is a `setup` function
1190    pub has_setup: bool,
1191    /// True if there is a `teardown` function
1192    pub has_teardown: bool,
1193}
1194
1195/// The configuration values for the output format
1196#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1197pub struct OutputFormat {
1198    /// Show a grid instead of spaces in the terminal output
1199    pub show_grid: Option<bool>,
1200    /// Show intermediate results, for example in benchmarks for multi-threaded applications
1201    pub show_intermediate: Option<bool>,
1202    /// Don't show differences within the tolerance margin
1203    pub tolerance: Option<f64>,
1204    /// If set, truncate the description
1205    pub truncate_description: Option<Option<usize>>,
1206}
1207
1208/// The raw arguments to pass to a valgrind tool
1209#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
1210pub struct RawArgs(pub Vec<String>);
1211
1212/// The sandbox to run the benchmarks in
1213#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
1214pub struct Sandbox {
1215    /// If this sandbox is enabled or not
1216    pub enabled: Option<bool>,
1217    /// The fixtures to copy into the sandbox
1218    pub fixtures: Vec<PathBuf>,
1219    /// If true follow symlinks when copying the fixtures
1220    pub follow_symlinks: Option<bool>,
1221}
1222
1223/// The tool configuration
1224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1225pub struct Tool {
1226    /// If true the tool is run. Ignored for the default tool which always runs
1227    pub enable: Option<bool>,
1228    /// The entry point for the tool
1229    pub entry_point: Option<EntryPoint>,
1230    /// The configuration for flamegraphs
1231    pub flamegraph_config: Option<ToolFlamegraphConfig>,
1232    /// Any frames in the call stack which should be considered in addition to the entry point
1233    pub frames: Option<Vec<String>>,
1234    /// The valgrind tool this configuration is for
1235    pub kind: ValgrindTool,
1236    /// The configuration of the output format
1237    pub output_format: Option<ToolOutputFormat>,
1238    /// The arguments to pass to the tool
1239    pub raw_args: RawArgs,
1240    /// The configuration for regression checks of tools which perform regression checks
1241    pub regression_config: Option<ToolRegressionConfig>,
1242    /// If true show the logging output of Valgrind (not Iai-Callgrind)
1243    pub show_log: Option<bool>,
1244}
1245
1246/// The configurations of all tools to run in addition to the default tool
1247#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1248pub struct Tools(pub Vec<Tool>);
1249
1250impl BinaryBenchmarkConfig {
1251    /// Update this configuration with all other configurations in the given order
1252    #[must_use]
1253    pub fn update_from_all<'a, T>(mut self, others: T) -> Self
1254    where
1255        T: IntoIterator<Item = Option<&'a Self>>,
1256    {
1257        for other in others.into_iter().flatten() {
1258            self.default_tool = update_option(&self.default_tool, &other.default_tool);
1259            self.env_clear = update_option(&self.env_clear, &other.env_clear);
1260            self.current_dir = update_option(&self.current_dir, &other.current_dir);
1261            self.exit_with = update_option(&self.exit_with, &other.exit_with);
1262
1263            self.valgrind_args
1264                .extend_ignore_flag(other.valgrind_args.0.iter());
1265
1266            self.envs.extend_from_slice(&other.envs);
1267
1268            if let Some(other_tools) = &other.tools_override {
1269                self.tools = other_tools.clone();
1270            } else if !other.tools.is_empty() {
1271                self.tools.update_from_other(&other.tools);
1272            } else {
1273                // do nothing
1274            }
1275
1276            self.sandbox = update_option(&self.sandbox, &other.sandbox);
1277            self.setup_parallel = update_option(&self.setup_parallel, &other.setup_parallel);
1278            self.output_format = update_option(&self.output_format, &other.output_format);
1279        }
1280        self
1281    }
1282
1283    /// Resolve the environment variables and create key, value pairs out of them
1284    ///
1285    /// This is done especially for pass-through environment variables which have a `None` value at
1286    /// first.
1287    pub fn resolve_envs(&self) -> Vec<(OsString, OsString)> {
1288        self.envs
1289            .iter()
1290            .filter_map(|(key, value)| {
1291                value.as_ref().map_or_else(
1292                    || std::env::var_os(key).map(|value| (key.clone(), value)),
1293                    |value| Some((key.clone(), value.clone())),
1294                )
1295            })
1296            .collect()
1297    }
1298
1299    /// Collect all environment variables which don't have a `None` value
1300    ///
1301    /// Pass-through variables have a `None` value.
1302    pub fn collect_envs(&self) -> Vec<(OsString, OsString)> {
1303        self.envs
1304            .iter()
1305            .filter_map(|(key, option)| option.as_ref().map(|value| (key.clone(), value.clone())))
1306            .collect()
1307    }
1308}
1309
1310impl CachegrindMetric {
1311    /// Return true if this `EventKind` is a derived event
1312    ///
1313    /// Derived events are calculated from Cachegrind's native event types the same ways as for
1314    /// callgrind's [`EventKind`]
1315    ///
1316    /// * [`CachegrindMetric::L1hits`]
1317    /// * [`CachegrindMetric::LLhits`]
1318    /// * [`CachegrindMetric::RamHits`]
1319    /// * [`CachegrindMetric::TotalRW`]
1320    /// * [`CachegrindMetric::EstimatedCycles`]
1321    /// * [`CachegrindMetric::I1MissRate`]
1322    /// * [`CachegrindMetric::D1MissRate`]
1323    /// * [`CachegrindMetric::LLiMissRate`]
1324    /// * [`CachegrindMetric::LLdMissRate`]
1325    /// * [`CachegrindMetric::LLMissRate`]
1326    /// * [`CachegrindMetric::L1HitRate`]
1327    /// * [`CachegrindMetric::LLHitRate`]
1328    /// * [`CachegrindMetric::RamHitRate`]
1329    pub fn is_derived(&self) -> bool {
1330        matches!(
1331            self,
1332            Self::L1hits
1333                | Self::LLhits
1334                | Self::RamHits
1335                | Self::TotalRW
1336                | Self::EstimatedCycles
1337                | Self::I1MissRate
1338                | Self::D1MissRate
1339                | Self::LLiMissRate
1340                | Self::LLdMissRate
1341                | Self::LLMissRate
1342                | Self::L1HitRate
1343                | Self::LLHitRate
1344                | Self::RamHitRate
1345        )
1346    }
1347
1348    /// Return the name of the metric which is the exact name of the enum variant
1349    pub fn to_name(&self) -> String {
1350        format!("{:?}", *self)
1351    }
1352}
1353
1354impl Display for CachegrindMetric {
1355    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1356        match self {
1357            key @ (Self::Ir
1358            | Self::L1hits
1359            | Self::LLhits
1360            | Self::RamHits
1361            | Self::TotalRW
1362            | Self::EstimatedCycles
1363            | Self::I1MissRate
1364            | Self::D1MissRate
1365            | Self::LLiMissRate
1366            | Self::LLdMissRate
1367            | Self::LLMissRate
1368            | Self::L1HitRate
1369            | Self::LLHitRate
1370            | Self::RamHitRate) => write!(f, "{}", EventKind::from(*key)),
1371            _ => write!(f, "{self:?}"),
1372        }
1373    }
1374}
1375
1376#[cfg(feature = "runner")]
1377impl FromStr for CachegrindMetric {
1378    type Err = anyhow::Error;
1379
1380    fn from_str(string: &str) -> Result<Self, Self::Err> {
1381        let lower = string.to_lowercase();
1382        let metric = match lower.as_str() {
1383            "instructions" | "ir" => Self::Ir,
1384            "dr" => Self::Dr,
1385            "dw" => Self::Dw,
1386            "i1mr" => Self::I1mr,
1387            "ilmr" => Self::ILmr,
1388            "d1mr" => Self::D1mr,
1389            "dlmr" => Self::DLmr,
1390            "d1mw" => Self::D1mw,
1391            "dlmw" => Self::DLmw,
1392            "bc" => Self::Bc,
1393            "bcm" => Self::Bcm,
1394            "bi" => Self::Bi,
1395            "bim" => Self::Bim,
1396            "l1hits" => Self::L1hits,
1397            "llhits" => Self::LLhits,
1398            "ramhits" => Self::RamHits,
1399            "totalrw" => Self::TotalRW,
1400            "estimatedcycles" => Self::EstimatedCycles,
1401            "i1missrate" => Self::I1MissRate,
1402            "d1missrate" => Self::D1MissRate,
1403            "llimissrate" => Self::LLiMissRate,
1404            "lldmissrate" => Self::LLdMissRate,
1405            "llmissrate" => Self::LLMissRate,
1406            "l1hitrate" => Self::L1HitRate,
1407            "llhitrate" => Self::LLHitRate,
1408            "ramhitrate" => Self::RamHitRate,
1409            _ => return Err(anyhow!("Unknown cachegrind metric: '{string}'")),
1410        };
1411
1412        Ok(metric)
1413    }
1414}
1415
1416#[cfg(feature = "runner")]
1417impl TypeChecker for CachegrindMetric {
1418    fn is_int(&self) -> bool {
1419        match self {
1420            Self::Ir
1421            | Self::Dr
1422            | Self::Dw
1423            | Self::I1mr
1424            | Self::D1mr
1425            | Self::D1mw
1426            | Self::ILmr
1427            | Self::DLmr
1428            | Self::DLmw
1429            | Self::L1hits
1430            | Self::LLhits
1431            | Self::RamHits
1432            | Self::TotalRW
1433            | Self::EstimatedCycles
1434            | Self::Bc
1435            | Self::Bcm
1436            | Self::Bi
1437            | Self::Bim => true,
1438            Self::I1MissRate
1439            | Self::LLiMissRate
1440            | Self::D1MissRate
1441            | Self::LLdMissRate
1442            | Self::LLMissRate
1443            | Self::L1HitRate
1444            | Self::LLHitRate
1445            | Self::RamHitRate => false,
1446        }
1447    }
1448
1449    fn is_float(&self) -> bool {
1450        !self.is_int()
1451    }
1452}
1453
1454impl From<CachegrindMetric> for CachegrindMetrics {
1455    fn from(value: CachegrindMetric) -> Self {
1456        Self::SingleEvent(value)
1457    }
1458}
1459
1460#[cfg(feature = "runner")]
1461impl FromStr for CachegrindMetrics {
1462    type Err = anyhow::Error;
1463
1464    fn from_str(string: &str) -> Result<Self, Self::Err> {
1465        let lower = string.to_lowercase();
1466        match lower.as_str().strip_prefix('@') {
1467            Some(suffix) => match suffix {
1468                "default" | "def" => Ok(Self::Default),
1469                "all" => Ok(Self::All),
1470                "cachemisses" | "misses" | "ms" => Ok(Self::CacheMisses),
1471                "cachemissrates" | "missrates" | "mr" => Ok(Self::CacheMissRates),
1472                "cachehits" | "hits" | "hs" => Ok(Self::CacheHits),
1473                "cachehitrates" | "hitrates" | "hr" => Ok(Self::CacheHitRates),
1474                "cachesim" | "cs" => Ok(Self::CacheSim),
1475                "branchsim" | "bs" => Ok(Self::BranchSim),
1476                _ => Err(anyhow!("Invalid cachegrind metric group: '{string}")),
1477            },
1478            // Use `string` instead of `lower` for the correct error message
1479            None => CachegrindMetric::from_str(string).map(Self::SingleEvent),
1480        }
1481    }
1482}
1483
1484impl From<EventKind> for CallgrindMetrics {
1485    fn from(value: EventKind) -> Self {
1486        Self::SingleEvent(value)
1487    }
1488}
1489
1490#[cfg(feature = "runner")]
1491impl FromStr for CallgrindMetrics {
1492    type Err = anyhow::Error;
1493
1494    fn from_str(string: &str) -> Result<Self, Self::Err> {
1495        let lower = string.to_lowercase();
1496        match lower.as_str().strip_prefix('@') {
1497            Some(suffix) => match suffix {
1498                "default" | "def" => Ok(Self::Default),
1499                "all" => Ok(Self::All),
1500                "cachemisses" | "misses" | "ms" => Ok(Self::CacheMisses),
1501                "cachemissrates" | "missrates" | "mr" => Ok(Self::CacheMissRates),
1502                "cachehits" | "hits" | "hs" => Ok(Self::CacheHits),
1503                "cachehitrates" | "hitrates" | "hr" => Ok(Self::CacheHitRates),
1504                "cachesim" | "cs" => Ok(Self::CacheSim),
1505                "cacheuse" | "cu" => Ok(Self::CacheUse),
1506                "systemcalls" | "syscalls" | "sc" => Ok(Self::SystemCalls),
1507                "branchsim" | "bs" => Ok(Self::BranchSim),
1508                "writebackbehaviour" | "writeback" | "wb" => Ok(Self::WriteBackBehaviour),
1509                _ => Err(anyhow!("Invalid event group: '{string}")),
1510            },
1511            // Keep the `string` instead of the more efficient `lower` to produce the correct error
1512            // message in `EventKind::from_str`
1513            None => EventKind::from_str(string).map(Self::SingleEvent),
1514        }
1515    }
1516}
1517
1518impl Default for DelayKind {
1519    fn default() -> Self {
1520        Self::DurationElapse(Duration::from_secs(60))
1521    }
1522}
1523
1524impl Display for DhatMetric {
1525    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1526        match self {
1527            Self::TotalUnits => f.write_str("Total units"),
1528            Self::TotalEvents => f.write_str("Total events"),
1529            Self::TotalBytes => f.write_str("Total bytes"),
1530            Self::TotalBlocks => f.write_str("Total blocks"),
1531            Self::AtTGmaxBytes => f.write_str("At t-gmax bytes"),
1532            Self::AtTGmaxBlocks => f.write_str("At t-gmax blocks"),
1533            Self::AtTEndBytes => f.write_str("At t-end bytes"),
1534            Self::AtTEndBlocks => f.write_str("At t-end blocks"),
1535            Self::ReadsBytes => f.write_str("Reads bytes"),
1536            Self::WritesBytes => f.write_str("Writes bytes"),
1537            Self::TotalLifetimes => f.write_str("Total lifetimes"),
1538            Self::MaximumBytes => f.write_str("Maximum bytes"),
1539            Self::MaximumBlocks => f.write_str("Maximum blocks"),
1540        }
1541    }
1542}
1543
1544#[cfg(feature = "runner")]
1545impl FromStr for DhatMetric {
1546    type Err = anyhow::Error;
1547
1548    fn from_str(string: &str) -> Result<Self, Self::Err> {
1549        let lower = string.to_lowercase();
1550        let metric = match lower.as_str() {
1551            "totalunits" | "tun" => Self::TotalUnits,
1552            "totalevents" | "tev" => Self::TotalEvents,
1553            "totalbytes" | "tb" => Self::TotalBytes,
1554            "totalblocks" | "tbk" => Self::TotalBlocks,
1555            "attgmaxbytes" | "gb" => Self::AtTGmaxBytes,
1556            "attgmaxblocks" | "gbk" => Self::AtTGmaxBlocks,
1557            "attendbytes" | "eb" => Self::AtTEndBytes,
1558            "attendblocks" | "ebk" => Self::AtTEndBlocks,
1559            "readsbytes" | "rb" => Self::ReadsBytes,
1560            "writesbytes" | "wb" => Self::WritesBytes,
1561            "totallifetimes" | "tl" => Self::TotalLifetimes,
1562            "maximumbytes" | "mb" => Self::MaximumBytes,
1563            "maximumblocks" | "mbk" => Self::MaximumBlocks,
1564            _ => return Err(anyhow!("Unknown dhat metric: '{string}'")),
1565        };
1566
1567        Ok(metric)
1568    }
1569}
1570
1571#[cfg(feature = "runner")]
1572impl Summarize for DhatMetric {}
1573
1574#[cfg(feature = "runner")]
1575impl TypeChecker for DhatMetric {
1576    fn is_int(&self) -> bool {
1577        true
1578    }
1579
1580    fn is_float(&self) -> bool {
1581        false
1582    }
1583}
1584
1585impl From<DhatMetric> for DhatMetrics {
1586    fn from(value: DhatMetric) -> Self {
1587        Self::SingleMetric(value)
1588    }
1589}
1590
1591#[cfg(feature = "runner")]
1592impl FromStr for DhatMetrics {
1593    type Err = anyhow::Error;
1594
1595    fn from_str(string: &str) -> Result<Self, Self::Err> {
1596        let lower = string.to_lowercase();
1597        match lower.as_str().strip_prefix('@') {
1598            Some(suffix) => match suffix {
1599                "default" | "def" => Ok(Self::Default),
1600                "all" => Ok(Self::All),
1601                _ => Err(anyhow!("Invalid dhat metrics group: '{string}")),
1602            },
1603            // Use `string` instead of `lower` for the correct error message
1604            None => DhatMetric::from_str(string).map(Self::SingleMetric),
1605        }
1606    }
1607}
1608
1609impl Default for Direction {
1610    fn default() -> Self {
1611        Self::BottomToTop
1612    }
1613}
1614
1615impl<T> From<T> for EntryPoint
1616where
1617    T: Into<String>,
1618{
1619    fn from(value: T) -> Self {
1620        Self::Custom(value.into())
1621    }
1622}
1623
1624impl Display for ErrorMetric {
1625    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1626        match self {
1627            Self::Errors => f.write_str("Errors"),
1628            Self::Contexts => f.write_str("Contexts"),
1629            Self::SuppressedErrors => f.write_str("Suppressed Errors"),
1630            Self::SuppressedContexts => f.write_str("Suppressed Contexts"),
1631        }
1632    }
1633}
1634
1635#[cfg(feature = "runner")]
1636impl FromStr for ErrorMetric {
1637    type Err = anyhow::Error;
1638
1639    fn from_str(string: &str) -> Result<Self, Self::Err> {
1640        let lower = string.to_lowercase();
1641        let metric = match lower.as_str() {
1642            "errors" | "err" => Self::Errors,
1643            "contexts" | "ctx" => Self::Contexts,
1644            "suppressederrors" | "serr" => Self::SuppressedErrors,
1645            "suppressedcontexts" | "sctx" => Self::SuppressedContexts,
1646            _ => return Err(anyhow!("Unknown error metric: '{string}'")),
1647        };
1648
1649        Ok(metric)
1650    }
1651}
1652
1653#[cfg(feature = "runner")]
1654impl Summarize for ErrorMetric {}
1655
1656impl EventKind {
1657    /// Return true if this `EventKind` is a derived event
1658    ///
1659    /// Derived events are calculated from Callgrind's native event types. See also
1660    /// [`crate::runner::callgrind::model::Metrics::make_summary`]. Currently all derived events
1661    /// are:
1662    ///
1663    /// * [`EventKind::L1hits`]
1664    /// * [`EventKind::LLhits`]
1665    /// * [`EventKind::RamHits`]
1666    /// * [`EventKind::TotalRW`]
1667    /// * [`EventKind::EstimatedCycles`]
1668    /// * [`EventKind::I1MissRate`]
1669    /// * [`EventKind::D1MissRate`]
1670    /// * [`EventKind::LLiMissRate`]
1671    /// * [`EventKind::LLdMissRate`]
1672    /// * [`EventKind::LLMissRate`]
1673    /// * [`EventKind::L1HitRate`]
1674    /// * [`EventKind::LLHitRate`]
1675    /// * [`EventKind::RamHitRate`]
1676    pub fn is_derived(&self) -> bool {
1677        matches!(
1678            self,
1679            Self::L1hits
1680                | Self::LLhits
1681                | Self::RamHits
1682                | Self::TotalRW
1683                | Self::EstimatedCycles
1684                | Self::I1MissRate
1685                | Self::D1MissRate
1686                | Self::LLiMissRate
1687                | Self::LLdMissRate
1688                | Self::LLMissRate
1689                | Self::L1HitRate
1690                | Self::LLHitRate
1691                | Self::RamHitRate
1692        )
1693    }
1694
1695    /// Return the name of the metric which is the exact name of the enum variant
1696    pub fn to_name(&self) -> String {
1697        format!("{:?}", *self)
1698    }
1699}
1700
1701impl Display for EventKind {
1702    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1703        match self {
1704            Self::Ir => f.write_str("Instructions"),
1705            Self::L1hits => f.write_str("L1 Hits"),
1706            Self::LLhits => f.write_str("LL Hits"),
1707            Self::RamHits => f.write_str("RAM Hits"),
1708            Self::TotalRW => f.write_str("Total read+write"),
1709            Self::EstimatedCycles => f.write_str("Estimated Cycles"),
1710            Self::I1MissRate => f.write_str("I1 Miss Rate"),
1711            Self::D1MissRate => f.write_str("D1 Miss Rate"),
1712            Self::LLiMissRate => f.write_str("LLi Miss Rate"),
1713            Self::LLdMissRate => f.write_str("LLd Miss Rate"),
1714            Self::LLMissRate => f.write_str("LL Miss Rate"),
1715            Self::L1HitRate => f.write_str("L1 Hit Rate"),
1716            Self::LLHitRate => f.write_str("LL Hit Rate"),
1717            Self::RamHitRate => f.write_str("RAM Hit Rate"),
1718            _ => write!(f, "{self:?}"),
1719        }
1720    }
1721}
1722
1723impl From<CachegrindMetric> for EventKind {
1724    fn from(value: CachegrindMetric) -> Self {
1725        match value {
1726            CachegrindMetric::Ir => Self::Ir,
1727            CachegrindMetric::Dr => Self::Dr,
1728            CachegrindMetric::Dw => Self::Dw,
1729            CachegrindMetric::I1mr => Self::I1mr,
1730            CachegrindMetric::D1mr => Self::D1mr,
1731            CachegrindMetric::D1mw => Self::D1mw,
1732            CachegrindMetric::ILmr => Self::ILmr,
1733            CachegrindMetric::DLmr => Self::DLmr,
1734            CachegrindMetric::DLmw => Self::DLmw,
1735            CachegrindMetric::L1hits => Self::L1hits,
1736            CachegrindMetric::LLhits => Self::LLhits,
1737            CachegrindMetric::RamHits => Self::RamHits,
1738            CachegrindMetric::TotalRW => Self::TotalRW,
1739            CachegrindMetric::EstimatedCycles => Self::EstimatedCycles,
1740            CachegrindMetric::Bc => Self::Bc,
1741            CachegrindMetric::Bcm => Self::Bcm,
1742            CachegrindMetric::Bi => Self::Bi,
1743            CachegrindMetric::Bim => Self::Bim,
1744            CachegrindMetric::I1MissRate => Self::I1MissRate,
1745            CachegrindMetric::D1MissRate => Self::D1MissRate,
1746            CachegrindMetric::LLiMissRate => Self::LLiMissRate,
1747            CachegrindMetric::LLdMissRate => Self::LLdMissRate,
1748            CachegrindMetric::LLMissRate => Self::LLMissRate,
1749            CachegrindMetric::L1HitRate => Self::L1HitRate,
1750            CachegrindMetric::LLHitRate => Self::LLHitRate,
1751            CachegrindMetric::RamHitRate => Self::RamHitRate,
1752        }
1753    }
1754}
1755
1756#[cfg(feature = "runner")]
1757impl FromStr for EventKind {
1758    type Err = anyhow::Error;
1759
1760    fn from_str(string: &str) -> Result<Self, Self::Err> {
1761        let lower = string.to_lowercase();
1762        let event_kind = match lower.as_str() {
1763            "instructions" | "ir" => Self::Ir,
1764            "dr" => Self::Dr,
1765            "dw" => Self::Dw,
1766            "i1mr" => Self::I1mr,
1767            "d1mr" => Self::D1mr,
1768            "d1mw" => Self::D1mw,
1769            "ilmr" => Self::ILmr,
1770            "dlmr" => Self::DLmr,
1771            "dlmw" => Self::DLmw,
1772            "syscount" => Self::SysCount,
1773            "systime" => Self::SysTime,
1774            "syscputime" => Self::SysCpuTime,
1775            "ge" => Self::Ge,
1776            "bc" => Self::Bc,
1777            "bcm" => Self::Bcm,
1778            "bi" => Self::Bi,
1779            "bim" => Self::Bim,
1780            "ildmr" => Self::ILdmr,
1781            "dldmr" => Self::DLdmr,
1782            "dldmw" => Self::DLdmw,
1783            "accost1" => Self::AcCost1,
1784            "accost2" => Self::AcCost2,
1785            "sploss1" => Self::SpLoss1,
1786            "sploss2" => Self::SpLoss2,
1787            "l1hits" => Self::L1hits,
1788            "llhits" => Self::LLhits,
1789            "ramhits" => Self::RamHits,
1790            "totalrw" => Self::TotalRW,
1791            "estimatedcycles" => Self::EstimatedCycles,
1792            "i1missrate" => Self::I1MissRate,
1793            "d1missrate" => Self::D1MissRate,
1794            "llimissrate" => Self::LLiMissRate,
1795            "lldmissrate" => Self::LLdMissRate,
1796            "llmissrate" => Self::LLMissRate,
1797            "l1hitrate" => Self::L1HitRate,
1798            "llhitrate" => Self::LLHitRate,
1799            "ramhitrate" => Self::RamHitRate,
1800            _ => return Err(anyhow!("Unknown event kind: '{string}'")),
1801        };
1802
1803        Ok(event_kind)
1804    }
1805}
1806
1807#[cfg(feature = "runner")]
1808impl TypeChecker for EventKind {
1809    fn is_int(&self) -> bool {
1810        match self {
1811            Self::Ir
1812            | Self::Dr
1813            | Self::Dw
1814            | Self::I1mr
1815            | Self::D1mr
1816            | Self::D1mw
1817            | Self::ILmr
1818            | Self::DLmr
1819            | Self::DLmw
1820            | Self::L1hits
1821            | Self::LLhits
1822            | Self::RamHits
1823            | Self::TotalRW
1824            | Self::EstimatedCycles
1825            | Self::SysCount
1826            | Self::SysTime
1827            | Self::SysCpuTime
1828            | Self::Ge
1829            | Self::Bc
1830            | Self::Bcm
1831            | Self::Bi
1832            | Self::Bim
1833            | Self::ILdmr
1834            | Self::DLdmr
1835            | Self::DLdmw
1836            | Self::AcCost1
1837            | Self::AcCost2
1838            | Self::SpLoss1
1839            | Self::SpLoss2 => true,
1840            Self::I1MissRate
1841            | Self::LLiMissRate
1842            | Self::D1MissRate
1843            | Self::LLdMissRate
1844            | Self::LLMissRate
1845            | Self::L1HitRate
1846            | Self::LLHitRate
1847            | Self::RamHitRate => false,
1848        }
1849    }
1850
1851    fn is_float(&self) -> bool {
1852        !self.is_int()
1853    }
1854}
1855
1856#[cfg(feature = "runner")]
1857impl From<CachegrindMetrics> for IndexSet<CachegrindMetric> {
1858    fn from(value: CachegrindMetrics) -> Self {
1859        let mut metrics = Self::new();
1860        match value {
1861            CachegrindMetrics::None => {}
1862            CachegrindMetrics::All => metrics.extend(CachegrindMetric::iter()),
1863            CachegrindMetrics::Default => {
1864                metrics.insert(CachegrindMetric::Ir);
1865                metrics.extend(Self::from(CachegrindMetrics::CacheHits));
1866                metrics.extend([CachegrindMetric::TotalRW, CachegrindMetric::EstimatedCycles]);
1867                metrics.extend(Self::from(CachegrindMetrics::BranchSim));
1868            }
1869            CachegrindMetrics::CacheMisses => metrics.extend([
1870                CachegrindMetric::I1mr,
1871                CachegrindMetric::D1mr,
1872                CachegrindMetric::D1mw,
1873                CachegrindMetric::ILmr,
1874                CachegrindMetric::DLmr,
1875                CachegrindMetric::DLmw,
1876            ]),
1877            CachegrindMetrics::CacheMissRates => metrics.extend([
1878                CachegrindMetric::I1MissRate,
1879                CachegrindMetric::LLiMissRate,
1880                CachegrindMetric::D1MissRate,
1881                CachegrindMetric::LLdMissRate,
1882                CachegrindMetric::LLMissRate,
1883            ]),
1884            CachegrindMetrics::CacheHits => {
1885                metrics.extend([
1886                    CachegrindMetric::L1hits,
1887                    CachegrindMetric::LLhits,
1888                    CachegrindMetric::RamHits,
1889                ]);
1890            }
1891            CachegrindMetrics::CacheHitRates => {
1892                metrics.extend([
1893                    CachegrindMetric::L1HitRate,
1894                    CachegrindMetric::LLHitRate,
1895                    CachegrindMetric::RamHitRate,
1896                ]);
1897            }
1898            CachegrindMetrics::CacheSim => {
1899                metrics.extend([CachegrindMetric::Dr, CachegrindMetric::Dw]);
1900                metrics.extend(Self::from(CachegrindMetrics::CacheMisses));
1901                metrics.extend(Self::from(CachegrindMetrics::CacheMissRates));
1902                metrics.extend(Self::from(CachegrindMetrics::CacheHits));
1903                metrics.extend(Self::from(CachegrindMetrics::CacheHitRates));
1904                metrics.insert(CachegrindMetric::TotalRW);
1905                metrics.insert(CachegrindMetric::EstimatedCycles);
1906            }
1907            CachegrindMetrics::BranchSim => {
1908                metrics.extend([
1909                    CachegrindMetric::Bc,
1910                    CachegrindMetric::Bcm,
1911                    CachegrindMetric::Bi,
1912                    CachegrindMetric::Bim,
1913                ]);
1914            }
1915            CachegrindMetrics::SingleEvent(metric) => {
1916                metrics.insert(metric);
1917            }
1918        }
1919
1920        metrics
1921    }
1922}
1923
1924#[cfg(feature = "runner")]
1925impl From<DhatMetrics> for IndexSet<DhatMetric> {
1926    fn from(value: DhatMetrics) -> Self {
1927        use DhatMetric::*;
1928        match value {
1929            DhatMetrics::All => DhatMetric::iter().collect(),
1930            DhatMetrics::Default => indexset! {
1931            TotalUnits,
1932            TotalEvents,
1933            TotalBytes,
1934            TotalBlocks,
1935            AtTGmaxBytes,
1936            AtTGmaxBlocks,
1937            AtTEndBytes,
1938            AtTEndBlocks,
1939            ReadsBytes,
1940            WritesBytes },
1941            DhatMetrics::SingleMetric(dhat_metric) => indexset! { dhat_metric },
1942        }
1943    }
1944}
1945
1946#[cfg(feature = "runner")]
1947impl From<CallgrindMetrics> for IndexSet<EventKind> {
1948    fn from(value: CallgrindMetrics) -> Self {
1949        let mut event_kinds = Self::new();
1950        match value {
1951            CallgrindMetrics::None => {}
1952            CallgrindMetrics::All => event_kinds.extend(EventKind::iter()),
1953            CallgrindMetrics::Default => {
1954                event_kinds.insert(EventKind::Ir);
1955                event_kinds.extend(Self::from(CallgrindMetrics::CacheHits));
1956                event_kinds.extend([EventKind::TotalRW, EventKind::EstimatedCycles]);
1957                event_kinds.extend(Self::from(CallgrindMetrics::SystemCalls));
1958                event_kinds.insert(EventKind::Ge);
1959                event_kinds.extend(Self::from(CallgrindMetrics::BranchSim));
1960                event_kinds.extend(Self::from(CallgrindMetrics::WriteBackBehaviour));
1961                event_kinds.extend(Self::from(CallgrindMetrics::CacheUse));
1962            }
1963            CallgrindMetrics::CacheMisses => event_kinds.extend([
1964                EventKind::I1mr,
1965                EventKind::D1mr,
1966                EventKind::D1mw,
1967                EventKind::ILmr,
1968                EventKind::DLmr,
1969                EventKind::DLmw,
1970            ]),
1971            CallgrindMetrics::CacheMissRates => event_kinds.extend([
1972                EventKind::I1MissRate,
1973                EventKind::LLiMissRate,
1974                EventKind::D1MissRate,
1975                EventKind::LLdMissRate,
1976                EventKind::LLMissRate,
1977            ]),
1978            CallgrindMetrics::CacheHits => {
1979                event_kinds.extend([EventKind::L1hits, EventKind::LLhits, EventKind::RamHits]);
1980            }
1981            CallgrindMetrics::CacheHitRates => {
1982                event_kinds.extend([
1983                    EventKind::L1HitRate,
1984                    EventKind::LLHitRate,
1985                    EventKind::RamHitRate,
1986                ]);
1987            }
1988            CallgrindMetrics::CacheSim => {
1989                event_kinds.extend([EventKind::Dr, EventKind::Dw]);
1990                event_kinds.extend(Self::from(CallgrindMetrics::CacheMisses));
1991                event_kinds.extend(Self::from(CallgrindMetrics::CacheMissRates));
1992                event_kinds.extend(Self::from(CallgrindMetrics::CacheHits));
1993                event_kinds.extend(Self::from(CallgrindMetrics::CacheHitRates));
1994                event_kinds.insert(EventKind::TotalRW);
1995                event_kinds.insert(EventKind::EstimatedCycles);
1996            }
1997            CallgrindMetrics::CacheUse => event_kinds.extend([
1998                EventKind::AcCost1,
1999                EventKind::AcCost2,
2000                EventKind::SpLoss1,
2001                EventKind::SpLoss2,
2002            ]),
2003            CallgrindMetrics::SystemCalls => {
2004                event_kinds.extend([
2005                    EventKind::SysCount,
2006                    EventKind::SysTime,
2007                    EventKind::SysCpuTime,
2008                ]);
2009            }
2010            CallgrindMetrics::BranchSim => {
2011                event_kinds.extend([EventKind::Bc, EventKind::Bcm, EventKind::Bi, EventKind::Bim]);
2012            }
2013            CallgrindMetrics::WriteBackBehaviour => {
2014                event_kinds.extend([EventKind::ILdmr, EventKind::DLdmr, EventKind::DLdmw]);
2015            }
2016            CallgrindMetrics::SingleEvent(event_kind) => {
2017                event_kinds.insert(event_kind);
2018            }
2019        }
2020
2021        event_kinds
2022    }
2023}
2024
2025impl LibraryBenchmarkConfig {
2026    /// Update this configuration with all other configurations in the given order
2027    #[must_use]
2028    pub fn update_from_all<'a, T>(mut self, others: T) -> Self
2029    where
2030        T: IntoIterator<Item = Option<&'a Self>>,
2031    {
2032        for other in others.into_iter().flatten() {
2033            self.default_tool = update_option(&self.default_tool, &other.default_tool);
2034            self.env_clear = update_option(&self.env_clear, &other.env_clear);
2035
2036            self.valgrind_args
2037                .extend_ignore_flag(other.valgrind_args.0.iter());
2038
2039            self.envs.extend_from_slice(&other.envs);
2040            if let Some(other_tools) = &other.tools_override {
2041                self.tools = other_tools.clone();
2042            } else if !other.tools.is_empty() {
2043                self.tools.update_from_other(&other.tools);
2044            } else {
2045                // do nothing
2046            }
2047
2048            self.output_format = update_option(&self.output_format, &other.output_format);
2049        }
2050        self
2051    }
2052
2053    /// Resolve the environment variables and create key, value pairs out of them
2054    ///
2055    /// Same as [`BinaryBenchmarkConfig::resolve_envs`]
2056    pub fn resolve_envs(&self) -> Vec<(OsString, OsString)> {
2057        self.envs
2058            .iter()
2059            .filter_map(|(key, value)| match value {
2060                Some(value) => Some((key.clone(), value.clone())),
2061                None => std::env::var_os(key).map(|value| (key.clone(), value)),
2062            })
2063            .collect()
2064    }
2065
2066    /// Collect all environment variables which don't have a `None` value
2067    ///
2068    /// Same as [`BinaryBenchmarkConfig::collect_envs`]
2069    pub fn collect_envs(&self) -> Vec<(OsString, OsString)> {
2070        self.envs
2071            .iter()
2072            .filter_map(|(key, option)| option.as_ref().map(|value| (key.clone(), value.clone())))
2073            .collect()
2074    }
2075}
2076
2077#[cfg(feature = "runner")]
2078impl From<runner::metrics::Metric> for Limit {
2079    fn from(value: runner::metrics::Metric) -> Self {
2080        match value {
2081            runner::metrics::Metric::Int(a) => Self::Int(a),
2082            runner::metrics::Metric::Float(b) => Self::Float(b),
2083        }
2084    }
2085}
2086
2087impl From<f64> for Limit {
2088    fn from(value: f64) -> Self {
2089        Self::Float(value)
2090    }
2091}
2092
2093impl From<u64> for Limit {
2094    fn from(value: u64) -> Self {
2095        Self::Int(value)
2096    }
2097}
2098
2099impl RawArgs {
2100    /// Create new arguments for a valgrind tool
2101    pub fn new<I, T>(args: T) -> Self
2102    where
2103        I: Into<String>,
2104        T: IntoIterator<Item = I>,
2105    {
2106        Self(args.into_iter().map(Into::into).collect())
2107    }
2108
2109    /// Extend the arguments with the contents of an iterator
2110    pub fn extend_ignore_flag<I, T>(&mut self, args: T)
2111    where
2112        I: AsRef<str>,
2113        T: IntoIterator<Item = I>,
2114    {
2115        self.0.extend(
2116            args.into_iter()
2117                .filter(|s| !s.as_ref().is_empty())
2118                .map(|s| {
2119                    let string = s.as_ref();
2120                    if string.starts_with('-') {
2121                        string.to_owned()
2122                    } else {
2123                        format!("--{string}")
2124                    }
2125                }),
2126        );
2127    }
2128
2129    /// Return true if there are no tool arguments
2130    pub fn is_empty(&self) -> bool {
2131        self.0.is_empty()
2132    }
2133
2134    /// Append the arguments of another `RawArgs`
2135    pub fn update(&mut self, other: &Self) {
2136        self.extend_ignore_flag(other.0.iter());
2137    }
2138
2139    /// Prepend the arguments of another `RawArgs`
2140    pub fn prepend(&mut self, other: &Self) {
2141        if !other.is_empty() {
2142            let mut other = other.clone();
2143            other.update(self);
2144            *self = other;
2145        }
2146    }
2147}
2148
2149impl<I> FromIterator<I> for RawArgs
2150where
2151    I: AsRef<str>,
2152{
2153    fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
2154        let mut this = Self::default();
2155        this.extend_ignore_flag(iter);
2156        this
2157    }
2158}
2159
2160impl Stdin {
2161    #[cfg(feature = "runner")]
2162    pub(crate) fn apply(
2163        &self,
2164        command: &mut StdCommand,
2165        stream: Stream,
2166        child: Option<&mut Child>,
2167    ) -> Result<(), String> {
2168        match (self, child) {
2169            (Self::Setup(Pipe::Stdout), Some(child)) => {
2170                command.stdin(
2171                    child
2172                        .stdout
2173                        .take()
2174                        .ok_or_else(|| "Error piping setup stdout".to_owned())?,
2175                );
2176                Ok(())
2177            }
2178            (Self::Setup(Pipe::Stderr), Some(child)) => {
2179                command.stdin(
2180                    child
2181                        .stderr
2182                        .take()
2183                        .ok_or_else(|| "Error piping setup stderr".to_owned())?,
2184                );
2185                Ok(())
2186            }
2187            (Self::Setup(_) | Self::Pipe, _) => Stdio::Pipe.apply(command, stream),
2188            (Self::Inherit, _) => Stdio::Inherit.apply(command, stream),
2189            (Self::Null, _) => Stdio::Null.apply(command, stream),
2190            (Self::File(path), _) => Stdio::File(path.clone()).apply(command, stream),
2191        }
2192    }
2193}
2194
2195impl From<Stdio> for Stdin {
2196    fn from(value: Stdio) -> Self {
2197        match value {
2198            Stdio::Inherit => Self::Inherit,
2199            Stdio::Null => Self::Null,
2200            Stdio::File(file) => Self::File(file),
2201            Stdio::Pipe => Self::Pipe,
2202        }
2203    }
2204}
2205
2206impl From<PathBuf> for Stdin {
2207    fn from(value: PathBuf) -> Self {
2208        Self::File(value)
2209    }
2210}
2211
2212impl From<&PathBuf> for Stdin {
2213    fn from(value: &PathBuf) -> Self {
2214        Self::File(value.to_owned())
2215    }
2216}
2217
2218impl From<&Path> for Stdin {
2219    fn from(value: &Path) -> Self {
2220        Self::File(value.to_path_buf())
2221    }
2222}
2223
2224impl Stdio {
2225    #[cfg(feature = "runner")]
2226    pub(crate) fn apply(&self, command: &mut StdCommand, stream: Stream) -> Result<(), String> {
2227        let stdio = match self {
2228            Self::Pipe => StdStdio::piped(),
2229            Self::Inherit => StdStdio::inherit(),
2230            Self::Null => StdStdio::null(),
2231            Self::File(path) => match stream {
2232                Stream::Stdin => StdStdio::from(File::open(path).map_err(|error| {
2233                    format!(
2234                        "Failed to open file '{}' in read mode for {stream}: {error}",
2235                        path.display()
2236                    )
2237                })?),
2238                Stream::Stdout | Stream::Stderr => {
2239                    StdStdio::from(File::create(path).map_err(|error| {
2240                        format!(
2241                            "Failed to create file '{}' for {stream}: {error}",
2242                            path.display()
2243                        )
2244                    })?)
2245                }
2246            },
2247        };
2248
2249        match stream {
2250            Stream::Stdin => command.stdin(stdio),
2251            Stream::Stdout => command.stdout(stdio),
2252            Stream::Stderr => command.stderr(stdio),
2253        };
2254
2255        Ok(())
2256    }
2257
2258    #[cfg(feature = "runner")]
2259    pub(crate) fn is_pipe(&self) -> bool {
2260        match self {
2261            Self::Inherit => false,
2262            Self::Null | Self::File(_) | Self::Pipe => true,
2263        }
2264    }
2265}
2266
2267impl From<PathBuf> for Stdio {
2268    fn from(value: PathBuf) -> Self {
2269        Self::File(value)
2270    }
2271}
2272
2273impl From<&PathBuf> for Stdio {
2274    fn from(value: &PathBuf) -> Self {
2275        Self::File(value.to_owned())
2276    }
2277}
2278
2279impl From<&Path> for Stdio {
2280    fn from(value: &Path) -> Self {
2281        Self::File(value.to_path_buf())
2282    }
2283}
2284
2285#[cfg(feature = "runner")]
2286impl Display for Stream {
2287    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2288        f.write_str(&format!("{self:?}").to_lowercase())
2289    }
2290}
2291
2292impl Tool {
2293    /// Create a new `Tool` configuration
2294    pub fn new(kind: ValgrindTool) -> Self {
2295        Self {
2296            kind,
2297            enable: None,
2298            raw_args: RawArgs::default(),
2299            show_log: None,
2300            regression_config: None,
2301            flamegraph_config: None,
2302            output_format: None,
2303            entry_point: None,
2304            frames: None,
2305        }
2306    }
2307
2308    /// Create a new `Tool` configuration with the given command-line `args`
2309    pub fn with_args<I, T>(kind: ValgrindTool, args: T) -> Self
2310    where
2311        I: AsRef<str>,
2312        T: IntoIterator<Item = I>,
2313    {
2314        let mut this = Self::new(kind);
2315        this.raw_args = RawArgs::from_iter(args);
2316        this
2317    }
2318
2319    /// Update this tool configuration with another configuration
2320    pub fn update(&mut self, other: &Self) {
2321        if self.kind == other.kind {
2322            self.enable = update_option(&self.enable, &other.enable);
2323            self.show_log = update_option(&self.show_log, &other.show_log);
2324            self.regression_config =
2325                update_option(&self.regression_config, &other.regression_config);
2326            self.flamegraph_config =
2327                update_option(&self.flamegraph_config, &other.flamegraph_config);
2328            self.output_format = update_option(&self.output_format, &other.output_format);
2329            self.entry_point = update_option(&self.entry_point, &other.entry_point);
2330            self.frames = update_option(&self.frames, &other.frames);
2331
2332            self.raw_args.extend_ignore_flag(other.raw_args.0.iter());
2333        }
2334    }
2335}
2336
2337impl Tools {
2338    /// Return true if `Tools` is empty
2339    pub fn is_empty(&self) -> bool {
2340        self.0.is_empty()
2341    }
2342
2343    /// Update `Tools`
2344    pub fn update(&mut self, other: Tool) {
2345        if let Some(tool) = self.0.iter_mut().find(|t| t.kind == other.kind) {
2346            tool.update(&other);
2347        } else {
2348            self.0.push(other);
2349        }
2350    }
2351
2352    /// Update `Tools` with all [`Tool`]s from an iterator
2353    pub fn update_all<T>(&mut self, tools: T)
2354    where
2355        T: IntoIterator<Item = Tool>,
2356    {
2357        for tool in tools {
2358            self.update(tool);
2359        }
2360    }
2361
2362    /// Update `Tools` with another `Tools`
2363    pub fn update_from_other(&mut self, tools: &Self) {
2364        self.update_all(tools.0.iter().cloned());
2365    }
2366
2367    /// Search for the [`Tool`] with `kind` and if present remove it from this `Tools` and return it
2368    pub fn consume(&mut self, kind: ValgrindTool) -> Option<Tool> {
2369        self.0
2370            .iter()
2371            .position(|p| p.kind == kind)
2372            .map(|position| self.0.remove(position))
2373    }
2374}
2375
2376impl ValgrindTool {
2377    /// Return the id used by the `valgrind --tool` option
2378    pub fn id(&self) -> String {
2379        match self {
2380            Self::DHAT => "dhat".to_owned(),
2381            Self::Callgrind => "callgrind".to_owned(),
2382            Self::Memcheck => "memcheck".to_owned(),
2383            Self::Helgrind => "helgrind".to_owned(),
2384            Self::DRD => "drd".to_owned(),
2385            Self::Massif => "massif".to_owned(),
2386            Self::BBV => "exp-bbv".to_owned(),
2387            Self::Cachegrind => "cachegrind".to_owned(),
2388        }
2389    }
2390
2391    /// Return true if this tool has output files in addition to log files
2392    pub fn has_output_file(&self) -> bool {
2393        matches!(
2394            self,
2395            Self::Callgrind | Self::DHAT | Self::BBV | Self::Massif | Self::Cachegrind
2396        )
2397    }
2398
2399    /// Return true if this tool supports xtree memory files
2400    pub fn has_xtree_file(&self) -> bool {
2401        matches!(self, Self::Memcheck | Self::Massif | Self::Helgrind)
2402    }
2403
2404    /// Return true if this tool supports xleak files
2405    pub fn has_xleak_file(&self) -> bool {
2406        *self == Self::Memcheck
2407    }
2408}
2409
2410impl Display for ValgrindTool {
2411    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2412        f.write_str(&self.id())
2413    }
2414}
2415
2416#[cfg(feature = "runner")]
2417impl FromStr for ValgrindTool {
2418    type Err = anyhow::Error;
2419
2420    fn from_str(s: &str) -> Result<Self, Self::Err> {
2421        Self::try_from(s.to_lowercase().as_str())
2422    }
2423}
2424
2425#[cfg(feature = "runner")]
2426impl TryFrom<&str> for ValgrindTool {
2427    type Error = anyhow::Error;
2428
2429    fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
2430        match value {
2431            "callgrind" => Ok(Self::Callgrind),
2432            "cachegrind" => Ok(Self::Cachegrind),
2433            "dhat" => Ok(Self::DHAT),
2434            "memcheck" => Ok(Self::Memcheck),
2435            "helgrind" => Ok(Self::Helgrind),
2436            "drd" => Ok(Self::DRD),
2437            "massif" => Ok(Self::Massif),
2438            "exp-bbv" => Ok(Self::BBV),
2439            v => Err(anyhow!("Unknown tool '{}'", v)),
2440        }
2441    }
2442}
2443
2444/// Update the value of an [`Option`]
2445pub fn update_option<T: Clone>(first: &Option<T>, other: &Option<T>) -> Option<T> {
2446    other.clone().or_else(|| first.clone())
2447}
2448
2449#[cfg(test)]
2450mod tests {
2451    use indexmap::indexset;
2452    use pretty_assertions::assert_eq;
2453    use rstest::rstest;
2454
2455    use super::EventKind::*;
2456    use super::{CachegrindMetric as Cm, *};
2457
2458    #[test]
2459    fn test_cachegrind_metric_from_str_ignore_case() {
2460        for metric in CachegrindMetric::iter() {
2461            let string = format!("{metric:?}");
2462            let actual = CachegrindMetric::from_str(&string);
2463            assert_eq!(actual.unwrap(), metric);
2464        }
2465    }
2466
2467    #[test]
2468    fn test_event_kind_from_str_ignore_case() {
2469        for event_kind in EventKind::iter() {
2470            let string = format!("{event_kind:?}");
2471            let actual = EventKind::from_str(&string);
2472            assert_eq!(actual.unwrap(), event_kind);
2473        }
2474    }
2475
2476    #[test]
2477    fn test_library_benchmark_config_update_from_all_when_default() {
2478        assert_eq!(
2479            LibraryBenchmarkConfig::default()
2480                .update_from_all([Some(&LibraryBenchmarkConfig::default())]),
2481            LibraryBenchmarkConfig::default()
2482        );
2483    }
2484
2485    #[test]
2486    fn test_library_benchmark_config_update_from_all_when_no_tools_override() {
2487        let base = LibraryBenchmarkConfig::default();
2488        let other = LibraryBenchmarkConfig {
2489            env_clear: Some(true),
2490            valgrind_args: RawArgs(vec!["--valgrind-arg=yes".to_owned()]),
2491            envs: vec![(OsString::from("MY_ENV"), Some(OsString::from("value")))],
2492            tools: Tools(vec![Tool {
2493                kind: ValgrindTool::DHAT,
2494                enable: None,
2495                raw_args: RawArgs(vec![]),
2496                show_log: None,
2497                regression_config: Some(ToolRegressionConfig::Callgrind(
2498                    CallgrindRegressionConfig::default(),
2499                )),
2500                flamegraph_config: Some(ToolFlamegraphConfig::Callgrind(
2501                    FlamegraphConfig::default(),
2502                )),
2503                entry_point: Some(EntryPoint::default()),
2504                output_format: Some(ToolOutputFormat::None),
2505                frames: Some(vec!["some::frame".to_owned()]),
2506            }]),
2507            tools_override: None,
2508            output_format: None,
2509            default_tool: Some(ValgrindTool::BBV),
2510        };
2511
2512        assert_eq!(base.update_from_all([Some(&other.clone())]), other);
2513    }
2514
2515    #[test]
2516    fn test_library_benchmark_config_update_from_all_when_tools_override() {
2517        let base = LibraryBenchmarkConfig::default();
2518        let other = LibraryBenchmarkConfig {
2519            env_clear: Some(true),
2520            valgrind_args: RawArgs(vec!["--valgrind-arg=yes".to_owned()]),
2521            envs: vec![(OsString::from("MY_ENV"), Some(OsString::from("value")))],
2522            tools: Tools(vec![Tool {
2523                kind: ValgrindTool::DHAT,
2524                enable: None,
2525                raw_args: RawArgs(vec![]),
2526                show_log: None,
2527                regression_config: Some(ToolRegressionConfig::Callgrind(
2528                    CallgrindRegressionConfig::default(),
2529                )),
2530                flamegraph_config: Some(ToolFlamegraphConfig::Callgrind(
2531                    FlamegraphConfig::default(),
2532                )),
2533                entry_point: Some(EntryPoint::default()),
2534                output_format: Some(ToolOutputFormat::None),
2535                frames: Some(vec!["some::frame".to_owned()]),
2536            }]),
2537            tools_override: Some(Tools(vec![])),
2538            output_format: Some(OutputFormat::default()),
2539            default_tool: Some(ValgrindTool::BBV),
2540        };
2541        let expected = LibraryBenchmarkConfig {
2542            tools: other.tools_override.as_ref().unwrap().clone(),
2543            tools_override: None,
2544            ..other.clone()
2545        };
2546
2547        assert_eq!(base.update_from_all([Some(&other)]), expected);
2548    }
2549
2550    #[rstest]
2551    #[case::env_clear(
2552        LibraryBenchmarkConfig {
2553            env_clear: Some(true),
2554            ..Default::default()
2555        }
2556    )]
2557    fn test_library_benchmark_config_update_from_all_truncate_description(
2558        #[case] config: LibraryBenchmarkConfig,
2559    ) {
2560        let actual = LibraryBenchmarkConfig::default().update_from_all([Some(&config)]);
2561        assert_eq!(actual, config);
2562    }
2563
2564    #[rstest]
2565    #[case::all_none(None, None, None)]
2566    #[case::some_and_none(Some(true), None, Some(true))]
2567    #[case::none_and_some(None, Some(true), Some(true))]
2568    #[case::some_and_some(Some(false), Some(true), Some(true))]
2569    #[case::some_and_some_value_does_not_matter(Some(true), Some(false), Some(false))]
2570    fn test_update_option(
2571        #[case] first: Option<bool>,
2572        #[case] other: Option<bool>,
2573        #[case] expected: Option<bool>,
2574    ) {
2575        assert_eq!(update_option(&first, &other), expected);
2576    }
2577
2578    #[rstest]
2579    #[case::empty(vec![], &[], vec![])]
2580    #[case::empty_base(vec![], &["--a=yes"], vec!["--a=yes"])]
2581    #[case::no_flags(vec![], &["a=yes"], vec!["--a=yes"])]
2582    #[case::already_exists_single(vec!["--a=yes"], &["--a=yes"], vec!["--a=yes","--a=yes"])]
2583    #[case::already_exists_when_multiple(vec!["--a=yes", "--b=yes"], &["--a=yes"], vec!["--a=yes", "--b=yes", "--a=yes"])]
2584    fn test_raw_args_extend_ignore_flags(
2585        #[case] base: Vec<&str>,
2586        #[case] data: &[&str],
2587        #[case] expected: Vec<&str>,
2588    ) {
2589        let mut base = RawArgs(base.iter().map(std::string::ToString::to_string).collect());
2590        base.extend_ignore_flag(data.iter().map(std::string::ToString::to_string));
2591
2592        assert_eq!(base.0.into_iter().collect::<Vec<String>>(), expected);
2593    }
2594
2595    #[rstest]
2596    #[case::none(CallgrindMetrics::None, indexset![])]
2597    #[case::all(CallgrindMetrics::All, indexset![Ir, Dr, Dw, I1mr, D1mr, D1mw, ILmr, DLmr,
2598        DLmw, I1MissRate, LLiMissRate, D1MissRate, LLdMissRate, LLMissRate, L1hits, LLhits, RamHits,
2599        TotalRW, L1HitRate, LLHitRate, RamHitRate, EstimatedCycles, SysCount, SysTime, SysCpuTime,
2600        Ge, Bc, Bcm, Bi, Bim, ILdmr, DLdmr, DLdmw, AcCost1, AcCost2, SpLoss1, SpLoss2]
2601    )]
2602    #[case::default(CallgrindMetrics::Default, indexset![Ir, L1hits, LLhits, RamHits, TotalRW,
2603        EstimatedCycles, SysCount, SysTime, SysCpuTime, Ge, Bc,
2604        Bcm, Bi, Bim, ILdmr, DLdmr, DLdmw, AcCost1, AcCost2, SpLoss1, SpLoss2]
2605    )]
2606    #[case::cache_misses(CallgrindMetrics::CacheMisses, indexset![I1mr, D1mr, D1mw, ILmr,
2607        DLmr, DLmw]
2608    )]
2609    #[case::cache_miss_rates(CallgrindMetrics::CacheMissRates, indexset![I1MissRate,
2610        D1MissRate, LLMissRate, LLiMissRate, LLdMissRate]
2611    )]
2612    #[case::cache_hits(CallgrindMetrics::CacheHits, indexset![L1hits, LLhits, RamHits])]
2613    #[case::cache_hit_rates(CallgrindMetrics::CacheHitRates, indexset![
2614        L1HitRate, LLHitRate, RamHitRate
2615    ])]
2616    #[case::cache_sim(CallgrindMetrics::CacheSim, indexset![Dr, Dw, I1mr, D1mr, D1mw, ILmr, DLmr,
2617        DLmw, I1MissRate, LLiMissRate, D1MissRate, LLdMissRate, LLMissRate, L1hits, LLhits, RamHits,
2618        TotalRW, L1HitRate, LLHitRate, RamHitRate, EstimatedCycles]
2619    )]
2620    #[case::cache_use(CallgrindMetrics::CacheUse, indexset![AcCost1, AcCost2, SpLoss1, SpLoss2])]
2621    #[case::system_calls(CallgrindMetrics::SystemCalls, indexset![SysCount, SysTime, SysCpuTime])]
2622    #[case::branch_sim(CallgrindMetrics::BranchSim, indexset![Bc, Bcm, Bi, Bim])]
2623    #[case::write_back(CallgrindMetrics::WriteBackBehaviour, indexset![ILdmr, DLdmr, DLdmw])]
2624    #[case::single_event(CallgrindMetrics::SingleEvent(Ir), indexset![Ir])]
2625    fn test_callgrind_metrics_into_index_set(
2626        #[case] callgrind_metrics: CallgrindMetrics,
2627        #[case] expected_metrics: IndexSet<EventKind>,
2628    ) {
2629        assert_eq!(IndexSet::from(callgrind_metrics), expected_metrics);
2630    }
2631
2632    #[rstest]
2633    #[case::none(CachegrindMetrics::None, indexset![])]
2634    #[case::all(CachegrindMetrics::All, indexset![Cm::Ir, Cm::Dr, Cm::Dw, Cm::I1mr, Cm::D1mr,
2635        Cm::D1mw, Cm::ILmr, Cm::DLmr, Cm::DLmw, Cm::I1MissRate, Cm::LLiMissRate, Cm::D1MissRate,
2636        Cm::LLdMissRate, Cm::LLMissRate, Cm::L1hits, Cm::LLhits, Cm::RamHits, Cm::TotalRW,
2637        Cm::L1HitRate, Cm::LLHitRate, Cm::RamHitRate, Cm::EstimatedCycles, Cm::Bc, Cm::Bcm, Cm::Bi,
2638        Cm::Bim,
2639    ])]
2640    #[case::default(CachegrindMetrics::Default, indexset![Cm::Ir, Cm::L1hits, Cm::LLhits,
2641        Cm::RamHits, Cm::TotalRW, Cm::EstimatedCycles, Cm::Bc, Cm::Bcm, Cm::Bi, Cm::Bim
2642    ])]
2643    #[case::cache_misses(CachegrindMetrics::CacheMisses, indexset![Cm::I1mr, Cm::D1mr, Cm::D1mw,
2644        Cm::ILmr, Cm::DLmr, Cm::DLmw
2645    ])]
2646    #[case::cache_miss_rates(CachegrindMetrics::CacheMissRates, indexset![Cm::I1MissRate,
2647        Cm::D1MissRate, Cm::LLMissRate, Cm::LLiMissRate, Cm::LLdMissRate
2648    ])]
2649    #[case::cache_hits(CachegrindMetrics::CacheHits, indexset![
2650        Cm::L1hits, Cm::LLhits, Cm::RamHits
2651    ])]
2652    #[case::cache_hit_rates(CachegrindMetrics::CacheHitRates, indexset![
2653        Cm::L1HitRate, Cm::LLHitRate, Cm::RamHitRate
2654    ])]
2655    #[case::cache_sim(CachegrindMetrics::CacheSim, indexset![Cm::Dr, Cm::Dw, Cm::I1mr, Cm::D1mr,
2656        Cm::D1mw, Cm::ILmr, Cm::DLmr, Cm::DLmw, Cm::I1MissRate, Cm::LLiMissRate, Cm::D1MissRate,
2657        Cm::LLdMissRate, Cm::LLMissRate, Cm::L1hits, Cm::LLhits, Cm::RamHits, Cm::TotalRW,
2658        Cm::L1HitRate, Cm::LLHitRate, Cm::RamHitRate, Cm::EstimatedCycles
2659    ])]
2660    #[case::branch_sim(CachegrindMetrics::BranchSim, indexset![
2661        Cm::Bc, Cm::Bcm, Cm::Bi, Cm::Bim
2662    ])]
2663    #[case::single_event(CachegrindMetrics::SingleEvent(Cm::Ir), indexset![Cm::Ir])]
2664    fn test_cachegrind_metrics_into_index_set(
2665        #[case] cachegrind_metrics: CachegrindMetrics,
2666        #[case] expected_metrics: IndexSet<CachegrindMetric>,
2667    ) {
2668        assert_eq!(IndexSet::from(cachegrind_metrics), expected_metrics);
2669    }
2670
2671    #[rstest]
2672    #[case::empty(&[], &[], &[])]
2673    #[case::prepend_empty(&["--some"], &[], &["--some"])]
2674    #[case::initial_empty(&[], &["--some"], &["--some"])]
2675    #[case::both_same_arg(&["--some"], &["--some"], &["--some", "--some"])]
2676    #[case::both_different_arg(&["--some"], &["--other"], &["--other", "--some"])]
2677    fn test_raw_args_prepend(
2678        #[case] raw_args: &[&str],
2679        #[case] other: &[&str],
2680        #[case] expected: &[&str],
2681    ) {
2682        let mut raw_args = RawArgs::new(raw_args.iter().map(ToOwned::to_owned));
2683        let other = RawArgs::new(other.iter().map(ToOwned::to_owned));
2684        let expected = RawArgs::new(expected.iter().map(ToOwned::to_owned));
2685
2686        raw_args.prepend(&other);
2687        assert_eq!(raw_args, expected);
2688    }
2689
2690    #[test]
2691    fn test_tool_update_when_tools_match() {
2692        let mut base = Tool::new(ValgrindTool::Callgrind);
2693        let other = Tool {
2694            kind: ValgrindTool::Callgrind,
2695            enable: Some(true),
2696            raw_args: RawArgs::new(["--some"]),
2697            show_log: Some(false),
2698            regression_config: Some(ToolRegressionConfig::None),
2699            flamegraph_config: Some(ToolFlamegraphConfig::None),
2700            output_format: Some(ToolOutputFormat::None),
2701            entry_point: Some(EntryPoint::Default),
2702            frames: Some(vec!["some::frame".to_owned()]),
2703        };
2704        let expected = other.clone();
2705        base.update(&other);
2706        assert_eq!(base, expected);
2707    }
2708
2709    #[test]
2710    fn test_tool_update_when_tools_not_match() {
2711        let mut base = Tool::new(ValgrindTool::Callgrind);
2712        let other = Tool {
2713            kind: ValgrindTool::DRD,
2714            enable: Some(true),
2715            raw_args: RawArgs::new(["--some"]),
2716            show_log: Some(false),
2717            regression_config: Some(ToolRegressionConfig::None),
2718            flamegraph_config: Some(ToolFlamegraphConfig::None),
2719            output_format: Some(ToolOutputFormat::None),
2720            entry_point: Some(EntryPoint::Default),
2721            frames: Some(vec!["some::frame".to_owned()]),
2722        };
2723
2724        let expected = base.clone();
2725        base.update(&other);
2726
2727        assert_eq!(base, expected);
2728    }
2729}