我正在寻找像ltrace或strace这样的工具,它可以跟踪可执行文件中的本地定义函数.ltrace仅跟踪动态库调用,而strace仅跟踪系统调用.例如,给定以下C程序:
#includeint triple ( int x ) { return 3 * x; } int main (void) { printf("%d\n", triple(10)); return 0; }
运行程序ltrace
将显示调用,printf
因为这是一个标准库函数(我的系统上是一个动态库),strace
并将显示启动代码,用于实现printf的系统调用和关闭代码的所有系统调用,但我想要一些能告诉我函数triple
被调用的东西.假设优化编译器没有内联本地函数,并且二进制文件没有被剥离(符号被删除),是否有工具可以做到这一点?
编辑
几点澄清:
如果该工具还提供非本地功能的跟踪信息,那也没关系.
我不想重新编译支持特定工具的程序,可执行文件中的符号信息应该足够了.
如果我可以使用该工具附加到现有的进程,就像我可以使用ltrace/strace一样,我会非常高兴.
Johannes Sch.. 52
假设您只想获得特定功能的通知,您可以这样做:
用调试信息编译(因为你已经有了符号信息,你可能也有足够的调试)
特定
#includeint fac(int n) { if(n == 0) return 1; return n * fac(n-1); } int main() { for(int i=0;i<4;i++) std::cout << fac(i) << std::endl; }
使用gdb跟踪:
[js@HOST2 cpp]$ g++ -g3 test.cpp [js@HOST2 cpp]$ gdb ./a.out (gdb) b fac Breakpoint 1 at 0x804866a: file test.cpp, line 4. (gdb) commands 1 Type commands for when breakpoint 1 is hit, one per line. End with a line saying just "end". >silent >bt 1 >c >end (gdb) run Starting program: /home/js/cpp/a.out #0 fac (n=0) at test.cpp:4 1 #0 fac (n=1) at test.cpp:4 #0 fac (n=0) at test.cpp:4 1 #0 fac (n=2) at test.cpp:4 #0 fac (n=1) at test.cpp:4 #0 fac (n=0) at test.cpp:4 2 #0 fac (n=3) at test.cpp:4 #0 fac (n=2) at test.cpp:4 #0 fac (n=1) at test.cpp:4 #0 fac (n=0) at test.cpp:4 6 Program exited normally. (gdb)
以下是我收集所有函数地址的方法:
tmp=$(mktemp) readelf -s ./a.out | gawk ' { if($4 == "FUNC" && $2 != 0) { print "# code for " $NF; print "b *0x" $2; print "commands"; print "silent"; print "bt 1"; print "c"; print "end"; print ""; } }' > $tmp; gdb --command=$tmp ./a.out; rm -f $tmp
请注意,不是只打印当前帧(bt 1
),而是可以执行任何您喜欢的操作,打印某些全局值,执行一些shell命令或者发送一些内容(如果它命中fatal_bomb_exploded
函数):)可悲的是,gcc输出一些"当前语言已更改"两者之间的消息.但这很容易被人看出来.没什么大不了.
假设您只想获得特定功能的通知,您可以这样做:
用调试信息编译(因为你已经有了符号信息,你可能也有足够的调试)
特定
#includeint fac(int n) { if(n == 0) return 1; return n * fac(n-1); } int main() { for(int i=0;i<4;i++) std::cout << fac(i) << std::endl; }
使用gdb跟踪:
[js@HOST2 cpp]$ g++ -g3 test.cpp [js@HOST2 cpp]$ gdb ./a.out (gdb) b fac Breakpoint 1 at 0x804866a: file test.cpp, line 4. (gdb) commands 1 Type commands for when breakpoint 1 is hit, one per line. End with a line saying just "end". >silent >bt 1 >c >end (gdb) run Starting program: /home/js/cpp/a.out #0 fac (n=0) at test.cpp:4 1 #0 fac (n=1) at test.cpp:4 #0 fac (n=0) at test.cpp:4 1 #0 fac (n=2) at test.cpp:4 #0 fac (n=1) at test.cpp:4 #0 fac (n=0) at test.cpp:4 2 #0 fac (n=3) at test.cpp:4 #0 fac (n=2) at test.cpp:4 #0 fac (n=1) at test.cpp:4 #0 fac (n=0) at test.cpp:4 6 Program exited normally. (gdb)
以下是我收集所有函数地址的方法:
tmp=$(mktemp) readelf -s ./a.out | gawk ' { if($4 == "FUNC" && $2 != 0) { print "# code for " $NF; print "b *0x" $2; print "commands"; print "silent"; print "bt 1"; print "c"; print "end"; print ""; } }' > $tmp; gdb --command=$tmp ./a.out; rm -f $tmp
请注意,不是只打印当前帧(bt 1
),而是可以执行任何您喜欢的操作,打印某些全局值,执行一些shell命令或者发送一些内容(如果它命中fatal_bomb_exploded
函数):)可悲的是,gcc输出一些"当前语言已更改"两者之间的消息.但这很容易被人看出来.没什么大不了.
System Tap可以在现代Linux机器上使用(Fedora 10,RHEL 5等).
首先下载para-callgraph.stp脚本.
然后运行:
$ sudo stap para-callgraph.stp 'process("/bin/ls").function("*")' -c /bin/ls 0 ls(12631):->main argc=0x1 argv=0x7fff1ec3b038 276 ls(12631): ->human_options spec=0x0 opts=0x61a28c block_size=0x61a290 365 ls(12631): <-human_options return=0x0 496 ls(12631): ->clone_quoting_options o=0x0 657 ls(12631): ->xmemdup p=0x61a600 s=0x28 815 ls(12631): ->xmalloc n=0x28 908 ls(12631): <-xmalloc return=0x1efe540 950 ls(12631): <-xmemdup return=0x1efe540 990 ls(12631): <-clone_quoting_options return=0x1efe540 1030 ls(12631): ->get_quoting_style o=0x1efe540
另请参阅:Observe,systemtap和oprofile更新
假设您想在~/Desktop/datalog-2.2/datalog
使用参数调用它时跟踪所有函数-l ~/Desktop/datalog-2.2/add.lua ~/Desktop/datalog-2.2/test.dl
cd /usr/src/linux-`uname -r`/tools/perf
for i in `./perf probe -F -x ~/Desktop/datalog-2.2/datalog`; do sudo ./perf probe -x ~/Desktop/datalog-2.2/datalog $i; done
sudo ./perf record -agR $(for j in $(sudo ./perf probe -l | cut -d' ' -f3); do echo "-e $j"; done) ~/Desktop/datalog-2.2/datalog -l ~/Desktop/datalog-2.2/add.lua ~/Desktop/datalog-2.2/test.dl
sudo ./perf report -G
假设您可以使用gcc选项重新编译(无需更改源代码)要跟踪的代码-finstrument-functions
,则可以使用etrace来获取函数调用图.
这是输出的样子:
\-- main | \-- Crumble_make_apple_crumble | | \-- Crumble_buy_stuff | | | \-- Crumble_buy | | | \-- Crumble_buy | | | \-- Crumble_buy | | | \-- Crumble_buy | | | \-- Crumble_buy | | \-- Crumble_prepare_apples | | | \-- Crumble_skin_and_dice | | \-- Crumble_mix | | \-- Crumble_finalize | | | \-- Crumble_put | | | \-- Crumble_put | | \-- Crumble_cook | | | \-- Crumble_put | | | \-- Crumble_bake
在Solaris上,truss(strace equivalent)能够过滤要跟踪的库.当我发现strace没有这样的能力时,我感到很惊讶.