番外

crash一直是app安稳性最重要的规范之一,一般根据特性,分为java层crash与native层crash,关于java层crash,咱们作为应用开发者,其实很简略就能在应用层进步行解决,与之相对的native层crash,却没有那么简略明了,很简略打得咱们应用层开发者一个“措手不及”。此时,如果crash的so由第三方提供的话,咱们也只能等待第三方进行后续修正。

当然,在笔者的个人经历中,产生的native crash大部分也是由第三方进行提供,咱们也没有“直接”手段对这些bug进行处理,与此同时,归于这部分的bug中,大部分是在异步线程调用时产生的,其实很简略理解,这种异步线程调用引起的crash,往往没那么简略在测验环境复现。比方一般在native 子线程中产生crash,比方一般在backtrace文件有类似log。

 #01 pc 00000000000007f0  crash 栈顶的音讯xxxx
 #02 pc 00000000000f43f0  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+64) (BuildId: cea9159973293d0520e9105a73ca766b)
 #03 pc 000000000008ef34  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: cea9159973293d0520e9105a73ca766b)

咱们都知道,这个一般是pthread_create调用,在调用后的子线程运转时“某处”产生了crash。当然,问题可不是出在pthread_create自身调用,而是内部的异步逻辑的问题,那么针对这种特定场景,咱们有没有处理办法呢?

兜底计划

安全气囊

安全气囊,比方咱们之前说到的,监听信号之后进行一次重启,这样能保证一次用户流程的完好。比方咱们之前说到的计划Signal。可是由于安全气囊计划自身的限制性,可是在后台还好,如果在前台,那便是真实有用户感知了,这样会丢失用户的体验,可是这个计划也有优点,便是成本满足简略而且通用各种native crash场景。

针对这种case的特定计划

咱们的场景满足清晰,便是只针对这种异脚步线程的场景进行兜底,在native层也能做到类似java层的try catch效果,那么有什么计划呢?有!它来了,这便是今日要讲的代码,mooner,下面要解说的计划完成代码现已放在github上了。

mooner的计划

咱们都知道,java层有try catch协助咱们进行反常的捕获,在反常处理这块,其实算不上一个“真反常”,这是由于java层的反常其实相关于虚拟机来说,都是已知可控的,因而咱们有捕获的逻辑,就算引起了咱们app运转逻辑的反常,可是也不影响虚拟机自身。而在native层,反常一般以signal 信号的方式进行传递,native层的反常,可是会相当于影响整个虚拟机,比方读写受阻,内存映射过错等等,这些都导致了虚拟机无法再正常进行处理下去了,因而关于native crash来说,反而不那么可控。

可是关于这些native层的crash,咱们真的没有一点办法吗?其实也不是,咱们在Android功能优化 – SIGSEGV的段过错保护完成(基于sigsetjmp)这篇文章中,有说到,咱们也能够在一些状况下,能够采用sigsetjmp与siglongjmp这种十分强力的native层可用的操作,协助咱们回溯到栈某一个安稳的时期,然后避免了本次反常操作。可是这种方式有一个限制,便是咱们有必要要知道调用方的栈状况,才干采纳sigsetjmp与siglongjmp这种方式。咱们举个比如

聊一聊应用层开发者怎么应对Native Crash
咱们在fun2调用longjmp回到fun1,这是可行的,这是由于当时fun1(setjmp调用方)的调用栈还存在内存上,因而咱们回溯能够到达。

可是咱们换成一个fun3,即现已脱离了fun1自身调用栈可达的另一个栈帧上建议一次longjmp的话,此时fun1的栈帧很有或许被销毁了,因而就会过错呈现SIGSEGV或许SIGABORT

聊一聊应用层开发者怎么应对Native Crash

可是在咱们这个场景下,咱们只需保证调用栈的联系,那么是能够彻底安全的运用sigsetjmp与siglongjmp进行回溯处理。

mooner针对这种异步线程产生的crash,采纳的计划是通过hook pthread_create,在咱们的的hook函数进步行了sigsetjmp的注入,再调用一次原函数的pthread_create,然后保证了在之后产生信号监听的时候,保证了调用栈的可达。或许大家或许会问,为什么只hook pthread_create,其他的创立线程,比方clone 调用也能够创立线程呀!现在由于mooner还在建设中,同时运用pthread_create创立线程的场景是遍及多的,因而只hook pthread_create也够用了。

咱们再给出一些全体的流程:

聊一聊应用层开发者怎么应对Native Crash

pthread_create 的hook

咱们清晰了计划,那么怎样完成对pthread_create的hook呢?其实很简略,用plthook即可,这个也是最安稳的计划,咱们之前也聊过plt hook Android功能优化 – plt hook 与native线程监控

mooner采用的plthook计划是比较老练与新的bhook,也是xhook作者开发的

终究api展示

终究,通过笔者花费了半响的周末时刻,依照上面的计划,现已把相关代码上传到github了,便是咱们今日讲的mooner

调用者只需要把lib名称,与需要兜底的信号传入即可,产生兜底时同时也会回调到block中

Mooner.initMooner("libmooner.so",11){
    Log.e("mooner","catch exception")
}

限制

咱们再次清晰一下,mooner这个计划只针对pthread_create创立的子线程中产生了信号才进行兜底操作,如果是由其他处产生了信号然后导致溃散,则没有阻拦。同时现在还在建设中,现在只支撑兜底一个so库的一个信号,当然后面会往一次调用即可兜底多个so库多个信号的方向发展!

当然,关于信号处理器无法处理的信号,比方SIGKILL,mooner也是无法做到阻拦的,由于里边便是基于sigaction处理

总结

本篇贴出的代码有点少!没事,现已上传到github上啦,mooner如果对你有协助,请别忘记给我一个star!同时欢迎后续的pr!