Android-JNI开发系列《四》Native-Crash定位

人间观察

你有多久没有十点之前睡过觉了。

假期ing~~~

在Android中进行JNI的开发的当然也会发生crash,而发生crash后比较难定位。
因为jni是使用C/C++来进行开发的,熟悉C/C++语言的同学都知道,指针和内存申请的使用时需要自己申请和释放的,它不像java那样有jvm有垃圾回收管理机制gc,稍微管理不当就会导致问题。比如:内存地址访问错误、堆栈溢出、指针使用错误等等,最后都会导致程序崩溃。

幸好Android NDK提供了一些工具来帮助精确定位到出问题的代码。
我们模拟一下crash,模拟一个指针未初始化访问的case。

#include "common.h"
#include <malloc.h>

extern "C"
JNIEXPORT void JNICALL
Java_com_bj_gxz_jniapp_crash_JNICrash_crash(JNIEnv *env, jobject thiz) {
//    int *p = static_cast<int *>(malloc(sizeof(int)));
    int *p;
    *p = 1;
    LOG_D("*P=%d", *p);
    free(p);
}

*p没有初始化
崩溃的日志如下:


2020-09-28 14:36:49.737 19047-19047/com.bj.gxz.jniapp A/libc: Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x6fcabb4d34 in tid 19047 (m.bj.gxz.jniapp), pid 19047 (m.bj.gxz.jniapp)
2020-09-28 14:36:49.856 19167-19167/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2020-09-28 14:36:49.856 19167-19167/? A/DEBUG: Build fingerprint: 'HONOR/STF-AL00/HWSTF:9/HUAWEISTF-AL00/9.1.0.219C00:user/release-keys'
2020-09-28 14:36:49.856 19167-19167/? A/DEBUG: Revision: '0'
2020-09-28 14:36:49.856 19167-19167/? A/DEBUG: ABI: 'arm64'
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG: Happend: 'Mon Sep 28 14:36:49 2020
    '
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG: SYSVMTYPE: Art
    APPVMTYPE: Art
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG: pid: 19047, tid: 19047, name: m.bj.gxz.jniapp  >>> com.bj.gxz.jniapp <<<
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG: signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x6fcabb4d34
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x0  0000006fcb0c5460  x1  0000007fdd935134  x2  0000006fac4b2b84  x3  0000000070cfc600
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x4  0000000000000000  x5  0000000000000000  x6  0000000000000000  x7  0000000000000000
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x8  0000000000000001  x9  0000000000000003  x10 0000006fac4b2b80  x11 0000006fcabb4d34
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x12 0000007051586630  x13 0f2fce861ea823e3  x14 000000705118b000  x15 ffffffffffffffff
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x16 0000006fac4990bc  x17 00000070507c5d48  x18 0000000000000001  x19 0000006fcb015c00
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x20 0000000000000000  x21 0000006fcb015c00  x22 0000007fdd935400  x23 0000006faeae68f1
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x24 0000000000000004  x25 00000070518f65e0  x26 0000006fcb015ca0  x27 0000000000000001
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x28 0000007fdd935130  x29 0000007fdd935100
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     sp  0000007fdd9350e0  lr  0000006fcaeb6fe4  pc  0000006fac4990ec
2020-09-28 14:36:49.891 744-2617/? E/LOGSERVER_UTILS: [Erecovery]readEvent: eRecEventManager readEvent 0
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG: backtrace:
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #00 pc 000000000000e0ec  /data/app/com.bj.gxz.jniapp-wTViTmvyh8_za4_TB4rqtQ==/lib/arm64/libnative-lib.so (Java_com_bj_gxz_jniapp_crash_JNICrash_crash+48)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #01 pc 0000000000588fe0  /system/lib64/libart.so (art_quick_generic_jni_trampoline+144)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #02 pc 000000000057ff88  /system/lib64/libart.so (art_quick_invoke_stub+584)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #03 pc 00000000000d8608  /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #04 pc 000000000028cec8  /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #05 pc 0000000000286ed0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+968)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #06 pc 000000000054747c  /system/lib64/libart.so (MterpInvokeVirtual+588)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #07 pc 0000000000572514  /system/lib64/libart.so (ExecuteMterpImpl+14228)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #08 pc 00000000001678da  /dev/ashmem/dalvik-classes.dex extracted in memory from /data/app/com.bj.gxz.jniapp-wTViTmvyh8_za4_TB4rqtQ==/base.apk (deleted) (com.bj.gxz.jniapp.MainActivity.onJniCrash+10)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #09 pc 0000000000260bd4  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.2850692617+488)
2

//  ...省略其它日志不重要的日志

crash日志里 最有用的backtrace信息。 backtrace描述了crash发生时函数的调用关系及其它地址信息等。
在backtrace中,从#00到#04 共五行信息代表是crash时函数调用关系,从下往上倒着看,#04行的方法调用了#03行的方法;#03行的方法调用了#02行的方法;向上类推,最后就是#01行的方法调用了#00行的方法。而最终出现的crash就是在#00行中。

如何定位解决呢?

使用ndk提供的命令addr2line

addr2line命令在ndk版本的toolchains目录下,我的是在

