(转)Linux利器 strace

原文:http://www.perfgeeks.com/?p=501

今天才发现strace是个好东西呀

strace常用来跟踪进程执行时的系统调用和所接收的信号。在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。

strace使用参数

-p跟踪指定的进程

-f跟踪由fork子进程系统调用

-F尝试跟踪vfork子进程系统调吸入,与-f同时出现时,vfork不被跟踪

-ofilename默认strace将结果输出到stdout。通过-o可以将输出写入到filename文件中

-ff常与-o选项一起使用,不同进程(子进程)产生的系统调用输出到filename.PID文件

-r打印每一个系统调用的相对时间

-t在输出中的每一行前加上时间信息。-tt时间确定到微秒级。还可以使用-ttt打印相对时间

-v输出所有系统调用。默认情况下,一些频繁调用的系统调用不会输出

-s指定每一行输出字符串的长度,默认是32。文件名一直全部输出

-c统计每种系统调用所执行的时间,调用次数,出错次数。

-eexpr输出过滤器,通过表达式,可以过滤出掉你不想要输出

应用场景

#1.跟踪你的web服务器系统调用

系统调用优化,也是web性能优化的一个较为重要的方向,尤其是在I/O密集型web应用的情况。我们这里的测试环境是CentOS5.4+Nginx+FastCGI。

<?php

//file:hello.php

define('DOCUMENT_ROOT',dirname(__FILE__));

include("hello.inc");

include("./hello.inc");

include(DOCUMENT_ROOT."/hello.inc");

?>

#strace-f-F-ostrace_nginxstrace/wwwchroot/nginx/sbin/nginx-c/wwwchroot/nginx/nginx.conf

...(有部分不重要的数据影响排版,在这里使用...代替)

//--接受来自客户端的http请求

4165recv(16,"GET/hello.phpHTTP/1.1\r\nHost:f"...,32768,0)=391

4165epoll_ctl(9,EPOLL_CTL_MOD,16,{EPOLLIN|EPOLLOUT|EPOLLET,{u32=3081162952,u64=698098541354471624}})=0

//--进行DNS查找

4165getsockname(16,{sa_family=AF_INET,sin_port=htons(80),sin_addr=inet_addr("222.73.211.214")},[16])=0

//--新建一个socket,连接Fast-CGI,端口号为9000

4165socket(PF_INET,SOCK_STREAM,IPPROTO_IP)=17

4165ioctl(17,FIONBIO,[1])=0

4165epoll_ctl(9,EPOLL_CTL_ADD,17,{EPOLLIN|EPOLLOUT|EPOLLET,{u32=3081163048,u64=697886249710965032}})=0

4165connect(17,{sa_family=AF_INET,sin_port=htons(9000),sin_addr=inet_addr("127.0.0.1")},16)=-1)

