总结:
搜索backtrace 
然后:
$ /d/android-ndk-r10c/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86
_64/bin/arm-linux-androideabi-addr2line.exe -e /e/devel/tools/androidProjects/J
niDemux/app/src/main/obj/local/armeabi-v7a/libjnimedia.so 0006494f
e:\devel\tools\androidProjects\JniDemux\app\src\main/jni/avdemux.c:1207
附:
       在Android开发中,程序Crash分三种情况:未捕获的异常、ANR(Application Not Responding)和闪退(NDK引发错误)。其中未捕获的异常根据logcat打印的堆栈信息很容易定位错误。ANR错误也好查,Android规定,应用与用户进行交互时,如果5秒内没有响应用户的操作,则会引发ANR错误,并弹出一个系统提示框,让用户选择继续等待或立即关闭程序。并会在/data/anr目录下生成一个traces.txt文件,记录系统产生anr异常的堆栈和线程信息。如果是闪退,这问题比较难查,通常是项目中用到了NDK引发某类致命的错误导致闪退。因为NDK是使用C/C++来进行开发,熟悉C/C++的程序员都知道,指针和内存管理是最重要也是最容易出问题的地方,稍有不慎就会遇到诸如内存地址访问错误、使用野针对、内存泄露、堆栈溢出、初始化错误、类型转换错误、数字除0等常见的问题,导致最后都是同一个结果:程序崩溃。不会像在Java层产生的异常时弹出“xxx程序无响应,是否立即关闭”之类的提示框。当发生NDK错误后,logcat打印出来的那堆日志根据看不懂,更别想从日志当中定位错误的根源,当时尝试过在各个jni函数中打印日志来跟踪问题,那效率实在是太低了,而且还定位不到问题。还好老天有眼,让我找到了NDK提供的几款调试工具,能够精确的定位到产生错误的根源。
       NDK安装包中提供了三个调试工具:addr2line、objdump和ndk-stack,其中ndk-stack放在$NDK_HOME目录下,与ndk-build同级目录。addr2line和objdump在ndk的交叉编译器工具链目录下,下面是我本机NDK交叉编译器工具链的目录结构:
从上图的目录结构中可以看出来,NDK针对不同的CPU架构实现了多套相同的工具。所以在选择addr2line和objdump工具的时候,要根据你目标机器的CPU架构来选择。如果是arm架构,选择arm-linux-androidabi-4.6/4.8(一般选择高版本)。x86架构,选择x86-4.6/4.8。mipsel架构,选择mipsel-linux-android-4.6/4.8。如果不知道目标机器的CPU架构,把手机连上电脑,用adb shell cat /proc/cpuinfo可以查看手机的CPU信息。下图是我本机的arm架构工具链目录结构:
adb shell cat /proc/cpuinfo
Processor       : ARMv7 Processor rev 3 (v7l)
下面通过NDK自带的例子hello-jni项目来演示一下如何精确的定位错误
  1. #include <string.h>
  2. #include <jni.h>
  3. // hell-jni.c
  4. #ifdef __cplusplus
  5. extern "C" {
  6. #endif
  7. void willCrash()
  8. {
  9. int i = 10;
  10. int y = i / 0;
  11. }
  12. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
  13. {
  14. willCrash();
  15. return JNI_VERSION_1_4;
  16. }
  17. jstring
  18. Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
  19. jobject thiz )
  20. {
  21. // 此处省略实现逻辑。。。
  22. }
  23. #ifdef __cplusplus
  24. }
  25. #endif
