人间观察

你有多久没有十点之前睡过觉了。

假期ing~~~

在Android中进行JNI的开发的当然也会发生crash,而发生crash后比较难定位。
因为jni是使用C/C++来进行开发的,熟悉C/C++语言的同学都知道,指针和内存申请的使用时需要自己申请和释放的,它不像java那样有jvm有垃圾回收管理机制gc,稍微管理不当就会导致问题。比如:内存地址访问错误、堆栈溢出、指针使用错误等等,最后都会导致程序崩溃。

幸好Android NDK提供了一些工具来帮助精确定位到出问题的代码。
我们模拟一下crash,模拟一个指针未初始化访问的case。

#include "common.h"
#include <malloc.h>extern "C"
JNIEXPORT void JNICALL
Java_com_bj_gxz_jniapp_crash_JNICrash_crash(JNIEnv *env, jobject thiz) {
//    int *p = static_cast<int *>(malloc(sizeof(int)));int *p;*p = 1;LOG_D("*P=%d", *p);free(p);
}

*p没有初始化
崩溃的日志如下:


2020-09-28 14:36:49.737 19047-19047/com.bj.gxz.jniapp A/libc: Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x6fcabb4d34 in tid 19047 (m.bj.gxz.jniapp), pid 19047 (m.bj.gxz.jniapp)
2020-09-28 14:36:49.856 19167-19167/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2020-09-28 14:36:49.856 19167-19167/? A/DEBUG: Build fingerprint: 'HONOR/STF-AL00/HWSTF:9/HUAWEISTF-AL00/9.1.0.219C00:user/release-keys'
2020-09-28 14:36:49.856 19167-19167/? A/DEBUG: Revision: '0'
2020-09-28 14:36:49.856 19167-19167/? A/DEBUG: ABI: 'arm64'
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG: Happend: 'Mon Sep 28 14:36:49 2020'
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG: SYSVMTYPE: ArtAPPVMTYPE: Art
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG: pid: 19047, tid: 19047, name: m.bj.gxz.jniapp  >>> com.bj.gxz.jniapp <<<
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG: signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x6fcabb4d34
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x0  0000006fcb0c5460  x1  0000007fdd935134  x2  0000006fac4b2b84  x3  0000000070cfc600
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x4  0000000000000000  x5  0000000000000000  x6  0000000000000000  x7  0000000000000000
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x8  0000000000000001  x9  0000000000000003  x10 0000006fac4b2b80  x11 0000006fcabb4d34
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x12 0000007051586630  x13 0f2fce861ea823e3  x14 000000705118b000  x15 ffffffffffffffff
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x16 0000006fac4990bc  x17 00000070507c5d48  x18 0000000000000001  x19 0000006fcb015c00
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x20 0000000000000000  x21 0000006fcb015c00  x22 0000007fdd935400  x23 0000006faeae68f1
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x24 0000000000000004  x25 00000070518f65e0  x26 0000006fcb015ca0  x27 0000000000000001
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     x28 0000007fdd935130  x29 0000007fdd935100
2020-09-28 14:36:49.857 19167-19167/? A/DEBUG:     sp  0000007fdd9350e0  lr  0000006fcaeb6fe4  pc  0000006fac4990ec
2020-09-28 14:36:49.891 744-2617/? E/LOGSERVER_UTILS: [Erecovery]readEvent: eRecEventManager readEvent 0
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG: backtrace:
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #00 pc 000000000000e0ec  /data/app/com.bj.gxz.jniapp-wTViTmvyh8_za4_TB4rqtQ==/lib/arm64/libnative-lib.so (Java_com_bj_gxz_jniapp_crash_JNICrash_crash+48)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #01 pc 0000000000588fe0  /system/lib64/libart.so (art_quick_generic_jni_trampoline+144)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #02 pc 000000000057ff88  /system/lib64/libart.so (art_quick_invoke_stub+584)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #03 pc 00000000000d8608  /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #04 pc 000000000028cec8  /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #05 pc 0000000000286ed0  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+968)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #06 pc 000000000054747c  /system/lib64/libart.so (MterpInvokeVirtual+588)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #07 pc 0000000000572514  /system/lib64/libart.so (ExecuteMterpImpl+14228)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #08 pc 00000000001678da  /dev/ashmem/dalvik-classes.dex extracted in memory from /data/app/com.bj.gxz.jniapp-wTViTmvyh8_za4_TB4rqtQ==/base.apk (deleted) (com.bj.gxz.jniapp.MainActivity.onJniCrash+10)
2020-09-28 14:36:49.998 19167-19167/? A/DEBUG:     #09 pc 0000000000260bd4  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.2850692617+488)
2//  ...省略其它日志不重要的日志

crash日志里 最有用的backtrace信息。 backtrace描述了crash发生时函数的调用关系及其它地址信息等。
在backtrace中,从#00到#04 共五行信息代表是crash时函数调用关系,从下往上倒着看,#04行的方法调用了#03行的方法;#03行的方法调用了#02行的方法;向上类推,最后就是#01行的方法调用了#00行的方法。而最终出现的crash就是在#00行中。

