xCrash
xCrash 能为安卓 app 提供捕获 java 崩溃,native 崩溃和 ANR 的能力。不需要 root 权限或任何系统权限。
xCrash 能在 app 进程崩溃或 ANR 时,在你指定的目录中生成一个 tombstone 文件(格式与安卓系统的 tombstone 文件类似)。
特征
- 支持 Android 4.1 - 11(API level 16 - 30)。
- 支持 armeabi-v7a,arm64-v8a,x86 和 x86_64。
- 捕获 java 崩溃,native 崩溃和 ANR。
- 获取详细的进程、线程、内存、FD、网络统计信息。
- 通过正则表达式设置需要获取哪些线程的信息。
- 不需要 root 权限或任何系统权限。
概览图
架构
捕获 native 崩溃
捕获 ANR
使用
1. 增加依赖。
dependencies {
implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0'
}
2. 指定一个或多个你需要的 ABI。
android {
defaultConfig {
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
}
3. 初始化 xCrash。
Java
public class MyCustomApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
xcrash.XCrash.init(this);
}
}
Kotlin
class MyCustomApplication : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
xcrash.XCrash.init(this)
}
}
Tombstone 文件默认将被写入到 Context#getFilesDir() + "/tombstones"
目录。(通常在: /data/data/PACKAGE_NAME/files/tombstones
)
在 xcrash_sample 文件夹中,有一个更实际和复杂的示例 app。
配置示例
// Initialize xCrash.
XCrash.init(this, new XCrash.InitParameters()
.setAppVersion("1.2.3-beta456-patch789")
.setJavaRethrow(true)
.setJavaLogCountMax(10)
.setJavaDumpAllThreadsWhiteList(new String[]{"^main$", "^Binder:.*", ".*Finalizer.*"})
.setJavaDumpAllThreadsCountMax(10)
.setJavaCallback(callback)
.setNativeRethrow(true)
.setNativeLogCountMax(10)
.setNativeDumpAllThreadsWhiteList(new String[]{"^xcrash\\.sample$", "^Signal Catcher$", "^Jit thread pool$", ".*(R|r)ender.*", ".*Chrome.*"})
.setNativeDumpAllThreadsCountMax(10)
.setNativeCallback(callback)
// .setAnrCheckProcessState(false)
.setAnrRethrow(true)
.setAnrLogCountMax(10)
.setAnrCallback(callback)
.setAnrFastCallback(anrFastCallback)
.setPlaceholderCountMax(3)
.setPlaceholderSizeKb(512)
// .setLogDir(getExternalFilesDir("xcrash").toString())
.setLogFileMaintainDelayMs(1000));
上传
在应用每次 startup 时获取崩溃报告,上传后删除文件
for(File file : TombstoneManager.getAllTombstones()) {
sendThenDeleteCrashLog(file.getAbsolutePath(), null);
}
private void sendThenDeleteCrashLog(String logPath, String emergency) {
// Parse
Map<String, String> map = TombstoneParser.parse(logPath, emergency);
String crashReport = new JSONObject(map).toString();
// 发送到服务端
// ......
// onSuccess(){
// If the server-side receives successfully, delete the log file.
//
// Note: When you use the placeholder file feature,
// please always use this method to delete tombstone files.
//
//TombstoneManager.deleteTombstone(logPath);
// }
}
分析
java crash
就像正常堆栈日志信息一样看就行
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Tombstone maker: 'xCrash 3.1.0'
Crash type: 'java'
Start time: '2024-07-09T23:08:43.105+0800'
Crash time: '2024-07-09T23:08:44.853+0800'
App ID: 'xcrash.sample'
App version: '1.2.3-beta456-patch789'
Rooted: 'Yes'
API level: '31'
OS version: '12'
ABI list: 'arm64-v8a,armeabi-v7a,armeabi'
Manufacturer: 'Xiaomi'
Brand: 'Xiaomi'
Model: 'Xiaomi 14 Ultra'
Build fingerprint: 'Xiaomi/odin/odin:12/SKQ1.211006.001/V13.0.5.1.32.DEV:user/release-keys'
pid: 24082, tid: 24082, name: main >>> xcrash.sample <<<
java stacktrace:
java.lang.IllegalStateException: Could not execute method for android:onClick
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:414)
at android.view.View.performClick(View.java:7750)
at android.view.View.performClickInternal(View.java:7727)
at android.view.View.access$3700(View.java:861)
at android.view.View$PerformClick.run(View.java:29133)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:299)
at android.app.ActivityThread.main(ActivityThread.java:8105)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409)
... 12 more
Caused by: java.lang.RuntimeException: test java exception
at xcrash.XCrash.testJavaCrash(XCrash.java:911)
at xcrash.sample.MainActivity.testJavaCrashInMainThread_onClick(MainActivity.java:66)
... 14 more
logcat:
--------- tail end of log main (/system/bin/logcat -b main -d -v threadtime -t 200 --pid 24082 *:D)
--------- beginning of main
07-09 23:08:42.953 24082 24082 I xcrash.sample: Late-enabling -Xcheck:jni
07-09 23:08:42.984 24082 24082 D ProcessState: Binder ioctl to enable oneway spam detection failed: Invalid argument
07-09 23:08:43.018 24082 24112 D AppScoutStateMachine: 24082-ScoutStateMachinecreated
07-09 23:08:43.045 24082 24082 W xcrash.sample: DexFile /data/data/xcrash.sample/code_cache/.studio/instruments-bd0f66b3.jar is in boot class path but is not in a known location
07-09 23:08:43.056 24082 24082 W xcrash.sample: Redefining intrinsic method java.lang.Thread java.lang.Thread.currentThread(). This may cause the unexpected use of the original definition of java.lang.Thread java.lang.Thread.currentThread()in methods that have already been compiled.
native crash
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Tombstone maker: 'xCrash 3.1.0'
Crash type: 'native'
Start time: '2024-07-09T23:21:27.249+0800'
Crash time: '2024-07-09T23:21:31.906+0800'
App ID: 'xcrash.sample'
App version: '1.2.3-beta456-patch789'
Rooted: 'Yes'
API level: '31'
OS version: '12'
Kernel version: 'Linux version 5.4.147-qgki-gcd25038ebc13 #1 SMP PREEMPT Tue May 17 14:30:03 CST 2022 f2fs-hash:9f5701dafc (aarch64)'
ABI list: 'arm64-v8a,armeabi-v7a,armeabi'
Manufacturer: 'Xiaomi'
Brand: 'Xiaomi'
Model: 'Xiaomi 14 Ultra'
Build fingerprint: 'Xiaomi/odin/odin:12/SKQ1.211006.001/V13.0.5.1.32.DEV:user/release-keys'
ABI: 'arm64'
pid: 5713, tid: 5713, name: xcrash.sample >>> xcrash.sample <<<
signal 5 (SIGTRAP), code 1 (TRAP_BRKPT), fault addr 0x7539cd8838
Abort message: 'abort message for xCrash internal testing'
x0 00000074c22ecae8 x1 b40000749d945780 x2 0000007539ce0ac0 x3 0000000000000004
x4 206c616e7265746e x5 00676e6974736574 x6 0000000000000080 x7 8000000000000000
x8 e7f8f5571bf6f539 x9 e7f8f5571bf6f539 x10 0000000000000040 x11 0000007480000000
x12 0000000000000000 x13 000000001f7fabe0 x14 0000000000080100 x15 0000000000000080
x16 00000075f198ed60 x17 00000075f197e654 x18 0000007608710000 x19 0000000000000000
x20 0000007607963000 x21 b400007607724c00 x22 0000007607963000 x23 b400007607724cb0
x24 0000007539f0f630 x25 b400007607708000 x26 0000007607963000 x27 0000000000000037
x28 0000007ff54a2850 x29 0000007ff54a27d0
sp 0000007ff54a27d0 lr 0000007539cd8838 pc 0000007539cd8838
backtrace:
#00 pc 0000000000005838 /data/app/~~xqV_C9StmNFMJK_KKAPszg==/xcrash.sample-6s4VRLXNrk3-hcS-5AIu6A==/lib/arm64/libxcrash.so (xc_test_call_4+12)
#01 pc 00000000000058e0 /data/app/~~xqV_C9StmNFMJK_KKAPszg==/xcrash.sample-6s4VRLXNrk3-hcS-5AIu6A==/lib/arm64/libxcrash.so (xc_test_call_3+8)
#02 pc 00000000000058f0 /data/app/~~xqV_C9StmNFMJK_KKAPszg==/xcrash.sample-6s4VRLXNrk3-hcS-5AIu6A==/lib/arm64/libxcrash.so (xc_test_call_2+12)
#03 pc 0000000000005900 /data/app/~~xqV_C9StmNFMJK_KKAPszg==/xcrash.sample-6s4VRLXNrk3-hcS-5AIu6A==/lib/arm64/libxcrash.so (xc_test_call_1+12)
#04 pc 0000000000005974 /data/app/~~xqV_C9StmNFMJK_KKAPszg==/xcrash.sample-6s4VRLXNrk3-hcS-5AIu6A==/lib/arm64/libxcrash.so (xc_test_crash+112)
#05 pc 0000000000222244 /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+148)
#06 pc 0000000000218be8 /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub+568)
#07 pc 000000000028600c /apex/com.android.art/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+220)
build id:
/data/app/~~xqV_C9StmNFMJK_KKAPszg==/xcrash.sample-6s4VRLXNrk3-hcS-5AIu6A==/lib/arm64/libxcrash.so (BuildId: 8b1d9f09cb8d2f09cb4b11c60433581396a138f2. FileSize: 76152. LastModified: 1981-01-01T01:01:02.000+0800. MD5: 10ff20800e9da1ccca9264e35be94534)
/apex/com.android.art/lib64/libart.so (BuildId: b4d22984878b4aa738b6636ddca31dac. FileSize: 10213768. LastModified: 1970-01-01T08:00:00.000+0800. MD5: b08d4f6d5957c3dcbc98b5ceef652613)
ANR
找到 main 线程,查看调用堆栈信息
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Tombstone maker: 'xCrash 3.1.0'
Crash type: 'anr'
Start time: '2024-07-09T23:22:06.430+0800'
Crash time: '2024-07-09T23:25:00.389+0800'
App ID: 'xcrash.sample'
App version: '1.2.3-beta456-patch789'
Rooted: 'Yes'
API level: '31'
OS version: '12'
Kernel version: 'Linux version 5.4.147-qgki-gcd25038ebc13 #1 SMP PREEMPT Tue May 17 14:30:03 CST 2022 f2fs-hash:9f5701dafc (aarch64)'
ABI list: 'arm64-v8a,armeabi-v7a,armeabi'
Manufacturer: 'Xiaomi'
Brand: 'Xiaomi'
Model: 'Xiaomi 14 Ultra'
Build fingerprint: 'Xiaomi/odin/odin:12/SKQ1.211006.001/V13.0.5.1.32.DEV:user/release-keys'
ABI: 'arm64'
pid: 6827 >>> xcrash.sample <<<
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
Cmd line: xcrash.sample
……
"main" prio=5 tid=1 Sleeping
| group="main" sCount=1 ucsCount=0 flags=1 obj=0x7204c878 self=0xb400007607724c00
| sysTid=6827 nice=0 cgrp=background sched=0/0 handle=0x7608d7b4f8
| state=S schedstat=( 20554744043 404750139 34421 ) utm=1386 stm=669 core=0 HZ=100
| stack=0x7ff4caa000-0x7ff4cac000 stackSize=8188KB
| held mutexes=
at java.lang.Thread.sleep(Native method)
- sleeping on <0x07f41d9d> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:451)
- locked <0x07f41d9d> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:356)
at xcrash.sample.MainActivity.testAnrInput_onClick(MainActivity.java:83)
at java.lang.reflect.Method.invoke(Native method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409)
at android.view.View.performClick(View.java:7750)
at android.view.View.performClickInternal(View.java:7727)
at android.view.View.access$3700(View.java:861)
at android.view.View$PerformClick.run(View.java:29133)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:299)
at android.app.ActivityThread.main(ActivityThread.java:8105)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)