对于崩溃的收集有不少第三方的不错的工具,好比Bugly。可是若是想要本身收集崩溃日志(好比对于企业内部发布的应用,在局域网内使用),天然也是有方法的。linux
这里介绍两种抓取崩溃堆栈信息的方法,若是发现问题,但愿不吝赐教。也算是给本身作个笔记。:-)git
方法一:本身调用c函数抓取堆栈信息,话很少说,代码来objective-c
#import <Foundation/Foundation.h>服务器
extern NSString *const SKSAppExceptionInfo;架构
@interface SKSExceptionRecord : NSObject函数
+ (void)startExceptionHandler;工具
@endoop
#import "SKSExceptionRecord.h"post
#include <execinfo.h>ui
NSString *const SKSAppExceptionInfo = @"SKSAppExceptionInfo";
static int s_fatal_signals[] = {
SIGABRT,
SIGBUS,
SIGFPE,
SIGILL,
SIGSEGV,
SIGTRAP,
SIGTERM,
SIGKILL,
};
static const char* s_fatal_signal_names[] = {
"SIGABRT",
"SIGBUS",
"SIGFPE",
"SIGILL",
"SIGSEGV",
"SIGTRAP",
"SIGTERM",
"SIGKILL",
};
static int s_fatal_signal_num = sizeof(s_fatal_signals) / sizeof(s_fatal_signals[0]);
//信号处理函数
void signalHandler(int signal) {
for (int i = 0; i < s_fatal_signal_num; ++i) {
if (signal == s_fatal_signals[i]) {
//signal 异常不上传log,但还需捕捉,否则Exception异常也不上传
//[SKSExceptionRecord handleException:[NSString stringWithFormat:@"%s",s_fatal_signal_names[i]] description:[SKSExceptionRecord backtrace]];
break;
}
}
}
void exceptionHandler(NSException *exception) {
[SKSExceptionRecord handleException:[exception reason] description:[exception callStackSymbols]];
}
@implementation SKSExceptionRecord
+ (NSArray *)backtrace {
void *callstack[128];
int frames = backtrace(callstack, 128);
char **strs = backtrace_symbols(callstack, frames);
NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
for (int i = 0; i < frames; ++i) {
[backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
}
free(strs);
return backtrace;
}
+ (void)startCrashHandler
{
// 1 linux错误信号捕获
for (int i = 0; i < s_fatal_signal_num; ++i) {
signal(s_fatal_signals[i], signalHandler);
}
// 2 objective-c未捕获异常的捕获
NSSetUncaughtExceptionHandler(&exceptionHandler);
}
+ (void)handleException:(NSString *)reason description:(id)description
{
//这里抛出消息,应用中可根据须要监听该消息,作相应的崩溃处理,好比上传到服务器
NSDictionary *dicInfo = [NSDictionary dictionaryWithObjectsAndKeys:reason, @"reason", description, @"description", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:SKSAppExceptionInfo object:nil userInfo:dicInfo];
//[self keepAppSurvive];
}
//+ (void)keepAppSurvive
//{
// CFRunLoopRef runLoop = CFRunLoopGetCurrent();
// CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
//
// //BOOL dismissed = NO;
// while (YES)
// {
// for (NSString *mode in (__bridge NSArray *)allModes)
// {
// CFRunLoopRunInMode((__bridge CFStringRef)mode, 0.001, false);
// }
// sleep(2);
// //dismissed = YES;
// }
//
// CFRelease(allModes);
//}
@end
此方法的缺陷,若是符号表被disable,那么抓取的信息将不具备可读性,只是纯粹的内存堆栈。
方法二:使用KSCrash,它的好处是能够将崩溃堆栈直接解析成可读字符。首先须要去git下载KSCrash,将其中的source目录添加到应用工程,或是将KSCrash工程直接添加到应用。添加后基本须要扩展本身的方法来处理崩溃信息,由于其中已经包含的方法(好比邮件)可能并非你想要的长久的崩溃追踪方法。
如下是我定义的崩溃log收集方法:
#import <Foundation/Foundation.h>
#import "KSCrashInstallation.h"
#import "KSCrash.h"
#import "KSCrashAdvanced.h"
@interface SKSCrashManager : NSObject
+ (SKSCrashManager *)sharedManager;
- (void)checkLocalCrashLogs:(KSCrashReportFilterCompletion)block;
@end
#import "SKSCrashManager.h"
#import "KSCrashInstallation+Private.h"
#import "KSSingleton.h"
#import "KSCrashReportFilterAppleFmt.h"
#import "SkySeaManager.h"
#import "SKSEncrypt.h"
NSString *const SKSAppExceptionWithKSCrash = @"SKSAppExceptionWithKSCrash";
//-----------------SKSCrashReportSink----------------------------------------------
@interface SKSCrashReportSink : NSObject <KSCrashReportFilter>
- (id <KSCrashReportFilter>) defaultCrashReportFilterSet;
@end
@implementation SKSCrashReportSink
- (id <KSCrashReportFilter>) defaultCrashReportFilterSet
{
//这个方法很重要,KSAppleReportStyleSymbolicated这种格式的才能返回可读的crash log
return [KSCrashReportFilterPipeline filterWithFilters:
[KSCrashReportFilterAppleFmt filterWithReportStyle:KSAppleReportStyleSymbolicated],
self,
nil];
}
- (void) filterReports:(NSArray*) reports
onCompletion:(KSCrashReportFilterCompletion) onCompletion
{
NSDictionary *dicInfo = [NSDictionary dictionaryWithObjectsAndKeys:reports, @"reports", onCompletion, @"callback", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:SKSAppExceptionWithKSCrash object:nil userInfo:dicInfo];
}
@end
//--------------------SKSCrashInstallation-----------------------------------------
@interface SKSCrashInstallation : KSCrashInstallation
+ (SKSCrashInstallation*) sharedInstance;
@end
@implementation SKSCrashInstallation
IMPLEMENT_EXCLUSIVE_SHARED_INSTANCE(SKSCrashInstallation)
- (id) init
{
if((self = [super initWithRequiredProperties:nil]))
{
}
return self;
}
- (id<KSCrashReportFilter>) sink
{
SKSCrashReportSink* sink = [[SKSCrashReportSink alloc] init];
return [KSCrashReportFilterPipeline filterWithFilters:[sink defaultCrashReportFilterSet], nil];
}
@end
static SKSCrashManager *g_crashManager = NULL;
@interface SKSCrashManager()
@property (strong, nonatomic) KSCrashInstallation* crashInstallation;
@end
@implementation SKSCrashManager
+ (SKSCrashManager *)sharedManager {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (g_crashManager == NULL) {
g_crashManager = [[SKSCrashManager alloc] init];
[g_crashManager installCrashHandler];
}
});
return g_crashManager;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void) installCrashHandler
{
[self configureAdvancedSettings];
self.crashInstallation = [SKSCrashInstallation sharedInstance];
[self.crashInstallation install];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(uploadExceptionLog:) name:SKSAppExceptionWithKSCrash object:nil];
}
static void advanced_crash_callback(const KSCrashReportWriter* writer)
{
// You can add extra user data at crash time if you want.
writer->addBooleanElement(writer, "some_bool_value", NO);
}
- (void) configureAdvancedSettings
{
KSCrash* handler = [KSCrash sharedInstance];
handler.deleteBehaviorAfterSendAll = KSCDeleteOnSucess;
handler.zombieCacheSize = 16384*4;
handler.deadlockWatchdogInterval = 0;
handler.userInfo = @{@"someKey": @"someValue"};
handler.onCrash = advanced_crash_callback;
handler.printTraceToStdout = NO;
handler.searchThreadNames = YES;
handler.searchQueueNames = YES;
handler.handlingCrashTypes = KSCrashTypeAll;
}
- (void)checkLocalCrashLogs:(KSCrashReportFilterCompletion)block {
[self.crashInstallation sendAllReportsWithCompletion:block];
}
- (void)uploadExceptionLog:(NSNotification *)note
{
//这里作相应的崩溃处理
}
@end
以上都是在抓取到崩溃后,抛出消息,而后能够在任意地方作崩溃处理。能够依据本身架构需求作相应地改动。