使 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 打印回溯的最后一帧(即它是当前函数)ccontinue 的快捷方式,以继续执行,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),然后使用grepegrep 过滤结果。

缩进更难,但是bt 输出是有序的,因此您在跟踪的顶部有当前函数,在最底部有main

所以你用命令创建文件:

br <function name where to break>
run
bt
kill
quit

然后运行gdb &lt;program&gt; -x&lt;command file&gt;

过滤以#&lt;digit&gt; 开头的字符串 - 你会得到堆栈跟踪。

【讨论】:

但这需要手动编写每个函数名,可能有数百个!

以上是关于使 GDB 在调用函数时打印控制流的主要内容,如果未能解决你的问题,请参考以下文章

gdb:在多线程程序中调用函数而不进行线程

如何在gdb中打印特定条件下的变量?

GDB:如何在调试期间调用具有修改参数的函数

gdb 使用啥机制来知道在哪里“完成”函数调用?

GDB:打印/转储到文件时自动展平结构

20145234黄斐《信息安全系统设计基础》GDB调试汇编堆栈过程分析