Xcode/LLDB:如何获取刚刚抛出的异常信息?

85

好的,假设我的 objc_exception_throw 断点刚刚触发了。我正在调试器提示符处,想要获取有关异常对象的更多信息。我在哪里可以找到它?


2
请记住,异常刚刚被引发,它的描述还没有被打印到控制台上。 - Karoy Lorentey
请查看这个问题:https://dev59.com/P3RB5IYBdhLWcg3wH0WW - Nikolai Fetissov
4个回答

164

异常对象作为第一个参数传递给 objc_exception_throw。 LLDB 提供了 $arg1..$argn 变量以引用正确调用约定中的参数,从而可以简单地打印异常详细信息:

(lldb) po $arg1
(lldb) po [$arg1 name]
(lldb) po [$arg1 reason]

在执行这些命令之前,请确保在调用堆栈中选择objc_exception_throw帧。请参阅WWDC15会话视频中的“高级调试和地址污点分析器”以查看此过程的演示。

已过时信息

如果你正在使用GDB,那么引用第一个参数的语法取决于你运行的体系结构的调用约定。如果你在实际的iOS设备上进行调试,则对象的指针在寄存器r0中。要打印它或向它发送消息,请使用以下简单语法:

(gdb) po $r0
(gdb) po [$r0 name]
(gdb) po [$r0 reason]

在iPhone模拟器上,所有的函数参数都是通过栈传递的,因此语法相当可怕。我能构造的最短表达式是*(id *)($ebp + 8)。为了使事情不那么痛苦,建议使用方便变量:

(gdb) set $exception = *(id *)($ebp + 8)
(gdb) po $exception
(gdb) po [$exception name]
(gdb) po [$exception reason]

如果想要在断点触发时自动设置$exception,可以向objc_exception_throw断点添加命令列表来实现。

(注意,在我测试的所有情况中,异常对象也同时出现在触发断点时的eaxedx寄存器中。但我不确定这种情况是否总是如此。)

以下为从下面评论中添加的内容:

LLDB 中,选择objc_exception_throw的堆栈帧,然后输入这个命令:

(lldb) po *(id *)($esp + 4)

6
在lldb中要如何做到这一点?我得到了一个错误“error: reference to 'id' is ambiguous”。 - offex
2
你能提供这个信息的来源吗?我想多了解一些。 - João Nunes
3
目前在 LLDB 中,在 prologue 之前,以下命令对我有效:po *(id *)($esp + 4),可以捕获 objc_exception_throw 异常。 - wbyoung
7
这个方法可行!但是,直到我选择了堆栈帧0才起作用。(objc_exception_throw)。 - funroll
7
在模拟器中,po $eax 对我很有用,作为设备上的 $r0 的替代品。 - monkeydom
显示剩余3条评论

12
在新的模拟器上(iOS 8,64位),我在使用Xcode 6时遇到了异常框架:objc_exception_throw
po $rax

在32位系统中:

po $eax

什么是rax?

rax是一个64位寄存器,用于替代旧版的eax。

如何查找所有寄存器?

register read

来源:维基百科


在Xcode 6.1中,我遇到了以下问题:(lldb) po $rax 错误:无法实现:无法读取寄存器rax的值 在执行时出错,无法准备执行JIT表达式。 - bradheintz
@bradheintz 模拟器还是真机?我已经尝试了6.0.1版本。 - João Nunes
你可以提供一下这个的源链接吗?谢谢! - Chris Conover
我刚在 lldb 中写了这个命令:register read。 通过这个信息,我们知道异常帧中的第一个寄存器保存了异常消息。 - João Nunes
好的,我找到了一些文档: rax是一个64位寄存器:在64位长模式下,您可以使用64位寄存器(例如rax代替eax,rbx代替ebx等)。 - João Nunes
现在似乎需要将这些寄存器转换为NSException。 po (NSException*)$eax po (NSException*)$rax - Rickster

7

在撰写本文时,我的Google搜索结果中排名最高的是“lldb打印异常”。因此,我添加了这个答案来解决lldb和x86_64的问题。

我的尝试使用po $eax查找异常失败了,出现了error: Couldn't materialize struct: Couldn't read eax (materialize)的错误。之前其他回答中提到的尝试也都失败了。

关键在于,我必须先点击我的主线程中的objc_exception_throw帧。 lldb不会直接进入该帧。

在我所有的搜索和按照之前的例子操作后,这篇博客文章是第一篇以一种对我有效的方式解释的文章。它更加现代化,发布于2012年8月。


1
如果有catch语句,请在其中设置断点,此时您可以检查异常对象。
如果没有catch语句,请继续。
您将在终端中收到此类消息:
由于未捕获的异常“NSInvalidArgumentException”,应用程序终止,原因:“*” - [__NSPlaceholderDictionary initWithObjects:forKeys:count:]:尝试从objects [0]插入nil对象
然而,您可能正在寻找一种在终止应用程序时不继续的方法,因为这样会丢失漂亮的堆栈跟踪。
为此,Fnord的答案似乎是最好的选择,但我无法在LLDB中使其正常工作。

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