文章目录

  • 首先制造一个 so crash问题
  • addr2line
  • addr2line工具位置
  • addr2line命令如下:
  • 分析crash log
  • so strip
  • 欢迎联系、指正、批评

首先制造一个 so crash问题

public class MainActivity extends AppCompatActivity {// Used to load the 'ndkcrashdemo' library on application startup.static {System.loadLibrary("ndkcrashdemo");}private ActivityMainBinding binding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());// Example of a call to a native methodTextView tv = binding.sampleText;tv.setText(stringFromJNI());}/*** A native method that is implemented by the 'ndkcrashdemo' native library,* which is packaged with this application.*/public native String stringFromJNI();
}

故意空指针

#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_cy_ndkcrashdemo_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {std::string hello=NULL;//故意空指针hello.c_str();return env->NewStringUTF(hello.c_str());
}

运行到真机,发生crash,部分LOG如图:

发现并不能看出代码哪里有毛病

工欲善其事必先利其器

addr2line

addr2line translates addresses into file names and line numbers.
Given an address in an executable or an offset in a section of a relocatable object,
it uses the debugging information to figure out which file name and line number are associated with it.

addr2line工具是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具。
一般适用于 debug 版本或带有 symbol 信息的库。

addr2line工具位置

addr2line工具在NDK 里的路径如下(注意:每个版本都不一样)

Windows:
32位:D:\AndroidSDK\ndk\21.4.7075529\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin\
arm-linux-androideabi-addr2line.exe
64位:D:\AndroidSDK\ndk\21.4.7075529\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin\
aarch64-linux-android-addr2line.exe

addr2line命令如下:

The options are:@<file>                Read options from <file>-a --addresses         Show addresses//显示地址-b --target=<bfdname>  Set the binary file format-e --exe=<executable>  Set the input file name (default is a.out)//设置so库的路径-i --inlines           Unwind inlined functions-j --section=<name>    Read section-relative offsets instead of addresses-p --pretty-print      Make the output easier to read for humans//设置输出信息可读性更强-s --basenames         Strip directory names-f --functions         Show function names//显示函数名称-C --demangle[=style]  Demangle function names-h --help              Display this information-v --version           Display the program's version

分析crash log

com.cy.ndkcrashdemo A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 22618 (cy.ndkcrashdemo), pid 22618 (cy.ndkcrashdemo)
DEBUG: Softversion: PD2073B_A_1.8.15
DEBUG: Time: 2022-04-20 15:34:49
DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
DEBUG: Build fingerprint: 'vivo/PD2073/PD2073:11/RP1A.200720.012/compiler1228233519:user/release-keys'
DEBUG: Revision: '0'
DEBUG: ABI: 'arm64'
DEBUG: Timestamp: 2022-04-20 15:34:49+0800
DEBUG: pid: 22618, tid: 22618, name: cy.ndkcrashdemo  >>> com.cy.ndkcrashdemo <<<
DEBUG: uid: 10252
DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
DEBUG: Cause: null pointer dereference
DEBUG:     x0  0000000000000000  x1  0000000000000000  x2  0000000000000018  x3  00000076eecbf000
DEBUG:     x4  0000000000000000  x5  b4000076ee95ac00  x6  00000076497d53af  x7  000000000000001b
DEBUG:     x8  0**************1  x9  ad1a1eb4209bd757  x10 0000000000430000  x11 00000000069d2cd0
DEBUG:     x12 0000000000371314  x13 00000000006476c0  x14 000000000051d6c0  x15 ffffffffffffffff
DEBUG:     x16 000000765387ce98  x17 00000076ec1cf4b0  x18 00000076eeec6000  x19 b4000076ee95ac00
DEBUG:     x20 0000000000000000  x21 b4000076ee95ac00  x22 00000076eecbf000  x23 b4000076ee95acb8
DEBUG:     x24 0000007667d48478  x25 00000076eecbf000  x26 000000000000000b  x27 0000000000000002
DEBUG:     x28 0000007fdeb3db00  x29 0000007fdeb3da10
DEBUG:     lr  000000765385757c  sp  0000007fdeb3da00  pc  00000076ec1cf4c0  pst 0000000080001000
DEBUG: backtrace:
DEBUG:       #00 pc 00000000000894c0  /apex/com.android.runtime/lib64/bionic/libc.so (strlen_default+16) (BuildId: d010ec9d0da07ff241689a4e9691c733)
DEBUG:       #01 pc 000000000000f578  /data/app/~~ipBdrIwT8qi72hDEkPx-ZQ==/com.cy.ndkcrashdemo-bwJhACJJBkJfPLjIt-tS0Q==/lib/arm64/libndkcrashdemo.so (std::__ndk1::char_traits<char>::length(char const*)+20) (BuildId: 34e2ac7e22782a65c51f760e5d9b4376d0cbea1d)
DEBUG:       #02 pc 000000000000f064  /data/app/~~ipBdrIwT8qi72hDEkPx-ZQ==/com.cy.ndkcrashdemo-bwJhACJJBkJfPLjIt-tS0Q==/lib/arm64/libndkcrashdemo.so (std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >::basic_string<std::nullptr_t>(char const*)+48) (BuildId: 34e2ac7e22782a65c51f760e5d9b4376d0cbea1d)
DEBUG:       #03 pc 000000000000efa4  /data/app/~~ipBdrIwT8qi72hDEkPx-ZQ==/com.cy.ndkcrashdemo-bwJhACJJBkJfPLjIt-tS0Q==/lib/arm64/libndkcrashdemo.so (Java_com_cy_ndkcrashdemo_MainActivity_stringFromJNI+56) (BuildId: 34e2ac7e22782a65c51f760e5d9b4376d0cbea1d)
DEBUG:       #04 pc 000000000013ced4  /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+148) (BuildId: 5aeb6fe6030b3ee85ca5c14f3f2f06e9)
DEBUG:       #05 pc 0000000000133564  /apex/com.android.art/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: 5aeb6fe6030b3ee85ca5c14f3f2f06e9)
DEBUG:       #06 pc 0000000000197e94  /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+204) (BuildId: 5aeb6fe6030b3ee85ca5c14f3f2f06e9)
DEBUG:       #07 pc 000000000030347c  /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+376) (BuildId: 5aeb6fe6030b3ee85ca5c14f3f2f06e9)
DEBUG:       #84 pc 0000000000529d10  /apex/com.android.art/lib64/libart.so (art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+92) (BuildId: 5aeb6fe6030b3ee85ca5c14f3f2f06e9)
DEBUG:       #85 pc 000000000041bd84  /apex/com.android.art/lib64/libart.so (art::JNI<true>::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+656) (BuildId: 5aeb6fe6030b3ee85ca5c14f3f2f06e9)
DEBUG:       #86 pc 0000000000099434  /system/lib64/libandroid_runtime.so (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+124) (BuildId: c8f8eb3a06894fb8e5258f8c2f08e801)
DEBUG:       #87 pc 00000000000a0ca0  /system/lib64/libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)+836) (BuildId: c8f8eb3a06894fb8e5258f8c2f08e801)
DEBUG:       #88 pc 0000000000003564  /system/bin/app_process64 (main+1308) (BuildId: a4535eefb34c582385a2cf7c5548aea0)
DEBUG:       #89 pc 0000000000088188  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+108) (BuildId: d010ec9d0da07ff241689a4e9691c733)

