如何分析、定位Android Native Crash

http://crash.163.com/index.do#news/!newsId=2

出于执行效率、业务安全、复用已有代码的需求,目前市场上越来越多的 Android App 采用 C/C++ 来实现其关键逻辑。C/C++ 有内存管理灵活、与 linux 底层联系更紧密、多种编程范式等特点,但也正是由于这些特点,使得普通开发人员在使用 C/C++ 开发时,更容易出让进程直接崩溃的 bug。所以能分析 C/C++ 崩溃日志并能从日志中分析出原因,成为 Android 开发人员一项必备技能。本文介绍如何通过分析 Native 崩溃日志来定位出错的 C/C++ 代码及出错原因。 

一、Native 崩溃日志格式

                        extern "C" JNIEXPORT int gen_stack(int i)
{
    if (i > 2)
        return gen_stack(i - 2) + gen_stack(i - 1);
    else
    {
        int *p = NULL;
        *p = 123;
        return 1;
    }
}

当调用 gen_stack(4) 发生 Native 崩溃时,一般 logcat 会打印如下格式的日志:

#00 pc 00000c1c /data/app-lib/com.testbugrpt-1/libtestNDKCrash.so (gen_stack+27)

#00 表示堆栈序号
pc 00000c1c 表示崩溃发生时 程序计数器 位于 libtestNDKCrash.so 偏移 0xc1c 处
gen_stack+27 表示0xc1c处正好是 gen_stack 符号(此处为函数名)偏移为27的一条指令

#01 pc 00000c0f /data/app-lib/com.testbugrpt-1/libtestNDKCrash.so (gen_stack+14) 

这是第二层堆栈,表示在离 libtestNDKCrash.so 0xc0f(也就是gen_stack + 14)位置的指令发生了一次函数调用,产生了第一层堆栈。

二、Native崩溃分析工具

在介绍工具之前,先简单讲一下有调试与无调试信息的两个版本 so 。 一个含有 native 代码的 app 项目的典型结构是这样的: 


                    --jni
                        --Android.mk
                        --其它源文件
                    --libs
                        --armeabi
                        --armeabi-v7a
                        --arm64-v8a
                        ....
                    --obj
                        --local
                            --armeabi
                            --armeabi-v7a
                            --arm64-v8a
                            ....
                    

通常一次编译会先生成一个有含有调试信息的 so, 路径通常是在 obj/local/ 各 abi 目录下,其中还有一些中间文件(比如.o文件);再通过对这些含有调试信息的 so 进行一次 strip , 产生对应的无调试信息 so, 放到 libs 目录下各 abi 目录中, 发布产品时,我们都是用这些 strip 后的 so。

