Linux环境下的内核态后端调试神器strace
按照strace官网的描述,strace是Linux环境下的一款程序调试工具,可用于诊断、调试和教学的Linux用户空间跟踪器。更直白的说就用来监察一个应用程序所使用的系统调用。它可以从开始到结束跟踪二进制的执行,并在进程的生命周期中输出一行具有系统调用名称,每个系统调用的参数和返回值的文本行。
在Linux中,进程是不能直接去访问硬件设备的,比如读取磁盘文件、接收网络数据等,但可以将用户态模式切换到内核模式,通过系统调用来访问硬件设备。这时strace就可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间、调用次数,成功和失败的次数。
strace能做什么
- 它可以基于特定的系统调用或系统调用组进行过滤
- 它可以通过统计特定系统调用的使用次数,所花费的时间,以及成功和错误的数量来分析系统调用的使用。
- 它跟踪发送到进程的信号。
- 可以通过pid附加到任何正在运行的进程。
- 调试性能问题,查看系统调用的频率,找出耗时的程序段
- 查看程序读取的是哪些文件从而定位比如配置文件加载错误问题
- 查看某个php脚本长时间运行“假死”情况
- 当程序出现“Out of memory”时被系统发出的SIGKILL信息所kill
- 另外因为strace拿到的是系统调用相关信息,一般也即是IO操作信息,这个对于排查比如cpu占用100%问题是无能为力的。这个时候就可以使用GDB工具了。
如何使用strace
假如我想统计echo函数的系统调用的耗时以及调用次数和出错次数,那么可以这样:
[chenxi@lo ~]$ strace -c echo 5 5 # 这里书echo函数本身的输出值,下方表格就是strace打印出来的具体系统调用的统计信息了 % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 22.78 0.000018 4 5 close 18.99 0.000015 5 3 open 17.72 0.000014 2 9 mmap 15.19 0.000012 3 4 fstat 13.92 0.000011 6 2 munmap 11.39 0.000009 9 1 write 0.00 0.000000 0 1 read 0.00 0.000000 0 4 mprotect 0.00 0.000000 0 4 brk 0.00 0.000000 0 1 1 access 0.00 0.000000 0 1 execve 0.00 0.000000 0 1 arch_prctl ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000079 36 1 total
如果我想查看某个进程的系统调用过程呢?
[chenxi@lo ~]$ sudo strace -p 27305 # -p 后面的参数是进程pid strace: Process 27305 attached getpid() = 1 epoll_pwait(8, [], 101, 192, NULL, 8) = 0 getpid() = 1 getsockopt(7, SOL_TCP, TCP_INFO, "\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0"..., [144]) = 0 getpid() = 1 getpid() = 1 epoll_pwait(8, [], 101, 1000, NULL, 8) = 0 getpid() = 1 getsockopt(7, SOL_TCP, TCP_INFO, "\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0"..., [144]) = 0 getpid() = 1 getpid() = 1 epoll_pwait(8, [], 101, 1000, NULL, 8) = 0 getpid() = 1 getsockopt(7, SOL_TCP, TCP_INFO, "\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0"..., [144]) = 0 getpid() = 1 getpid() = 1 epoll_pwait(8, [], 101, 1000, NULL, 8) = 0 getpid() = 1 getsockopt(7, SOL_TCP, TCP_INFO, "\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0"..., [144]) = 0 getpid() = 1 getpid() = 1 epoll_pwait(8, [], 101, 1000, NULL, 8) = 0 getpid() = 1 getsockopt(7, SOL_TCP, TCP_INFO, "\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0"..., [144]) = 0 getpid() = 1 getpid() = 1
strace的常用选项
-tt 在每行输出的前面,显示毫秒级别的时间 -T 显示每次系统调用所花费的时间 -v 对于某些相关调用,把完整的环境变量,文件stat结构等打出来。 -f 跟踪目标进程,以及目标进程创建的所有子进程 -e 控制要跟踪的事件和跟踪行为,比如指定要跟踪的系统调用名称 -o 把strace的输出单独写到指定的文件 -s 当系统调用的某个参数是字符串时,最多输出指定长度的内容,默认是32个字节 -p 指定要跟踪的进程pid, 要同时跟踪多个pid, 重复多次-p选项即可。
特别说明的-e选项
这里特别说下strace的-e trace选项。要跟踪某个具体的系统调用,-e trace=xxx即可。但有时候我们要跟踪一类系统调用,比如所有和文件名有关的调用、所有和内存分配有关的调用。如果人工输入每一个具体的系统调用名称,可能容易遗漏。于是strace提供了几类常用的系统调用组合名字。
-e trace=file 跟踪和文件访问相关的调用(参数中有文件名) -e trace=process 和进程管理相关的调用,比如fork/exec/exit_group -e trace=network 和网络通信相关的调用,比如socket/sendto/connect -e trace=signal 信号发送和处理相关,比如kill/sigaction -e trace=desc 和文件描述符相关,比如write/read/select/epoll等 -e trace=ipc 进程相关,比如shmget等
绝大多数情况,我们使用上面的组合名字就够了。实在需要跟踪具体的系统调用时,可能需要注意C库实现的差异。
使用总结
当发现进程或服务异常时,我们可以通过strace来跟踪其系统调用,“看看它在干啥”,进而找到异常的原因。熟悉常用系统调用,能够更好地理解和使用strace。不过strace只能用于查看内核态的调用,用户态的函数调用跟踪用则需要使用ltrace、gdb,其他情况则需要使用perf、SystemTap等工具了。
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可,非商业性质可转载须署名链接,详见本站版权声明。
发表评论