这个命令是干啥的
strace 是 Linux 上的系统调用追踪器。它能拦截并记录进程发起的每一个系统调用(syscall),包括调用的参数、返回值、耗时。简单说,strace 能让你看清:你的程序到底在干什么、卡在哪里、为什么慢。
我第一次用 strace 是在定位一个 Tomcat 启动慢的问题。启动日志卡在某个地方不动,没有报错、没有异常、什么都没有。用 strace -p PID 一看,发现进程一直在 open() 一个根本不存在的配置文件,超时后才继续。找到问题后修复,启动时间从 3 分钟降到 10 秒。从那天起,strace 就成了我的"程序卡住三件套"之一(另外两个是 jstack 和 tcpdump)。
strace 能帮你回答这些问题:
- 我的程序在卡在哪个 syscall 上了?
- 哪个文件加载失败了?
- 为什么程序这么慢?哪一步耗时最多?
- 这个配置到底被读了没有?
基本用法(3分钟上手)
追踪正在运行的进程
# 追踪 PID 为 12345 的进程
strace -p 12345
输出会哗哗地往下滚,每一行都是一个系统调用。你会看到类似 read()、write()、open()、close() 这些。
# 只看一段时间,然后 Ctrl+C 退出
strace -p 12345
默认 strace 会把所有 syscall 打印到终端,信息量巨大。别慌,后面有过滤方法。
追踪新启动的命令
# 启动命令并同时追踪它的系统调用
strace ls -l
# 追踪 curl 发请求过程
strace curl https://www.baidu.com
直接在命令前面加 strace 就行,它会 fork 一个子进程来执行。
统计系统调用
# 统计每个 syscall 的调用次数和耗时,-c 是统计模式
strace -c -p 12345
等个几秒钟 Ctrl+C,strace 会打印一张统计表:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
45.23 0.123456 12.34 10000 read
30.12 0.089012 8.90 10000 write
10.05 0.030000 30.00 1000 open
...
一眼就能看出哪个 syscall 最耗时间。我经常用 -c 做性能初筛。
进阶骚操作
按系统调用类型过滤
# 只看文件打开和读文件的操作
strace -e trace=open,openat,read -p 12345
# 只看网络相关的 syscall
strace -e trace=network -p 12345
# 只看进程管理的 syscall(fork, exec, clone)
strace -e trace=process -p 12345
# 只看信号相关的 syscall
strace -e trace=signal -p 12345
过滤是 strace 的精髓,不然输出量太大根本看不完。
显示时间戳
# 显示每个 syscall 的绝对时间(精确到秒)
strace -t -p 12345
# 显示相对时间(精确到微秒,从第一个 syscall 开始计时)
strace -r -p 12345
# 显示每个 syscall 的耗时(syscall 执行了多久)
strace -T -p 12345
我最爱 -t 结合 -T,一个看什么时候调用的,一个看花了多久:
# 又看时间又看耗时
strace -t -T -p 12345
输出重定向到文件
# 把 strace 输出保存到文件,方便慢慢看
strace -o /tmp/strace.log -p 12345
# 用 -ff 分离子进程的输出,每个进程一个文件
strace -ff -o /tmp/strace_child -p 12345
不加 -o 的话,输出直接刷屏,手滑 Ctrl+C 重来又得抓一次。
限制追踪的 syscall 数量和字符串长度
# 只追踪前 100 个 syscall
strace -c 100 -p 12345
# 限制打印的字符串长度,默认 32 字节
strace -s 256 -p 12345
-s 控制打印参数里字符串的长度。如果你在追踪 HTTP 请求的内容,默认 32 字节太短,加 -s 8192 基本够看。
避坑指南
1. strace 会让进程变慢
这是最重要的一点。strace 在每一个 syscall 前后都要做 interception,性能开销很大。根据经验,加了 strace 的程序会慢 10-100 倍。
# 追踪一个频繁 IO 的程序,速度会肉眼可见地变慢
strace -p 12345
我的建议:
- 不要在压测的时候跑 strace
- 用 -c 统计模式比逐条打印轻量
- 用 -e trace= 精确过滤,减少 overhead
- 只追踪一小段时间,抓到关键信息就停
2. strace 看不到 sleep 和 idle
如果你看到输出停在了 nanosleep() 或者 select() 上,那不是程序卡了,是它在正常等待:
# 程序在 sleep,不是在卡
nanosleep({tv_sec=5, tv_nsec=0}, ...
需要判断是"正常等待"(poll, select, epoll_wait)还是"异常卡住"(反复失败的 connect、慢速的 open、无响应的 read)。
3. 权限不足
# 非 root 用户 strace 其他用户的进程会报错
strace -p 1
# 报错: strace: attach: ptrace(PTRACE_SEIZE, 1): Operation not permitted
# 需要用 root 或者 sudo
sudo strace -p 1
大部分生产环境需要 sudo strace。
4. 大量子进程的处理
对于 Python、Java、Node.js 这种多线程/多进程程序,用 -f 追踪子进程:
# 追踪主进程及其所有子线程/子进程
strace -f -p 12345
但注意 Java 的 JVM 有大堆线程,-f 会导致输出暴涨。建议用 -f -c 先看统计,再用 -f -e trace=xxx 具体过滤。
5. strace 输出乱码
# 如果看到 \x00\x01 之类的,加 -s 控制字符串显示长度
strace -s 4096 -v -p 12345
-v 是 verbose 模式,显示更详细的参数信息。
实战场景
场景一:程序启动卡住,找它卡在哪里
一个新服务启动时卡住没反应,日志也不写,用 strace 看:
# 找进程 PID
ps aux | grep myapp
# 追踪进程
sudo strace -t -T -p PID
输出里会显示它卡在哪个 syscall。常见的卡住原因:
- connect() 很久没返回 = 连接远程服务超时
- open() 返回 ENOENT 且重试 = 配置文件路径不对
- read() 一直阻塞 = 读管道或 socket 没数据
- stat() 反复调用同一个不存在的路径 = 在等待文件出现
场景二:找配置文件被哪个路径读了
很多应用会从多个位置尝试加载配置,失败后又去下一个地方找,导致启动慢:
# 追踪读文件操作,看配置到底从哪读的
sudo strace -e trace=open,openat,stat -s 256 -p PID 2>&1 | grep "\.conf"
输出会显示程序尝试 open("/etc/myapp/config.conf") 等操作,如果返回 ENOENT 说明没找到,返回 FD 数字说明成功打开了。
场景三:断网时观察网络行为
# 应用连不上数据库,看它怎么处理的
sudo strace -e trace=network -t -T -p PID
你会看到:
connect(3, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("10.0.0.5")}, 16) = -1 ETIMEDOUT (Connection timed out)
这说明连接超时了。或者:
connect(3, ...) = -1 EINPROGRESS
poll([{fd=3, events=POLLOUT}], 1, 30000) = 1
...
EINPROGRESS 说明是非阻塞连接,poll 在等连接完成。如果 poll 返回 0 就是超时了。
场景四:诊断性能瓶颈
程序突然变慢,用 strace 统计看哪个 syscall 最耗时间:
# 统计 10 秒内的系统调用分布
sudo strace -c -p PID &
sleep 10
kill %1
或者用时间戳看各阶段耗时:
# 追踪文件 IO 并显示耗时
sudo strace -e trace=read,write,open,close -t -T -p PID
看到 stat() 或 open() 耗时明显,可能是磁盘 IO 瓶颈。看到 read() 经常返回很少字节,可能是小文件太多。
场景五:排查文件描述符泄漏
# 看进程打开文件和关闭文件的比例
sudo strace -e trace=open,openat,close -c -p PID
如果 open 的调用次数远大于 close,说明文件描述符在泄漏,需要检查程序逻辑。
今日作业
你的 Nginx 突然变得很慢,每个请求响应时间从 50ms 飙升到 5 秒。你用 strace 排查性能问题,要求:
1. 找到 Nginx 的 worker 进程 PID
2. 用 strace 统计 30 秒内的系统调用分布
3. 找出最耗时的 3 个系统调用
4. 如果看到 open() 频繁失败(ENOENT),说明什么?
5. 如果看到 write() 经常阻塞很久,说明什么?
写出完整的 strace 命令,并根据输出结果给出修复建议。