用strace排查故障的5种简单方法(每日一译)

原文链接: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
open("/usr/local/bin/php.ini", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/lib/php.ini", O_RDONLY) = 4
lstat64("/usr/local/lib/php.ini", {st_mode=S_IFLNK|0777, st_size=27, ...}) = 0
readlink("/usr/local/lib/php.ini", "/usr/local/Zend/etc/php.ini", 4096) = 27
lstat64("/usr/local/Zend/etc/php.ini", {st_mode=S_IFREG|0664, st_size=40971, ...}) = 0
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
open("/usr/local/bin/php.ini", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/local/lib/php.ini", O_RDONLY) = 4

如果安装多个版本的程序库,想搞清楚自己的程序加载的是哪个也可以如法炮制~。

2)为什么程序打不开这个文件?

是否有遇到过读取文件的时候被拒绝呢,你可以试试下面的命令:

$ strace -e open,access 2>&1 | grep your-filename

查看open()和access()系统调用是否有异常

3)进程现在在做啥?

是否有遇到过进程突然cpu占用率很高?又或者进程莫名其妙被挂起的情况?

找到这个进程的pid,然后执行下列命令:

root@dev:~# strace -p 15427
Process 15427 attached - interrupt to quit
futex(0x402f4900, FUTEX_WAIT, 2, NULL 
Process 15427 detached

在这个示例里进程在调用futex的时候被挂起了。顺带一说,在这个例子里调用futex挂起可能有很多的原因(Futex是Linux的一种线程同步原语)。上述场景是个正常工作等待处理请求的apache子进程。

“strace -p”可以让你省去很多猜测,不需要重新编译,重启应用打log就能够找到问题。

4)统计程序的调用时间

要对程序进行性能分析往往需要重新编译程序,并打开跟踪选项。用strace可以很容易的附加到进程上查看实时的时间消耗。

如下:

root@dev:~# strace -c -p 11084
Process 11084 attached - interrupt to quit
Process 11084 detached
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 94.59    0.001014          48        21           select
  2.89    0.000031           1        21           getppid
  2.52    0.000027           1        21           time
------ ----------- ----------- --------- --------- ----------------
100.00    0.001072                    63           total
root@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           getdents64
 18.78    0.000163          15        11         1 open
 15.09    0.000131          19         7           read
 12.79    0.000111           7        16           old_mmap
  7.03    0.000061           6        11           close
  4.84    0.000042          11         4           munmap
  4.84    0.000042          11         4           mmap2
  4.03    0.000035           6         6         6 access
  3.80    0.000033           3        11           fstat64
  1.38    0.000012           3         4           brk
  0.92    0.000008           3         3         3 ioctl
  0.69    0.000006           6         1           uname
  0.58    0.000005           5         1           set_thread_area
  0.35    0.000003           3         1           write
  0.35    0.000003           3         1           rt_sigaction
  0.35    0.000003           3         1           fcntl64
  0.23    0.000002           2         1           getrlimit
  0.23    0.000002           2         1           set_tid_address
  0.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 80
sendto(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) = 20
connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("62.30.112.39")}, 28) = 0
poll([{fd=3, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1
sendto(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) = 30
poll([{fd=3, events=POLLIN, revents=POLLIN}], 1, 5000) = 1
recvfrom(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]) = 153
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("62.30.112.39")}, 28) = 0
poll([{fd=3, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1
sendto(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) = 30
poll([{fd=3, events=POLLIN, revents=POLLIN}], 1, 5000) = 1
recvfrom(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]) = 106
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("62.30.112.39")}, 28) = 0
poll([{fd=3, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1
sendto(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) = 30
poll([{fd=3, events=POLLIN, revents=POLLIN}], 1, 5000) = 1
recvfrom(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]) = 106
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("216.239.122.102")}, 16) = -1 EINPROGRESS (Operation now in progress)
select(4, NULL, [3], NULL, NULL)        = 1 (out [3])

这个连接尝试访问了/var/run/nscd/socket,这意味着nc命令首先尝试连接NSCD服务(名称缓存守护进程,通常用于NIS,YP,LDAP中的名字查找)。在上面的例子里执行失败。

nc命令转而使用DNS(DNS的端口是53,"sin_port=htons(53)" )。可以看到程序调用”sendto()"发送包含www.news.com信息的DNS数据包。因为某些原因,尝试了3次。为什么会这样呢?我猜测可能www.news.com是CNAME ,最后,程序连接从DNS获取到的IP,注意这里,返回EINPROGRESS。这意味这这个连接是非堵塞的。连接成功后nc调用select。

添加read和write到跟踪的系统调用列表里,连接上后敲入test,你会看到如下信息:

