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