Cause: null pointer dereference 告诉我们出现了空指针
backtrace 以下,显示了程序调用过程
可以看到自己写的JNI方法Java_com_cy_ndkcrashdemo_MainActivity_stringFromJNI+56,这个函数对应的pc值(函数编译地址)是000000000000efa4

根据 .so 是 32 位还是 64 位选择对应的 addr2line 工具
使用方式如下:

addr2line -f -p -e so文件路径  报错函数对应的pc值

打开命令行窗口,进入aarch64-linux-android-addr2line(这里举例用的64位)所在目录,输入如下命令:

D:\AndroidSDK\ndk\21.4.7075529\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin>
aarch64-linux-android-addr2line -f -p -e
E:\AndroidStudioWorkspace\NDKCrashDemo\app\build\intermediates\merged_native_libs\
debug\out\lib\arm64-v8a\libndkcrashdemo.so 000000000000efa4

输出如下:

Java_com_cy_ndkcrashdemo_MainActivity_stringFromJNI at
E:/AndroidStudioWorkspace/NDKCrashDemo/app/src/main/cpp/native-lib.cpp:8

指出了crash发生在哪个文件的哪个函数,以及行号

你也可以将addr2line工具所在目录添加到系统环境变量path中,在命令行窗口中不用再进入addr2line所在目录,也可执行

so strip

一个完整的 so 由C/C++代码编译后的输出和debug信息组成,这些debug信息会记录 so
中所有方法的对照表,就是方法名和其编译地址的对应表,也叫做符号表,这种 so 也叫做未 strip 的,通常体积会比较大。

通常release的 so 都是需要经过一个strip操作的,这样strip之后的 so 中的debug信息会被剥离,整个 so
的体积也会缩小。

如下可以看到strip之前和之后的大小对比

strip的so

strip的so

如果debug信息丢了,无法再定位代码。

所以,这些debug信息尤为重要,是我们分析native crash问题的关键信息,那么我们在编译 so
时候务必保留一份未被strip的so 或者剥离后的符号表信息,以供后面问题分析,并且每次编译的so
都需要保存,一旦产生代码修改重新编译,那么修改前后的符号表信息会无法对应,也无法进行分析。

如下图所示,merged_native_libs下是含有debug信息的so,striped_native_libs下是去除了debug信息的so。
注意:不同版本死丢丢目录不一致。


所以开发so库的时候,每次编译之后,如果需要提供给别人使用,我们需要保留一份未strip的so库和JNI代码strip的so库提供给别人使用,当出现native crash的时候,我们可以通过未strip的so库定位strip的so库发生crash的代码位置,因为未strip已strip的so库的函数的编译地址是一致的,strip去除的只是debug信息。

欢迎联系、指正、批评

Github:https://github.com/AnJiaoDe

CSDN:https://blog.csdn.net/confusing_awakening