第7行定义了一个willCrash函数,函数中有一个除0的非法操作,会造成程序崩溃。第13行JNI_OnLoad函数中调用了willCrash,这个函数会在Java加载完.so文件之后回调,也就是说程序一启动就会崩溃。下面是运行程序后打印的log:
  1. 01-01 17:59:38.246: D/dalvikvm(20794): Late-enabling CheckJNI
  2. 01-01 17:59:38.246: I/ActivityManager(1185):
  3. Start proc com.example.hellojni for activity com.example.hellojni/.HelloJni: pid=20794 uid=10351 gids={50351, 1028, 1015}
  4. 01-01 17:59:38.296: I/dalvikvm(20794): Enabling JNI app bug workarounds for target SDK version 3...
  5. 01-01 17:59:38.366: D/dalvikvm(20794): Trying to load lib /data/app-lib/com.example.hellojni-1/libhello-jni.so 0x422a4f58
  6. 01-01 17:59:38.366: D/dalvikvm(20794): Added shared lib /data/app-lib/com.example.hellojni-1/libhello-jni.so 0x422a4f58
  7. 01-01 17:59:38.366: A/libc(20794): Fatal signal 8 (SIGFPE) at 0x0000513a (code=-6), thread 20794 (xample.hellojni)
  8. 01-01 17:59:38.476: I/DEBUG(253): pid: 20794, tid: 20794, name: xample.hellojni >>> com.example.hellojni <<<
  9. 01-01 17:59:38.476: I/DEBUG(253): signal 8 (SIGFPE), code -6 (SI_TKILL), fault addr 0000513a
  10. 01-01 17:59:38.586: I/DEBUG(253): r0 00000000 r1 0000513a r2 00000008 r3 00000000
  11. 01-01 17:59:38.586: I/DEBUG(253): r4 00000008 r5 0000000d r6 0000513a r7 0000010c
  12. 01-01 17:59:38.586: I/DEBUG(253): r8 75226d08 r9 00000000 sl 417c5c38 fp bedbf134
  13. 01-01 17:59:38.586: I/DEBUG(253): ip 41705910 sp bedbf0f0 lr 4012e169 pc 4013d10c cpsr 000f0010
  14. // 省略部份日志 。。。。。。
  15. 01-01 17:59:38.596: I/DEBUG(253): backtrace:
  16. 01-01 17:59:38.596: I/DEBUG(253): #00 pc 0002210c /system/lib/libc.so (tgkill+12)
  17. 01-01 17:59:38.596: I/DEBUG(253): #01 pc 00013165 /system/lib/libc.so (pthread_kill+48)
  18. 01-01 17:59:38.596: I/DEBUG(253): #02 pc 00013379 /system/lib/libc.so (raise+10)
  19. 01-01 17:59:38.596: I/DEBUG(253): #03 pc 00000e80 /data/app-lib/com.example.hellojni-1/libhello-jni.so (__aeabi_idiv0+8)
  20. 01-01 17:59:38.596: I/DEBUG(253): #04 pc 00000cf4 /data/app-lib/com.example.hellojni-1/libhello-jni.so (willCrash+32)
  21. 01-01 17:59:38.596: I/DEBUG(253): #05 pc 00000d1c /data/app-lib/com.example.hellojni-1/libhello-jni.so (JNI_OnLoad+20)
  22. 01-01 17:59:38.596: I/DEBUG(253): #06 pc 00052eb1 /system/lib/libdvm.so (dvmLoadNativeCode(char const*, Object*, char**)+468)
  23. 01-01 17:59:38.596: I/DEBUG(253): #07 pc 0006a62d /system/lib/libdvm.so
  24. 01-01 17:59:38.596: I/DEBUG(253): // 省略部份日志 。。。。。。
  25. 01-01 17:59:38.596: I/DEBUG(253): stack:
  26. 01-01 17:59:38.596: I/DEBUG(253): bedbf0b0 71b17034 /system/lib/libsechook.so
  27. 01-01 17:59:38.596: I/DEBUG(253): bedbf0b4 7521ce28
  28. 01-01 17:59:38.596: I/DEBUG(253): bedbf0b8 71b17030 /system/lib/libsechook.so
  29. 01-01 17:59:38.596: I/DEBUG(253): bedbf0bc 4012c3cf /system/lib/libc.so (dlfree+50)
  30. 01-01 17:59:38.596: I/DEBUG(253): bedbf0c0 40165000 /system/lib/libc.so
  31. 01-01 17:59:38.596: I/DEBUG(253): // 省略部份日志 。。。。。。
  32. 01-01 17:59:38.736: W/ActivityManager(1185): Force finishing activity com.example.hellojni/.HelloJni
日志分析:
第3行开始启动应用,第5行尝试加载应用  数据目录下的so,第6行在加载so文件的时候产生了一个致命的错误,第7行的Fatal signal 8提示这是一个致命的错误,这个信号是由linux内核发出来的,信号8的意思是浮点数运算异常,应该是在willCrash函数中做除0操作所产生的。下面重点看第15行backtrace的日志,backtrace日志可以看作是JNI调用的堆栈信息,以“#两位数字 pc”开头的都是backtrace日志。注意看第20行和21行,是我们自己编译的so文件和定义的两个函数,在这里引发了异常,导致程序崩溃
  1. 01-01 17:59:38.596: I/DEBUG(253): #04 pc 00000cf4 /data/app-lib/com.example.hellojni-1/libhello-jni.so (willCrash+32)
  2. 01-01 17:59:38.596: I/DEBUG(253): #05 pc 00000d1c /data/app-lib/com.example.hellojni-1/libhello-jni.so (JNI_OnLoad+20)
开始有些眉目了,但具体崩在这两个函数的哪个位置,我们是不确定的,如果函数代码比较少还好查,如果比较复杂的话,查起来也费劲。这时候就需要靠NDK为我们提供的工具来精确定位了。在这之前,我们先记录下让程序崩溃的汇编指令地址,willCrash:00000cf4,JNI_OnLoad:00000d1c
方式1:使用arm-linux-androideabi-addr2line  定位出错位置
以arm架构的CPU为例,执行如下命令:
  1. /Users/yangxin/Documents/devToos/java/android-ndk-r9d/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line -e /Users/yangxin/Documents/devToos/java/android-ndk-r9d/samples/hello-jni/obj/local/armeabi-v7a/libhello-jni.so 00000cf4 00000d1c
