码迷,mamicode.com
首页 > 移动开发 > 详细

iOS中崩溃收集

时间:2015-12-25 18:45:46      阅读:1116      评论:0      收藏:0      [点我收藏+]

标签:

对于崩溃的收集有很多第三方的不错的工具,比如Bugly。但是如果想要自己收集崩溃日志(比如对于企业内部发布的应用,在局域网内使用),自然也是有方法的。

这里介绍两种抓取崩溃堆栈信息的方法,如果发现问题,希望不吝赐教。也算是给自己做个笔记。:-)

 

方法一:自己调用c函数抓取堆栈信息,话不多说,代码来

#import <Foundation/Foundation.h>

extern NSString *const SKSAppExceptionInfo;

@interface SKSExceptionRecord : NSObject

+ (void)startExceptionHandler;

@end

 

#import "SKSExceptionRecord.h"

#include <execinfo.h>

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

 

以上都是在抓取到崩溃后,抛出消息,然后可以在任意地方做崩溃处理。可以依据自己架构需求做相应地改动。

iOS中崩溃收集

标签:

原文地址:http://www.cnblogs.com/zhizi-material/p/5076359.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!