Skip to content
shellmap

stracestrace syscall tracer — bash/Linux ptrace, macOS dtruss, Windows procmon, pwsh Trace-Command + Get-WinEvent ETW equivalents across all 5 shells

Equivalents in every shell

Bashunix
strace -f -e trace=openat,connect ./myprogram

Linux's syscall tracer using `ptrace(2)`. `-f` follow forks/threads (CRITICAL — without it you miss child processes); `-e trace=<list>` filter to specific syscalls (`openat`, `read`, `connect`, `execve` etc.); `-e trace=%file` macro for all file-related syscalls; `-e trace=%network` for network; `-p <PID>` attach to running process. `-o file.log` write to file (not stderr — useful when the traced program also writes stderr). `-s 1024` increase string truncation length (default 32 — strings get `"...truncated"...` mid-output without it). Distro install: `apt install strace`, `dnf install strace`, `pacman -S strace`. Linux-only — does NOT work on macOS or Windows native.

Zshunix
strace -f -e trace=openat,connect ./myprogram

Same `strace` binary. On macOS, `strace` does NOT exist — Apple does not ship ptrace-based tracing tools. Use `dtruss` (DTrace wrapper) instead: `sudo dtruss -f ./myprogram` (requires SIP partially disabled on Apple Silicon; `csrutil enable --without dtrace` from Recovery Mode). For non-disabled-SIP macOS: `sample <PID>` gives a sampled stack snapshot — not syscall-level but useful for "what is this process doing right now". `fs_usage` and `sc_usage` are also macOS syscall-adjacent tools.

Fishunix
strace -f -e trace=openat,connect ./myprogram

No fish-specific wrinkle — `strace` is a separate Linux binary. CAVEAT: `strace`'s long output is BUFFERED with `\n` separators that fish handles cleanly, but when watching live (`strace -f ./myprogram`), terminal-resize SIGWINCH can interrupt mid-output. Use `strace -f -o /tmp/strace.log ./myprogram &` and `tail -F /tmp/strace.log` for sane scrollback.

PowerShellwindows
Trace-Command -Name ParameterBinding -Expression { Get-ChildItem } -PSHost

pwsh has NO syscall-level tracer — `Trace-Command` operates at the cmdlet level (parameter binding, pipeline construction), NOT at the kernel-syscall level. For Windows syscall-level tracing: `procmon.exe` (Sysinternals, GUI; ProcessName / Operation / Path filters) is the canonical equivalent. For ETW (Event Tracing for Windows) at the command line: `logman.exe create trace MyTrace -p Microsoft-Windows-Kernel-File -o trace.etl` + `logman start MyTrace` / `logman stop MyTrace`; analyze with `Microsoft.Diagnostics.Tracing.PerfView`. For LINUX pwsh: `& strace -f pwsh ./script.ps1` works — strace traces pwsh's syscalls (revealing assembly loading, file opens, etc.).

cmd.exewindows
procmon.exe /BackingFile C:\trace.pml

cmd has no native syscall tracer. `procmon.exe` (Sysinternals — `winget install Microsoft.Sysinternals.ProcessMonitor`) is the Windows-canonical tool — GUI-driven but supports CLI: `/BackingFile <file.pml>` saves to a file, `/Quiet` suppresses launch dialog, `/Terminate` stops a running session. For older/lighter-weight tracing: `tracerpt.exe` (analyze ETW files), `xperf.exe` (Windows Performance Toolkit). For the simplest "what files is this exe opening": `handle.exe -p <process> -a` (Sysinternals — snapshots open handles).

Worked examples

Trace all syscalls of a command and write to a log

Bash
strace -f -o trace.log ./myprogram
Zsh
strace -f -o trace.log ./myprogram
PowerShell
wsl strace -f -o trace.log ./myprogram
cmd.exe
procmon.exe /BackingFile C:\trace.pml /Quiet

Trace only file-system syscalls (debug "file not found" errors)

Bash
strace -f -e trace=%file ./myprogram 2>&1 | grep ENOENT
Zsh
strace -f -e trace=%file ./myprogram 2>&1 | grep ENOENT
PowerShell
wsl bash -c "strace -f -e trace=%file ./myprogram 2>&1 | grep ENOENT"
cmd.exe
procmon.exe /BackingFile C:\trace.pml

Attach to an already-running process

Bash
sudo strace -f -p $(pidof myprogram)
Zsh
sudo strace -f -p $(pidof myprogram)
PowerShell
wsl sudo strace -f -p $(pidof myprogram)
cmd.exe
handle.exe -p myprogram.exe -a

Trace network syscalls only

