debugging 如何在gdb中获取C++中当前异常的值和类型?

pod7payv  于 2023-10-24  发布在  其他
关注(0)|答案(3)|浏览(159)

gdb允许在抛出异常时捕获异常,以及在捕获异常时捕获异常。但有时抛出异常的行没有符号,或者在异常处理期间触发断点。如何检查当前异常的值?

hsvhsicv

hsvhsicv1#

早期的答案在编写时(2013年)是正确的,但从那时起,gdb和libstdc发生了变化。
libstdc
现在有一些钩子,让gdb更好地与异常系统交互。特别是,现在有足够的信息公开给gdb,为用户提供一个$_exception方便变量。这个变量保存被抛出的异常。它只在异常被捕获的位置有效;你可以在使用catch catch时停止。
详情请参见the page from the manual

bd1hkmkf

bd1hkmkf2#

更新

下面是广发手册中的一些信息
目前在gdb中对C异常处理(catch throw和catch catch)有一些限制:
如果你以交互方式调用一个函数,gdb通常会在函数完成执行后将控制权返回给你。但是,如果调用引发异常,调用可能会绕过将控制权返回给你的机制,导致你的程序要么中止,要么继续运行,直到它遇到断点,捕获gdb正在监听的信号,或exits。即使您为异常设置了捕获点,情况也是如此;异常上的捕获点在交互式调用中被禁用。您不能交互式引发异常。您不能交互式安装异常处理程序。有时catch不是调试异常处理的最佳方法:如果你需要知道异常发生的确切位置,最好在异常处理程序被调用之前停止,因为这样你就可以在任何展开发生之前看到堆栈。如果你在异常处理程序中设置断点,可能不容易找到异常发生的位置。
要在异常处理程序被调用之前停止,你需要一些实现的知识。在gnu C
的情况下,异常是通过调用一个名为__raise_exception的库函数来引发的,该函数具有以下ANSI C接口:

/* addr is where the exception identifier is stored.
        id is the exception identifier.  */
     void __raise_exception (void **addr, void *id); To make the debugger catch all exceptions before any stack unwinding takes place,

在__raise_exception上设置断点(请参阅断点、监视点和断点)。
所述
这取决于代码和你在堆栈中的位置。如果你实际上捕获了异常,如:

try { .... } catch (std::exception &e) {
   //do stuff
}

你可以尝试打印e.what(),或者查看异常的成员。如果你只是将其捕获为(...),那么我不确定你能收集到什么。
你可以做的另一件处理事情是在gdb中捕获'throw',如果你真的想跟踪整个流程,也可以捕获'catch'。

gdb> catch catch  
gdb> catch throw

这样你就可以在抛出异常之前得到一个断点,在捕获异常的时候得到一个断点,然后你可以遍历堆栈来获得更多关于发生了什么的信息。即使你在另一个断点,你也应该能够遍历堆栈(使用up或down)来获得异常可见的帧。

tag5nh1u

tag5nh1u3#

简短的回答:你不能,因为异常处理的大部分工作是在你的程序之外完成的,因此在gdb的范围之外。
解释答案:
有时抛出异常的行没有符号
如果你正在调试的二进制文件没有调试符号,那么这个二进制文件很可能被剥离了,你将无法找到任何关于类型/值的信息。
如何检查当前异常的值?
我认为你在这里假设异常是gdb可以检查的语言特性;事实上,C中的异常是C作为一种语言的特性,libc和ABI的组合。
就像UpAndAdam指出的那样,你可以用类型说明符在catch块中设置一个断点,然后检查那个元素,但我怀疑你的问题是在你找到一个“catch(...)"的情况下。在这些情况下,除非你深入研究异常处理的实现,否则你将无法了解更多关于当前异常的信息。
通过一个非常简短和不完整的描述,我们可以说抛出一个异常:
1.您的程序将调用libc
来引发异常

  1. libc++将在glibc中调用“unwind”来启动堆栈展开
  2. unwind将从libc中为每个堆栈帧(基本上是堆栈中的每个函数调用)回调一个“个性化函数”。
    1.个性函数将以某种方式决定当前堆栈帧是否可以处理此异常
    1.如果可以处理异常,则将执行catch块
    现在,很难谈论细节,因为很多异常处理取决于你的工具链(编译器,平台,架构,libc
    等),但在大多数情况下,“catch(...)”甚至不会接收原始异常作为参数。无论如何,为了回答你的问题:在gcc中使用gnu的libc++,你可以尝试这样做:
    1.获取带有调试符号的libc++
    1.在__gxx_personality_v0中设置一个断点(称为personality函数)。该函数将被调用以确定堆栈帧(基本上是函数调用)是否有合适的catch块来处理异常
    1.在personality函数中,可以找到指向_Unwind_Exception的指针,它是真实的异常的 Package 器
    1.像这样获取异常的类型信息:__cxa_exception exception_header =(__cxa_exception)(unwind_exception+1)-1; std::type_info *thrown_exception_type = exception_header->exceptionType;
    1.您将获得一个异常类型,然后可以使用为代码定义的RTTI的其余部分查找该异常类型
    无论如何,你可能需要花很多时间来理解异常处理是如何在你的平台上实现的。如果你想了解更多关于异常处理的知识,我在过去花了一些时间写了关于@http://monoinfinito.wordpress.com/series/exception-handling-in-c/的主题。它不是官方来源,但它确实有链接到处理异常所涉及的每个部分的规范。

相关问题