strace 手册
如果能了解程序的每行代码是如何执行的,程序在运行时便了无秘密可言,是谓程序的可观察性。在我09年最早接触strace的时候,可观察性的概念还没有流行起来。使用至今,虽然systemtap,bpf等先进工具不断涌现,但它仍是首选,因为太简单易用。
strace是个诊断,教学,调试工具,最简单的用法是通过strace启动程序 strace command args ,如果程序已启动,可以 strace -p <pid> 。strace 拦截并记录程序发出的 系统调用 以及程序收到的 信号 ,打印每个系统调用的名称,参数,返回值到标准错误。
1 strace 的用途
- 无源码即可了解程序的机制。例如,通过观察
strace ip addr使用了哪些系统调用,可以了解如何编程获取机器IP。 - 正常和异常对比。例如,通过对比
strace cp data data.1和strace cp -a data data.2可以看看为啥有时cp -a才能正常工作。 - 性能问题。例如,通过观察关键系统调用的时间间隔,是否符合预期,可以判断性能问题,锁是不是太大了,网络是不是太慢等。
- 了解程序正在干啥。有时候程序既不退出,也不打印输出,完全不知道它在忙啥,可以用strace看看。
1.1 strace 的机制
程序的运行离不开操作系统,而系统调用正是操作系统和应用的边界,通过观察边界的行为,进而了解程序的行为。
2 strace 的输出
2.1 输出格式
每行包含了系统调用名称,被括号包围的参数,以及返回值,例如strace cat /dev/null 的输出包括 open("/dev/null", O_RDONLY) = 3 。当错误发生时(通常返回-1),会包含额外的错误码和错误字符串,例如 open("/notexist", O_RDONLY) = -1 ENOENT (No such file or directory)
收到信号会打印信号名称,以及解码后的siginfo结构,例如 sleep 666 被 SIGINT 中断后,打印:
sigsuspend([] <unfinished ...>
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=...} ---
+++ killed by SIGINT +++
2.2 未完成的系统调用
如果系统调用正在执行,同时另一个系统调用被其它线程调用(strace 可以同时跟踪多个线程),strace会保留调用的顺序,同时标记该系统调用为 unfinished ,当该系统调用返回时,会被标记为 resumed 。
[pid 28772] select(4, [3], NULL, NULL, NULL <unfinished ...>
[pid 28779] clock_gettime(CLOCK_REALTIME, {1130322148, 939977000}) = 0
[pid 28772] <... select resumed> = 1 (in [3])
可重启系统调用被信号中断后,打印略有不同,因为操作系统会立即重启该系统调用。
read(0, 0x7ffff72cf5cf, 1) = ? ERESTARTSYS (To be restarted) --- SIGALARM ... --- rt_sigreturn(0xe) = 0 read(0, "", 1) = 0
2.3 参数的输出
很多时候函数参数都是数字、指针,在输出时,strace尽量讲这些不可读的内容转成有意义的字符串。
输出时参数会被尽量转成符号。例如,执行strace重定向 >>xyzzy 时输出 open("xyzzy", O_WRONLY|O_APPEND|O_CREAT, 0666) = 3 这里 open 的第二个参数被逆向转成宏,第三个参数被转成8进制。(熟悉C的同学了解,二进制程序里,这些参数都是数字,宏是源码里的东西,可见strace做了很多可读性的转换)。
结构体指针会被解引用,打印成接近源码的形式,例如,strace ls -l /dev/null 时输出 lstat("/dev/null", {st_mode=S_IFCHR|0666, st_rdev=makedev(0x1,0x3), ...}) = 0 ,这里第二个参数 struct stat 的指针,存放返回值,输出时尽量可读。如果系统调用失败了,指针不会被解引用。
字符指针打印成C字符串,默认打印32个,超过部分用省略号代替。
3 strace的参数
3.1 常用的参数
-p pid 用于trace运行中的程序, CTRL-C 退出trace。
-f 连同trace的子进程一起trace。
-s strsize 指定输出字符串时的最大长度,默认32。
3.2 性能用途方面的参数
-r 打印两次系统调用之间的相对时间
-t 打印系统调用的时间
-tt 打印系统调用的时间,包含毫秒数据
-ttt 打印系统调用的时间,以 秒.毫秒 的形式