翻译说明:这是一个日本人写的用户态下的函数tracer, 我们知道系统调用可以用strace, 库调用可以使用ltrace, 但是linux下竟然没有一个比较有名的用户程序的tracer, 这真是比较奇怪。
这个工具好的地方就是用ptrace系统调用来实现,只要跟踪的程序没有被strip,就可以使用,而不要重新编译程序。而另一种函数跟踪的方式(使用gcc -finstruction-functions),目标程序必须要重新编译,这个就大大降低了tracer的实用性。
但是目前这个工具只支持x86架构,arm之类的嵌入式环境不支持。
目前还剩下实现机制这一部分没有翻译完。
原文来自: http://binary.nahi.to/hogetrace/
tracef是、面向Linux的「函数调用追踪器」。 和一般在Linux发行版使用的ltrace相类似、但是其有下面的特征和不同点。
根据上述的这些特征、
等等,都可以灵活利用该工具。但是遗憾的是,函数调用时参数的信息没有ltrace那么详细,用于调试还比较困难。目前我手头上的用C++写的比较大的执行文件(.text的大小约为5MB、text/weak的symbol数量是2万左右、进程/线程数量有几十个)的解析都没有什么问题。
如果解析对象没有被strip,就不需要再编译。只要是能使用gdb来调试的可执行文件、也能用tracef来跟踪。除了作为解析対象的可执行文件所包含的「自身函数」的调用以外、如ltrace表示的内容那样、库函数的调用状况也在某种程度上可以表示(命令行选项可以选项是否表示)。
tracef和、ltrace/strace、或gdb一样、通过使用Linux kernel的ptrace(2)系统调用、从别的进程来观察作为解析対象的程序,并显观察状况。
例如要追踪下面的程序的话、
<span class="com" style="color:#8800;">#include <stdio.h></span><span class="pln"> </span><span class="kwd" style="color:#0088;">void</span><span class="pln"> my_func_2</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> puts</span><span class="pun" style="color:#66660;">(</span><span class="str" style="color:#0880;">"hello, world!"</span><span class="pun" style="color:#66660;">);</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">void</span><span class="pln"> my_func_1</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> my_func_2</span><span class="pun" style="color:#66660;">();</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> main</span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">int</span><span class="pln"> argc</span><span class="pun" style="color:#66660;">,</span><span class="pln"> </span><span class="kwd" style="color:#0088;">char</span><span class="pun" style="color:#66660;">**</span><span class="pln"> argv</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> my_func_1</span><span class="pun" style="color:#66660;">();</span><span class="pln"> fflush</span><span class="pun" style="color:#66660;">(</span><span class="pln">stdout</span><span class="pun" style="color:#66660;">);</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span>
結果如下所示。
$ gcc -g -o hello hello.c $ tracef --plt --line-numbers --call-tree hello [pid 30126] +++ process 30126 attached (ppid 30125) +++ [pid 30126] === symbols loaded: './hello' === [pid 30126] ==> _start() at 0x08048300 [pid 30126] ==> __libc_start_main@plt() at 0x080482cc [pid 30126] ==> __libc_csu_init() at 0x08048440 [pid 30126] ==> _init() at 0x08048294 [pid 30126] ==> call_gmon_start() at 0x08048324 [pid 30126] <== call_gmon_start() [eax = 0x0] [pid 30126] ==> frame_dummy() at 0x080483b0 [pid 30126] <== frame_dummy() [eax = 0x0] [pid 30126] ==> __do_global_ctors_aux() at 0x080484b0 [pid 30126] <== __do_global_ctors_aux() [eax = 0xffffffff] [pid 30126] <== _init() [eax = 0xffffffff] [pid 30126] <== __libc_csu_init() [eax = 0x8049514] [pid 30126] ==> main() at 0x080483f5 [/home/sato/tracef/sample/hello.c:14] [pid 30126] ==> my_func_1() at 0x080483e8 [/home/sato/tracef/sample/hello.c:9] [pid 30126] ==> my_func_2() at 0x080483d4 [/home/sato/tracef/sample/hello.c:4] [pid 30126] ==> puts@plt() at 0x080482ec [pid 30126] <== puts@plt() [eax = 0xe] [pid 30126] <== my_func_2() [eax = 0xe] [pid 30126] <== my_func_1() [eax = 0xe] [pid 30126] ==> fflush@plt() at 0x080482dc hello, world! [pid 30126] <== fflush@plt() [eax = 0x0] [pid 30126] <== main() [eax = 0x0] [pid 30126] ==> _fini() at 0x080484d8 [pid 30126] ==> __do_global_dtors_aux() at 0x08048350 [pid 30126] <== __do_global_dtors_aux() [eax = 0x0] [pid 30126] <== _fini() [eax = 0x0] [pid 30126] +++ process 30126 detached (ppid 30125) +++ tracef: done
main()调用my_func_1()、my_func_1()调用my_func_2()的过程被显示。另外、 定义这些函数的源文件名也会显示。
这次因为追加了--synthetic选项、puts@plt() 等、DSO里的函数调用也能追踪到。不使用--synthetic 选项的话、仅仅是单纯的追踪程序自身的函数。根据不同的选项,函数的参数的简单的信息也能表示。
需要额外的libdwarf包。
$ tar xvzf tracef-0.1.tar.gz $ cd tracef-0.1 $ ./configure $ cd src $ make
make结果生成的 src/tracef 就是追踪器。这个二进制文件(tracef)、不安装在/usr/local/bin 下也可以、放在自己想放的地方、单独执行也没有问题。这里准备了一些测试用例 sample/用于测试结果。
$ cd ../sample $ ./sample.sh
执行sample.sh后、会编译并链接若干程序,这些程序被tracef解析的结果会放在sample/logs/下。
解析对象没有被strip的话就不需要重新编译。gdb能解析的二进制就不需要重新编译。
另外、解析対象是有用 -g 编译的话、输出的信息就会增多。例如行号的信息、参数信息都能输出。解析対象は、最好是用-O0 编译的、但这不是必须的。被优化的场合下、一部分的函数调用可能无法被检测出来。
被strip后的二进制的话,就算被追踪了tracef也不会异常退出,但不会输出任何解析結果。
是否被strip、可以使用file命令来确认输出 "stripped" "not stipped"的哪一个。或是用 readelf -S 命令来判断二进制文件是否存在.symtab 段。 下面的例子的话a.out就没有被strip。
$ file a.out a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped $ readelf -S a.out | grep '\.symtab' [40] .symtab SYMTAB 00000000 5d20b4 00d4d0 10 41 1056 4
另外、因为/lib/lib*.a 或 /usr/lib/lib*.a 文件 包含.symtab、如果链接这些 lib*.a 文件的话,这些文件里包含的函数也变成追踪的対象。不希望追踪这些函数的话,请不要使用.a 文件而是链接到 .so上。
程序用 gcc -g编译时就会包含调试信息、追踪结果里就能够包含定义该函数的源文件名、行编号。另外,一部分函数的参数信息也能表示。。程序是否是用-g (或 -ggdb 或 -g3 等等)编译的 、可以用readelf -S 命令确认。如果 有.debug_* 这些段名的话就是用 -g 编译的。
$ readelf -S a.out | grep '\.debug_' [29] .debug_aranges PROGBITS 00000000 0cb8a6 002aa8 00 0 0 1 [30] .debug_pubnames PROGBITS 00000000 0ce34e 02d72c 00 0 0 1 [31] .debug_info PROGBITS 00000000 0fba7a 1670e5 00 0 0 1 (略)
其他一些细微之处:
<span class="com" style="color:#8800;">#include <stdio.h></span><span class="pln"> </span><span class="com" style="color:#8800;">#include <sys/types.h></span><span class="pln"> </span><span class="com" style="color:#8800;">#include <unistd.h></span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> main</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">if</span><span class="pln"> </span><span class="pun" style="color:#66660;">(</span><span class="pln">fork</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">==</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> printf</span><span class="pun" style="color:#66660;">(</span><span class="str" style="color:#0880;">"hello world\n"</span><span class="pun" style="color:#66660;">);</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> </span><span class="lit" style="color:#06666;">1</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span>
用tracef解析以上程序的话会输出如下结果。fork后的进程自动开始解析。 --ff 选项可以把每个进程/线程的结果输出到Log文件。
$ ../src/tracef --synthetic -flATu ./fork [pid 30133] 13:56:14.041015 +++ process 30133 attached (ppid 30132) +++ [pid 30133] 13:56:14.065944 === symbols loaded: './fork' === [pid 30133] 13:56:14.086854 ==> _start() at 0x080482e0 [pid 30133] 13:56:14.103301 ==> __libc_start_main@plt() at 0x080482a4 [pid 30133] 13:56:14.120804 ==> __libc_csu_init() at 0x08048410 [pid 30133] 13:56:14.142981 ==> _init() at 0x0804826c [pid 30133] 13:56:14.167027 ==> call_gmon_start() at 0x08048304 [pid 30133] 13:56:14.198099 <== call_gmon_start() [eax = 0x0] [pid 30133] 13:56:14.228514 ==> frame_dummy() at 0x08048390 [pid 30133] 13:56:14.256405 <== frame_dummy() [eax = 0x0] [pid 30133] 13:56:14.287810 ==> __do_global_ctors_aux() at 0x08048480 [pid 30133] 13:56:14.316187 <== __do_global_ctors_aux() [eax = 0xffffffff] [pid 30133] 13:56:14.346243 <== _init() [eax = 0xffffffff] [pid 30133] 13:56:14.373957 <== __libc_csu_init() [eax = 0x80494e0] [pid 30133] 13:56:14.400881 ==> main() at 0x080483b4 [/home/sato/tracef/sample/fork.c:6] [pid 30133] 13:56:14.424637 ==> fork@plt() at 0x080482c4 [pid 30134] 13:56:14.440226 +++ process 30134 attached (ppid 30133) +++ [pid 30134] 13:56:14.444849 <== fork@plt() [eax = 0x0] [pid 30134] 13:56:14.455462 ==> puts@plt() at 0x080482b4 hello world [pid 30134] 13:56:14.468085 <== puts@plt() [eax = 0xc] [pid 30134] 13:56:14.495761 <== main() [eax = 0x1] [pid 30134] 13:56:14.519362 ==> _fini() at 0x080484a8 [pid 30134] 13:56:14.539950 ==> __do_global_dtors_aux() at 0x08048330 [pid 30134] 13:56:14.566927 <== __do_global_dtors_aux() [eax = 0x0] [pid 30134] 13:56:14.592884 <== _fini() [eax = 0x0] [pid 30134] 13:56:14.616904 +++ process 30134 detached (ppid 30133) +++ [pid 30133] 13:56:14.625031 <== fork@plt() [eax = 0x75b6] [pid 30133] 13:56:14.652768 --- SIGCHLD received (#17 Child exited) --- [pid 30133] 13:56:14.660887 <== main() [eax = 0x0] [pid 30133] 13:56:14.685255 ==> _fini() at 0x080484a8 [pid 30133] 13:56:14.701960 ==> __do_global_dtors_aux() at 0x08048330 [pid 30133] 13:56:14.726249 <== __do_global_dtors_aux() [eax = 0x0] [pid 30133] 13:56:14.755024 <== _fini() [eax = 0x0] [pid 30133] 13:56:14.781833 +++ process 30133 detached (ppid 30132) +++
写了下面一个程序它自身调用exec来做加法运算 (原来的代码是来自哪里的?...BinaryHacks? 记不起来了)。 $ ./exec 0 5 这样起动后,不可见的地方execve(2)反复执行来计算 5+4+3+2+1 、最后输出结果。argv[1]是累积的变量。
<span class="com" style="color:#8800;">#include <stdio.h></span><span class="pln"> </span><span class="com" style="color:#8800;">#include <stdlib.h></span><span class="pln"> </span><span class="com" style="color:#8800;">#include <unistd.h></span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> main</span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">int</span><span class="pln"> argc</span><span class="pun" style="color:#66660;">,</span><span class="pln"> </span><span class="kwd" style="color:#0088;">char</span><span class="pun" style="color:#66660;">**</span><span class="pln"> argv</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">if</span><span class="pln"> </span><span class="pun" style="color:#66660;">(</span><span class="pln">argc </span><span class="pun" style="color:#66660;">!=</span><span class="pln"> </span><span class="lit" style="color:#06666;">3</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="com" style="color:#8800;">/* usage: ./a.out 0 N */</span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> accum </span><span class="pun" style="color:#66660;">=</span><span class="pln"> atoi</span><span class="pun" style="color:#66660;">(</span><span class="pln">argv</span><span class="pun" style="color:#66660;">[</span><span class="lit" style="color:#06666;">1</span><span class="pun" style="color:#66660;">]);</span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> n </span><span class="pun" style="color:#66660;">=</span><span class="pln"> atoi</span><span class="pun" style="color:#66660;">(</span><span class="pln">argv</span><span class="pun" style="color:#66660;">[</span><span class="lit" style="color:#06666;">2</span><span class="pun" style="color:#66660;">]);</span><span class="pln"> </span><span class="kwd" style="color:#0088;">if</span><span class="pln"> </span><span class="pun" style="color:#66660;">(</span><span class="pln">n </span><span class="pun" style="color:#66660;">==</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> printf</span><span class="pun" style="color:#66660;">(</span><span class="str" style="color:#0880;">"answer: %d\n"</span><span class="pun" style="color:#66660;">,</span><span class="pln"> accum</span><span class="pun" style="color:#66660;">);</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> accum</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">char</span><span class="pln"> p</span><span class="pun" style="color:#66660;">[</span><span class="lit" style="color:#06666;">32</span><span class="pun" style="color:#66660;">]</span><span class="pln"> </span><span class="pun" style="color:#66660;">=</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">},</span><span class="pln"> q</span><span class="pun" style="color:#66660;">[</span><span class="lit" style="color:#06666;">32</span><span class="pun" style="color:#66660;">]</span><span class="pln"> </span><span class="pun" style="color:#66660;">=</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">};</span><span class="pln"> snprintf</span><span class="pun" style="color:#66660;">(</span><span class="pln">p</span><span class="pun" style="color:#66660;">,</span><span class="pln"> </span><span class="lit" style="color:#06666;">31</span><span class="pun" style="color:#66660;">,</span><span class="pln"> </span><span class="str" style="color:#0880;">"%d"</span><span class="pun" style="color:#66660;">,</span><span class="pln"> accum </span><span class="pun" style="color:#66660;">+</span><span class="pln"> n</span><span class="pun" style="color:#66660;">);</span><span class="pln"> snprintf</span><span class="pun" style="color:#66660;">(</span><span class="pln">q</span><span class="pun" style="color:#66660;">,</span><span class="pln"> </span><span class="lit" style="color:#06666;">31</span><span class="pun" style="color:#66660;">,</span><span class="pln"> </span><span class="str" style="color:#0880;">"%d"</span><span class="pun" style="color:#66660;">,</span><span class="pln"> n </span><span class="pun" style="color:#66660;">-</span><span class="pln"> </span><span class="lit" style="color:#06666;">1</span><span class="pun" style="color:#66660;">);</span><span class="pln"> execlp</span><span class="pun" style="color:#66660;">(</span><span class="str" style="color:#0880;">"/proc/self/exe"</span><span class="pun" style="color:#66660;">,</span><span class="pln"> </span><span class="str" style="color:#0880;">"exe"</span><span class="pun" style="color:#66660;">,</span><span class="pln"> p</span><span class="pun" style="color:#66660;">,</span><span class="pln"> q</span><span class="pun" style="color:#66660;">,</span><span class="pln"> NULL</span><span class="pun" style="color:#66660;">);</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> </span><span class="pun" style="color:#66660;">-</span><span class="lit" style="color:#06666;">1</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span>
...程序内容暂且不提,如下所示,就算是有execve(2)的程序,处理内容也能被正确解析。
$ ../src/tracef --synthetic -flATuv ./exec 0 5 [pid 30137] 13:59:40.506880 +++ process 30137 attached (ppid 30136) +++ [pid 30137] 13:59:40.516425 === symbols loaded: './exec' === [pid 30137] 13:59:40.523785 ==> _start() at 0x08048340 [pid 30137] 13:59:40.526590 ==> __libc_start_main@plt() at 0x080482e4 [pid 30137] 13:59:40.528794 ==> __libc_csu_init() at 0x08048550 [pid 30137] 13:59:40.533544 ==> _init() at 0x080482ac [pid 30137] 13:59:40.536994 ==> call_gmon_start() at 0x08048364 [pid 30137] 13:59:40.540511 <== call_gmon_start() [eax = 0x0] [pid 30137] 13:59:40.545888 ==> frame_dummy() at 0x080483f0 [pid 30137] 13:59:40.549673 <== frame_dummy() [eax = 0x0] [pid 30137] 13:59:40.560435 ==> __do_global_ctors_aux() at 0x080485c0 [pid 30137] 13:59:40.585169 <== __do_global_ctors_aux() [eax = 0xffffffff] [pid 30137] 13:59:40.618919 <== _init() [eax = 0xffffffff] [pid 30137] 13:59:40.648524 <== __libc_csu_init() [eax = 0x8049638] [pid 30137] 13:59:40.674937 ==> main(int argc <3>, POINTER argv <0xbff88a44>) at 0x08048414 [/home/sato/tracef/sample/exec.c:6] [pid 30137] 13:59:40.718245 ==> atoi@plt() at 0x08048314 [pid 30137] 13:59:40.744948 <== atoi@plt() [eax = 0x0] [pid 30137] 13:59:40.770063 ==> atoi@plt() at 0x08048314 [pid 30137] 13:59:40.793048 <== atoi@plt() [eax = 0x5] [pid 30137] 13:59:40.819975 ==> snprintf@plt() at 0x08048324 [pid 30137] 13:59:40.843943 <== snprintf@plt() [eax = 0x1] [pid 30137] 13:59:40.873277 ==> snprintf@plt() at 0x08048324 [pid 30137] 13:59:40.898579 <== snprintf@plt() [eax = 0x1] [pid 30137] 13:59:40.923149 ==> execlp@plt() at 0x080482f4 [pid 30137] 13:59:40.947893 === execve(2) called. reloading symbols... === [pid 30137] 13:59:40.962784 === symbols loaded: 'exe' === [pid 30137] 13:59:40.977023 ==> _start() at 0x08048340 ... [pid 30137] 13:59:42.522945 ==> execlp@plt() at 0x080482f4 [pid 30137] 13:59:42.546478 === execve(2) called. reloading symbols... === [pid 30137] 13:59:42.556684 === symbols loaded: 'exe' === [pid 30137] 13:59:42.568359 ==> _start() at 0x08048340 [pid 30137] 13:59:42.580952 ==> __libc_start_main@plt() at 0x080482e4 [pid 30137] 13:59:42.594888 ==> __libc_csu_init() at 0x08048550 [pid 30137] 13:59:42.607220 ==> _init() at 0x080482ac [pid 30137] 13:59:42.616299 ==> call_gmon_start() at 0x08048364 [pid 30137] 13:59:42.625258 <== call_gmon_start() [eax = 0x0] [pid 30137] 13:59:42.643251 ==> frame_dummy() at 0x080483f0 [pid 30137] 13:59:42.654002 <== frame_dummy() [eax = 0x0] [pid 30137] 13:59:42.664041 ==> __do_global_ctors_aux() at 0x080485c0 [pid 30137] 13:59:42.673053 <== __do_global_ctors_aux() [eax = 0xffffffff] [pid 30137] 13:59:42.688869 <== _init() [eax = 0xffffffff] [pid 30137] 13:59:42.697093 <== __libc_csu_init() [eax = 0x8049638] [pid 30137] 13:59:42.706019 ==> main(int argc <3>, POINTER argv <0xbf966364>) at 0x08048414 [/home/sato/tracef/sample/exec.c:6] [pid 30137] 13:59:42.719013 ==> atoi@plt() at 0x08048314 [pid 30137] 13:59:42.726840 <== atoi@plt() [eax = 0xf] [pid 30137] 13:59:42.731108 ==> atoi@plt() at 0x08048314 [pid 30137] 13:59:42.735355 <== atoi@plt() [eax = 0x0] [pid 30137] 13:59:42.738764 ==> printf@plt() at 0x08048304 answer: 15 [pid 30137] 13:59:42.745753 <== printf@plt() [eax = 0xb] [pid 30137] 13:59:42.749255 <== main() [eax = 0xf] [pid 30137] 13:59:42.752250 ==> _fini() at 0x080485e8 [pid 30137] 13:59:42.755001 ==> __do_global_dtors_aux() at 0x08048390 [pid 30137] 13:59:42.758079 <== __do_global_dtors_aux() [eax = 0x0] [pid 30137] 13:59:42.767404 <== _fini() [eax = 0x0] [pid 30137] 13:59:42.780894 +++ process 30137 detached (ppid 30136) +++
下面是末尾递归调用做加法运算的程序。根据是否有优化的不同,跟踪的结果输出也不同。除此之外,tar.gz里也包含执行相互递归 (mutual recursion) 的示例。
<span class="com" style="color:#8800;">#include <stdio.h></span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> sum</span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">int</span><span class="pln"> n</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> n </span><span class="pun" style="color:#66660;">==</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pln"> </span><span class="pun" style="color:#66660;">?</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pln"> </span><span class="pun" style="color:#66660;">:</span><span class="pln"> n </span><span class="pun" style="color:#66660;">+</span><span class="pln"> sum</span><span class="pun" style="color:#66660;">(</span><span class="pln">n </span><span class="pun" style="color:#66660;">-</span><span class="pln"> </span><span class="lit" style="color:#06666;">1</span><span class="pun" style="color:#66660;">);</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> main</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> s </span><span class="pun" style="color:#66660;">=</span><span class="pln"> sum</span><span class="pun" style="color:#66660;">(</span><span class="lit" style="color:#06666;">10</span><span class="pun" style="color:#66660;">);</span><span class="pln"> printf</span><span class="pun" style="color:#66660;">(</span><span class="str" style="color:#0880;">"sum(10) = %d\n"</span><span class="pun" style="color:#66660;">,</span><span class="pln"> s</span><span class="pun" style="color:#66660;">);</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> s</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span>
首先是没有优化的执行文件的跟踪结果。递归被调用的过程如下所示,很容易理解。
$ ../src/tracef -lATv ./recursion [pid 30102] +++ process 30102 attached (ppid 30101) +++ [pid 30102] === symbols loaded: './recursion' === [pid 30102] ==> _start() at 0x080482b0 [pid 30102] ==> __libc_csu_init() at 0x08048410 [pid 30102] ==> _init() at 0x08048250 [pid 30102] ==> call_gmon_start() at 0x080482d4 [pid 30102] <== call_gmon_start() [eax = 0x0] [pid 30102] ==> frame_dummy() at 0x08048360 [pid 30102] <== frame_dummy() [eax = 0x0] [pid 30102] ==> __do_global_ctors_aux() at 0x08048480 [pid 30102] <== __do_global_ctors_aux() [eax = 0xffffffff] [pid 30102] <== _init() [eax = 0xffffffff] [pid 30102] <== __libc_csu_init() [eax = 0x80494e4] [pid 30102] ==> main() at 0x080483b4 [/home/sato/tracef/sample/recursion.c:9] [pid 30102] ==> sum(int n <10>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] ==> sum(int n <9>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] ==> sum(int n <8>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] ==> sum(int n <7>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] ==> sum(int n <6>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] ==> sum(int n <5>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] ==> sum(int n <4>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] ==> sum(int n <3>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] ==> sum(int n <2>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] ==> sum(int n <1>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] ==> sum(int n <0>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30102] <== sum() [eax = 0x0] [pid 30102] <== sum() [eax = 0x1] [pid 30102] <== sum() [eax = 0x3] [pid 30102] <== sum() [eax = 0x6] [pid 30102] <== sum() [eax = 0xa] [pid 30102] <== sum() [eax = 0xf] [pid 30102] <== sum() [eax = 0x15] [pid 30102] <== sum() [eax = 0x1c] [pid 30102] <== sum() [eax = 0x24] [pid 30102] <== sum() [eax = 0x2d] [pid 30102] <== sum() [eax = 0x37] [pid 30102] <== main() [eax = 0x37] [pid 30102] ==> _fini() at 0x080484a8 [pid 30102] ==> __do_global_dtors_aux() at 0x08048300 [pid 30102] <== __do_global_dtors_aux() [eax = 0x0] [pid 30102] <== _fini() [eax = 0x0] sum(10) = 55 [pid 30102] +++ process 30102 detached (ppid 30101) +++
下一次是优化后 (gcc -O1 -foptimize-sibling-calls) 的结果输出。函数调用 sum(10); 里被执行的加法运算已经看不到了。据此,跟踪优化后的二进制时需要注意一下。反之,也可以用tracef来调查优化是不是有效果。
$ ../src/tracef -lATv ./recursion_opt [pid 30104] +++ process 30104 attached (ppid 30103) +++ [pid 30104] === symbols loaded: './recursion_opt' === [pid 30104] ==> _start() at 0x080482b0 [pid 30104] ==> __libc_csu_init() at 0x080483f0 [pid 30104] ==> _init() at 0x08048250 [pid 30104] ==> call_gmon_start() at 0x080482d4 [pid 30104] <== call_gmon_start() [eax = 0x0] [pid 30104] ==> frame_dummy() at 0x08048360 [pid 30104] <== frame_dummy() [eax = 0x0] [pid 30104] ==> __do_global_ctors_aux() at 0x08048460 [pid 30104] <== __do_global_ctors_aux() [eax = 0xffffffff] [pid 30104] <== _init() [eax = 0xffffffff] [pid 30104] <== __libc_csu_init() [eax = 0x80494c4] [pid 30104] ==> main() at 0x0804839c [/home/sato/tracef/sample/recursion.c:9] [pid 30104] ==> sum(int n <10>) at 0x08048384 [/home/sato/tracef/sample/recursion.c:4] [pid 30104] <== sum() [eax = 0x37] [pid 30104] <== main() [eax = 0x37] [pid 30104] ==> _fini() at 0x08048488 [pid 30104] ==> __do_global_dtors_aux() at 0x08048300 [pid 30104] <== __do_global_dtors_aux() [eax = 0x0] [pid 30104] <== _fini() [eax = 0x0] sum(10) = 55 [pid 30104] +++ process 30104 detached (ppid 30103) +++
使用pthread的程序也能被解析。
<span class="com" style="color:#8800;">#include <stdio.h></span><span class="pln"> </span><span class="com" style="color:#8800;">#include <pthread.h></span><span class="pln"> </span><span class="kwd" style="color:#0088;">void</span><span class="pun" style="color:#66660;">*</span><span class="pln"> thread_entry</span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">void</span><span class="pun" style="color:#66660;">*</span><span class="pln"> p</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> printf</span><span class="pun" style="color:#66660;">(</span><span class="str" style="color:#0880;">"pthread_self()=%lu\n"</span><span class="pun" style="color:#66660;">,</span><span class="pln"> pthread_self</span><span class="pun" style="color:#66660;">());</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> NULL</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> main</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> pthread_t t</span><span class="pun" style="color:#66660;">;</span><span class="pln"> pthread_create</span><span class="pun" style="color:#66660;">(&</span><span class="pln">t</span><span class="pun" style="color:#66660;">,</span><span class="pln"> NULL</span><span class="pun" style="color:#66660;">,</span><span class="pln"> thread_entry</span><span class="pun" style="color:#66660;">,</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">);</span><span class="pln"> pthread_join</span><span class="pun" style="color:#66660;">(</span><span class="pln">t</span><span class="pun" style="color:#66660;">,</span><span class="pln"> NULL</span><span class="pun" style="color:#66660;">);</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span>
tracef中的线程ID、和$ ps -L 输出的 "LWP" 数字或、生成线程的父线程的 /proc/pid/task/ 下面显示的数字一致。用pthread_self()获得的 unsigned long int值是不一样的值。容易混乱的点所以说明一下。
$ ../src/tracef --synthetic -flT ./thread [pid 30154] +++ process 30154 attached (ppid 30153) +++ [pid 30154] === symbols loaded: './thread' === [pid 30154] ==> _start() at 0x080483c0 [pid 30154] ==> __libc_start_main@plt() at 0x08048380 [pid 30154] ==> __libc_csu_init() at 0x08048520 [pid 30154] ==> _init() at 0x08048338 [pid 30154] ==> call_gmon_start() at 0x080483e4 [pid 30154] <== call_gmon_start() [eax = 0x0] [pid 30154] ==> frame_dummy() at 0x08048470 [pid 30154] <== frame_dummy() [eax = 0x0] [pid 30154] ==> __do_global_ctors_aux() at 0x08048590 [pid 30154] <== __do_global_ctors_aux() [eax = 0xffffffff] [pid 30154] <== _init() [eax = 0xffffffff] [pid 30154] <== __libc_csu_init() [eax = 0x80495f8] [pid 30154] ==> main() at 0x080484b6 [/home/sato/tracef/sample/thread.c:11] [pid 30154] ==> pthread_create@plt() at 0x080483a0 [pid 30155] +++ thread 30155 attached (ppid 30154) +++ [pid 30154] <== pthread_create@plt() [eax = 0x0] [pid 30154] ==> pthread_join@plt() at 0x08048360 [pid 30155] ==> thread_entry() at 0x08048494 [/home/sato/tracef/sample/thread.c:5] [pid 30155] ==> pthread_self@plt() at 0x080483b0 [pid 30155] <== pthread_self@plt() [eax = 0xb7efcb90] [pid 30155] ==> printf@plt() at 0x08048390 pthread_self()=3085945744 [pid 30155] <== printf@plt() [eax = 0x1a] [pid 30155] <== thread_entry() [eax = 0x0] [pid 30154] <== pthread_join@plt() [eax = 0x0] [pid 30154] <== main() [eax = 0x0] [pid 30154] ==> _fini() at 0x080485b8 [pid 30154] ==> __do_global_dtors_aux() at 0x08048410 [pid 30154] <== __do_global_dtors_aux() [eax = 0x0] [pid 30154] <== _fini() [eax = 0x0] [pid 30155] +++ thread 30155 detached (ppid 30154) +++ [pid 30154] +++ process 30154 detached (ppid 30153) +++ tracef: done
异常抛用、栈打印的例子。
<span class="com" style="color:#8800;">#include <stdio.h></span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> c</span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">int</span><span class="pln"> i</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">if</span><span class="pln"> </span><span class="pun" style="color:#66660;">(</span><span class="pln">i </span><span class="pun" style="color:#66660;">==</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="kwd" style="color:#0088;">throw</span><span class="pln"> </span><span class="lit" style="color:#06666;">0xff</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> c</span><span class="pun" style="color:#66660;">(--</span><span class="pln">i</span><span class="pun" style="color:#66660;">);</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">void</span><span class="pln"> b</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> c</span><span class="pun" style="color:#66660;">(</span><span class="lit" style="color:#06666;">3</span><span class="pun" style="color:#66660;">);</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> a</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">try</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> b</span><span class="pun" style="color:#66660;">();</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">catch</span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">int</span><span class="pun" style="color:#66660;">&</span><span class="pln"> e</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> e</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> main</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> a</span><span class="pun" style="color:#66660;">();</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span>
c()函数中参数 i = 0被调用的时刻抛出异常(__cxa_throw@plt)、到a()为止的栈信息被打印出来。a()返回值0xff也被明确地表示出来。C++的符号的demangle也可以执行。
$ ../src/tracef --synthetic -flT ./thread [pid 30110] +++ process 30110 attached (ppid 30109) +++ [pid 30110] === symbols loaded: './throw' === [pid 30110] ==> _start() at 0x080484d0 [pid 30110] ==> __libc_start_main@plt() at 0x08048458 [pid 30110] ==> __libc_csu_init() at 0x08048680 [pid 30110] ==> _init() at 0x08048420 [pid 30110] ==> call_gmon_start() at 0x080484f4 [pid 30110] <== call_gmon_start() [eax = 0x2e75d4] [pid 30110] ==> frame_dummy() at 0x08048580 [pid 30110] <== frame_dummy() [eax = 0x0] [pid 30110] ==> __do_global_ctors_aux() at 0x080486f0 [pid 30110] <== __do_global_ctors_aux() [eax = 0xffffffff] [pid 30110] <== _init() [eax = 0xffffffff] [pid 30110] <== __libc_csu_init() [eax = 0x804982c] [pid 30110] ==> main() at 0x0804864c [/home/sato/tracef/sample/throw.cpp:26] [pid 30110] ==> a()() at 0x08048604 [/home/sato/tracef/sample/throw.cpp:16] [pid 30110] ==> b()() at 0x080485f0 [/home/sato/tracef/sample/throw.cpp:11] [pid 30110] ==> c(int)(int i <3>) at 0x080485a4 [/home/sato/tracef/sample/throw.cpp:5] [pid 30110] ==> c(int)(int i <2>) at 0x080485a4 [/home/sato/tracef/sample/throw.cpp:5] [pid 30110] ==> c(int)(int i <1>) at 0x080485a4 [/home/sato/tracef/sample/throw.cpp:5] [pid 30110] ==> c(int)(int i <0>) at 0x080485a4 [/home/sato/tracef/sample/throw.cpp:5] [pid 30110] ==> __cxa_allocate_exception@plt() at 0x08048468 [pid 30110] <== __cxa_allocate_exception@plt() [eax = 0x9e70058] [pid 30110] ==> __cxa_throw@plt() at 0x08048478 [pid 30110] ==> __gxx_personality_v0@plt() at 0x080484a8 [pid 30110] <== __gxx_personality_v0@plt() [eax = 0x8] ... [pid 30110] ==> __gxx_personality_v0@plt() at 0x080484a8 [pid 30110] <== __gxx_personality_v0@plt() [eax = 0x7] [pid 30110] ==> __cxa_begin_catch@plt() at 0x08048498 [pid 30110] <== __cxa_begin_catch@plt() [eax = 0x9e70058] [pid 30110] ==> __cxa_end_catch@plt() at 0x08048488 [pid 30110] <== __cxa_end_catch@plt() [eax = 0x0] [pid 30110] <== a()() [eax = 0xff] [pid 30110] <== main() [eax = 0xff] [pid 30110] ==> _fini() at 0x08048718 [pid 30110] ==> __do_global_dtors_aux() at 0x08048520 [pid 30110] <== __do_global_dtors_aux() [eax = 0x0] [pid 30110] <== _fini() [eax = 0x0] [pid 30110] +++ process 30110 detached (ppid 30109) +++
能观察到main函数被调用之后什么函数被调用。
<span class="com" style="color:#8800;">#include <cstdio></span><span class="pln"> </span><span class="com" style="color:#8800;">#include <cstddef></span><span class="pln"> </span><span class="com" style="color:#8800;">// main之前被调用的三个函数</span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> foo</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> </span><span class="lit" style="color:#06666;">1</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> g </span><span class="pun" style="color:#66660;">=</span><span class="pln"> foo</span><span class="pun" style="color:#66660;">();</span><span class="pln"> </span><span class="kwd" style="color:#0088;">struct</span><span class="pln"> bar </span><span class="pun" style="color:#66660;">{</span><span class="pln"> bar</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{}</span><span class="pln"> </span><span class="pun" style="color:#66660;">~</span><span class="pln">bar</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="kwd" style="color:#0088;">throw</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{}</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> g2</span><span class="pun" style="color:#66660;">;</span><span class="pln"> __attribute__</span><span class="pun" style="color:#66660;">((</span><span class="kwd" style="color:#0088;">constructor</span><span class="pun" style="color:#66660;">))</span><span class="pln"> </span><span class="kwd" style="color:#0088;">void</span><span class="pln"> baz</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{}</span><span class="pln"> </span><span class="com" style="color:#8800;">// main之前被初始化的变量value</span><span class="pln"> </span><span class="kwd" style="color:#0088;">template</span><span class="pun" style="color:#66660;"><</span><span class="kwd" style="color:#0088;">const</span><span class="pln"> </span><span class="kwd" style="color:#0088;">char</span><span class="pun" style="color:#66660;">*</span><span class="pln"> S</span><span class="pun" style="color:#66660;">,</span><span class="pln"> std</span><span class="pun" style="color:#66660;">::</span><span class="pln">size_t L</span><span class="pun" style="color:#66660;">,</span><span class="pln"> std</span><span class="pun" style="color:#66660;">::</span><span class="pln">size_t N </span><span class="pun" style="color:#66660;">=</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">></span><span class="pln"> </span><span class="kwd" style="color:#0088;">struct</span><span class="pln"> strSum_ </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">static</span><span class="pln"> </span><span class="kwd" style="color:#0088;">const</span><span class="pln"> </span><span class="kwd" style="color:#0088;">unsigned</span><span class="pln"> </span><span class="kwd" style="color:#0088;">long</span><span class="pln"> value</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">};</span><span class="pln"> </span><span class="kwd" style="color:#0088;">template</span><span class="pun" style="color:#66660;"><</span><span class="kwd" style="color:#0088;">const</span><span class="pln"> </span><span class="kwd" style="color:#0088;">char</span><span class="pun" style="color:#66660;">*</span><span class="pln"> S</span><span class="pun" style="color:#66660;">,</span><span class="pln"> std</span><span class="pun" style="color:#66660;">::</span><span class="pln">size_t L</span><span class="pun" style="color:#66660;">,</span><span class="pln"> std</span><span class="pun" style="color:#66660;">::</span><span class="pln">size_t N</span><span class="pun" style="color:#66660;">></span><span class="pln"> </span><span class="kwd" style="color:#0088;">const</span><span class="pln"> </span><span class="kwd" style="color:#0088;">unsigned</span><span class="pln"> </span><span class="kwd" style="color:#0088;">long</span><span class="pln"> strSum_</span><span class="pun" style="color:#66660;"><</span><span class="pln">S</span><span class="pun" style="color:#66660;">,</span><span class="pln"> L</span><span class="pun" style="color:#66660;">,</span><span class="pln"> N</span><span class="pun" style="color:#66660;">>::</span><span class="pln">value </span><span class="pun" style="color:#66660;">=</span><span class="pln"> S</span><span class="pun" style="color:#66660;">[</span><span class="pln">N</span><span class="pun" style="color:#66660;">]</span><span class="pln"> </span><span class="pun" style="color:#66660;">+</span><span class="pln"> strSum_</span><span class="pun" style="color:#66660;"><</span><span class="pln">S</span><span class="pun" style="color:#66660;">,</span><span class="pln"> L</span><span class="pun" style="color:#66660;">,</span><span class="pln"> N </span><span class="pun" style="color:#66660;">+</span><span class="pln"> </span><span class="lit" style="color:#06666;">1</span><span class="pun" style="color:#66660;">>::</span><span class="pln">value</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="com" style="color:#8800;">// XXX: runtime computation</span><span class="pln"> </span><span class="kwd" style="color:#0088;">template</span><span class="pun" style="color:#66660;"><</span><span class="kwd" style="color:#0088;">const</span><span class="pln"> </span><span class="kwd" style="color:#0088;">char</span><span class="pun" style="color:#66660;">*</span><span class="pln"> S</span><span class="pun" style="color:#66660;">,</span><span class="pln"> std</span><span class="pun" style="color:#66660;">::</span><span class="pln">size_t L</span><span class="pun" style="color:#66660;">></span><span class="pln"> </span><span class="kwd" style="color:#0088;">struct</span><span class="pln"> strSum_</span><span class="pun" style="color:#66660;"><</span><span class="pln">S</span><span class="pun" style="color:#66660;">,</span><span class="pln"> L</span><span class="pun" style="color:#66660;">,</span><span class="pln"> L</span><span class="pun" style="color:#66660;">></span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">static</span><span class="pln"> </span><span class="kwd" style="color:#0088;">const</span><span class="pln"> </span><span class="kwd" style="color:#0088;">unsigned</span><span class="pln"> </span><span class="kwd" style="color:#0088;">long</span><span class="pln"> value </span><span class="pun" style="color:#66660;">=</span><span class="pln"> </span><span class="lit" style="color:#06666;">0</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">};</span><span class="pln"> </span><span class="com" style="color:#8800;">// http://www.thescripts.com/forum/thread156880.html</span><span class="pln"> </span><span class="kwd" style="color:#0088;">template</span><span class="pun" style="color:#66660;"><</span><span class="kwd" style="color:#0088;">typename</span><span class="pln"> T</span><span class="pun" style="color:#66660;">,</span><span class="pln"> std</span><span class="pun" style="color:#66660;">::</span><span class="pln">size_t L</span><span class="pun" style="color:#66660;">></span><span class="pln"> </span><span class="kwd" style="color:#0088;">char</span><span class="pln"> </span><span class="pun" style="color:#66660;">(&</span><span class="pln">lengthof_helper_</span><span class="pun" style="color:#66660;">(</span><span class="pln">T</span><span class="pun" style="color:#66660;">(&)[</span><span class="pln">L</span><span class="pun" style="color:#66660;">]))[</span><span class="pln">L</span><span class="pun" style="color:#66660;">];</span><span class="pln"> </span><span class="com" style="color:#8800;">#define LENGTHOF(array) sizeof(lengthof_helper_(array))</span><span class="pln"> </span><span class="kwd" style="color:#0088;">extern</span><span class="pln"> </span><span class="kwd" style="color:#0088;">const</span><span class="pln"> </span><span class="kwd" style="color:#0088;">char</span><span class="pln"> s</span><span class="pun" style="color:#66660;">[]</span><span class="pln"> </span><span class="pun" style="color:#66660;">=</span><span class="pln"> </span><span class="str" style="color:#0880;">"C++0x"</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="com" style="color:#8800;">// external linkage </span><span class="pln"> </span><span class="kwd" style="color:#0088;">int</span><span class="pln"> main</span><span class="pun" style="color:#66660;">()</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> </span><span class="pun" style="color:#66660;">(</span><span class="kwd" style="color:#0088;">int</span><span class="pun" style="color:#66660;">)</span><span class="pln"> strSum_</span><span class="pun" style="color:#66660;"><</span><span class="pln">s</span><span class="pun" style="color:#66660;">,</span><span class="pln"> LENGTHOF</span><span class="pun" style="color:#66660;">(</span><span class="pln">s</span><span class="pun" style="color:#66660;">)</span><span class="pln"> </span><span class="pun" style="color:#66660;">-</span><span class="pln"> </span><span class="lit" style="color:#06666;">1</span><span class="pun" style="color:#66660;">>::</span><span class="pln">value</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span>
foo(), bar(), baz() 函数、 main()之前被调用、这些函数能够被跟踪出来。成员变量value的初始化也是在运行时发生(编译时不计算。这代码写的很烂大家可不要拷贝 ^^;)、这里根据函数的调用情况并不是都能被初始化,所以很遗憾这里不能跟踪。
$ ../src/tracef --plt -ClAT ./before_main2 [pid 17098] +++ process 17098 attached (ppid 17097) +++ [pid 17098] === symbols loaded: './before_main2' === [pid 17098] ==> _start() at 0x08048370 [pid 17098] ==> __libc_start_main@plt() at 0x08048358 [pid 17098] ==> __libc_csu_init() at 0x080485f0 [pid 17098] ==> _init() at 0x08048310 [pid 17098] ==> call_gmon_start() at 0x08048394 [pid 17098] <== call_gmon_start() [eax = 0x2e75d4] [pid 17098] ==> frame_dummy() at 0x08048420 [pid 17098] <== frame_dummy() [eax = 0x0] [pid 17098] ==> __do_global_ctors_aux() at 0x08048660 [pid 17098] ==> global constructors keyed to _Z3foov() at 0x080485ac [/home/sato/tracef-trunk/sample/before_main2.cpp:44] [pid 17098] ==> __static_initialization_and_destruction_0(int, int)() at 0x08048482 [/home/sato/tracef-trunk/sample/before_main2.cpp:43] [pid 17098] ==> foo()() at 0x08048444 [/home/sato/tracef-trunk/sample/before_main2.cpp:6] [pid 17098] <== foo()() [eax = 0x1] [pid 17098] ==> bar::bar()() at 0x080485c8 [/home/sato/tracef-trunk/sample/before_main2.cpp:10] [pid 17098] <== bar::bar()() [eax = 0x1] [pid 17098] ==> __cxa_atexit@plt() at 0x08048338 [pid 17098] <== __cxa_atexit@plt() [eax = 0x0] [pid 17098] <== __static_initialization_and_destruction_0(int, int)() [eax = 0x141] [pid 17098] ==> baz()() at 0x0804844e [/home/sato/tracef-trunk/sample/before_main2.cpp:16] [pid 17098] <== baz()() [eax = 0x141] [pid 17098] <== global constructors keyed to _Z3foov() [eax = 0x141] [pid 17098] <== __do_global_ctors_aux() [eax = 0xffffffff] [pid 17098] <== _init() [eax = 0xffffffff] [pid 17098] <== __libc_csu_init() [eax = 0x80496bc] [pid 17098] ==> main() at 0x08048454 [/home/sato/tracef-trunk/sample/before_main2.cpp:41] [pid 17098] <== main() [eax = 0x141] [pid 17098] ==> __tcf_0() at 0x0804846e [/home/sato/tracef-trunk/sample/before_main2.cpp:13] [pid 17098] ==> bar::~bar()() at 0x080485ce [/home/sato/tracef-trunk/sample/before_main2.cpp:11] [pid 17098] <== bar::~bar()() [eax = 0x804846e] [pid 17098] <== __tcf_0() [eax = 0x804846e] [pid 17098] ==> _fini() at 0x08048688 [pid 17098] ==> __do_global_dtors_aux() at 0x080483c0 [pid 17098] <== __do_global_dtors_aux() [eax = 0x0] [pid 17098] <== _fini() [eax = 0x0] [pid 17098] +++ process 17098 detached (ppid 17097) +++
交代目前知道的问题点:
$ readelf -h pie_binary | grep Type: Type: DYN (Shared object file)
推荐使用 tracef --plt -CflT 。
Usage: % tracef [option ...] command [arg ...] % tracef [option ...] -p pid Options: -? [ --help ] 显示帮助 -V [ --version ] 显示版本并退出 -o [ --output ] arg 跟踪的结果不输出到stderr、而是输出到'arg'指定的文件里 --ff 记录到每个进程或线程单独的日志里。日志文件名是 「用-o 指定的文件名」+「进程/线程ID」。 -f [ --trace-child ] 用fork()或clone()生成的子进程、子线程也能跟踪。 --synthetic --plt 合成符号也成为跟踪的对象。使用该选项的话、库函数调用和系统调用 (的一部分)也可以跟踪。例如、printf@plt() 和 signal@plt() 等等。 -C [ --demangle ] 将C++低级别的函数名变换成可读形式并表示。 -t [ --time ] 追加现在的时间到输出的各行。 -u [ --microseconds ] 追加现在的时间(单位为微秒)到输出的各行。 -A [ --arg ] 表示函数的参数名(EXPERIMENTAL) -v [ --arg-val ] 表示函数的参数值(EXPERIMENTAL, 仅仅是x86) -T [ --call-tree ] 用树状图表示函数调用。解析对象是多进程/多进程的情况下、 推荐同时使用-o 或 --ff 选项。 --offset arg 指定树形表示时函数调用的偏移量(空格数)。 默认值是3。 --no-pid 不表示进程ID。 -i [ --no-eip ] 不表示函数的地址。 -l [ --line-numbers ] 使用调试信息、表示函数被定义的文件和行号。 -p [ --attach-process ] arg attach 到进程ID为 'arg' 的进程。 -X [ --exclude ] arg 不跟踪 'arg'、无视。可以指定多个。指定arg为mangle后的符号。
想写的时候写到日记里。下面是memo。
<span class="pln"> </span><span class="kwd" style="color:#0088;">switch</span><span class="pun" style="color:#66660;">(...)</span><span class="pln"> </span><span class="pun" style="color:#66660;">{</span><span class="pln"> </span><span class="kwd" style="color:#0088;">case</span><span class="pln"> X</span><span class="pun" style="color:#66660;">:</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> hoge</span><span class="pun" style="color:#66660;">;</span><span class="pln"> </span><span class="com" style="color:#8800;">// leave-ret になる</span><span class="pln"> </span><span class="kwd" style="color:#0088;">default</span><span class="pun" style="color:#66660;">:</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="kwd" style="color:#0088;">return</span><span class="pln"> fuga</span><span class="pun" style="color:#66660;">();</span><span class="pln"> </span><span class="com" style="color:#8800;">// 自分以外の関数を呼ぶ。jmp になりがち</span><span class="pln"> </span><span class="pun" style="color:#66660;">}</span><span class="pln"> </span><span class="com" style="color:#8800;">// 関数の終わり</span>
开发中感到乐趣无穷。
整理了能动态跟踪可执行文件自身的函数的工具的一览表。实现机制并不限定于的ptrace、有许多的方式。 基于ptrace的工具tracef、因为速度上比不上oprofile、可视化的功能上不敌callgrind、所以打算重点是放在实现一个和strace, ltrace 那样的容易使用的工具上。
callgrind和oprofile确实很厉害。