Commit ab942219 authored by jD91mZM2's avatar jD91mZM2

Split strace into two modes: Simple and advanced

Advanced mode will let you recurse children
parent 93af0d85
......@@ -6,7 +6,12 @@ edition = "2018"
[dependencies]
bitflags = "1.1.0"
structopt = { version = "0.2.18", optional = true }
# redox_syscall = { git = "https://gitlab.redox-os.org/jD91mZM2/syscall.git", branch = "ptrace" }
# redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" }
redox_syscall = { path = "/home/user/redox-nix/redox/kernel/syscall" }
[features]
default = [ "advanced" ]
advanced = [ "structopt" ]
use std::{
collections::HashMap,
fs::File,
io::{prelude::*, Result},
os::unix::io::AsRawFd,
};
use syscall::{
data::Event,
flag::EVENT_READ
};
use strace::{Flags, EventData, NonblockTracer, Pid, Tracer};
use structopt::StructOpt;
#[derive(StructOpt)]
// Only make `pub` features that are in both simple and advanced modes
pub struct Opt {
#[structopt(short, long)]
/// Specify whether or not strace should trace more than just the
/// top level child process
recursive: bool,
/// Specify the command and arguments to run
pub cmd: Vec<String>
}
pub fn parse_args() -> Opt {
Opt::from_args()
}
struct Handle {
pid: Pid,
tracer: NonblockTracer,
unclosed: Vec<String>
}
pub fn inner_main(root: Pid, tracer: Tracer, opt: Opt) -> Result<()> {
let mut tracer = tracer.nonblocking()?;
tracer.next(crate::TRACE_FLAGS)?;
let mut events = File::open("event:")?;
let mut next_id = 0;
events.write(&Event {
id: tracer.file.as_raw_fd() as usize,
flags: EVENT_READ,
data: next_id,
})?;
let mut tracers = HashMap::new();
tracers.insert(next_id, Handle {
pid: root,
tracer,
unclosed: Vec::new(),
});
next_id += 1;
loop {
let mut event = Event::default();
events.read(&mut event)?;
let index = event.data;
let handle = tracers.get_mut(&index).unwrap();
handle.tracer.next(crate::TRACE_FLAGS)?;
for event in handle.tracer.events()? {
let event = event?;
// We don't want to mutably borrow tracer across the
// entire loop - rather, re-fetch it at each iteration.
let handle = tracers.get_mut(&index).unwrap();
if event.cause == Flags::STOP_PRE_SYSCALL {
let regs = handle.tracer.regs.get_int()?;
let syscall = regs.format_syscall_full(&mut handle.tracer.mem);
eprintln!("SYSCALL (pid {}): {}", handle.pid, syscall);
handle.unclosed.push(syscall);
} else if event.cause == Flags::STOP_POST_SYSCALL {
let syscall = handle.unclosed.pop();
let syscall = syscall.as_ref().map(|s| &**s).unwrap_or("<unmatched syscall>");
let regs = handle.tracer.regs.get_int()?;
let ret = regs.return_value();
eprint!("SYSCALL RET (pid {}): {} = ", handle.pid, syscall);
match syscall::Error::demux(ret) {
Ok(val) => eprintln!("Ok({} ({:#X}))", val, val),
Err(err) => eprintln!("Err(\"{}\" ({:#X})) ({:#X})", err, err.errno, ret),
}
} else {
eprintln!("OTHER EVENT: {:?}", event);
if opt.recursive {
if let EventData::EventClone(pid) = event.data {
let mut child = NonblockTracer::attach(pid)?;
child.next(crate::TRACE_FLAGS)?;
events.write(&Event {
id: child.file.as_raw_fd() as usize,
flags: EVENT_READ,
data: next_id,
})?;
tracers.insert(next_id, Handle {
pid,
tracer: child,
unclosed: Vec::new(),
});
next_id += 1;
}
}
}
}
}
}
#[cfg(not(feature = "advanced"))]
mod simple;
#[cfg(not(feature = "advanced"))]
pub use simple::*;
#[cfg(feature = "advanced")]
mod advanced;
#[cfg(feature = "advanced")]
pub use advanced::*;
use std::{
env,
io::Result,
process,
};
use strace::{Flags, Pid, Tracer};
pub struct Opt {
pub cmd: Vec<String>
}
pub fn parse_args() -> Opt {
let cmd = env::args().collect();
if cmd.is_empty() {
eprintln!("Usage: strace <path>");
process::exit(1);
}
Opt { cmd }
}
pub fn inner_main(_pid: Pid, tracer: Tracer, _opt: Opt) -> Result<()> {
let mut unclosed = Vec::new();
loop {
let event = tracer.next_event(crate::TRACE_FLAGS)?
.from_callback(|event| -> Result<()> {
eprintln!("EVENT: {:?}", event);
Ok(())
})?;
if event.cause == Flags::STOP_PRE_SYSCALL {
let regs = tracer.regs.get_int()?;
let syscall = regs.format_syscall_full(&mut tracer.mem);
eprintln!("SYSCALL: {}", syscall);
unclosed.push(syscall);
} else if event.cause == Flags::STOP_POST_SYSCALL {
let syscall = unclosed.pop();
let syscall = syscall.as_ref().map(|s| &**s).unwrap_or("<unmatched syscall>");
let regs = tracer.regs.get_int()?;
let ret = regs.return_value();
eprint!("SYSCALL RET: {} = ", syscall);
match syscall::Error::demux(ret) {
Ok(val) => eprintln!("Ok({} ({:#X}))", val, val),
Err(err) => eprintln!("Err(\"{}\" ({:#X})) ({:#X})", err, err.errno, ret),
}
} else {
eprintln!("OTHER EVENT: {:?}", event);
}
}
}
......@@ -218,11 +218,11 @@ pub fn format_call(mut mem: Option<&mut Memory>, a: usize, b: usize, c: usize, d
SYS_CLOCK_GETTIME => format!(
"clock_gettime({}, {:?})",
b,
validate_slice!(c as *const TimeSpec, 1)
validate_slice!(c as *mut TimeSpec, 1)
),
SYS_CLONE => format!(
"clone({})",
b
"clone({:?})",
CloneFlags::from_bits(b)
),
SYS_EXIT => format!(
"exit({})",
......@@ -254,7 +254,7 @@ pub fn format_call(mut mem: Option<&mut Memory>, a: usize, b: usize, c: usize, d
SYS_FUTEX => format!(
"futex({:#X} [{:?}], {}, {}, {}, {})",
b,
validate_slice!(b as *const i32, 1).map(|uaddr| uaddr[0]),
validate_slice!(b as *mut i32, 1).map(|uaddr| uaddr[0]),
c,
d,
e,
......@@ -302,10 +302,10 @@ pub fn format_call(mut mem: Option<&mut Memory>, a: usize, b: usize, c: usize, d
validate_slice!(b as *const [usize; 2], c)
),
SYS_MPROTECT => format!(
"mprotect({:#X}, {}, {:#X})",
"mprotect({:#X}, {}, {:?})",
b,
c,
d
ProtFlags::from_bits(d)
),
SYS_NANOSLEEP => format!(
"nanosleep({:?}, ({}, {}))",
......@@ -323,10 +323,10 @@ pub fn format_call(mut mem: Option<&mut Memory>, a: usize, b: usize, c: usize, d
c
),
SYS_PHYSMAP => format!(
"physmap({:#X}, {}, {:#X})",
"physmap({:#X}, {}, {:?})",
b,
c,
d
PhysmapFlags::from_bits(d)
),
SYS_PHYSUNMAP => format!(
"physunmap({:#X})",
......@@ -338,7 +338,7 @@ pub fn format_call(mut mem: Option<&mut Memory>, a: usize, b: usize, c: usize, d
),
SYS_PIPE2 => format!(
"pipe2({:?}, {})",
validate_slice!(b as *const usize, 2),
validate_slice!(b as *mut usize, 2),
c
),
SYS_SETREGID => format!(
......@@ -361,10 +361,10 @@ pub fn format_call(mut mem: Option<&mut Memory>, a: usize, b: usize, c: usize, d
b
),
SYS_WAITPID => format!(
"waitpid({}, {:#X}, {})",
"waitpid({}, {:#X}, {:?})",
b,
c,
d
WaitFlags::from_bits(d)
),
SYS_YIELD => format!("yield()"),
_ => format!(
......
......@@ -24,17 +24,17 @@ fn e<T>(res: syscall::Result<T>) -> Result<T> {
bitflags! {
pub struct Flags: u64 {
const STOP_PRE_SYSCALL = syscall::PTRACE_STOP_PRE_SYSCALL;
const STOP_POST_SYSCALL = syscall::PTRACE_STOP_POST_SYSCALL;
const STOP_SINGLESTEP = syscall::PTRACE_STOP_SINGLESTEP;
const STOP_SIGNAL = syscall::PTRACE_STOP_SIGNAL;
const STOP_PRE_SYSCALL = syscall::PTRACE_STOP_PRE_SYSCALL.bits();
const STOP_POST_SYSCALL = syscall::PTRACE_STOP_POST_SYSCALL.bits();
const STOP_SINGLESTEP = syscall::PTRACE_STOP_SINGLESTEP.bits();
const STOP_SIGNAL = syscall::PTRACE_STOP_SIGNAL.bits();
const STOP_ALL = Self::STOP_PRE_SYSCALL.bits | Self::STOP_POST_SYSCALL.bits | Self::STOP_SINGLESTEP.bits | Self::STOP_SIGNAL.bits;
const EVENT_CLONE = syscall::PTRACE_EVENT_CLONE;
const EVENT_CLONE = syscall::PTRACE_EVENT_CLONE.bits();
const EVENT_ALL = Self::EVENT_CLONE.bits;
const FLAG_SYSEMU = syscall::PTRACE_FLAG_SYSEMU;
const FLAG_WAIT = syscall::PTRACE_FLAG_WAIT;
const FLAG_SYSEMU = syscall::PTRACE_FLAG_SYSEMU.bits();
const FLAG_WAIT = syscall::PTRACE_FLAG_WAIT.bits();
const FLAG_ALL = Self::FLAG_SYSEMU.bits | Self::FLAG_WAIT.bits;
}
}
......@@ -99,7 +99,7 @@ pub struct Event {
impl Event {
pub fn new(inner: syscall::PtraceEvent) -> Self {
Self {
cause: Flags::from_bits_truncate(inner.cause),
cause: Flags::from_bits_truncate(inner.cause.bits()),
data: match inner.cause {
syscall::PTRACE_EVENT_CLONE => EventData::EventClone(inner.a),
syscall::PTRACE_STOP_SIGNAL => EventData::StopSignal(inner.a, inner.b),
......@@ -183,9 +183,11 @@ impl Tracer {
/// Attach to a tracer with the specified PID. This will stop it.
pub fn attach(pid: Pid) -> Result<Self> {
Ok(Self {
file: OpenOptions::new().read(true).write(true).truncate(true).open(format!("proc:{}/trace", pid))?,
file: OpenOptions::new().read(true).write(true)
.truncate(true)
.open(format!("proc:{}/trace", pid))?,
regs: Registers::attach(pid)?,
mem: Memory::attach(pid)?
mem: Memory::attach(pid)?,
})
}
/// Set a breakpoint on the next specified stop, and wait for the
......@@ -218,20 +220,25 @@ impl Tracer {
let new_flags = old_flags | syscall::O_NONBLOCK;
e(syscall::fcntl(self.file.as_raw_fd() as usize, syscall::F_SETFL, new_flags))?;
Ok(NonblockTracer {
old_flags,
old_flags: Some(old_flags),
inner: self
})
}
/// Same as `EventHandler::iter`, but does not rely on having an
/// event handler. When only using a blocking tracer you shouldn't
/// need to worry about this.
pub fn events(&'_ mut self) -> impl Iterator<Item = Result<Event>> + '_ {
pub fn events(&self) -> Result<impl Iterator<Item = Result<Event>>> {
let mut buf = [MaybeUninit::<syscall::PtraceEvent>::uninit(); 4];
let mut i = 0;
let mut len = 0;
iter::from_fn(move || {
// I don't like this clone, but I don't want tracer.events()
// to prevent tracer from being borrowed again.
let mut file = self.file.try_clone()?;
Ok(iter::from_fn(move || {
if i >= len {
len = match self.file.read(unsafe {
len = match file.read(unsafe {
slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len() * mem::size_of::<syscall::PtraceEvent>())
}) {
Ok(n) => n / mem::size_of::<syscall::PtraceEvent>(),
......@@ -247,7 +254,7 @@ impl Tracer {
});
i += 1;
Some(Ok(ret))
})
}))
}
}
impl fmt::Debug for Tracer {
......@@ -276,7 +283,7 @@ impl<'a> EventHandler<'a> {
/// it'll never get another one. This is because non-breakpoint
/// events keep the tracee still running which can add more to the
/// queue at any time essentially.
pub fn iter(&'_ mut self) -> impl Iterator<Item = Result<Event>> + '_ {
pub fn iter(&self) -> Result<impl Iterator<Item = Result<Event>>> {
self.inner.events()
}
/// Tries to wait for a breakpoint event to be reached. To find
......@@ -294,7 +301,7 @@ impl<'a> EventHandler<'a> {
E: From<io::Error>
{
'outer: loop {
let mut events = self.iter();
let mut events = self.iter()?;
while let Some(event) = events.next() {
let event = event?;
......@@ -323,10 +330,24 @@ impl<'a> EventHandler<'a> {
}
pub struct NonblockTracer {
old_flags: usize,
old_flags: Option<usize>,
inner: Tracer
}
impl NonblockTracer {
/// Similar to `Tracer::attach`, but opens directly in nonblocking
/// mode which saves one system call.
pub fn attach(pid: Pid) -> Result<Self> {
Ok(Self {
old_flags: None,
inner: Tracer {
file: OpenOptions::new()
.read(true).write(true)
.truncate(true).open(format!("proc:{}/trace", pid))?,
regs: Registers::attach(pid)?,
mem: Memory::attach(pid)?,
}
})
}
/// Sets a breakpoint on the specified stop, without doing
/// anything else: No handling of events, no getting what
/// breakpoint actually caused this, no waiting for the
......@@ -344,9 +365,16 @@ impl NonblockTracer {
/// Convert this tracer back to a blocking version. Any yet unread
/// events are ignored.
pub fn blocking(mut self) -> Result<Tracer> {
self.events().for_each(|_| ());
e(syscall::fcntl(self.file.as_raw_fd() as usize, syscall::F_SETFL, self.old_flags))?;
pub fn blocking(self) -> Result<Tracer> {
self.events()?.for_each(|_| ());
let old_flags = match self.old_flags {
Some(flags) => flags,
None => {
let flags = e(syscall::fcntl(self.file.as_raw_fd() as usize, syscall::F_GETFL, 0))?;
flags & !syscall::O_NONBLOCK
}
};
e(syscall::fcntl(self.file.as_raw_fd() as usize, syscall::F_SETFL, old_flags))?;
Ok(self.inner)
}
}
......
......@@ -9,27 +9,25 @@ use std::{
use strace::{Flags, Pid, Tracer};
mod bin_modes;
use bin_modes as mode;
fn e<T>(res: syscall::Result<T>) -> Result<T> {
res.map_err(|err| Error::from_raw_os_error(err.errno))
}
const TRACE_FLAGS: Flags = Flags::from_bits_truncate(
pub const TRACE_FLAGS: Flags = Flags::from_bits_truncate(
(Flags::STOP_ALL.bits() & !Flags::STOP_SINGLESTEP.bits())
| Flags::EVENT_ALL.bits()
);
fn main() -> Result<()> {
let cmd = match env::args().nth(1) {
Some(cmd) => cmd,
None => {
eprintln!("Usage: strace <path>");
return Ok(());
}
};
let opt = mode::parse_args();
let mut file = None;
for mut path in env::split_paths(&env::var_os("PATH").unwrap_or(OsString::new())) {
path.push(&cmd);
path.push(&opt.cmd[0]);
if let Ok(fd) = e(syscall::open(&path.as_os_str().as_bytes(), syscall::O_RDONLY)) {
file = Some((path, fd));
break;
......@@ -44,15 +42,15 @@ fn main() -> Result<()> {
}
};
match e(unsafe { syscall::clone(0) })? {
0 => child(fd),
pid => parent(path, pid)
match e(unsafe { syscall::clone(syscall::CloneFlags::empty()) })? {
0 => child(fd, opt.cmd.clone()),
pid => parent(path, pid, opt)
}
}
fn child(fd: usize) -> Result<()> {
fn child(fd: usize, cmd_args: Vec<String>) -> Result<()> {
let mut args = Vec::new();
for arg in env::args().skip(1) {
for arg in cmd_args {
let len = arg.len();
let ptr = arg.as_ptr() as usize;
mem::forget(arg);
......@@ -76,7 +74,7 @@ fn child(fd: usize) -> Result<()> {
unreachable!("fexec can't return Ok(_)")
}
fn parent(path: PathBuf, pid: Pid) -> Result<()> {
fn parent(path: PathBuf, pid: Pid, opt: mode::Opt) -> Result<()> {
let mut status = 0;
eprintln!("Executing {} (PID {})", path.display(), pid);
......@@ -89,43 +87,10 @@ fn parent(path: PathBuf, pid: Pid) -> Result<()> {
// Won't actually restart the process, because it's stopped by ptrace
e(syscall::kill(pid, syscall::SIGCONT))?;
let mut main_loop = move || -> Result<()> {
let mut unclosed = Vec::new();
// There will first be a post-syscall for `kill`.
tracer.next(Flags::STOP_POST_SYSCALL)?;
loop {
let event = tracer.next_event(TRACE_FLAGS)?
.from_callback(|event| -> Result<()> {
eprintln!("EVENT: {:?}", event);
Ok(())
})?;
if event.cause == Flags::STOP_PRE_SYSCALL {
let regs = tracer.regs.get_int()?;
let syscall = regs.format_syscall_full(&mut tracer.mem);
eprintln!("SYSCALL: {}", syscall);
unclosed.push(syscall);
} else if event.cause == Flags::STOP_POST_SYSCALL {
let syscall = unclosed.pop();
let syscall = syscall.as_ref().map(|s| &**s).unwrap_or("<unmatched syscall>");
let regs = tracer.regs.get_int()?;
let ret = regs.return_value();
eprint!("SYSCALL RET: {} = ", syscall);
match syscall::Error::demux(ret) {
Ok(val) => eprintln!("Ok({} ({:#X}))", val, val),
Err(err) => eprintln!("Err(\"{}\" ({:#X})) ({:#X})", err, err.errno, ret),
}
} else {
eprintln!("OTHER BREAKPOINT: {:?}", event);
}
}
};
match main_loop() {
// There will first be a post-syscall for `kill`.
tracer.next(Flags::STOP_POST_SYSCALL)?;
match mode::inner_main(pid, tracer, opt) {
Err(ref err) if err.raw_os_error() == Some(syscall::ESRCH) => {
e(syscall::waitpid(pid, &mut status, syscall::WNOHANG))?;
if syscall::wifexited(status) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment