strace,truss,ltrace追踪系统情况

简介:

 以下内容转自

 http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316692.html 

 http://www.ibm.com/developerworks/cn/linux/l-tsl/

 https://linux.cn/article-3935-1.html

1、strace命令参数介绍

 strace常用来跟踪进程执行时的系统调用和所接收的信号。 在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通 过系统调用访问硬件设备。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 所有字符串以十六进制形式输出. 

-a column 

设置返回值的输出位置.默认 为40. 

-e expr 

指定一个表达式,用来控制如何跟踪.格式如下: 

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

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


qualifier是 trace.感叹号是否定符号.例如: 

-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open表示跟踪除了open以外的其他调用.有两个特殊的


符号 all 和 none. 

注意有些shell使用!来执行历史记录里的命令,所以要使用\\. 

-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 

输出从指定文件中读出 的数据.例如: 

-e read=3,5 

-e write=set 

输出写入到指定文件中的数据. 

-o filename 

将strace的输出写入文件filename 

-p pid 

跟踪指定的进程pid. 

-s strsize 

指定输出的字符串的最大长度.默认为32.文件名一直全部输出. 

-u username 

以username 的UID和GID执行被跟踪的命令


进程无法启动,软件运行速度突然变慢,程序的"Segment Fault"等等都是让每个Unix系统用户头痛的问题,下面介绍如何应对。

-f :除了跟踪当前进程外,还跟踪其子进程。

-o file :将输出信息写到文件file中,而不是显示到标准错误输出(stderr)。

-p pid :绑定到一个由pid对应的正在运行的进程。此参数常用来调试后台进程。


使用上述三个参数基本上就可以完成大多数调试任务了

2、strace,truss,ltrace命令参数使用案例。

   

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

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


采用strace看一下在启动 dcopserver时到底程序做了什么:

strace -f -F -o ~/dcop-strace.txt dcopserver

这里 -f -F选项告诉strace同时跟踪fork和vfork出来的进程,-o选项把所有strace输出写到~/dcop-strace.txt里 面,dcopserver是要启动和调试的程序。再次出现错误之后,我检查了错误输出文件dcop-strace.txt,文件里有很多 系统调用的记录。


truss -o ls.truss ls -al: 跟踪ls -al的运行,将输出信息写到文件/tmp/ls.truss中。

strace -f -o vim.strace vim: 跟踪vim及其子进程的运行,将输出信息写到文件vim.strace。

ltrace -p 234: 跟踪一个pid为234的已经在运行的进程。


三个调试工具的输出结果格式也很相似,以strace为例:


brk(0)                                  = 0x8062aa8

brk(0x8063000)                          = 0x8063000

mmap2(NULL, 4096, PROT_READ, MAP_PRIVATE, 3, 0x92f) = 0x40016000


每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。 truss、strace和ltrace


的工作原理大同小异,都是使用ptrace系统调用跟踪调试运行中的进程,详细原理不在本文讨论范围内,有兴趣可以参


考它们的源代码。



[root@test ~]# strace ls

execve("/bin/ls", ["ls"], [/* 31 vars */]) = 0

brk(0)                                  = 0x237c000

mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2411570000

access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)

open("/etc/ld.so.cache", O_RDONLY)      = 3

fstat(3, {st_mode=S_IFREG|0644, st_size=82420, ...}) = 0

