返回顶部
首页 > 资讯 > 移动开发 >捕获与解析Android NativeCrash
  • 661
分享到

捕获与解析Android NativeCrash

2024-04-02 19:04:59 661人浏览 八月长安
摘要

目录一、NE 简介1.1、so 组成1.2、查看 so 状态1.3、获取 strip 和未被 strip 的 so二、NE 捕获与解析2.1、loGCat捕获2.2、通过DropBo

一、NE 简介

NE全称 NativeException,就是C或者c++运行过程中产生的错误,NE不同于普通的 Java 错误,普通的logcat无法直接还原成可阅读的堆栈,一般没有源码也无法调试。

所以日常应用层的工程师,即使我们内部有云诊断的日志,一般也会忽略NE的错误,那么遇到这些问题,作为应用层、对C++不甚了解的工程师能否解决还原堆栈,能否快速定位或者解决NE的问题呢?

下面将着重介绍:

1.1、so 组成

我们先了解一下 so 的组成,一个完整的 so 由C代码加一些 debug 信息组成,这些debug信息会记录 so 中所有方法的对照表,就是方法名和其偏移地址的对应表,也叫做符号表,这种 so 也叫做未 strip 的,通常体积会比较大。

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

如下图所示:

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

如果对 NE 或者 so 不了解的,可以简单将这个debug信息理解为Java代码混淆中的mapping文件,只有拥有这个mapping文件才能进行堆栈分析。

如果堆栈信息丢了,基本上堆栈无法还原,问题也无法解决。

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

1.2、查看 so 状态

事实上,也可以通过命令行来查看 so 的状态,Mac下使用 file 命令即可,在命令返回值里面可以查看到so的一些基本信息。

如下图所示,stripped代表是没有debug信息的so,with debug_info, not stripped代表携带debug信息的so。

file libbreakpad-core-s.so

libbreakpad-core-s.so: *******, BuildID[sha1]=54ad86d708f4dc0926ad220b098d2a9e71da235a, stripped

file libbreakpad-core.so

libbreakpad-core.so: ******, BuildID[sha1]=54ad86d708f4dc0926ad220b098d2a9e71da235a, with debug_info, not stripped

如果你是 windows系统的话,那么我劝你装一个 linux子系统,然后在 Linux执行同样的命令,同样也可以得到该信息。

接下来看下我们如何获取两种状态下的so。

1.3、获取 strip 和未被 strip 的 so

目前Android Studio无论是使用mk或者Cmake编译的方式都会同时输出strip和未strip的so,如下图是Cmake编译so产生的两个对应的so。

strip之前的so路径:build/intermediates/transfORMs/mergeJniLibs

strip之后的so路径:build/intermediates/transforms/stripDebugSymbol

另外也可以通过Android SDK提供的工具aarch64-linux-android-strip手动进行strip,aarch64-linux-android-strip这个工具位于/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains目录下。

这个工具有多种版本,主要针对不同的手机CPU架构,如果不知道手机的CPU架构,可以连接手机使用以下命令查看:

adb shell cat /proc/cpuinfo

Processor   : AArch64 Processor rev 12 (aarch64)

如上图可以看到我的手机CPU用的是aarch64,所以使用aarch64对应的工具aarch64-linux-android-strip,由于NDK提供了很多工具,后续都依照此原则使用即可:

aarch64架构

/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-strip

arm架构

/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-android-strip

使用如下命令可以直接将debug的so进行strip

aarch64-linux-android-strip --strip-all libbreakpad-core.so

使用Cmake进行编译的时候,可以增加如下命令,可以直接编译出strip的so

#set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")

#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")

使用mk文件进行编译的时候,可以增加如下命令,也可以直接编译出strip的so

-fvisibility=hidden

二、NE 捕获与解析

NE解析顾名思议就是堆栈解析,当然所有的前提就是需要保存一份带符号表、也就是未被strip的so,如果你只有strip之后的so,那就无能为力了,堆栈基本无法还原了。

一般有以下三种方式可以捕获和还原堆栈。

2.1、logcat捕获

