当前位置: 首页 > 科技观察

使用Strace进行故障排除的五种简单方法

时间:2023-03-22 10:10:01 科技观察

我经常感到惊讶的是,很少有人知道他们可以使用strace做什么。它始终是我最先使用的调试工具之一,因为它在我运行的Linux系统上普遍可用,可用于解决范围如此广泛的问题。什么是跟踪?Strace是一个非常简单的跟踪系统调用执行的工具。在其最简单的形式中,它可以从头到尾跟踪二进制文件的执行,并输出一行文本,其中包含系统调用的名称、参数以及进程生命周期内每个系统调用的返回值。但它可以做的更多:它可以根据特定的系统调用或系统调用组进行过滤它可以通过计算特定系统调用的使用次数、花费的时间以及成功和错误的次数来分析系统调用的使用情况。它跟踪发送到进程的信号。它可以通过pid附加到任何正在运行的进程。如果您使用过其他Unix系统,这类似于“truss”。另一个(更全面)是Sun的Dtrace。如何使用这只是触及表面,没有特定的重要性顺序:1)找出程序在启动时读取了哪些配置文件是否曾经试图弄清楚为什么某些程序不读取您认为它们应该读取的配置文件?不得不与自定义编译的或特定于发行版的二进制文件搏斗,这些二进制文件从您认为是“错误”的位置读取它们的配置?天真的方法:所以这个版本的PHP从/usr/local/lib/php.ini读取php.ini(但它首先尝试/usr/local/bin)。如果我只关心特定的系统调用,则方法会更复杂:同样的方法适用于许多其他事情。在不同路径上安装了多个版本的库,想知道实际加载的是哪个版本?等待。$stracephp2>&1|grepphp.iniopen("/usr/local/bin/php.ini",O_RDONLY)=-1ENOENT(没有那个文件或目录)open("/usr/local/lib/php.ini",O_RDONLY)=4lstat64("/usr/local/lib/php.ini",{st_mode=S_IFLNK|0777,st_size=27,...})=0readlink("/usr/local/lib/php.ini","/usr/local/Zend/etc/php.ini",4096)=27lstat64("/usr/local/Zend/etc/php.ini",{st_mode=S_IFREG|0664,st_size=40971,...})=0$strace-e打开php2>&1|grepphp.iniopen("/usr/local/bin/php.ini",O_RDONLY)=-1ENOENT(没有那个文件或目录)open("/usr/local/lib/php.ini",O_RDONLY)=42)为什么这个程序不能打开我的文件?有没有遇到过一个程序默默地拒绝读取它没有读取权限的文件,经过多年的咒骂才发现是因为您认为它实际上没有找到该文件?好吧,您已经知道该怎么做:查找失败的open()或access()系统调用$strace-eopen,access2>&1|grepyour-filename3)这个进程现在在做什么?曾经有过一个进程突然占用大量CPU吗?还是一个似乎悬而未决的过程?然后你找到pid,然后做:啊。所以在这种情况下它挂在对futex()的调用上。顺便说一句,在这种情况下,它并没有告诉我们那么多——挂在futex上可能是由很多事情引起的(futex是Linux内核中的一种锁定机制)。以上来自正在等待接收请求的工作但空闲的Apache子进程。但是“strace-p”非常有用,因为它消除了很多猜测,而且通常不需要重新启动应用程序(甚至重新编译)并进行更广泛的日志记录。root@dev:~#strace-p15427Process15427attached-interrupttoquitfutex(0x402f4900,FUTEX_WAIT,2,NULLProcess15427detached4)什么是花时间?您始终可以在打开分析的情况下重新编译应用程序并获得准确的信息,尤其是关于您自己的代码的哪些部分需要时间,这是您应该做的。但通常情况下,能够快速将strace附加到一个进程以查看它当前花费了多长时间(尤其是诊断问题)是非常有用的。这是90%的CPU使用率,因为它实际上在做真正的工作,或者一些失控的事情。这是您要做的:在使用-c-p启动strace之后,您只需等待所需的时间,然后使用ctrl-c退出。Strace将吐出如上所述的分析数据。在这种情况下,它是一个空闲的Postgres“postmaster”进程,大部分时间都在select()中安静地等待。在这种情况下,它会在每次select()调用之间调用getppid()和time(),这是一个相当标准的事件循环。您也可以“从上到下”运行此命令,此处使用“ls”:几乎如您所料,它大部分时间都花在两次读取目录条目的调用上(只有两次,因为它在一个小目录中运行在)。root@dev:~#strace-c-p11084Process11084attached-interrupttoquitProcess11084detached%timesecondsusecs/callcallserrorssyscall------------------------------------------------------------94.590.0010144821选择2.890.000031121getppid2.520.000027121时间------------------------------------------------------------100.000.00107263totalroot@dev:~#root@dev:~#strace-c>/dev/nullls%timesecondsusecs/call调用错误系统调用----------------------------------------------------------------23.620.0002051032getdents6418.780.00016315111打开15.090.000131197读取12.790.000111716old_mmap7.030.000061611close4.840.000042114munmap4.840.000042114mmap24.030.000035666access3.800.000033311fstat641.380.00001234brk0.920.000008333ioctl0.690.00000661uname0.580.00000551set_thread_area0.350.00000331write0.350.00000331rt_sigaction0.350.00000331fcntl640.230.00000221getrlimit0.230.00000221set_tid_address0.120.00000111rt_sigprocmask------------------------------------------------------------100.000.0008688710吨otal5)为什么****我无法连接到这个服务器?调试为什么某些进程没有连接到远程服务器可能会非常令人沮丧;DNS可能会失败,连接可能会挂起,服务器可能会发送一些意外的东西等等。您可以使用tcpdump进行很多分析,这也是一个非常好的工具,但是很多时候strace会让您少说话,仅仅是因为它只会返回数据与“您的”进程进行的系统调用相关。例如,如果您试图弄清楚连接到同一数据库服务器的数百个正在运行的进程中的一个正在做什么(选择正确的tcpdump连接是一场噩梦),strace会让生活变得更加轻松。下面是“nc”跟踪端口80上与www.news.com的连接的示例,没有任何问题:那么这里发生了什么?注意到连接到/var/run/nscd/socket的尝试了吗?这意味着nc首先尝试连接到NSCD-名称服务缓存守护进程-通常用于依赖NIS、YP、LDAP或类似目录协议进行名称查找的设置。在这种情况下,连接失败。然后它移动到DNS(DNS是端口53,所以下面连接中的“sin_port=htons(53)”。您可以看到它随后执行“sendto()”调用,发送包含www.news.com数据包的DNS.然后,它读回一个数据包。不知什么原因,它尝试了三次,最后一次请求略有不同。在这种情况下,我最好的猜测是www.news.com是一个CNAME(“别名”),多次请求可能只是nc处理它的人工制品。最后,它最终向它找到的IP发出一个connect()。注意它返回EINPROGRESS。这意味着连接是非阻塞的-nc想要继续处理。然后,它调用select(),当连接成功时它会成功。尝试将“read”和“write”添加到提供给strace的系统调用列表中,并在连接时输入一个字符串,你会得到这样的结果:从stdin读取“test”+换行符,将其写回网络连接,然后调用poll()等待回复,从网络读取回复k连接并将其写入标准输出。一切似乎都运行良好。$strace-epoll,select,connect,recvfrom,sendtoncwww.news.com80sendto(3,"\\24\\0\\0\\0\\26\\0\\1\\3\\255\\373NH\\0\\0\\0\\0\\0\\0\\0\\0",20,0,{sa_family=AF_NETLINK,pid=0,groups=00000000},12)=20connect(3,{sa_family=AF_FILE,path="/var/run/nscd/socket"},110)=-1ENOENT(没有那个文件或目录)connect(3,{sa_family=AF_FILE,path="/var/run/nscd/socket"},110)=-1ENOENT(Nosuchfileordirectory)connect(3,{sa_family=AF_INET,sin_port=htons(53),sin_addr=inet_addr("62.30.112.39")},28)=0poll([{fd=3,events=POLLOUT,revents=POLLOUT}],1,0)=1sendto(3,"\\213\\321\\1\\0\\0\\1\\0\\0\\0\\0\\0\\0\\3www\\4news\\3com\\0\\0\\34\\0\\1",30,MSG_NOSIGNAL,NULL,0)=30poll([{fd=3,events=POLLIN,revents=POLLIN}],1,5000)=1recvfrom(3,"\\213\\321\\201\\200\\0\\1\\0\\1\\0\\1\\0\\0\\3www\\4news\\3com\\0\\0\\34\\0\\1\\300\\f"...,1024,0,{sa_family=AF_INET,sin_port=htons(53),sin_addr=inet_addr("62.30.112.39")},[16])=153connect(3,{sa_family=AF_INET,sin_port=htons(53),sin_addr=inet_addr("62.30.112.39")},28)=0poll([{fd=3,events=POLLOUT,revents=POLLOUT}],1,0)=1sendto(3,"k\\374\\1\\0\\0\\1\\0\\0\\0\\0\\0\\0\\3www\\4news\\3com\\0\\0\\1\\0\\1",30,MSG_NOSIGNAL,NULL,0)=30poll([{fd=3,events=POLLIN,revents=POLLIN}],1,5000)=1recvfrom(3,"k\\374\\201\\200\\0\\1\\0\\2\\0\\0\\0\\0\\3www\\4news\\3com\\0\\0\\1\\0\\1\\300\\f"...,1024,0,{sa_family=AF_INET,sin_port=htons(53),sin_addr=inet_addr("62.30.112.39")},[16])=106connect(3,{sa_family=AF_INET,sin_port=htons(53),sin_addr=inet_addr("62.30.112.39")},28)=0poll([{fd=3,events=POLLOUT,revents=POLLOUT}],1,0)=1sendto(3,"\\\\\\2\\1\\0\\0\\1\\0\\0\\0\\0\\0\\0\\3www\\4news\\3com\\0\\0\\1\\0\\1",30,MSG_NOSIGNAL,NULL,0)=30poll([{fd=3,events=POLLIN,revents=POLLIN}],1,5000)=1recvfrom(3,"\\\\\\2\\201\\200\\0\\1\\0\\2\\0\\0\\0\\0\\3www\\4news\\3com\\0\\0\\1\\0\\1\\300\\f"...,1024,0,{sa_family=AF_INET,sin_port=htons(53),sin_addr=inet_addr("62.30.112.39")},[16])=106connect(3,{sa_family=AF_INET,sin_port=htons(80),sin_addr=inet_addr("216.239.122.102")},16)=-1EINPROGRESS(操作正在进行中)select(4,NULL,[3],NULL,NULL)=1(out[3])read(0,"test\\n",1024)=5write(3,"test\\n",5)=5poll([{fd=3,events=POLLIN,revents=POLLIN},{fd=0,events=POLLIN}],2,-1)=1read(3,"\"-//IETF//"...,1024)=216write(1,"\"-//IETF//"...,216)=216

最新推荐
猜你喜欢