33

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

4

3 回答 3

10

早期的答案在编写时(2013 年)是正确的,但从那时起 gdb 和 libstdc++ 发生了变化。

libstdc++ 现在有一些钩子可以让 gdb 更好地与异常系统交互。特别是,现在为 gdb 公开了足够的信息来为$_exception用户提供方便的变量。该变量保存被抛出的异常。它仅在捕获异常的确切位置有效;您可以停止使用catch catch.

有关详细信息,请参阅手册中的页面。

于 2015-10-20T18:40:19.800 回答
8

更新


这是 GDB 手册中的一些信息

目前在 gdb 中对 C++ 异常处理(catch throw 和 catch catch)有一些限制:

如果您以交互方式调用函数,gdb 通常会在函数完成执行后将控制权返回给您。但是,如果调用引发异常,则该调用可能会绕过将控制权返回给您的机制,并导致程序中止或继续运行,直到遇到断点、捕获 gdb 正在侦听的信号或退出。即使您为异常设置了捕获点,情况也是如此;在交互式调用中禁用异常捕获点。您不能以交互方式引发异常。您不能以交互方式安装异常处理程序。有时 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 中捕获“抛出”并捕获“捕获”,如果您真的想遵循整个流程。

gdb> catch catch  
gdb> catch throw

这样,您将在抛出异常之前和捕获异常时立即获得断点,然后您可以遍历堆栈以获取有关正在发生的事情的更多信息。即使您处于另一个断点,您也应该能够向上走堆栈(使用向上或向下)以获取异常可见的帧。

于 2013-05-01T18:28:35.957 回答
3

简短的回答:你不能,因为大多数异常处理工作都是在你的程序之外完成的,因此不在 gdb 的范围内。

解释答案:

有时抛出异常的行没有符号

如果您正在调试的二进制文件没有调试符号,那么该二进制文件可能已被剥离,您将根本无法找到任何关于任何类型/值的内容。

如何检查当前异常的值?

我认为您在这里假设异常是 gdb 可以检查的语言功能;事实上,C++ 中的一个例外是 C++ 作为一种语言、libc++ 和 ABI 的特性的组合。甚至可能有不止一个活动电流异常。

就像 UpAndAdam 指出的那样,您可以使用类型说明符在 catch 块中设置断点,然后检查该元素,但我怀疑您的问题是在您找到“catch (...)”的情况下。在这些情况下,除非您深入研究异常处理的实现,否则您将无法了解有关当前异常的很多信息。

通过非常简短且不完整的描述,我们可以说抛出异常:

  1. 您的程序将调用 libc++ 来引发异常
  2. libc++ 将在 glibc 中调用“unwind”来启动堆栈展开
  3. unwind 将从 libc++ 中为每个堆栈帧回调一个“个性函数”(基本上是堆栈中的每个函数调用)
  4. 个性函数将以某种方式决定当前堆栈帧是否可以处理此异常
  5. 如果可以处理异常,将执行 catch 块

现在,很难谈论细节,因为很多异常处理取决于您的工具链(编译器、平台、架构、libc++ 等),但在大多数情况下,“catch (...)”甚至不会收到原始异常作为论据。无论如何,要以某种方式回答您的问题:在 gcc 和 gnu 的 libc++ 中,您可以尝试这样的事情:

  1. 获取带有调试符号的 libc++
  2. 在 __gxx_personality_v0 中设置断点(称为个性函数)。将调用此函数以确定堆栈帧(基本上是函数调用)是否具有合适的 catch 块来处理异常
  3. 在个性函数中,您将能够找到指向 _Unwind_Exception 的指针,它是您真正异常的包装器
  4. 获取异常的类型信息,如下所示: __cxa_exception *exception_header = (__cxa_exception*)(unwind_exception+1)-1; std::type_info *throw_exception_type = exception_header->exceptionType;
  5. 您将获得一个异常类型,然后您可以使用为您的代码定义的其余 RTTI 进行查找

无论如何,您可能需要花费大量时间来尝试了解异常处理是如何在您的平台中实现的。如果您想了解更多关于异常处理的信息,我在过去花了一些时间写了关于 @ http://monoinfinito.wordpress.com/series/exception-handling-in-c/的主题。它不是官方来源,但它确实链接到处理异常所涉及的每个部分的规范。

于 2013-05-06T14:10:14.520 回答