mmap(NULL, 82420, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f241155b000

close(3)                                = 0

open("/lib64/libselinux.so.1", O_RDONLY) = 3

read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0PX\200\234>\0\0\0"..., 832) = 832

fstat(3, {st_mode=S_IFREG|0755, st_size=124624, ...}) = 0

mmap(0x3e9c800000, 2221912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3e9c800000

mprotect(0x3e9c81d000, 2093056, PROT_NONE) = 0



跟踪系统调用

strace命令的-e选项仅仅被用来展示特定的系统调用(例如,open,write等等)

让我们跟踪一下cat命令的‘open’系统调用。

  1. raghu@raghu-Linoxide ~ $ strace -e open cat dead.letter

strace统计概要

它包括系统调用的概要,执行时间,错误等等。使用-c选项能够以一种整洁的方式展示:


[root@test ~]# strace -c ls

anaconda-ks.cfg  ceshi  Documents  install.log Music   Public     Videos

ce1 Desktop  Downloads  install.log.syslog  Pictures  Templates

% time     seconds  usecs/call     calls    errors syscall

------ ----------- ----------- --------- --------- ----------------

100.00    0.000034          17         2           write

  0.00    0.000000           0         8           read

  0.00    0.000000           0        11           open

  0.00    0.000000           0        13           close

  0.00    0.000000           0        11           fstat

  0.00    0.000000           0        27           mmap

  0.00    0.000000           0        16           mprotect

  0.00    0.000000           0         2           munmap

  0.00    0.000000           0         3           brk

  0.00    0.000000           0         2           rt_sigaction

  0.00    0.000000           0         1           rt_sigprocmask

  0.00    0.000000           0         2           ioctl

  0.00    0.000000           0         1         1 access

  0.00    0.000000           0         1           execve

  0.00    0.000000           0         1           fcntl

  0.00    0.000000           0         2           getdents

  0.00    0.000000           0         1           getrlimit

  0.00    0.000000           0         1           statfs

  0.00    0.000000           0         1           arch_prctl

  0.00    0.000000           0         2         1 futex

  0.00    0.000000           0         1           set_tid_address

  0.00    0.000000           0         1           set_robust_list

------ ----------- ----------- --------- --------- ----------------

100.00    0.000034                   110         2 total

显示时间戳

[root@test ~]# strace -t ls

17:37:37 execve("/bin/ls", ["ls"], [/* 31 vars */]) = 0

17:37:37 brk(0)                         = 0x201e000

17:37:37 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd0334d4000

17:37:37 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)

17:37:37 open("/etc/ld.so.cache", O_RDONLY) = 3

17:37:37 fstat(3, {st_mode=S_IFREG|0644, st_size=82420, ...}) = 0

17:37:37 mmap(NULL, 82420, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd0334bf000

17:37:37 close(3)                       = 0

17:37:37 open("/lib64/libselinux.so.1", O_RDONLY) = 3

17:37:37 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0PX\200\234>\0\0\0"..., 832) = 832

17:37:37 fstat(3, {st_mode=S_IFREG|0755, st_size=124624, ...}) = 0

17:37:37 mmap(0x3e9c800000, 2221912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3e9c800000

17:37:37 mprotect(0x3e9c81d000, 2093056, PROT_NONE) = 0

17:37:37 mmap(0x3e9ca1c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c000) = 0x3e9ca1c000

17:37:37 mmap(0x3e9ca1e000, 1880, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3e9ca1e000

17:37:37 close(3)                       = 0

17:37:37 open("/lib64/librt.so.1", O_RDONLY) = 3


3、使用strace诊断问题

案例1


# whoami

root


假设因为某种原因在升 级glibc的过程中负责用户名和用户ID转换的库NSS丢失,我们可以通过把nss库改名来模拟这个


环境:


# mv /lib/libnss_files.so.2 /lib/libnss_files.so.2.backup 

# whoami

whoami: cannot find username for UID 0

这里你可以看到,运行whoami时出现了错误,ldd程序的输出不会提供有用的帮助:

# ldd /usr/bin/whoami

libc.so.6 => /lib/libc.so.6 (0x4001f000)

/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)


你只会看到whoami依赖Libc.so.6和ld-linux.so.2,它没有给出运行whoami所必须的其他库。这里时用strace跟踪 


whoami时的输出:


strace -o whoami-strace.txt whoami


open("/lib/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)

open("/lib/i686/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)

stat64("/lib/i686/mmx", 0xbffff190) = -1 ENOENT (No such file or directory) 

open("/lib/i686/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)

stat64("/lib/i686", 0xbffff190) = -1 ENOENT (No such file or directory)

open("/lib/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)

stat64("/lib/mmx", 0xbffff190) = -1 ENOENT (No such file or directory) 

open("/lib/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)

stat64("/lib", {st_mode=S_IFDIR|0755, st_size=2352, ...}) = 0

open("/usr/lib/i686/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)

stat64("/usr/lib/i686/mmx", 0xbffff190) = -1 ENOENT (No such file or directory) 

open("/usr/lib/i686/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)

复制代码


你可以发现在不同目录下面查找libnss.so.2的尝试,但是都失败了。如果没有strace这样的工具,很难发现这个错误


是由于缺少动态库造成的。现 在只需要找到libnss.so.2并把它放回到正确的位置就可以了。

限制strace只跟踪特定的系统调用

如果你已经知道你要找什么,你可以让strace只跟踪一些类型的系统调用。例如,你需要看看在configure脚本里面执行的程序,你需要监视的系统调 用就是execve。让strace只记录execve的调用用这个命令:

strace -f -o configure-strace.txt -e execve ./configure


案例2、


运行clint出现Segment Fault错误

操作系统:FreeBSD-5.2.1-release

clint是一个C++静态源代码分析工具,通过Ports安装好之后,运行:


# clint foo.cpp

Segmentation fault (core dumped)


在Unix系统中遇见"Segmentation Fault"就像在MS Windows中弹出"非法操作"对话框一样令人讨厌。OK,我们用truss


给clint"把把脉":


# truss -f -o clint.truss clint

Segmentation fault (core dumped)

# tail clint.truss

  739: read(0x6,0x806f000,0x1000)               = 4096 (0x1000)

  739: fstat(6,0xbfbfe4d0)                       = 0 (0x0)

  739: fcntl(0x6,0x3,0x0)                        = 4 (0x4)

  739: fcntl(0x6,0x4,0x0)                        = 0 (0x0)

  739: close(6)                                    = 0 (0x0)

  739: stat("/root/.clint/plugins",0xbfbfe680)   ERR#2 'No such file or directory'

SIGNAL 11

SIGNAL 11

Process stopped because of:  16

process exit, rval = 139


我们用truss跟踪clint的系统调用执行情况,并把结果输出到文件clint.truss,然后用tail查看最后几行。注意看clint执行的最后一条系统调用(倒数第五行): stat("/root/.clint/plugins",0xbfbfe680) ERR#2 'No such file or directory',问题就出在这里:clint找不到目录"/root/.clint/plugins",从而引发了段错误。怎样解决?很简单


: mkdir -p /root/.clint/plugins,不过这次运行clint还是会"Segmentation Fault"9。继续用truss跟踪,发现clint还需要这个目录"/root/.clint/plugins/python",建好这个目录后clint终于能够正常运行了。

案例三


vim启动速度明显变慢

操作系统:FreeBSD-5.2.1-release

vim版本为6.2.154,从命令行运行vim后,要等待近半分钟才能进入编辑界面,而且没有任何错误输出。仔细检查了.vimrc和所有的vim脚本都没有错误配置,在网上也找不到类似问题的解决办法,难不成要hacking source code?没有必要,用truss就能找到问题所在:


# truss -f -D -o vim.truss vim

这里-D参数的作用是:在每行输出前加上相对时间戳,即每执行一条系统调用所耗费的时间。我们只要关注哪些系统调


用耗费的时间比较长就可以了,用less仔细查看输出文件vim.truss,很快就找到了疑点:


735: 0.000021511 socket(0x2,0x1,0x0)       = 4 (0x4)

735: 0.000014248 setsockopt(0x4,0x6,0x1,0xbfbfe3c8,0x4) = 0 (0x0)

735: 0.000013688 setsockopt(0x4,0xffff,0x8,0xbfbfe2ec,0x4) = 0 (0x0)

735: 0.000203657 connect(0x4,{ AF_INET 10.57.18.27:6000 },16) ERR#61 'Connection refused'

735: 0.000017042 close(4)          = 0 (0x0)

735: 1.009366553 nanosleep(0xbfbfe468,0xbfbfe460) = 0 (0x0)

735: 0.000019556 socket(0x2,0x1,0x0)       = 4 (0x4)

735: 0.000013409 setsockopt(0x4,0x6,0x1,0xbfbfe3c8,0x4) = 0 (0x0)

735: 0.000013130 setsockopt(0x4,0xffff,0x8,0xbfbfe2ec,0x4) = 0 (0x0)

735: 0.000272102 connect(0x4,{ AF_INET 10.57.18.27:6000 },16) ERR#61 'Connection refused'

735: 0.000015924 close(4)          = 0 (0x0)

735: 1.009338338 nanosleep(0xbfbfe468,0xbfbfe460) = 0 (0x0)


vim试图连接10.57.18.27这台主机的6000端口(第四行的connect()),连接失败后,睡眠一秒钟继续重试(第6行的nanosleep())。

以上片断循环出现了十几次,每次都要耗费一秒多钟的时间,这就是vim明显变慢的原因。可是,你肯定会纳闷:"vim怎么会无缘无故连接其它计算机的6000端口呢?"。问得好,那么请你回想一下6000是什么服务的端口?没错,就是X Server。看来vim是要把输出定向到一个远程X Server,那么Shell中肯定定义了DISPLAY变量,查看.cshrc,果然有这么一行: 

setenv DISPLAY${REMOTEHOST}:0,把它注释掉,再重新登录,问题就解决了。


案例四


用调试工具掌握软件的工作原理

操作系统:Red Hat Linux 9.0

用调试工具实时跟踪软件的运行情况不仅是诊断软件"疑难杂症"的有效的手段,也可帮助我们理清软件的"脉络",即快速掌握软件的运行流程和工作原理,不失为一种学习源代码的辅助方法。下面这个案例展现了如何使用strace通过跟踪别的软件来"触发灵感",从而解决软件开发中的难题的。

大家都知道,在进程内打开一个文件,都有唯一一个文件描述符(fd:file descriptor)与这个文件对应。而本人在开发一个软件过程中遇到这样一个问题:已知一个fd ,如何获取这个fd所对应文件的完整路径?不管是Linux、FreeBSD或是其它Unix系统都没有提供这样的API,怎么办呢?我们换个角度思考:Unix下有没有什件可以获取进程打开了哪些文件?如果你经验足够丰富,很容易想到lsof,使用它既可以知道进程打开了哪些文件,也可以了解一个文件被哪个进程打开。

好,我们用一个小程序来试验一下lsof,看它是如何获取进程打开了哪些文件。


/* testlsof.c */

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int main(void)

{

        open("/tmp/foo", O_CREAT|O_RDONLY);    /* 打开文件/tmp/foo */

        sleep(1200);                                /* 睡眠1200秒,以便进行后续操作 */

        return 0;

}


将testlsof放入后台运行,其pid为3125。命令lsof -p 3125查看进程3125打开了哪些文件,我们用strace跟踪lsof的


运行,输出结果保存在lsof.strace中:


# gcc testlsof.c -o testlsof

# ./testlsof &

[1] 3125

# strace -o lsof.strace lsof -p 3125


我们以"/tmp/foo"为关键字搜索输出文件lsof.strace,结果只有一条:


# grep '/tmp/foo' lsof.strace

readlink("/proc/3125/fd/3", "/tmp/foo", 4096) = 8


原来lsof巧妙的利用了/proc/nnnn/fd/目录(nnnn为pid):Linux内核会为每一个进程在/proc/建立一个以其pid为名的目录用来保存进程的相关信息,而其子目录fd保存的是该进程打开的所有文件的fd。目标离我们很近了。好,我们到


/proc/3125/fd/看个究竟:


# cd /proc/3125/fd/

# ls -l

total 0

lrwx------    1 root     root           64 Nov  5 09:50 0 -> /dev/pts/0

lrwx------    1 root     root           64 Nov  5 09:50 1 -> /dev/pts/0

lrwx------    1 root     root           64 Nov  5 09:50 2 -> /dev/pts/0

lr-x------    1 root     root           64 Nov  5 09:50 3 -> /tmp/foo

# readlink /proc/3125/fd/3

/tmp/foo


答案已经很明显了:/proc/nnnn/fd/目录下的每一个fd文件都是符号链接,而此链接就指向被该进程打开的一个文件。


我们只要用readlink()系统调用就可以获取某个fd对应的文件了,代码如下:


#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/stat.h>

int get_pathname_from_fd(int fd, char pathname[], int n)

{

        char buf[1024];

        pid_t  pid;

        bzero(buf, 1024);

        pid = getpid();

        snprintf(buf, 1024, "/proc/%i/fd/%i", pid, fd);

        return readlink(buf, pathname, n);

}

int main(void)

{

        int fd;

        char pathname[4096];

        bzero(pathname, 4096);

        fd = open("/tmp/foo", O_CREAT|O_RDONLY);

        get_pathname_from_fd(fd, pathname, 4096);

        printf("fd=%d; pathname=%s\n", fd, pathname);

        return 0;

}


【注】出于安全方面的考虑,在FreeBSD 5 之后系统默认已经不再自动装载proc文件系统,因此,要想使用truss或


strace跟踪程序,你必须手工装载proc文件系统:mount -t procfs proc /proc;或者在/etc/fstab中加上一行:


proc                   /proc           procfs  rw              0       0


ltrace不需要使用procfs。



本文转自aaa超超aaa 51CTO博客,原文链接:http://blog.51cto.com/10983441/1826140


相关文章
|
Ubuntu 前端开发 Linux
通过Ftrace实现高效、精确的内核调试与分析(上)
通过Ftrace实现高效、精确的内核调试与分析
【收藏】利用SEH进行反跟踪
【收藏】利用SEH进行反跟踪
|
2月前
|
Rust 关系型数据库 C语言
使用uftrace跟踪bpf程序的执行
使用uftrace跟踪bpf程序的执行
|
4月前
|
监控 安全 Linux
Linux命令truss详解:系统调用跟踪的利器
`truss`(或`strace`)是Linux调试利器,用于跟踪系统调用和信号。它帮助开发者优化性能、调试错误和进行安全审计。通过附加到进程,记录调用细节、参数、返回值和错误。使用参数如`-d`显示调试信息,`-e`跟踪特定调用,`-o`输出到文件,`-p`跟踪指定进程。注意其对性能的影响,通常需要root权限,并建议过滤输出和结合其他工具分析。
|
存储 网络协议 Linux
高效调试与分析:利用ftrace进行Linux内核追踪(下)
高效调试与分析:利用ftrace进行Linux内核追踪
|
运维 前端开发 关系型数据库
高效调试与分析:利用ftrace进行Linux内核追踪(上)
高效调试与分析:利用ftrace进行Linux内核追踪
|
NoSQL 关系型数据库 编译器
通过Ftrace实现高效、精确的内核调试与分析(下)
通过Ftrace实现高效、精确的内核调试与分析
|
6月前
|
NoSQL Java 编译器
|
Linux Shell 网络安全
使用Systemtap跟踪系统调用 (一)
SystemTap是一个诊断Linux系统性能或功能问题的开源软件。它使得对运行时的Linux系统进行诊断调式变得更容易、更简单。有了它,开发者或调试人员不再需要重编译、安装新内核、重启动等烦人的步骤。
655 0
使用Systemtap跟踪系统调用 (一)
|
存储 安全 前端开发
【译】eBPF 概述:第 5 部分:跟踪用户进程
【译】eBPF 概述:第 5 部分:跟踪用户进程
671 0