use colored::Colorize;
use crossbeam_channel;
use std::sync::mpsc;
use crate::Output;
use crate::TraceError;
use crate::{
ptrace::{SyscallPtrace, Tracer},
TraceType,
};
use crate::{HTraceIterator, Syscall};
use crate::{TraceOptions, TraceOutput, TraceThread};
#[derive(Debug)]
pub struct HStrace {
trace_type: TraceType,
tracing_options: TraceOptions,
syscall_event_receiver: Option<crossbeam_channel::Receiver<TraceOutput>>,
exit_receiver: Option<crossbeam_channel::Receiver<()>>,
}
impl HStrace {
pub fn new(
trace_type: TraceType,
tracing_options: TraceOptions,
exit_receiver: Option<crossbeam_channel::Receiver<()>>,
) -> Self {
Self {
trace_type,
tracing_options,
exit_receiver,
syscall_event_receiver: None,
}
}
pub fn start(&mut self) -> Result<(), TraceError> {
let (startup_result_sender, startup_result_receiver) = mpsc::channel();
let (trace_output_event_sender, trace_output_event_receiver) =
crossbeam_channel::bounded(50);
self.syscall_event_receiver = Some(trace_output_event_receiver);
let options = self.tracing_options.clone();
let set_exit_receiver = self.exit_receiver.clone();
let trace_type = self.trace_type.clone();
std::thread::spawn(move || {
run_tracing_thread(
trace_type,
&startup_result_sender,
trace_output_event_sender,
options,
set_exit_receiver,
);
});
match startup_result_receiver
.recv()
.map_err(|_| TraceError::MpscError)?
{
Err(e) => Err(e),
Ok(()) => Ok(()),
}
}
pub fn iter(&mut self) -> crossbeam_channel::Iter<TraceOutput> {
self.syscall_event_receiver.as_mut().unwrap().iter()
}
pub fn iter_grouped(&mut self) -> HTraceIterator {
HTraceIterator::new(self.syscall_event_receiver.clone().unwrap())
}
pub fn iter_as_syscall(
&mut self,
) -> std::iter::Map<crossbeam_channel::Iter<TraceOutput>, impl FnMut(TraceOutput) -> Syscall>
{
self.syscall_event_receiver
.as_mut()
.unwrap()
.iter()
.map(|message| Syscall::from(message))
}
pub fn print_totals(&self, out: &mut Output) {
out.write(format!(
"{}",
format!("-------------------------------------------------------------").blue()
));
out.write(format!(
"{}: 0, {}: 2352kB",
format!("Pids").magenta(),
format!("Max memory usage").magenta()
));
out.write(format!(
"Network: {}, {}",
format!(
"{} (main, {}/{})",
"127.0.0.1:8080".cyan(),
"52b".green(),
"52b".green()
),
format!(
"{} (main, {}, {})",
"10.5.2.1:8080".cyan(),
"52b".green(),
"52b".green()
),
));
out.write(format!(
"Files: {}, {}, (5 supressed)",
format!("{} ({})", "/etc/passwd".cyan(), "RW".red(),),
format!("{} ({})", "/usr/include/test.h".cyan(), "R".green(),),
));
out.write(format!(
"{}: run with --file-all to view all files",
format!("Info").magenta()
));
out.write(format!(
"^ above information is not real data, but displays a possibility of adding a summary"
));
}
}
fn run_tracing_thread(
trace_type: TraceType,
state_sender: &mpsc::Sender<Result<(), TraceError>>,
sender: crossbeam_channel::Sender<TraceOutput>,
options: TraceOptions,
exit_receiver: Option<crossbeam_channel::Receiver<()>>,
) {
let mut ptrace = match SyscallPtrace::new(trace_type) {
Ok(sd) => sd,
Err(e) => {
return state_sender.send(Err(e)).unwrap();
}
};
if let Err(e) = ptrace.initialize() {
return state_sender.send(Err(e)).unwrap();
}
let mut tracer_thread = TraceThread::new(ptrace, sender, options);
log::debug!("Tracer_thread started, going into loop");
state_sender.send(Ok(())).unwrap();
crossbeam_utils::thread::scope(|_s| {
loop {
if let Some(exit_receiver) = &exit_receiver {
match exit_receiver.try_recv() {
Ok(_) => {
log::debug!("exit_receiver triggered, break loop");
break;
}
Err(e) => match e {
crossbeam_channel::TryRecvError::Empty => (),
crossbeam_channel::TryRecvError::Disconnected => {
log::debug!("exit_receiver disconnected, break loop");
break;
}
},
}
}
match tracer_thread.iterate() {
Err(e) => {
log::error!("Tracer thread sent an error: {:?}", e);
break;
}
Ok((has_more, child_pid)) => {
if let Some(pid) = child_pid {
log::debug!("Detected fork, new pid {}", pid);
}
if !has_more {
break;
}
}
}
}
})
.unwrap();
if let Err(e) = tracer_thread.finalize() {
log::warn!("Finalizing tracer received error: {:?}", e);
}
}