最近为了性能需求,开始搞JNI,白手起搞真心不容易。中间差点崩溃了好几次,最终总算得到一点心得。
JNI对性能的提升没有我预想中的大,对于for循环的速度提升大概在1倍左右,所以如果数量级不大的话,性能提升不会很明显
JNI编完之后,不能调试,是不是很蛋疼,不像android Java可以看出错信息。JNI crash之后,界面上表示为没有任何反应,过段时间直接退出应用,没有提示、也没有XXX已停止运行。第一次遇到真是无从下手,有没有!!!,经过对Java层的log研究发现,原来JNI crash后,cpu就直接死在那里(相当于assert,程序停止运行)。果断时间后JAVA层发现程序已挂之后(进程僵死),就强制杀死该进程。于是程序直接退出而无提示。(以上分析纯属个人分析,如有不对,敬请指正)
jni crash之后在logcat中会打出一段dump,这段dump没有tag,没有所属进程,所以如果ddms有filter会忽略这段dump而只看到
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
signal后面一般是11,目测应该是crash信号,code表示不同错误码,有linux基础应该能看懂
JNI crash dump 一般以***********************************开头
11-25 15:13:20.788: I/DEBUG(1825): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
11-25 15:13:20.788: I/DEBUG(1825): Build fingerprint: 'samsung/GT-I9100/GT-I9100:4.0.3/IML74K/ZSLPG:user/release-keys'
11-25 15:13:20.788: I/DEBUG(1825): pid: 6454, tid: 6454 >>> com.ty.jnidebug <<<
11-25 15:13:20.788: I/DEBUG(1825): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
11-25 15:13:20.788: I/DEBUG(1825): r0 0000f448 r1 95e00019 r2 00000001 r3 00000000
11-25 15:13:20.788: I/DEBUG(1825): r4 4c1c9a88 r5 000129d0 r6 00000000 r7 4be45da0
11-25 15:13:20.788: I/DEBUG(1825): r8 beafe668 r9 4be45d98 10 00000000 fp beafe67c
11-25 15:13:20.788: I/DEBUG(1825): ip 4c773bb9 sp beafe650 lr 40917cf4 pc 4c773bc6 cpsr 20000030
11-25 15:13:20.788: I/DEBUG(1825): d0 0000000080000000 d1 0000000080000000
11-25 15:13:20.788: I/DEBUG(1825): d2 0000000000000000 d3 0000000000000000
11-25 15:13:20.798: I/DEBUG(1825): d4 8000000000000000 d5 42dc00003f800000
11-25 15:13:20.798: I/DEBUG(1825): d6 00000000c2dc0000 d7 0000000000000000
11-25 15:13:20.798: I/DEBUG(1825): d8 0000000000000000 d9 0000000000000000
11-25 15:13:20.798: I/DEBUG(1825): d10 0000000000000000 d11 0000000000000000
11-25 15:13:20.798: I/DEBUG(1825): d12 0000000000000000 d13 0000000000000000
11-25 15:13:20.803: I/DEBUG(1825): d14 0000000000000000 d15 0000000000000000
11-25 15:13:20.803: I/DEBUG(1825): d16 000000000000000c d17 c05b800000000000
11-25 15:13:20.803: I/DEBUG(1825): d18 0000000000000000 d19 0000000000000000
11-25 15:13:20.803: I/DEBUG(1825): d20 3ff0000000000000 d21 8000000000000000
11-25 15:13:20.803: I/DEBUG(1825): d22 0000000000000000 d23 ff00ff00ff00ff00
11-25 15:13:20.803: I/DEBUG(1825): d24 ed12c639a9569e61 d25 ff00ff00ff00ff00
11-25 15:13:20.803: I/DEBUG(1825): d26 f5f5f5f5f5f5f5f5 d27 ffffffffffffffff
11-25 15:13:20.803: I/DEBUG(1825): d28 00f7003e00f1003d d29 3ff0000000000000
11-25 15:13:20.803: I/DEBUG(1825): d30 0000000000000000 d31 3ff0000000000000
11-25 15:13:20.803: I/DEBUG(1825): scr 20000012
11-25 15:13:21.003: I/DEBUG(1825): #00 pc 00000bc6 /data/data/com.ty.jnidebug/lib/libJNIDebug.so (Java_com_ty_jnidebug_MainActivity_test)
11-25 15:13:21.003: I/DEBUG(1825): #01 pc 0001ecf0 /system/lib/libdvm.so (dvmPlatformInvoke)
11-25 15:13:21.003: I/DEBUG(1825): #02 pc 00058fac /system/lib/libdvm.so (_Z16dvmCallJNIMethodPKjP6JValuePK6MethodP6Thread)
11-25 15:13:21.008: I/DEBUG(1825): code around pc:
11-25 15:13:21.008: I/DEBUG(1825): 4c773ba4 0000345c e59f0004 e08f0000 eaffffe5 \4..............
11-25 15:13:21.008: I/DEBUG(1825): 4c773bb4 0000344c 9001b086 23009100 9b049304 L4.........#....
11-25 15:13:21.008: I/DEBUG(1825): 4c773bc4 601a2201 93032300 9b03e004 9b039305 .".`.#..........
11-25 15:13:21.008: I/DEBUG(1825): 4c773bd4 93033301 4b029a03 ddf6429a 4770b006 .3.....K.B....pG
11-25 15:13:21.008: I/DEBUG(1825): 4c773be4 000003e7 e5903000 e3130101 13833102 .....0.......1..
11-25 15:13:21.008: I/DEBUG(1825): code around lr:
11-25 15:13:21.008: I/DEBUG(1825): 40917cd4 3497c004 3488c004 3afffff9 e2888004 ...4...4...:....
11-25 15:13:21.008: I/DEBUG(1825): 40917ce4 eafffff9 e899000c e59bc00c e12fff3c ............<./.
11-25 15:13:21.008: I/DEBUG(1825): 40917cf4 e3560000 159bc010 e24bd014 188c0003 ..V.......K.....
11-25 15:13:21.008: I/DEBUG(1825): 40917d04 e8bd8bc0 e1a0ce22 e59b6008 e2866001 ...."....`...`..
11-25 15:13:21.008: I/DEBUG(1825): 40917d14 e3a02000 e4d6c001 e35c0000 0a000007 . ........\.....
11-25 15:13:21.008: I/DEBUG(1825): stack:
11-25 15:13:21.008: I/DEBUG(1825): beafe610 beafe644 [stack]
11-25 15:13:21.008: I/DEBUG(1825): beafe614 40111091 /system/lib/libutils.so
11-25 15:13:21.008: I/DEBUG(1825): beafe618 001d2690 [heap]
11-25 15:13:21.008: I/DEBUG(1825): beafe61c 40094540
11-25 15:13:21.008: I/DEBUG(1825): beafe620 00000068
11-25 15:13:21.008: I/DEBUG(1825): beafe624 400944b4
11-25 15:13:21.008: I/DEBUG(1825): beafe628 001d2698 [heap]
11-25 15:13:21.008: I/DEBUG(1825): beafe62c 4be45cf4
11-25 15:13:21.008: I/DEBUG(1825): beafe630 00000000
11-25 15:13:21.008: I/DEBUG(1825): beafe634 40061ab9 /system/lib/libc.so
11-25 15:13:21.008: I/DEBUG(1825): beafe638 00000000
11-25 15:13:21.008: I/DEBUG(1825): beafe63c 00186258 [heap]
11-25 15:13:21.013: I/DEBUG(1825): beafe640 00000000
11-25 15:13:21.013: I/DEBUG(1825): beafe644 4be45cfc
11-25 15:13:21.013: I/DEBUG(1825): beafe648 df0027ad
11-25 15:13:21.013: I/DEBUG(1825): beafe64c 00000000
11-25 15:13:21.013: I/DEBUG(1825): #00 beafe650 95e00019
11-25 15:13:21.013: I/DEBUG(1825): beafe654 0000f448 [heap]
11-25 15:13:21.013: I/DEBUG(1825): beafe658 000129d0 [heap]
11-25 15:13:21.013: I/DEBUG(1825): beafe65c 00012a78 [heap]
11-25 15:13:21.013: I/DEBUG(1825): beafe660 00000000
11-25 15:13:21.013: I/DEBUG(1825): beafe664 4094d4d7 /system/lib/libdvm.so
11-25 15:13:21.013: I/DEBUG(1825): #01 beafe668 4be45d94
11-25 15:13:21.013: I/DEBUG(1825): beafe66c 00000001
11-25 15:13:21.013: I/DEBUG(1825): beafe670 414fc1a8 /dev/ashmem/dalvik-heap (deleted)
11-25 15:13:21.013: I/DEBUG(1825): beafe674 000129e0 [heap]
11-25 15:13:21.013: I/DEBUG(1825): beafe678 beafe8f8 [stack]
11-25 15:13:21.013: I/DEBUG(1825): beafe67c 40951faf /system/lib/libdvm.so
这段dump里面,包含了crash时cpu当时寄存器的信息,数据总线,callstack等信息,一般主要看lr pc寄存器来程序死在哪个代码上,看这么一堆地址自然看不出到底是哪个位置,这时我们需要一个symbol文件,sym文件包含了地址和函数入口的映射信息。为此google release了ndk-stack这个工具来查看死机时的callstack(其余的一些寄存器信息被过滤掉了,因为对于上层开发大部分没什么大用)
ndk-stack 使用方法
ndk-stack -sym <sym-file path> [-dump <dump-file-path>]
sym-file 一般用ndk-build编译出来生成在$project\obj\local\armeabi
dump-file就是logcat抓出来的log,注意里面需要有以***********************开头的信息,这个dump-file是可选的,如果没有指定则默认为默认输入流,那么可以这样用
adb logcat | $NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi
注意 “|”前后没有空格,否则会报错
更多信息可以查看ndk文档 your ndk-path/docs/NDK-STACK.html
JNI logging方法
在android.mk 里LOCAL_LDLIBS:= -llog
在C/C++源文件里面包含头文件#include <android/log.h>
为了使用方便,最好使用如下宏定义
#define LOG_TAG "libripplejni"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
这样,在logcat中的打印出来的JNI log 和 Java的就一样了
注意,如果实在framework下编JNI和使用NDK编译的方法有点不一样
注意实在android.mk中的定义和在include的头文件不一样。。。在framework中编写jni最好参考其他的JNI源码,并且注意要在AndroidRuntime.cpp中注册该函数