ACCESSiOS Crash日志 收集

明天在微信公众号上收看一篇作品,做一下简化整理,我们能够品尝联合来做一下投机的Crash日志记录

开发iOS应用,解决Crash问题始终是一个难题。Crash分为两种,一种是由EXC_BAD_ACCESS引起的,原因是造访了不属于本进程的内存地址,有可能是访问已被放出的内存;另一种是未被破获的Objective-C十分(NSException),导致程序向自身发送了SIGABRT信号而夭折。其实对于未捕获的Objective-C非常,大家是有措施将它记录下来的,假如日志记录得当,可以化解绝大部分崩溃的问题。

一. 系统Crash

对此系统Crash而引起的次序非凡退出,可以经过UncaughtExceptionHandler机制捕获;也就是说在先后中catch以外的始末,被系统自带的错误处理而抓获。我们要做的就是用自定义的函数替代该ExceptionHandler即可。

二. 处理signal

动用Objective-C的不行处理是不可以收获signal的,尽管要拍卖它,我们还要采纳unix标准的signal机制,注册SIGABRT,
SIGBUS,
SIGSEGV等信号暴发时的处理函数
。该函数中我们得以输出栈信息,版本信息等其它任何我们所想要的。

下面是一对信号表明:
1) SIGHUP
本信号在用户终端连接(正常或不规则)结束时发出,
通常是在终极的支配过程截至时, 通告同一session内的一一作业,
这时它们与控制终端不再涉及。
登录Linux时,系统会分配给登录用户一个巅峰(Session)。在这些极限运行的持有程序,包括前台进程组和后台进程组,一般都属于那一个Session。当用户退出Linux登录时,前台进程组和后台有对极端输出的长河将会接受SIGHUP信号。这些信号的默认操作为终止进程,因从前台进
程组和后台有极限输出的进程就会中止。然而可以捕获这多少个信号,比如wget能捕获SIGHUP信号,并忽略它,这样就是退出了Linux登录,
wget也 能继续下载。
另外,对于与终端脱离关系的医护进程,这一个信号用于通告它再度读取配置文件。

2) SIGINT
先后终止(interrupt)信号,
在用户键入INTR字符(通常是Ctrl-C)时暴发,用于通告前台进程组终止进程。

3) SIGQUIT
和SIGINT类似, 但由QUIT字符(日常是Ctrl-)来控制.
进程在因接受SIGQUIT退出时会暴发core文件,
在这个意义上好像于一个主次不当信号。

4) SIGILL
实施了不法指令. 通常是因为可执行文件本身出现谬误, 或者试图实施多少段.
堆栈溢出时也有可能发生那一个信号。

5) SIGTRAP
由断点指令或任何trap指令爆发. 由debugger使用。

6) SIGABRT
调用abort函数生成的信号。

7) SIGBUS
非法地址, 包括内存地址对齐(alignment)出错。比如访问一个多少个字长的整数,
但其地方不是4的翻番。它与SIGSEGV的界别在于后者是由于对合法存储地点的非官方访问触发的(如访问不属于自己积存空间或只读存储空间)。

8) SIGFPE
在发出致命的算术运算错误时发出. 不仅包括浮点运算错误,
还包括溢出及除数为0等任何具有的算术的一无是处。

9) SIGKILL
用来立时截止程序的运行.
本信号不可以被卡住、处理和疏忽。假设管理员发现某个进程终止不了,可尝试发送这一个信号。

10) SIGUSR1
预留用户拔取

11) SIGSEGV
准备访问未分配给自己的内存, 或打算往没有写权限的内存地址写数据.

12) SIGUSR2
留下用户使用

13) SIGPIPE
管道破裂。这多少个信号平时在过程间通信发生,比如选取FIFO(管道)通信的六个经过,读管道没打开或者意想不到终止就往管道写,写进程会吸纳SIGPIPE信号。另外用Socket通信的三个过程,写进程在写Socket的时候,读进程一度告一段落。

14) SIGALRM
钟表定时信号, 总括的是实在的年华或时钟时间. alarm函数使用该信号.

15) SIGTERM
次第为止(terminate)信号,
与SIGKILL不同的是该信号可以被打断和处理。通常用来要求程序自己正常退出,shell命令kill缺省时有暴发那些信号。假使经过终止不了,我们才会尝试SIGKILL。

17) SIGCHLD
子进程截止时, 父进程会吸纳这么些信号。
设若父进程没有拍卖这多少个信号,也未曾等待(wait)子进程,子进程即便平息,但是还会在基础进程表中据为己有表项,那时的子进程称为僵尸进程。这种情形大家相应制止(父进程或者忽视SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程
来接管)。

18) SIGCONT
让一个停下(stopped)的进程继续执行. 本信号不可以被阻塞.
可以用一个handler来让程序在由stopped状态成为继续执行时形成一定的工作.
例如, 重新显示提醒符

