我正在尝试调试一个程序,该程序通常会导致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_USER
,SI_KERNEL
等代码的数值(在我的系统上,它使用 header /usr/include/bits/siginfo.h
)。我遇到的一些值(value)观是:
SI_USER
SI_KERNEL
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
)。这样做之后,我了解了有关SIGTRAP
和si_code
的信息:SI_KERNEL
)触发SIGTRAP。 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进行了测试。