前言¶
strace 可以用来查看/记录程序运行过程中调用的 系统调用 以及接收到的进程信号(signal), 对于我们日常 debug 疑难杂症非常的有帮助,是一个非常好的 debug 工具。本文简单记录一下 strace 的常用功能和操作。
输出的含义¶
我们通过一个简单的命令来查看 strace 程序的典型输出:
$ strace ls /tmp/trace
execve("/bin/ls", ["ls", "/tmp/trace"], [/* 41 vars */]) = 0
brk(0) = 0x11af000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0738c16000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=35759, ...}) = 0
mmap(NULL, 35759, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0738c0a000
close(3) = 0
open("/lib64/libselinux.so.1", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0PX\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=122040, ...}) = 0
....
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb97a060000
write(1, "test_dir test.txt\n", 19test_dir test.txt
) = 19
close(1) = 0
munmap(0x7fb97a060000, 4096) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
比如下面的这个输出:
execve("/bin/ls", ["ls", "/tmp/trace"], [/* 41 vars */]) = 0
的含义如下:
- execve: 系统调用的名称。
- ("/bin/ls", ["ls", "/tmp/trace"], [/* 41 vars */]): 这个括号里是系统调用的参数。
- = 0 : 这个 0 是系统调用的返回值,不一定是数字看具体的系统调用返回啥结果就是啥值。
上面有些系统调用的返回值比较特殊,比如:
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
后面这个 ENOENT (No such file or directory) 中的 ENOENT 是错误码, (No such file or directory) 是错误码的解释。
上面是系统调用相关的输出格式和含义,下面再看一个进程信号(signal)的输出格式:
$ strace -p 15718
Process 15718 attached
select(1, [0], NULL, NULL, NULL) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=15757, si_uid=500} ---
+++ killed by SIGTERM +++
其中:
--- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=15757, si_uid=500} ---
这一句就是进程接收到的具体 signal 的信息。
常用命令行参数¶
常用的参数组合:
strace -f -s 1024 -tt -T -yy -p <pid> strace -f -s 1024 -tt -T -yy -p <pid> -o <filename> strace -c -p <pid> strace -f -s 1024 -tt -T -yy -C -p <pid>
-p <pid>: 附加到进程中,记录某个进程的系统调用和进程信号信息。多个 -p <pid> 可以实现同时追踪多个进程。
-f: 同时追踪子进程的系统调用情况(如果是多线程程序的话,会同时追踪所有线程)。如果不加 -f 参数的话默认只追踪指定的单个进程。
-o <filename>: 把输出结果保存到文件中(默认是输出到标准错误)。
-ff: 与 -o <filename> 一起使用,会把每个 pid (进程 pid 或线程 id) 的 strace 数据保存到 <filename>.<pid> 的文件中。
-e <expr>: 过滤事件,只输出符合规则的事件,可以用来指定只看某些系统调用的情况,比如: -e open,read 。 或者不看某些系统调用: -e '!open,read' ,更强大的过滤功能可以查看 strace(1) 。
-s <size>: 指定字符串数据的大小,默认 32。可以通过 -s 来显示更详细的信息或精简信息(比如很多系统调用的参数的值会被截断,可以通过 -s 配置更大的 size 来查看更详细的参数值)。
-tt: 显示系统调用是在哪个时刻调用的,包含微秒( -t 参数一样的效果就是时间不包含微秒):
$ strace -e open,read -s 2 -tt ls 14:32:18.877470 read(3, "\177E"..., 832) = 832 14:32:18.878249 read(3, "\177E"..., 832) = 832 14:32:18.878867 read(3, "\177E"..., 832) = 832 14:32:18.879386 read(3, "\177E"..., 832) = 832 14:32:18.879871 read(3, "\177E"..., 832) = 832 14:32:18.880923 read(3, "no"..., 1024) = 420 14:32:18.881255 read(3, "", 1024) = 0 上面的第一列时间信息即为 -tt 的效果。
-T: 显示执行系统调用所花费的时间,单位是秒
$ strace -e read -s 2 -T pwd read(3, "\177E"..., 832) = 832 <0.000012> 上面行末的 <0.000012> 即为 -T 的效果。
-y: 输出文件描述符所对应的文件路径
$ strace -e read -s 10 cat test.txt read(3, "\177ELF\2\1\1\3\0\0"..., 832) = 832 read(3, "hello\n", 131072) = 6 hello read(3, "", 131072) = 0 $ strace -e read -s 10 -y cat test.txt read(3</lib/x86_64-linux-gnu/libc-2.27.so>, "\177ELF\2\1\1\3\0\0"..., 832) = 832 read(3</path/to/test.txt>, "hello\n", 131072) = 6 hello read(3</path/to/test.txt>, "", 131072) = 0 可以看到加了 -y 后文件描述符后面会跟它所对应的文件的路径
-yy: 输出文件描述更详细的信息,比如 socket 文件描述符输出所对应的协议信息。
$ strace -e connect -s 3 nc baidu.com 80 ... connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.53")}, 16) = 0 ... $ strace -e connect -s 3 -y nc baidu.com 80 ... connect(3<socket:[22180]>, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.53")}, 16) = 0 $ strace -e connect -s 3 -yy nc baidu.com 80 ... connect(3<UDP:[22202]>, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.53")}, 16) = 0 可以看到 -yy 会显示上面的 UDP 这个协议信息。
-c: 统计系统调用的次数、种类以及错误信息,会隐藏详细的追踪信息(前面的 -e 过滤参数也可以用于这个):
$ strace -c cat test.txt hello % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 18.45 0.000019 3 7 mmap 18.45 0.000019 5 4 mprotect 17.48 0.000018 9 2 munmap 13.59 0.000014 4 4 openat 9.71 0.000010 10 1 write 6.80 0.000007 2 3 read 5.83 0.000006 2 3 3 access 3.88 0.000004 1 5 fstat 2.91 0.000003 1 6 close 1.94 0.000002 1 3 brk 0.97 0.000001 1 1 execve 0.00 0.000000 0 1 arch_prctl 0.00 0.000000 0 1 fadvise64 ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000103 41 3 total
-C: 跟 -c 类似,只是增加了会输出详细的追踪信息
$ strace -e read -C -s 3 cat test.txt read(3, "\177EL"..., 832) = 832 read(3, "hel"..., 131072) = 6 hello read(3, "", 131072) = 0 +++ exited with 0 +++ % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 0.00 0.000000 0 3 read ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000000 3 total
-v: 显示系统调用时传入的参数变量的具体内容,而不是默认显示为 /* 8 vars */
常用过滤表达式¶
-e <expr> 这个参数可以用来指定需要 trace 哪些事件,格式如下:
[qualifier=][!][?]value1[,[?]value2]... 其中 qualifier 的值是 trace, abbrev, verbose, raw, signal, read, write, fault, inject, or kvm 。默认是 trace value 就是各种过滤条件了。
- -e trace=<set> : <set> 是系统调用名称(默认是 trace=all ),比如 trace=open,close,read,write
- -e trace=/<regex>: 可以通过这种方式来用正则表达式指定系统调用名称,支持的正则语法可以参考 regex(7) 。
- -e trace=%file: 文件相关系统调用。
- -e trace=%desc: 文件描述符相关。
- -e trace=%process: 进程管理相关系统调用。
- -e trace=%network: 网络相关。
- -e trace=%signal: 信号相关。
- -e trace=%ipc: IPC 相关。
- -e trace=%memory: 内存 mapping 相关。
- -e signal=<set>: <set> 是进程信号的名称(默认是 signal=all ),比如 -e signal=SIGTERM
- -e read=<set>: 追踪指定 fd 上的数据并打印 hex 和 ascii 格式的数据, <set> 是 fd 比如:read=3,5
常见系统调用¶
- open() : 用于打开或创建一个文件。
- read() : 用于读取一个文件。
- write() : 写文件。
- connect() : 建立网络连接。
- sendto() : 发送网络数据。
- recvfrom() : 接收网络数据。
- futex() : 锁相关操作。
更多关于某个系统调用的含义/用途可以通过 man 2 <syscall> (把 <syscall> 替换为实际的系统调用名称,比如 man 2 open )命令行查看或者访问 http://man7.org/linux/man-pages/man2/<syscall>.2.html 这个网址(把 <syscall> 替换为实际的系统调用名称, 比如 http://man7.org/linux/man-pages/man2/open.2.html)亦或是 Google 一下。
总结¶
更多关于 strace 的信息可以从参考资料的 strace(1) 中获取,如果对输出中系统调用不了解的话可以参考参考资料中的 syscalls(2) 中的信息,同时也别忘了搜索引擎是你的好朋友,有啥不明白的记得 Google 一下。
Comments