Bash
strace -f -e trace=%network -e read=all -s 256 ./myprogram
Zsh
strace -f -e trace=%network -e read=all -s 256 ./myprogram
PowerShell
Get-NetTCPConnection
cmd.exe
netstat -ano

Gotchas

  • **`-f` (follow forks) is almost always what you want — without it you miss children.** A bare `strace ./myprogram` traces ONLY the initial process. If `myprogram` forks a child (very common — `sh -c "cmd"`, `exec`, daemon double-fork), the child's syscalls are NOT traced and the parent's trace usually shows the `fork()` then a `wait()`. Result: "I straced it but I see nothing useful". Always pass `-f` unless you have a specific reason not to. CAVEAT: `-f` produces interleaved output from all processes — each line is prefixed with `[pid 1234]` for disambiguation. For complex multi-process traces, save to file and post-process by PID: `strace -ff -o trace ./myprogram` writes one file per PID (`trace.1234`, `trace.1235`, etc.).
  • **strace slows the traced program by ~50–500x for syscall-heavy workloads.** Each syscall trap requires ptrace round-trips between strace and the kernel. CPU-bound code (no syscalls) sees minimal slowdown; I/O-heavy or fork-heavy code grinds. If your bug is timing-related (race conditions, deadlocks), strace WILL distort it — Heisenbug. Faster alternative for production-style tracing: `perf trace` (kernel's built-in perf tool, lower overhead via tracepoints), `bpftrace` (eBPF-based, near-zero overhead), or `sysdig` (commercial-friendly equivalent). For "is this program syscall-bound at all": `strace -c ./myprogram` (`-c` summary mode) prints a per-syscall time/count table — production-safe-ish, ~2x slowdown.
  • **String truncation: default `-s 32` is too short for HTTP / SQL payloads.** strace truncates string arguments to syscalls at 32 bytes by default — useful for terse output, painful for actually reading what was sent: `write(7, "POST /api/v1/users HTTP/1.1\r\nHo"..., 256) = 256`. Bump with `-s 1024` (or `-s 4096` for full HTTP headers). For "show me the FULL contents read/written on fd 7": `strace -e read=7 -e write=7 -s 4096 ./myprogram`. Without these, debugging HTTP / database / RPC issues via strace is mostly guessing.
  • **Permission gotchas: strace needs ptrace permissions; Yama may block.** On hardened Linux (Ubuntu since 22.04, Debian 11+, Fedora since 35), `/proc/sys/kernel/yama/ptrace_scope` is `1` by default — a process can only be traced by its OWN parent. Attaching to an unrelated running process (`strace -p <PID>` to a process YOU started in another terminal) FAILS with `Operation not permitted` even as the same user. Fix: `sudo strace -p <PID>` (root bypasses yama), or temporarily lower yama: `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope`. Containers running with `--cap-drop=SYS_PTRACE` strip the capability entirely — add it back with `docker run --cap-add=SYS_PTRACE`. For strace on docker exec sessions: `docker run --security-opt seccomp=unconfined --cap-add=SYS_PTRACE`.
  • **`ltrace` is the library-call sibling — different abstraction layer.** `strace` traces SYSCALLS (kernel boundary). `ltrace` traces LIBRARY CALLS (libc / libssl / etc — userspace boundary). For "what arguments was `malloc` called with?": `ltrace -f ./myprogram`. For "what crypto operations happened": `ltrace -e 'libssl.so.*' -f ./myprogram`. ltrace catches a different bug class: misuse of library APIs (wrong argument, missing free), whereas strace catches syscall-level issues (file-not-found, permission-denied, network refused). Often you want BOTH: `strace -f` first to find the failing syscall, then `ltrace -f` to trace the library call that issued it.

WSL & PowerShell Core notes

pwshpwsh-native `Trace-Command` operates at cmdlet/parameter-binding level — NOT syscall-level. For Windows syscall tracing: `procmon.exe` (Sysinternals — GUI + CLI flags) is the analogue. ETW (Event Tracing for Windows) at the command line via `logman.exe` + PerfView for kernel-level events. On Linux/macOS pwsh: shell to `strace` (Linux) or `dtruss` (macOS) — pwsh imposes no abstraction over the underlying ptrace facilities.
WSLWSL Linux `strace` works on Linux processes inside the WSL VM. CANNOT trace Windows-side processes (`strace notepad.exe` won't work — strace is Linux ptrace-based, Windows uses its own debugger API). To trace pwsh.exe / cmd.exe / Win32 programs, use `procmon.exe` or attach a debugger via WinDbg/x64dbg. For the common WSL scenario "my Linux binary fails when run from Windows pwsh.exe through wsl.exe interop": `wsl strace -f /usr/bin/mybinary` from pwsh works fine.

Related glossary

Common tasks using strace

Related commands