黑科技!让Native Crash 与ANR无处发泄!

黑科技!让Native Crash 与ANR无处发泄!

前语

高产似母猪的我,又带来了干货记录,本次是对signal的一个总结与回忆。不知道你们开发中,是否会遇到小部分的nativecrash 或许 anr,这部分往往是由第三方库导致的或许当时版别没办法修正的bug导致的,往往这些难啃的crash,对现有的crash数据目标形成必定影响,一起也对这小部分crash用户不友好,那么咱们有没有办法完成一套crash or指针是什么 anr重启机制呢?其实是有的,相信在各个大厂都有一套“安全气囊”设备,比方cr线程和进程的区别是什么ash必定次数就启用轻线程数越多越好吗量版别或许自动重新发动等等,下面咱们来着手搞一个这样的设备!这也是我第三个s最初的开源库Signal

留意:前方高能!阅览本文最好有一点ndk开发的知识噢!没有也没关系,冲吧!

Native Crash

natapplicationive crasappstoreh不同于java/kotlin层的crash,在java环境中,假如程序呈现了不行预期的crash(即没有捕获),就会往上抛出给终究的线程uncaghtexceptionhan源码网站dler,在这里咱源码网站们能够再次处理,比方屏蔽某个exception即可保持appetiteapp的稳定,然后native层的crash不一样,native 层的crash大多数是“不行恢复”的,比方变量是什么意思某个内存方面的过错,这些往往是不行处理的,需求中断当时进程,所以假如产生了native crash,咱们转移到自界说的安全处源码编程器理,比方自动重启后提示用户等等,就会提高用户很大的体源码之家会感(比起闪退)

线程的几种状态号机制

当native 层产生异常的时分,往往是经过信号的办法发送,给相对应的信指针万用表读数图解号处理器处理

黑科技!让Native Crash 与ANR无处宣泄!
咱们能够从signal.h看到,大约已经界说的信号有

/**
 * #define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGILL 4
#define SIGTRAP 5
#define SIGABRT 6
#define SIGIOT 6
#define SIGBUS 7
#define SIGFPE 8
#define SIGKILL 9
#define SIGUSR1 10
#define SIGSEGV 11
#define SIGUSR2 12
## define SIGPIPE 13
#define SIGALRM 14
#define SIGTERM 15
#define SIGSTKFLT 16
#define SIGCHLD 17
#define SIGCONT 18
#define SIGSTOP 19
#define SIGTSTP 20
#define SIGTTIN 21
#define SIGTTOU 22
#define SIGURG 23
#define SIGXCPU 24
#define SIGXFSZ 25
#define SIGVTALRM 26
#define SIGPROF 27
#define SIGWINCH 28
#define SIGIO 29
#define SIGPOLL SIGIO
#define SIGPWR 30
#define SIGSYS 31
 */

详细的含义可自定百度或许goog指针数组和数组指针的区别le,相信假如开发者都能在bugly等bug平台上看到

信号处理函数si线程gaction

一般的咱们有很多种办法界说信号处理函数,这里介绍sigaction 头文件:#include<signal.h>

界说函数:int sigaction(int signum,const sapprovet源码编辑器ru变量与函数ct sigact指针式万用表使用方法ion *act ,str指针万用表读数图解uct sigaction *oldac源码t)

函数说明:sigaction会依参数signum指定的信号编号来设置该信号的处理函数。参数signum能够指定SIGKILL和SIGSTOP以外的一切信号。如参数结构sigaction界说如下

struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
};

信号处源码1688理函数APP能够选用void (*sa_handler)(int)或void (*sa_sigaction)源码之家(int, siginfo_t *, void *)。究竟选用appearance哪个要看s变量名的命名规则a_flags中是否设置了SA_SIGINFO位,假如设置了就选用void (*sa_s源码中的图片igac变量与函数tion)(int, siginfo_t *, void *),此刻能够向处理函数发源码编程器送附加信息;默认情况下选用void (*sa_handler)(int),此刻只能向处理函数发送信号的数值。

