strace、ltrace是Linux环境下的程序调试工具,strace用来监察一个应用程序所使用的系统调用及它所接收的系统信息。追踪程序运行时的整个生命周期,输出每一个系统调用的名字,参数,返回值和执行消耗的时间等。其与ltrace的区别是:strace主要用于跟踪系统调用和信号;ltrace用于跟踪用户级别的函数。

一、strace程序

strace程序常用的参数如下:

 1-p 跟踪指定的进程
 2-f 跟踪由fork子进程系统调用
 3-F 尝试跟踪vfork子进程系统调吸入,与-f同时出现时, vfork不被跟踪
 4-o filename 默认strace将结果输出到stdout。通过-o可以将输出写入到filename文件中
 5-ff 常与-o选项一起使用,不同进程(子进程)产生的系统调用输出到filename.PID文件
 6-r 打印每一个系统调用的相对时间
 7-t 在输出中的每一行前加上时间信息。 -tt 时间确定到微秒级。还可以使用-ttt打印相对时间
 8-v 输出所有系统调用。默认情况下,一些频繁调用的系统调用不会输出
 9-s 指定每一行输出字符串的长度,默认是32。文件名一直全部输出
10-c 统计每种系统调用所执行的时间,调用次数,出错次数。
11-e expr 输出过滤器,通过表达式,可以过滤出掉你不想要输出

对于即将执行的命令,可以通过strace直接跟踪,这里以执行uname命令为例,如下:

 1[student@desktop0 ~]$ strace uname
 2execve("/bin/uname", ["uname"], [/* 17 vars */]) = 0
 3brk(0)                                  = 0x1cfe000
 4mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdaca195000
 5access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
 6open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
 7fstat(3, {st_mode=S_IFREG|0644, st_size=67468, ...}) = 0
 8mmap(NULL, 67468, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fdaca184000
 9close(3)                                = 0
10open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
11………………略

如果是已经运行的程序,可以通过-p参数查看进程的系统调用:

 1[root@desktop0 ~]# strace -p 1135
 2Process 1135 attached
 3select(7, [3 4], NULL, NULL, NULL
 4)      = ? ERESTARTNOHAND (To be restarted if no handler)
 5--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4164, si_status=255, si_utime=2, si_stime=24} ---
 6wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 255}], WNOHANG, NULL) = 4164
 7wait4(-1, 0x7fffb3ff5a94, WNOHANG, NULL) = 0
 8rt_sigaction(SIGCHLD, NULL, {0x7f172309f080, [], SA_RESTORER, 0x7f1720248a00}, 8) = 0
 9rt_sigreturn()                          = -1 EINTR (Interrupted system call)
10select(7, [3 4], NULL, NULL, NULL)      = 1 (in [3])
11accept(3, {sa_family=AF_INET, sin_port=htons(52208), sin_addr=inet_addr("172.25.0.250")}, [16]) = 5
12fcntl(5, F_GETFL)                       = 0x2 (flags O_RDWR)
13pipe([6, 7])                            = 0
14…………略

不过这里使用-p参数输出的结果比较少,需要使用-f参数才能捕获fork的进程。使用-c参数能可以统计系统调用:

 1[root@desktop0 ~]# strace -c uname
 2Linux
 3% time     seconds  usecs/call     calls    errors syscall
 4------ ----------- ----------- --------- --------- ----------------
 5 26.88    0.000050           6         9           mmap
 6 17.74    0.000033          11         3           open
 7 15.05    0.000028          14         2           munmap
 8 13.98    0.000026           7         4           mprotect
 9  5.38    0.000010          10         1           write
10  4.30    0.000008           2         4           fstat
11  4.30    0.000008           8         1         1 access
12  3.76    0.000007           1         5           close
13  3.76    0.000007           2         4           brk
14  1.61    0.000003           3         1           read
15  1.61    0.000003           3         1           execve
16  1.08    0.000002           2         1           uname
17  0.54    0.000001           1         1           arch_prctl
18------ ----------- ----------- --------- --------- ----------------
19100.00    0.000186                    37         1 total

