Strace 和 GDB 跟踪进程

功能说明
strace 命令是一种强大的工具 能够显示任何由用户空间程式发出的系统调用 strace 显示这些调用的参数并返回符号形式的值 strace 从内核接收信息 而且无需以任何特别的方式来构建内核 strace 的每一行输出包括系统调用名称,   然后是参数和返回值.
下面记录几个常用option :
-f -F选项告诉strace同时跟踪fork和vfork出来的进程
-o xxx.txt 输出到某个文档
-e execve 只记录 execve 这类系统调用 .

详细用法


usage: strace [-dffhiqrtttTvVxx] [-a column] [-e expr] ... [-o file]
              [-p pid] ... [-s strsize] [-u username] [-E var=val] ...
              [command [arg ...]]
   or: strace -c [-e expr] ... [-O overhead] [-S sortby] [-E var=val] ...
              [command [arg ...]]
-c -- count time, calls, and errors for each syscall and report summary
-f -- follow forks, -ff -- with output into separate files
-F -- attempt to follow vforks, -h -- print help message
-i -- print instruction pointer at time of syscall
-q -- suppress messages about attaching, detaching, etc.
-r -- print relative timestamp, -t -- absolute timestamp, -tt -- with usecs
-T -- print time spent in each syscall, -V -- print version
-v -- verbose mode: print unabbreviated argv, stat, termio[s], etc. args
-x -- print non-ascii strings in hex, -xx -- print all strings in hex
-a column -- alignment COLUMN for printing syscall results (default 40)
-e expr -- a qualifying expression: option=[!]all or option=[!]val1[,val2]...
   options: trace, abbrev, verbose, raw, signal, read, or write
-o file -- send trace output to FILE instead of stderr
-O overhead -- set overhead for tracing syscalls to OVERHEAD usecs
-p pid -- trace process with process id PID, may be repeated
-s strsize -- limit length of print strings to STRSIZE chars (default 32)
-S sortby -- sort syscall counts by: time, calls, name, nothing (default time)
-u username -- run command as username handling setuid and/or setgid
-E var=val -- put var=val in the environment for command
-E var -- remove var from the environment for command

参数说明


-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执行被跟踪的命令.

-----------------------------------------------------------------------
UNIX 家族总是为用户提供了丰富的工具。UNIX 是一个工具财宝箱,有了这些工具,您不仅可以完成具有创造性的工作,还可以在深入研究该操作系统的同时得到教育和娱乐。strace(用来跟踪任何程序的 系统调用)和 GDB 调试工具(用来在受控的环境中运行程序的功能齐全的调试工具)是实现这个目标的两个有价值的工具。
UNIX 的设计由大量的函数调用(称为 系统调用) 组成,其中包括一些简单的任务,如在屏幕上显示字符串来设置任务优先级。所有的 UNIX 程序都是通过调用操作系统提供的这些底层服务来完成它们的任务,使用 strace 工具,您可以清楚地看到这些调用过程及其使用的参数。通过这种方式,您可以操作这些程序,以了解它们与操作系统之间的底层交互。
开始游戏
让我们以一个简单的 UNIX 命令 pwd 作为开始,然后更深入地研究该命令在完成其任务的过程中进行了哪些工作。启动 xterm 以创建一个进行实验的受控环境,然后输入下面的命令:
$ pwd

这个 pwd 命令显示了当前的工作目录。在我的计算机上,当时的输出是:
/home/bill/

一个如此简单的函数掩饰了该命令底层的复杂性(顺便说一下,所有的计算机程序都是这样的)。要真正地了解其复杂性,请使用 strace 工具再次运行 pwd 命令:
$ strace pwd

通过该命令,您可以看到,在显示和列举当前工作目录的过程中,UNIX 计算机执行了相当多的操作(请参见 清单 1)。

