strace抓取隐藏错误
# strace 抓取隐藏错误
# 一、什么是 strace
strace 是 Linux 下的系统调用追踪工具,能够拦截并记录进程执行过程中的所有系统调用(syscall)以及收到的信号。
核心原理:
- 使用 ptrace 系统调用附加到目标进程
- 在每个系统调用前后触发断点
- 记录系统调用的参数、返回值和耗时
为什么能发现隐藏错误: 很多错误不会直接抛出异常,但会在系统调用层面留下痕迹:
- 文件操作失败(权限不足、路径不存在)
- 网络连接异常(连接超时、拒绝)
- 内存映射问题(mmap 失败)
- 进程间通信错误(管道破裂、信号丢失)
# 二、核心用法
# 2.1 基础命令
# 追踪进程的所有系统调用
strace <command>
# 追踪已运行的进程
strace -p <pid>
# 输出到文件
strace -o trace.log <command>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 2.2 常用选项
| 选项 | 作用 | 示例 |
|---|---|---|
-e trace=file | 只追踪文件相关调用 | strace -e trace=file ls |
-e trace=network | 只追踪网络调用 | strace -e trace=network curl http://example.com |
-e trace=process | 只追踪进程调用 | strace -e trace=process fork |
-e trace=memory | 只追踪内存调用 | strace -e trace=memory ./app |
-f | 跟踪子进程 | strace -f ./app |
-t | 显示时间戳 | strace -t <command> |
-T | 显示每个调用耗时 | strace -T <command> |
-c | 统计摘要 | strace -c <command> |
-s | 设置字符串长度 | strace -s 1024 <command> |
# 三、抓取隐藏错误的实战场景
# 3.1 文件操作错误
场景: 程序静默失败,没有输出错误信息
# 追踪文件操作
strace -e trace=file -f ./my_program 2>&1 | grep -E "(ENOENT|EACCES|EPERM)"
1
2
2
常见隐藏错误模式:
open("/etc/config.conf", O_RDONLY) = -1 ENOENT (No such file or directory)
access("/var/run/app.pid", W_OK) = -1 EACCES (Permission denied)
stat("/usr/lib/libfoo.so", 0x7ffd...) = -1 EACCES (Permission denied)
1
2
3
2
3
# 3.2 网络连接问题
场景: 程序卡住但没有超时错误
# 追踪网络调用
strace -e trace=network -T ./my_program 2>&1 | grep -E "(ETIMEDOUT|ECONNREFUSED|ECONNRESET)"
1
2
2
常见隐藏错误模式:
connect(5, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 ETIMEDOUT (Connection timed out)
sendto(5, "SELECT...", 1024, 0, NULL, 0) = -1 ECONNRESET (Connection reset by peer)
1
2
2
# 3.3 权限问题
场景: 程序在某些用户下失败,但报错信息不明确
# 统计系统调用错误
strace -c -f ./my_program 2>&1 | tail -20
1
2
2
关注:
EPERM(Operation not permitted) - 权限不足EACCES(Permission denied) - 访问被拒绝EEXIST(File exists) - 文件已存在(可能是锁文件)
# 3.4 内存映射问题
场景: 程序崩溃但 core dump 信息不明确
# 追踪内存操作
strace -e trace=memory -f ./my_program 2>&1 | grep -E "(ENOMEM|MAP_FAILED)"
1
2
2
常见错误模式:
mmap(NULL, 1073741824, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM (Cannot allocate memory)
mmap(0x7f0000000000, 4096, PROT_READ, MAP_FIXED|MAP_PRIVATE, 3, 0) = MAP_FAILED (-1 errno = 13)
1
2
2
# 3.5 进程间通信错误
场景: 管道或共享内存通信失败
# 追踪进程调用
strace -e trace=process,signal -f ./my_program 2>&1 | grep -E "(SIGPIPE|ECHILD)"
1
2
2
常见错误模式:
write(4, "data", 4) = -1 EPIPE (Broken pipe)
wait4(-1, 0x7ffd..., WNOHANG|WUNTRACED, NULL) = -1 ECHILD (No child processes)
1
2
2
# 3.6 库文件加载问题
场景: 动态库加载失败但报错不清晰
# 追踪文件操作,关注 .so 文件
strace -e trace=file ./my_program 2>&1 | grep "\.so"
1
2
2
常见错误模式:
openat(AT_FDCWD, "./libfoo.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT
openat(AT_FDCWD, "/usr/lib/libfoo.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT
1
2
2
# 四、高级技巧
# 4.1 只关注失败的调用
# 过滤返回值为错误的调用
strace -e inject=error:retval=error ./my_program
# 或者用 grep 过滤
strace ./my_program 2>&1 | grep "= -"
1
2
3
4
5
2
3
4
5
# 4.2 带时间分析的追踪
# 追踪并显示耗时,找出慢操作
strace -T -e trace=file,network ./my_program 2>&1 | awk '{if($NF+0 > 0.1) print}'
1
2
2
# 4.3 附着到运行中的进程
# 找到目标进程
ps aux | grep my_program
# 附着追踪(Ctrl+C 停止)
strace -p <pid> -e trace=file -f
1
2
3
4
5
2
3
4
5
# 4.4 输出到文件并分析
# 保存完整追踪
strace -f -o /tmp/trace.log ./my_program
# 分析错误统计
grep -E "= -[A-Z]" /tmp/trace.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -20
1
2
3
4
5
2
3
4
5
# 五、错误码速查表
| 错误码 | 含义 | 常见原因 |
|---|---|---|
ENOENT | No such file or directory | 文件路径不存在 |
EACCES | Permission denied | 权限不足 |
EPERM | Operation not permitted | 无操作权限 |
ECONNREFUSED | Connection refused | 连接被拒绝 |
ETIMEDOUT | Connection timed out | 连接超时 |
ECONNRESET | Connection reset | 连接被重置 |
ENOMEM | Cannot allocate memory | 内存不足 |
EPIPE | Broken pipe | 管道断裂 |
ECHILD | No child processes | 子进程不存在 |
EEXIST | File exists | 文件已存在(如锁文件) |
EROFS | Read-only file system | 只读文件系统 |
# 六、实战案例
# 案例 1:程序启动失败但无报错
# 问题:程序启动后立即退出,没有错误输出
# 分析
strace -f -o /tmp/trace.log ./my_program
grep "= -" /tmp/trace.log | head -20
# 发现:
# openat(AT_FDCWD, "./config.yaml", O_RDONLY) = -1 ENOENT
# 结论:缺少配置文件
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 案例 2:程序卡住不动
# 问题:程序运行一段时间后卡住
# 分析
strace -p <pid> -T -e trace=network
# 发现:
# connect(5, ..., 16) = 0 (0.000123s)
# write(5, "query", 5) = 5 (0.000089s)
# read(5, 0x7ffd..., 8192) (30.000000s) <unfinished ...>
# 结论:数据库查询超时
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 案例 3:权限问题排查
# 问题:普通用户运行失败,root 正常
# 分析
strace -e trace=file,openat ./my_program 2>&1 | grep -E "EACCES|EPERM"
# 发现:
# openat(AT_FDCWD, "/var/log/app.log", O_WRONLY|O_CREAT) = -1 EACCES
# 结论:日志目录权限不足
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 七、注意事项
- 性能影响: strace 会显著降低程序执行速度,生产环境慎用
- 安全风险: strace 需要 ptrace 权限,可能被安全策略禁止
- 多线程程序: 使用
-f参数跟踪所有线程 - 输出量大: 使用
-e trace=过滤,避免信息过多 - 内核版本: 某些选项在老版本内核上可能不支持
# 八、与其他工具的配合
| 工具 | 配合 strace 的用途 |
|---|---|
ltrace | 追踪库函数调用 |
perf | 性能分析和热点定位 |
gdb | 调试程序逻辑错误 |
dmesg | 查看内核错误日志 |
journalctl | 查看系统服务日志 |
# 总结
strace 的核心价值在于:它能看到程序与操作系统交互的真实情况。
当程序行为异常但没有明显错误信息时,strace 能帮你:
- 发现失败的系统调用
- 看到实际的参数和返回值
- 定位权限、路径、连接等隐蔽问题
- 分析程序的执行流程
记住:程序可以对用户撒谎(没有错误输出),但无法对内核撒谎(系统调用记录)。