iOS中崩潰收集

對於崩潰的收集有不少第三方的不錯的工具,好比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

 

以上都是在抓取到崩潰後,拋出消息,而後能夠在任意地方作崩潰處理。能夠依據本身架構需求作相應地改動。

相關文章
相關標籤/搜索