一、前言
在NDK开发中经常会出现应用Crash的情况,而JNI层的报错信息,不像Java层报错信息那样可以直接在日志中看到错误的行数,JNI层中出现的错误直接看根本定位不到错误的位置。通常来说,JNI报的基本都是堆栈信息,需要NDK的一些工具进行地址转换,转换后即可看到错误的位置。这些地址转换的工具有addr2line、ndk-stack等,我比较喜欢addr2line,平时也用这个工具进行调试。
二、分析
错误信息如下:
A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
A/DEBUG: Build fingerprint: 'xiaomi/mido/mido:7.0/NRD90M/V10.1.1.0.NCFCNFI:user/release-keys'
A/DEBUG: Revision: '0'
A/DEBUG: ABI: 'arm'
A/DEBUG: pid: 29290, tid: 29290, name: e.hasee.ndkdemo >>> com.example.hasee.ndkdemo <<<
A/DEBUG: signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
A/DEBUG: r0 00000000 r1 0000726a r2 00000006 r3 00000008
A/DEBUG: r4 f6cbc590 r5 00000006 r6 f6cbc538 r7 0000010c
A/DEBUG: r8 12e45dc0 r9 f3f88000 sl ff90e8ec fp f3f88000
A/DEBUG: ip 00000058 sp ff90e5f8 lr f5c3a2c7 pc f5c3cb48 cpsr 200f0010
A/DEBUG: backtrace://堆栈信息&#xff0c;只需要关注这部分就好
A/DEBUG: #00 pc 00049b48 /system/lib/libc.so (tgkill&#43;12)
A/DEBUG: #01 pc 000472c3 /system/lib/libc.so (pthread_kill&#43;34)
A/DEBUG: #02 pc 0001d565 /system/lib/libc.so (raise&#43;10)
A/DEBUG: #03 pc 000190b1 /system/lib/libc.so (__libc_android_abort&#43;34)
A/DEBUG: #04 pc 00017114 /system/lib/libc.so (abort&#43;4)
A/DEBUG: #05 pc 0009063f /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN7GSMutex4LockEv&#43;90)
A/DEBUG: #06 pc 00090837 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN11GSAutoMutexC1ER7GSMutex&#43;10)
A/DEBUG: #07 pc 0004b4d7 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN12GSGB28181SDK9CSipStack18GetHistoryRegisterEv&#43;34)
A/DEBUG: #08 pc 0004b60b /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN12GSGB28181SDK9CSipStack13SendRegistMsgERK16_Stru_Regist_MsgjPc&#43;58)
A/DEBUG: #09 pc 000033a3 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libnative-lib.so (Java_com_example_hasee_ndkdemo_NDKUtil_agentRegister&#43;686)
1、先提取backtrace部分
上面的这些报错信息里面&#xff0c;backtrace部分显示的是错误的堆栈信息&#xff0c;所以我们只需要关注 backtrace部分就可以了。
提取后如下所示&#xff1a;
A/DEBUG: backtrace:
A/DEBUG: #00 pc 00049b48 /system/lib/libc.so (tgkill&#43;12)
A/DEBUG: #01 pc 000472c3 /system/lib/libc.so (pthread_kill&#43;34)
A/DEBUG: #02 pc 0001d565 /system/lib/libc.so (raise&#43;10)
A/DEBUG: #03 pc 000190b1 /system/lib/libc.so (__libc_android_abort&#43;34)
A/DEBUG: #04 pc 00017114 /system/lib/libc.so (abort&#43;4)
A/DEBUG: #05 pc 0009063f /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN7GSMutex4LockEv&#43;90)
A/DEBUG: #06 pc 00090837 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN11GSAutoMutexC1ER7GSMutex&#43;10)
A/DEBUG: #07 pc 0004b4d7 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN12GSGB28181SDK9CSipStack18GetHistoryRegisterEv&#43;34)
A/DEBUG: #08 pc 0004b60b /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN12GSGB28181SDK9CSipStack13SendRegistMsgERK16_Stru_Regist_MsgjPc&#43;58)
A/DEBUG: #09 pc 000033a3 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libnative-lib.so (Java_com_example_hasee_ndkdemo_NDKUtil_agentRegister&#43;686)
2、提取对应so库的信息
可以看到每一行最后面都跟着“xxx.so”信息
“#00” 至 “#04” 部分后面的都是 “/system/lib/libc.so” &#xff0c;这些都是系统的库&#xff0c;这个我们不需要管&#xff1b;
“#05” 至 “#09” 部分后面的so库都是我自己的&#xff0c;这些才是我们需要定位的部分。
“/data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so” // 引入的第三方so库
“/data/app/com.example.hasee.ndkdemo-2/lib/arm/libnative-lib.so ”// 编译生成的so库
将“#05” 至 “#09” 部分提取出来&#xff0c;如下所示&#xff1a;
A/DEBUG: #05 pc 0009063f /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN7GSMutex4LockEv&#43;90)
A/DEBUG: #06 pc 00090837 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN11GSAutoMutexC1ER7GSMutex&#43;10)
A/DEBUG: #07 pc 0004b4d7 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN12GSGB28181SDK9CSipStack18GetHistoryRegisterEv&#43;34)
A/DEBUG: #08 pc 0004b60b /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so (_ZN12GSGB28181SDK9CSipStack13SendRegistMsgERK16_Stru_Regist_MsgjPc&#43;58)
A/DEBUG: #09 pc 000033a3 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libnative-lib.so (Java_com_example_hasee_ndkdemo_NDKUtil_agentRegister&#43;686)
3、提取错误地址
剩下到的这几行中&#xff0c;重点看 “pc” 后面的十六进制数&#xff0c;这些是对应so库中的具体错误信息地址&#xff0c;这些地址才是我们最终需要转换的对象。为了更直观的展示&#xff0c;我把前面的“A/DEBUG” 和 “pc” 两个标签去掉&#xff0c;剩下行号还有地址以及so库部分提取出来。
提取如下所示&#xff1a;
#050009063f /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so
#0600090837 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so
#070004b4d7 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so
#080004b60b /data/app/com.example.hasee.ndkdemo-2/lib/arm/libthird.so
#09000033a3 /data/app/com.example.hasee.ndkdemo-2/lib/arm/libnative-lib.so
上面就是最终我们需要关注的部分&#xff0c;有了十六进制地址值以及后面的so库&#xff0c;下面我们就可以使用addr2line进行地址转换了。
三、使用addr2line对地址进行转换
1、addr2line工具的路径放在
" ${NDK} / toolchains / ${ABI} / prebuilt / windows-x86_64 / bin / "下
${NDK} // 你的NDK解压包路径
${ABI} // 你的调试设备的CPU架构,通常来说实体机一般都是对应 arm-linux-androideabi
2、Terminal中转换地址的命令
arm-linux-androideabi-addr2line -C -f -e ${SOPATH} ${Address}
-C -f //打印错误行数所在的函数名称
-e //打印错误地址的对应路径及行数
${SOPATH} //so库路径
${Address}//需要转换的堆栈错误信息地址&#xff0c;可以添加多个&#xff0c;但是中间要用空格隔开
四、例子
对上面最终提取的错误信息(“#05 – #09”)中的堆栈地址进行转换
1、首先找到自己的so库路径
对于“#05” 至 “#08” 部分的地址&#xff0c;我对应的so库路径为
F:/Android/workspace/NDKDemo/app/src/main/jniLibs/armeabi-v7a/libthird.so
对于“#09”行中的地址&#xff0c;我对应的so库为&#xff1a;
F:/Android/workspace/NDKDemo/app/build/intermediates/cmake/debug/obj/armeabi-v7a/libnative-lib.so
2、在Terminal中输入命令
对于“#05” 至 “#08” 部分的地址&#xff0c;我输入的命令为&#xff1a;
arm-linux-androideabi-addr2line -C -f -e F:/Android/workspace/NDKDemo/app/src/main/jniLibs/armeabi-v7a/libthird.so 0009063f 00090837 0004b4d7 0004b60b
对于“#09”行中的地址&#xff0c;我输入的命令为&#xff1a;
arm-linux-androideabi-addr2line -C -f -e F:/Android/workspace/NDKDemo/app/build/intermediates/cmake/debug/obj/armeabi-v7a/libnative-lib.so 000033a3
注意&#xff1a;
1)我这里已经将addr2line配置到环境变量中&#xff0c;所以可以直接使用arm-linux-androideabi-addr2line命令。如果没有配置的话&#xff0c;记得要加上自己的完整路径,否则会出现Terminal报错&#xff1a;
“&#39;arm-linux-androideabi-addrline2&#39; 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。”
2)要看清楚后面的so库&#xff0c;我这里第三方的so库和我自己的编译打包的so库都有报错地址信息&#xff0c;所以转换地址的时候&#xff0c;要注意地址后面对应的是哪个so库&#xff0c;不然的话会转换失败&#xff0c;转换的地址显示为 " ? ? 0 “(一个so库的错误地址信息跑到另外一个so库去查&#xff0c;当然查不到&#xff0c;除非两个错误位置在两个so库中的堆栈地址相同&#xff0c;查不到的时候&#xff0c;就会输出” ? ? 0 ")这个问题也困扰了我一个上午&#xff0c;要特别注意。
3)libnative-lib.so是我自己打包编译的库&#xff0c;自己打包编译的so库可以在Project视图的
" ./app/build/intermediates/cmake/debug/obj/&{ABI}/ " 路径下找到
3、效果
第三方libthird.so库的显示信息
自己编译的libnatve-lib.so库的显示信息
点击一下转换后的地址就可以跳转到错误的位置了。
如有错误&#xff0c;欢迎指正&#xff0c;虚心学习&#xff01;