Xcode 4.2调试无法解析堆栈调用符号

142

我在 Xcode 4.2 中使用 iOS 5 模拟器/设备调试时遇到问题。以下代码会崩溃,这是预期的:

NSArray *arr=[NSArray array];
[arr objectAtIndex:100];
在iOS 4中,我可以获得一堆有用的十六进制数字的堆栈跟踪。但在iOS 5中,它只给了我:

*** First throw call stack:
(0x16b4052 0x1845d0a 0x16a0674 0x294c 0x6f89d6 0x6f98a6 0x708743 0x7091f8 0x7fcaa9 0x2257fa9 0x16881c5 0x15ed022 0x15eb90a 0x15eadb4 0x15eaccb 0x6f02a7 0x6faa93 0x2889 0x2805)

谢谢。

9个回答

259

我尝试了所有方法都无法修复这个问题(尝试了两种编译器、两个调试器等)。在升级XCode进行iOS 5更新后,似乎没有堆栈跟踪起作用。

然而,我找到了一个有效的解决方法 - 创建自己的异常处理程序(也有其他用途)。首先,创建一个函数来处理错误并将其输出到控制台(以及您想对其执行的任何其他操作):

void uncaughtExceptionHandler(NSException *exception) {
    NSLog(@"CRASH: %@", exception);
    NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    // Internal error reporting
}

接下来,在您的应用程序委托中添加异常处理程序:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{   
    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
    // Normal launch stuff
}

就是这样!

如果这个方法不起作用,那么只有两个可能的原因:

  1. 有东西在覆盖你的 NSSetUncaughtExceptionHandler 调用(整个应用程序只能有一个处理程序)。例如,一些第三方库设置了它们自己的未捕获异常处理程序。所以,在你的 didFinishLaunchingWithOptions 函数的结尾处设置它(或者有选择性地禁用第三方库)。或者更好的办法是,在 NSSetUncaughtExceptionHandler 上设置一个符号断点,快速查看谁在调用它。您可能想要做的是修改当前的处理程序而不是添加另一个处理程序。
  2. 你实际上没有遇到异常(例如,EXC_BAD_ACCESS 不是异常; 感谢@Erik B的评论,下面)

2
这好像不起作用 -- uncaughtExceptionHandler例程从未被调用。 - Hot Licks
1
请问您能否更具体地说明如何使用它?对我来说它好像无效。 - Dani Pralea
1
非常感谢!令人困惑的是,苹果为何不将这种基本功能实现到IDE中。 - devios1
1
@HenrikErlandsson EXC_BAD_ACCESS 不是未捕获的异常。当您尝试访问不属于您的内存时,会出现 EXC_BAD_ACCESS。通常是因为您正在尝试使用已经被释放的对象。NSZombie 应该可以帮助您解决这个问题。 - Erik B
1
如果不是操作系统代码中未处理的异常,那么它是什么呢? - Henrik Erlandsson
显示剩余11条评论

110

添加异常断点是一个有用的选项(使用断点导航器底部的+号)。这将在任何异常情况下触发断点(或您可以设置条件)。我不知道这个选择是否在4.2中新增,还是我只是试图解决缺少符号的问题时才注意到它。

一旦您触发了这个断点,您可以像往常一样使用调试导航器来导航调用堆栈、检查变量等等。

如果您确实需要一个适合复制/粘贴等操作的符号化调用堆栈,则可以从那里使用gdb backtrace:

(gdb) bt
#0  0x01f84cf0 in objc_exception_throw ()
#1  0x019efced in -[NSObject doesNotRecognizeSelector:] ()
(etc)

3
目前为止这对我来说完美无缺。现在调试器会在崩溃的那一行停下来,不需要回溯了。 - Tim
1
这对我也完美地起作用了。非常感谢,没有这个断点我会疯掉的... - William Denniss
+1 表示它起作用了。虽然它不会给你一个很好的错误消息来解释异常的原因,但这是一个开始... - Nicu Surdu
你是我的 HotD @WiseOldDuck。 - Maverick1st
这为我恢复了预期的行为。注意:在新项目上也记得重新添加断点! - MechEthan
这个之前对我来说是可以工作的,但今天突然就不行了。XCode 在 Cocoa 汇编代码中的 objc_exception_throw 处停止了,但堆栈上没有有用的信息(是的,我已经将堆栈详细信息滑块调到最右边了)。然而,上面 Zane 的回答对我仍然有效。所以,如果你遇到这种情况,可以尝试一下那个方法。 - Joe Strout