sa_handler:此参数和signal()的参数handler相同,代表新的信号处理函数,其他意义请参阅signal(); sa_mask:用来设置在处理该信号时暂时将sa_mask指定的信号集放置; sa_线程的几种状态restorer:此参数没有运用; sa_f线程的几种状态lags :用来设置信号处理的其他相关操作,下列的数值可用。sa_flags还能指针c语言够设置其他标志: SA_RESETHAND:当调用信号处理函数时,将信号的处理函变量名的命名规则数重置为缺省值SIG_DFL SA_RESTART:假如信号中断了进程的某个体系调用,则体指针c语言系自动发动该体系调用 SA_NODEFER :指针数学一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。可是假如设置SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号。参阅

即咱们能够经过这个函数,注册咱们想要的信号处理,假如当SIGABRT信号到来时,咱们希望将其引到自咱们自界说信号处理,即可选用以下办法

 sigaction(SIGABRT, &sigc, nullptr);

其中sigc为sigaction结构appstore体的变量

struct sigaction sigc;
//sigc.sa_handler = SigFunc;
sigc.sa_sigaction = SigFunc;
sigemptyset(&sigc.sa_mask);
sigc.sa_flags = SA_SIGINFO;

SigFunc为咱们界说处理函数的指针,咱们能够设定这样一个函数,去向理咱指针数组和数组指针的区别们想要阻拦的信号

void SigFunc(int sig_num, siginfo *info, void *ptr) {
   自界说处理
}

native crash阻拦

有了前面这些基础知识,咱们就开端封装咱们的crash阻拦吧,作为库开发者,咱们希望把阻拦的信号量交给上层去向理,所以咱们的层次是这样的

黑科技!让Native Crash 与ANR无处宣泄!
所以咱们能够有指针万用表读数图解以下代码,详细细节能够看Signal 咱们给出函数处理器

jobject currentObj;
JNIEnv *currentEnv = nullptr;
void SigFunc(int sig_num, siginfo *info, void *ptr) {
    // 这里判空并不代表这个目标便是安全的,由于有可能是脏内存
    if (currentEnv == nullptr || currentObj == nullptr) {
        return;
    }
    __android_log_print(ANDROID_LOG_INFO, TAG, "%d catch", sig_num);
    __android_log_print(ANDROID_LOG_INFO, TAG, "crash info pid:%d ", info->si_pid);
    jclass main = currentEnv->FindClass("com/example/lib_signal/SignalController");
    jmethodID id = currentEnv->GetMethodID(main, "callNativeException", "(I)V");
    if (!id) {
        return;
    }
    currentEnv->CallVoidMethod(currentObj, id, sig_num);
    currentEnv->DeleteGlobalRef(currentObj);
}
当so库被加载的时分由体系自动调用JNI_OnLoad
extern "C" jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    jint result = -1;
    // 直接用vm进行赋值,否则不行靠
    if (vm->GetEnv((void **) &currentEnv, JNI_VERSION_1_4) != JNI_OK) {
        return result;
    }
    return JNI_VERSION_1_4;
}

其中currenappletEnv代表着当时jni环境,咱们在JNI_OnLoad阶段进行初始化即可,cur线程池的七个参数rentappleObj即代表咱们要调用的办法目标,由于咱们要回调到java层,所以native必定需求一个java目标,详细能够看到Signal里边的处理,值得留意的是,咱application们在native想要在其他函数运用java目标的话,在初始函数赋值的时分,就必须选用env->Ne源码网站wGlobalRe指针数组和数组指针的区别f办法分配application一个全局变量,否则在该函数完毕的时分,目标的内存就会变成脏变量(留意不是NULL)。

Spi机制的运用

假如还不理解spi机指针制的话,能够检查我之前写的这线程是什么意思篇spi机制,由于咱们终究会将信号信息传递给java层,所以终究会在java最终执源码精灵永久兑换码行咱们的重启处理,可是重启前咱们可能会运用各种自界说的处理方案,比方弹出toast或许各种自界说操作,那么这种自界说的处理就很合适用spi接口露出给详细的运用者即可,所以咱们Signal界说了一个接口

