strace, perf and Profiling
1. strace — Syscall Interception
strace uses the ptrace(2) syscall to intercept every system call made by a process. The kernel pauses the tracee at each syscall entry/exit, allowing strace to inspect arguments and return values.
# Trace a new process
strace ls /tmp
# Attach to running process by PID
strace -p 1234
# Filter: only open/read/write syscalls
strace -e trace=openat,read,write ls
# Count syscalls (summary table)
strace -c ls /tmp
# Show time spent per syscall
strace -T ls /tmp
# Trace child processes (fork/exec)
strace -f nginx -g daemon off
# Trace with timestamps
strace -t ls # wall clock time
strace -tt ls # microseconds
strace -ttt ls # epoch microseconds
# Write output to file (essential for -f)
strace -f -o trace.txt nginx
# Network syscalls only
strace -e trace=network curl http://example.com
# File syscalls only
strace -e trace=file myapp
Real examples:
# Why is my process hung?
strace -p $(pgrep myapp) -e trace=all
# What files does it open at startup?
strace -e openat ./myapp 2>&1 | grep -v ENOENT
# Find slow syscalls (>1ms)
strace -T ./myapp 2>&1 | awk -F'<' '$2+0 > 0.001'
# Count syscalls during a test
strace -c -p 1234 # Ctrl-C to stop and print summary
sequenceDiagram
participant T as tracee process
participant K as Kernel
participant S as strace
T->>K: syscall entry (e.g. read)
K-->>S: ptrace stop: SYSCALL_ENTER
S->>K: ptrace(PEEKUSER) reads args
S->>K: ptrace(SYSCALL) resume
K->>T: syscall executes
T->>K: syscall returns
K-->>S: ptrace stop: SYSCALL_EXIT
S->>K: ptrace(PEEKUSER) reads retval
S->>K: ptrace(SYSCALL) resume
T->>T: continues running
Production warning: strace adds ~10-100x overhead. On live systems, use
-cfor aggregated stats or attach briefly. Useperf tracefor lower overhead.
2. perf stat — Hardware Counters
perf stat measures hardware performance counters (PMU) via kernel perf_event subsystem.
# Basic stats for a command
perf stat ls /tmp
# Attach to running PID
perf stat -p 1234
# Run for 5 seconds then report
perf stat -p 1234 sleep 5
# Specific events
perf stat -e cycles,instructions,cache-misses,branch-misses ls
# Repeat 3 times for variance
perf stat -r 3 ./mybenchmark
Example output:
Performance counter stats for 'ls /tmp':
2,345,678 cycles # 1.23 GHz
1,890,123 instructions # 0.81 insn per cycle (IPC)
12,456 cache-misses # 2.3% of all cache refs
8,901 branch-misses # 0.4% of all branches
0.001234 seconds time elapsed
Key metrics:
| Metric | Good | Investigate if |
|---|---|---|
| IPC (insn/cycle) | > 1.5 | < 0.5 (memory-bound?) |
| Cache miss rate | < 1% | > 10% (working set too large) |
| Branch miss rate | < 1% | > 5% (unpredictable branches) |
| Cycles/instruction | < 1 | > 3 (pipeline stalls) |
# Memory bandwidth events
perf stat -e LLC-loads,LLC-load-misses,LLC-stores ./myapp
# TLB events
perf stat -e dTLB-loads,dTLB-load-misses ./myapp
3. perf top — Live Hot Functions
# Live view of CPU-consuming functions system-wide
perf top
# For specific PID
perf top -p 1234
# Show kernel symbols too
perf top -a
# Specific event (cache misses)
perf top -e cache-misses
# Update every 2 seconds
perf top -d 2
Navigation: Arrow keys to select, Enter to annotate (assembly view), q to quit.
4. perf record + report — Flame Graph Pipeline
# Sample at 99Hz for 30s (freq avoids lockstep with 100Hz timer)
perf record -F 99 -g ./myapp
# Sample running process
perf record -F 99 -g -p 1234 sleep 30
# System-wide sampling
perf record -F 99 -ag sleep 30
# View interactive report (TUI)
perf report
# Dump raw text for flame graph
perf report --stdio --no-header -n -q > perf_report.txt
Full flame graph pipeline (Brendan Gregg's method):
# 1. Record with stack traces
perf record -F 99 -ag -o perf.data sleep 30
# 2. Export to folded format
git clone https://github.com/brendangregg/FlameGraph
perf script -i perf.data | \
./FlameGraph/stackcollapse-perf.pl > out.folded
# 3. Generate SVG
./FlameGraph/flamegraph.pl out.folded > flamegraph.svg
# 4. Open in browser
open flamegraph.svg
flowchart LR
A["perf record<br/>-F 99 -ag"] --> B["perf.data<br/>sample file"]
B --> C["perf script<br/>decode stacks"]
C --> D["stackcollapse-perf.pl<br/>fold stacks"]
D --> E["flamegraph.pl<br/>render SVG"]
E --> F["flamegraph.svg<br/>open in browser"]
5. Flame Graphs — How to Read
graph TD
TOP["top frame = on-CPU function"]
MID1["caller of top frame"]
MID2["another call path"]
BOT["main / thread entry"]
TOP --> MID1
TOP --> MID2
MID1 --> BOT
MID2 --> BOT
WIDE["wide frame = high CPU time"]
NARR["narrow frame = low CPU time"]
Axes:
- X-axis: Aggregate CPU time (width = % of samples). Left-to-right order is alphabetical, NOT time sequence.
- Y-axis: Stack depth. Bottom = thread entry point, top = on-CPU function.
What to look for:
| Pattern | Meaning |
|---|---|
| Wide frame at top | Hot function — optimize this |
| Wide tower | Deep call chain all consuming CPU |
| Flat wide plateau | Single function dominating |
| Thin frames | Many different functions, no clear hotspot |
Brendan Gregg methodology:
- Look for the widest frames near the top — these are the actual CPU consumers
- Trace down the stack to find which code path called into the hot function
- Off-CPU flame graphs (perf + sleep analysis) show blocking time — different tool
- Differential flame graphs compare before/after a change
6. perf trace — Lightweight Syscall Tracer
perf trace uses eBPF/tracepoints instead of ptrace — much lower overhead than strace.
# Trace syscalls for a command
perf trace ls /tmp
# Attach to PID
perf trace -p 1234
# Summary (like strace -c)
perf trace -s -p 1234 sleep 5
# Only specific syscalls
perf trace -e openat,read,write ls
# Filter by pid and syscall
perf trace --pid 1234 -e sendto
# Show timing
perf trace -T ls
strace vs perf trace:
| strace | perf trace | |
|---|---|---|
| Mechanism | ptrace (per-process) | tracepoints/eBPF (kernel) |
| Overhead | ~10-100x | ~2-5x |
| Attach to running | Yes | Yes |
| Child tracing (-f) | Yes | Yes |
| Production safe | No | Cautious yes |
| Output format | Detailed | Concise |