How it works
The explanation, with examples you can run.
Watch cat read a file
echo hello > hi.txtstrace -e trace=openat,read,write,close cat hi.txt 2>&1 >/dev/null | tail -8openat(AT_FDCWD, "hi.txt", O_RDONLY) = 3read(3, "hello\n", 131072) = 6write(1, "hello\n", 6) = 6read(3, "", 131072) = 0close(3) = 0close(1) = 0close(2) = 0+++ exited with 0 +++(strace writes to stderr, so 2>&1 >/dev/null sends the trace through the pipe and drops cat's own output.) The earlier lines, cut off by tail, are the dynamic loader opening the C library. Then cat opens the file and gets descriptor 3, reads up to 128 KiB and gets 6 bytes, writes them to descriptor 1, reads again and gets 0 (end of file), and closes. That is the whole program, as the kernel sees it.
Find the file a program is looking for
strace -f -e trace=openat python3 -c 'open("/etc/app/config.yaml")' 2>&1 | grep config.yamlopenat(AT_FDCWD, "/etc/app/config.yaml", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)FileNotFoundError: [Errno 2] No such file or directory: '/etc/app/config.yaml'A failed call returns -1 and sets errno; strace prints its name. The ones you meet most: ENOENT (no such file), EACCES (permission denied), EMFILE (too many open files), ECONNREFUSED (nothing listening), EAGAIN (would block, try later) and EINTR (interrupted by a signal). Real applications often swallow these into a vague message; the trace shows the actual call and path.
Count instead of print
strace -c -f ls /usr/bin > /dev/null 2> summary.txt; head -8 summary.txt% time seconds usecs/call calls errors syscall------ ----------- ----------- --------- --------- ---------------- 33.69 0.000220 110 2 getdents64 15.47 0.000101 6 15 mmap 8.58 0.000056 7 8 mprotect 7.81 0.000051 8 6 openat 6.43 0.000042 5 8 close 6.13 0.000040 5 7 munmapgetdents64 reads directory entries; most of the rest is the loader mapping libraries. Other useful flags: -p <pid> attaches to a running process (a hung one is often sitting in futex, epoll_wait, read or connect), -T shows the time spent in each call, -tt adds timestamps, and -e trace=%network or %file selects groups of calls.
Cost and alternatives
strace uses ptrace, which stops the traced process at every system call so the tracer can inspect it. That can slow a busy program down many times over, so use it briefly. For production, eBPF-based tools (bpftrace, perf trace, the BCC tools such as opensnoop and execsnoop) trace the same events inside the kernel with far lower overhead. In a container, tracing needs CAP_SYS_PTRACE (docker run --cap-add SYS_PTRACE).
On macOS
macOS has a different kernel (XNU) with its own system calls. dtruss and dtrace can trace them, but only with System Integrity Protection partly disabled, so the practical way to learn Linux system calls on a Mac is strace inside a Linux container.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.