5 simple ways to troubleshoot using Strace
我很意外大部分人都不知道如何使用strace。strace一直是我的首选debug工具,因为它非常的有效,很多问题都能够用它进行排查。
strace是什么?
Strace是一个用来跟踪系统调用的简易工具。它最简单的用途就是跟踪一个程序整个生命周期里所有的系统调用,并把调用参数和返回值以文本的方式输出。
当然它还可以做更多的事情:
strace可以过筛选出特定的系统调用。
strace可以记录系统调用的次数,时间,成功和失败的次数。
strace可以跟踪发给进程的信号。
strace可以通过pid附加到任何正在运行的进程上。
strace类似其他Unix系统上的truss,或者Sun's Dtrace
使用教程
以下这些只是些皮毛的用法:
1)查看初始化时程序读取的配置文件
你是否遇到过程序去错误的位置读取配置文件的情况?
简单的排查方式:
$ strace php 2>&1 | grep php.ini
So this version of PHP reads php.ini from /usr/local/lib/php.ini (but it tries /usr/local/bin first).如果只关心特定的系统调用可以使用以下略微复杂的使用方法:
$ strace -e open php 2>&1 | grep php.ini如果安装多个版本的程序库,想搞清楚自己的程序加载的是哪个也可以如法炮制~。
2)为什么程序打不开这个文件?
是否有遇到过读取文件的时候被拒绝呢,你可以试试下面的命令:
$ strace -e open,access 2>&1 | grep your-filename查看open()和access()系统调用是否有异常
3)进程现在在做啥?
是否有遇到过进程突然cpu占用率很高?又或者进程莫名其妙被挂起的情况?
找到这个进程的pid,然后执行下列命令:
root@dev:~# strace -p 15427Process 15427 attached - interrupt to quitfutex(0x402f4900, FUTEX_WAIT, 2, NULLProcess 15427 detached在这个示例里进程在调用futex的时候被挂起了。顺带一说,在这个例子里调用futex挂起可能有很多的原因(Futex是Linux的一种线程同步原语)。上述场景是个正常工作等待处理请求的apache子进程。
“strace -p”可以让你省去很多猜测,不需要重新编译,重启应用打log就能够找到问题。
4)统计程序的调用时间
要对程序进行性能分析往往需要重新编译程序,并打开跟踪选项。用strace可以很容易的附加到进程上查看实时的时间消耗。
如下:
root@dev:~# strace -c -p 11084Process 11084 attached - interrupt to quitProcess 11084 detached% time seconds usecs/call calls errors syscall------ ----------- ----------- --------- --------- ----------------94.59 0.001014 48 21 select2.89 0.000031 1 21 getppid2.52 0.000027 1 21 time------ ----------- ----------- --------- --------- ----------------100.00 0.001072 63 totalroot@dev:~#启用strace -c -p命令后,在你按ctrl-c退出前程序的调用时间将会打印出来。
在上面的例子里。空闲的Postgres进程大部分时间都在安静的等待select()返回。在每个select()调用中调用getppid() 和time()。这是个标准的event loop方式。
也可以跟踪一次程序的开始和结束,比如下面的“ls”例子
root@dev:~# strace -c >/dev/null ls% time seconds usecs/call calls errors syscall------ ----------- ----------- --------- --------- ----------------23.62 0.000205 103 2 getdents6418.78 0.000163 15 11 1 open15.09 0.000131 19 7 read12.79 0.000111 7 16 old_mmap7.03 0.000061 6 11 close4.84 0.000042 11 4 munmap4.84 0.000042 11 4 mmap24.03 0.000035 6 6 6 access3.80 0.000033 3 11 fstat641.38 0.000012 3 4 brk0.92 0.000008 3 3 3 ioctl0.69 0.000006 6 1 uname0.58 0.000005 5 1 set_thread_area0.35 0.000003 3 1 write0.35 0.000003 3 1 rt_sigaction0.35 0.000003 3 1 fcntl640.23 0.000002 2 1 getrlimit0.23 0.000002 2 1 set_tid_address0.12 0.000001 1 1 rt_sigprocmask------ ----------- ----------- --------- --------- ----------------100.00 0.000868 87 10 total如你所料,这个命令花费两个调用用于读取目录实体。
5)为什么连不上服务器?
调试某些进程为什么连不上远程的服务器是个很让人头疼的事情。DNS可能挂了,连接可能断了,服务器可能返回什么无法识别的东西。。。你可以使用tcpdump去分析这些问题,tcpdump是个很棒的工具,但是很多情况下,strace可以给你提供更简洁的信息。如果你的程序里有很多进程连接到某台服务器,用tcpdump来处理就是很蛋疼的事情,因为你会看到太多的信息,没法抓到重点。
以下是一个跟踪“nc”命令连接到www.news.com的80端口的例子:
$ strace -e poll,select,connect,recvfrom,sendto nc www.news.com 80poll([{fd=3, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1poll([{fd=3, events=POLLIN, revents=POLLIN}], 1, 5000) = 1poll([{fd=3, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1poll([{fd=3, events=POLLIN, revents=POLLIN}], 1, 5000) = 1poll([{fd=3, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1poll([{fd=3, events=POLLIN, revents=POLLIN}], 1, 5000) = 1select(4, NULL, [3], NULL, NULL) = 1 (out [3])这个连接尝试访问了/var/run/nscd/socket,这意味着nc命令首先尝试连接NSCD服务(名称缓存守护进程,通常用于NIS,YP,LDAP中的名字查找)。在上面的例子里执行失败。
添加read和write到跟踪的系统调用列表里,连接上后敲入test,你会看到如下信息:
poll([{fd=3, events=POLLIN, revents=POLLIN}, {fd=0, events=POLLIN}], 2, -1) = 1你可以看到,程序从标准输入中读取到“test”,写到网络连接中,然后调用poll()等待响应,读取响应然后写入到标准输出中。