19) SIGSTOP
截止(stopped)进程的执行.
注意它和terminate以及interrupt的分别:该过程还未结束, 只是搁浅执行.
本信号不可以被卡住, 处理或忽略.

20) SIGTSTP
停下进程的运作, 但该信号能够被处理和忽略.
用户键入SUSP字符时(平日是Ctrl-Z)发出那么些信号

21) SIGTTIN
当后台作业要从用户终端读数据时, 该学业中的所有进程会吸收SIGTTIN信号.
缺省时这多少个过程会终止执行.

22) SIGTTOU
好像于SIGTTIN, 但在写终端(或改动终端格局)时收到.

23) SIGURG
有”紧急”数据或out-of-band数据到达socket时暴发.

24) SIGXCPU
超过CPU时间资源限制. 这些限制可以由getrlimit/setrlimit来读取/改变。

25) SIGXFSZ
当进程企图扩张文件以至于领先文件大小资源限制。

26) SIGVTALRM
编造时钟信号. 类似于SIGALRM, 不过精打细算的是该过程占用的CPU时间.

27) SIGPROF
接近于SIGALRM/SIGVTALRM, 但包括该过程用的CPU时间以及系统调用的时间.

28) SIGWINCH
窗口大小改变时发出.

29) SIGIO
文本讲述符准备妥当, 可以起先举办输入/输出操作.

30) SIGPWR
Power failure

31) SIGSYS
非法的系统调用。

关键点
在以上列出的信号中,程序不得捕获、阻塞或不经意的信号有:SIGKILL,SIGSTOP
不可能还原至默认动作的信号有:SIGILL,SIGTRAP
默认会导致进程早产的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
默认会导致进程退出的信号有:
SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
默认会促成进程截止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
默认进程忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH
此外,SIGIO在SVR4是退出,在4.3BSD中是忽视;SIGCONT在过程挂起时是继续,否则是忽视,无法被卡住。
【具体原因怎么,我也不精通,问微信大神去把】

好了,说了那么多,终于可以实战写代码了:
1.AppDelegate.m中

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.

InstallSignalHandler();//信号量截断
InstallUncaughtExceptionHandler();//系统异常捕获

return YES;
}

2.SignalHandler.m的实现

void SignalExceptionHandler(int signal)
{
    NSMutableString *mstr = [[NSMutableString alloc] init];
    [mstr appendString:@"Stack: "];
    void* callstack[128];
    int i, frames = backtrace(callstack, 128);
    char** strs = backtrace_symbols(callstack, frames);
    for (i = 0; i
        [mstr appendFormat:@"%s ", strs[i]];
    }
    [SignalHandler saveCreash:mstr];

}

void InstallSignalHandler(void)
{
    signal(SIGHUP, SignalExceptionHandler);
    signal(SIGINT, SignalExceptionHandler);
    signal(SIGQUIT, SignalExceptionHandler);

    signal(SIGABRT, SignalExceptionHandler);
    signal(SIGILL, SignalExceptionHandler);
    signal(SIGSEGV, SignalExceptionHandler);
    signal(SIGFPE, SignalExceptionHandler);
    signal(SIGBUS, SignalExceptionHandler);
    signal(SIGPIPE, SignalExceptionHandler);
}

3.UncaughtExceptionHandler.m的实现

void HandleException(NSException *exception)
{
    // 异常的堆栈信息
    NSArray *stackArray = [exception callStackSymbols];
    // 出现异常的原因
    NSString *reason = [exception reason];
    // 异常名称
    NSString *name = [exception name];
    NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@ Exception name:%@ Exception stack:%@",name, reason, stackArray];
    NSLog(@"%@", exceptionInfo);
    [UncaughtExceptionHandler saveCreash:exceptionInfo];
}

void InstallUncaughtExceptionHandler(void)
{
    NSSetUncaughtExceptionHandler(&HandleException);
}

4、测试一下
这边最重大的一步,SignalHandler不要在debug环境下测试。因为系统的debug会优先去阻止。大家要运行一次后,关闭debug状态。应该直接在模拟器上点击大家build上去的app去运作。而UncaughtExceptionHandler可以在调试境况下捕捉

- (void)buttonClick:(UIButton *)sender {
//1.信号量
    Test *pTest = {1,2};
    free(pTest);//导致SIGABRT的错误,因为内存中根本就没有这个空间,哪来的free,就在栈中的对象而已
    pTest->a = 5;
}
- (IBAction)buttonOCException:(UIButton *)sender
{
    //2.ios崩溃
    NSArray *array= @[@"tom",@"xxx",@"ooo"];
    [array objectAtIndex:5];
}

附上demo:【demo下载地址

相关文章