一般的分析崩溃日志的工具都是利用含有调试信息的 so, 结合崩溃信息,分析崩溃点在源代码中的行号。

  1. 1、ndk-stack

    ndk-stack.exe位于ndk根目录。运行以下命令:

    D:\Android\android-ndk-r10c\ndk-stack.exe -sym E:\workspace\TestBugrpt\app\src\main\obj\local\armeabi-v7a\ -dump log.txt

    其中 log.txt 为崩溃日志,可以从 monitor 中点击保存得到。或者运行:

    adb logcat | ndk-stack.exe -sym E:\workspace\TestBugrpt\app\src\main\obj\local\armeabi-v7a\

    这样再运行程序,当崩溃发生时,ndk-stack.exe 会自动从 logcat 中获取崩溃日志。

    运行以上命令时,要 注意 -sym 参数指示的路径都是 obj\local\ 目录,同时要匹配对应机器的 abi 目录。可以得到:

    表明gen_stack + 27对应testNDKCrash.cpp的第13行,即*p = 123; 查看对应的源代码,可以发现是此处的写空指针导致崩溃。

  2. 2、addr2line

    addr2line 一般位于 android-ndk-r10c\toolchains\arm-linux-androideabi-4.9\prebuilt\windows\bin\ ,其路径与文件名因操作系统、 abi 不同而有所不同。

    可以运行如下命令:

    arm-linux-androideabi-addr2line.exe -e E:\workspace\TestBugrpt\app\src\main\obj\local\armeabi-v7a\libtestNDKCrash.so 00000c1c 00000c0f

    与 ndk-stack 不同的是,ndk-stack 接受一个 obj/local/abi 目录为参数,而 addr2line 接受 local 下一个具体的 so 文件路径为参数。其中 00000c1c 00000c0f 就是上面第一节中分析的崩溃点离libtestNDKCrash.so的偏移量,即

    得到输出:

    E:/workspace/TestBugrpt/app/src/main//jni/testNDKCrash.cpp:13

    E:/workspace/TestBugrpt/app/src/main//jni/testNDKCrash.cpp:9

    分别对应两个偏移在源码中的位置。

  3. 3、objdump

    上面两种工具都是将崩溃点对应到源码再进行分析,objdump 则是可以在汇编层对崩溃原因进行分析。当然这要求开发人员了解一些 arm/x86 汇编知识。

    objdump 也是 ndk 自带的一个工具,通常与 addr2line 在同一目录。运行如下命令:

    arm-linux-androideabi-objdump.exe -S -D E:\workspace\TestBugrpt\app\src\main\obj\local\armeabi-v7a\libtestNDKCrash.so > e:\dump.txt

    由于输出比较多,将输出重定位到 e:\\dump.txt 便于查看。打开 dump.txt , 定位到 00000c1c :

                                int *p = NULL;
                                *p = 123;
    
                                c16: 2300 movs r3, #0
                                c18: 227b movs r2, #123 ; 0x7b
                                c1a: 1c68 adds r0, r5, #1
                                c1c: 601a str r2, [r3, #0]

    上面两句是源代码,下是对应的Arm汇编。

    如果要分析的 so 没有调试信息, ndk-stack 与 addr2line 就无能为力了,只有 objdump 还能派上用场。当然,这种情况下有更好用的工具,比如 IDA Pro。不过那又是另外一个故事了。

三、常见崩溃类型及原因

  1. 1、SIGSEGV 段错误
    SEGV_MAPERR 要访问的地址没有映射到内存空间。 比如上面对空指针的写操作, 当指针被意外复写为一个较小的数值时。
    SEGV_ACCERR 访问的地址没有权限。比如试图对代码段进行写操作。
  2. 2、SIGFPE 浮点错误,一般发生在算术运行出错时。
    FPE_INTDIV 除以0
    FPE_INTOVE 整数溢出
  3. 3、SIGBUS 总线错误
    BUS_ADRALN 地址对齐出错。arm cpu比x86 cpu 要求更严格的对齐机制,所以在 arm cpu 机器中比较常见。
  4. 4、SIGILL 发生这种错误一般是由于某处内存被意外改写了。
    ILL_ILLOPC 非法的指令操作码
    ILL_ILLOPN 非法的指令操作数
  5. 5、当调用堆栈中出现 stack_chk_fail 函数时,一般是由于比如 strcpy 之类的函数调用将栈上的内容覆盖,而引起栈检查失败。

更多信号信息请参考文献 [1]。 

四、线上Native崩溃处理

对于第二节中的分析方法,前提是可以得到 Native 层崩溃日志。由于 Android 设备的碎片化,必然存在在测试时覆盖不到的机型。如果 App 在用户机器上发生了崩溃,如何获取 Native 崩溃日志?

目前网易云捕已经实现了对 Java、Native 层崩溃日志的获取,并能自动上传到服务器进行分析。具体功能及接入方法请参考网易云捕集成说明


参考文献:
[1] http://man7.org/linux/man-pages/man2/sigaction.2.html
[2] http://blog.csdn.net/xyang81/article/details/42319789
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android原生崩溃(android native crash)是指在Android平台上,由于代码执行错误或者资源耗尽等原因,导致应用程序无法正常运行而崩溃或者闪退的现象。原生崩溃产生的原因可能是由于C或者C++代码编写错误、内存溢出、线程竞争等。针对原生崩溃问题,开发人员需要使用调试工具进行定位和修复。 ### 回答2: Android Native Crash发生在安卓应用程序运行时,由于C或C++库的错误或者其他原因导致应用程序崩溃。有时候Native Crash可能会影响整个设备,尤其是当Native Crash发生在系统级别的代码中时。 产生Native Crash的原因通常包括以下几个方面: 1. 内存管理问题:Native Crash通常与内存管理问题相关,这可能是由于访问未初始化的内存,使用错误的指针或释放已释放的内存等原因引起的。 2. 硬件问题:Native Crash也可能与设备相关的硬件问题有关,例如访问不可用的硬件资源或硬件设备故障。 3. 应用程序代码问题:Native Crash可能发生在应用程序代码的错误、资源泄漏、堆栈溢出等问题引起的。 4. 第三方库问题:Native Crash也可能由第三方库中的错误或bug引起。这些库可能没有经过充分的测试,或者与设备硬件不兼容。 为了更好地解决Native Crash问题,开发者可以通过日志或崩溃报告(Crash Report)来检测和分析崩溃日志,并查看堆栈跟踪信息来确定导致Native Crash的来源。在开发过程中,经常使用除了自己编写的代码之外的第三方库时,还可以考虑使用崩溃的回溯工具,如Firebase Crashlytics等。 总之,Native CrashAndroid应用程序开发过程中经常遇到的问题,它可能会对用户体验和开发进度产生重大影响,因此开发者需要强化对Native Crash的理解和分析能力,以更好地解决Native Crash的问题。 ### 回答3: Android Native Crash,指的是在 Android 系统中发生的本地崩溃。本地崩溃是指应用程序使用本地代码,而不是 Java 代码,导致应用程序崩溃的问题。本地代码可以是编写在 C/C++ 等语言中的库,或是应用程序本身所编写的 Native 代码。 本地代码崩溃后,会在应用程序崩溃的同时发生。本地崩溃可发生在 Android 应用程序中的任何部分,比如,应用程序本地库、Android 系统库等等。大多数情况下,本地崩溃是由于访问无效内存、访问不合法指针、数组越界等问题引起的。 为了解决本地崩溃问题,Android 提供了一些工具和技术。比如,使用 ndk-stack 工具可以解析本地崩溃日志。Android Studio 也提供了一些工具来分析应用程序崩溃的原因。同时,我们也可以在应用程序中添加自定义的日志跟踪信息,以便更好地了解应用程序的崩溃原因。 还有一些其他的技术可以使用,如使用 Google 的 Crashlytics 来跟踪应用程序的崩溃问题。这个平台可以帮助开发者收集和分析应用程序在用户设备上的崩溃信息,并彻底解决这些问题。此外,Android 还提供了一些实用工具和技术,如 ANR(Application Not Responding)错误处理器、Tracer for OpenGL ES 和 Traceview 示例等。 总之,Android Native CrashAndroid 系统中常见的崩溃问题之一。了解它的原因并采用适当的解决方案可以使得我们更好地保持我们的应用程序的稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值