如何在Cocoa中将堆栈跟踪输出到控制台/日志?

305

我希望在某些点,如失败的断言或未捕获的异常时记录调用跟踪

7个回答

564

这段代码可在任何线程上运行:

NSLog(@"%@", NSThread.callStackSymbols);
返回一个包含调用堆栈符号的数组。每个元素都是一个 NSString 对象,其值的格式由 backtrace_symbols() 函数确定。

15
在最初提出这个问题时不存在的 Mac OS X 10.6 中新增了一个功能。对于 Snow Leopard 之前的版本,可以使用 backtracebacktrace_symbols 函数;请参阅 backtrace(3) 手册页面。 - Peter Hosey
6
仅适用于 iOS 4.0 及以上版本。 - Danra
谢谢!有没有办法让它只打印堆栈跟踪,比如说只打印6层而不是全部? - sudo
9000,直接使用backtrace/backtrace_symbols - dymv
2
@sudo 这是一个数组,所以只需定义范围 [NSThread.callStackSymbols subarrayWithRange:NSMakeRange(0, MIN(6, NSThread.callStackSymbols.count))]; - Albert Renshaw
关于Swift,请参考https://stackoverflow.com/a/53263697/211292。 - undefined

35

n13的回答不太适用 - 我稍微改了一下,得到了这个结果


n13的回答不完全有效 - 我稍作修改后得到了上述结果
#import <UIKit/UIKit.h>

#import "AppDelegate.h"

int main(int argc, char *argv[])
{
    @autoreleasepool {
        int retval;
        @try{
            retval = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
        @catch (NSException *exception)
        {
            NSLog(@"Gosh!!! %@", [exception callStackSymbols]);
            @throw;
        }
        return retval;
    }
}

4
噫...苹果至少在开发应用程序时应该将此作为标准。一堆内存地址太...过时了。 - Russ
我将你的改进加入了我的答案; 这是在ARC之前完成的。谢谢。 - n13
1
这种方法并不适用于所有情况。如果您想捕获所有未处理的异常,这是更好的方法:http://codereview.stackexchange.com/questions/56162/unhandled-exception-handler-that-captures-a-screenshot(该问题中的代码有点过于复杂,但它也不仅仅是记录调用堆栈符号。) - nhgrif
如果你想要实际的异常信息,可以添加 NSLog(@"[Error] - %@ %@", exception.name, exception.reason); - Corentin S.

9

Cocoa已经可以将未捕获异常的堆栈跟踪记录到控制台中,尽管它们只是原始的内存地址。如果您想在控制台中获取符号信息,可以参考苹果公司提供的示例代码

如果您想在代码的任意点生成堆栈跟踪(且使用Leopard操作系统),请查看backtrace man页面。在Leopard之前,您实际上必须深入调用堆栈本身进行研究。


6
显然在iOS 4中可用,但不适用于3.2版本。以下是我使用的内容,毫不害羞地从backtrace手册页面抄袭:#include <execinfo.h>...void* callstack[128]; int i, frames = backtrace(callstack, 128); char** strs = backtrace_symbols(callstack, frames); for (i = 0; i < frames; ++i) { printf("%s\n", strs[i]); } free(strs); - mharper
在HandleException中被调用时,它会写入处理程序函数本身的回溯,而[NSException callStackSymbols]显示的是异常发生的地方的堆栈。但是,如果您将"backtrace(...)"替换为:"NSArray arr = [ex callStackReturnAddresses]; int frames = arr.count; for (i = 0; i < frames; ++i) callstack[i] = (void) [((NSNumber *) [arr objectAtIndex:i]) intValue];",则可以获得当前异常的堆栈跟踪。我想这就是[NSException callStackSymbols]的工作原理:它们返回的跟踪是相等的,在发布中,两个应用程序调用都被_mh_execute_header替换。 - Tertium

6

这篇文章基本上告诉你该怎么做。

实质上,你需要设置应用程序的异常处理来进行日志记录,例如:

#import <ExceptionHandling/NSExceptionHandler.h>

[[NSExceptionHandler defaultExceptionHandler] 
                  setExceptionHandlingMask: NSLogUncaughtExceptionMask | 
                                            NSLogUncaughtSystemExceptionMask | 
                                            NSLogUncaughtRuntimeErrorMask]

1
请注意,这仅适用于已注册的异常处理程序(例如,不适用于@catch块)。 - Barry Wark

2

对于异常情况,您可以使用异常的userInfo字典中的NSStackTraceKey成员来实现此操作。请参见Apple网站上的控制程序对异常的响应


如何在Swift中使用? - Pedro Paulo Amorim

2

用 Swift 这样打印:

print("stack trace:\(Thread.callStackSymbols)")

-2

如果你想将它作为NSString返回。

[NSThread  callStackSymbols].description

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