在 gdb 中中断失败断言的正确方法是啥?
Posted
技术标签:
【中文标题】在 gdb 中中断失败断言的正确方法是啥?【英文标题】:What is the proper way to break on failed asserts in gdb?在 gdb 中中断失败断言的正确方法是什么? 【发布时间】:2015-11-11 08:40:19 【问题描述】:我试图在我的程序中捕获失败的断言。我正在使用一个直接调用 assert() 的库,而不是自定义函数或宏,我目前正试图在这个库中跟踪几个与移植相关的错误。所涉及的所有内容都已使用 g++ 中的调试符号编译。
我发现的最佳解决方案是在断言的文件:行处中断,并使用断言表达式的条件。这允许在断言失败之前停止断言,但这是一个可怕的解决方案。它需要对每个可能失败的断言进行特殊设置,在我的 IDE 中无法使用,而且总体上需要付出太多努力。
如何使用 gdb 和 gcc 打破 任何 失败的断言,以允许检查断言调用范围内的调用堆栈和变量?
如果解决方案允许我放弃断言的失败并继续运行,那就更好了。
【问题讨论】:
在 Visual Studio 中的 Windows 上,中断是失败assert
的默认行为。我很惊讶在 *nix 世界中并非如此 - 失败的 assert
通常在那里做什么?
在gdb中,在程序运行之前,可以使用break abort
(或者只是b abort
)在函数abort()处添加断点。这至少可以让您在断言失败时进行回溯(假设它在失败时调用abort()
;某些实现可能会调用exit()
)。但不确定是否继续执行。
通常默认情况下这是有效的,因为 assert() 调用 abort,abort 会引发 SIGABRT 信号,gdb 默认情况下会中断该信号,允许您检查堆栈,向上/向下移动堆栈例如到包含 assert() 和检查变量等的函数。
@notmyfriend 在我的实现中,似乎不是默认中断失败的断言/中止;可能是因为这是一个 MSYS2-MinGW 设置,尽管我可以发誓这对我来说在 Linux 上发生过一次。在任何情况下,break abort
似乎都有效,并且在我的 IDE 中也有效。如果您想将其格式化为答案,我会接受。
@WilliamKappler 似乎您应该自己写下答案并自己接受 - 我相信您可以获得某种徽章。 ;-)。我对 MinGW 有同样的问题。
【参考方案1】:
在abort()
上设置断点似乎是最好的答案。
break abort
在 gdb 的 CLI 中。
【讨论】:
原发帖者和@notmyfriend都在他们的cmets中发布了答案,但是在建议某人写一个官方答案之后,每当我忘记时都会回到这个页面,然后不得不重新阅读所有的 cmets 都想知道它是什么,我只是把它写出来! 这真是天才!【参考方案2】:在 Linux 上不需要break
,只需在提示符下输入bt
abort()
导致在 Linux 中引发SIGABRT
信号,默认情况下 GDB 已经中断信号。例如:
交流
#include <assert.h>
void g(int i)
assert(0);
void f(int i)
g(i);
int main(void)
f(1);
然后:
gcc -std=c99 -O0 -ggdb3 -o a a.c
gdb -ex run ./a
然后只需在 shell 中输入bt
:
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:58
#1 0x00007ffff7a483ea in __GI_abort () at abort.c:89
#2 0x00007ffff7a3ebb7 in __assert_fail_base (fmt=<optimized out>, assertion=assertion@entry=0x555555554788 "0", file=file@entry=0x555555554784 "a.c", line=line@entry=4,
function=function@entry=0x55555555478a <__PRETTY_FUNCTION__.1772> "g") at assert.c:92
#3 0x00007ffff7a3ec62 in __GI___assert_fail (assertion=0x555555554788 "0", file=0x555555554784 "a.c", line=4, function=0x55555555478a <__PRETTY_FUNCTION__.1772> "g")
at assert.c:101
#4 0x00005555555546ca in g (i=1) at a.c:4
#5 0x00005555555546df in f (i=1) at a.c:8
#6 0x00005555555546f0 in main () at a.c:12
其中已经显示了函数值 (f (i=1)
)。
你也可以照常做:
(gdb) f 4
#4 0x00005555555546ca in g (i=1) at a.c:4
4 assert(0);
(gdb) p i
$1 = 1
控制 GDB 是否默认中断信号的设置是:handle all nostop
显示在:How to handle all signals in GDB
在 Ubuntu 16.10、gdb 7.11 中测试。
【讨论】:
注意:在某些平台上(例如某些 Windows shell 下的 MinGW)gdb 看不到 SIGABRT 信号,因此程序只是在断言上死掉而不停止 gdb 但在中止时设置断点仍然有效。 一个值得注意的例外是在 GTest 中运行测试,因此在调试测试时您可能仍希望在abort
上设置断点。【参考方案3】:
如果上述建议的答案对您不起作用,您可以尝试打破 __assert_fail 函数。
break __assert_fail
名称很可能是依赖于实现的,但如果您查看平台上的断言宏定义,很容易找到。这将允许您在 SIGABRT 之前中断。
【讨论】:
【参考方案4】:代码中的另一个选项:
#include <windows.h>
#include <signal.h>
static void abortHandler(int signalNumber)
DebugBreak();
int main()
signal(SIGABRT, &abortHandler);
【讨论】:
以上是关于在 gdb 中中断失败断言的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
断言失败:第 6075 行 pos 12:'child == _child':不正确
断言失败:在 Flutter 中使用 Navigator.popUntil() 时 !_debugLocked 不正确