use std::collections::{HashMap, HashSet};
use anyhow::Result;
use log::debug;
use crate::sys_trace::strace::funcs::Function;
use super::funcs::{FunctionExtractor, FunctionExtractorOutput, FunctionTrace};
pub struct ProcSynchronizer {
extractor: FunctionExtractor,
primed: bool,
known_pids: HashSet<i32>,
suppressed: HashMap<i32, Vec<FunctionExtractorOutput>>,
}
impl ProcSynchronizer {
#[must_use]
pub fn new() -> Self {
ProcSynchronizer {
extractor: FunctionExtractor::new(),
primed: false,
known_pids: HashSet::new(),
suppressed: HashMap::new(),
}
}
pub fn extract(&mut self, input: String) -> Result<Vec<FunctionExtractorOutput>> {
let output = self.extractor.extract(input)?;
let Some(function_trace) = output.borrow_function_trace() else {
return Ok(vec![output]);
};
match function_trace {
FunctionTrace::Function { pid, function } => {
if !self.primed {
self.known_pids.insert(*pid);
self.primed = true;
}
if !self.known_pids.contains(pid) {
debug!("suppressing syscall: {output:?}");
self.suppressed.entry(*pid).or_default().push(output);
return Ok(vec![]);
}
if let Function::Clone { child_pid, .. } = function {
let child_pid = *child_pid;
let mut unsuppressed_calls = vec![output];
self.release_pid(&mut unsuppressed_calls, child_pid);
Ok(unsuppressed_calls)
} else {
Ok(vec![output])
}
}
FunctionTrace::Exit { pid } => {
self.known_pids.remove(pid);
Ok(vec![output])
}
FunctionTrace::ExitThreadGroup { pid: _ } => {
Ok(vec![output])
}
}
}
fn release_pid(
&mut self,
unsuppressed_calls: &mut Vec<FunctionExtractorOutput>,
child_pid: i32,
) {
self.known_pids.insert(child_pid);
let suppressed = self.suppressed.remove(&child_pid);
if let Some(suppressed) = suppressed {
let mut addt_children: Vec<i32> = vec![];
for x in &suppressed {
if let Some(FunctionTrace::Function {
function: Function::Clone { child_pid, .. },
..
}) = x.borrow_function_trace()
{
addt_children.push(*child_pid);
}
}
debug!(
"releasing {} suppressed syscalls and then {} other suppressed clones within",
suppressed.len(),
addt_children.len()
);
unsuppressed_calls.extend(suppressed);
for child in addt_children {
self.release_pid(unsuppressed_calls, child);
}
}
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use crate::sys_trace::strace::{
funcs::{Function, FunctionTrace, StringArgument},
tokenizer::EncodedString,
};
use super::ProcSynchronizer;
#[test]
fn passthrough() -> Result<()> {
let mut fe = ProcSynchronizer::new();
let t = fe.extract(String::from(r"1234321 close(3) = 0"))?;
assert_eq!(t.len(), 1);
let t = &t[0];
assert_eq!(
t.borrow_function_trace(),
&Some(FunctionTrace::Function {
pid: 1_234_321,
function: Function::Close { fd: 3 }
})
);
Ok(())
}
#[test]
fn clone_emitted() -> Result<()> {
let mut fe = ProcSynchronizer::new();
let t = fe.extract(String::from(r"15615 close(3) = 0"))?;
assert_eq!(t.len(), 1);
let t = fe.extract(String::from(r"15615 clone3({flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, child_tid=0x7f67099ff990, parent_tid=0x7f67099ff990, exit_signal=0, stack=0x7f67091ff000, stack_size=0x7fff80, tls=0x7f67099ff6c0} => {parent_tid=[0]}, 88) = 15620"))?;
assert_eq!(t.len(), 1);
let t = fe.extract(String::from(r"15620 close(3) = 0"))?;
assert_eq!(t.len(), 1);
Ok(())
}
#[test]
fn clone_incomplete() -> Result<()> {
let mut fe = ProcSynchronizer::new();
let t = fe.extract(String::from(r"15615 close(3) = 0"))?;
assert_eq!(t.len(), 1);
let t = fe.extract(String::from(r"15615 clone3({flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, child_tid=0x7f67099ff990, parent_tid=0x7f67099ff990, exit_signal=0, stack=0x7f67091ff000, stack_size=0x7fff80, tls=0x7f67099ff6c0} <unfinished ...>"))?;
assert_eq!(t.len(), 1);
let t = fe.extract(String::from(r"15620 close(3) = 0"))?;
assert_eq!(t.len(), 0);
let t = fe.extract(String::from(
r"15615 <... clone3 resumed> => {parent_tid=[0]}, 88) = 15620",
))?;
assert_eq!(t.len(), 2);
let syscall = &t[0];
assert_eq!(
syscall.borrow_function_trace(),
&Some(FunctionTrace::Function {
pid: 15615,
function: Function::Clone {
child_pid: 15620,
thread: true
}
})
);
let syscall = &t[1];
assert_eq!(
syscall.borrow_function_trace(),
&Some(FunctionTrace::Function {
pid: 15620,
function: Function::Close { fd: 3 }
})
);
Ok(())
}
#[test]
fn recursive_release() -> Result<()> {
let mut fe = ProcSynchronizer::new();
let t = fe.extract(String::from("55498 clone(child_stack=0xc00004c000, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS, tls=0xc000088098) = 55624"))?;
assert_eq!(t.len(), 1);
let t = fe.extract(String::from("55498 clone(child_stack=0xc0000a4000, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS <unfinished ...>"))?;
assert_eq!(t.len(), 1); let syscall = &t[0];
assert_eq!(syscall.borrow_function_trace(), &None);
let t = fe.extract(String::from("55626 clone(child_stack=0xc0000a0000, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS, tls=0xc0000a4098) = 55631"))?;
assert_eq!(t.len(), 0);
let t = fe.extract(String::from(
"55631 read(9, \"\\x01\", 1) = 1",
))?;
assert_eq!(t.len(), 0);
let t = fe.extract(String::from(
"55498 <... clone resumed>, tls=0xc000088798) = 55626",
))?;
assert_eq!(t.len(), 3); let syscall = &t[0];
assert_eq!(
syscall.borrow_function_trace(),
&Some(FunctionTrace::Function {
pid: 55498,
function: Function::Clone {
child_pid: 55626,
thread: true
}
})
);
let syscall = &t[1];
assert_eq!(
syscall.borrow_function_trace(),
&Some(FunctionTrace::Function {
pid: 55626,
function: Function::Clone {
child_pid: 55631,
thread: true
}
})
);
let syscall = &t[2];
assert_eq!(
syscall.borrow_function_trace(),
&Some(FunctionTrace::Function {
pid: 55631,
function: Function::Read {
fd: 9,
data: StringArgument::Complete(&EncodedString::new("\\x01")),
}
})
);
let t = fe.extract(String::from("55631 clone(child_stack=0xc000198000, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS, tls=0xc000180098) = 55644"))?;
assert_eq!(t.len(), 1); let syscall = &t[0];
assert_eq!(
syscall.borrow_function_trace(),
&Some(FunctionTrace::Function {
pid: 55631,
function: Function::Clone {
child_pid: 55644,
thread: true
}
})
);
Ok(())
}
}