summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ripple/Cargo.lock42
-rw-r--r--ripple/Cargo.toml2
-rw-r--r--ripple/minitrace/.gitignore4
-rw-r--r--ripple/minitrace/Cargo.toml12
-rw-r--r--ripple/minitrace/src/main.rs147
5 files changed, 206 insertions, 1 deletions
diff --git a/ripple/Cargo.lock b/ripple/Cargo.lock
index fdab2f2..994b4bc 100644
--- a/ripple/Cargo.lock
+++ b/ripple/Cargo.lock
@@ -312,12 +312,45 @@ dependencies = [
 ]
 
 [[package]]
+name = "minitrace"
+version = "0.0.0"
+dependencies = [
+ "anyhow",
+ "nix 0.19.1",
+ "spawn-ptrace",
+]
+
+[[package]]
 name = "multimap"
 version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
 
 [[package]]
+name = "nix"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if 0.1.10",
+ "libc",
+]
+
+[[package]]
+name = "nix"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if 1.0.0",
+ "libc",
+]
+
+[[package]]
 name = "num_cpus"
 version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -553,6 +586,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
 
 [[package]]
+name = "spawn-ptrace"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b572503c81aa40e1e61954bca89b863c344e0f310fd51f308d933255bf4f756"
+dependencies = [
+ "nix 0.18.0",
+]
+
+[[package]]
 name = "subtle"
 version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/ripple/Cargo.toml b/ripple/Cargo.toml
index 5255ca8..ffc8527 100644
--- a/ripple/Cargo.toml
+++ b/ripple/Cargo.toml
@@ -9,4 +9,4 @@ edition = "2018"
 [dependencies]
 
 [workspace]
-members = ["fossil"]
+members = ["fossil", "minitrace"]
diff --git a/ripple/minitrace/.gitignore b/ripple/minitrace/.gitignore
new file mode 100644
index 0000000..be75022
--- /dev/null
+++ b/ripple/minitrace/.gitignore
@@ -0,0 +1,4 @@
+# SPDX-FileCopyrightText: edef <edef@unfathomable.blue>
+# SPDX-License-Identifier: OSL-3.0
+
+/target
diff --git a/ripple/minitrace/Cargo.toml b/ripple/minitrace/Cargo.toml
new file mode 100644
index 0000000..4a64930
--- /dev/null
+++ b/ripple/minitrace/Cargo.toml
@@ -0,0 +1,12 @@
+# SPDX-FileCopyrightText: edef <edef@unfathomable.blue>
+# SPDX-License-Identifier: OSL-3.0
+
+[package]
+name = "minitrace"
+version = "0.0.0"
+edition = "2018"
+
+[dependencies]
+nix = "0.19.1"
+spawn-ptrace = "0.1.2"
+anyhow = "1.0.43"
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(())
+}