-
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’ failedcrash 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:13crash utility v7.04 source code -
百度地图毕业设计源码-Crash:这是一个研究androidcrash的源码项目
2021-06-06 06:48:51百度地图毕业设计源码 APP Crash 研究报告 1、研究背景 1.1、什么是Crash Crash 即闪退,多指在移动设备中,在打开或运行应用程序过程中出现突然中断的情况。 1.2、为什么会Crash 由于出现异常且没有被应用程序捕捉... -
crash 查看动态内存中的代码
2022-04-01 15:16:25linux 下查看动态内存(/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)
-
Centos:
1.下载:
kernel-debuginfo-$(uname -r).rpm
kernel-debug-debuginfo-$(uname -r).rpm
kernel-debugindo-common-$(uname -r).rpm
下载网址:http://debuginfo.centos.org
可以直接使用命令下载:
wget http://debuginfo.centos.org/7/x86_64/file_name
2.安装:yum install *.rpm -y
安装完成后会生成,/usr/lib/debug/lib/modules/$(uname -r)/vmlinux
-
Ubuntu:
1.下载:
linux-image-unsigned-$(uname -r)-dbgsym.ddeb
下载网址:http://ddebs.ubuntu.com/pool/main/l/linux/
2.安装:sudo dpkg -i *.ddeb
https://blog.csdn.net/qq_42931917/article/details/108236139
https://www.ebpf.top/post/ubuntu_kdump_crash/
四、使用
-
启动:
[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>
-
查看相应地址的反汇编代码:
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 crash course 2nd edition_CrashCourse_python_python教程_源码
2021-10-02 02:47:48不管新手还是老手都能从中受益的英文原版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包调用和解析,具体使用是使用sdk下ndk包中的工具,命令结构如下:
工具:/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的崩溃日志解析一致。
-
Python Crash Course 2nd Edition原版电子书及源码
2019-06-21 23:17:48Second 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
2021-10-13 15:04:21java源码:Java开发的SHELL CRaSH.gz -
java红酒网站源码-Crash-Bandicoot-Resources:有关操作CrashBandicoot数据的资源
2021-06-05 09:00:01java网站源码Crash Bandicoot 资源 该存储库旨在收集有关 crash bandicoot 文件格式和相关逆向工程研究和工具的所有资源,请随时通过 fork + pull request 做出贡献。 Crash Bandicoot N Sane 三部曲 (CB NST) CBNST... -
kernel crash panic, 保存kernel crash信息的功能
2018-03-06 15:14:25保存kernel crash信息的一种方式,文件内为源代码实现。可自行分析。稍加修改即自用。 -
毕业设计php源码-javascript-crash-course:WomenTechmakersGDGCloudLondonJavaScri
2021-06-06 10:11:26毕业设计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
2021-10-18 22:59:53bugly_crash_release.jar_release_bugly_源码.zip -
InnoDBCrashRecovery流程源码实现分析
2021-01-31 07:39:321.1CrashRecovery流程11.2CrashRecovery优化61.2.1HashTableSize...81.3.1rollbacksegment81.3.2Transaction101.3.3DB_ROLLBACK_PTR11本文主要分析了InnoDB整个crashrecovery的源码处理流程,总入口函数是innobase_st -
Linux Kernel Crash Dumps-开源
2021-05-03 02:46:53该项目涉及向Linux内核添加代码以实现崩溃转储,以对内核崩溃进行故障后分析。 -
Crash分析
2019-04-20 01:09:49NULL 博文链接:https://buptrock.iteye.com/blog/1887305 -
Android Crash的产生与解决方案( 一. 源码篇 )
2020-11-16 22:35:57App 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@时间戳.txt1.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); } }
此方法主要做的两件事:
- 调用
makeAppCrashingLocked
,继续处理Crash流程; - 发送消息,弹出提示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); }
此方法主要做的两件事:
- 如果开启了错误报告功能,获取广播接收器的组件名称;
- 忽略发生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:31NULL 博文链接: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查看... -
结合源码深入理解Android Crash处理流程
2019-07-03 21:32:05应用程序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:58Crash分析过程: Linux内核(以下简称内核)是一个不与特定进程相关的功能集合,内核的代码很难轻易的在调试器中执行和跟踪。开发者认为,内核如果发生了错误,就不应该继续运行。因此内核发生错误时,它的行为通常... -
InnoDB Crash Recovery 流程源码实现分析
2015-09-25 10:58:17InnoDB 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 -
系统崩溃 - crash工具介绍
2021-05-27 08:09:00本文主要介绍linux下crash工具常用命令的功能和使用。背景知识crash是redhat的工程师开发的,主要用来离线分析linux内核转存文件,它整合了gdb工具,功能非常强大。可以查看堆栈,dmesg日志,内核数据结构,反汇编...