我正在尝试调试一个经常导致GDB不在断点时停止并显示SIGTRAP的程序。加载动态库和其他普通内容时会发生这种情况。大概有1000种这样的情况在我的断点最终被击中之前发生,因此对我而言,手动“继续”所有这些不相关的SIGTRAP是不可行的。但是,如果使用命令handle SIGTRAP nostop noprint,那么GDB不会在我的断点处停止。
似乎必须有一种教育GDB的方法,以便它了解哪种SIGTRAP有利于停止,而哪种不利于停止。显然,GDB知道它是否处于断点,因为输出非常可靠地不同:在断点处,它提及“ breakpoint”并显示断点编号-但在其他任何SIGTRAP中,它仅表示“ SIGTRAP”。因此,我真的希望GDB自己对自己说:“哇,这是SIGTRAP,这里没有断点-看着我,我要停止并打印一个SIGTRAP消息。无用的SIGTRAP消息完全破坏了调试会话!我如何安静地继续呢?” 请让我知道是否有人可以这样做。
您可以设置捕获点来捕获 SIGTRAP 信号并添加命令来决定是继续还是停止。在此处理程序中,您可以检查便利变量,例如$_siginfo信号的原因。
特别令人感兴趣的是$_siginfo.si_code,它的值取决于传递的信号。该的sigaction(2) Linux的手册页描述的确切关系。您可以通过编译程序或查看标头(在我的系统上它使用标头)来找到那些SI_USER、SI_KERNEL等代码的数值/usr/include/bits/siginfo.h。我遇到的一些价值观是:
SI_USERSI_KERNELTRAP_TRACE有了这些信息,这里是一个捕获 SIGTRAP 并打印原因的示例,然后继续:
catch signal SIGTRAP
commands
p $_siginfo.si_code
c
end
# Set another breakpoint for testing
break sleep
Run Code Online (Sandbox Code Playgroud)
现在考虑这个休眠 5 秒的测试程序,然后在 x86(-64) 上触发调试陷阱:
#include <unistd.h>
int main(void) {
for (;;) {
sleep(5);
asm("int3");
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该程序一直停gdb在该int3行,因为信号被捕获(si_code恰好是 0x80, SI_KERNEL),但随后再次重复该指令。所以要跳过这条指令,程序计数器 ( $pc) 必须递增。这样做之后,我了解到有关SIGTRAP和的信息si_code:
SI_KERNEL)触发 SIGTRAP 。TRAP_TRACE会收到一个带有代码 2 ( )的 SIGTRAP (因为 的捕获点SIGTRAP)。int3指令使用代码 128 触发 SIGTRAP。因此您需要一些东西来区分指令。以下是跳过int3陷阱并仍然保持断点功能的最终 GDB 命令:
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
Run Code Online (Sandbox Code Playgroud)
最后一点:SIGTRAP 是调试器内部使用的,有可能上面捕获的太多了。这是在 Arch Linux 上用 GDB 7.10 测试的。