使用 ndk-stack 的时候需要你的 lib 编译为 debug版的,通常需要下面的修改:

1. 修改 android.mk,增加,为 LOCAL_CFLAGS 增加 -g 选项
2. 修改 application.mk,增加 APP_OPTIM := debug
3. nkd-build -B
4. 从 obj/local/armeabi/ 下面把包复制到 libs/armeabi/
5. 出现异常后,调用 adb logcat | ndk-stack -sym libs/armeabi/

可以在链接的时候生成一个mapfile,总览整个代码的地址列表:
You need to generate a map file. The map file contains the function address and memory locations in your executable. Have your build system modified to generate a map file.
From the map file, you can use a text editor and search for addresses. I once wrote a program to find the two symbols bounding a given address. Worked great for environments like yours.
For android, I used the following line in the Application.mk or Android.mk (note that LOCAL_LDFLAGS is semi-undocumented in the Android NDK docs). LOCAL_LDFLAGS := -Wl,-Map,app.map

http://blog.csdn.net/xyang81/article/details/42319789

在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架构工具链目录结构:

下面通过NDK自带的例子hello-jni项目来演示一下如何精确的定位错误

  1. - ::38.246: D/dalvikvm(): Late-enabling CheckJNI
  2. - ::38.246: I/ActivityManager():
  3. Start proc com.example.hellojni for activity com.example.hellojni/.HelloJni: pid= uid= gids={, , }
  4. - ::38.296: I/dalvikvm(): Enabling JNI app bug workarounds for target SDK version ...
  5. - ::38.366: D/dalvikvm(): Trying to load lib /data/app-lib/com.example.hellojni-/libhello-jni.so 0x422a4f58
  6. - ::38.366: D/dalvikvm(): Added shared lib /data/app-lib/com.example.hellojni-/libhello-jni.so 0x422a4f58
  7. - ::38.366: A/libc(): Fatal signal (SIGFPE) at 0x0000513a (code=-), thread (xample.hellojni)
  8. - ::38.476: I/DEBUG(): pid: , tid: , name: xample.hellojni >>> com.example.hellojni <<<
  9. - ::38.476: I/DEBUG(): signal (SIGFPE), code - (SI_TKILL), fault addr 0000513a
  10. - ::38.586: I/DEBUG(): r0 r1 0000513a r2 r3
  11. - ::38.586: I/DEBUG(): r4 r5 0000000d r6 0000513a r7 0000010c
  12. - ::38.586: I/DEBUG(): r8 75226d08 r9 sl 417c5c38 fp bedbf134
  13. - ::38.586: I/DEBUG(): ip sp bedbf0f0 lr 4012e169 pc 4013d10c cpsr 000f0010
  14. // 省略部份日志 。。。。。。
  15. - ::38.596: I/DEBUG(): backtrace:
  16. - ::38.596: I/DEBUG(): # pc 0002210c /system/lib/libc.so (tgkill+)
  17. - ::38.596: I/DEBUG(): # pc /system/lib/libc.so (pthread_kill+)
  18. - ::38.596: I/DEBUG(): # pc /system/lib/libc.so (raise+)
  19. - ::38.596: I/DEBUG(): # pc 00000e80 /data/app-lib/com.example.hellojni-/libhello-jni.so (__aeabi_idiv0+)
  20. - ::38.596: I/DEBUG(): # pc 00000cf4 /data/app-lib/com.example.hellojni-/libhello-jni.so (willCrash+)
  21. - ::38.596: I/DEBUG(): # pc 00000d1c /data/app-lib/com.example.hellojni-/libhello-jni.so (JNI_OnLoad+)
  22. - ::38.596: I/DEBUG(): # pc 00052eb1 /system/lib/libdvm.so (dvmLoadNativeCode(char const*, Object*, char**)+)
  23. - ::38.596: I/DEBUG(): # pc 0006a62d /system/lib/libdvm.so
  24. - ::38.596: I/DEBUG(): // 省略部份日志 。。。。。。
  25. - ::38.596: I/DEBUG(): stack:
  26. - ::38.596: I/DEBUG(): bedbf0b0 71b17034 /system/lib/libsechook.so
  27. - ::38.596: I/DEBUG(): bedbf0b4 7521ce28
  28. - ::38.596: I/DEBUG(): bedbf0b8 71b17030 /system/lib/libsechook.so
  29. - ::38.596: I/DEBUG(): bedbf0bc 4012c3cf /system/lib/libc.so (dlfree+)
  30. - ::38.596: I/DEBUG(): bedbf0c0 /system/lib/libc.so
  31. - ::38.596: I/DEBUG(): // 省略部份日志 。。。。。。
  32. - ::38.736: W/ActivityManager(): 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. - ::38.596: I/DEBUG(): # pc 00000cf4 /data/app-lib/com.example.hellojni-/libhello-jni.so (willCrash+)
  2. - ::38.596: I/DEBUG(): # pc 00000d1c /data/app-lib/com.example.hellojni-/libhello-jni.so (JNI_OnLoad+)

开始有些眉目了,但具体崩在这两个函数的哪个位置,我们是不确定的,如果函数代码比较少还好查,如果比较复杂的话,查起来也费劲。这时候就需要靠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。

方式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可以帮你减轻操作步聚,直接定位到代码出错的位置。

  1. adb logcat | ndk-stack -sym /Users/yangxin/Documents/devToos/java/android-ndk-r9d/samples/hello-jni/obj/local/armeabi-v7a

