最近一直研究JNI和NDK方面与Java的对接,今天遇到一个这样的错,就是打开App,然后通过JNI调用C++代码,然后就闪退, 日志如下所示:
09-05 10:07:59.626 10962-10962/com.daniulive.smartpublisher A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0xc in tid 10962 (.smartpublisher)
[ 09-05 10:07:59.626 458: 458 W/ ]
debuggerd: handling request: pid=10962 uid=10062 gid=10062 tid=10962
09-05 10:07:59.693 10984-10984/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
09-05 10:07:59.693 10984-10984/? A/DEBUG: Build fingerprint: 'Hisense/Z1/HS8953QC:7.1.1/NMF26F/L1370.6.01.01:user/release-keys'
09-05 10:07:59.694 10984-10984/? A/DEBUG: Revision: '0'
09-05 10:07:59.694 10984-10984/? A/DEBUG: ABI: 'arm64'
09-05 10:07:59.694 10984-10984/? A/DEBUG: pid: 10962, tid: 10962, name: .smartpublisher >>> com.daniulive.smartpublisher <<<
09-05 10:07:59.694 10984-10984/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc
09-05 10:07:59.694 10984-10984/? A/DEBUG: x0 0000000000000000 x1 0000000000000001 x2 0000000000000004 x3 0000000000000003
09-05 10:07:59.694 10984-10984/? A/DEBUG: x4 656d616e5f726669 x5 0000808080808080 x6 0000007fa116e000 x7 0000000000000000
09-05 10:07:59.694 10984-10984/? A/DEBUG: x8 0000000000000000 x9 0000000000000000 x10 0000000000000000 x11 000000000000001e
09-05 10:07:59.695 10984-10984/? A/DEBUG: x12 0000000000000018 x13 0000000000000000 x14 0000000000000000 x15 00253207056c0de8
09-05 10:07:59.695 10984-10984/? A/DEBUG: x16 0000007f94fea328 x17 0000007f94f1bc84 x18 00000000ffffffff x19 0000007f9d296a00
09-05 10:07:59.695 10984-10984/? A/DEBUG: x20 0000007f9ccaea50 x21 0000007f9d296a00 x22 0000007fd09aaaac x23 0000007f9bb3b83a
09-05 10:07:59.695 10984-10984/? A/DEBUG: x24 000000000000000c x25 a5dd763335e33087 x26 0000007f9d296a98 x27 0000007f9d296a00
09-05 10:07:59.695 10984-10984/? A/DEBUG: x28 0000000000000012 x29 0000007fd09aa2e0 x30 0000007f94f1b48c
09-05 10:07:59.695 10984-10984/? A/DEBUG: sp 0000007fd09aa2d0 pc 0000007f94f1bc90 pstate 0000000020000000
09-05 10:07:59.700 10984-10984/? A/DEBUG: backtrace:
09-05 10:07:59.701 10984-10984/? A/DEBUG: #00 pc 000000000001bc90 /data/app/com.daniulive.smartpublisher-1/lib/arm64/libnative-lib.so (_ZN10CDeviceLib12LaunchDeviceEv+12)
09-05 10:07:59.701 10984-10984/? A/DEBUG: #01 pc 000000000001b488 /data/app/com.daniulive.smartpublisher-1/lib/arm64/libnative-lib.so (_Z12LaunchDevicem+80)
09-05 10:07:59.701 10984-10984/? A/DEBUG: #02 pc 000000000001ae68 /data/app/com.daniulive.smartpublisher-1/lib/arm64/libnative-lib.so (startStream+804)
09-05 10:07:59.701 10984-10984/? A/DEBUG: #03 pc 000000000001a3c4 /data/app/com.daniulive.smartpublisher-1/lib/arm64/libnative-lib.so (Java_com_daniulive_smartpublisher_jbjniapis_jnistartStream+220)
09-05 10:07:59.701 10984-10984/? A/DEBUG: #04 pc 000000000024831c /data/app/com.daniulive.smartpublisher-1/oat/arm64/base.odex (offset 0x242000)
这个日志重要的开始标志是在这句下面:signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc
直接找最有用的backtrace信息。 backtrace描述了crash发生时函数的调用关系及其它地址信息等,可谓是真实地还原了crash的现场。接下来就看下现场的具体情况是怎么样的。
backtrace中,从#00到#04 共五行信息代表是crash时函数调用关系,从下往上倒着看,#04行的方法调用了#03行的方法;#03行的方法调用了#02行的方法;向上类推,最后就是#01行的方法调用了#00行的方法。而最终出现的crash就是在#00行中。
#** pc 后面跟着就是异常时PC寄存器值,后面需要用到。
现在,我们用一个工具:addr2line,这个工具可以查看到出错的文件名、哪行代码出错了。这个工具已经包含在NDK里面了,目录如下:你的NDK路径\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin,如下图所示:
接着需要拿到当前调试出错时的那个so库,路径在:你的项目路径app\build\intermediates\cmake\debug\obj\arm64-v8a。(因为日志有提示说arm64,所以选择这个arm64-v8a),另外,一定是要在obj目录下的,因为每调试一次,生成的So信息都不一样,如果对不上的话就查不到信息了。。。
将so库复制到一个新的文件里(路径随意,不过最好不好带中文),然后再新建一个Bat脚本,脚本信息如下:
@echo off
rem current direction
set cur_dir=%cd%
rem addr2line tool path
set add2line_path=E:\android-ndk-r16b-windows-x86_64\android-ndk-r16b\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin\aarch64-linux-android-addr2line.exe
rem debug file
set /p debug_file=请输入当前目录下debug文件名:
rem debug_file_path
set debug_file_path=%cur_dir%\%debug_file%
rem debug address
set /p debug_addr=请输入异常时PC寄存器值:
echo ----------------------- addr2line ------------------------
echo debug文件路径: %debug_file_path% PC=%debug_addr%
if exist %debug_file_path% (
%add2line_path% -e %debug_file_path% -f %debug_addr%
) else (
echo debug file is no exist.
)
echo ---------------------------------------------------------
pause
上面代码的set add2line_path=后面跟的就是那个addr2line工具路径。
所以此新建的文件夹里有so库和上面那个脚本:
双击此脚本,然后输入库名和寄存器地址,然后就可以查到出错的行号了,效果如下所示:
注:上面演示的是最最最幸运的效果,但实际中,第三方的so库一般都是不提供源码,又或者已加密了,所以此时得出的是行号为??:?或??:0
如果遇到addr2line得到??:?或??:0的情况,原因就是编译得到的so文件没有附加上符号表(symbolic)信息。
贴上两个类似??:?或??:0情况的链接吧:https://blog.csdn.net/u010477502/article/details/50626263
http://jileniao.net/post-201.html