A minimal Linux syscall tracer for x86_64 using ptrace(2).
make
pstrace [OPTION]... COMMAND [ARG]...
pstrace [OPTION]... -p PID
| Flag | Description |
|---|---|
-f |
Follow forks — trace child processes |
-p PID |
Attach to an existing process |
-o FILE |
Write trace output to FILE (default: stderr) |
-h |
Show help |
pstrace /bin/ls /tmp
pstrace -f make
pstrace -p $(pgrep nginx)
pstrace -o trace.out curl example.com
pstrace.c Main tracing engine: fork/attach, ptrace loop,
register decoding, signal/exit handling,
argument and return-value formatting.
syscalls.h Core types: arg_type enum, syscall_info struct.
syscalls.c Syscall name table (~170 entries, binary search),
errno-to-name mapping.
flags.h flag_def struct, table declarations,
format_flags() signature.
flags.c Symbolic constant tables (open flags, mmap prot/flags,
signals, socket domains/types, clone flags, access modes),
flag-to-string formatting engine.
Each module does one thing. pstrace.c orchestrates; data lives where
it belongs — syscall names in syscalls.c, flag constants in flags.c.
- No dependencies. libc only. No libunwind, libcap, libelf.
- One file, one concern. Syscall names aren't flag tables aren't the tracing loop.
- Data drives code. Adding a syscall is one line in a table.
Adding a flag type is one table + one case in
format_arg(). - Correct over clever. The ptrace state machine handles fork/clone/execve/signal interleaving properly. If it can't be made simple, it isn't made at all.
- Compiles clean.
-std=c11 -Wall -Wextra -Wpedantic -O2.
- x86_64 only. The register ABI (orig_rax, rdi, rsi, rdx, r10, r8, r9) is baked in.
- No syscall filtering (
-e). Add it when you need it. - No timestamp prefix (
-t). Pipe output throughtsif needed. - String arguments read up to 256 bytes via PTRACE_PEEKDATA.
-prequires ptrace_scope=0 or root on modern kernels (YAMA LSM).- Anti-debugging seccomp filters (e.g., spotify) produce ENOSYS for all syscalls — a process-level issue, not a tracer bug.
- Fork child,
PTRACE_TRACEME, exec target. - Parent waits, sets
PTRACE_O_TRACESYSGOOD | TRACEFORK | TRACECLONE | TRACEEXEC. - Loop: waitpid → PTRACE_GETREGS → decode syscall → PTRACE_SYSCALL → repeat.
- On syscall-enter: print name and decoded arguments (no newline).
- On syscall-exit: print
) = return_value\n. - Signals, fork/clone children, and execve are handled with the appropriate PTRACE_EVENT stops and state transitions.