当程序发生crash时,会输出如下信息:

  1. pid: , tid: , name: xample.hellojni >>> com.example.hellojni <<<
  2. signal (SIGFPE), code - (SI_TKILL), fault addr 0000587e
  3. Stack frame # pc 0002210c /system/lib/libc.so (tgkill+)
  4. Stack frame # pc /system/lib/libc.so (pthread_kill+)
  5. Stack frame # pc /system/lib/libc.so (raise+)
  6. Stack frame # pc 00000e80 /data/app-lib/com.example.hellojni-/libhello-jni.so (__aeabi_idiv0+): Routine __aeabi_idiv0 at /s/ndk-toolchain/src/build/../gcc/gcc-4.6/libgcc/../gcc/config/arm/lib1funcs.asm:
  7. Stack frame # pc 00000cf4 /data/app-lib/com.example.hellojni-/libhello-jni.so (willCrash+): Routine willCrash at /Users/yangxin/Documents/devToos/java/android-ndk-r9d/samples/hello-jni/jni/hello-jni.c:
  8. Stack frame # pc 00000d1c /data/app-lib/com.example.hellojni-/libhello-jni.so (JNI_OnLoad+): Routine JNI_OnLoad at /Users/yangxin/Documents/devToos/java/android-ndk-r9d/samples/hello-jni/jni/hello-jni.c:
  9. Stack frame # pc 00052eb1 /system/lib/libdvm.so (dvmLoadNativeCode(char const*, Object*, char**)+)
  10. Stack frame # pc 0006a62d /system/lib/libdvm.so

第7行和第8行分别打印出了在源文件中出错的位置,和addr2line得到的结果一样。

  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

得到的结果和上面的方式是一样的。

Android NDK开发Crash错误定位[转]的更多相关文章

  1. Android NDK开发Crash错误定位

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

  2. JNI NDK开发Crash错误定位 调试

    总结: 搜索backtrace  然后: $ /d/android-ndk-r10c/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86 ...

  3. Android NDK开发常见错误

    错误一: make: *** No rule to make target `/cygdrive/d/1-workspace/showmap-android-opengles/jni/showmap_ ...

  4. Android NDK开发调试

    ndk-stack: https://developer.android.com/ndk/guides/ndk-stack?hl=zh-cn JNI开发: https://developer.andr ...

  5. Android NDK 开发(四)java传递数据到C【转】

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/41845701 前面几篇文章介绍了Android NDK开发的简单概念.常见错误及处 ...

  6. Android NDK 开发(三)--常见错误锦集合Log的使用【转】

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/41826511  Android NDK开发经常因某些因素会出现一些意想不到的错误, ...

  7. android NDK开发在本地C/C++源码中设置断点单步调试具体教程

    近期在学android NDK开发,折腾了一天,最终可以成功在ADT中设置断点单步调试本地C/C++源码了.网上关于这方面的资料太少了,并且大都不全,并且调试过程中会出现各种各样的问题,真是非常磨人. ...

  8. Android NDK开发Hello Word!

    在之前的博客中已经为大家介绍了,如何在win环境下配置DNK程序,本篇我将带大家实现一个简单的Hello jni程序,让大家真正感受一下NDK开发的魅力.这里我们选择使用C+JAVA开发Android ...

  9. Android NDK开发

    Android NDK 开发教程(极客学院) 一.Android NDK环境搭建 使用最新ndk,直接抛弃cygwin,以前做Android的项目要用到NDK就必须要下载NDK,下载安装Cygwin( ...

随机推荐

  1. leetcode problem 31 -- Next Permutation

    Implement next permutation, which rearranges numbers into the lexicographically next greater permuta ...

  2. javascript下动态this与动态绑定实例代码

    this 的值取决于 function 被调用的方式,一共有四种, 如果一个 function 是一个对象的属性,该 funtion 被调用的时候,this 的值是这个对象. 如果 function ...

  3. TDirectory.GetAttributes、TDirectory.SetAttributes获取和设置文件夹属性

    使用函数: System.IOUtils.TDirectory.GetAttributes//获取属性 System.IOUtils.TDirectory.SetAttributes//设置属性 注: ...

  4. iOS - 打电话, 发短信

    电话.短信是手机的基础功能,iOS中提供了接口,让我们调用.这篇文章简单的介绍一下iOS的打电话.发短信在程序中怎么调用. 1.打电话 [[UIApplication sharedApplicatio ...

  5. 一个奇怪的编码 big5-hkscs

    # --*-- coding:utf-8 --*-- import urllib2 import urllib postDict = { 'IsExist_Slt_Part_Id': 'False', ...

  6. libSVM 参数选择

    libSVM 参数选择  [预测标签,准确率,决策值]=svmpredict(测试标签,测试数据,训练的模型);    原文参考:http://blog.csdn.net/carson2005/art ...

  7. NWERC 2012 Problem A Admiral

    一个最小费用最大流的简单建模题: 比赛的时候和小珺合力想到了这个题目的模型: 方法:拆点+边的容量为1 这样就可以保证他们不会在点上和边上相遇了! 感谢刘汝佳大神的模板,让我这个网络流的小白A了这个题 ...

  8. Spring In Action 第4版笔记-第一章-001架构

    1.Spring’s fundamental mission: Spring simplifies Java development. 2.To back up its attack on Java ...

  9. Nginx配置性能优化(转)

    大多数的Nginx安装指南告诉你如下基础知识——通过apt-get安装,修改这里或那里的几行配置,好了,你已经有了一个Web服务器了.而且,在大多数情况下,一个常规安装的nginx对你的网站来说已经能 ...

  10. CKFinder 2.4 ASP.NET 破解

    网上的破解教程对于2.4版本来说已经过时了. 以下是CKFinder 2.4 ASP.NET的亲测可用破解方法,经测试,只需修改两处代码. 打开ckfinder.js, 步骤1. 搜索替换如下代码: ...