/Users/guxiuzhong/Library/Android/sdk/ndk/21.0.6113669/toolchains
B000000073160:toolchains guxiuzhong$ ls
aarch64-linux-android-4.9   renderscript
arm-linux-androideabi-4.9   x86-4.9
llvm                x86_64-4.9

从上面可以看得出来NDK针对不同的CPU架构实现了多套相同的工具,所以你要根据目标机器的CPU架构来选择。如果不知道目标机器的CPU架构,把手机连上电脑,用adb shell cat /proc/cpuinfo可以查看手机的CPU信息。我的目标机器是Processor : AArch64 Processor rev 1 (aarch64)

addr2line的使用方式是

addr2line -e 000000000000e0ec

-e 指定带符号表的so文件路径,也需要对应目标机器的CPU架构的so文件的绝对路径

000000000000e0ec 崩溃的汇编指令地址

addr2line一下

/Users/guxiuzhong/Library/Android/sdk/ndk/21.0.6113669/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line  -e /Users/guxiuzhong/Desktop/JNIAPP/app/build/intermediates/cmake/debug/obj/arm64-v8a/libnative-lib.so  000000000000e0ec

结果如下

/Users/guxiuzhong/Desktop/JNIAPP/app/src/main/cpp/crash.cpp:9

回看下我们.cpp的源码,第9行由于int* p指针没有初始化导致了fault addr。

使用ndk提供的命令ndk-stack

这个命令可以简化上面的步骤。

使用adb获取logcat的日志,并通过管道输出给ndk-stack进行分析。

ndk-stack -sym使用方式是

ndk-stack -sym XXXX

-sym 指定带符号表的so文件路径,也需要对应目标机器的CPU架构的so文件的根目录即可

ndk-stack一下

adb logcat | /Users/guxiuzhong/Library/Android/sdk/ndk/21.0.6113669/ndk-stack -sym /Users/guxiuzhong/Desktop/JNIAPP/app/build/intermediates/cmake/debug/obj/arm64-v8a

这时候模拟一下crash,检测到crash,ndk-stack自动解析了,结果和上面的一样第9行:

********** Crash dump: **********
Build fingerprint: 'HONOR/STF-AL00/HWSTF:9/HUAWEISTF-AL00/9.1.0.219C00:user/release-keys'
#00 0x000000000000e0ec /data/app/com.bj.gxz.jniapp-wTViTmvyh8_za4_TB4rqtQ==/lib/arm64/libnative-lib.so (Java_com_bj_gxz_jniapp_crash_JNICrash_crash+48)
                                                                                                        Java_com_bj_gxz_jniapp_crash_JNICrash_crash
                                                                                                        /Users/guxiuzhong/Desktop/JNIAPP/app/src/main/cpp/crash.cpp:9:8
#01 0x0000000000588fe0 /system/lib64/libart.so (art_quick_generic_jni_trampoline+144)
#02 0x000000000057ff88 /system/lib64/libart.so (art_quick_invoke_stub+584)

这个命令也支持文件的解析(使用-dump参数),这个好,因为有些crash不是必须的。

ndk-stack -dump 使用方式是

ndk-stack -sym XXXX -dump XXXX

-sym 指定带符号表的so文件路径,也需要对应目标机器的CPU架构的so文件的根目录即可
-dump crash的堆栈信息的文件

B000000073160:bin guxiuzhong$ /Users/guxiuzhong/Library/Android/sdk/ndk/21.0.6113669/ndk-stack -sym /Users/guxiuzhong/Desktop/JNIAPP/app/build/intermediates/cmake/debug/obj/arm64-v8a -dump  ~/Desktop/crash.log 
********** Crash dump: **********
Build fingerprint: 'HONOR/STF-AL00/HWSTF:9/HUAWEISTF-AL00/9.1.0.219C00:user/release-keys'
#00 0x000000000000e0ec /data/app/com.bj.gxz.jniapp-wTViTmvyh8_za4_TB4rqtQ==/lib/arm64/libnative-lib.so (Java_com_bj_gxz_jniapp_crash_JNICrash_crash+48)
                                                                                                        Java_com_bj_gxz_jniapp_crash_JNICrash_crash
                                                                                                        /Users/guxiuzhong/Desktop/JNIAPP/app/src/main/cpp/crash.cpp:9:8
#01 0x0000000000588fe0 /system/lib64/libart.so (art_quick_generic_jni_trampoline+144)
#02 0x000000000057ff88 /system/lib64/libart.so (art_quick_invoke_stub+584)
#03 0x00000000000d8608 /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
#04 0x000000000028cec8 /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
#05 0x0000000000286ed0 /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+968)
#06 0x000000000054747c /system/lib64/libart.so (MterpInvokeVirtual+588)
#07 0x0000000000572514 /system/lib64/libart.so (ExecuteMterpImpl+14228)

总结:两种方法来定位crash。 addr2line -e xxxx 指定汇编指令地址。 adb logcat | ndk-stack -sym 指定对应目标机器的CPU架构的so文件的绝对路径; ndk-stack -dump 对应目标机器的CPU架构的so文件的根目录 -dump crash的堆栈信息的文件。

源代码:https://github.com/ta893115871/JNIAPP

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354