我正在尝试调试一个程序,该程序通常会导致GDB不在断点时停止并显示SIGTRAP。加载动态库和其他普通内容时会发生这种情况。在我的断点最终被击中之前,大约有1,000种这样的情况发生,所以对我而言,手动“继续”所有这些不相关的SIGTRAP是不可行的。但是,如果我使用handle SIGTRAP nostop noprint命令,那么GDB不会在我的断点处停止。

似乎必须有一种教育GDB的方法,以便它了解哪种SIGTRAP有利于停止,而哪种不利于停止。显然,GDB知道它是否在断点处,因为输出非常可靠地不同:在断点处,它提到“断点”并显示断点编号-但是在其他任何SIGTRAP中,它只是说“SIGTRAP”。因此,我真的希望GDB自己对自己说:“哇,这是SIGTRAP,这里没有断点-看着我,我要停止并打印一个SIGTRAP消息。无用的SIGTRAP消息完全破坏了调试 session !我如何安静地继续下去呢?”请让我知道是否有人可以做到这一点。

最佳答案

您可以设置catchpoints来捕获SIGTRAP信号,并添加命令来决定继续还是停止。在此处理程序中,您可以检查convenience variables(例如 $_siginfo )作为信号的原因。

特别感兴趣的是$_siginfo.si_code,它的值取决于传递的信号。 sigaction(2) Linux手册页描述了确切的关系。您可以通过编译程序或查找 header 来找到这些SI_USERSI_KERNEL等代码的数值(在我的系统上,它使用 header /usr/include/bits/siginfo.h)。我遇到的一些值(value)观是:

  • 0x00(0):SI_USER
  • 0x80(128):SI_KERNEL
  • 0x02(2):TRAP_TRACE

  • 掌握了这些信息后,这里有一个捕获SIGTRAP并打印原因,然后继续的示例:
    catch signal SIGTRAP
    commands
     p $_siginfo.si_code
     c
    end
    # Set another breakpoint for testing
    break sleep
    

    现在考虑以下测试程序,该程序休眠5秒钟,然后在x86(-64)上触发调试陷阱:
    #include <unistd.h>
    int main(void) {
        for (;;) {
            sleep(5);
            asm("int3");
        }
        return 0;
    }
    

    由于捕获了信号,该程序一直在gdb行停止int3(si_code恰好是0x80,SI_KERNEL),但随后再次重复执行该指令。因此,要跳过此指令,必须增加程序计数器($pc)。这样做之后,我了解了有关SIGTRAPsi_code的信息:
  • 断点使用代码128(SI_KERNEL)触发SIGTRAP。
  • 继续进行分组处理之后,将接收到代码为2(TRAP_TRACE)的SIGTRAP(因为SIGTRAP的捕获点)。
  • int3指令使用代码128触发SIGTRAP。因此,您需要一些区分指令的方法。

  • 以下是最终的GDB命令,这些命令将跳过int3陷阱并仍使断点保持功能:
    catch signal SIGTRAP
    commands
     silent # do not print catchpoint hits
     # ignore the int3 instruction (this address was looked up at
     # the tracepoint using print $pc)
     if $pc == 0x400568
      set $pc++ # skip int3
      c
     end
     # Ignore TRAP_TRACE that is used for breakpoints
     if $_siginfo.si_code == 2
      c
     end
    end
    

    最后一点:调试器在内部使用SIGTRAP,以上内容可能捕获的过多。这已在Arch Linux上使用GDB 7.10进行了测试。

    10-01 11:54