From 91dc8997b2430b71b98fc2b6b3edd8968746d108 Mon Sep 17 00:00:00 2001 From: sectordistrict <157201659+sectordistrict@users.noreply.github.com> Date: Sat, 16 Nov 2024 18:38:50 +0200 Subject: [PATCH] ctrl-c prints summary if enabled + small modifications, Fixes #8 --- Cargo.lock | 22 ++++- Cargo.toml | 3 +- src/main.rs | 172 ++++++++++++++++++-------------------- src/one_line_formatter.rs | 10 +-- src/syscall_object.rs | 7 +- src/utilities.rs | 32 +++---- 6 files changed, 123 insertions(+), 123 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b998694..00a6072 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,6 +130,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ctrlc" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +dependencies = [ + "nix 0.29.0", + "windows-sys 0.59.0", +] + [[package]] name = "errno" version = "0.2.8" @@ -230,10 +240,11 @@ dependencies = [ [[package]] name = "intentrace" -version = "0.2.5" +version = "0.2.6" dependencies = [ "clone3", "colored", + "ctrlc", "errno 0.3.9", "exec", "lazy_static", @@ -858,6 +869,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" diff --git a/Cargo.toml b/Cargo.toml index 19be55b..0f70589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "intentrace" -version = "0.2.5" +version = "0.2.6" description = "intentrace is strace with intent, it goes all the way for you instead of half the way." edition = "2021" license = "MIT" @@ -9,6 +9,7 @@ repository = "https://github.com/sectordistrict/intentrace" [dependencies] clone3 = "0.2.3" colored = "2.1.0" +ctrlc = { version = "3.4.5", features = ["termination"] } errno = "0.3.9" exec = "0.3.1" lazy_static = "1.5.0" diff --git a/src/main.rs b/src/main.rs index 2a381f4..2413752 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,23 +44,12 @@ use nix::{ }; use procfs::process::{MMapPath, MemoryMap}; use std::{ - cell::{Cell, RefCell}, - collections::{HashMap, HashSet}, - env::args, - error::Error, - fmt::Debug, - mem::{self, transmute, MaybeUninit}, - os::{raw::c_void, unix::process::CommandExt}, - path::PathBuf, - process::{exit, Command, Stdio}, - ptr::null, - time::Duration, + cell::{Cell, RefCell}, collections::{HashMap, HashSet}, env::args, error::Error, fmt::Debug, mem::{self, transmute, MaybeUninit}, os::{raw::c_void, unix::process::CommandExt}, path::PathBuf, process::{exit, Command, Stdio}, ptr::null, sync::atomic::Ordering, time::Duration }; use pete::{Ptracer, Restart, Stop, Tracee}; use syscalls::Sysno; use utilities::{ - display_unsupported, errno_check, parse_args, set_memory_break, ATTACH, EXITERS, FAILED_ONLY, - FOLLOW_FORKS, OUTPUT, OUTPUT_FOLLOW_FORKS, QUIET, SUMMARY, + display_unsupported, errno_check, parse_args, set_memory_break, ATTACH, EXITERS, FAILED_ONLY, FOLLOW_FORKS, HALT_FORK_FOLLOW, OUTPUT, OUTPUT_FOLLOW_FORKS, QUIET, SUMMARY }; mod syscall_object; @@ -73,19 +62,26 @@ mod utilities; fn main() { + ctrlc::set_handler(||{ + HALT_FORK_FOLLOW.store(true,Ordering::SeqCst); + if SUMMARY.load(Ordering::SeqCst) { + print_table(); + } + std::process::exit(0); + }).unwrap(); let cl = parse_args(); runner(cl); } fn runner(command_line: Vec) { - if FOLLOW_FORKS.get() { - if ATTACH.get().0 { + if FOLLOW_FORKS.load(Ordering::SeqCst) { + if ATTACH.get().is_some() { follow_forks(None); } else { follow_forks(Some(command_line)); } } else { - if ATTACH.get().0 { + if ATTACH.get().is_some() { parent(None); } else { match unsafe { fork() }.expect("Error: Fork Failed") { @@ -98,6 +94,9 @@ fn runner(command_line: Vec) { } } } + if SUMMARY.load(Ordering::SeqCst) { + print_table(); + } } fn child_trace_me(comm: Vec) { @@ -112,6 +111,10 @@ fn child_trace_me(comm: Vec) { let _ = ptrace::traceme().unwrap(); // EXECUTE let res = command.exec(); + + // This won't be reached unless exec fails + eprintln!("Error: could not execute program"); + std::process::exit(res.raw_os_error().unwrap()) } fn follow_forks(command_to_run: Option>) { @@ -132,13 +135,13 @@ fn follow_forks(command_to_run: Option>) { } // ATTACHING TO PID None => { - if ATTACH.get().0 { + if ATTACH.get().is_some() { let mut ptracer = Ptracer::new(); *ptracer.poll_delay_mut() = Duration::from_nanos(1); let child = ptracer - .attach(pete::Pid::from_raw(ATTACH.get().1.unwrap() as i32)) + .attach(pete::Pid::from_raw(ATTACH.get().unwrap() as i32)) .unwrap(); - ptrace_ptracer(ptracer, Pid::from_raw(ATTACH.get().1.unwrap() as i32)); + ptrace_ptracer(ptracer, Pid::from_raw(ATTACH.get().unwrap() as i32)); } else { eprintln!("Usage: invalid arguments\n"); } @@ -150,7 +153,7 @@ fn parent(child_or_attach: Option) { let child = if child_or_attach.is_some() { child_or_attach.unwrap() } else { - let child = Pid::from_raw(ATTACH.get().1.unwrap() as i32); + let child = Pid::from_raw(ATTACH.get().unwrap() as i32); let _ = ptrace::attach(child).unwrap(); child }; @@ -169,7 +172,7 @@ fn parent(child_or_attach: Option) { match nix::sys::ptrace::getregs(child) { Ok(registers) => { syscall = SyscallObject::build(®isters, child); - syscall_will_run(&mut syscall, ®isters, child); + syscall_will_run(&mut syscall); if syscall.is_exiting() { break 'main_loop; } @@ -185,7 +188,7 @@ fn parent(child_or_attach: Option) { end = Some(std::time::Instant::now()); match nix::sys::ptrace::getregs(child) { Ok(registers) => { - OUTPUT.with_borrow_mut(|ref mut output| { + let mut output= OUTPUT.lock().unwrap(); output .entry(syscall.sysno) .and_modify(|value| { @@ -198,7 +201,6 @@ fn parent(child_or_attach: Option) { 1, end.unwrap().duration_since(start.unwrap()), )); - }); start = None; end = None; syscall_returned(&mut syscall, ®isters) @@ -221,9 +223,6 @@ fn parent(child_or_attach: Option) { } } } - if SUMMARY.get() { - print_table(); - } } fn ptrace_ptracer(mut ptracer: Ptracer, child: Pid) { @@ -232,12 +231,14 @@ fn ptrace_ptracer(mut ptracer: Ptracer, child: Pid) { let mut pid_syscall_map: HashMap = HashMap::new(); while let Some(mut tracee) = ptracer.wait().unwrap() { + if HALT_FORK_FOLLOW.load(Ordering::SeqCst) { + break; + } let syscall_pid = Pid::from_raw(tracee.pid.as_raw()); match tracee.stop { Stop::SyscallEnter => 'label_for_early_break: { match nix::sys::ptrace::getregs(syscall_pid) { Ok(registers) => { - // p!(tracee.registers().unwrap()); if syscall_pid != last_pid { if let Some(last_syscall) = pid_syscall_map.get_mut(&last_pid) { last_syscall.paused = true; @@ -246,17 +247,16 @@ fn ptrace_ptracer(mut ptracer: Ptracer, child: Pid) { } } let mut syscall = SyscallObject::build(®isters, syscall_pid); - if SUMMARY.get() { - OUTPUT_FOLLOW_FORKS.with_borrow_mut(|ref mut output| { - output - .entry(syscall.sysno) - .and_modify(|value| { - *value += 1; - }) - .or_insert(1); - }); + if SUMMARY.load(Ordering::SeqCst) { + let mut output = OUTPUT_FOLLOW_FORKS.lock().unwrap(); + output + .entry(syscall.sysno) + .and_modify(|value| { + *value += 1; + }) + .or_insert(1); } - syscall_will_run(&mut syscall, ®isters, syscall_pid); + syscall_will_run(&mut syscall); if syscall.is_exiting() { break 'label_for_early_break; } @@ -293,12 +293,9 @@ fn ptrace_ptracer(mut ptracer: Ptracer, child: Pid) { } ptracer.restart(tracee, Restart::Syscall).unwrap(); } - if SUMMARY.get() { - print_table(); - } } -fn syscall_will_run(syscall: &mut SyscallObject, registers: &user_regs_struct, child: Pid) { +fn syscall_will_run(syscall: &mut SyscallObject) { // GET PRECALL DATA (some data will be lost if not saved in this time frame) syscall.get_precall_data(); @@ -306,8 +303,7 @@ fn syscall_will_run(syscall: &mut SyscallObject, registers: &user_regs_struct, c if syscall.is_mem_alloc_dealloc() { set_memory_break(syscall.child); } - - if FOLLOW_FORKS.get() || syscall.is_exiting() { + if FOLLOW_FORKS.load(Ordering::SeqCst) || syscall.is_exiting() { syscall.format(); if syscall.is_exiting() { let exited = " EXITED ".on_bright_red(); @@ -328,7 +324,7 @@ fn syscall_returned(syscall: &mut SyscallObject, registers: &user_regs_struct) { // GET POSTCALL DATA (some data will be lost if not saved in this time frame) syscall.get_postcall_data(); - if !FOLLOW_FORKS.get() { + if !FOLLOW_FORKS.load(Ordering::SeqCst) { if FAILED_ONLY.get() && !syscall.parse_return_value_one_line().is_err() { return; } @@ -357,57 +353,55 @@ fn handle_getting_registers_error(errno: Errno, syscall_enter_or_exit: &str, sys } fn print_table() { - if FOLLOW_FORKS.get() { - OUTPUT_FOLLOW_FORKS.with_borrow_mut(|output| { - let mut vec = Vec::from_iter(output); - vec.sort_by(|(_sysno, count), (_sysno2, count2)| count2.cmp(count)); + if FOLLOW_FORKS.load(Ordering::SeqCst) { + let output = OUTPUT_FOLLOW_FORKS.lock().unwrap(); + let mut vec = Vec::from_iter(output.iter()); + vec.sort_by(|(_sysno, count), (_sysno2, count2)| count2.cmp(count)); - use tabled::{builder::Builder, settings::Style}; - let mut builder = Builder::new(); + use tabled::{builder::Builder, settings::Style}; + let mut builder = Builder::new(); - builder.push_record(["calls", "syscall"]); - builder.push_record([""]); - for (sys, count) in vec { - builder.push_record([&count.to_string(), sys.name()]); - } - let table = builder.build().with(Style::ascii_rounded()).to_string(); + builder.push_record(["calls", "syscall"]); + builder.push_record([""]); + for (sys, count) in vec { + builder.push_record([&count.to_string(), sys.name()]); + } + let table = builder.build().with(Style::ascii_rounded()).to_string(); - println!("\n{}", table); - }); + println!("\n{}", table); } else { - OUTPUT.with_borrow_mut(|output| { - let mut vec = Vec::from_iter(output); - vec.sort_by( - |(_sysno, (count, duration)), (_sysno2, (count2, duration2))| { - duration2.cmp(duration) - }, - ); + let mut output= OUTPUT.lock().unwrap(); + let mut vec = Vec::from_iter(output.iter()); + vec.sort_by( + |(_sysno, (count, duration)), (_sysno2, (count2, duration2))| { + duration2.cmp(duration) + }, + ); - use tabled::{builder::Builder, settings::Style}; - let mut builder = Builder::new(); + use tabled::{builder::Builder, settings::Style}; + let mut builder = Builder::new(); - builder.push_record(["% time", "seconds", "usecs/call", "calls", "syscall"]); - builder.push_record([""]); - let total_time = vec - .iter() - .map(|(_, (_, time))| time.as_micros()) - .sum::(); - for (sys, (count, time)) in vec { - let time_MICROS = time.as_micros() as f64; - let time = time_MICROS / 1_000_000.0; - let usecs_call = (time_MICROS / *count as f64) as i64; - let time_percent = time_MICROS / total_time as f64; - builder.push_record([ - &format!("{:.2}", time_percent * 100.0), - &format!("{:.6}", time), - &format!("{}", usecs_call), - &count.to_string(), - sys.name(), - ]); - } - let table = builder.build().with(Style::ascii_rounded()).to_string(); + builder.push_record(["% time", "seconds", "usecs/call", "calls", "syscall"]); + builder.push_record([""]); + let total_time = vec + .iter() + .map(|(_, (_, time))| time.as_micros()) + .sum::(); + for (sys, (count, time)) in vec { + let time_MICROS = time.as_micros() as f64; + let time = time_MICROS / 1_000_000.0; + let usecs_call = (time_MICROS / *count as f64) as i64; + let time_percent = time_MICROS / total_time as f64; + builder.push_record([ + &format!("{:.2}", time_percent * 100.0), + &format!("{:.6}", time), + &format!("{}", usecs_call), + &count.to_string(), + sys.name(), + ]); + } + let table = builder.build().with(Style::ascii_rounded()).to_string(); - println!("\n{}", table); - }); + println!("\n{}", table); } } diff --git a/src/one_line_formatter.rs b/src/one_line_formatter.rs index 8556770..81e6103 100644 --- a/src/one_line_formatter.rs +++ b/src/one_line_formatter.rs @@ -2,7 +2,7 @@ use std::{ env::current_dir, mem, os::fd::RawFd, - path::{Path, PathBuf}, + path::{Path, PathBuf}, sync::atomic::Ordering, }; use crate::{ @@ -77,7 +77,7 @@ impl SyscallObject { use crate::syscall_object::SyscallState::*; if self.state == Entering { - if FOLLOW_FORKS.get() { + if FOLLOW_FORKS.load(Ordering::SeqCst) { self.one_line.extend(vec![ "\n".white(), self.child.to_string().bright_blue(), @@ -5819,12 +5819,6 @@ impl SyscallObject { self.one_line.push(" |=> ".white()); self.one_line.push("thread id of the child: ".green()); self.one_line.push(eph_return.unwrap().yellow()); - // let a = -38; - // let b: u32 = unsafe { mem::transmute(a) }; - // pp!("b: ", b); - // let a: i64 = -38; - // let b: u64 = unsafe { mem::transmute(a) }; - // pp!("b: ", b); // TODO! fix occasional error (syscall returns -38) if clone_vm { self.one_line.push(new_thread()); diff --git a/src/syscall_object.rs b/src/syscall_object.rs index 0262037..cd1c88e 100644 --- a/src/syscall_object.rs +++ b/src/syscall_object.rs @@ -43,7 +43,7 @@ use std::{ io::IoSliceMut, mem::{self, transmute, zeroed}, os::{fd::RawFd, raw::c_void}, - ptr::null, + ptr::null, sync::atomic::Ordering, }; #[derive(Clone, Debug, PartialEq)] @@ -107,7 +107,7 @@ impl SyscallObject { let mut output = vec![]; output.push("\n".dimmed()); let eph_return = self.parse_return_value(1); - if FOLLOW_FORKS.get() { + if FOLLOW_FORKS.load(Ordering::SeqCst) { output.push(self.child.to_string().bright_blue()); } else { if eph_return.is_ok() { @@ -652,8 +652,6 @@ impl SyscallObject { } } Always_Successful_Numeric => { - // p!(register_value as isize); - // p!(register_value as usize); Ok(format!("{}", register_value as isize)) } Signal_Or_Errno(signal) => { @@ -840,7 +838,6 @@ impl SyscallObject { if size > 100 { let size = -1 * (size as i32); let error = nix::errno::Errno::from_raw(size); - p!(self.errno); } else { match SyscallObject::read_string_specific_length( self.args[index] as usize, diff --git a/src/utilities.rs b/src/utilities.rs index a6c5a26..5151a4f 100644 --- a/src/utilities.rs +++ b/src/utilities.rs @@ -4,9 +4,7 @@ use nix::{errno::Errno, libc::__errno_location, unistd::Pid}; use phf::phf_set; use procfs::process::{MMapPath, MemoryMap}; use std::{ - cell::{Cell, RefCell}, - collections::HashMap, - time::Duration, + borrow::BorrowMut, cell::{Cell, RefCell}, collections::HashMap, sync::{atomic::{AtomicBool, Ordering}, Arc, Mutex}, time::Duration }; use syscalls::Sysno; @@ -38,19 +36,20 @@ pub static EXITERS: phf::Set<&'static str> = phf_set! { thread_local! { pub static PRE_CALL_PROGRAM_BREAK_POINT: Cell = Cell::new(0); pub static INTENT: Cell = Cell::new(true); - pub static SUMMARY: Cell = Cell::new(false); pub static STRING_LIMIT: Cell = Cell::new(36); - pub static FOLLOW_FORKS: Cell = Cell::new(false); pub static QUIET: Cell = Cell::new(false); pub static FAILED_ONLY: Cell = Cell::new(false); - pub static ATTACH: Cell<(bool,Option)> = Cell::new((false,None)); - pub static OUTPUT: RefCell> = RefCell::new(HashMap::new()); - pub static OUTPUT_FOLLOW_FORKS: RefCell> = RefCell::new(HashMap::new()); + pub static ATTACH: Cell> = Cell::new(None); // TODO! Time blocks feature // pub static TIME_BLOCKS: Cell = Cell::new(false); } lazy_static! { + pub static ref HALT_FORK_FOLLOW: AtomicBool = AtomicBool::new(false); + pub static ref FOLLOW_FORKS: AtomicBool = AtomicBool::new(false); + pub static ref SUMMARY: AtomicBool = AtomicBool::new(false); + pub static ref OUTPUT: Mutex> = Mutex::new(HashMap::new()); + pub static ref OUTPUT_FOLLOW_FORKS: Mutex> = Mutex::new(HashMap::new()); pub static ref SYSCALL_MAP: HashMap = initialize_syscall_map(); pub static ref PAGE_SIZE: usize = page_size::get(); } @@ -98,11 +97,11 @@ Options: // ); // std::process::exit(100); // } - SUMMARY.set(true); + SUMMARY.store(true, Ordering::SeqCst); } "-p" | "--attach" => { let _ = args.next().unwrap(); - if FOLLOW_FORKS.get() { + if FOLLOW_FORKS.load(Ordering::SeqCst) { eprintln!( "Usage: attaching to a running process and fork following are mutually exclusive\n" ); @@ -111,7 +110,7 @@ Options: let pid = match args.next() { Some(pid_str) => match pid_str.parse::() { Ok(pid) => { - ATTACH.set((true, Some(pid))); + ATTACH.set(Some(pid)); } Err(_) => { eprintln!("Usage: pid is not valid\n"); @@ -126,7 +125,7 @@ Options: } "-f" | "--follow-forks" => { let _ = args.next().unwrap(); - if ATTACH.get().0 { + if ATTACH.get().is_some() { eprintln!( "Usage: attaching to a running process and fork following are mutually exclusive\n" ); @@ -138,11 +137,11 @@ Options: // ); // std::process::exit(100); // } - FOLLOW_FORKS.set(true); + FOLLOW_FORKS.store(true,Ordering::SeqCst); } "-z" | "--failed-only" => { let _ = args.next().unwrap(); - if FOLLOW_FORKS.get() { + if FOLLOW_FORKS.load(Ordering::SeqCst) { eprintln!( "Usage: failed only retrieval and fork following are mutually exclusive\n" ); @@ -207,11 +206,6 @@ pub fn get_child_memory_break(child: Pid) -> (usize, (u64, u64)) { } pub fn errno_check(rax: u64) -> Option { - // let a = unsafe { &*__errno_location() }; - // p!("ERRNO LOCATION"); - // p!(a); - // p!("ERRNO LOCATION"); - // TODO! improve on this hack let max_errno = 4095; // strace does something similar to this