iai_callgrind_runner/runner/tool/
args.rs

1use std::ffi::OsString;
2use std::fmt::Display;
3use std::str::FromStr;
4
5use anyhow::{anyhow, Result};
6use log::warn;
7
8use super::{ToolOutputPath, ValgrindTool};
9use crate::api::{self};
10use crate::error::Error;
11use crate::util::{bool_to_yesno, yesno_to_bool};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum FairSched {
15    Yes,
16    No,
17    Try,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct ToolArgs {
22    pub tool: ValgrindTool,
23    pub output_paths: Vec<OsString>,
24    pub log_path: Option<OsString>,
25    pub error_exitcode: String,
26    pub verbose: bool,
27    pub trace_children: bool,
28    pub fair_sched: FairSched,
29    pub other: Vec<String>,
30}
31
32impl Display for FairSched {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        let string = match self {
35            FairSched::Yes => "yes",
36            FairSched::No => "no",
37            FairSched::Try => "try",
38        };
39        write!(f, "{string}")
40    }
41}
42
43impl FromStr for FairSched {
44    type Err = anyhow::Error;
45
46    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
47        match s {
48            "no" => Ok(FairSched::No),
49            "yes" => Ok(FairSched::Yes),
50            "try" => Ok(FairSched::Try),
51            _ => Err(anyhow!(
52                "Invalid argument for --fair-sched. Valid arguments are: 'yes', 'no', 'try'"
53            )),
54        }
55    }
56}
57
58impl ToolArgs {
59    pub fn try_from_raw_args(tool: ValgrindTool, raw_args: api::RawArgs) -> Result<Self> {
60        let mut tool_args = Self {
61            tool,
62            output_paths: Vec::default(),
63            log_path: Option::default(),
64            error_exitcode: match tool {
65                ValgrindTool::Memcheck | ValgrindTool::Helgrind | ValgrindTool::DRD => {
66                    "201".to_owned()
67                }
68                ValgrindTool::Callgrind
69                | ValgrindTool::Massif
70                | ValgrindTool::DHAT
71                | ValgrindTool::BBV => "0".to_owned(),
72            },
73            verbose: false,
74            other: Vec::default(),
75            trace_children: true,
76            fair_sched: FairSched::Try,
77        };
78
79        for arg in raw_args.0 {
80            match arg
81                .trim()
82                .split_once('=')
83                .map(|(k, v)| (k.trim(), v.trim()))
84            {
85                Some(("--tool", _)) => warn!("Ignoring {} argument '{arg}'", tool.id()),
86                Some((
87                    "--dhat-out-file" | "--massif-out-file" | "--bb-out-file" | "--pc-out-file"
88                    | "--log-file" | "--log-fd" | "--log-socket" | "--xml" | "--xml-file"
89                    | "--xml-fd" | "--xml-socket" | "--xml-user-comment",
90                    _,
91                )) => warn!(
92                    "Ignoring {} argument '{arg}': Output/Log files of tools are managed by \
93                     Iai-Callgrind",
94                    tool.id()
95                ),
96                Some(("--error-exitcode", value)) => {
97                    value.clone_into(&mut tool_args.error_exitcode);
98                }
99                Some((key @ "--trace-children", value)) => {
100                    tool_args.trace_children = yesno_to_bool(value).ok_or_else(|| {
101                        Error::InvalidBoolArgument((key.to_owned(), value.to_owned()))
102                    })?;
103                }
104                Some(("--fair-sched", value)) => {
105                    tool_args.fair_sched = FairSched::from_str(value)?;
106                }
107                None if matches!(
108                    arg.as_str(),
109                    "-h" | "--help"
110                        | "--help-dyn-options"
111                        | "--help-debug"
112                        | "--version"
113                        | "-q"
114                        | "--quiet"
115                ) =>
116                {
117                    warn!("Ignoring {} argument '{arg}'", tool.id());
118                }
119                None if matches!(arg.as_str(), "--verbose") => tool_args.verbose = true,
120                None | Some(_) => tool_args.other.push(arg),
121            }
122        }
123
124        Ok(tool_args)
125    }
126
127    // TODO: memcheck: --xtree-leak-file=<filename> [default: xtleak.kcg.%p]
128    pub fn set_output_arg<T>(&mut self, output_path: &ToolOutputPath, modifier: Option<T>)
129    where
130        T: AsRef<str>,
131    {
132        if !self.tool.has_output_file() {
133            return;
134        }
135
136        match self.tool {
137            ValgrindTool::Callgrind => {
138                let mut arg = OsString::from("--callgrind-out-file=");
139                let callgrind_out_path = if let Some(modifier) = modifier {
140                    output_path.with_modifiers([modifier.as_ref()])
141                } else if self.trace_children {
142                    output_path.with_modifiers(["#%p"])
143                } else {
144                    output_path.clone()
145                };
146                arg.push(callgrind_out_path.to_path());
147                self.output_paths.push(arg);
148            }
149            ValgrindTool::Massif => {
150                let mut arg = OsString::from("--massif-out-file=");
151                let massif_out_path = if let Some(modifier) = modifier {
152                    output_path.with_modifiers([modifier.as_ref()])
153                } else if self.trace_children {
154                    output_path.with_modifiers(["#%p"])
155                } else {
156                    output_path.clone()
157                };
158                arg.push(massif_out_path.to_path());
159                self.output_paths.push(arg);
160            }
161            ValgrindTool::DHAT => {
162                let mut arg = OsString::from("--dhat-out-file=");
163                let dhat_out_path = if let Some(modifier) = modifier {
164                    output_path.with_modifiers([modifier.as_ref()])
165                } else if self.trace_children {
166                    output_path.with_modifiers(["#%p"])
167                } else {
168                    output_path.clone()
169                };
170                arg.push(dhat_out_path.to_path());
171                self.output_paths.push(arg);
172            }
173            ValgrindTool::BBV => {
174                let mut bb_arg = OsString::from("--bb-out-file=");
175                let mut pc_arg = OsString::from("--pc-out-file=");
176                let (bb_out, pc_out) = if let Some(modifier) = modifier {
177                    (
178                        output_path.with_modifiers(["bb", modifier.as_ref()]),
179                        output_path.with_modifiers(["pc", modifier.as_ref()]),
180                    )
181                } else if self.trace_children {
182                    (
183                        output_path.with_modifiers(["bb", "#%p"]),
184                        output_path.with_modifiers(["pc", "#%p"]),
185                    )
186                } else {
187                    (
188                        output_path.with_modifiers(["bb"]),
189                        output_path.with_modifiers(["pc"]),
190                    )
191                };
192                bb_arg.push(bb_out.to_path());
193                pc_arg.push(pc_out.to_path());
194                self.output_paths.push(bb_arg);
195                self.output_paths.push(pc_arg);
196            }
197            // The other tools don't have an outfile
198            _ => {}
199        }
200    }
201
202    pub fn set_log_arg<T>(&mut self, output_path: &ToolOutputPath, modifier: Option<T>)
203    where
204        T: AsRef<str>,
205    {
206        let log_output = if let Some(modifier) = modifier {
207            output_path
208                .to_log_output()
209                .with_modifiers([modifier.as_ref()])
210        } else if self.trace_children {
211            output_path.to_log_output().with_modifiers(["#%p"])
212        } else {
213            output_path.to_log_output()
214        };
215        let mut arg = OsString::from("--log-file=");
216        arg.push(log_output.to_path());
217        self.log_path = Some(arg);
218    }
219
220    pub fn to_vec(&self) -> Vec<OsString> {
221        let mut vec: Vec<OsString> = vec![];
222
223        vec.push(format!("--tool={}", self.tool.id()).into());
224        vec.push(format!("--error-exitcode={}", &self.error_exitcode).into());
225        vec.push(format!("--trace-children={}", &bool_to_yesno(self.trace_children)).into());
226        vec.push(format!("--fair-sched={}", self.fair_sched).into());
227        if self.verbose {
228            vec.push("--verbose".into());
229        }
230
231        vec.extend(self.other.iter().map(OsString::from));
232        vec.extend_from_slice(&self.output_paths);
233        if let Some(log_arg) = self.log_path.as_ref() {
234            vec.push(log_arg.clone());
235        }
236
237        vec
238    }
239}