android使用ndk stack调试JNI部分的C C++代码
android使用ndk-stack调试JNI部分的C/C++代码
法一:使用ndk-stack输出调用堆栈
我这里的ndk-stack位置为:/home/hwh/Android_Project/Environment/android-ndk-r8e
'ndk-stack' 是一个工具,可以将.so文件的地址映射到相应的编译此.so文件的.h/.cpp的具体地址.
举例:
我这里有2个文件SampleJNIBinder.h和SampleJNIBinder.cpp,编译后生成libSampleJNIBinder.so这个动态库。
调用此.so文件的时候,出现了如下的错误。
F/libc ( 1783): Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1)
I/DEBUG ( 115): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG ( 115): Build fingerprint: 'intel/mfld_dv10/mfld_dv10:4.0.4/IMM76D/release.20130301:eng/dev-keys'
I/DEBUG ( 115): pid: 1783, tid: 1783 >>> com.example.apis <<<
I/DEBUG ( 115): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
I/DEBUG ( 115): eax bfb0eb4e ebx 5e35753c ecx 00000000 edx 5f200019
I/DEBUG ( 115): esi bfb0eb48 edi 08b20db0
I/DEBUG ( 115): xcs 00000073 xds 0000007b xes 0000007b xfs 00000000 xss 0000007b
I/DEBUG ( 115): eip 5e35464a ebp bfb0eb98 esp bfb0eb30 flags 00010206
**I/DEBUG ( 115): #00 eip: 0000264a /data/data/com.example.apis/lib/libSampleJNIBinder.so**
I/DEBUG ( 115): #01 eip: 0007a959 /system/lib/libdvm.so (_Z16dvmCallJNIMethodPKjP6JValuePK6MethodP6Thread+0x1e9)
红色部分的代码是错误的位置,对于eip这个寄存器里面的值:0000264a,我们找不到任何线索。
但是可以通过'adb logcat > foo.txt' 将错误信息导出到当前目录的foo.txt文件中。
然后通过:./ndk-stack -sym /home/<user-name>/workspace_android/HHDemos/obj/local/x86 -dump foo.txt
这个命令可以解析
这样返回的结果为:
huanghao@Mtn:~/Projects/AndroidNDK$ ./ndk-stack -sym /home/huanghao/workspace_android/HHDemos/obj/local/x86 -dump foo.txt
********** Crash dump: **********
Build fingerprint: 'intel/mfld_dv10/mfld_dv10:4.0.4/IMM76D/release.20130301:eng/dev-keys'
pid: 2319, tid: 2319 >>> com.example.apis <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
**Stack frame #00 eip: 0000264a /data/data/com.example.apis/lib/libSampleJNIBinder.so:
Routine Java_com_example_apis_JNI_SampleJNIBinder_JTCGetStringFromJNI
in /home/huanghao/workspace_android/HHDemos/jni/SampleJNIBinder/SampleJNIBinder.cpp:35**
Stack frame #01 eip: 0007a959 /system/lib/libdvm.so (_Z16dvmCallJNIMethodPKjP6JValuePK6MethodP6Thread+0x1e9)
Stack frame #02 eip: 00063ba1 /system/lib/libdvm.so (_Z21dvmCheckCallJNIMethodPKjP6JValuePK6MethodP6Thread+0x41)
Stack frame #03 eip: 00085d2d /system/lib/libdvm.so (_Z22dvmResolveNativeMethodPKjP6JValuePK6MethodP6Thread+0x13d)
Stack frame #04 eip: 00125d34 /system/lib/libdvm.so
Stack frame #05 eip: 0003f466 /system/lib/libdvm.so (_Z11dvmMterpStdP6Thread+0x46)
Stack frame #06 eip: 0003bb06 /system/lib/libdvm.so (_Z12dvmInterpretP6ThreadPK6MethodP6JValue+0xd6)
Stack frame #07 eip: 000a81fa /system/lib/libdvm.so (_Z15dvmInvokeMethodP6ObjectPK6MethodP11ArrayObjectS5_P11ClassObjectb+0x51a)
这里我们很容易看出SampleJNIBinder.cpp:35有错误,那么我们找到那部分的代码:
如下
JNIEXPORT jstring JNICALL
Java_com_example_apis_JNI_SampleJNIBinder_JTCGetStringFromJNI(JNIEnv *env, jobject thiz) {
int * p = NULL;
*p = 100; //这里是35行
int n1 = true;
int n2 = false;
LogPrintf("jni-hh", "n1: %d, n2: %d", n1, n2);
好了,需要总结一下。
使用'ndk-stack'查看backstack信息的步骤如下:
1. 进入Android-NDK的路径
我这里为:/home/<user-name>/Projects/AndroidNDK
$ cd /home/<user-name>/Projects/AndroidNDK
2.导出logcat信息到foo.txt中
$ adb logcat > foo.txt
3.通过GPF的地址和.so文件 获取实际在.cpp文件错误的地址
例子:<user-name>@Mtn:~/Projects/AndroidNDK$ ./ndk-stack -sym <.so文件目录> -dump foo.txt
<user-name>@Mtn:~/Projects/AndroidNDK$ ./ndk-stack -sym /home/<user-name>/workspace_android/HHDemos/obj/local/x86 -dump foo.txt
当然,我们也可以将上面2句合并成一句:
如下:
$ adb logcat | $NDK_HOME/ndk-stack -sym /home/hwh/Android_Project/Code/HHDemos/obj/local/x86
注意:
- 此时我们的操作路径是AndroidSDK目录, foo.txt也在该目录。
2.上面的x86是你平板的CPU决定了,如果你的平板是ARM的CPU那么应该改为:armeabi了。
但是我公司同事的机子adb logcat好像自动就将错误的地址转换为.cpp文件中的地址,可能需要具体配置。
暂时不清楚,我弄好了的话就在这里添加。。。。。;
法二:使用i686-linux-android-addr2line输出调试信息
下面是该文件的路径
/home/hwh/Android_Project/Environment/android-ndk-r8e/toolchains/x86-4.6/prebuilt/linux-x86/bin
其中android-ndk-r8e是我在ndk官网下载的android-ndk-r8e-linux-x86.tar.bz2解压缩后的文件夹。
用法:
$ ./i686-linux-android-addr2line -f -e /home/hwh/Android_Project/Code/HHDemos/obj/local/x86/libSampleJNIBinder.so 0000415C
总结一下就是:
$ ./i686-linux-android-addr2line -f -e <.so文件的pathName> <出错的地址>
输出结果为:
hwh@Mountain:~/Android_Project/Environment/android-ndk-r8e/toolchains/x86-4.6/prebuilt/linux-x86/bin$ ./i686-linux-android-addr2line -f -e
/home/hwh/Android_Project/Code/HHDemos/obj/local/x86/libSampleJNIBinder.so 0000415C
Java_com_example_apis_JNI_SampleJNIBinder_JTCGetStringFromJNI
/home/hwh/Android_Project/Code/HHDemos/jni/SampleJNIBinder/SampleJNIBinder.cpp:35
-
出错的函数
-
出错地点在该.cpp文件的行数
Android ndk-stack tool
Introduction:
-------------
This document describes the 'ndk-stack' tool that is distributed withthe Android NDK, since release r6.
Overview:
---------
'ndk-stack' is a simple tool that allows you to filter stack traces as theyappear in the output of 'adb logcat' and replace any address inside a shared
library with the corresponding : values.
In a nutshell, it will translate something like:
I/DEBUG ( 31): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG ( 31): Build fingerprint: 'generic/google_sdk/generic/:2.2/FRF91/43546:eng/test-keys'
I/DEBUG ( 31): pid: 351, tid: 351 %gt;%gt;%gt; /data/local/ndk-tests/crasher <<<
I/DEBUG ( 31): signal 11 (SIGSEGV), fault addr 0d9f00d8
I/DEBUG ( 31): r0 0000af88 r1 0000a008 r2 baadf00d r3 0d9f00d8
I/DEBUG ( 31): r4 00000004 r5 0000a008 r6 0000af88 r7 00013c44
I/DEBUG ( 31): r8 00000000 r9 00000000 10 00000000 fp 00000000
I/DEBUG ( 31): ip 0000959c sp be956cc8 lr 00008403 pc 0000841e cpsr 60000030
I/DEBUG ( 31): #00 pc 0000841e /data/local/ndk-tests/crasher
I/DEBUG ( 31): #01 pc 000083fe /data/local/ndk-tests/crasher
I/DEBUG ( 31): #02 pc 000083f6 /data/local/ndk-tests/crasher
I/DEBUG ( 31): #03 pc 000191ac /system/lib/libc.so
I/DEBUG ( 31): #04 pc 000083ea /data/local/ndk-tests/crasher
I/DEBUG ( 31): #05 pc 00008458 /data/local/ndk-tests/crasher
I/DEBUG ( 31): #06 pc 0000d362 /system/lib/libc.so
I/DEBUG ( 31):
Into the more readable output:
********** Crash dump: **********
Build fingerprint: 'generic/google_sdk/generic/:2.2/FRF91/43546:eng/test-keys'
pid: 351, tid: 351 >>> /data/local/ndk-tests/crasher <<<
signal 11 (SIGSEGV), fault addr 0d9f00d8
Stack frame #00 pc 0000841e /data/local/ndk-tests/crasher : Routine zoo in /tmp/foo/crasher/jni/zoo.c:13
Stack frame #01 pc 000083fe /data/local/ndk-tests/crasher : Routine bar in /tmp/foo/crasher/jni/bar.c:5
Stack frame #02 pc 000083f6 /data/local/ndk-tests/crasher : Routine my_comparison in /tmp/foo/crasher/jni/foo.c:9
Stack frame #03 pc 000191ac /system/lib/libc.so
Stack frame #04 pc 000083ea /data/local/ndk-tests/crasher : Routine foo in /tmp/foo/crasher/jni/foo.c:14
Stack frame #05 pc 00008458 /data/local/ndk-tests/crasher : Routine main in /tmp/foo/crasher/jni/main.c:19
Stack frame #06 pc 0000d362 /system/lib/libc.so
Usage:
------
To do this, you will first need a directory containing symbolic versions of yourapplication's shared libraries. If you use the NDK build system (i.e. ndk-build),
then these are always located under $PROJECT_PATH/obj/local/<ab>, where<ab> stands for your device's ABI (i.e. 'armeabi' by default).
You can feed the logcat text either as direct input to the program, e.g.:
adb logcat | $NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi
Or you can use the -dump option to specify the logcat as an input file, e.g.:
adb logcat > /tmp/foo.txt
$NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi -dump foo.txt
TODO:
-----
A future version of 'ndk-stack' will try to launch 'adb logcat' and select thelibrary path automatically. For now, you'll have to do these steps manually.
As of now, ndk-stack doesn't handle libraries that don't have debug informationin them. It may be useful to try to detect the nearest function entry point to
a given PC address (e.g. as in the libc.so example above).