如何定位解决呢?

使用ndk提供的命令addr2line

addr2line命令在ndk版本的toolchains目录下,我的是在

/Users/guxiuzhong/Library/Android/sdk/ndk/21.0.6113669/toolchains
B000000073160:toolchains guxiuzhong$ ls
aarch64-linux-android-4.9   renderscript
arm-linux-androideabi-4.9   x86-4.9
llvm                x86_64-4.9

从上面可以看得出来NDK针对不同的CPU架构实现了多套相同的工具,所以你要根据目标机器的CPU架构来选择。如果不知道目标机器的CPU架构,把手机连上电脑,用adb shell cat /proc/cpuinfo可以查看手机的CPU信息。我的目标机器是Processor : AArch64 Processor rev 1 (aarch64)

addr2line的使用方式是

addr2line -e 000000000000e0ec

-e 指定带符号表的so文件路径,也需要对应目标机器的CPU架构的so文件的绝对路径

000000000000e0ec 崩溃的汇编指令地址

addr2line一下

/Users/guxiuzhong/Library/Android/sdk/ndk/21.0.6113669/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line  -e /Users/guxiuzhong/Desktop/JNIAPP/app/build/intermediates/cmake/debug/obj/arm64-v8a/libnative-lib.so  000000000000e0ec

结果如下

/Users/guxiuzhong/Desktop/JNIAPP/app/src/main/cpp/crash.cpp:9

回看下我们.cpp的源码,第9行由于int* p指针没有初始化导致了fault addr。

使用ndk提供的命令ndk-stack

这个命令可以简化上面的步骤。

使用adb获取logcat的日志,并通过管道输出给ndk-stack进行分析。

ndk-stack -sym使用方式是

ndk-stack -sym XXXX

-sym 指定带符号表的so文件路径,也需要对应目标机器的CPU架构的so文件的根目录即可

ndk-stack一下

adb logcat | /Users/guxiuzhong/Library/Android/sdk/ndk/21.0.6113669/ndk-stack -sym /Users/guxiuzhong/Desktop/JNIAPP/app/build/intermediates/cmake/debug/obj/arm64-v8a

这时候模拟一下crash,检测到crash,ndk-stack自动解析了,结果和上面的一样第9行:

********** Crash dump: **********
Build fingerprint: 'HONOR/STF-AL00/HWSTF:9/HUAWEISTF-AL00/9.1.0.219C00:user/release-keys'
#00 0x000000000000e0ec /data/app/com.bj.gxz.jniapp-wTViTmvyh8_za4_TB4rqtQ==/lib/arm64/libnative-lib.so (Java_com_bj_gxz_jniapp_crash_JNICrash_crash+48)Java_com_bj_gxz_jniapp_crash_JNICrash_crash/Users/guxiuzhong/Desktop/JNIAPP/app/src/main/cpp/crash.cpp:9:8
#01 0x0000000000588fe0 /system/lib64/libart.so (art_quick_generic_jni_trampoline+144)
#02 0x000000000057ff88 /system/lib64/libart.so (art_quick_invoke_stub+584)

这个命令也支持文件的解析(使用-dump参数),这个好,因为有些crash不是必须的。

ndk-stack -dump 使用方式是

ndk-stack -sym XXXX -dump XXXX

-sym 指定带符号表的so文件路径,也需要对应目标机器的CPU架构的so文件的根目录即可
-dump crash的堆栈信息的文件

B000000073160:bin guxiuzhong$ /Users/guxiuzhong/Library/Android/sdk/ndk/21.0.6113669/ndk-stack -sym /Users/guxiuzhong/Desktop/JNIAPP/app/build/intermediates/cmake/debug/obj/arm64-v8a -dump  ~/Desktop/crash.log
********** Crash dump: **********
Build fingerprint: 'HONOR/STF-AL00/HWSTF:9/HUAWEISTF-AL00/9.1.0.219C00:user/release-keys'
#00 0x000000000000e0ec /data/app/com.bj.gxz.jniapp-wTViTmvyh8_za4_TB4rqtQ==/lib/arm64/libnative-lib.so (Java_com_bj_gxz_jniapp_crash_JNICrash_crash+48)Java_com_bj_gxz_jniapp_crash_JNICrash_crash/Users/guxiuzhong/Desktop/JNIAPP/app/src/main/cpp/crash.cpp:9:8
#01 0x0000000000588fe0 /system/lib64/libart.so (art_quick_generic_jni_trampoline+144)
#02 0x000000000057ff88 /system/lib64/libart.so (art_quick_invoke_stub+584)
#03 0x00000000000d8608 /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
#04 0x000000000028cec8 /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
#05 0x0000000000286ed0 /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+968)
#06 0x000000000054747c /system/lib64/libart.so (MterpInvokeVirtual+588)
#07 0x0000000000572514 /system/lib64/libart.so (ExecuteMterpImpl+14228)

