GDB:自动'下一步'?

时间:2011-04-28 00:54:25

标签: c debugging gdb

这次快点。

是否有可能(除了永远按下输入之外)gdb通过一行程序不断地next来查找错误发生的位置?

编辑:continue不是我想要的;我希望能够有效地看到完整的程序执行,就像你从next一遍又一遍地得到的那样。

5 个答案:

答案 0 :(得分:7)

这就是这样的黑客攻击我发布它有点尴尬。但是,如果你只需要一次性,它可能会让你获得你想要的信息。确实应该有更好的方法。

您可以定义一个愚蠢的小gdb脚本,该脚本执行stepnext命令一定次数:

# file: step_mult.gdb

define step_mult
    set $step_mult_max = 1000
    if $argc >= 1
        set $step_mult_max = $arg0
    end

    set $step_mult_count = 0
    while ($step_mult_count < $step_mult_max)
        set $step_mult_count = $step_mult_count + 1
        printf "step #%d\n", $step_mult_count
        step
    end
end

(我使用step代替next没有特别好的理由;只需将其更改为您需要的任何内容。)

然后你可以运行那个命令(带有一个可选的计数),它会很好地显示每个stepnext

这是一个示例程序,当它试图取消引用NULL指针时会崩溃:

#include<stdio.h>

int x[] = {
    0, 1, 2, 3, 4, 5, 6, 7, 8,9, 10 
};

int* p[11];

int main()
{
    int i;

    for (i = 0; i < 11; ++i) {
        p[i] = &x[i];
    }

    p[5] = 0;

    for (i = 0; i < 11; ++i) {
        printf( "*p[%d] == %d\n", i, *p[i]);
    }

    return 0;
}

这是一个gdb会话(在Windows上)调试该程序并使用step_mult脚本:

C:\temp>gdb test.exe
GNU gdb (GDB) 7.2
...
Reading symbols from C:\temp/test.exe...done.

(gdb) source c:/temp/step_mult.gdb
(gdb) start

Temporary breakpoint 1 at 0x401385: file C:\temp\test.c, line 23.
Starting program: C:\temp/test.exe
[New Thread 5396.0x1638]