interface CallOnCatchSignal {
    fun onCatchSignal(signal: Int,context: Context)
}

外部库的调用者完成线程的几种状态这个接口,将完成类配置在META-INF.services目录即可,如图

黑科技!让Native Crash 与ANR无处宣泄!
如此一来,咱们就能够在自界说的MyHandler完成自己的重启逻辑,比方指针是什么重启/自界说上报crash等等,demo能够看Signal的处理appear

ANR

关于anr也是一个很有趣的话题,咱们能够看到anr也会导致闪退,主要是国内各个厂商都有自己的自界说化处理,比方常规的弹出anr框或许自动闪退,无论是哪一种,对于用户来说都不是一个好的体会。

ANR传递进程

以android 11为比如,终究anr被检测产生后,会调用ProcessErrorStateReappetitecord类的appNotResponding办法源码之家,去进行dump 墓碑文件的操作,这个时分就会调用发送一个信号为Signal_Q指针数学uit的信号,对应的常量为3,所以假如咱appearance们想检测到anr后去进行自界说处理的话,按照上面所说直接用sigaction能够变量泵吗?

黑科技!让Native Crash 与ANR无处宣泄!

但是假如直接用siappetitegaction去注册Signal_Quit信号进行处理的话,会发变量名的命名规则现竟然什么都没有回调!那么这产生了什么!

原因便是咱们进程继承Zygote进行的时分就把变量值主线程信号的approve掩码也继承了,Zygote进程把这三个信号量加入了掩码,该办法被调用在init办法中

黑科技!让Native Crash 与ANR无处宣泄!
掩码的效果便是使得当时的线appetite程不相应源码交易平台这三个信号量,交给其他线程处理

那么其他线程这里指的是源码精灵永久兑换码什么?其实便变量之间的关系SignalCatcher线程,一般咱们产生anr的时分也能看到log输出,终究在run办法注册处理函数

黑科技!让Native Crash 与ANR无处宣泄!
终究调用WaitForSignal

黑科技!让Native Crash 与ANR无处宣泄!
调用wait办法

黑科技!让Native Crash 与ANR无处宣泄!
这个si源码之家gwait办法也线程撕裂者是一变量的定义个注册信号处理函数的办法,跟sigaction的区别可参阅

撤销block

经过上面的剖析,相源码网站信能了解到为appearance什么Signal_源码中的图片Quit源码1688监听不了了,咱们也知道,zygote经过掩码把信号进行了屏蔽,那么咱们有办法把这个屏蔽给打开吗?答案是有的

pthread_sigmask(SIG_UNBLOCK, &mask, &old))
sigemptyset(&mask);
sigaddset(&mask, SIGQUIT);

咱们能够经过pthread_sigmask设置为非block,即参数1的标志,把要撤销屏蔽的信号放入即可,如图便是把SIGQUIT撤销了,这样一来咱们再运用sigaction去注册SIGQUIT就能够在信号动身时执行咱们的anr处理逻辑了。值得留意的是,S线程池的七个参数IGQUIT触发也不必定由anr产生,这是一个必要但不充沛的条件,所以咱们还要添加其他的判别,比方咱们能够判别一个queu线程是什么意思e里边的当时meappstoressage的when参数来判别这个音讯在队列待了多久,又或许是咱们自界说一个异步音讯去检查这个音讯源码编程器什么时分回调了handler等等办法,终究判别是否是anr,当然这个不是百分百准确,现在我也没想到百分appointment百准确的办法,由于FileObserve监听traces文件已经在android源码16885以上不能用了,所以Signa源码之家l里边没有指针数学给出详细的判别,只给了一个参阅比如。

最终

上述所讲的都在Signal这个库里边有源码与注释,用起来吧!自界说处理能够用作检测crash,anr,也能够用作一个安全设备变量,产生crash重启等等,只要有脑洞,都能够完成!最终记指针说漫得点个赞啦!

文章引证

blog.csdn.net/qq_32指针万用表读数图解198115…

往期高燃推荐

动态so加载 /post/710795…

评论

发表回复