总结:两种方法来定位crash。 addr2line -e xxxx 指定汇编指令地址。 adb logcat | ndk-stack -sym 指定对应目标机器的CPU架构的so文件的绝对路径; ndk-stack -dump 对应目标机器的CPU架构的so文件的根目录 -dump crash的堆栈信息的文件。

Android-JNI开发系列《四》Native-Crash定位相关推荐

  1. Android JNI开发系列(二)HelloWorld

    2019独角兽企业重金招聘Python工程师标准>>> 入门HelloWorld 新建项目 Configure your new project部分选中 Include C++ Su ...

  2. android 字符串函数,Android JNI开发系列(六)字符串操作

    JNI字符串操作 字符串是引用数据类型,不属于基本数据类型 Java 使用unicode编码,C使用UTF-8,所以在操作中 C语言的字符串操作在头文件中 示例代码 public native Str ...

  3. 【转】Android 驱动开发系列四

    原文网址:http://www.2cto.com/kf/201304/202040.html 时隔多日,终于都抽出时间来写blog了.废话不多说,接着上一篇,这里将介绍如何编写HAL层(硬件抽象层)对 ...

  4. Android 系统开发系列四

    这里将介绍如何编写HAL层(硬件抽象层)对应的JNI方法. 1.定义JNI层接口 进入到android-4.0.4_r1.2/hardware/libhardware/include/hardware ...

  5. Android JNI开发入门之二

    在上一篇文章<Android JNI开发入门之一>中,我介绍了Android应用程序(APK)怎样通过JNI调用Native C实现的共享库.本文将进一步介绍Android应用程序通过JN ...

  6. Android蓝牙开发系列文章-蓝牙设备类型知多少?

    在写<Android蓝牙开发系列文章-蓝牙音箱连接>时,计划细化出一篇讲解蓝牙设备类型的文章,现在它来了~ 阅读其他内容,可以点击<Android蓝牙开发系列文章-策划篇>,或 ...

  7. Android商城开发系列

    Android商城开发系列(一)--开篇 Android商城开发系列(二)--App启动欢迎页面制作 Android商城开发系列(三)--使用Fragment+RadioButton实现商城底部导航栏 ...

  8. Android蓝牙开发系列文章-玩转BLE开发(一)

    我们在<Android蓝牙开发系列文章-策划篇>中计划讲解一下蓝牙BLE,现在开始第一篇:Android蓝牙开发系列文章-玩转BLE开发(一).计划要写的BLE文章至少分四篇,其他三篇分别 ...

  9. Android Studio开发(四)SQLite数据库的DAO标准CRUD操作模拟微信通讯录

    Android Studio开发(四)SQLite数据库的DAO标准CRUD操作模拟微信通讯录 Android Studio开发(四)SQLite数据库的DAO标准CRUD操作模拟微信通讯录 一.任务 ...

  10. android 相机编程,Android相机开发系列

    Android Camera Develop Series 简介 Android相机开发系列文章循序渐进,教你从一个没有任何功能的相机APP开始,逐步完善实现一般相机APP的各种功能,甚至还能拿来做图 ...

最新文章

  1. 在Ubuntu服务器上使用python3+selenium模块
  2. Sumblime Text 2 常用插件以及安装方法
  3. Server Tomcat v7.0 Server at localhost failed to start.解决办法(图文详解)
  4. 【CentOS Linux 7】实验2【Linux多用户管理】
  5. Attention模型:我的注意力跟你们人类不一样
  6. while的用法java_java中的while循环和do while循环
  7. Cracking the Coding Interview(Stacks and Queues)
  8. 卸载centos7自带mysql_centos7完全卸载mysql
  9. JAVA将list2合并到list1_java如何将两个list合并的问题
  10. 分享一个完整的Mybatis分页解决方案
  11. 自治系统间的路由协议--BGP详细介绍
  12. bzoj3714 [PA2014]Kuglarz
  13. java中Executor、ExecutorService、ThreadPoolExecutor介绍(转)
  14. 线性调频信号与脉冲压缩
  15. mysql实现pr曲线_如何画PR curve (PR曲线)基于COCO格式数据集 在maskrcnn_benchmark中
  16. NTDETECT.COM 丢失(NTDETECT failed)解决方法
  17. 海思hi3516dv300 配置uart3
  18. Lerna管理npm包
  19. 实验二 Java基础语法练习-基本数据类型、运算符与表达式、选择结构
  20. 通过MERL100计算Blender Disney BRDF参数

热门文章

  1. Notepad++ 64位 Jsonviewer Compareplugin 安装
  2. Beyond Compare可以进行二进制比较
  3. [日记]游长白遇梅花,植物大战僵尸
  4. (译)如何使用cocos2d来制作简单的iphone游戏:更猛的怪物和更多的关卡。(第三部分。完!)...
  5. 实训汇编语言设计——内存多字节10进制数相加
  6. oracle学习总结一(基础)
  7. 网页header 的 meta使用
  8. ReactJS 知识简介
  9. 如何使用powerdesigner导出sql脚本
  10. java 比较2个时间大小写_date - Java 8:计算两个LocalDateTime之间的差异