Skip to content
daniel@cosenza:~/blog
LinuxHow-To March 18, 2026 3 min read

How to Debug a Program on Linux with strace

A complete walkthrough using strace to see exactly which system calls a misbehaving program is making — often the fastest way to diagnose a problem with no useful log output at all.

When a program fails with no useful error message, strace shows you exactly what it’s actually doing at the system-call level — every file it tries to open, every socket it touches, every signal it receives — often revealing the real cause in minutes when application logs offer nothing.

Step 1: install strace, if it isn’t already present

sudo apt install strace      # Debian/Ubuntu
sudo dnf install strace      # Fedora/RHEL

Step 2: run a command directly under strace

strace ./myprogram

This prints every system call the program makes, in real time, as it runs — a firehose for anything non-trivial, but immediately useful for simple cases.

Step 3: attach to an already-running process

strace -p <pid>

Useful for a program that’s already misbehaving and you don’t want to (or can’t) restart — attaching live shows you what it’s doing from this point forward.

Step 4: filter to a specific category of system calls

strace -e trace=open,openat,read,write ./myprogram
strace -e trace=network ./myprogram

Filtering to just file operations, or just network calls, cuts through the noise when you already have a hypothesis about which category of syscall is relevant.

Step 5: find exactly which file a program can’t find or open

strace -e trace=openat ./myprogram 2>&1 | grep ENOENT

A program failing with a vague “file not found” error, but not telling you which file, is one of strace’s most common and satisfying uses — the syscall trace shows the exact path it tried and failed to open.

Step 6: measure time spent in each system call

strace -T ./myprogram

The -T flag appends the time each system call took — useful for spotting a specific syscall (often a network call or a slow disk read) responsible for an otherwise-unexplained pause.

Step 7: get a summary of syscall frequency and total time

strace -c ./myprogram

Rather than a full line-by-line trace, -c produces a summary table — call counts and total time per syscall — useful for spotting a syscall being called suspiciously often (a classic sign of a retry loop or inefficient polling) without reading a huge trace manually.

Step 8: follow child processes too, if the program forks

strace -f ./myprogram

Without -f, strace only follows the initial process — for anything that forks or execs child processes, -f is necessary to see what those children are actually doing as well.

Step 9: save a large trace to a file rather than reading it live

strace -o trace.log -f ./myprogram

Why strace works even when a program has no logging of its own

strace operates entirely at the kernel/syscall boundary, using ptrace(2) to intercept every system call a traced process makes — this means it works identically regardless of whether the program itself has any logging, error handling, or debug output implemented at all. For a genuinely opaque binary, or a program whose own error messages are misleading or absent, strace is often the only way to see what it’s actually trying to do versus what it’s succeeding at.