summary refs log tree commit diff
path: root/ripple/minitrace/src
diff options
context:
space:
mode:
authoredef <edef@unfathomable.blue>2021-12-27 11:57:33 +0000
committeredef <edef@unfathomable.blue>2021-12-27 11:59:37 +0000
commit54eaee06413c60434ffb77e65aee8b9d9a09d8ab (patch)
treef90ab01297c87e34b4ca1a7c70488f54f07e1c09 /ripple/minitrace/src
parentb41a93355ebd37312266b5c1e599f5008414e396 (diff)
downloadunf-legacy-54eaee06413c60434ffb77e65aee8b9d9a09d8ab.tar.zst
ripple/minitrace: init
Minimal PTRACE_SYSCALL tracer for further narrowing down the syscalls
we need to support.

Change-Id: I562ee6e88e52d7deeee6de588ef00dfc1c38a71a
Diffstat (limited to 'ripple/minitrace/src')
-rw-r--r--ripple/minitrace/src/main.rs147
1 files changed, 147 insertions, 0 deletions
diff --git a/ripple/minitrace/src/main.rs b/ripple/minitrace/src/main.rs
new file mode 100644
index 0000000..362be9c
--- /dev/null
+++ b/ripple/minitrace/src/main.rs
@@ -0,0 +1,147 @@
+// SPDX-FileCopyrightText: edef <edef@unfathomable.blue>
+// SPDX-License-Identifier: OSL-3.0
+
+use {
+	nix::{
+		libc,
+		sys::{
+			ptrace,
+			wait::{waitpid, WaitPidFlag, WaitStatus},
+		},
+		unistd::Pid,
+	},
+	spawn_ptrace::CommandPtraceSpawn,
+	std::{env, io, process::Command},
+};
+
+// TODO(edef): consider implementing this in terms of TID?
+// tgids are a strict subset of tids
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct Tgid(pub libc::pid_t);
+
+impl Tgid {
+	fn as_pid(&self) -> Pid {
+		Pid::from_raw(self.0)
+	}
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct Tid(pub libc::pid_t);
+
+impl Tid {
+	fn as_pid(&self) -> Pid {
+		Pid::from_raw(self.0)
+	}
+}
+
+#[derive(Debug)]
+struct Process {
+	tgid: Tgid,
+}
+
+impl Process {
+	fn spawn(cmd: &mut Command) -> io::Result<Process> {
+		let child = cmd.spawn_ptrace()?;
+
+		// the thread group leader's TID is equal to the TGID
+		let tgid = Tgid(child.id() as _);
+
+		Ok(Process { tgid })
+	}
+}
+
+#[derive(Debug, Copy, Clone)]
+struct SyscallEntry {
+	number: u64,
+	// rdi, rsi, rdx, rcx, r8, r9
+	args: [u64; 6],
+}
+
+impl SyscallEntry {
+	fn from_regs(regs: libc::user_regs_struct) -> SyscallEntry {
+		SyscallEntry {
+			number: regs.orig_rax,
+			args: [regs.rdi, regs.rsi, regs.rdx, regs.rcx, regs.r8, regs.r9],
+		}
+	}
+}
+
+#[derive(Debug, Copy, Clone)]
+enum EntryExit {
+	/// Process is about to enter a syscall
+	Entry(SyscallEntry),
+	/// Process is about to exit a syscall
+	Exit(SyscallEntry, i64),
+}
+
+fn main() -> anyhow::Result<()> {
+	let process = Process::spawn(&mut {
+		let mut args = env::args();
+
+		// drop argv[0]
+		args.next();
+
+		let mut cmd = Command::new(args.next().unwrap());
+		for arg in args {
+			cmd.arg(arg);
+		}
+
+		cmd
+	})?;
+
+	let options = ptrace::Options::PTRACE_O_TRACESYSGOOD | ptrace::Options::PTRACE_O_TRACECLONE;
+	ptrace::setoptions(process.tgid.as_pid(), options)?;
+
+	// this is always equal to tgid for now,
+	// but I'm keeping this separate so it's obvious what has to be tgid
+	let tid = Tid(process.tgid.0);
+
+	let mut syscall_state: Option<EntryExit> = None;
+
+	loop {
+		ptrace::syscall(tid.as_pid(), None)?;
+		if let Some(EntryExit::Exit(..)) = syscall_state {
+			// syscall has completed now
+			syscall_state = None;
+		}
+
+		let status = waitpid(tid.as_pid(), Some(WaitPidFlag::__WALL))?;
+		println!("{:?}", status);
+
+		match (syscall_state, status) {
+			(None, WaitStatus::PtraceSyscall(event_tid)) => {
+				let event_tid = Tid(event_tid.as_raw());
+				assert_eq!(tid, event_tid);
+
+				let regs = ptrace::getregs(event_tid.as_pid())?;
+				let entry = SyscallEntry::from_regs(regs);
+
+				syscall_state = Some(EntryExit::Entry(entry));
+				println!("entry: {:?}", regs);
+			}
+			(Some(EntryExit::Entry(entry)), WaitStatus::PtraceSyscall(event_tid)) => {
+				let event_tid = Tid(event_tid.as_raw());
+				assert_eq!(tid, event_tid);
+
+				let regs = ptrace::getregs(event_tid.as_pid())?;
+				let ret = regs.rax as i64;
+				syscall_state = Some(EntryExit::Exit(entry, ret));
+
+				println!("syscall returned {:?} with {:?}", ret, regs);
+			}
+			(_, WaitStatus::Exited(event_tid, _)) => {
+				let event_tid = Tid(event_tid.as_raw());
+				assert_eq!(tid, event_tid);
+
+				// TODO(edef): this only works for main thread
+				break;
+			}
+			_ => panic!(
+				"unknown status {:?} with syscall_state = {:?}",
+				status, syscall_state
+			),
+		}
+	}
+
+	Ok(())
+}