精华内容
下载资源
问答
  • Redhat Crash下载和编译
    千次阅读
    2018-12-30 22:34:26

    源码下载

    官网:
    http://people.redhat.com/anderson/
    官网新地址:
    https://crash-utility.github.io/
    不同版本下载地址:
    https://github.com/crash-utility/crash/releases
    截止到目前,最新版本是crash-7.2.4,crash-7.2.4.tar.gz大概37.4MB
    之所以这么大,主要是因为其中包含了gdb-7.6.tar.gz的源码。

    编译

    我要编译ARM64的crash,具体操作如下:
    $ tar -xf crash-7.2.4.tar.gz
    $ cd crash-7.2.4/
    $ make target=arm64

    首次编译时总是报同一个错误,具体信息如下:
    我的PC系统是ubuntu16.04
    编译输出的关键日志:
    TARGET: ARM64
    CRASH: 7.2.4
    GDB: 7.6

    Makefile:1174: recipe for target ‘gdb’ failed
    Makefile:8264: recipe for target ‘all-gdb’ failed
    Makefile:834: recipe for target ‘all’ failed

    crash build failed

    Makefile:229: recipe for target ‘gdb_merge’ failed
    Makefile:224: recipe for target ‘all’ failed

    后来安装了一些工具后解决
    sudo apt-get install libaio-dev libncurses5-dev zlib1g-dev liblzma-dev flex bison byacc
    参考这个博客:
    https://blog.csdn.net/paul_liao/article/details/40581869
    编译成功后会生成crash文件

    $ ./crash

    crash 7.2.4
    Copyright © 2002-2017 Red Hat, Inc.
    Copyright © 2004, 2005, 2006, 2010 IBM Corporation
    Copyright © 1999-2006 Hewlett-Packard Co
    Copyright © 2005, 2006, 2011, 2012 Fujitsu Limited
    Copyright © 2006, 2007 VA Linux Systems Japan K.K.
    Copyright © 2005, 2011 NEC Corporation
    Copyright © 1999, 2002, 2007 Silicon Graphics, Inc.
    Copyright © 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
    This program is free software, covered by the GNU General Public License,
    and you are welcome to change it and/or distribute copies of it under
    certain conditions. Enter “help copying” to see the conditions.
    This program has absolutely no warranty. Enter “help warranty” for details.

    crash: compiled for the ARM64 architecture

    $ ./crash --buildinfo
    build_command: crash
    build_data: 2018年 12月 30日 星期日 21:26:41 CST by uid=1000(yutao) on yutao
    build_target: ARM64
    build_version: 7.2.4
    compiler version: gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609

    更多相关内容
  • crash 工具源码

    2018-04-06 16:24:41
    自测可用的crash 工具源码,比如用arm 32位的,make target=arm
  • crash utility 源代码

    2014-01-17 22:06:13
    crash utility v7.04 source code
  • 百度地图毕业设计源码 APP Crash 研究报告 1、研究背景 1.1、什么是Crash Crash 即闪退,多指在移动设备中,在打开或运行应用程序过程中出现突然中断的情况。 1.2、为什么会Crash 由于出现异常且没有被应用程序捕捉...
  • crash 查看动态内存中的代码

    千次阅读 2022-04-01 15:16:25
    linux 下查看动态内存(/proc/kcore)的反汇编,即运行时代码,crash的使用

    一、作用

    • 查看linux 镜像加载到内存后,代码的情况
    • 查看linux 驱动加载到内存后,代码的情况
    • 使用objdump -d /proc/kcore > log.txt 失败的情况

    二、安装crash

    • 使用命令下载:
      Centos/Redhat:
      	yum install crash -y
      Ubuntu:
      	apt-get install crash -y
      
    • 使用 crash 源码安装:
      root:~/Desktop$ git clone https://github.com/crash-utility/crash.git
      ...
      root:~/Desktop$ cd crash/
      root:~/Desktop/crash$ make -j8
      ...
      root:~/Desktop/crash$ make install
      ...
      

    三、下载安装相应版本的 debug-info(dbgsym)

    四、使用

    1. 启动:

      [root@localhost crypto]# crash
      	  ...	
            KERNEL: /usr/lib/debug/lib/modules/3.10.0-1062.12.1.el7.x86_64/vmlinux
          DUMPFILE: /dev/crash
              CPUS: 1
              DATE: Fri Apr  1 16:04:05 2022
            UPTIME: 1 days, 05:41:03
      LOAD AVERAGE: 0.16, 0.05, 0.06
             TASKS: 329
          NODENAME: localhost.localdomain
           RELEASE: 3.10.0-1062.12.1.el7.x86_64
           VERSION: #1 SMP Tue Feb 4 23:02:59 UTC 2020
           MACHINE: x86_64  (3200 Mhz)
            MEMORY: 15.9 GB
               PID: 21863
           COMMAND: "crash"
              TASK: ffff88d5fe8bd230  [THREAD_INFO: ffff88d9ac650000]
               CPU: 0
             STATE: TASK_RUNNING (ACTIVE)
      
      crash>
      
    2. 查看相应地址的反汇编代码:

      crash> dis 0xffffffffc01702f0
      0xffffffffc01702f0 <crc_pcl>:   push   %rbx
      0xffffffffc01702f1 <crc_pcl+1>: push   %rdi
      0xffffffffc01702f2 <crc_pcl+2>: push   %rsi
      0xffffffffc01702f3 <crc_pcl+3>: mov    %rdx,%r8
      0xffffffffc01702f6 <crc_pcl+6>: mov    %rdi,%rcx
      0xffffffffc01702f9 <crc_pcl+9>: neg    %rdi
      ...
      

      具体用法可以使用:help [function] 命令

    展开全文
  • 不管新手还是老手都能从中受益的英文原版python教学书
  • Android Crash详解

    千次阅读 2021-10-22 16:52:16
    目前我们知晓的Android客户端上会出现的三种导致APP无法使用的现象是Java崩溃,Native崩溃以及ANR。以下内容从三种错误展开,均建立在自己自行调研以及实践的基础上。 Java崩溃 Java崩溃就是在Java/kotlin代码中,...

    目前我们知晓的Android客户端上会出现的三种导致APP无法使用的原因有Java崩溃,Native崩溃以及ANR。以下内容从三种错误展开,建立在自行调研以及实践的基础上。

    Java崩溃

    Java崩溃就是在Java/kotlin代码中,出现了未捕获异常,导致程序异常退出。通常是由我们自己的业务代码导致,例如空指针,索引越界等常见的崩溃。Java的崩溃日志相对于Native和ANR的堆栈日志,阅读和定位难度为最低。一般在配合mapping文件反混淆之后都可以直接定位错误。

    java崩溃捕获 

    class JavaCrashHandler implements UncaughtExceptionHandler{
        
        //在初始化的时候
        void initialize(){
        ......
            Thread.setDefaultUncaughtExceptionHandler(this);
    
        }
    
         @Override
        public void uncaughtException(Thread thread, Throwable throwable) {
    
            //处理异常 读取信息 上传或者其他处理
            handleException(thread, throwable);
        }
    
    }

    这部分异常捕捉由于有现成的接口提供所以很容易。只需要大家处理好读取部分的逻辑就没什么大问题,上报的时候采集一些附带信息即可。

    java崩溃日志解析

    解析java日志,首先需要的是mapping.txt文件。Grade 3.4版本之前,使用Proguard工具,之后Android 在新版中启用了 R8 编译器,没有使用 Proguard 工具,虽然兼容 Proguard 的配置和字典等,但是编译出来的 Mapping 文件格式还是有一点不同。如果开启混淆功能,则会产生Mapping 文件,用来逆向推出原始的堆栈信息,更快更方便的定位问题。位置路径:build/output/mapping/release/mapping.txt

    或者

    build/output/mapping/${flavorDir}/release/mapping.txt

    大家可以自行查看mapping文件的内容,可以发现就是一张映射表,利用不同的字母组合代表指定的类或者方法等。如下图展示:

    用来解析崩溃日志的工具是sdk中自带的retrace。路径为:sdk/tools/proguard/bin/retrace.sh。解析命令为:

    retrace (mapping文件路径) (java crash文件路径)

    其中java日志的格式如下,大家获取日志的时候可以自行搜索retrace解析日志格式,随着版本升级应该会有变动。

    *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    
    其他字段信息....
    ....
    java stacktrace:
        ....
    
    其他信息...
    
    +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
    

    举例如:

    *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    Build fingerprint: 'samsung/dreamqltezc/dreamqltechn:9/PPR1.180610.011/G9500ZCU4DSH2:user/release-keys'
    ABI: 'arm64'
    
    java stacktrace:
    java.lang.IllegalStateException: Could not execute method for android:onClick
    	at d.b.c.t$a.onClick(:2)
    	at android.view.View.performClick(View.java:7352)
    	at android.widget.TextView.performClick(TextView.java:14177)
    	at com.google.android.material.button.MaterialButton.performClick(Unknown Source:3)
    	at android.view.View.performClickInternal(View.java:7318)
    	at android.view.View.access$3200(View.java:846)
    	at android.view.View$PerformClick.run(View.java:27800)
    	at android.os.Handler.handleCallback(Handler.java:873)
    	at android.os.Handler.dispatchMessage(Handler.java:99)
    	at android.os.Looper.loop(Looper.java:214)
    	at android.app.ActivityThread.main(ActivityThread.java:7050)
    	at java.lang.reflect.Method.invoke(Native Method)
    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
    Caused by: java.lang.reflect.InvocationTargetException
    	at java.lang.reflect.Method.invoke(Native Method)
    	... 14 more
    Caused by: java.lang.RuntimeException: test java exception
    	at j.n.b(Unknown Source:20)
    	at com.chinapnr.postbev2.SecondActivity.testJavaCrashInMainThread_onClick(Unknown Source:1)
    	... 15 more
    +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
    

    通过使用上面的命令行解析之后的结果即替换掉了混淆的部分,比如上面的日志,就是替换了

    	at d.b.c.t$a.onClick(:2)
    

    这一行为未混淆时候的代码。结果如下:

    至此java崩溃的捕捉到解析就完成了。

    Native崩溃

     Native崩溃一般都 是因为在Native代码中访问非法地址,也可能是地址对⻬出现了问题,或者发生了程序主动abort,这些都会产生相应的 signal信号,导致程序异常退出。通常我们常见的几种信号大致如下:

    #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 // 非法的系统调用

    Native崩溃捕获 

    当一个动态库(native 程序)开始执行时,系统会注册一些连接到 debuggerd 的 signal handlers,当系统 crash 的时候,会保存一个 tombstone 文件到/data/tombstones目录下(Logcat中也会有相应的信息),文件就像墓碑一样记录了死亡了的进程的基本信息(例如进程的进程号,线程号),死亡的地址(在哪个地址上发生了 Crash),死亡时的现场是什么样的(记录了一系列的堆栈调用信息)等。

    大致的流程步骤如下:

    • 当Native进程发生了异常,比如NULL指针
    • 操作系统会去异常向量表的地址去处理异常,然后发送信号
    • 在debuggred_init注册的信号处理函数就会收到处理
    • 创建伪线程去启动crash_dump进程,crash_dump则会获取当前进程中各个线程的crash信息
    • tombstoned进程是开机就启动的,开机时注册好了socket等待监听
    • 当在crash_dump中去连接tombstoned进程的时候,根据传递的dump_type类型会返回一个/data/tombstones/下文件描述符
    • crash_dump进程后续通过engrave_tombstone函数将所有的线程的详细信息写入到tombstone文件中
    • 在/data/tombstones下生成了此次对应的tombstone_XX文件

    Native崩溃的捕捉重点就在于在C层替换信号处理函数,安装信号,进行信号处理,然后通过ptrace技术来获取线程的regs,backtrace等信息。以下的处理方案来源于Xcrash开源库的分析,大家可以去看源码。

    1、java层
         加载libscrash.so, nativeInit调用进行native层的初始化。
    2、native层
         nativeInit() 所映射的 jni 实现是 xc_jni_init()。xc_jni_init分3小步初始化:
         1)xc_common_init:初始化公共参数,初始化两个文件fd(非负整数,索引值,指向内核为每一个进程所维护的该进程打开文件的记录)。
         2)xc_crash_init:xc_crash_init_callback初始化 jni call back。初始化Ntaive线程通过eventfd(进程或者线程间的通信(如通知/等待机制的实现))阻塞等待native发生crash向上层java发出通知。
        

    Native崩溃解析

    同java崩溃一样,Native崩溃也需要一个映射文件和工具。Native的映射文件为带有调试符号信息的so包。

    一个完整的 so 由C代码加一些 debug 信息组成,这些debug信息会记录 so 中所有方法的对照表,就是方法名和其偏移地址的对应表,也叫做符号表,这种 so 也是未 strip 的,通常体积会比较大。

    IDE如果使用Android Sutdio+NDK,即项目中存在cpp项目,则在每次编译之后会生成对应的debug so文件(需保持最新最后一次编译产物),会按照对应的CPU指令架构集分类,较低版本的gradle插件使用ndk build的话,可能存在于如下路径(由于版本等,可能存在于其他路径,开发自行查找):

     如果gradle4.0以上和使用Cmake打包so文件的生成路径,开发者需要上传的就是下图路径下对应环境下的obj/下的内容:

     解析native崩溃信息。多了一步前提,需要一个匹配条件:cpu 架构指令集类型。即崩溃日志中统计到的abi的类型。

    //日志中信息
    ABI: 'arm64'
    
    //abi对应debug so所在文件夹名称
    arm64===》amr64-v8a
    armeabi===》armeabi
    armeabi-v7a===》armeabi-v7a
    x86===》x86
    x86_64===》x86_64
    
    

    确认好native对应崩溃的abi平台,之后可以进行对应的debug so包调用和解析,具体使用是使用sdkndk包中的工具,命令结构如下:

    工具:/sdk/ndk/21.1.6352462/ndk-stack
    
          ndk-stack: ndk-stack -sym (对应abi下面的符号表文件路径) -dump (日志文件)
    
    工具:/sdk/ndk/21.1.6352462/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line
    
          addr2line: addr2line -C -f -e (对应abi下面的符号表so包文件路径)(日志中显示的地址)

     同样,Native的崩溃日志格式也有要求:

    *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    Build fingerprint: 'samsung/dreamqltezc/dreamqltechn:9/PPR1.180610.011/G9500ZCU4DSH2:user/release-keys'
    ABI: 'arm64'
    .....报错信息等
    
    backtrace:
        内容
    

    使用上面的ndk-stack工具跑命令行解析输出结果如下:

    使用addr2line工具命令行解析单个地址输出结果如下:

     至此,Native的崩溃日志解析完成啦~

    ANR

    遇上ANR,一般都是个耗时的事儿。需要先学会如何看ANR和定位。Traces.txt系统自动生成的记录anr等异常的文件,只记录java代码产生的异常。我们通过使用adb工具USB连接手机在终端是可以导出系统生成的文件:

    adb bugreport

    导出的文件目录如下:

    这是我们在程序之外的操作,下面大致讲述一下如果在线上APP中捕获ANR。

    ANR的捕获 

    Android 7.0(可能是6.0)之前,可以通过监听 /data/anr 目录的变化。获取系统生成好的ANR日志。

    fileObserver = new FileObserver("/data/anr/", CLOSE_WRITE) {
                public void onEvent(int event, String path) {
                    try {
                        if (path != null) {
                            String filepath = "/data/anr/" + path;
                            if (filepath.contains("trace")) {
                                handleAnr(filepath);
                            }
                        }
                    } catch (Exception e) {
                        XCrash.getLogger().e(Util.TAG, "AnrHandler fileObserver onEvent failed", e);
                    }
                }
            };
    
            try {
                fileObserver.startWatching();
            } catch (Exception e) {
                fileObserver = null;
                XCrash.getLogger().e(Util.TAG, "AnrHandler fileObserver startWatching failed", e);
            }

    高版本的 Android 系统中,应用已经访问不到 /data/anr 了。C层面的方案就是捕获了 SIGQUIT 信号,这个是 Android App 发生 ANR 时由 ActivityMangerService 向 App 发送的信号。和处理Native Crash是一样的原理。大家可以去详细学习Xcrash的源码思路

    ANR部分主要还是Trace文件的详解,这个大家可以去自行搜索相关文档,很多大佬已经将如何一步一步定位ANR总结出来了。我这里就不做啰嗦啦,如何设计到解析,原理也同Native的崩溃日志解析一致。

    展开全文
  • Second edition of the best selling Python book in the world. A fast-paced, no-nonsense guide to programming in Python. This book teaches beginners the basics of programming in Python with a focus on ...
  • java源码:Java开发的SHELL CRaSH.gz
  • java网站源码Crash Bandicoot 资源 该存储库旨在收集有关 crash bandicoot 文件格式和相关逆向工程研究和工具的所有资源,请随时通过 fork + pull request 做出贡献。 Crash Bandicoot N Sane 三部曲 (CB NST) CBNST...
  • 保存kernel crash信息的一种方式,文件内为源代码实现。可自行分析。稍加修改即自用。
  • 毕业设计php源码初学者 这是一门旨在介绍 JavaScript 和 Node.js 作为后端应用程序平台的课程。 它假定您了解(面向对象)编程的基本概念,但我们将从初学者级别开始并逐步建立。...(#js-crash-cours
  • crash使用总结

    2021-01-20 13:18:13
    根据栈,确定相关函数在源码中的具体位置 想要知道该down_read所处的位置 crash> bt 1620 PID: 1620 TASK: ffff88812edb4a00 CPU: 6 COMMAND: mount_clear_soc  #0 [ffffc900005e3c88] __schedule at ffffffff...
  • bugly_crash_release.jar_release_bugly_源码.zip
  • 1.1CrashRecovery流程11.2CrashRecovery优化61.2.1HashTableSize...81.3.1rollbacksegment81.3.2Transaction101.3.3DB_ROLLBACK_PTR11本文主要分析了InnoDB整个crashrecovery的源码处理流程,总入口函数是innobase_st
  • 该项目涉及向Linux内核添加代码以实现崩溃转储,以对内核崩溃进行故障后分析。
  • Crash分析

    2019-04-20 01:09:49
    NULL 博文链接:https://buptrock.iteye.com/blog/1887305
  • App Crash全称Application crash, 对于Crash可分为Java Crash和Native Crash。   对于Crash所有的Android App开发者都会遇到,那么为什么会出现Crash呢?系统又是如何处理Crash的呢? 例如,在开发中大家...

    什么是Crash?

      App Crash全称Application crash, 对于Crash可分为Java Crash和Native Crash。

      对于Crash所有的Android App开发者都会遇到,那么为什么会出现Crash呢?系统又是如何处理Crash的呢? 例如,在开发中大家经常使用try…catch语句来进行异常捕获,但还是会有一些异常是在运行中动态产生的,这些没有被有效捕获的异常就是导致应用Crash的原因。

    小结:Crash是由于代码异常而导致App非正常退出现象,也就是我们常说的崩溃

    系统的Crash 处理过程

    1.1 Java Crash 产生过程

    在这里插入图片描述
      上图是Android App启动时序图,我们想要探知的Crash秘密就存在于ZygoteInit.zygoteInit()函数中。下面让我们一起来看一下它的内部实现。

        public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
                String[] argv, ClassLoader classLoader) {
           //重定向的System.out和System.err到Android日志
            RuntimeInit.redirectLogStreams();
           // **重点关注** 用于初始化一些App运行期间需要用到的配置
            RuntimeInit.commonInit();
           // 调用Native方法进行初始化
            ZygoteInit.nativeZygoteInit();
          	...
        }
    

       可以看到zygoteInit 是一个组装函数,下面让我们一探隐藏在内部的RuntimeInit.commonInit()函数。

    protected static final void commonInit() {
            // LoggingHandler 用于组装异常信息并打印
            LoggingHandler loggingHandler = new LoggingHandler();
            // API 30 开始加入RuntimeHooks。RuntimeHooks.setUncaughtExceptionPreHandler()内部调用了Thread.setUncaughtExceptionPreHandler()
            RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
            // KillApplicationHandler 用于弹出Dialog并杀死进程
            Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
    		...
        }
    

       所以当Android App发生未被捕获的异常发生时会终止线程,此时系统便会调用UncaughtExceptionHandler,告诉它被出错的线程以及对应的异常,然后便会调用uncaughtException函数.如果UncaughtExceptionHandler没有被设置,则会调用对应线程组的DefaultUncaughtExceptionHandler

    Thread.setUncaughtExceptionPreHandler覆盖所有线程,会在DefaultUncaughtExceptionHandler之前调用,只能在Android Framework内部调用该方法.

    Thread.setDefaultUncaughtExceptionHandler任意线程中设置即可作为所有线程的默认异常处理,可以在应用层调用,每次调用传入的Thread.UncaughtExceptionHandler都会替换上一次的.

    new Thread().setUncaughtExceptionHandler()只可以处理当前线程的异常,如果有Thread设置了UncaughtExceptionHandler,则在当前线程不会再使用全局的DefaultUncaughtExceptionHandler.

       LoggingHandler.Java

     private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
     		// 用于标识是否已触发处理逻辑
            public volatile boolean mTriggered = false;
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                mTriggered = true;
              	// 如果 KillApplicationHandler.uncaughtException()已经被触发了则不再继续执行
                if (mCrashing) return;
                // 判断是否为系统进程,mApplicationObject == null,一定不是普通的app进程. 但是除了system进程, 也有可能是shell进程, 即通过app_process + 命令参数 的方式创建的进程
                if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
                    Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
                } else {
                    StringBuilder message = new StringBuilder();
                    message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
                    final String processName = ActivityThread.currentProcessName();
                    if (processName != null) {
                        message.append("Process: ").append(processName).append(", ");
                    }
                    message.append("PID: ").append(Process.myPid());
                    Clog_e(TAG, message.toString(), e);
                }
            }
        }
    
    • 当system进程Crash的信息:

    *** FATAL EXCEPTION IN SYSTEM PROCESS [线程名]为开头;
    在第二行开始输出发生Crash时的调用栈信息;

    • 其他进程Crash时的信息:

    FATAL EXCEPTION: [线程名] 为开头
    在第二行输出 Process: [进程名], PID: [进程id]
    第三行开始输出发生Crash时的调用栈信息;

       KillApplicationHandler.Java

     private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
            // 内部持有LoggingHandler用于输出异常信息。
            private final LoggingHandler mLoggingHandler;
            public KillApplicationHandler(LoggingHandler loggingHandler) {
                this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
            }
            
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                try {
                // 触发异常信息记录
                    ensureLogging(t, e);
                    //避免在处理崩溃信息时发生异常导致无限重入
                    if (mCrashing) return;
                    mCrashing = true;
    				//进行意外停止分析
                    if (ActivityThread.currentActivityThread() != null) {
                        ActivityThread.currentActivityThread().stopProfiling();
                    }
                    // 在此处弹出弹窗并杀死所在进程 **核心 见小节1.2** 
                    ActivityManager.getService().handleApplicationCrash(
                            mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
                } catch (Throwable t2) {
                  ...
                } finally {
                    // 通过finally语句块保证能执行并彻底杀掉Crash进程。当Crash进程被杀后,并没有完全结束,还有Binder死亡通知的流程还没有处理完成。
                    Process.killProcess(Process.myPid());
                    System.exit(10);
                }
            }
            private void ensureLogging(Thread t, Throwable e) {
               // 调用mLoggingHandler组装并打印异常信息
            }
        }
    

       上面的代码注释很详细就不再赘述了。现在我们可以总结一下Android Crash的特点:

    1. 一般情况下程序出错时会弹出提示框;
    2. 程序所在进程被杀死,JVM虚拟机退出;
    3. 系统提供了捕获Crash的接口;
    4. 由Java 层代码引发的Java Crash 较容易捕获分析;
    5. 有C++ 层代码引发的Native Crash 一般的工具不能将其捕获;

    1.2 发生Crash 时系统都做了什么?

       在上一节中我们详细的梳理了Android Crash的产生过程,其中在KillApplicationHandler .uncaughtException()函数中我们看到了ActivityManager.getService().handleApplicationCrash()这样一个函数,其实这才是Android系统在发生Crash时所做事情的核心入口,接下来我们就将对其展开讲解。

      我们先来看一下处理Crash的流程图:

    在这里插入图片描述

       ActivityManager.getService()返回的是ActivityManagerProxy实例(简称AMP),AMP经过binder调用最终交给ActivityManagerService(简称AMS)中相应的方法去处理,故接下来调用的是AMS.handleApplicationCrash()。

    1.2.1 AMS.handleApplicationCrash()

     public void handleApplicationCrash(IBinder app,
                ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
            // 获取进程对象,见1.2.2小节
            ProcessRecord r = findAppProcess(app, "Crash");
            final String processName = app == null ? "system_server"
                    : (r == null ? "unknown" : r.processName);
            // 见1.2.3小节        
            handleApplicationCrashInner("crash", r, processName, crashInfo);
        }
    

      关于进程名(processName):

    • 当远程IBinder对象为空时,则进程名为system_server;
    • 当远程IBinder对象不为空,且ProcessRecord为空时,则进程名为unknown;
    • 当远程IBinder对象不为空,且ProcessRecord不为空时,则进程名为ProcessRecord对象中相应进程名。

    1.2.1 AMS.findAppProcess()

        private ProcessRecord findAppProcess(IBinder app, String reason) {
            if (app == null) {
                return null;
            }
    
            synchronized (this) {
                return 找到app对应的进程信息,如果没找到会返回Null;
            }
        }
    

    1.2.3 AMS.handleApplicationCrashInner()

      void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
                ApplicationErrorReport.CrashInfo crashInfo) {
     		//将Crash信息写入到 Event log
        	EventLog.writeEvent(EventLogTags.AM_CRASH,...);
        	//将错误信息添加到 DropBox
        	addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
        	// 见1.2.4
        	mAppErrors.crashApplication(r, crashInfo);
        }
    

    addErrorToDropBox是将Crash的信息输出到目录/data/system/dropbox。例如system_server的dropbox文件名为system_server_crash@时间戳.txt

    1.2.4 AppErrors.crashApplication()

       void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
           	//清除远程调用者uid和pid信息,并保存到origId
           	final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            try {
            	if(存在ActivityController,比如monkey){
            		// 调用monkey的appCrashed
            		retutn;
            	}
            	  // 见1.2.5
            	if (!makeAppCrashingLocked()) {
                	Binder.restoreCallingIdentity(origId);
                	return;
            	}
            	...
            	//发送消息SHOW_ERROR_MSG,弹出提示crash的对话框,等待用户选择【见小节10】
            	mUiHandler.sendMessage(msg);
            	//进入阻塞等待,直到用户选择crash对话框
        		int res = result.get();
            } finally {
            	//恢复远程调用者uid和pid
                Binder.restoreCallingIdentity(origId);
            }
        }
    

       此方法主要做的两件事:

    1. 调用makeAppCrashingLocked,继续处理Crash流程;
    2. 发送消息,弹出提示Crash的对话框,等待用户选择;

    1.2.5 AppErrors.makeAppCrashingLocked()

        private boolean makeAppCrashingLocked(ProcessRecord app,
                String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
            app.setCrashing(true);
            //封装crash信息到crashingReport对象
            app.crashingReport = generateProcessError(app,
                    ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
            //  见1.2.6
            app.startAppProblemLocked();
            // 停止屏幕冻结 见1.2.7 
            app.getWindowProcessController().stopFreezingActivities();
            // 见1.2.8 
            return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
                    data);
        }
    

    1.2.6 ProcessRecord.startAppProblemLocked()

     void startAppProblemLocked() {
           ComponentName errorReportReceiver = null;
            for (int userId : mService.mUserController.getCurrentProfileIds()) {
                if (this.userId == userId) {
                  	// 获取获取当前用户下的Crash应用的 ErrorReceiver(需要在系统的设置中的错误报告功能开启时才会有值)
                    errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
                            mService.mContext, info.packageName, info.flags);
                }
            }
            //调用AMS忽略当前app的广播接收
            mService.skipCurrentReceiverLocked(this);
        }
    

       此方法主要做的两件事:

    1. 如果开启了错误报告功能,获取广播接收器的组件名称;
    2. 忽略发生Crash应用的所有广播接收;

    1.2.7 ProcessRecord.stopFreezingActivities()

    public void stopFreezingActivities(boolean force) {
      ...
      //其中activities类型为ArrayList<ActivityRecord>,停止进程里所有的Activity
      int i = activities.size();
        while (i > 0) {
            i--;
            activities.get(i).stopFreezingScreenLocked(true); 
        }
    }
    
    public void stopFreezingScreenLocked(boolean force) {
    	// appToken是WindowManager的token。 见1.7.9
    	WMS.stopAppFreezingScreen(appToken, force);
    }
    

    1.2.8 AppErrors.handleAppCrashLocked()

    boolean handleAppCrashLocked(ProcessRecord app, String reason, String shortMsg, String longMsg, String stackTrace) {
        if(同一进程在1分钟内连续两次crash){
        	if(不是persistent进程){
        		// ActivityStackSupervisor 简称ASS
        		ASS.handleAppCrashLocked, 直接结束该应用所有activity
        		AMS.removeProcessLocked,杀死该进程以及同一个进程组下的所有进程
        	}
        	ASS.resumeTopActivitiesLocked,恢复栈顶第一个非finishing状态的activity
        }else{
        	ASS.finishTopRunningActivityLocked,执行结束栈顶正在运行activity
        }
    }
    

    1.2.9 WMS.stopFreezingScreenLocked()

    public void stopFreezingScreen() {
        if (权限检查) {
            throw new SecurityException("Requires FREEZE_SCREEN permission");
        }
     
        synchronized(mWindowMap) {
         	1. 处理屏幕旋转相关逻辑;
    	 	2. 移除冻屏的超时消息;
    	 	3. 屏幕旋转动画的相关操作;
    	 	4. 使能输入事件分发功能;
    	 	5. display冻结时,执行gc操作;
    	 	6. 更新当前的屏幕方向;
    	 	7. 发送configuraion改变的消息。
        }
    }
    

    1.2.10 AMS.UiHandler

    final class UiHandler extends Handler {
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case SHOW_ERROR_MSG: 
    		   1. 创建提示crash对话框,等待用户选择,5分钟操作等待。
    		   2. 阻塞等待用户选择,当用户不做任何选择5分钟超时后,默认选择“确定”,当手机休眠时也默认选择“确定”
    		 break;
            ...
        }
    }
    

    1.3 总结

      本文主要结合源码,详细介绍了App Crash后系统的处理流程:

    1.首先发生Crash所在进程,在创建之初便准备好了DefaultUncaughtHandler,用来来处理Uncaught Exception,并输出当前Crash基本信息;
    2. 调用当前进程中的AMP.handleApplicationCrash;经过binder ipc机制,传递到system_server进程;
    3. 接下来,进入system_server进程,调用binder服务端执行AMS.handleApplicationCrash;
    4. 从mProcessNames查找到目标进程的ProcessRecord对象;并将进程crash信息输出到目录/data/system/dropbox;
    5. 执行makeAppCrashingLocked

    如果开启了错误报告功能,创建当前用户下的Crash应用的ErrorReceiver,并忽略当前应用的广播;
    停止当前进程中所有Activity中的WMS的冻结屏幕消息,并执行相关一些屏幕相关操作;
    6. 执行handleAppCrashLocked方法,
    当1分钟内同一进程连续Crash两次时,且非persistent进程,则直接结束该应用所有Activity,并杀死该进程以及同一个进程组下的所有进程。然后再恢复栈顶第一个非finishing状态的Activity;
    当1分钟内同一进程连续crash两次时,且persistent进程,,则只执行恢复栈顶第一个非finishing状态的Activity;
    当1分钟内同一进程未发生连续Crash两次时,则执行结束栈顶正在运行Activity的流程。

    7. 通过UiHandler发送消息SHOW_ERROR_MSG,弹出Crash对话框;
    8. system_server进程执行完成。回到Crash进程开始执行杀掉当前进程的操作;
    9. 当Crash进程被杀,通过binder死亡通知,告知system_server进程来执行appDiedLocked();
    10. 最后,执行清理应用相关的activity/service/ContentProvider/receiver组件信息。

       这基本就是整个应用Crash后系统的执行过程。

    小知识:
    当60s内连续Crash两次的非persistent进程时,被认定为bad进程:那么如果第3次从后台启动该进程(Intent.getFlags来判断),则会拒绝创建进程;
    当Crash次数达到两次的非persistent进程发生Crash时,则再次杀该进程,随后即便允许自启的Service也会在被杀后拒绝再次启动。

    展开全文
  • JVM crash 错误日志分析

    2019-04-02 01:09:31
    NULL 博文链接:https://myspace1916.iteye.com/blog/1441465
  • crash常用的调试命令

    千次阅读 2020-05-08 11:07:25
    我的crash常用命令如下所示: log/dmesg: 打印出故障现场的kmsg缓冲区log_buf中的内容。 struct:展示结构体的定义,或者从指定的地址开始解析一个结构体。 union:与struct类似,但是用于union的展示 p:print查看...
  • 应用程序crash在开发过程中还是很常见的,本文主要是从源码的角度去跟踪下Android对于crash的处理流程。App crash的全称:Application crash。而Crash又分为:native crash和framework crash(包含App Crash)。我们...
  • SafeKit:防止crash

    2021-04-05 07:12:33
    作者JJMM,源码SafeKit,SafeKit用来防止低级错误引起的crash 使用SafeKit,[array addObject:nil]等会crash的代码,都可以正常运行 原理是method swizzling替换了系统方法,处理参数边界,现多个项目使用,一切正常...
  • Crash初步分析过程:

    2012-08-03 15:56:58
    Crash分析过程: Linux内核(以下简称内核)是一个不与特定进程相关的功能集合,内核的代码很难轻易的在调试器中执行和跟踪。开发者认为,内核如果发生了错误,就不应该继续运行。因此内核发生错误时,它的行为通常...
  • InnoDB Crash Recovery 流程源码实现分析

    千次阅读 2015-09-25 10:58:17
    InnoDB Crash Recovery 流程源码实现分析 1 Crash Recovery问题 1 1.1 Crash Recovery流程 1 1.2 Crash Recovery优化 6 1.2.1 Hash Table Size 7 1.2.2 Red Bl
  • 本文主要介绍linux下crash工具常用命令的功能和使用。背景知识crash是redhat的工程师开发的,主要用来离线分析linux内核转存文件,它整合了gdb工具,功能非常强大。可以查看堆栈,dmesg日志,内核数据结构,反汇编...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,229
精华内容 9,691
关键字:

crash 源码