美文网首页android技术
崩溃日志收集框架 - native crash收集流程分析

崩溃日志收集框架 - native crash收集流程分析

作者: Stan_Z | 来源:发表于2020-07-03 20:04 被阅读0次

    一、基础铺垫

    信号机制:


    整个流程理解:

    程序在cpu上运行的时候发生了错误,cpu发送中断指令,程序陷入内核,内核添加信号到进程的信号队列,之后程序转到用户态,进行信号检测,检查到信号队列中有新信号,进行信号处理。

    用户态信号怎么处理?

    应用程序被内核加载起来后,并不是先跑程序代码,而是先跑linker,linker初始化过程target进程注册信号处理函数。而通过System.loadLibrary加载的so文件,最终由linker链接到bionic,当native程序出现异常时,kernel会发送相应signal过来,target进程捕获signal,然后与debuggerd建立socket通信,交由debuggerd来执行相关dump操作。

    注:
    linker:Android系统加载动态链接的链接器。
    debuggerd:一个init孵化的daemon进程,主要负责将进程运行时的信息dump到文件或者控制台中。

    二、native crash收集流程

    代码:android 6.0

    2.1 信号处理函数注册:

    信号注册流程

    应用程序入口begin.S通过linker初始化,调用debuggerd的debuggers_init执行信号处理函数的注册,在debuggers_signal_handler中通过sigaction,注册要接收的信号。同时通过send_debuggerd_packet建立target进程与debuggerd的socket通信连接。
    注册点:

    __LIBC_HIDDEN__ void debuggerd_init() {
      struct sigaction action;
      memset(&action, 0, sizeof(action));
      sigemptyset(&action.sa_mask);
      action.sa_sigaction = debuggerd_signal_handler;
      action.sa_flags = SA_RESTART | SA_SIGINFO;
     
      // Use the alternate signal stack if available so we can catch stack overflows.
      action.sa_flags |= SA_ONSTACK;
     
      sigaction(SIGABRT, &action, NULL);//调用abort函数生成的信号,表示程序异常
      sigaction(SIGBUS, &action, NULL);//非法地址,包括内存地址对齐出错,比如访问一个4字节的整数, 但其地址不是4的倍数
      sigaction(SIGFPE, &action, NULL);//计算错误,比如除0、溢出
      sigaction(SIGILL, &action, NULL);//执行了非法指令,或者试图执行数据段,堆栈溢出
      sigaction(SIGPIPE, &action, NULL);//管道破裂,通常在进程间通信产生
      sigaction(SIGSEGV, &action, NULL);//非法内存操作,与 SIGBUS不同,他是对合法地址的非法访问,    比如访问没有读权限的内存,向没有写权限的地址写数据
    #if defined(SIGSTKFLT)
      sigaction(SIGSTKFLT, &action, NULL);//协处理器堆栈错误
    #endif
      sigaction(SIGTRAP, &action, NULL);//断点时产生,由debugger使用
    }
    

    信号量定义:

    signal.h
    
    #define SIGHUP 1  // 终端连接结束时发出(不管正常或非正常)
    #define SIGINT 2  // 程序终止(例如Ctrl-C)
    #define SIGQUIT 3 // 程序退出(Ctrl-\)
    #define SIGILL 4 // 执行了非法指令,或者试图执行数据段,堆栈溢出
    #define SIGTRAP 5 // 断点时产生,由debugger使用
    #define SIGABRT 6 // 调用abort函数生成的信号,表示程序异常
    #define SIGIOT 6 // 同上,更全,IO异常也会发出
    #define SIGBUS 7 // 非法地址,包括内存地址对齐出错,比如访问一个4字节的整数, 但其地址不是4的倍数
    #define SIGFPE 8 // 计算错误,比如除0、溢出
    #define SIGKILL 9 // 强制结束程序,具有最高优先级,本信号不能被阻塞、处理和忽略
    #define SIGUSR1 10 // 未使用,保留
    #define SIGSEGV 11 // 非法内存操作,与 SIGBUS不同,他是对合法地址的非法访问,    比如访问没有读权限的内存,向没有写权限的地址写数据
    #define SIGUSR2 12 // 未使用,保留
    #define SIGPIPE 13 // 管道破裂,通常在进程间通信产生
    #define SIGALRM 14 // 定时信号,
    #define SIGTERM 15 // 结束程序,类似温和的 SIGKILL,可被阻塞和处理。通常程序如    果终止不了,才会尝试SIGKILL
    #define SIGSTKFLT 16  // 协处理器堆栈错误
    #define SIGCHLD 17 // 子进程结束时, 父进程会收到这个信号。
    #define SIGCONT 18 // 让一个停止的进程继续执行
    #define SIGSTOP 19 // 停止进程,本信号不能被阻塞,处理或忽略
    #define SIGTSTP 20 // 停止进程,但该信号可以被处理和忽略
    #define SIGTTIN 21 // 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号
    #define SIGTTOU 22 // 类似于SIGTTIN, 但在写终端时收到
    #define SIGURG 23 // 有紧急数据或out-of-band数据到达socket时产生
    #define SIGXCPU 24 // 超过CPU时间资源限制时发出
    #define SIGXFSZ 25 // 当进程企图扩大文件以至于超过文件大小资源限制
    #define SIGVTALRM 26 // 虚拟时钟信号. 类似于SIGALRM,     但是计算的是该进程占用的CPU时间.
    #define SIGPROF 27 // 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间
    #define SIGWINCH 28 // 窗口大小改变时发出
    #define SIGIO 29 // 文件描述符准备就绪, 可以开始进行输入/输出操作
    #define SIGPOLL SIGIO // 同上,别称
    #define SIGPWR 30 // 电源异常
    #define SIGSYS 31 // 非法的系统调用
    
    

    2.2 debuggerd收集奔溃信息

    当发生crash时,kernel发送相应signal给target进程,因为target进程注册过了接收信号,因此能捕获signal进行处理,因为target进程与debuggerd建立了socket连接,此时会将action = DEBUGGER_ACTION_CRASH的消息发送给debuggerd服务端,debuggerd处理流程如下图:

    debuggerd收集native奔溃信息流程

    debuggerd处理过程简析:

    • 接收target进程发送过来的socket请求,fork子进程来处理任务。
    • 子进程创建tombstone文件,并dump收集相关信息。包括设备基本信息、backtrace、stack、系统logcat等。
    • socket到system_server进程走crash流程。
    $:/data/tombstones # ls -al
    -rw-r-----  1 tombstoned system 623950 2020-06-24 00:06 tombstone_00
    -rw-r-----  1 tombstoned system 630274 2020-06-24 10:52 tombstone_01
    

    一份tombstone文件内容:

    *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    //基本信息
    Build fingerprint: 'Xiaomi/cepheus/cepheus:10/QKQ1.190825.002/9.10.29:user/release-keys'
    Revision: '0'
    ABI: 'arm64'
    Timestamp: 2020-06-24 00:06:13+0800
    pid: 27232, tid: 27232, name: crashhandlerprj  >>> com.stan.crashhandlerprj <<<
    uid: 10258
    
    //信号信息
    signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
    Abort message: 'FORTIFY: strcat: prevented write past end of 0-byte buffer'
        x0  0000000000000000  x1  0000000000006a60  x2  0000000000000006  x3  0000007fe28391e0
        x4  0000800000808080  x5  0000800000808080  x6  0000800000808080  x7  0000000000000018
         ...
    
    backtrace:
          #00 pc 0000000000073430  /apex/com.android.runtime/lib64/bionic/libc.so (abort+160) (BuildId: 084c8a81b8c78e19cd9a1ff6208e77cf)
          #01 pc 0000000000099594  /apex/com.android.runtime/lib64/bionic/libc.so (__fortify_fatal(char const*, ...)+120) (BuildId: 084c8a81b8c78e19cd9a1ff6208e77cf)
          #02 pc 000000000009a390  /apex/com.android.runtime/lib64/bionic/libc.so (__strcat_chk+84) (BuildId: 084c8a81b8c78e19cd9a1ff6208e77cf)
          ...
    
    stack:
             0000007fe2839140  0000007fe2839268  [stack]
             0000007fe2839148  0000000000000000
             0000007fe2839150  0000007fe28391b0  [stack]
             ...
    
    memory near x1:
        0000000000006a40 ---------------- ----------------  ................
        0000000000006a50 ---------------- ----------------  ................
        0000000000006a60 ---------------- ----------------  ................
        ...
    
    memory map (2210 entries):
        00000000'12c00000-00000000'12dbffff rw-         0    1c0000  [anon:dalvik-main space (region space)]
        00000000'12dc0000-00000000'135fffff ---         0    840000  [anon:dalvik-main space (region space)]
        00000000'13600000-00000000'13bbffff ---         0    5c0000  [anon:dalvik-main space (region space)]
       ...
    
    //系统logcat
    --------- tail end of log main
    06-24 00:06:08.910 27232 27232 I FeatureParser: can't find cepheus.xml in assets/device_features/,it may be in /system/etc/device_features
    06-24 00:06:08.916 27232 27232 E libc    : Access denied finding property "ro.vendor.df.effect.conflict"
    06-24 00:06:08.910 27232 27232 W crashhandlerprj: type=1400 audit(0.0:512880): avc: denied { read } for name="u:object_r:vendor_displayfeature_prop:s0" dev="tmpfs" ino=28719 scontext=u:r:untrusted_app:s0:c2,c257,c512,c768
    ...
    
    //fd信息
    open files:
        fd 0: /dev/null (unowned)
        fd 1: /dev/null (unowned)
        fd 2: /dev/null (unowned)
        fd 3: socket:[4540882] (unowned)
        …
    

    2.3 ActivityManagerService走应用crash流程

    AMS走应用crash流程

    到ActivityManagerService就是走app的死亡流程了,包括crash信息写入dropbox,然后走死亡流程:handlerAppCrashLocked函数主要工作内容包括:杀进程、同时清理进程的四大组件,最终弹出crash对话框。 这部分与上一篇文章写的java crash流程一致。

    $:/data/system/dropbox # ls -al
    //java crash文件
    -rw-------  1 system system   963 2020-07-03 16:28 data_app_crash@1593764897731.txt
    
    //native crash文件 有两个
    -rw-------  1 system system 12826 2020-07-03 16:28 SYSTEM_TOMBSTONE@1593764922307.txt.gz
    -rw-------  1 system system  2449 2020-07-03 16:28 data_app_native_crash@1593764922301.txt.gz
    

    data_app_crash@1593764897731.txt内容:

    Process: com.stan.crashhandlerprj
    PID: 29409
    UID: 10258
    Flags: 0x30e8bf46
    Package: com.stan.crashhandlerprj v1 (1.0)
    Foreground: Yes
    Build: Xiaomi/cepheus/cepheus:10/QKQ1.190825.002/9.10.29:user/release-keys
    java.lang.ArithmeticException: divide by zero
       at com.stan.crashhandlerprj.MainActivity.onClick(MainActivity.java:39)
       at android.view.View.performClick(View.java:7160)
       at android.view.View.performClickInternal(View.java:7137)
       at android.view.View.access$3500(View.java:810)
       at android.view.View$PerformClick.run(View.java:27418)
       at android.os.Handler.handleCallback(Handler.java:883)
       at android.os.Handler.dispatchMessage(Handler.java:100)
       at android.os.Looper.loop(Looper.java:221)
       at android.app.ActivityThread.main(ActivityThread.java:7540)
       at java.lang.reflect.Method.invoke(Native Method)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
    

    而data_app_native_crash@1593764922301.txt.gz和SYSTEM_TOMBSTONE@1593764922307.txt.gz的内容基本就是tombstone的内容。

    至此native crash处理流程就分析完了。

    参考:
    理解Native Crash处理流程
    debuggerd守护进程
    Android平台Native代码的崩溃捕获机制及实现

    相关文章

      网友评论

        本文标题:崩溃日志收集框架 - native crash收集流程分析

        本文链接:https://www.haomeiwen.com/subject/smqrqktx.html