OpenCV入门教程:https://blog.csdn.net/confusing_awakening/article/details/113372425

ffmpeg入门教程:https://blog.csdn.net/confusing_awakening/article/details/102007792

微信公众号

QQ群

Android NDK入门教程之快速定位Crash问题相关推荐

  1. 《Delphi XE6 android 编程入门教程》推荐

    近5.6年已经没有看见关于delphi的新技术的书出来了(看来在国内delphi的使用量确实很低了), 高勇同学最近出了一本<Delphi XE6 android 编程入门教程>,上周刚拿 ...

  2. Android基础入门教程——4.2.3 Service精通

    Android基础入门教程--4.2.3 Service精通 标签(空格分隔): Android基础入门教程 本节引言: 本节,我们继续来研究Service(服务)组件,本节将会学习下Android中 ...

  3. el-date-picker设置默认日期_ERP入门教程:快速掌握金蝶ERP的基础-物料批次管理的应用及设置...

    ERP入门教程:快速掌握金蝶ERP的基础-物料的批次管理的应用及设置 关注我,我将定期分享更多的ERP解决方案 转发关注并私信我,了解更多的解决方案及操作方法哦 一.应用软件版本:金蝶KIS旗舰版6. ...

  4. android 编辑9图片,Android基础入门教程——1.6 .9(九妹)图片怎么玩

    Android基础入门教程--1.6 .9(九妹)图片怎么玩 Android基础入门教程 1.本节引言: 可能有的一些疑问: 1.什么是.9图片? 答:图片后缀名前有.9的图片,如pic1.9.png ...

  5. android设置webview缓存目录,Android基础入门教程——7.5.5 WebView缓存问题

    Android基础入门教程--7.5.5 WebView缓存问题 Android基础入门教程 本节引言:现在很多门户类信息网站,比如虎嗅,ifanr,钛媒体等等的APP,简单点说是信息阅读类的APP, ...

  6. Android WebRTC 入门教程(一) -- 使用相机

    前言,最近在搞网页投屏,发现 WebRTC 的Android 版本较少,这里的话,参考了一些优秀的博客,主要是这个大佬的 https://www.jianshu.com/p/eb5fd116e6c8 ...

  7. Android基础入门教程——4.3.1 BroadcastReceiver牛刀小试

    Android基础入门教程--4.3.1 BroadcastReceiver牛刀小试 标签(空格分隔): Android基础入门教程 本节引言 本节我们将来学习Android四大组件中的第三个:Bro ...

  8. Android基础入门教程——10.1 TelephonyManager(电话管理器)

    Android基础入门教程--10.1 TelephonyManager(电话管理器) 标签(空格分隔): Android基础入门教程 本节引言: 本章节是Android基础入门教程的最后一章,主要讲 ...

  9. 最新Android基础入门教程目录(完结版)

    第一章:环境搭建与开发相关(已完结 10/10) https://blog.csdn.net/coder_pig/article/details/50000773 Android基础入门教程--1.1 ...

最新文章

  1. Linux C编程--进程间通信(IPC)3--信号集和发送信号介绍
  2. java 广义表_java 输入广义表 生成二叉树 | 学步园
  3. C#和NewSQL更配 —— TiDB入门
  4. linux系统刷分辨率,Linux下设置其分辨率及刷新率
  5. python内置函数返回序列中最大元素_Python之路(第八篇)Python内置函数、zip()、max()、min()...
  6. 如何在Ubuntu 14.04上安装MySQL
  7. 如何利用MySQL Workbench创建Model EER 图
  8. 通才还是专才——由摩托裁员引发的讨论
  9. Hadoop完全分布式集群安装Hbase
  10. PyTorch1.2安装(Anaconda3 + Python3.6 + cpu版本)
  11. 《计算机组成原理》第二版第一章课后习题答案
  12. VirtualBox安装教程(Win10)含软件安装包
  13. flash 围棋_中国卫视执白0.5目胜flash77
  14. Mac Dotnet 坑 - Donet EF
  15. Windows取证分析基础知识大全
  16. python实现ddos防护_python实现的防DDoS脚本
  17. CentOS 7.6使用Percona XtraBackup 2.4备份恢复MySQL 5.7
  18. Tivoli Storage Manager安装配置
  19. 漫谈大数据 - 实时数据仓库以及大厂实际应用
  20. 中国工程师如何获 Google 的工作机会?

热门文章

  1. Segmentation Fault 错误原因总结及解决方法
  2. ORACLE ORA错误码大全 (备忘)
  3. 此beta版已额满_日志MIUI 11 第439周开发版内测日志补充
  4. python实例练习(12)身体质量指数BMI计算
  5. 文档级关系抽取:A Densely Connected Criss-Cross Attention Network for Document-level Relation Extraction
  6. 文读懂安防视频监控系统中H.265、SVAC、GB/T28181、ONVIF、PSIA的区别。
  7. matlab 可见度和衬噪比
  8. 微软雅黑字体包替换XP的宋体(附下载)
  9. 洛谷 - P3374 树状数组1
  10. 三方协议服务器不填,毕业生三方协议可以不填么