1use std::any::TypeId;
2use std::fmt::Display;
3use std::marker::PhantomData;
4
5use shadow_shim_helper_rs::emulated_time::EmulatedTime;
6use shadow_shim_helper_rs::syscall_types::SyscallReg;
7use shadow_shim_helper_rs::util::time::TimeParts;
8
9use crate::core::worker::Worker;
10use crate::host::memory_manager::MemoryManager;
11use crate::host::process::Process;
12use crate::host::syscall::types::{SyscallError, SyscallResult};
13use crate::host::thread::ThreadId;
14
15#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16pub enum FmtOptions {
17 Standard,
18 Deterministic,
19 Long,
20}
21
22#[derive(Copy, Clone, Debug, PartialEq, Eq)]
24#[repr(C)]
25pub enum StraceFmtMode {
26 Off,
27 Standard,
28 Deterministic,
29 Long,
30}
31
32impl From<StraceFmtMode> for Option<FmtOptions> {
33 fn from(x: StraceFmtMode) -> Self {
34 match x {
35 StraceFmtMode::Off => None,
36 StraceFmtMode::Standard => Some(FmtOptions::Standard),
37 StraceFmtMode::Deterministic => Some(FmtOptions::Deterministic),
38 StraceFmtMode::Long => Some(FmtOptions::Long),
39 }
40 }
41}
42
43impl From<Option<FmtOptions>> for StraceFmtMode {
44 fn from(x: Option<FmtOptions>) -> Self {
45 match x {
46 None => StraceFmtMode::Off,
47 Some(FmtOptions::Standard) => StraceFmtMode::Standard,
48 Some(FmtOptions::Deterministic) => StraceFmtMode::Deterministic,
49 Some(FmtOptions::Long) => StraceFmtMode::Long,
50 }
51 }
52}
53
54pub trait SyscallDisplay {
55 fn fmt(
56 &self,
57 f: &mut std::fmt::Formatter<'_>,
58 options: FmtOptions,
59 mem: &MemoryManager,
60 ) -> std::fmt::Result;
61}
62
63pub struct SyscallVal<'a, T> {
66 pub reg: SyscallReg,
67 pub args: [SyscallReg; 6],
68 options: FmtOptions,
69 mem: &'a MemoryManager,
70 _phantom: PhantomData<T>,
71}
72
73impl<'a, T> SyscallVal<'a, T> {
74 pub fn new(
75 reg: SyscallReg,
76 args: [SyscallReg; 6],
77 options: FmtOptions,
78 mem: &'a MemoryManager,
79 ) -> Self {
80 Self {
81 reg,
82 args,
83 options,
84 mem,
85 _phantom: PhantomData,
86 }
87 }
88
89 pub fn cast<V>(&self) -> SyscallVal<'a, V> {
91 SyscallVal {
92 reg: self.reg,
93 args: self.args,
94 options: self.options,
95 mem: self.mem,
96 _phantom: PhantomData,
97 }
98 }
99}
100
101impl<'a, T> Display for SyscallVal<'a, T>
102where
103 SyscallVal<'a, T>: SyscallDisplay,
104{
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 SyscallDisplay::fmt(self, f, self.options, self.mem)
107 }
108}
109
110#[derive(Default)]
112pub struct NoArg {}
113
114impl SyscallDisplay for SyscallVal<'_, NoArg> {
115 fn fmt(
116 &self,
117 _f: &mut std::fmt::Formatter<'_>,
118 _options: FmtOptions,
119 _mem: &MemoryManager,
120 ) -> std::fmt::Result {
121 panic!("We shouldn't ever try to format this.");
122 }
123}
124
125pub struct SyscallArgsFmt<'a, A = NoArg, B = NoArg, C = NoArg, D = NoArg, E = NoArg, F = NoArg> {
127 a: SyscallVal<'a, A>,
128 b: SyscallVal<'a, B>,
129 c: SyscallVal<'a, C>,
130 d: SyscallVal<'a, D>,
131 e: SyscallVal<'a, E>,
132 f: SyscallVal<'a, F>,
133}
134
135impl<'a, A, B, C, D, E, F> SyscallArgsFmt<'a, A, B, C, D, E, F>
136where
137 SyscallVal<'a, A>: Display,
138 SyscallVal<'a, B>: Display,
139 SyscallVal<'a, C>: Display,
140 SyscallVal<'a, D>: Display,
141 SyscallVal<'a, E>: Display,
142 SyscallVal<'a, F>: Display,
143{
144 pub fn new(args: [SyscallReg; 6], options: FmtOptions, mem: &'a MemoryManager) -> Self {
145 Self {
146 a: SyscallVal::new(args[0], args, options, mem),
147 b: SyscallVal::new(args[1], args, options, mem),
148 c: SyscallVal::new(args[2], args, options, mem),
149 d: SyscallVal::new(args[3], args, options, mem),
150 e: SyscallVal::new(args[4], args, options, mem),
151 f: SyscallVal::new(args[5], args, options, mem),
152 }
153 }
154}
155
156impl<'a, A, B, C, D, E, F> Display for SyscallArgsFmt<'a, A, B, C, D, E, F>
157where
158 SyscallVal<'a, A>: Display,
159 SyscallVal<'a, B>: Display,
160 SyscallVal<'a, C>: Display,
161 SyscallVal<'a, D>: Display,
162 SyscallVal<'a, E>: Display,
163 SyscallVal<'a, F>: Display,
164 A: 'static,
165 B: 'static,
166 C: 'static,
167 D: 'static,
168 E: 'static,
169 F: 'static,
170{
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 let args: [&dyn Display; 6] = [&self.a, &self.b, &self.c, &self.d, &self.e, &self.f];
173
174 let types: [TypeId; 6] = [
175 TypeId::of::<A>(),
176 TypeId::of::<B>(),
177 TypeId::of::<C>(),
178 TypeId::of::<D>(),
179 TypeId::of::<E>(),
180 TypeId::of::<F>(),
181 ];
182
183 let mut first = true;
184 for (arg, arg_type) in args.iter().zip(types) {
185 if arg_type == TypeId::of::<NoArg>() {
186 break;
189 }
190
191 if first {
192 write!(f, "{arg}")?;
193 first = false;
194 } else {
195 write!(f, ", {arg}")?;
196 }
197 }
198
199 Ok(())
200 }
201}
202
203pub struct SyscallResultFmt<'a, RV>
205where
206 SyscallVal<'a, RV>: Display,
207 RV: std::fmt::Debug,
208{
209 rv: &'a SyscallResult,
210 args: [SyscallReg; 6],
211 options: FmtOptions,
212 mem: &'a MemoryManager,
213 _phantom: PhantomData<RV>,
214}
215
216impl<'a, RV> SyscallResultFmt<'a, RV>
217where
218 SyscallVal<'a, RV>: Display,
219 RV: std::fmt::Debug,
220{
221 pub fn new(
222 rv: &'a SyscallResult,
223 args: [SyscallReg; 6],
224 options: FmtOptions,
225 mem: &'a MemoryManager,
226 ) -> Self {
227 Self {
228 rv,
229 args,
230 options,
231 mem,
232 _phantom: PhantomData,
233 }
234 }
235}
236
237impl<'a, RV> Display for SyscallResultFmt<'a, RV>
238where
239 SyscallVal<'a, RV>: Display,
240 RV: std::fmt::Debug,
241{
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 match self.rv {
244 SyscallResult::Ok(x) => {
245 let rv = SyscallVal::<'_, RV>::new(*x, self.args, self.options, self.mem);
246 write!(f, "{rv}")
247 }
248 SyscallResult::Err(SyscallError::Failed(failed)) => {
249 let errno = failed.errno;
250 let rv = SyscallReg::from(errno.to_negated_i64());
251 let rv = SyscallVal::<'_, RV>::new(rv, self.args, self.options, self.mem);
252 write!(f, "{rv} ({errno})")
253 }
254 SyscallResult::Err(SyscallError::Native) => {
255 write!(f, "<native>")
256 }
257 SyscallResult::Err(SyscallError::Blocked(_)) => {
258 write!(f, "<blocked>")
259 }
260 }
261 }
262}
263
264pub fn write_syscall(
266 mut writer: impl std::io::Write,
267 sim_time: &EmulatedTime,
268 tid: ThreadId,
269 name: impl Display,
270 args: impl Display,
271 rv: impl Display,
272) -> std::io::Result<()> {
273 let sim_time = sim_time.duration_since(&EmulatedTime::SIMULATION_START);
274 let sim_time = TimeParts::from_nanos(sim_time.as_nanos());
275 let sim_time = sim_time.fmt_hr_min_sec_nano();
276
277 writeln!(writer, "{sim_time} [tid {tid}] {name}({args}) = {rv}")
278}
279
280pub fn log_syscall_simple(
282 proc: &Process,
283 logging_mode: Option<FmtOptions>,
284 tid: ThreadId,
285 syscall_name: &str,
286 args_str: &str,
287 result: &SyscallResult,
288) -> std::io::Result<()> {
289 let Some(logging_mode) = logging_mode else {
290 return Ok(());
292 };
293
294 let args = [SyscallReg::from(0i64); 6];
295 let mem = proc.memory_borrow();
296 let rv = SyscallResultFmt::<libc::c_long>::new(result, args, logging_mode, &mem);
297
298 proc.with_strace_file(|file| {
299 let time = Worker::current_time();
300
301 if let Some(time) = time {
302 write_syscall(file, &time, tid, syscall_name, args_str, rv)
303 } else {
304 log::warn!("Could not log syscall {syscall_name} with time {time:?}");
305 Ok(())
306 }
307 })
308 .unwrap_or(Ok(()))?;
309
310 Ok(())
311}
312
313#[cfg(test)]
314mod test {
315 use std::process::Command;
316
317 use linux_api::posix_types::Pid;
318 use shadow_shim_helper_rs::syscall_types::SyscallArgs;
319
320 use super::*;
321
322 #[test]
323 #[cfg_attr(miri, ignore)]
325 fn test_no_args() {
326 let args = SyscallArgs {
327 number: 100,
328 args: [0u32.into(); 6],
329 };
330
331 let mut proc = Command::new("sleep").arg(10.to_string()).spawn().unwrap();
333 let pid = Pid::from_raw(proc.id().try_into().unwrap()).unwrap();
334
335 let mem = unsafe { MemoryManager::new(pid) };
336
337 let _syscall_args = <SyscallArgsFmt>::new(args.args, FmtOptions::Standard, &mem);
339
340 proc.kill().unwrap();
341 proc.wait().unwrap();
342 }
343}