evobench_tools/evaluator/options.rs
1//! Options parameterizing the evaluation (excludes subcommands or
2//! similar, those remain in src/bin/*.rs).
3
4use std::path::PathBuf;
5
6use anyhow::{Result, bail};
7
8use crate::stats_tables::stats::StatsField;
9
10// We use 101 buckets for percentiles instead of 100, so that we get
11// buckets at positions 50, 25, 75 for exact matches, OK? (Although
12// note that the `Stats` median is not based on those buckets
13// (anymore).)
14pub const TILE_COUNT: usize = 101;
15
16#[derive(clap::Args, Debug)]
17pub struct EvaluationOpts {
18 /// The width of the column with the probes path, in characters
19 /// (as per Excel's definition of characters)
20 // (This is for Excel output only; could there be a better place?
21 // But the `OutputOpts` are just options on the same level (via
22 // flatten), currently, only subcommands would allow selective
23 // choice, and there may be no way to specify multiple subcommands
24 // in the same command invocation.)
25 #[clap(short, long, default_value = "100")]
26 pub key_width: f64,
27
28 /// Include the internally-allocated thread number in call
29 /// path strings in the output.
30 #[clap(short, long)]
31 pub show_thread_number: bool,
32
33 /// Show the call path so that the leaf instead of the root is on
34 /// the left (only has an effect on tables (Excel), not
35 /// flamegraphs).
36 #[clap(short = 'r', long)]
37 pub show_reversed: bool,
38}
39
40/// Using private fields, to enforce calling .check()!
41#[derive(clap::Args, Debug)]
42pub struct OutputOpts {
43 /// Path to write Excel output to
44 #[clap(short, long)]
45 excel: Option<PathBuf>,
46
47 /// Base path to write flame graph SVG to; "-$type.svg" is
48 /// appended, where type is "real", "cpu", "sys" or
49 /// "ctx-switches".
50 #[clap(short, long)]
51 flame: Option<PathBuf>,
52}
53
54/// Do not use for level 0 (i.e. `single` subcommand), there sum must
55/// always be used!
56#[derive(clap::Args, Debug)]
57pub struct FlameFieldOpt {
58 /// What field to select from the last stage for the flame graph
59 /// (tables show all stats values, but flame graphs can only show
60 /// one, that's why this option is needed for those).
61 #[clap(long, default_value = "avg")]
62 pub flame_field: StatsField<TILE_COUNT>,
63}
64
65/// OutputOpts split into checked `OutputVariants` and possibly other
66/// options (flame_field was here in the past, not any more, but maybe
67/// other fields will come in the future, thus keeping the separation
68/// into OutputVariants and the wrapper)
69pub struct CheckedOutputOptions {
70 pub variants: OutputVariants<PathBuf>,
71}
72
73impl OutputOpts {
74 pub fn check(self) -> Result<CheckedOutputOptions> {
75 let Self { excel, flame } = self;
76
77 let any_given = [excel.is_some(), flame.is_some()].iter().any(|b| *b);
78 if !any_given {
79 bail!("no output files were specified")
80 }
81
82 Ok(CheckedOutputOptions {
83 variants: OutputVariants { excel, flame },
84 })
85 }
86}
87
88/// Same as OutputOpts but at least one file is set; parameterized so
89/// it can be used for pipelining via its `map` method.
90#[derive(Clone)]
91pub struct OutputVariants<T> {
92 pub excel: Option<T>,
93 pub flame: Option<T>,
94}
95
96#[derive(Clone, Copy, PartialEq)]
97pub enum CheckedOutputOptionsMapCase {
98 Excel,
99 Flame,
100}
101
102impl<T> OutputVariants<T> {
103 /// get a field
104 pub fn get(&self, case: CheckedOutputOptionsMapCase) -> &Option<T> {
105 match case {
106 CheckedOutputOptionsMapCase::Excel => &self.excel,
107 CheckedOutputOptionsMapCase::Flame => &self.flame,
108 }
109 }
110
111 /// `f` is applied to all fields that are `Some`
112 pub fn map<U>(self, f: impl Fn(CheckedOutputOptionsMapCase, T) -> U) -> OutputVariants<U> {
113 let Self { excel, flame } = self;
114 OutputVariants {
115 excel: excel.map(|v| f(CheckedOutputOptionsMapCase::Excel, v)),
116 flame: flame.map(|v| f(CheckedOutputOptionsMapCase::Flame, v)),
117 }
118 }
119
120 /// `f` is applied to all fields that are `Some`
121 pub fn try_map<U, E>(
122 self,
123 f: impl Fn(CheckedOutputOptionsMapCase, T) -> Result<U, E>,
124 ) -> Result<OutputVariants<U>, E> {
125 let Self { excel, flame } = self;
126 Ok(OutputVariants {
127 excel: excel
128 .map(|v| f(CheckedOutputOptionsMapCase::Excel, v))
129 .transpose()?,
130 flame: flame
131 .map(|v| f(CheckedOutputOptionsMapCase::Flame, v))
132 .transpose()?,
133 })
134 }
135}
136
137#[derive(clap::Args, Debug)]
138pub struct EvaluationAndOutputOpts {
139 #[clap(flatten)]
140 pub evaluation_opts: EvaluationOpts,
141 #[clap(flatten)]
142 pub output_opts: OutputOpts,
143}
144
145#[derive(clap::Args, Debug)]
146pub struct FieldSelectorDimension3Opt {
147 /// What stats field to select for the summary stats (i.e. of the
148 /// 2nd dimension, for calculating the 3rd dimension in the data
149 /// evaluation, after dimensions 1 (probe name) and 2 (stats
150 /// fields)). Valid values: n|sum|average|median|sd or a floating
151 /// point number between 0 and 1 for selecting a percentile.
152 #[clap(long, default_value = "avg")]
153 pub summary_field: StatsField<TILE_COUNT>,
154}
155
156#[derive(clap::Args, Debug)]
157pub struct FieldSelectorDimension4Opt {
158 /// What stats field to select for the trend stats (i.e. of the
159 /// 3rd dimension, for calculating the 4nd dimension in the data
160 /// evaluation, after dimensions 1 (probe name), 2 (stats fields),
161 /// 3 (stats of the field from dimension 2 selected by the
162 /// --summary-field option)). See --summary-field docs for the
163 /// valid values.
164 #[clap(long, default_value = "median")]
165 pub trend_field: StatsField<TILE_COUNT>,
166}