Linux环境下的内核态后端调试神器strace

  • 内容
  • 评论
  • 相关

按照strace官网的描述,strace是Linux环境下的一款程序调试工具,可用于诊断、调试和教学的Linux用户空间跟踪器。更直白的说就用来监察一个应用程序所使用的系统调用。它可以从开始到结束跟踪二进制的执行,并在进程的生命周期中输出一行具有系统调用名称,每个系统调用的参数和返回值的文本行。

在Linux中,进程是不能直接去访问硬件设备的,比如读取磁盘文件、接收网络数据等,但可以将用户态模式切换到内核模式,通过系统调用来访问硬件设备。这时strace就可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间、调用次数,成功和失败的次数。

strace能做什么

  1. 它可以基于特定的系统调用或系统调用组进行过滤
  2. 它可以通过统计特定系统调用的使用次数,所花费的时间,以及成功和错误的数量来分析系统调用的使用。
  3. 它跟踪发送到进程的信号。
  4. 可以通过pid附加到任何正在运行的进程。
  5. 调试性能问题,查看系统调用的频率,找出耗时的程序段
  6. 查看程序读取的是哪些文件从而定位比如配置文件加载错误问题
  7. 查看某个php脚本长时间运行“假死”情况
  8. 当程序出现“Out of memory”时被系统发出的SIGKILL信息所kill
  9. 另外因为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 国际许可协议进行许可,非商业性质可转载须署名链接,详见本站版权声明。

评论

0条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注