Android-JNI開(kāi)發(fā)系列《四》Native-Crash定位

人間觀察

你有多久沒(méi)有十點(diǎn)之前睡過(guò)覺(jué)了歌焦。

假期ing~~~

在Android中進(jìn)行JNI的開(kāi)發(fā)的當(dāng)然也會(huì)發(fā)生crash虎忌,而發(fā)生crash后比較難定位佑惠。
因?yàn)閖ni是使用C/C++來(lái)進(jìn)行開(kāi)發(fā)的嘹履,熟悉C/C++語(yǔ)言的同學(xué)都知道宙址,指針和內(nèi)存申請(qǐng)的使用時(shí)需要自己申請(qǐng)和釋放的轴脐,它不像java那樣有jvm有垃圾回收管理機(jī)制gc,稍微管理不當(dāng)就會(huì)導(dǎo)致問(wèn)題抡砂。比如:內(nèi)存地址訪問(wèn)錯(cuò)誤大咱、堆棧溢出、指針使用錯(cuò)誤等等注益,最后都會(huì)導(dǎo)致程序崩潰碴巾。

幸好Android NDK提供了一些工具來(lái)幫助精確定位到出問(wèn)題的代碼。
我們模擬一下crash丑搔,模擬一個(gè)指針未初始化訪問(wèn)的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沒(méi)有初始化
崩潰的日志如下:


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發(fā)生時(shí)函數(shù)的調(diào)用關(guān)系及其它地址信息等啤月。
在backtrace中煮仇,從#00到#04 共五行信息代表是crash時(shí)函數(shù)調(diào)用關(guān)系,從下往上倒著看顽冶,#04行的方法調(diào)用了#03行的方法欺抗;#03行的方法調(diào)用了#02行的方法;向上類推强重,最后就是#01行的方法調(diào)用了#00行的方法绞呈。而最終出現(xiàn)的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

從上面可以看得出來(lái)NDK針對(duì)不同的CPU架構(gòu)實(shí)現(xiàn)了多套相同的工具,所以你要根據(jù)目標(biāo)機(jī)器的CPU架構(gòu)來(lái)選擇倘要。如果不知道目標(biāo)機(jī)器的CPU架構(gòu)圾亏,把手機(jī)連上電腦,用adb shell cat /proc/cpuinfo可以查看手機(jī)的CPU信息封拧。我的目標(biāo)機(jī)器是Processor : AArch64 Processor rev 1 (aarch64)

addr2line的使用方式是

addr2line -e 000000000000e0ec

-e 指定帶符號(hào)表的so文件路徑志鹃,也需要對(duì)應(yīng)目標(biāo)機(jī)器的CPU架構(gòu)的so文件的絕對(duì)路徑

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

結(jié)果如下

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

回看下我們.cpp的源碼,第9行由于int* p指針沒(méi)有初始化導(dǎo)致了fault addr泽西。

使用ndk提供的命令ndk-stack

這個(gè)命令可以簡(jiǎn)化上面的步驟曹铃。

使用adb獲取logcat的日志,并通過(guò)管道輸出給ndk-stack進(jìn)行分析捧杉。

ndk-stack -sym使用方式是

ndk-stack -sym XXXX

-sym 指定帶符號(hào)表的so文件路徑陕见,也需要對(duì)應(yīng)目標(biāo)機(jī)器的CPU架構(gòu)的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

這時(shí)候模擬一下crash秘血,檢測(cè)到crash,ndk-stack自動(dòng)解析了评甜,結(jié)果和上面的一樣第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)

這個(gè)命令也支持文件的解析(使用-dump參數(shù))灰粮,這個(gè)好,因?yàn)橛行ヽrash不是必須的忍坷。

ndk-stack -dump 使用方式是

ndk-stack -sym XXXX -dump XXXX

-sym 指定帶符號(hào)表的so文件路徑粘舟,也需要對(duì)應(yīng)目標(biāo)機(jī)器的CPU架構(gòu)的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)

總結(jié):兩種方法來(lái)定位crash。 addr2line -e xxxx 指定匯編指令地址承匣。 adb logcat | ndk-stack -sym 指定對(duì)應(yīng)目標(biāo)機(jī)器的CPU架構(gòu)的so文件的絕對(duì)路徑蓖乘; ndk-stack -dump 對(duì)應(yīng)目標(biāo)機(jī)器的CPU架構(gòu)的so文件的根目錄 -dump crash的堆棧信息的文件。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末韧骗,一起剝皮案震驚了整個(gè)濱河市嘉抒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌袍暴,老刑警劉巖些侍,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異政模,居然都是意外死亡岗宣,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)淋样,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)耗式,“玉大人,你說(shuō)我怎么就攤上這事趁猴】龋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵儡司,是天一觀的道長(zhǎng)娱挨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)捕犬,這世上最難降的妖魔是什么跷坝? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮碉碉,結(jié)果婚禮上柴钻,老公的妹妹穿的比我還像新娘。我一直安慰自己垢粮,他們只是感情好顿颅,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般粱腻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上斩跌,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天绍些,我揣著相機(jī)與錄音,去河邊找鬼耀鸦。 笑死柬批,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的袖订。 我是一名探鬼主播氮帐,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼洛姑!你這毒婦竟也來(lái)了上沐?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤楞艾,失蹤者是張志新(化名)和其女友劉穎参咙,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體硫眯,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蕴侧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了两入。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片净宵。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖裹纳,靈堂內(nèi)的尸體忽然破棺而出择葡,到底是詐尸還是另有隱情,我是刑警寧澤痊夭,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布刁岸,位于F島的核電站,受9級(jí)特大地震影響她我,放射性物質(zhì)發(fā)生泄漏虹曙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一番舆、第九天 我趴在偏房一處隱蔽的房頂上張望酝碳。 院中可真熱鬧,春花似錦恨狈、人聲如沸疏哗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)返奉。三九已至贝搁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芽偏,已是汗流浹背雷逆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留污尉,地道東北人膀哲。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像被碗,于是被迫代替她去往敵國(guó)和親某宪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359