-e:指定so文件路径
0000cf4 0000d1c:出错的汇编指令地址
结果如下:
是不是惊喜的看到我们想要的结果了,分别在hello-jni.c的10和15行的出的错,再回去看看hello-jni.c的源码,15行的Jni_OnLoad函内调用了willCrash函数,第10行做了除0的操作引发的crash。
$ /d/android-ndk-r10c/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86
_64/bin/arm-linux-androideabi-addr2line.exe -e /e/devel/tools/androidProjects/J
niDemux/app/src/main/obj/local/armeabi-v7a/libjnimedia.so 0006494f
e:\devel\tools\androidProjects\JniDemux\app\src\main/jni/avdemux.c:1207
方式2:使用arm-linux-androideabi-objdump  定位出错的函数信息
在第一种方式中,通过addr2lin已经获取到了代码出错的位置,但是不知道函数的上下文信息,显得有点不是那么的“完美”,对于追求极致的我来说,这显然是不够的,下面我们来看一下怎么来定位函数的信息。
首先使用如下命令导出so的函数表信息:
  1. /Users/yangxin/Documents/devToos/java/android-ndk-r9d/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-objdump -S -D /Users/yangxin/Documents/devToos/java/android-ndk-r9d/samples/hello-jni/obj/local/armeabi-v7a/libhello-jni.so > Users/yangxin/Desktop/dump.log
在生成的asm文件中,找出我们开始定位到的那两个出错的汇编指令地址(在文件中搜索cf4或willCrash可以找到),如下图所示:
通过这种方式,也可以查出这两个出错的指针地址分别位于哪个函数中。
方式3:ndk-stack
如果你觉得上面的方法太麻烦的话,ndk-stack可以帮你减轻操作步聚,直接定位到代码出错的位置。
实时分析日志:
使用adb获取logcat的日志,并通过管道输出给ndk-stack分析,并指定包含符号表的so文件位置。如果程序包含多种CPU架构,需要根据手机的CPU类型,来选择不同的CPU架构目录。以armv7架构为例,执行如下命令:
  1. adb logcat | ndk-stack -sym /Users/yangxin/Documents/devToos/java/android-ndk-r9d/samples/hello-jni/obj/local/armeabi-v7a
当程序发生crash时,会输出如下信息:
  1. pid: 22654, tid: 22654, name: xample.hellojni >>> com.example.hellojni <<<
  2. signal 8 (SIGFPE), code -6 (SI_TKILL), fault addr 0000587e
  3. Stack frame #00 pc 0002210c /system/lib/libc.so (tgkill+12)
  4. Stack frame #01 pc 00013165 /system/lib/libc.so (pthread_kill+48)
  5. Stack frame #02 pc 00013379 /system/lib/libc.so (raise+10)
  6. Stack frame #03 pc 00000e80 /data/app-lib/com.example.hellojni-1/libhello-jni.so (__aeabi_idiv0+8): Routine __aeabi_idiv0 at /s/ndk-toolchain/src/build/../gcc/gcc-4.6/libgcc/../gcc/config/arm/lib1funcs.asm:1270
  7. Stack frame #04 pc 00000cf4 /data/app-lib/com.example.hellojni-1/libhello-jni.so (willCrash+32): Routine willCrash at /Users/yangxin/Documents/devToos/java/android-ndk-r9d/samples/hello-jni/jni/hello-jni.c:10
  8. Stack frame #05 pc 00000d1c /data/app-lib/com.example.hellojni-1/libhello-jni.so (JNI_OnLoad+20): Routine JNI_OnLoad at /Users/yangxin/Documents/devToos/java/android-ndk-r9d/samples/hello-jni/jni/hello-jni.c:15
  9. Stack frame #06 pc 00052eb1 /system/lib/libdvm.so (dvmLoadNativeCode(char const*, Object*, char**)+468)
  10. Stack frame #07 pc 0006a62d /system/lib/libdvm.so
第7行和第8行分别打印出了在源文件中出错的位置,和addr2line得到的结果一样。
先获取日志再分析:
这种方式和上面的方法差不多,只是获取log的来源不一样。适用于应用或游戏给测试部们测试的时候,测试人员发现crash,用adb logcat保存日志文件,然后发给程序员通过ndk-stack命令分析。操作流程如下:
  1. adb logcat > crash.log
  2. ndk-stack -sym /Users/yangxin/Documents/devToos/java/android-ndk-r9d/samples/hello-jni/obj/local/armeabi-v7a -dump crash.log