read(0, "test\\n", 1024)                 = 5
write(3, "test\\n", 5)                   = 5
poll([{fd=3, events=POLLIN, revents=POLLIN}, {fd=0, events=POLLIN}], 2, -1) = 1
read(3, "

你可以看到,程序从标准输入中读取到“test”,写到网络连接中,然后调用poll()等待响应,读取响应然后写入到标准输出中。

时间: 2024-09-16 04:40:49

用strace排查故障的5种简单方法(每日一译)的相关文章

调整优化您的LAMP应用程序的5种简单方法

简介 Wikipedia.Facebook 和 Yahoo! 等主要 web 属性使用 LAMP 架构来为每天数百万的请求提供服务,而 Wordpress.Joomla.Drupal 和 SugarCRM 等 web 应用程序软件使用其架构来让组织轻松部署基于 web 的应用程序. 该架构的优势在于其简单性.而 .NET 这样的堆栈和 Java 技术可能使用大量硬件.昂贵的软件栈和复杂的性能调优,LAMP 堆栈可以运行于商品硬件之上,使用开源软件栈.由于软件栈是一个松散的组件集,而非一个整体堆栈

ps怎么去水印? PS去水印的三种简单方法

  ps怎么去水印?  PS去水印的三种简单方法          方法1:用内容感知移动工具去水印 打开素材图片[ctrl+o],如下图所示. 复制图层[ctrl+j],如下图所示. 单击工具箱中的内容感知移动工具,选中图片中的水印,如下图所示. 往下移,水印不见了,如下图所示. 方法2:用污点修复画笔工具去水印 打开素材图片[ctrl+o],复制图层[ctrl+j],如下图所示.

WiFi网络提速的6种简单方法

  WiFi网络提速的6种简单方法.据<财富>网站报道,不给力的WiFi连接使得无线上网给人的感觉像是回到了拨号时代.WiFi顾问.Sniff WiFi博主本·米勒(Ben Miller)表示,有些简单方法能给家中的互联网连接提速,"去朋友家中时我最喜欢的事情之一是帮助他们解决WiFi路由器存在的问题". <财富>表示,以下是米勒经常用来为WiFi网络提速的6种简单方法: 1.升级过时的路由器 并非所有人都愿意花钱解决WiFi网速慢的问题,但许多人都需要这么做.

用JQuery实现全选与取消的两种简单方法

 本篇文章主要是对JQuery实现全选与取消的两种简单方法进行了介绍,需要的朋友可以过来参考下,希望对大家有所帮助 用JQUERY实现全选和取消全选,没有js那么繁琐,而且支持更多浏览器.  代码如下: <mce:script type="text/javascript"><!--   $(function() {       $("#checkall").click(function() {           $("input[@na

Java之递归求和的两种简单方法(推荐)_java

方法一: package com.smbea.demo; public class Student { private int sum = 0; /** * 递归求和 * @param num */ public void sum(int num) { this.sum += num--; if(0 < num){ sum(num); } else { System.out.println("sum = " + sum); } } } 方法二: package com.smbea

php解析xml 的四种简单方法(附实例)_php实例

XML处理是开发过程中经常遇到的,PHP对其也有很丰富的支持,本文只是对其中某几种解析技术做简要说明,包括:Xml parser, SimpleXML, XMLReader, DOMDocument. 1. XML Expat Parser: XML Parser使用Expat XML解析器.Expat是一种基于事件的解析器,它把XML文档视为一系列事件.当某个事件发生时,它调用一个指定的函数处理它.Expat是无验证的解析器,忽略任何链接到文档的DTD.但是,如果文档的形式不好,则会以一个错误

调整优化您的LAMP应用程序的5种简单方法_php技巧

简介 Wikipedia.Facebook 和 Yahoo! 等主要 web 属性使用 LAMP 架构来为每天数百万的请求提供服务,而 Wordpress.Joomla.Drupal 和 SugarCRM 等 web 应用程序软件使用其架构来让组织轻松部署基于 web 的应用程序. 该架构的优势在于其简单性.而 .NET 这样的堆栈和 Java 技术可能使用大量硬件.昂贵的软件栈和复杂的性能调优,LAMP 堆栈可以运行于商品硬件之上,使用开源软件栈.由于软件栈是一个松散的组件集,而非一个整体堆栈

php解析xml 的四种简单方法(附实例)

XML处理是开发过程中经常遇到的,PHP对其也有很丰富的支持,本文只是对其中某几种解析技术做简要说明,包括:Xml parser, SimpleXML, XMLReader, DOMDocument. 1. XML Expat Parser: XML Parser使用Expat XML解析器.Expat是一种基于事件的解析器,它把XML文档视为一系列事件.当某个事件发生时,它调用一个指定的函数处理它.Expat是无验证的解析器,忽略任何链接到文档的DTD.但是,如果文档的形式不好,则会以一个错误

实现非阻塞套接字的一种简单方法 使用 JSSE 和 NIO 实现非阻塞通信的一种快速方法

简介: 尽管 SSL 阻塞操作――当读写数据的时候套接字的访问被阻塞――与对应的非阻塞方式相比提供了更好的 I/O 错误通知,但是非阻塞操作允许调用的线程继续运行.本文中,作者同时就客户端和服务器端描述了如何使用Java Secure Socket Extensions (JSSE) 和 Java NIO (新 I/O)库创建非阻塞的安全连接,并且介绍了创建非阻塞套接字的传统方法,以及使用JSSE 和 NIO 的一种可选的(必需的)方法. 阻塞,还是非阻塞?这就是问题所在.无论在程序员的头脑中多