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


相关文章
|
5月前
|
Ubuntu 前端开发 Linux
通过Ftrace实现高效、精确的内核调试与分析(上)
通过Ftrace实现高效、精确的内核调试与分析
|
1月前
|
安全 Linux 开发者
⭐⭐⭐⭐⭐Linux C/C++ 进程崩溃诊断以及有效数据收集:解锁代码问题快速定位与修复的方法
⭐⭐⭐⭐⭐Linux C/C++ 进程崩溃诊断以及有效数据收集:解锁代码问题快速定位与修复的方法
82 1
|
5月前
|
运维 前端开发 关系型数据库
高效调试与分析:利用ftrace进行Linux内核追踪(上)
高效调试与分析:利用ftrace进行Linux内核追踪
|
5月前
|
存储 网络协议 Linux
高效调试与分析:利用ftrace进行Linux内核追踪(下)
高效调试与分析:利用ftrace进行Linux内核追踪
|
5月前
|
NoSQL 关系型数据库 编译器
通过Ftrace实现高效、精确的内核调试与分析(下)
通过Ftrace实现高效、精确的内核调试与分析
|
2月前
|
NoSQL Java 编译器
|
Linux Shell 网络安全
使用Systemtap跟踪系统调用 (一)
SystemTap是一个诊断Linux系统性能或功能问题的开源软件。它使得对运行时的Linux系统进行诊断调式变得更容易、更简单。有了它,开发者或调试人员不再需要重编译、安装新内核、重启动等烦人的步骤。
510 0
使用Systemtap跟踪系统调用 (一)
|
监控
|
存储 安全 前端开发
【译】eBPF 概述:第 5 部分:跟踪用户进程
【译】eBPF 概述:第 5 部分:跟踪用户进程
580 0
|
监控 安全 API
使用UnhookMe分析恶意软件中未受监控的系统调用问题
关于UnhookMe UnhookMe是一款通用的Windows API动态解析工具,可以帮助广大研究人员分析和处理恶意软件中未受监控的系统调用问题。 在这个侵入式反病毒产品和EDR产品盛行的年代,很多网络攻击者必须拥有强大的工具来绕过这些安全防御工具。而动态导入解析器能够在运行中取消已用函数的钩子,这也是增强攻击者攻击能力的又一方法。 而UnhookMe可以帮助广大研究人员在编译的可执行文件的PE头中保持可视性。
174 0