iai_callgrind_runner/runner/tool/
args.rs1use 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 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 _ => {}
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}