strace 是 linux 下无需改代码、不依赖符号即可实时跟踪 c++ 等任意语言程序系统调用的核心工具,常用方式为 strace ./my_program,配合 -t -tt 查耗时与时间戳,-p pid 动态附加,-e trace=open,connect 精准过滤,-c 统计瓶颈,并需注意重定向程序输出、加 -f 跟踪子进程、设 -s 256 防路径截断。

直接用 strace 就能跟踪,不需要改代码、不依赖调试符号、也不用重新编译。 它看到的是进程实际发出的系统调用,和语言无关——C++ 程序启动后,execve、open、read、write、socket 这些调用照常被记录下来。
怎么启动一个 C++ 程序并实时看它的系统调用
最常用也最直接的方式:
strace ./my_program
如果程序输出太多(比如带 std::cout),建议把程序 stdout/stderr 重定向,避免和 strace 的日志混在一起:
strace -T -tt ./my_program > /dev/null 2>&1
其中:
- -T 显示每个系统调用耗时(关键!能看出卡在哪)
- -tt 打印微秒级时间戳(便于对齐行为时序)
- > /dev/null 2>&1 把程序自己的输出全吃掉,只留 strace 的跟踪日志在终端
常见漏点:
- 忘了加 -T,结果只看到调用名,看不出是快是慢
- 没屏蔽程序输出,日志滚动太快,根本找不到 open 或 connect 那行
- 用 ./my_program | strace 是错的——strace 跟踪的是管道左边的 shell 进程,不是你的程序
程序已经跑起来了,怎么 attach 上去
比如你发现某个后台 C++ 服务响应变慢,但没开调试模式:
strace -p $(pidof my_service) -T -s 256
说明:
- -p 后必须跟真实 PID,pidof 要确保匹配唯一进程(多个同名进程时建议用 pgrep -f "my_service")
- -s 256 把字符串参数截断长度从默认 32 字节提到 256,否则像 open("/very/long/config/path.yaml", ...) 只能看到 open("/very/long/conf...", ...)
- 如果程序开了多线程,记得加 -f,否则只看到主线程的调用
容易踩的坑:
- strace -p 1234 后按 Ctrl+C 会向目标进程发 SIGINT,可能意外中断服务
- 没加 -f 却在跟踪一个 fork 多进程的 C++ 服务(如用 fork() 做 worker 模型),会漏掉子进程行为
- 在容器里运行时,pidof 查到的是宿主机 PID,但 strace -p 需要进入容器命名空间才能 attach,否则报 Operation not permitted
只想看文件或网络相关的系统调用
C++ 程序出问题,往往集中在几个场景:配置文件打不开、日志写失败、连不上数据库、DNS 解析卡住。这时候过滤比全量日志更高效:
strace -e trace=open,close,read,write,stat,fstat ./my_program 2>&1 | grep -E "(ENOENT|EACCES|Permission denied)"
或者专盯网络:
strace -e trace=socket,connect,bind,sendto,recvfrom,accept4 -T ./my_program
注意点:
- -e trace=network 是快捷写法,但部分老版本 strace 不支持,保险起见还是列具体调用名
- getaddrinfo 是库函数,不是系统调用,strace 看不到;它背后实际触发的是 open(读 /etc/resolv.conf)、sendto/recvfrom(UDP 查询)等,所以得看底层调用
- epoll_wait、poll、select 这类 I/O 多路复用调用,经常是阻塞点,别漏掉它们
输出太长没法人工看,怎么快速定位问题
别硬翻几千行日志。优先用这几个组合:
strace -c ./my_program 2>&1 → 看统计摘要,找出调用次数最多或耗时最长的几项strace -o trace.log ./my_program → 全量存盘,再用 grep 或 awk 挖关键线索strace -T ./my_program 2>&1 | awk '$NF ~ // && substr($NF,2,length($NF)-2)+0 > 0.1 {print}' → 找出所有耗时超 100ms 的调用
真正难的不是“怎么输出”,而是理解返回值含义:
- open("/path/to/file", O_RDONLY) = -1 ENOENT (No such file or directory):文件不存在,但程序没处理这个错误
- connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress):非阻塞 socket 正在连接中,正常
- write(2, "error: ...\n", 12) = -1 EAGAIN (Resource temporarily unavailable):stderr fd 被设成非阻塞且缓冲区满,可能掩盖真实错误
这些细节不结合上下文和 errno 手册页,很容易误判。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