清单 1:strace pwd 命令的输出
execve("/bin/pwd", ["pwd"], [/* 39 vars */]) = 0uname({sys="Linux", node="sammy", ...}) = 0brk(0) = 0x804c000old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4001... . . . fstat64(3, {st_mode=S_IFREG|0644, st_size=115031, ...}) = 0old_mmap(NULL, 115031, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000close(3) = 0open("/lib/tls/libc.so.6", O_RDONLY) = 3read(3, "\177ELF\1\1\1\3\3\1\360U\1"..., 1024) = 1024fstat64(3, {st_mode=S_IFREG|0755, st_size=1547996, ...}) = 0old_mmap(0x42000000, 1257224, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x42000000mprotect(0x4212e000, 20232, PROT_NONE) = 0old_mmap(0x4212e000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x12e000)...old_mmap(0x42131000, 7944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,...close(3) = 0set_thread_area({entry_number:-1 -> 6, base_addr:0x40016ac0, limit:1048575, seg_32bit...munmap(0x40017000, 115031) = 0brk(0) = 0x804c000brk(0x804d000) = 0x804d000brk(0) = 0x804d000open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3fstat64(3, {st_mode=S_IFREG|0644, st_size=30301680, ...}) = 0mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000close(3) = 0brk(0) = 0x804d000brk(0x804e000) = 0x804e000getcwd("/home/bill", 4096) = 11fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 6), ...}) = 0mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4021700...write(1, "/home/bill\n", 11/home/bill) = 11munmap(0x40217000, 4096) = 0exit_group(0) = ?

UNIX 系统调用的具体细节
关于检索和显示当前工作目录所需的所有系统调用的详细细节,已经超出了本文的讨论范围,但我会介绍如何获取这些信息。 清单 1 中的每一行都以类似 C 的格式,清楚地说明了一项系统调用及其参数,这也是 C 程序员希望看到的。Strace 同样以这种方式显示这些调用,不管在实际创建该程序时使用的是何种编程语言。
如果要了解 清单 1 中的所有细节信息,对于所有这些系统调用,UNIX 为您提供了大量的文档。 清单 1 中“最重要”的函数是 getcwd() 函数,它表示 获取当前工作目录。当前的 xterm 显示了 strace pwd 的输出,同时再启动另一个 xterm 并输入下面的命令以查看 UNIX 对该函数的显示:
$ man getcwd

您 所看到的应该是 getcwd() 函数完整的清单以及这个重要的 C 函数需要的和返回的参数清单。同样地,您可以输入 man brk 或 man fstat64 等等。通常,UNIX 系统通过文档对这些系统函数进行了详细的说明,如果花些时间仔细地研究它们,您将逐渐地了解到 UNIX 的功能是多么的强大,以及学习这些底层系统细节是多么的容易。在所有的操作系统中,UNIX 最善于帮助您理解其底层的处理过程。
观察 nweb
对于下面几个步骤,您需要使用更庞大且更复杂的程序,而不是像 pwd 这样简单的 UNIX 命令。简单的超文本传输协议 (HTTP) 服务器,如 nweb,是非常适合的。当您在 Internet 上 冲浪 的时候,HTTP 服务器侦听浏览器请求,然后通过发送所请求的对象,如 Web 页面和图形文件,以此响应浏览器的请求。
下载并安装 nweb,该软件由 IBM developerWorks 投稿作家 Nigel Griffiths 编写。 '"(请参阅 参考资料部分提供的 Nigel 的文章“nweb: ?"a tiny, safe Web server (static pages only)”(developerWorks,2004 年 6 月)的链接。)
下载 es-nweb.zip 到 $HOME/downloads 目录,然后输入 清单 2 中所示的简单命令,以提取、编译并启动该程序:
注意:我假设您需要为 Linux® 工作站编译这个程序。如果实际情况并非如此,那么有关在其他的 UNIX 操作系统上对该程序进行编译的详细信息,请阅读这篇 nweb 文章。

清单 2. 用于提取、编译和启动 nweb 的命令
$ cd src$ mkdir nweb$ cd nweb$ unzip $HOME/downloads/es-nweb.zip$ gcc -ggdb -O -DLINUX nweb.c -o nweb$ ./nweb 9090 $HOME/src/nweb &

注意: 清单 2 中的 -ggdb 选项和 Nigel 的文章中的内容有些不同,该选项用于告诉 GCC 编译器对该程序进行优化,以便使用 GDB 调试工具对其进行调试,您将在以后用到该调试工具。
接下来,要确认 nweb 服务器已经运行,可以使用 清单 3 中所示的 ps 命令来对它进行检查。

清单 3. ps 命令
$ ps PID TTY TIME CMD 2913 pts/5 00:00:00 bash 4009 pts/5 00:00:00 nweb 4011 pts/5 00:00:00 ps

最后,要确认 nweb 确实正在运行并且状态正常,可以在您的计算机上启动一个 Web 浏览器并在地址栏中输入 http://localhost:9090。
针对 nweb 使用 strace
现在,让我们来进行一些有趣的工作。启动另一个 xterm,然后使用 strace 来跟踪正在运行的 nweb 服务器。要完成该任务,您必须清楚该程序的进程 ID,并且必须具有适当的权限。您仅仅可以看到一组特定的系统调用,即那些与网络相关的系统调用。输入 清单 4 第一行所示的命令作为开始,其中使用了前面显示的 nweb 的进程 ID。您应该看到如下的输出( 清单 4 中的第二行)。

清单 4. 开始对 nweb 进行跟踪
$ strace -e trace=network -p 4009accept(0,

请 注意,在调用网络 accept() 函数的过程中停止了跟踪操作。在浏览器中刷新几次 http://localhost:9090 页面,请注意每次刷新该页面时 strace 的显示。这是不是很棒呢?您所看到的是,当 Web 浏览器调用 HTTP 服务器 (nweb) 时,服务器所进行的底层网络调用。简单地说,nweb 正在 接受 来自您的浏览器的调用。
您可以在运行 strace 的具有窗口焦点的 xterm 中按下 Ctrl+C 以停止对网络调用的跟踪。
再来研究 GDB 调试工具
正如您所看到的,strace 可以作为了解用户程序如何通过某些系统调用与操作系统进行交互的一个很好的程序。GDB 调试工具本身也可以附加于一个正在运行的进程,并帮助您进行更深入的研究。
GDB 调试工具非常有用,Internet 上提供了大量的有关该工具的可用信息。通常,调试工具是很有价值的工具,并且任何负责开发和维护计算机系统的人员应该了解如何使用它们。因此,在 nweb 运行于另一个 xterm 会话的同时,按下 Ctrl+C 停止 strace,然后输入 清单 5 中所示的命令启动 GDB 调试工具。

清单 5. 启动 GDB 调试工具
$ gdb --quiet(gdb) attach 4009Attaching to process 4009Reading symbols from /home/bill/src/nweb/nweb...done.Reading symbols from /lib/tls/libc.so.6...done.Loaded symbols for /lib/tls/libc.so.6Reading symbols from /lib/ld-linux.so.2...done.Loaded symbols for /lib/ld-linux.so.20xffffe410 in ?? ()(gdb)

-quiet 选项告诉 GDB 调试工具仅显示其提示符,而不要显示所有其他的启动信息。如果需要显示额外的文本信息,可以去掉 -quiet 选项。
attach 4009 命令启动对当前正在运行的 nweb 服务器的调试工作,并且 GDB 调试工具通过读取有关该进程的所有的符号信息来做出同样方式的响应。下一步,使用 info 命令来列举您所研究的程序的相关信息(请参见 清单 6)。

清单 6. info 命令列出程序信息
(gdb) info procprocess 4009cmdline = './nweb'cwd = '/home/bill/src/nweb'exe = '/home/bill/src/nweb/nweb'(gdb)

info 命令(请参见 清单 7)的另一个有用的变种是 info functions,然而,函数的列表可能很长。

清单 7. info functions 命令得到的函数列表
(gdb) info functionsAll defined functions:File nweb.c:void log(int, char *, char *, int);int main(int, char **);void web(int, int);File __finite:int __finite(); . . .(gdb)

因为使用 -ggdb 选项对 nweb 程序进行了编译,所以可执行文件中包含了大量的调试信息,允许该调试工具查看文件中列举的已定义的函数,如 清单 7 所示。
list 和 disassemble 命令
有两个重要的 GDB 调试工具命令,它们分别是 list 和 disassemble。通过使用 清单 8 中所示的代码尝试使用这些命令。

清单 8. list 命令
(gdb) list main121 exit(1);122 }123124125 main(int argc, char **argv)126 {127 int i, port, pid, listenfd, socketfd, hit;128 size_t length;129 char *str;130 static struct sockaddr_in cli_addr; /* static = initialised to zeros */(gdb) 131 static struct sockaddr_in serv_addr; /* static = initialised to zeros */132133 if( argc < 3 || argc > 3 || !strcmp(argv[1], "-?") ) {134 (void)printf("hint: nweb Port-Number Top-Directory\n\n"135 "\tnweb is a small and very safe mini web server\n"136 "\tnweb only servers out file/web pages with extensions named below\n"137 "\t and only from the named directory or its sub-directories.\n"138 "\tThere is no fancy features = safe and secure.\n\n"139 "\tExample: nweb 8181 /home/nwebdir &\n\n"140 "\tOnly Supports:");

正 如您所看到的,list 命令以源文件的形式列出了正在运行的程序,并标注了相应的行号。按下 Return 键(如第 130 行和第 131 行之间所示)以接着上次的列表继续进行列举。现在,尝试使用 disassemble 命令,可以缩写为 disass(请参见 清单 9)。

清单 9. disassemble 命令
(gdb) disass mainDump of assembler code for function main:0x08048ba2 <main+0>: push ebp0x08048ba3 <main+1>: mov ebp,esp0x08048ba5 <main+3>: push edi0x08048ba6 <main+4>: push esi0x08048ba7 <main+5>: push ebx0x08048ba8 <main+6>: sub esp,0xc0x08048bab <main+9>: mov ebx,DWORD PTR [ebp+12]0x08048bae <main+12>: and esp,0xfffffff0 . . .0x08048c01 <main+95>: call 0x8048664 <printf>0x08048c06 <main+100>: add esp,0x100x08048c09 <main+103>: inc esi0x08048c0a <main+104>: cmp DWORD PTR [ebx+esi],0x0---Type <return> to continue, or q <return> to quit---

反 汇编清单显示了该 main 函数的汇编语言清单。在本示例中,汇编代码指示出运行该代码的计算机使用的是 Intel® Pentium® 处理器。如果该程序运行于不同类型的处理器上,如基于 IBM Power PC® 的计算机,那么您的代码看上去将有很大的区别。
在其运行的过程中进行监视
因为您所监视的是一个正在运行的程序,所以可以设置相应的断点,然后在它响应浏览器请求并向提出请求的浏览器传输 .html 和 .jpg 文件的同时,对该程序进行 监视。 清单 10 介绍了如何完成该任务。

清单 10. 设置断点
(gdb) break 188Breakpoint 1 at 0x8048e70: file nweb.c, line 188.(gdb) commands 1Type commands for when breakpoint 1 is hit, one per line.End with a line saying just "end".>continue>end(gdb) cContinuing.

此时,GDB 调试工具已设置为在 nweb 服务器 接受 浏览器请求处进行中断,该调试工具将仅仅显示相应的请求并继续处理其他的请求,而不会中断正在运行的程序。刷新几次浏览器中的 http://localhost:9090/ 页面,可以观察到,GDB 调试工具显示了断点并继续运行。
在刷新浏览器页面的同时,您应该看到如 清单 11 所示的断点信息,在 GDB 调试工具 xterm 中滚动输出。与 strace 相同,您可以按下 Ctrl+C 来停止对 nweb 服务器的调试。在停止了跟踪操作之后,您可以输入 quit 命令以退出 GDB 调试工具。

清单 11. GDB 调试工具 xterm 中的断点信息
Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)Breakpoint 1, main (argc=3, argv=0x1) at nweb.c:188188 if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)Program received signal SIGINT, Interrupt.0xffffe410 in ?? ()(gdb) quitThe program is running. Quit anyway (and detach it)? (y or n) yDetaching from program: /home/bill/src/nweb/nweb, process 4009$

请注意,您正告诉 GDB 调试工具停止对一个仍在内存中活动的程序的调试。即使是在退出了调试工具之后,您还可以刷新浏览器页面,并将看到 nweb 仍在运行。可以输入 kill 4009 命令来停止该程序,或者在您退出会话时,该页面将会消失。
和平常一样,您可以通过其 man 和 info 页面来了解各种各样的工具,如 strace 和 GDB 调试工具。请确保使用 UNIX 为您提供的这些工具!


本文转自:http://topcat.blog.edu.cn/2012/727441.html

你可能感兴趣的:(strace;gdb)