1use crate::arch::parse_args;
2use crate::style::StyleConfig;
3use libc::{c_ulonglong, user_regs_struct};
4use libc::{
5 MAP_ANONYMOUS, MAP_FIXED, MAP_LOCKED, MAP_NONBLOCK, MAP_NORESERVE, MAP_POPULATE, MAP_PRIVATE,
6 MAP_SHARED, MAP_STACK, PROT_EXEC, PROT_NONE, PROT_READ, PROT_WRITE,
7};
8use nix::unistd::Pid;
9use serde::ser::{SerializeMap, SerializeSeq};
10use serde::Serialize;
11use serde_json::Value;
12use std::borrow::Cow::{self, Borrowed, Owned};
13use std::fmt::{Debug, Display};
14use std::io;
15use std::io::Write;
16use std::time::Duration;
17use syscalls::Sysno;
18
19#[derive(Debug)]
20pub struct SyscallInfo {
21 pub typ: &'static str,
22 pub pid: Pid,
23 pub syscall: Sysno,
24 pub args: SyscallArgs,
25 pub result: RetCode,
26 pub duration: Duration,
27}
28
29impl SyscallInfo {
30 pub fn new(
31 pid: Pid,
32 syscall: Sysno,
33 ret_code: RetCode,
34 registers: user_regs_struct,
35 duration: Duration,
36 ) -> Self {
37 Self {
38 typ: "SYSCALL",
39 pid,
40 syscall,
41 args: parse_args(pid, syscall, registers),
42 result: ret_code,
43 duration,
44 }
45 }
46
47 pub fn write_syscall(
48 &self,
49 style: StyleConfig,
50 string_limit: Option<usize>,
51 show_syscall_num: bool,
52 show_duration: bool,
53 output: &mut dyn Write,
54 ) -> anyhow::Result<()> {
55 if style.use_colors {
56 write!(output, "[{}] ", style.pid.apply_to(&self.pid.to_string()))?;
57 } else {
58 write!(output, "[{}] ", &self.pid)?;
59 }
60 if show_syscall_num {
61 write!(output, "{:>3} ", self.syscall.id())?;
62 }
63 if style.use_colors {
64 let styled = style.syscall.apply_to(self.syscall.to_string());
65 write!(output, "{styled}(")
66 } else {
67 write!(output, "{}(", &self.syscall)
68 }?;
69 for (idx, arg) in self.args.0.iter().enumerate() {
70 if idx > 0 {
71 write!(output, ", ")?;
72 }
73 if self.syscall == Sysno::execve || self.syscall == Sysno::execveat {
75 match (idx, arg) {
77 (1, SyscallArg::StrVec(_, _)) => arg.write(output, string_limit)?,
79 (2, SyscallArg::StrVec(vs, maybe_addr)) => {
81 if let Some(addr) = maybe_addr {
82 let count = if !vs.is_empty() {
83 vs.len()
84 } else {
85 crate::arch::read_string_array_count(self.pid, *addr as c_ulonglong)
87 };
88 write!(output, "{:#x} /* {} vars */", *addr, count)?
89 } else {
90 arg.write(output, string_limit)?;
92 }
93 }
94 _ => arg.write(output, string_limit)?,
96 }
97 } else if self.syscall == Sysno::mmap {
98 let parts = format_mmap_args(&self.args.0, string_limit);
101 write!(
102 output,
103 "{}",
104 parts.get(idx).map(|s| s.as_str()).unwrap_or("")
105 )?;
106 } else {
107 arg.write(output, string_limit)?;
108 }
109 }
110 write!(output, ") = ")?;
111 if self.syscall == Sysno::exit || self.syscall == Sysno::exit_group {
112 write!(output, "?")?;
113 } else {
114 if style.use_colors {
115 let style = style.from_ret_code(self.result);
116 write!(output, "{}", style.apply_to(self.result.to_string()))
121 } else {
122 write!(output, "{}", self.result)
123 }?;
124 if show_duration {
125 write!(output, " <{:.6}ns>", self.duration.as_nanos())?;
127 }
128 }
129 Ok(writeln!(output)?)
130 }
131}
132
133impl Serialize for SyscallInfo {
134 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
135 let mut map = serializer.serialize_map(Some(7))?;
136 map.serialize_entry("type", &self.typ)?;
137 map.serialize_entry("pid", &self.pid.as_raw())?;
138 map.serialize_entry("num", &self.syscall)?;
139 map.serialize_entry("syscall", &self.syscall.to_string())?;
140 map.serialize_entry("args", &self.args)?;
141 match self.result {
142 RetCode::Ok(value) => map.serialize_entry("success", &value)?,
143 RetCode::Err(value) => map.serialize_entry("error", &value)?,
144 RetCode::Address(value) => map.serialize_entry("result", &value)?,
145 }
146 map.serialize_entry("duration", &self.duration.as_secs_f64())?;
147 map.end()
148 }
149}
150
151#[derive(Debug)]
152pub struct SyscallArgs(pub Vec<SyscallArg>);
153
154impl Serialize for SyscallArgs {
155 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
156 let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
157 for arg in &self.0 {
158 let value = match arg {
159 SyscallArg::Int(v) => serde_json::to_value(v).unwrap(),
160 SyscallArg::Str(v) => serde_json::to_value(v).unwrap(),
161 SyscallArg::Addr(v) => Value::String(format!("{v:#x}")),
162 SyscallArg::StrVec(vs, _addr) => serde_json::to_value(vs).unwrap(),
163 };
164 seq.serialize_element(&value)?;
165 }
166 seq.end()
167 }
168}
169
170#[derive(Debug, Copy, Clone)]
171pub enum RetCode {
172 Ok(i32),
173 Err(i32),
174 Address(usize),
175}
176
177impl RetCode {
178 pub fn from_raw(ret_code: c_ulonglong) -> Self {
179 let ret_i32 = ret_code as isize;
180 if ret_i32.abs() > 0x8000 {
182 Self::Address(ret_code as usize)
183 } else if ret_i32 < 0 {
184 Self::Err(ret_i32 as i32)
185 } else {
186 Self::Ok(ret_i32 as i32)
187 }
188 }
189}
190
191impl Display for RetCode {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 match self {
194 Self::Ok(v) | Self::Err(v) => Display::fmt(v, f),
195 Self::Address(v) => write!(f, "{v:#X}"),
196 }
197 }
198}
199
200#[derive(Debug, Serialize)]
201pub enum SyscallArg {
202 Int(i64),
203 Str(String),
204 StrVec(Vec<String>, Option<usize>),
206 Addr(usize),
207}
208
209impl SyscallArg {
210 pub fn write(&self, f: &mut dyn Write, string_limit: Option<usize>) -> io::Result<()> {
211 match self {
212 Self::Int(v) => write!(f, "{v}"),
213 Self::Str(v) => {
214 let value: Value = match string_limit {
215 Some(width) => trim_str(v, width),
216 None => Borrowed(v.as_ref()),
217 }
218 .into();
219 write!(f, "{value}")
220 }
221 Self::StrVec(vs, _addr) => {
222 let mut parts = Vec::with_capacity(vs.len());
224 for s in vs {
225 let trimmed = match string_limit {
226 Some(width) => trim_str(s, width).into_owned(),
227 None => s.clone(),
228 };
229 parts.push(serde_json::to_string(&trimmed).unwrap());
230 }
231 write!(f, "[{}]", parts.join(", "))
232 }
233 Self::Addr(v) => write!(f, "{v:#X}"),
234 }
235 }
236}
237
238fn trim_str(string: &str, limit: usize) -> Cow<'_, str> {
239 match string.chars().as_str().get(..limit) {
240 None => Borrowed(string),
241 Some(s) => Owned(format!("{s}...")),
242 }
243}
244
245fn format_prot(flags: i64) -> String {
246 let mut parts = Vec::new();
247 if flags & (PROT_READ as i64) != 0 {
248 parts.push("PROT_READ");
249 }
250 if flags & (PROT_WRITE as i64) != 0 {
251 parts.push("PROT_WRITE");
252 }
253 if flags & (PROT_EXEC as i64) != 0 {
254 parts.push("PROT_EXEC");
255 }
256 if flags == (PROT_NONE as i64) {
257 parts.push("PROT_NONE");
258 }
259 if parts.is_empty() {
260 format!("{flags}")
261 } else {
262 parts.join("|")
263 }
264}
265
266fn format_map_flags(flags: i64) -> String {
267 let mut parts = Vec::new();
268 if flags & (MAP_SHARED as i64) != 0 {
269 parts.push("MAP_SHARED");
270 }
271 if flags & (MAP_PRIVATE as i64) != 0 {
272 parts.push("MAP_PRIVATE");
273 }
274 if flags & (MAP_ANONYMOUS as i64) != 0 {
275 parts.push("MAP_ANONYMOUS");
276 }
277 if flags & (MAP_FIXED as i64) != 0 {
278 parts.push("MAP_FIXED");
279 }
280 if flags & (MAP_STACK as i64) != 0 {
281 parts.push("MAP_STACK");
282 }
283 if flags & (MAP_NORESERVE as i64) != 0 {
284 parts.push("MAP_NORESERVE");
285 }
286 if flags & (MAP_LOCKED as i64) != 0 {
287 parts.push("MAP_LOCKED");
288 }
289 if flags & (MAP_POPULATE as i64) != 0 {
290 parts.push("MAP_POPULATE");
291 }
292 if flags & (MAP_NONBLOCK as i64) != 0 {
293 parts.push("MAP_NONBLOCK");
294 }
295 if parts.is_empty() {
296 format!("{flags}")
297 } else {
298 parts.join("|")
299 }
300}
301
302fn format_mmap_args(args: &[SyscallArg], string_limit: Option<usize>) -> Vec<String> {
303 let mut out = vec![String::new(); args.len()];
305 for (i, a) in args.iter().enumerate() {
306 match (i, a) {
307 (0, SyscallArg::Addr(addr)) => {
308 if *addr == 0 {
309 out[i] = "NULL".to_string();
310 } else {
311 out[i] = format!("{:#X}", addr);
312 }
313 }
314 (1, SyscallArg::Int(len)) => out[i] = format!("{}", len),
315 (2, SyscallArg::Int(prot)) => out[i] = format_prot(*prot),
316 (3, SyscallArg::Int(flags)) => out[i] = format_map_flags(*flags),
317 (4, SyscallArg::Int(fd)) => out[i] = format!("{}", fd),
318 (5, SyscallArg::Int(off)) => out[i] = format!("{}", off),
319 (_, other) => {
321 let mut buf = Vec::new();
322 other.write(&mut buf, string_limit).ok();
323 out[i] = String::from_utf8_lossy(&buf).to_string();
324 }
325 }
326 }
327 out
328}