Skip to content
Go back

strace for Beginners: See What Any Process Is Doing

By SumGuy 5 min read
strace for Beginners: See What Any Process Is Doing

strace is one of those tools that feels like debugging magic the first time you use it. You run it on a process, and suddenly you see exactly what it’s doing at the system call level — every file it tries to open, every permission error, every network connection. No source code reading required.

Here’s the thing: when an app fails with “permission denied” or “file not found,” the error message tells you what failed, but not why. strace shows you the whole chain. It’s invaluable when you’re staring at code you didn’t write or dealing with closed-source binaries.

What’s a System Call?

A system call is when a userspace application asks the kernel to do something it can’t do itself — open a file, make a network connection, allocate memory, spawn a process. strace intercepts these calls and logs them.

Terminal window
$ strace -e openat ls /tmp 2>&1 | head -n 20
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp", O_RDONLY|O_CLOEXEC|O_DIRECTORY) = 3

Each line is one system call. The number at the end (like = 3) is the file descriptor returned by the kernel, or an error code if it failed.

Basic Usage: Trace Everything

Terminal window
$ strace ./myapp
execve("./myapp", ["./myapp"], [/* 58 vars */]) = 0
brk(NULL) = 0x55555555a000
arch_prctl(ARCH_SET_FS, 0x55555555a880) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff7000
...

That’s noisy. Thousands of lines. Most of it is runtime initialization that you don’t care about. Usually you want to filter.

Pattern 1: Trace Only File Operations

Find what files an app is trying to open:

Terminal window
$ strace -e openat,open,read,write ./myapp 2>&1 | grep -E "(openat|open)" | head -n 20
openat(AT_FDCWD, "/etc/passwd", O_RDONLY) = 3
openat(AT_FDCWD, "/var/log/app.log", O_WRONLY|O_CREAT|O_APPEND, 0644) = 4
openat(AT_FDCWD, "/config.yaml", O_RDONLY) = -1 ENOENT (No such file or directory)

That last line? Permission error. The app tried to open /config.yaml and got “No such file or directory.” Now you know where to look.

Pattern 2: Trace System Calls for a Running Process

Your app is already running and doing weird things. Don’t restart it — attach to it:

Terminal window
$ sudo strace -p 1234 -e trace=open,openat,read,write

Add -f to follow child processes too:

Terminal window
$ sudo strace -p 1234 -f -e trace=file

Pattern 3: Trace Network Connections

See what network calls an app makes:

Terminal window
$ strace -e trace=network ./myapp
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 128) = 0

Your app creates a TCP socket, sets options, binds to port 8080, and starts listening. Useful for debugging network issues.

Pattern 4: Find Permission Errors

App won’t start, complains about permissions, but the error is vague. Trace it:

Terminal window
$ strace -e openat ./app 2>&1 | grep EACCES
openat(AT_FDCWD, "/etc/app.conf", O_RDONLY) = -1 EACCES (Permission denied)

Now you know: it’s trying to read /etc/app.conf but doesn’t have permission. Fix the file permissions, done.

Pattern 5: Trace a Specific Syscall with Arguments

See the actual arguments to a function:

Terminal window
$ strace -e write ./app 2>&1 | head -n 10
write(1, "Hello, World!\n", 14) = 14
write(2, "Error: something failed\n", 24) = 24

The middle part ("Hello, World!\n") is what got written. The 1 is stdout, 2 is stderr. Useful for understanding what data is actually flowing through the system.

Pattern 6: Get Statistics, Not Details

Running a web server through strace produces millions of lines. Get a summary instead:

Terminal window
$ strace -c ./myapp
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
20.45 0.001234 23 54 mmap
15.23 0.000921 18 52 mprotect
12.45 0.000751 15 49 open
8.90 0.000537 11 47 read
...

Shows which syscalls are slowest or called most often. Great for spotting performance problems.

Real-World Scenarios

Debugging a startup failure:

Terminal window
$ strace -f ./service 2>&1 | grep -E "ENOENT|EACCES" | head -n 5

Understanding database connection issues:

Terminal window
$ strace -e trace=network node app.js 2>&1 | grep -E "connect|bind"

Finding which config file an app actually reads:

Terminal window
$ strace -e openat ./app 2>&1 | grep "\.conf\|\.yaml\|\.json" | grep -v ENOENT

Things to Know

1. strace adds overhead. It slows down your app significantly. For quick debugging, that’s fine. For performance profiling, use perf instead.

2. You often need sudo. If you’re tracing a process you don’t own or tracing certain syscalls, root is required.

3. The output is huge. Filter with -e to see only what you care about. Piping to grep is your friend.

4. Errors aren’t always fatal. A syscall returning ENOENT (file not found) doesn’t mean the process crashed. Apps often try multiple paths.

When This Saves You

You’re integrating with a closed-source tool and it’s not working. You can’t read the source. strace is your X-ray. Run it, look for errors, and follow the evidence. It beats hours of blind debugging.

That’s the power of strace — it turns black boxes transparent.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
Running Docker Containers as Non-Root (And Why You Should)
Next Post
Docker Container Labels: The Metadata You're Ignoring

Related Posts