Temporary breakpoint 1, main () at C:\temp\test.c:23
23          for (i = 0; i < 11; ++i) {

(gdb) step_mult 70

step #1
24              p[i] = &x[i];
step #2
23          for (i = 0; i < 11; ++i) {
step #3
24              p[i] = &x[i];
step #4
23          for (i = 0; i < 11; ++i) {
step #5
24              p[i] = &x[i];
step #6
23          for (i = 0; i < 11; ++i) {
step #7
24              p[i] = &x[i];
step #8
23          for (i = 0; i < 11; ++i) {
step #9
24              p[i] = &x[i];
step #10
23          for (i = 0; i < 11; ++i) {
step #11
24              p[i] = &x[i];
step #12
23          for (i = 0; i < 11; ++i) {
step #13
24              p[i] = &x[i];
step #14
23          for (i = 0; i < 11; ++i) {
step #15
24              p[i] = &x[i];
step #16
23          for (i = 0; i < 11; ++i) {
step #17
24              p[i] = &x[i];
step #18
23          for (i = 0; i < 11; ++i) {
step #19
24              p[i] = &x[i];
step #20
23          for (i = 0; i < 11; ++i) {
step #21
24              p[i] = &x[i];
step #22
23          for (i = 0; i < 11; ++i) {
step #23
27          p[5] = 0;
step #24
29          for (i = 0; i < 11; ++i) {
step #25
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #26
*p[0] == 0
29          for (i = 0; i < 11; ++i) {
step #27
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #28
*p[1] == 1
29          for (i = 0; i < 11; ++i) {
step #29
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #30
*p[2] == 2
29          for (i = 0; i < 11; ++i) {
step #31
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #32
*p[3] == 3
29          for (i = 0; i < 11; ++i) {
step #33
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #34
*p[4] == 4
29          for (i = 0; i < 11; ++i) {
step #35
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #36

Program received signal SIGSEGV, Segmentation fault.
0x004013d2 in main () at C:\temp\test.c:30
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #37

Program received signal SIGSEGV, Segmentation fault.
0x004013d2 in main () at C:\temp\test.c:30
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #38

Program exited with code 030000000005.
step #39
The program is not being run.
(gdb)

不幸的是,由于在发生段错误时脚本没有停止,因此gdb决定只是停止调试程序,因此您无法进行任何进一步的有用查询。但是日志可能仍然有用。

我确信有很多方法可以使脚本更加智能化。不幸的是,我不知道如何做到这一点,GDB的用户级文档似乎对这些细节没有太大帮助。最好的方法是,如果脚本可以检测到段错误或信号已经发生,然后停止,而不是依赖于某些任意计数。我想gdb / MI界面,甚至可能是Python脚本界面可能都有一个很好的机制,但我对这些没有任何了解。

第一次运行后,您可以使用显示的计数(在我的示例中为37)并重新启动程序,并给出一个与之前崩溃的位置相差无几的计数并手动控制。

就像我说的那样,it's not particularly pretty - but it might get you there

答案 1 :(得分:0)

您可以使用continuec继续执行下一个断点。

另见Run an Application in GDB Until an Exception Occurs (StackOverflow)关于如何设置“catchpoint”,它会在抛出异常时中断执行。

答案 2 :(得分:0)

continue命令将一直运行,直到断点发生,应用程序导致异常(即崩溃)或应用程序终止。

答案 3 :(得分:0)

另见这篇文章:c - GDB auto stepping - automatic printout of lines, while free running?,它使用gthon的python接口。

答案 4 :(得分:0)

当程序出现段错误时,GDB 将默认停止(与许多其他信号一样),因此您可以检查 backtrace 和状态,例如通过更改 frame N 然后 {{ 1}}。

如果你想发出 print vars 直到有事情发生,那么 python api 可以提供帮助(需要一个启用了 python 的 GDB,但现在大多数都支持,如果你正在调试“嵌入式”或在其他情况下不起作用,通常可以在要调试的系统上使用 next 并使用带有“fat pc”的 GDB 远程调试(当时是当前的,启用了 python 并且可能支持多个目标 [你可以远程-从 win32 调试到 GNU/Linux)。 以下代码实现了自动下一步功能:

gdbserver

您可以将它放在 gdb-auto-step.py 中,该文件可以随时使用 import gdb import time class CmdAutoNext (gdb.Command): """Auto-Next through the code until something happens or manually interrupted. An argument says how often `next` is done (1-19, default 5).""" def __init__(self): print('Registering command auto-step') super(CmdAutoNext, self).__init__("auto-next", gdb.COMMAND_RUNNING) gdb.events.stop.connect(stop_handler_auto_next) def invoke(self, argument, from_tty): number = 5 # optional: use a parameter for the default if argument: if not argument.isdigit(): raise gdb.GdbError("argument must be a digit, not " + argument) number = int(argument) if number == 0 or number > 19: raise gdb.GdbError("argument must be a digit between 1 and 19") sleep_time = 3.0 / (number * 1.4) try: frame = gdb.newest_frame() except gdb.error: raise gdb.GdbError("No stack.") global last_stop_was_simple last_stop_was_simple = True pagination = gdb.execute("show pagination", False, True).find("on") if pagination: gdb.execute("set pagination off", False, False) try: while (last_stop_was_simple): gdb.execute ("next") time.sleep(sleep_time) except KeyboardInterrupt as ki: if pagination: gdb.execute("set pagination on", False, False) except gdb.GdbError as user_error: if pagination: gdb.execute("set pagination on", False, False) # pass user errors unchanged raise user_error except: if pagination: gdb.execute("set pagination on", False, False) traceback.print_exc() def stop_handler_auto_next(event): # check the type of stop, the following is the common one after step/next, # a more complex one would be a subclass (for example breakpoint or signal) global last_stop_was_simple last_stop_was_simple = type(event) is gdb.StopEvent CmdAutoNext() 激活(或包含在 .gdbinit 文件中以使其始终可用)。

代码说明:

  • 定义一个用户命令(带有额外的参数来说明自动步进的速度)
  • 可能要添加:为默认值定义一个参数(为简单起见,此处替换为固定值)
  • 在python中循环“next”命令,处理CTRL-C的“预期”键盘中断
  • 注册一个 source gdb-auto-step.py 事件处理程序,用于检查停止原因并将步骤类型存储在那里
  • 调整 while 循环以停止“不简单”的停止(断点/观察点/信号/...) - 如果您真的希望它忽略观察点或其他场景,您可以调整停止处理程序