顾名思义,就是通过logcat进行捕获,我们通过Android Studio打开logcat,制造一个NE,只能看到很多类似#00 pc 00000000000161a0的符号,并没有一个可以直接阅读的日志,我们想通过logcat直接输出一份可以直接阅读的log。

可以使用Android/SDK/NDK下面提供的一个工具ndk-stack,它可以直接将NE输出的log解析为可阅读的日志。

ndk-stack一般是位于ndk的工具下面,Mac下的地址为

/Users/XXXX/Library/Android/sdk/ndk/21.3.6528147/ndk-stack

然后在该目录下执行控制台命令,或者在 Android Studio的terminal中执行也可

adb shell logcat | androidsdk绝对路径/ndk-stack -sym so所在目录

如此控制台在应用发生NE的时候便会输出如下日志,由日志可以看出,崩溃对应的so以及对应的方法名,如果有c的源码,那么就很容易定位问题。

promote:~ njvivo$ adb shell logcat | ndk-stack -sym libbreakpad-core.so

********** Crash dump: **********

Build fingerprint: 'vivo/PD1809/PD1809:8.1.0/OPM1.171019.026/compil04252203:user/release-keys'

#00 0x00000000000161a0 /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/lib/arm64/libbreakpad-core.so (Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16)

#01 0x00000000000090cc /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/oat/arm64/base.odex (offset 0x9000)

Crash dump is completed

其实ndk-stack这个工具原理就是内部集成利用了addr2line来实时解析堆栈并且显示在控制台中。

看到这里有的小伙伴就觉得那这个不是很简单,但是实际的崩溃场景一是不容易复现,二是用户的场景有时候很难模拟,那么线上的NE崩溃又该如何监测和定位呢,有两种方式。

2.2、通过DropBox日志解析--适用于系统应用

这个很简单,DropBox会记录JE,NE,ANR的各种日志,只需要将DropBox下面的日志传上来即可进行分析解决,下面贴上一份日志示例。

解析方案1:

借助上述的ndk-stack工具,可以直接将DropBox下面的日志解析成堆栈,从中可以看出,崩溃在breakpad.cpp第111行的Crash()方法中。

ndk-stack -sym /Users/njvivo/Desktop/NE -dump data_app_native_crash@1605531663898.txt

********** Crash dump: **********

Build fingerprint: 'vivo/PD1809/PD1809:8.1.0/OPM1.171019.026/compil04252203:user/release-keys'

#00 0x00000000000161a0 /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/lib/arm64/libbreakpad-core.so (Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16)

Crash()

/Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:111:8

Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo

/Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:122:0

#01 0x00000000000090cc /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/oat/arm64/base.odex (offset 0x9000)

Crash dump is completed

解析方案2:

还是利用Android/SDK/NDK提供的工具linux-android-addr2line,这个工具位于/Users/njvivo/Library/Android/sdk/ndk目录下,有两个版本。

aarch64架构

/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line

arm架构

/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line

命令使用方法如下,结合未被strip的so以及日志里面出现的堆栈符号00000000000161a0,同样可以解析出崩溃地址和方法

aarch64-linux-android-addr2line -f -C -e libbreakpad-core.so 00000000000161a0

Crash()

/Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:111

基于以上,看似也很简单,但是有一个致命的问题就是DropBox只有系统应用能访问,非系统应用根本拿不到日志,那么,非系统应用该怎么办呢?

2.3、通过BreakPad捕获解析--适用于所有应用

非系统应用可以通过Google提供的开源工具BreakPad进行监测分析,CrashSDK也是采用的此种方式,可以实时监听到NE的发生,并且记录相关的文件, 从而可以将崩溃和相应的应用崩溃时的启动、场景等结合起来上报。

下面简单介绍一下BreakPad的使用方式。

2.3.1、BreakPad的实现功能

BreakPad主要提供两个个功能,NE的监听和回调,生成minidump文件,也就是dmp结尾的文件,另外提供两个工具,符号表工具和堆栈还原工具。

  • 符号表工具:用于从so中提取出debug信息,获取到堆栈对应的符号表。
  • 堆栈还原工具:用于将BreakPad生成的dump文件还原成符号,也就是堆栈偏移值。

