我的代码中似乎存在某种多线程错误,导致每运行 30 次测试套件就会崩溃。该测试套件是非交互式的。我想在 gdb 中运行我的测试套件,如果程序正常退出,则让 gdb 正常退出;如果程序崩溃,则中断(并显示调试提示)。这样我就可以让测试套件重复运行,去喝杯咖啡,回来后就会看到一个很好的调试提示。我怎样才能用 gdb 做到这一点?
这有点hacky,但你可以这样做:
gdb -ex='set confirm on' -ex=run -ex=quit --args ./a.out
如果 a.out 正常终止,它只会让你退出 GDB。但如果你崩溃了,程序仍然会处于活动状态,因此 GDB 通常会提示你是否确实想以活动的下级退出:
Program received signal SIGABRT, Aborted.
0x00007ffff72dad05 in raise (sig=...) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
in ../nptl/sysdeps/unix/sysv/linux/raise.c
A debugging session is active.
Inferior 1 [process 15126] will be killed.
Quit anyway? (y or n)
就像我说的,不太漂亮,但只要您没有关闭退出活动进程的提示,它就可以工作。可能还有一种方法可以使用 gdb 的
quit
命令:它需要一个数字参数,该参数是调试会话的退出代码。所以也许你可以使用 --eval-command="quit stuff",其中 stuff 是一些 GDB 表达式,反映下级是否正在运行。
这个程序可以用来测试一下:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
if (time(NULL) % 2) {
raise(SIGINT);
}
puts("no crash");
return EXIT_SUCCESS;
}
最简单的方法是使用gdb
提供的
Python API:
def exit_handler(event):
gdb.execute("quit")
gdb.events.exited.connect(exit_handler)
您甚至可以用一行代码完成:
(gdb) python gdb.events.exited.connect(lambda x : gdb.execute("quit"))
您还可以检查返回代码,以确保它是您期望的“正常”代码
event.exit_code
。
您可以将其与 @acm 提到的
--eval-command
或 --command
结合使用,从命令行或使用 .gdbinit
文件注册事件处理程序。
也可以在程序崩溃时触发回溯,让gdb带着子进程的返回码退出:
gdb -return-child-result -ex run -ex "thread apply all bt" -ex "quit" --args myProgram -myProgramArg
创建一个名为
.gdbinit
的文件,启动 gdb 时将使用该文件。
run
quit
不带任何选项运行:
gdb --args prog arg1...
您告诉 gdb 运行并退出,但如果发生错误,它应该停止处理文件。
让它在崩溃时转储核心。如果您使用的是 Linux,请阅读
man core
手册页,如果您正在运行 ulimit
,请阅读 bash
内置函数。
这样,当它崩溃时,你会找到一个不错的核心文件,可以将其提供给 gdb:
$ ulimit -c unlimited
$ ... run program ..., gopher coffee (or reddit ;)
$ gdb progname corefile
如果您将以下几行放入
~/.gdbinit
文件中,则当您的程序以状态代码 0 退出时,gdb
将会退出。
python
def exit_handler ( event ):
if event .exit_code == 0:
gdb .execute ( "quit" )
gdb .events .exited .connect ( exit_handler )
end
以上是Kevin答案的精炼。
崩溃时没有获取到核心文件吗?像这样“gdb -c core”启动 gdb 并进行堆栈回溯。
您更有可能想要使用 Valgrind。