$ ndk-stack.exe -sym /e/devel/tools/androidProjects/JniDemux/app/src/main/obj/l ocal/armeabi-v7a/ -dump /e/devel/tools/androidProjects/JniDemux/ccccc.log
会得到
Stack frame I/DEBUG   (12219):     #04  pc 0000dda7  /system/lib/libc.so (free+1 0) 
Stack frame I/DEBUG   (12219):     #05  pc 00062533  /data/app-lib/com.elesos.jn idemux-1/libjnimedia.so (start_demux+762): Routine start_demux at e:\devel\tools\androidProjects\JniDemux\app\src\main/jni/avdemux.c:474
得到的结果和上面的方式是一样的。
参考

JNI NDK开发Crash错误定位 调试的更多相关文章

  1. Android NDK开发Crash错误定位[转]

    使用 ndk-stack 的时候需要你的 lib 编译为 debug版的,通常需要下面的修改: 1. 修改 android.mk,增加,为 LOCAL_CFLAGS 增加 -g 选项 2. 修改 ap ...

  2. Android NDK开发Crash错误定位

    在Android开发中,程序Crash分三种情况:未捕获的异常.ANR(Application Not Responding)和闪退(NDK引发错误).其中未捕获的异常根据logcat打印的堆栈信息很 ...

  3. JNI/NDK开发指南(开山篇)

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/41759643 相信很多做过Java或Android开发的朋友经常会接触到JNI方面的技术, ...

  4. JNI/NDK开发

    公司的新需求终于解决完了,离测试和发布还有段时间,第一次体验了下没需求没bug的感觉,真是舒爽~然后翻了翻有什么可以学的.无意翻到了Android后期发展的五大趋势.一.性能优化.二.高级UI.三.J ...

  5. AS2.2使用CMake方式进行JNI/NDK开发

    之前写过一篇比较水的文章Android手机控制电脑撸出HelloWorld 里面用到了JNI/NDK技术. 这篇文章给大家介绍下JNI/NDK开发.采用的是Android Studio2.2开发环境, ...

  6. android Jni NDK开发环境搭建及其简单实例的编写

    android  Jni  NDK开发环境搭建及其简单实例的编写 由于工作需要,需要采用开发想要的JNI,由于之前没有接触过安卓的开发,所以更加网上的帖子,学习了下.遇到了些问题,然后总结下学习过程中 ...

  7. Android JNI/NDK开发教程

    JNI/NDK开发指南:http://blog.csdn.net/xyang81/article/details/41759643

  8. Android JNI(NDK)开发总结

    早就知道Java有个jni可以调用本地化代码,一直没有动力去研究它,现在公司想通过在Android中调用本地化代码来申请较多的内存以突破Android对单个进程的内存限制,这确实是可行的:我的Nexu ...

  9. Android学Jni/Ndk 开发记录(一)

      治疗拖延症的唯一办法就是:一想起些什么 / 要做些什么就 TM 立马去做! 是的,突然想起我不会 JNI.NDK 开发.解决办法:立马去学! 一:配置 NDK 环境 下载 NDK 写入到配置文件 ...

随机推荐

  1. subverison的安装与注意事项

    1.安装 :官网下载 http://blog.csdn.net/sinboy/article/details/4000524 http://sourceforge.net/projects/win32 ...

  2. Dual Palindromes

    Dual PalindromesMario Cruz (Colombia) & Hugo Rickeboer (Argentina) A number that reads the same ...

  3. Wormholes(Bellman-ford)

    Wormholes Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 33008   Accepted: 12011 Descr ...

  4. onfiguration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/security]

    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Una ...

  5. 对 Linux 新手非常有用的 20 个命令

    参考:http://www.oschina.net/translate/useful-linux-commands-for-newbies 英文原文:http://www.tecmint.com/us ...

  6. 24.栈的push和pop序列[StackPushPopSequence]

    [题目] 输入两个整数序列.其中一个序列表示栈的push顺序,判断另一个序列有没有可能是对应的pop顺序.为了简单起见,我们假设push序列的任意两个整数都是不相等的. 比如输入的push序列是1.2 ...

  7. sphinx 增量索引与主索引使用测试

    2013年10月28日 15:01:16 首先对新增的商品建立增量索引,搜索时只使用增量索引: array (size=1) 0 => array (size=6) 'gid' => st ...

  8. 【JAVA、C++】LeetCode 001 Two Sum

    Given an array of integers, find two numbers such that they add up to a specific target number. The ...

  9. July 24th, Week 31st Sunday, 2016

    Miracles happen every day. 奇迹每天都在发生. Miracles actually happen every day, every moment. You may think ...

  10. Fedora install chrome

    1)下载chrome:chrome download,选择rpm版,下载地址:https://dl.google.com/linux/direct/google-chrome-stable_curre ...