这两个工具会在编译BreakPad源码的时候产生。

编译完之后会产生minidump_stackwalk工具,有些同学不想编译的话,Android Studio本身也提供了这个工具。

这个minidump_stackwalk程序在Android Studio的目录下面也存在,可以拿出来直接使用,如果不想编译的话,直接到该目录下面取即可,Mac路径为:

/Applications/Android Studio.app/Contents/bin/lldb/bin/minidump_stackwalk

2.3.2、BreakPad的捕获原理

由上述可以得知,BreakPad在应用发生NE崩溃时,可以将NE对应的minidump文件写入到本地,同时会回调给应用层,应用层可以针对本次崩溃做一些处理,达到捕获统计的作用,后续将minidump文件上传之后结合minidump_stackwalk以及addr2line工具可以还原出实际堆栈,示意图如下:

在应用发生NE时,BreakPad会在手机本地生成一个dump文件,如图所示:

得到了以上文件,我们只能知道应用发生了NE,但是这些文件其实是不可读的,需要解析这些文件。

下面着重讲一下如何分析上面产生的NE:

2.3.3、解析dump文件

1、获取NE崩溃的dump文件,将刚才得到的minidump_stackwalk和dump文件放在同一个目录,也可以不放,填写路径的时候填写绝对路径即可。

然后在该目录下的终端窗口执行以下命令,该命令表示用minidump_stackwalk解析dump文件,解析后的信息输出到当前目录下的crashLog.txt文件。

./minidump_stackwalk xxxxxxxx.dmp >crashLog.txt

2、执行完之后,minidump_stackwalk会将NE的相关信息写到crashLog.txt里面,详细信息如图所示:

3、根据解析出的NE信息,关注图中红框,可以得知,这个崩溃发生的libbreakpad-core.so里面,0x161a0代表崩溃发生在相对根位置偏移161a0的位置

2.3.4、获取崩溃堆栈

1、利用之前提到的addr2line工具,可以根据发生Crash的so文件以及偏移地址(0x161a0)可以得出产生crash的方法、行数和调用堆栈关系。

2、在其根目录对的终端窗口运行以下命令。

arm-linux-androideabi-addr2line -C -f -e ${SOPATH} ${Address}

-C -f           //打印错误行数所在的函数名称

-e                //打印错误地址的对应路径及行数

${SOPATH}         //so库路径

${Address}        //需要转换的堆栈错误信息地址,可以添加多个,但是中间要用空格隔开,例如:0x161a0

3、如下图是真实运行的示例

aarch64-linux-android-addr2line -f -C -e libbreakpad-core.so 0x161a0

Crash()

/Users/njvivo/Documents/project/Breakpad/breakpad-build/src/main/cpp/breakpad.cpp:111

由上图可以知道,该崩溃发生在breakpad.cpp文件的第111行,函数名是Crash(),与真实的文件一致,崩溃代码如下:


void Crash() {
    volatile int *a = (int *) (NULL);
    *a = 1; //此处在代码里是111行
}
  
extern "C"
JNIEXPORT void JNICALL
Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo(JNIEnv *env, jobject instance,
                                                        jstring mLaunchInfoStr_) {
  
  
    DO_TRY
    {
        Crash();
        const char *mLaunchInfoStr = env->GetStringUTFChars(mLaunchInfoStr_, 0);
        launch_info = (char *) mLaunchInfoStr;
//        env->ReleaseStringUTFChars(mLaunchInfoStr_, mLaunchInfoStr);
    }
    DO_CATCH("updateLaunchInfo");
  
}

基于以上,便可以通过应用收集的dump文件解析的NE的详细堆栈信息。

三、so符号表的提取

3.1、提取 so 的符号表

通过以上内容,我们知道,so中包含了一些debug信息,又叫做符号表,那么我们如何将这些debug信息单独剥离出来呢,ndk也给我们提供了相关的工具。

aarch64架构

/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-objdump

arm架构

