最近做的一个东西需要jni去实现,对于一个我这样用惯了java,习惯了debug的人来说,用NDK,使用C/C++去编程是件很苦逼的事情,但是最苦逼的事情在于内存错误发生后如何排查,尤其是 Fatal signal 11 (SIGSEGV)这个错误尤其恼人,报出来之后程序就会崩溃,定位还不好定位,刚开始发生这样的错误时候,只能去到处加log,到处屏蔽代码去找问题发生的地方,效率比较低,并且很难找到问题真正出在哪,后来慢慢找到一些方法,可以相对比较快的定位到问题源头,一般情况下能相对容易的发现问题到底出在哪。下面简单的说说:
1:首先在logcat中加入debug(方法如图所示):
2:在运行后报错中会在debug中找到类似这样的一串错误
看到这个长长的错误,不要慌,我们就要拿着这个错误去分析:
首先,从这个长长的串中找到有用的信息
10-28 12:39:17.779: I/DEBUG(7749): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad
10-28 12:39:17.779: I/DEBUG(7749): r0 00000027 r1 deadbaad r2 a0000000 r3 00000000
10-28 12:39:17.779: I/DEBUG(7749): r4 00000001 r5 00000000 r6 44bc8c10 r7 44984e64
10-28 12:39:17.779: I/DEBUG(7749): r8 44bc8b70 r9 44984e54 10 44984e3c fp 800a5368
10-28 12:39:17.779: I/DEBUG(7749): ip afd46668 sp 44bc8ae0 lr afd191d9 pc afd15ca4 cpsr 60000030
10-28 12:39:17.779: I/DEBUG(7749): d0 646f423262287964 d1 6f42796f72747365
10-28 12:39:17.779: I/DEBUG(7749): d2 646e412f65737062 d3 6850656e69676e32
10-28 12:39:17.779: I/DEBUG(7749): d4 786f427363697379 d5 736e657478454432
10-28 12:39:17.779: I/DEBUG(7749): d6 2f696e6a2f6e6f69 d7 79442f4432786f42
10-28 12:39:17.779: I/DEBUG(7749): d8 42ce94f34e6e6b28 d9 3c8888893c888888
10-28 12:39:17.779: I/DEBUG(7749): d10 c26f7c303f800000 d11 3fed9bafc26f7e0e
10-28 12:39:17.779: I/DEBUG(7749): d12 0000000000000000 d13 0000000000000000
10-28 12:39:17.779: I/DEBUG(7749): d14 0000000000000000 d15 0000000000000000
10-28 12:39:17.779: I/DEBUG(7749): d16 3ef18d523ae9735c d17 3fca68bfcfa2d635
10-28 12:39:17.779: I/DEBUG(7749): d18 bf29fd09783e4216 d19 3ec6cd878c3b46a7
10-28 12:39:17.779: I/DEBUG(7749): d20 3f826164f3f641b5 d21 3f5e8ec8e86d933e
10-28 12:39:17.779: I/DEBUG(7749): d22 3e3782e7e0000000 d23 3e33401660000000
10-28 12:39:17.779: I/DEBUG(7749): d24 3fca99a880000000 d25 bfca99a880000000
10-28 12:39:17.779: I/DEBUG(7749): d26 be3ff77520000000 d27 3ef99342e0ee5069
10-28 12:39:17.779: I/DEBUG(7749): d28 bfa2b4442c6a6c2f d29 0000000000000000
10-28 12:39:17.779: I/DEBUG(7749): d30 0000000000000000 d31 0000000000000000
10-28 12:39:17.779: I/DEBUG(7749): scr 80000012
10-28 12:39:17.829: I/DEBUG(7749): #00 pc 00015ca4 /system/lib/libc.so
10-28 12:39:17.829: I/DEBUG(7749): #01 pc 0001c92c /system/lib/libc.so
主要看红色加粗的地方(#打头的部分),这个表明了错误发生的原因和地方,因此问题分析从这个入手,一般情况下这个红色加粗部分有两种情况,一种情况就如上边的错误一样,报错都来在系统的so包,并且没有进一步详细的信息,这种情况下,问题可能出现的都比较弱智,一般主要去分析下jni入口函数,反调,数组是否越界,使用已经delete的资源等这些常规错误,稍微注意下可能就能找到源头,也可能问题非常棘手,要拼人品了哈。
第二种报错可能是类似与这样:
#00 pc 00011784 /system/lib/libc.so (pthread_attr_init)
#01 pc 000de128 /data/data/com.test.android.Test/lib/libDemo.so (_ZN6Demo6Test4InitEv)
#02 pc 000def90 /data/data/com.test.android.Test/lib/libDemo.so (_ZN6Demo16TestDemo4InitESsiiil)
可以看到错误直接报道咱们自己的so包,并且Test4Init能看到是Test类下的init方法,越上边是问题的越根源,这样我们就可以按图索骥,从Test的init方法里去仔细排查错误的原因,可以大大减少排查耗费的时间。
接下来比较高深的是通过汇编方式找具体的错误,目前还没用到,定位到方法基本可以解决问题,大家有兴趣的可以看下后边给出的参考文章。
目前在最近的工作中通过这一方法基本可以较快的找到问题的发生地方,比之前各种log和各种屏代码效率高了很多,如果大家还有更好的办法希望留言讨论一下。
参考文章:http://blog.csdn.net/helldevil/article/details/6682211