4165epoll_wait(9,{{EPOLLOUT,{u32=3081163048,u64=697886249710965032}},{...},5\

12,300000)=2

4165gettimeofday({1295420285,130967},NULL)=0

4165recv(16,0xbfdd7d8b,1,MSG_PEEK)=-1EAGAIN(Resourcetemporarilyunavailable)

4165getsockopt(17,SOL_SOCKET,SO_ERROR,[0],[4])=0

//--将用户http请求交给Fast-CGI

4165writev(17,[{"\1\1\0\1\0\10\0\0\0\1\0\0\0\0\0\0\1\4\0\1\3\30\0\0\21\7GATEWA"...,832}],1)=832

4165epoll_wait(9,{{EPOLLIN|EPOLLOUT,{u32=3081163048,u64=697886249710965032}}},512,300000)=1

4165gettimeofday({1295420285,131559},NULL)=0

//--接收Fast-CGI响应

4165recv(17,"\1\6\0\1\0V\2\0X-Powered-By:PHP/5.2.10"...,65536,0)=112

4165readv(17,[{"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,65424}],1)=0

4165mmap2(NULL,274432,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0)=0xb7514000

4165close(17)=0

4165munmap(0xb7514000,274432)=0

//--响应客户端http请求,即http响应

4165writev(16,[{"HTTP/1.1200OK\r\nServer:nginx/0"...,228},{"22\r\n",4},...,5)=273

4165write(5,"116.66.34.82--[19/Jan/2011:14"...,191)=191

4165setsockopt(16,SOL_TCP,TCP_NODELAY,[1],4)=0

4165recv(16,0x9b024e8,32768,0)=-1EAGAIN(Resourcetemporarilyunavailable)

...

通过这些,我们只能够大概地了解,Nginx这里启用了epoll。同时,还可以了解到Nginx和Fast-CGI底层是如何运作的。奇怪,hello.php文件中有三个inclue,即加载了三次文件,这里没有看到相应的i/o逻辑操作,是为什么呢?这是因为,Nginx并没解析处理PHP脚本,而是交给Fast-CGI去做这部事情了。

#strace-f-F-ophp-cgi-strace/wwwchroot/php/bin/php-cgi--fpm-config/wwwchroot/php/etc/php-fpm.conf

//--接收来自Nginx发出的请求

4510<...acceptresumed>{sa_family=AF_INET,sin_port=htons(35983),sin_addr=inet_addr("127.0.0.1")},[16])=3

4510clock_gettime(CLOCK_MONOTONIC,{22638545,869965681})=0

4510poll([{fd=3,events=POLLIN}],1,5000)=1([{fd=3,revents=POLLIN}])

4510read(3,"\1\1\0\1\0\10\0\0",=8

4510read(3,"\0\1\0\0\0\0\0\0",=8

4510read(3,"\1\4\0\1\0035\3\0",=8

4510read(3,"\21\7GATEWAY_INTERFACECGI/1.1\17\5SERV"...,824)=824

4510read(3,"\1\4\0\1\0\0\0\0",=8

4510time(NULL)=1295425149

//--加载请求资源文件hello.php

4510lstat64("/var",{st_mode=S_IFDIR|0755,st_size=4096,...})=0

4510lstat64("/var/www",{st_mode=S_IFDIR|0755,st_size=4096,...})=0

4510lstat64("/var/www/ep",{st_mode=S_IFDIR|0755,st_size=4096,...})=0

4510lstat64("/var/www/ep/hello.php",{st_mode=S_IFREG|0644,st_size=119,...})=0

4510clock_gettime(CLOCK_MONOTONIC,{22638545,870893872})=0

4510setitimer(ITIMER_PROF,{it_interval={0,0},it_value={60,0}},NULL)=0

4510rt_sigaction(SIGPROF,{0x835c120,[PROF],SA_RESTART},{SIG_DFL,[],0},=0

4510rt_sigprocmask(SIG_UNBLOCK,[PROF],NULL,=0

4510time(NULL)=1295425149

4510open("/var/www/ep/hello.php",O_RDONLY)=4

4510fstat64(4,{st_mode=S_IFREG|0644,st_size=119,...})=0

4510time(NULL)=1295425149

4510chdir("/var/www/ep")=0

4510fstat64(4,{st_mode=S_IFREG|0644,st_size=119,...})=0

4510mmap2(NULL,4096,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0)=0xb7fe7000

4510read(4,"\n",8192)=29

4510read(4,"",8192)=0

4510read(4,"",8192)=0

4510close(4)=0

//--加载hello.inc,对应php代码include'./hello.inc'

4510getcwd("/var/www/ep"...,4096)=12

4510time(NULL)=1295425149

4510open("/var/www/ep/hello.inc",O_RDONLY)=4

4510fstat64(4,{st_mode=S_IFREG|0644,st_size=29,...})=0

4510read(4,"\n",8192)=29

4510read(4,"",8192)=0

4510read(4,"",8192)=0

4510close(4)=0

4510time(NULL)=1295425149

//--加载hello.inc,对应php代码includeDOCUMENT_ROOT.'/hello.inc'

4510lstat64("/var",{st_mode=S_IFDIR|0755,st_size=4096,...})=0

4510lstat64("/var/www",{st_mode=S_IFDIR|0755,st_size=4096,...})=0

4510lstat64("/var/www/ep",{st_mode=S_IFDIR|0755,st_size=4096,...})=0

4510lstat64("/var/www/ep/hello.inc",{st_mode=S_IFREG|0644,st_size=29,...})=0

4510open("/var/www/ep/hello.inc",O_RDONLY)=4

4510fstat64(4,{st_mode=S_IFREG|0644,st_size=29,...})=0

4510read(4,"\n",8192)=29

4510read(4,"",8192)=0

4510read(4,"",8192)=0

4510close(4)=0

//--将响结果输出给Nginx,并且关闭连接

4510write(3,"\1\6\0\1\0V\2\0X-Powered-By:PHP/5.2.10"...,96)=96

4510setitimer(ITIMER_PROF,{it_interval={0,0},it_value={0,0}},NULL)=0

4510write(3,"\1\3\0\1\0\10\0\0\0\0\0\0\0ere",16)=16

4510shutdown(3,1/*send*/)=0

4510recv(3,"\1\5\0\1\0\0\0\0",8,0)=8

4510recv(3,"",8,0)=0

4510close(3)=0

通过跟踪php-cgi,我们可以知道,相较与其它二种方法include‘./hello.inc’的性能是最高的。这里看到strace输出都被截断了,如果你需要看到更多的输出,可以通过-s选项,让strace输出更多内容。

当你发现某个http请求造成CPU占用效骤然升高,你可以通过strace跟踪查找问题的根源。同时,你也可以通过strace-c统计监控你的优化是否生效

#2.MySQL执行语句列表

当发生个http请求的时候,很多时候希望得到这个http请求发生了多少次数据库SELECT操作,是否在同一个mysqlconnection连接里面完成。这里以访问本页为例子,通过strace来跟踪这些MySQLSELECT查询语句。

//-9514是mysqld的进程号,为了看到整条SQL语句,我们通过-s1024希望输出更多内容

#strace-f-F-ff-ostrace-mysqld-s1024-p9514

#find.-name"strace-mysqld*"-typef-print|xargsgrep-n"SELECT.*FROMwp_"

./strace-mysqld.19203:64:

read(19,"\3SELECToption_name,option_valueFROMwp_optionsWHEREautoload='yes'",72)=72

./strace-mysqld.19203:165:

read(19,"\3SELECT*FROMwp_usersWHEREuser_login='admin'",50)=50

./strace-mysqld.19203:184:

read(19,"\3SELECTmeta_key,meta_valueFROMwp_usermetaWHEREuser_id=1",63)=63

./strace-mysqld.19203:295:

read(19,"\3SELECToption_valueFROMwp_optionsWHEREoption_name='rewrite_rules'LIMIT1",80)=80

./strace-mysqld.19203:311:

read(19,"\3SELECTwp_posts.*FROMwp_postsWHERE1=1ANDwp_posts.ID=501

ANDwp_posts.post_type='post'ORDERBYwp_posts.post_dateDESC",136)=136

...(这里省去了一些)

相关推荐