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

时间:2013-05-01 05:29:36

标签: c++ debugging exception gdb inspection

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

3 个答案:

答案 0 :(得分:8)

<强>更新


以下是GDB手册中的一些信息

  

目前C ++异常处理存在一些限制(catch   在gdb中抛出并捕获catch:

     

如果以交互方式调用函数,gdb通常会将控制权返回给   你在函数完成执行时。如果通话提出了   但是,例外,调用可能会绕过返回的机制   控制你并导致你的程序中止或简单   继续运行直到遇到断点,捕获gdb的信号   正在倾听或退出。即使您设置了一个,也是如此   异常的捕获点;异常的捕获点被禁用   在互动电话中。您不能以交互方式提出异常。   您无法以交互方式安装异常处理程序。有时赶上   不是调试异常处理的最佳方法:如果您需要知道   确切地说,引发异常的地方,最好在之前停止   调用异常处理程序,因为这样你就可以看到堆栈   在任何放松之前。如果在中设置断点   异常处理程序,它可能不容易找出在哪里   提出异常。

     

要在调用异常处理程序之前停止,您需要一些   实施知识。在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

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

答案 1 :(得分:6)

早期的答案在撰写时(2013年)是正确的,但从那以后gdb和libstdc ++已经改变。

libstdc ++现在有一些钩子让gdb与异常系统更好地交互。特别是,现在有足够的信息暴露给gdb,以便为用户提供$_exception便利变量。此变量保存抛出的异常。它仅在捕获异常的地方有效;您可以停止使用catch catch

有关详细信息,请参阅the page from the manual

答案 2 :(得分:3)

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

解释答案:

  

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

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

  

如何检查当前异常的值?

我认为你在这里假设一个异常是gdb可以检查的语言特性;实际上,C ++中的一个例外是C ++作为语言,libc ++和ABI的特性组合。甚至可能存在多个活动当前异常。

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

有一个非常简短和不完整的描述,我们可以说抛出异常:

  1. 您的程序将调用libc ++来引发异常
  2. libc ++将在glibc中调用“unwind”来启动堆栈展开
  3. unwind将从libc ++中为每个堆栈帧回调一个“个性函数”(堆栈中的每个函数调用,基本上)
  4. 个性化函数将以某种方式决定当前堆栈帧是否能够处理此异常
  5. 如果可以处理异常,则执行catch块
  6. 现在,很难谈论细节,因为很多异常处理取决于你的工具链(编译器,平台,架构,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 * thrown_exception_type = exception_header-&gt; exceptionType;
    5. 您将获得一个异常类型,然后您可以使用为您的代码定义的其余RTTI进行查找
    6. 无论如何,您可能需要花费大量时间来了解如何在您的平台中实现异常处理。如果你想阅读更多关于异常处理的内容,我过去花了一些时间写了关于@ http://monoinfinito.wordpress.com/series/exception-handling-in-c/主题的文章。它不是官方来源,但它确实链接到处理异常所涉及的每个部分的规范。