/Users/njvivo/Library/Android/sdk/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-android-objdump

如下是命令运行的方式,通过此命令,可以将so中的debug信息提取到文件中。

promote:~ njvivo$ aarch64-linux-android-objdump -S libbreakpad-core.so > breakpad.asm

3.2、符号表分析

3.2.1、直接分析

如下图所示就是输出的符号表文件,结合上面的log以及下面的符号表文件,我们同样可以分析出堆栈。

如log中所示,已经表明了崩溃地址是161a0,而161a0对应的代码是*a=1,由上面的分析我们已经知道该崩溃是在breakpad.cpp的111行,也就是*a=1的位置,完全符合预期。

backtrace:

#00 pc 00000000000161a0 /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/lib/arm64/libbreakpad-core.so (Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16)

#01 pc 00000000000090cc /data/app/com.android.necase-lEp0warh8FqicyY1YqGXXA==/oat/arm64/base.odex (offset 0x9000)

3.2.2、工具解析

google提供了一个python的工具,将符号表和log结合起来可以直接分析出堆栈

执行命令,就可以解析出相关堆栈,该工具可以用于服务端批量进行解析,此处不再详细说明。

Python parse_stack.py <asm-file> <logcat-file>

3.2.3、偏移位置简析

上面文章提到了一个偏移位置的概念,笔者对此了解也不多,不过大致有一个概念,C代码有一个根位置的代码的,每行代码相对根代码都有一个偏移位置。

如上图示例log中有一行语句(Java_com_online_breakpad_BreakpadInit_nUpdateLaunchInfo+16),+16就是代表相对nUpdateLaunchInfo方法的位置往后偏移16。

由上图可以看到,nUpdateLaunchInfo方法的位置是16190,偏移16,也就是16190+10(10进制的16转化16进制后为10)=161a0,同日志输出的一样。

四、总结

以上就是本篇文章的所有内容,主要简述了so的一些基础知识,以及Android中NE的崩溃,捕获解析方案,希望通过该文档对涉及到NE相关的小伙伴带来帮助,同时后续CrashSDK也会支持相关NE的解析功能。

以上就是捕获与解析Android NativeCrash的详细内容,更多关于Android NativeCrash的资料请关注编程网其它相关文章!

--结束END--

本文标题: 捕获与解析Android NativeCrash

