【问题标题】:How do I get the value and type of the current exception in C++ using gdb?如何使用 gdb 在 C++ 中获取当前异常的值和类型?
【发布时间】:2013-04-25 03:51:06
【问题描述】:

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

【问题讨论】:

    标签: c++ debugging exception gdb inspection


    【解决方案1】:

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

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

    详情请见the page from the manual

    【讨论】:

      【解决方案2】:

      更新


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

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

      如果您以交互方式调用函数,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
      

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

      【讨论】:

        【解决方案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 *thrown_exception_type = exception_header->exceptionType;
        5. 您将获得一个异常类型,然后您可以使用为您的代码定义的其余 RTTI 进行查找

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-07-31
          • 2019-03-08
          • 2019-08-31
          • 2010-11-19
          • 1970-01-01
          • 2014-01-14
          • 1970-01-01
          相关资源
          最近更新 更多