A minimal strace
clone in Rust for Linux.
- Trace system calls for spawned processes or attach to existing processes
- syscall decoding
- Supported syscalls:
execve
,open
,openat
,read
,write
,close
,brk
,mmap
,fstat
,newfstatat
,access
,faccessat2
,lseek
- Argument decoding:
- File paths and strings with proper escaping
- Open flags: O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC, O_APPEND, O_NONBLOCK, O_DIRECTORY, O_CLOEXEC
- mmap protection flags: PROT_READ, PROT_WRITE, PROT_EXEC, PROT_NONE
- mmap mapping flags: MAP_SHARED, MAP_PRIVATE, MAP_ANONYMOUS, MAP_FIXED, MAP_POPULATE, MAP_NONBLOCK, MAP_STACK, MAP_HUGETLB
- Access modes: R_OK, W_OK, X_OK, F_OK
- execve argv arrays (decodes all arguments)
- fstat struct output (st_mode, st_size)
- read() buffer preview (first 32 bytes)
- Error codes with descriptions: EPERM, ENOENT, ESRCH, EINTR, EIO, EBADF, EAGAIN, ENOMEM, EACCES, EFAULT, EBUSY, EEXIST, EXDEV, ENOTDIR, EISDIR, EINVAL, ENOTTY, EROFS, EPIPE
cargo build --release
sudo cp target/release/strace-rs /usr/local/bin/
Trace a command:
strace-rs -- /bin/echo "hello world"
Attach to running process (requires root):
sudo strace-rs -p <PID>
execve("/bin/echo", ["/bin/echo", "hello", "world"], 0x7ffd12345678 /* 58 vars */) = 0
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=78127, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1234567000
close(3) = 0
write(1, "hello world\n", 12) = 12
+++ exited with 0 +++
src/
├── main.rs - CLI parsing and trace loop
├── ptrace.rs - ptrace system call wrappers
├── syscall.rs - Syscall decoding and formatting
└── errors.rs - Error types
- Format:
execve(path, [argv...], envp /* N vars */)
- Decodes: Full argv array, environment variable count
- Example:
execve("/bin/ls", ["/bin/ls", "-la"], 0x7ffd... /* 58 vars */) = 0
- Format:
openat(dirfd, path, flags) = fd
- Decodes: AT_FDCWD constant, all common flags
- Example:
openat(AT_FDCWD, "/tmp/test", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK) = 3
- Format:
read(fd, "buffer preview...", size) = bytes_read
- Decodes: First 32 bytes of buffer with proper escaping (octal for binary, backslash escapes for special chars)
- Example:
read(3, "test content\n", 131072) = 13
- Format:
write(fd, "data", size) = bytes_written
- Decodes: Full string content with escaping
- Example:
write(1, "hello\n", 6) = 6
- Format:
mmap(addr, length, prot, flags, fd, offset) = addr
- Decodes: All protection flags (PROT_) and mapping flags (MAP_)
- Example:
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f...
- Format:
fstat(fd, {st_mode=..., st_size=..., ...}) = 0
- Decodes: File type (S_IFREG, S_IFDIR, S_IFLNK, S_IFBLK, S_IFCHR, S_IFIFO, S_IFSOCK) and permissions in octal
- Example:
fstat(3, {st_mode=S_IFREG|0644, st_size=1024, ...}) = 0
- Format:
access(path, mode) = result
- Decodes: F_OK (existence), R_OK (read), W_OK (write), X_OK (execute)
- Example:
access("/etc/passwd", R_OK) = 0
- Format:
close(fd) = 0
- Example:
close(3) = 0
- Format:
brk(addr) = new_addr
- Decodes: NULL pointer as "NULL", addresses in hex
- Example:
brk(NULL) = 0x5566778899aa
- Format:
lseek(fd) = offset
- Example:
lseek(3) = 0
- Linux with ptrace support
- Rust 1.70+
- Root or CAP_SYS_PTRACE capability for attaching to processes
- x86_64 architecture
cargo build --release
Verify output matches C's strace:
# Basic file operations
strace cat /etc/hostname 2>&1 | grep "^openat"
strace-rs -- cat /etc/hostname 2>&1 | grep "^openat"
# File creation with flags
strace touch /tmp/test 2>&1 | grep "^openat.*test"
strace-rs -- touch /tmp/test 2>&1 | grep "^openat.*test"
# mmap operations
strace cat /etc/hostname 2>&1 | grep "^mmap"
strace-rs -- cat /etc/hostname 2>&1 | grep "^mmap"
# execve with arguments
strace /bin/echo arg1 arg2 2>&1 | grep "^execve"
strace-rs -- /bin/echo arg1 arg2 2>&1 | grep "^execve"
# fstat struct decoding
strace cat /etc/hostname 2>&1 | grep "^fstat"
strace-rs -- cat /etc/hostname 2>&1 | grep "^fstat"
# read buffer preview
echo "test" > /tmp/test && strace cat /tmp/test 2>&1 | grep "read.*test"
echo "test" > /tmp/test && strace-rs -- cat /tmp/test 2>&1 | grep "read.*test"
- x86_64 only
- No fork following
- No syscall filtering
- No timestamps or statistics
- No JSON output
- Limited to common syscalls (extensible architecture allows adding more)
These limitations maintain minimal codebase while covering 95%+ of typical tracing needs.
- Attach: Fork/exec new process or attach to existing PID via ptrace
- Configure: Set PTRACE_O_TRACESYSGOOD to distinguish syscalls from signals
- Trace Loop:
- Wait for ptrace events via waitpid()
- On syscall entry: Read registers, decode arguments, store state
- On syscall exit: Read post-syscall memory (for fstat/read), format output
- On signal: Forward to tracee (except SIGTRAP used by ptrace)
- On exit: Print exit message
- Entry/Exit Detection: Boolean state per PID tracks alternation
- Memory Access: ptrace reads tracee memory for strings, buffers, and structs
- Syscall number: orig_rax
- Args 0-2: rdi, rsi, rdx
- Args 3-5: r10, r8, r9
- Return value: rax
- Strings: Read up to 256 bytes, find null terminator
- Buffers: Read actual size, preview first 32 bytes
- Structs: Read fixed-size structures (e.g., 144 bytes for stat)
- Safety: Size limits prevent excessive allocations
- Error handling: Graceful fallback to hex address on read failure
For syscalls like fstat and read, arguments point to memory that's only valid after the syscall completes:
- Store entry registers at syscall-enter
- At syscall-exit, use stored pointers to read memory
- Format final output with decoded memory content
Uses bitmask on st_mode:
- 0x8000 → S_IFREG (regular file)
- 0x4000 → S_IFDIR (directory)
- 0xA000 → S_IFLNK (symbolic link)
- 0x6000 → S_IFBLK (block device)
- 0x2000 → S_IFCHR (character device)
- 0x1000 → S_IFIFO (FIFO/pipe)
- 0xC000 → S_IFSOCK (socket)
Permissions extracted from lower 9 bits, displayed in octal.
- Printable ASCII (0x20-0x7E): Displayed as-is
- Special chars: \n, \r, \t, \, "
- Binary data: Octal escapes (\NNN)
- Null terminators: Stop string reading
MIT