本文链接: https://lsjlt.com/news/128497.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 捕获与解析Android NativeCrash
    目录一、NE 简介1.1、so 组成1.2、查看 so 状态1.3、获取 strip 和未被 strip 的 so二、NE 捕获与解析2.1、logcat捕获2.2、通过DropBo...
    99+
    2024-04-02
  • iOS block的值捕获与指针捕获详解
    目录指针与指针变量block捕获变量方式值捕获指针捕获__block修饰的变量关于block延伸的知识点总结指针与指针变量 通俗的理解: 指针:内存地址指针变量:存放内存地址的变量指...
    99+
    2024-04-02
  • Android性能优化之捕获javacrash示例解析
    目录背景java层crash由来为什么java层异常会导致crash捕获crash总结背景 crash一直是影响app稳定性的大头,同时在随着项目逐渐迭代,复杂性越来越提高的同时,由...
    99+
    2024-04-02
  • JavaScript 事件捕获冒泡与捕获详情
    目录一、事件流1、概念2、DOM事件流二、事件委托1、事件委托的优点2、事件委托的使用三、禁止事件冒泡与捕获四、参考文献一、事件流 JavaScript中,事件流指的是DOM事件流。...
    99+
    2024-04-02
  • iOS block值捕获与指针捕获的方法
    本文小编为大家详细介绍“iOS block值捕获与指针捕获的方法”,内容详细,步骤清晰,细节处理妥当,希望这篇“iOS block值捕获与指针捕获的方法”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知...
    99+
    2023-06-29
  • Android 捕获运行时异常详解
    Android 捕获运行时异常详解Android 异常分为两类:CheckedException 和 UnCheckedExceptionCheckException:在编译代码时就需要进行try()catch捕获的。UnCheckExce...
    99+
    2023-05-31
    android 捕获 异常
  • JavaScript中捕获与冒泡的示例分析
    小编给大家分享一下JavaScript中捕获与冒泡的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!JavaScript中...
    99+
    2024-04-02
  • Android 全局异常捕获实例详解
    Android 全局异常捕获今天就来说说作为程序猿的我们每天都会遇到的东西bug,出bug不可怕可怕的是没有出bug时的堆栈信息,那么对于bug的信息收集就显得尤为重要了,一般用第三方bugly或者友盟等等都能轻易收集,但是由于公司不让使用...
    99+
    2023-05-31
    android 全局 异常捕获
  • 浅析js中事件冒泡与事件捕获
    目录01-事件冒泡1.1-事件冒泡介绍1.2-事件冒泡利用(事件委托)1.3-事件冒泡影响 与 阻止事件冒泡02-事件捕获1.1-事件捕获介绍1.2-事件三个阶段01-事件冒泡 1...
    99+
    2024-04-02
  • Android 记录未捕获异常
    文章目录一、CrashHandler二、初始化三、测试四、打印 stackTrace 一、CrashHandler 自定义 Crash 处理器:...
    99+
    2022-06-06
    异常 捕获 Android
  • Android adb shell命令捕获systemtrace
    Android adb shell命令捕获systemtrace   (1)抓取trace文件: adb shell perfetto -o /data/misc/perfetto-traces/trace_file.perfetto-tr...
    99+
    2023-09-08
    android adb
  • python正则捕获日志解析实例
       去年工作中的一个实例,觉得较有意思,由于实例需求较繁琐也不太典型,我只能稍作整理和修改后,和大家分享整个案例的需求以及我写脚本的思路和想法,希望对大家有参考的价值。      大概需求:主站有个js文件记录用户设备和IP信息以及在...
    99+
    2023-01-31
    正则 实例 日志
  • JavaScript中事件与异常捕获的示例分析
    小编给大家分享一下JavaScript中事件与异常捕获的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!事件处理【onCl...
    99+
    2024-04-02
  • 故障诊断的利器:异常捕获与分析
    异常捕获和分析是软件开发中的关键工具,可帮助开发人员识别并解决程序中的错误。通过及时检测和报告异常,开发人员可以快速采取行动,防止错误升级为严重问题。 异常捕获 异常是程序运行时发生的意外事件,它会中断正常的执行流程。异常可以由各种因素触...
    99+
    2024-04-02
  • wireshark捕获过滤器语法使用解析
    目录指定捕获过滤器基于类型过滤主机host网段net端口port端口范围基于传输方向的过滤广播和多播的区别广播的优点:广播的缺点:组播:组播的优点:组播的缺点:基于协议过滤基于数据过...
    99+
    2024-04-02
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩...
    99+
    2022-06-06
    异常 方法 捕获 Android
  • 在Linux中使用tcpdump命令捕获与分析数据包详解
    前言 tcpdump 是一个有名的命令行数据包分析工具。我们可以使用 tcpdump 命令捕获实时 TCP/IP 数据包,这些数据包也可以保存到文件中。之后这些捕获的数据包可以通过 tcpdump 命令进行分析。tcpd...
    99+
    2022-06-04
    linux抓包命令tcpdump linux tcpdump linux tcpdump 抓包
  • JavaScript事件捕获冒泡分析
    本篇内容主要讲解“JavaScript事件捕获冒泡分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript事件捕获冒泡分析”吧!一、事件流JavaScript中,事件流指的是DOM...
    99+
    2023-06-25
  • golang错误捕获源码分析
    这篇文章主要介绍“golang错误捕获源码分析”,在日常操作中,相信很多人在golang错误捕获源码分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”golang错误捕获源码分析”的疑惑有所帮助!接下来,请跟...
    99+
    2023-07-06
  • js事件冒泡与事件捕获的示例分析
    这篇文章给大家分享的是有关js事件冒泡与事件捕获的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。(一)事件绑定1.普通事件绑定给html添加一个以on开头的特定的属性(如...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作