崩溃监控方案 xCrash

围巾🧣 2024年07月09日 162次浏览
xcrash_logo

xCrash

xCrash 能为安卓 app 提供捕获 java 崩溃,native 崩溃和 ANR 的能力。不需要 root 权限或任何系统权限。

intro

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 权限或任何系统权限。

概览图

架构

architecture

捕获 native 崩溃

capture_native_crash

捕获 ANR

capture_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)