调试跟踪利器---strace

通过这篇文章你会学习到strace的用法,strace可以帮助你高效地定位进程中的一些错误,关于strace的用处有很多,可以自行发掘

前面我们讲解了gdb调试程序,这篇文章介绍另一个调试跟踪工具strace,同样你可以在linux下执行man strace查看帮助信息

(一)starce是什么

我们直接看man打印的帮助信息

 strace - trace system calls and signals

根据上面的描述我们可以知道strace主要是跟踪系统的调用和信号的传递,其实我们还可以用它来监视用户进程和内核的交互,它能通过系统调用来侦测程序运行的详细过程

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

(二)strace如何使用
参数参数说明
-c统计每一系统调用的所执行的时间,次数和出错的次数等.
-d输出strace关于标准错误的调试信息.
-f跟踪由fork调用所产生的子进程.
-ff如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h输出简要的帮助信息.
-i输出系统调用的入口指针.
-q禁止输出关于脱离的消息.
-r打印出相对时间关于,每一个系统调用.
-t在输出中的每一行前加上时间信息.
-tt在输出中的每一行前加上时间信息,微秒级.
-ttt微秒级输出,以秒了表示时间.
-T显示每一调用所耗的时间.
-v输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V输出strace的版本信息.
-x以十六进制形式输出非标准字符串
-xx所有字符串以十六进制形式输出.

-e expr :指定一个表达式,用来控制如何跟踪,由于格式比较多我们单独拿出来

格式如下:

[qualifier=][!]value1[,value2]...

qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.

-e expr参数设置
-e trace=set只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.
-e trace=file只跟踪有关文件操作的系统调用.
-e trace=process只跟踪有关进程控制的系统调用.
-e trace=network跟踪与网络有关的所有系统调用.
-e strace=signal跟踪所有与系统信号有关的 系统调用
-e trace=ipc跟踪所有与进程通讯有关的系统调用
-e abbrev=set设定 strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
-e raw=set将指 定的系统调用的参数以十六进制显示.
-e signal=set指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
-e read=set输出从指定文件中读出 的数据.例如:
-o filename将strace的输出写入文件filename
-p pid跟踪指定的进程pid.
-s strsize指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
-u username以username 的UID和GID执行被跟踪的命令

示例:
(1)根据指定pid进程跟踪

strace -o output.txt -T -tt -e trace=all -p 28979

上面的含义是 跟踪28979进程的所有系统调用(-e trace=all),并统计系统调用的花费时间,以及开始时间(并以可视化的时分秒格式显示),最后将记录结果存在output.txt文件里面。
(2)根据进程名跟踪

strace -o output.txt -T -tt -e trace=all  ./a.out
(三)strace的一个分析实例

test.c

#include <stdio.h>
#include <unistd.h>

int main(int argc,char * argv[])
{
   char buff[256]={0};
   FILE* file=NULL;
   file=fopen(argv[1],"r");
   if(file == NULL)
   {
     printf("fopen error\n");
//     return -1;
   }
   fread(buff,sizeof(buff),1,file);
   printf("buff=%s\n",buff);
   return 0;
}

我们执行:

gcc test.c -o test 
./test  

上面我们执行时不输入任何参数,肯定会报段错误

fopen error
段错误 (核心已转储)

我们这里使用strace查看信息:

root@lvirtual-machine:~/test# strace  -T -tt -e trace=all  ./test
22:32:53.349314 execve("./test", ["./test"], [/* 60 vars */]) = 0 <0.000440>
22:32:53.350129 brk(NULL)               = 0xb3f000 <0.000097>
22:32:53.350417 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000094>
22:32:53.350769 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) <0.000062>
22:32:53.351014 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 <0.000127>
22:32:53.351316 fstat(3, {st_mode=S_IFREG|0644, st_size=96373, ...}) = 0 <0.000105>
22:32:53.351585 mmap(NULL, 96373, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0bec216000 <0.000065>
22:32:53.351793 close(3)                = 0 <0.000052>
22:32:53.352046 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000068>
22:32:53.352313 open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 <0.000106>
22:32:53.352597 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\t\2\0\0\0\0\0"..., 832) = 832 <0.000061>
22:32:53.352789 fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0 <0.000110>
22:32:53.353092 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0bec215000 <0.000122>
22:32:53.353409 mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0bebc3f000 <0.000117>
22:32:53.353629 mprotect(0x7f0bebdff000, 2097152, PROT_NONE) = 0 <0.000115>
22:32:53.353841 mmap(0x7f0bebfff000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f0bebfff000 <0.000110>
22:32:53.354073 mmap(0x7f0bec005000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0bec005000 <0.000060>
22:32:53.354297 close(3)                = 0 <0.000035>
22:32:53.354556 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0bec214000 <0.000060>
22:32:53.354736 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0bec213000 <0.000045>
22:32:53.354888 arch_prctl(ARCH_SET_FS, 0x7f0bec214700) = 0 <0.000040>
22:32:53.355125 mprotect(0x7f0bebfff000, 16384, PROT_READ) = 0 <0.000051>
22:32:53.355288 mprotect(0x600000, 4096, PROT_READ) = 0 <0.000050>
22:32:53.355487 mprotect(0x7f0bec22e000, 4096, PROT_READ) = 0 <0.000085>
22:32:53.355724 munmap(0x7f0bec216000, 96373) = 0 <0.000094>
22:32:53.356015 brk(NULL)               = 0xb3f000 <0.000072>
22:32:53.356203 brk(0xb60000)           = 0xb60000 <0.000075>
22:32:53.356426 open(NULL, O_RDONLY)    = -1 EFAULT (Bad address) <0.000078>
22:32:53.356664 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 11), ...}) = 0 <0.000065>
22:32:53.356879 write(1, "fopen error\n", 12fopen error
) = 12 <0.000065>
22:32:53.357116 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} ---
22:32:53.542212 +++ killed by SIGSEGV (core dumped) +++
段错误 (核心已转储)

上面输出的信息非常多,但是我们可以找到我们需要的

open(NULL, O_RDONLY)    = -1 EFAULT (Bad address) 

我们使用starce也就是根据这些系统调用去过滤出我们需要的信息


当我们发现程序运行异常时,我们可以使用strace来跟踪其系统调用,看看这些系统调用有没有异常,进而找到异常的原因。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值