strace 是什么?维基百科给出的定义如下:
strace 是 Linux 系统下的一个用于诊断、调试和指导用户空间的实用程序。它用于监视和篡改进程与 Linux 内核之间的交互,包括系统调用、信号传递和进程状态的更改。
不管什么编程语言写的程序,只要跑在 Linux 系统下,就必然需要与内核交互。某些高级编程语言,在语言或者标准库层面上或许并没有提供直接与内核交互的接口,但那也仅仅是虚拟机或者解释器之类的运行时屏蔽了这部分内容而已。作为运行在操作系统上的一个进程,与内核交互(也就是系统调用)是不可避免的。既然不可避免,当系统调用返回错误或者阻塞时,如何才能定位其中的原因?编程语言层面返回的错误固然能提供一部分信息,但这也属于是二手信息罢了。那怎么获取一手信息并快速定位问题的原因呢?答案就是 strace 。
不同的 strace 版本功能上存在不少差异,为了避免鸡同鸭讲的情况发生,我把我所使用的 strace 版本放这:
|
|
strace 选项
类别 | 选项 | 选项值 | 描述 | 示例 |
---|---|---|---|---|
Output format | -o | 目标文件 | 将系统调用跟踪信息输出到文件而非 stderr | strace -o trace.log ls / |
-A | 无 | 与-o 选项一起使用,会将输出以追加的方式写入文件。 |
strace -o trace.log -A ls / | |
-i | 无 | 打印系统调用时的指令指针 | strace -i ls / | |
-t | 无 | 打印系统调用时的时间(时、分、秒) | strace -t ls / | |
-tt | 无 | 打印系统调用时的时间(时、分、秒、微秒) | strace -tt ls / | |
-ttt | 无 | 打印系统调用时的时间(Unix时间戳、微秒) | strace -ttt ls / | |
-T | 无 | 打印每个系统调用的耗时 | strace -T ls / | |
-y | 无 | 打印文件描述符所对应的文件名 | strace -y ls / | |
-yy | 无 | 打印与套接字文件描述符相关的协议特定信息,以及与设备文件描述符相关的块/字符设备号。 | strace -yy -e trace=connect,write ping -c 1 www.baidu.com | |
Statistics | -c | 无 | 统计每个系统调动的耗时、调用次数、失败数 | strace -c ls / |
Filtering | -e | trace=set | 跟踪指定集合中的系统调用 | strace -e trace=read,write ./a.out |
trace=/regex | 跟踪名称与正则表达式匹配的系统调用 | strace -e trace=/epoll_ ./a.out | ||
trace=%file | 跟踪含有文件名参数的系统调用 | strace -e trace=%file ls / | ||
trace=%process | 跟踪所有进程管理相关的系统调用 | strace -e trace=%process bash | ||
trace=%network | 跟踪所有网络相关的系统调用 | strace -e trace=%network ping -c 1 github.com | ||
trace=%signal | 跟踪所有信号相关的系统调用 | strace -e trace=%signal bash | ||
trace=%ipc | 跟踪所有进程间通信相关系统调用 | |||
trace=%desc | 跟踪所有文件描述符相关的系统调用 | strace -e trace=%desc ls / | ||
trace=%memory | 跟踪所有内存相关的系统调用 | strace -e trace=%memory ls / | ||
signal=set | 跟踪指定信号集合中的系统调用 | |||
read=set | 转储从文件描述符集合中读取的内容(不会过滤并仅保留 read 系统调用) | strace -e read=3,4,5 -e trace=read ls / | ||
write=set | 转储往文件描述符集合中写入的内容(不会过滤并仅保留 write 系统调用) | strace -e write=1,3,5 -e trace=write echo “hello world” | ||
raw=set | 不解码以原生(十六进制)形式显示系统调用参数值 | strace -e raw=read -e trace=read,write ls -l / | ||
-P | 文件路径 | 跟踪访问指定路径的系统调用(可指定多个文件路径) | strace -P /dev/stdout -P /dev/fd/1 -y ls / | |
-v | 无 | 打印详细信息 | strace -v ls / | |
Tracing | -f | 无 | 跟踪由 fork 、vfork、 clone 等系统调用创建的子进程 | strace -f -e trace=fork,vfork,clone ./a.out |
-ff | 无 | 与-o 选项一起使用,会将输出写入名为filename.pid 的文件中(不能与-c 一同使用)。 |
strace -o trace.log -ff -f -e trace=clone ./a.out | |
Startup | -E | 环境变量名=值 | 启动进程时为进程提供额外的环境变量列表 | strace -E NAME=voidint -E AGE=24 -e trace=write sh -c ’echo $NAME;echo $AGE' |
-p | 进程ID | 待跟踪的目标进程ID(若要跟踪多个进程,可多次指定-p 选项) |
strace -p 1234 -p 5678 | |
Miscellaneous | -V | 无 | 打印 strace 版本信息 | strace -V |
示例
1、跟踪程序运行过程中发起的所有的系统调用
|
|
是不是很惊讶,一个 ls 命令竟然产生了这么多的系统调用(当然其中也包含了一部分 strace 进程 fork 和 execve 目标程序的系统调用)。其中,有不少内容我做了适当省略,的确是输出太多。
2、跟踪已运行进程实时发起的系统调用
|
|
此处以守护进程 rsyslogd 为例,尝试去跟踪该进程的系统调用。一般线上问题排查场景也是附加到一个正在运行的进程上,观察该进程的系统调用,看是否有错误发生,看是否某个系统调用执行时间过长等。
3、统计每个系统调动的耗时、调用次数、失败数
|
|
4、跟踪指定名称的系统调用
|
|
如今这个社会不同于古代社会,信息过载让人眼花缭乱,因此信息的过滤提炼变得尤为重要。而-e
选项就是 strace 工具提供的最重要的过滤选项。对于想要跟踪特定的某几个系统调用的需求,只需要指定-e trace=syscall1,syscall2,syscall3
的选项,那么输出的内容将只会保留你所指定的那三个系统调用,其它无关系统调用均不会被展示。
5、跟踪匹配正则表达式的系统调用
|
|
正则表达式是一个强大的工具,它也可以被用于 strace 工具的信息过滤中,-e trace=/regex
选项正是为此功能所设计。
6、跟踪含有文件名参数的系统调用
|
|
仔细观察上面的示例可以发现,指定-e trace=%file
选项后,输出的内容拥有一个共同的特点,即均是包含文件名参数的系统调用。
7、跟踪进程管理相关的系统调用
|
|
若是想要跟踪进程管理相关的系统调用,那么-e trace=%process
选项能满足这个需求。具体指代的系统调用主要是创建进程(fork 系列)、执行新程序(exec 系列)、回收子进程(wait 系列)相关的一些列系统调用。
8、跟踪所有网络相关的系统调用
|
|
-e trace=%network
或者-e trace=%net
是跟踪网络相关系统调用的选项。实际输出的内容远比这个多,这是做了适当省略后的输出。
9、跟踪所有信号相关的系统调用
|
|
10、跟踪所有文件描述符相关的系统调用
|
|
许多系统调用的参数中都包含了文件描述符,要想过滤出这类系统调用,只要加上-e trace=%desc
选项。
11、跟踪所有内存相关的系统调用
|
|
-e trace=%memory
选项用于过滤出内存相关的系统调用。
12、跟踪访问指定路径的系统调用
|
|
想要确定有哪些系统调用访问了某个或者某些文件路径,用-P
选项就能达到过滤效果。示例中用-P
选项指定了标准输出的两个文件路径,最终输出的内容中也的确如此。
13、跟踪子进程的系统调用
为演示创建子进程的系统调用,准备了以下C语言程序:
|
|
加上-f
选项后,会跟踪子进程中的系统调用,形如[pid 2600409] xxxxx
。
|
|
14、查看进程发起系统调用的时间戳
|
|
-tt
选项可以显示每个系统调用的时间信息
15、查看进程系统调用的耗时
|
|
加上-T
选项后,每行系统调用信息的最右侧会标识出该系统调用的耗时,如<0.000076>
。
16、查看进程系统调用中文件描述符所关联的文件名
细心的你是否发现示例跟踪访问指定路径的系统调用
中倒数第二行输出的close(2)
颇为奇怪,效果上应该仅显示有关标准输出的系统调用,为什么会显示fd=2
的系统调用?下面我们增加一个选项-y
或者-yy
试试。
|
|
看到这个输出,是否就能明白为什么了呢?看来文件描述符1
和2
都指向了标准输出。
参考
- strace(1) — Linux manual page
- strace 跟踪进程中的系统调用