46

调试器上有一个新功能。您可以设置断点,每当抛出异常时都会停止执行,就像在4.0上一样。

在“断点导航器”中添加“异常断点”,然后只需在选项弹出窗口上按“完成”即可。

就这些!

备注:在某些情况下,仅针对Objective-C异常进行中断可能更好。


这绝对是适合我的解决方案。 - bradgonesurfing
这对我来说就是问题所在。我和一位同事共享同一个Xcode项目,我问他是否遇到了这个问题,但他没有。不同之处在于,他的项目在objective-c异常(objc_exception_throw)上中断了。 - horseshoe7
你刚刚帮助找到了一个无法追踪的错误。非常感谢你。我一直在到处寻找这样的东西。 - rjgonzo
1
这个非常好用!这正是我一直在寻找的,这比添加异常处理器更好,因为添加断点可以带你到抛出异常的地方,异常处理器虽然也能起作用但只是给你一个想法。 - im8bit

21

这里还有一种解决方案,不如之前那么优雅,但如果您没有添加异常断点或处理程序,则可能只有一种方法可行。
当应用程序崩溃并且您获得了原始的第一个throw调用堆栈(使用十六进制数字),请在Xcode控制台中输入info line *hex(不要忘记星号和0x十六进制指示符),例如:

(gdb) info line *0x2658
Line 15 of "path/to/file/main.m" starts at address 0x25f2 <main+50>
and ends at 0x267e <main+190>.
如果你正在使用 lldb,你可以输入 image lookup -a hex(在这种情况下不需要星号),并且你会得到类似的输出。通过这种方法,你可以从throw堆栈的顶部(大约有5-7个系统异常传播器)遍历到导致崩溃的函数,并确定代码的确切文件和行。同样,为了达到类似的效果,你可以在终端中使用atos实用程序,只需输入:
atos -o path/to/AplicationBundle.app/Executable 0xAdress1 0xAdress2 0xAdress3 ...

而且您会得到符号化的堆栈跟踪(至少对于您具有调试符号的函数)。 此方法更可取,因为您不需要为每个地址调用 info line,只需从控制台输出中复制地址并将它们粘贴到终端中即可。


9
您可以在断点导航器底部使用 + 添加一个异常断点,并添加操作 bt(点击添加操作按钮,选择调试器命令,在文本字段中输入“ bt”)。这将在抛出异常时立即显示堆栈跟踪。

6
在你的主函数中使用以下代码:
int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    int retVal;
    @try {
        retVal = UIApplicationMain(argc, argv, nil, nil);
    }
    @catch (NSException *exception) {
        NSLog(@"CRASH: %@", exception);
        NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    }
    @finally {
        [pool release];
    }
    return retVal;
}

这似乎不能在Storyboard中工作。 2012-06-04 20:34:52.211 Problems[1944:207]如果应用程序想要使用一个主Storyboard文件,应用程序委托必须实现window属性。 2012-06-04 20:34:52.213 Problems[1944:207]预期应用程序在应用程序启动结束时具有根视图控制器。 - macasas

6
在Xcode的调试控制台提示符下输入以下命令:

image lookup -a 0x1234

它会显示类似以下内容:
  Address: MyApp[0x00018eb0] (MyApp.__TEXT.__text + 91088)
  Summary: MyApp`-[MyViewController viewDidAppear:] + 192 at MyViewController.m:202

谢谢,我真的正在寻找这个。令人惊讶的是,没有一种快捷方式可以将整个“第一次抛出调用堆栈”显示为调用堆栈,因为我猜Python lldb脚本很容易编写。 - Ilya

6
这是一个常见问题,在4.2版本中无法获得堆栈跟踪信息。您可以尝试在LLDB和GDB之间切换,看看是否能获得更好的结果。请在此处提交错误报告:http://developer.apple.com/bugreporter/ 编辑:我认为如果你切换回LLVM GCC 4.2,就不会出现这种情况了。但您可能会失去需要的功能。

是的,我尝试了切换编译器,但问题仍然存在。不过还是谢谢你 :) - cekisakurek
他建议更换调试器,而不是编译器。 - bames53
1
FYI:在这种情况下,与您正在使用的编译器或调试器的版本无关。这是来自iOS的控制台输出的更改。 - clarkcox3
有趣的是这方面的经验差异如此之大 - 我认为存在一些问题。我无法让调试器在异常断点处停止。从GDB切换到LLDB解决了这个问题。 - Matt

1

重新打开“编译为Thumb”(调试配置)对我有用。


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