如果需要捕获子进程调用,可以strace -fc配合使用。

二、ltrace程序

ltrace相关的参数如下:

 1-a 对齐具体某个列的返回值
 2-c 计算时间和调用,并在程序退出时打印摘要
 3-C 解码低级别名称(内核级)为用户级名称
 4-d 打印调试信息
 5-e 改变跟踪的事件
 6-f 跟踪子进
 7-h 打印帮助信息
 8-i 打印指令指针,当库调用时。
 9-l 只打印某个库中的调用。
10-L 不打印库调用。
11-n, --indent=NR 对每个调用级别嵌套以NR个空格进行缩进输出。
12-o, --output=file 把输出定向到文件。
13-p PID 附着在值为PID的进程号上进行ltrace。
14-r 打印相对时间戳。
15-s STRLEN 设置打印的字符串最大长度。
16-S 显示系统调用。
17-t, -tt, -ttt 打印绝对时间戳。
18-T 输出每个调用过程的时间开销。
19-u USERNAME 使用某个用户id或组ID来运行命令。

其使用方法如下:

 1[root@desktop0 ~]# ltrace uname
 2__libc_start_main(0x401490, 1, 0x7fff0fbd9648, 0x4042a0 <unfinished ...>
 3strrchr("uname", '/')                                      = nil
 4setlocale(LC_ALL, "")                                      = "en_US.UTF-8"
 5bindtextdomain("coreutils", "/usr/share/locale")           = "/usr/share/locale"
 6textdomain("coreutils")                                    = "coreutils"
 7__cxa_atexit(0x401c40, 0, 0, 0x736c6974756572)             = 0
 8getopt_long(1, 0x7fff0fbd9648, "asnrvmpio", 0x606be0, nil) = -1
 9uname(0x7fff0fbd93b0)                                      = 0
10fputs_unlocked(0x7fff0fbd93b0, 0x7fb4b5607400, 0x7fb4b5606280, -1) = 1
11__overflow(0x7fb4b5607400, 10, 0x7fb4b582b004, 1024Linux
12)       = 10
13…………略

其统计时间使用方法如下:

 1[root@desktop0 ~]# ltrace -c uname
 2Linux
 3% time     seconds  usecs/call     calls      function
 4------ ----------- ----------- --------- --------------------
 5 44.84    0.008281        8281         1 __libc_start_main
 6 23.00    0.004248        4248         1 exit
 7  4.19    0.000774         193         4 __freading
 8  3.64    0.000673         673         1 setlocale
 9  3.03    0.000559         279         2 fflush
10  2.95    0.000545         272         2 fclose
11  2.93    0.000541         541         1 __overflow
12  2.68    0.000494         247         2 fileno
13  2.27    0.000420         210         2 __fpending
14  1.62    0.000300         300         1 bindtextdomain
15  1.55    0.000287         287         1 fputs_unlocked
16  1.51    0.000278         278         1 getopt_long
17  1.44    0.000265         265         1 uname
18  1.28    0.000237         237         1 textdomain
19  1.23    0.000227         227         1 strrchr
20  1.06    0.000196         196         1 __cxa_atexit
21  0.76    0.000141         141         1 exit_group
22------ ----------- ----------- --------- --------------------
23100.00    0.018466                    24 total

通过-e进行函数类型匹配调用方法如下:

1[root@desktop0 ~]# ltrace -e fclose -c uname
2Linux
3% time     seconds  usecs/call     calls      function
4------ ----------- ----------- --------- --------------------
5 83.61    0.000699         349         2 fclose
6 16.39    0.000137         137         1 exit_group
7------ ----------- ----------- --------- --------------------
8100.00    0.000836                     3 total

通过strace、ltrace这类工具的分析,我们可以找出某些程序的性能问题在哪。