使 GDB 在调用函数时打印控制流
Posted
技术标签:
【中文标题】使 GDB 在调用函数时打印控制流【英文标题】:Make GDB print control flow of functions as they are called 【发布时间】:2010-09-23 15:22:23 【问题描述】:如何使 gdb 在调用时打印感兴趣的函数,根据它们在堆栈中的深度进行缩进?
我希望能够说出类似(编造)的话:
(gdb) trace Foo* Bar* printf
并让 gdb 打印所有以 Foo 或 Bar 开头的函数,因为它们被调用。有点像 gnu cflow,除了使用调试符号并且只打印实际调用的函数,而不是所有可能的调用流。
无法提供帮助的工具包括 cachegrind、callgrind 和 oprofile,它们对最常调用函数的结果进行排序。我需要保留调用顺序。
通配符(或等效的)是必不可少的,因为有很多 Foo 和 Bar funcs。虽然我会满足于记录每个功能。或者,也许告诉 gdb 记录特定库中的所有函数。
一些 GDB 向导必须有一个脚本来完成这个常见的工作!
【问题讨论】:
不将问题标记为“社区 wiki”可能会帮助您获得更多(和更好)的答案。 Tool to trace local function calls in Linux 的可能重复项 balau82.wordpress.com/2010/10/06/… 【参考方案1】:在你的情况下,我会求助于 gdb 中的 define
命令,它允许你定义一个函数,该函数最多可以接受 10 个参数。
您可以将函数的名称作为参数传递给您定义的函数以“跟踪”,或者将它们全部记录在函数本身中。我会做类似以下的事情
define functiontrace
if $arg0
break $arg0
commands
where
continue
end
end
if $arg1
...
gdb 中用户定义函数的参数引用为 $arg0-$arg9。或者,您可以只在函数中记录您想要跟踪的每个函数,而不是使用 $arg0-9。
注意:这不会缩进堆栈跟踪中的深度,但会在每次调用函数时打印堆栈跟踪。我发现这种方法比 strace 等更有用...因为它会记录您想要的 任何 函数、系统、库、本地或其他。
【讨论】:
【参考方案2】:rbreak
cmd 接受正则表达式来设置断点。您可以使用:
(gdb) rbreak Foo.*
(gdb) rbreak Bar.*
(gdb) break printf
See this 了解断点的详细信息。
然后使用commands
打印调用的每个函数。例如。设 α = 最后一个断点的编号(如果错过了可以用i br
查看),然后这样做:
(gdb) commands 1-α
Type commands for breakpoint(s) 1-α, one per line.
End with a line saying just "end".
>silent
>bt 1
>c
>end
(gdb)
一些详细说明:silent
抑制不必要的信息消息,bt 1
打印回溯的最后一帧(即它是当前函数),c
是 continue
的快捷方式,以继续执行,end
只是命令列表的分隔符。
注意:如果您跟踪库函数,您可能需要等待库被加载。例如。为main
或任何函数设置一个断点,运行应用程序直到该点,然后才设置你想要的断点。
【讨论】:
谢谢。关于我们如何在函数退出(返回)时进行跟踪的任何想法? (因为只跟踪调用了哪些函数,所以函数 A 调用 B 然后 C 与 A 调用 B 调用 C 的情况没有区别。)【参考方案3】:为工作使用正确的工具;)
How to print the next N executed lines automatically in GDB?
【讨论】:
一个 GDB 脚本就足够了。【参考方案4】:您是否看到 litb 对类似帖子 here 的出色回答?
他使用 readelf 获取有趣的符号,使用 gdb 命令获取跟踪,并使用 awk 粘合所有这些。
基本上你需要改变的是修改他的 gdb 命令脚本以从回溯中删除 1 深度以查看堆栈和过滤特定功能,并使用 awk/python/(...) 脚本重新格式化输出以呈现它就像一棵树。 (我承认我现在懒得做……)
【讨论】:
这看起来是一个非常相似的问题,尽管更笼统的是我专门询问了 gdb(尽管我会对任何事情感到满意)。所有的答案似乎都是错误的。您指向的那个中断了一个特定的感兴趣的函数,并找到了它的所有调用者。我想要所有 func 调用。【参考方案5】:您可以在批处理模式下调用gdb
(使用-x
选项),在需要的地方中断并请求回溯(bt
),然后使用grep
或egrep
过滤结果。
缩进更难,但是bt
输出是有序的,因此您在跟踪的顶部有当前函数,在最底部有main
。
所以你用命令创建文件:
br <function name where to break>
run
bt
kill
quit
然后运行gdb <program> -x<command file>
过滤以#<digit>
开头的字符串 - 你会得到堆栈跟踪。
【讨论】:
但这需要手动编写每个函数名,可能有数百个!以上是关于使 GDB 在调用函数时打印控制流的主要内容,如果未能解决你的问题,请参考以下文章