我可以为未处理的(Objective)C++异常获取堆栈跟踪吗?

6
我正在开发一款iOS应用程序,最近它的C++基础变得很大。C++不是我的强项,我对异常感到沮丧。我正在寻找一种方法,可以获得到抛出异常的现场堆栈跟踪信息(未处理的)异常。我会说,“未处理”的限定词是可选的; 作为最后手段,我可以断点任何异常抛出,尽管未处理的异常是理想的。
目前我得到的信息是无用的。假设我在调用栈上没有适当的异常处理程序,并且我执行以下操作:
std::vector<int> my_vector;
my_vector.at(40) = 2; // Throws std::out_of_range

该应用程序在main()中崩溃,并出现一条日志消息,显示“terminate called throwing an exception.” 毫无意义。将通用的try/catch块放在调用栈较高的位置也没有帮助,因为在异常处理期间,调用栈会向上展开到catch块的点,使我无法知道异常的实际起源。这也适用于提供自己的terminate_handler。断言更有用,但它们要求我在某种程度上预期错误条件,而这并非总是可行。即使意外异常通过了我的预防性assert(),我仍希望调试器能够步入调试。
我想避免的是不得不为每次可能抛出异常的调用都包装一个try/catch块,只是为了获得堆栈跟踪以找到错误。在运行时,我确实不感兴趣捕获这些异常。当它们发生时,意味着程序执行存在致命缺陷,无法正常继续。我只想被通知,以便可以确定原因并修复问题,以便不再出现。
在Objective C中,我可以在objc_exception_throw上设置符号断点,任何时候如果我犯了错误,就会立即中断执行,并显示一个漂亮的堆栈跟踪,以便我知道问题所在。非常有用。
我意识到这种行为实际上只有在两种语言之间的异常处理上存在哲学差异时才有用。Objective C异常仅用于表示不可恢复的错误。例行错误处理任务通过错误返回代码完成。这意味着任何Objective C异常都是开发人员断点的绝佳选择。
C++似乎对异常具有不同的用途。它们用于处理致命错误和一般错误(至少在我使用的第三方库中是这样)。这意味着我可能不会真的希望在C++中抛出的每个异常都进行断点,但如果我无法只在未处理的异常上断点,我仍然认为这种能力很有用。

C++异常并不是为了处理“常规”错误而设计的,但有时候它们会被过度使用。虽然这是真的,但它们并不仅仅用于不可恢复的错误。 - bames53
3个回答

5
您可以在Xcode中快速建立所有C++ throw条件的断点:
- cmd+6 - “+”按钮->添加异常断点 - C++ -> std::out_of_range - 在抛出时
更新:
如果您有很多要筛选的内容,您可能更喜欢:
- 创建符号断点 - 符号= __cxa_throw(可能因std库而异) - 操作>调试器命令= bt - 在eval之后自动继续=打开
bt命令记录回溯信息。配置为此方式后,它将自动继续。
因此,这将记录每个抛出异常的回溯信息 - 当您的程序由于未处理的异常而终止时,线索将在最终记录的回溯信息中(通常是最后一个,除非库重新抛出)。

然而我还在挣扎。似乎你应该能够指定只有某些异常才会中断。我正在使用ZXing,它无处不抛出异常(每帧多个,在正常操作期间)。我需要忽略这些异常。但是,当我在完全不相关的东西上设置断点,比如std::out_of_range,断点仍然会在所有ZXing的std::exception子类上触发。这正常吗? - Matt Wilding
好的,如果我理解正确的话,调试器应该在任何异常抛出时中断,记录回溯信息,然后自动继续执行?这种方法的优点是我可以通过阅读最近的所有异常堆栈跟踪来找到我关心的那些异常。 - Matt Wilding
@MattWilding 没错。如果您按照所述设置,它将在不暂停的情况下打印。然后,您的问题通常会出现在输出结束处,这将使您能够相对快速地定位问题。 - justin
这很有道理,但是无论“评估操作后自动继续”设置如何,Xcode(GDB)似乎都会停止执行。我已经尝试了几个断点。对于 C++ 异常断点的“命名”设置似乎旨在仅允许在特定异常上中断,但也没有得到尊重。 - Matt Wilding
@MattWilding 奇怪,它在 Xc4.2 + 10.6.8 上按照我描述的方式工作。哦!你看到第二个选项正在使用符号断点吗? - justin
太好了。你说的每件事听起来都非常有道理,所以给你打个绿勾。感谢你提供这些信息。 - Matt Wilding

1
在应用程序中,我可以使用许多C++异常进行调试。我会将“Catch C++ Exceptions on Throw”选项关闭,直到我到达应用程序将抛出异常的点,然后我打开该选项,通常下一个抛出的异常就是我要找的。这将比错误发生的位置深入几个级别,但堆栈保持完整,因此您可以弄清楚发生了什么。

0

请查看PLCrashReporter。我们将其与我们的应用程序一起使用(该应用程序严重依赖于C++),它甚至可以为C++代码生成堆栈跟踪。

您可能遇到的唯一问题是在使用未针对iOS本地编写的汇编例程时(苹果的编译器使用R7来保存堆栈帧以追溯符号,这不符合官方的ARM EBI规范)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接