文章目录

  • Android Native报错定位
    • 源码部分:
    • 日志分析
      • 线索一
      • 线索二
    • addr2line使用
      • 位置
      • 使用
      • 实战addr2line出现“??:?”
      • 小结
    • android studio编译的库
    • 其它
    • 总结

Android Native报错定位

今天调试Android stagefright模块,修改MediaCodec.cpp文件时,一不小心在代码里写了个空指针进去。

于是得到了下面这个报错日志:

--------- beginning of crash
libc    : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 5104 (MediaCodec_loop)
DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
DEBUG   : Build fingerprint: '*******************************************'
DEBUG   : Revision: '0'
DEBUG   : ABI: 'arm'
DEBUG   : pid: 5050, tid: 5104, name: MediaCodec_loop  >>> com.google.android.exoplayer2.demo <<<
DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
DEBUG   : Cause: null pointer dereference
DEBUG   :     r0 ********  r1 ********  r2 ********  r3 ********
DEBUG   :     r4 ********  r5 ********  r6 ********  r7 ********
DEBUG   :     r8 ********  r9 ********  sl ********  fp ********
DEBUG   :
DEBUG   : backtrace:
DEBUG   :     #00 pc 00018e90  /system/lib/libc.so (__memcpy_base+185)
DEBUG   :     #01 pc 000446e9  /system/lib/libc.so (__sfvwrite+310)
DEBUG   :     #02 pc 00044943  /system/lib/libc.so (fwrite+122)
DEBUG   :     #03 pc 000dc169  /system/lib/libstagefright.so (_ZN7android10MediaCodec21onReleaseOutputBufferERKNS_2spINS_8AMessageEEE+1432)
DEBUG   :     #04 pc 000d7a59  /system/lib/libstagefright.so (_ZN7android10MediaCodec17onMessageReceivedERKNS_2spINS_8AMessageEEE+156)

想着平时分析日志时,经常会遇到这样的打印,因为看不懂,通常都是直接绕过。索性今天悠闲,清扫一下知识上的战争迷雾吧。

源码部分:

status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {// 这是我添加的代码 也是报空指针的代码,代码在MediaCodec.cpp文件中的3077行size_t count = fwrite(heliBuf, sizeof(heliBuf) , 1, fp);// *****************
}

日志分析

简单看看logcat,能够提取到Cause: null pointer dereference

我们知道,是一个空指针异常导致了crash。

那么这个空指针是在哪一行代码调用的呢?

可以看到backtrace:下面有crash时的调用栈信息。格式为:

#编号 pc 异常地址 共享库路径 (函数名)

例1.0:

#00 pc 00018e90 /system/lib/libc.so (__memcpy_base+185)

所以,示例1.0,可以翻译为:在共享库 /system/lib/libc.so的*__memcpy_base*函数中报了一个空指针。

logcat中打印的调用顺序,都是从下到上看的。例如,这里是先调用#04这一行,再调用#03… ,最后调用#00。所以,严格的说,#00这一行,是本次报错的代码。

但通常,我们编码都会直接或间接的使用系统现有库,而这些库久经沙场,基本不会有什么问题,如果#00编号的调用指向的是一个系统库,不要犹豫,一定是你非法调用了它。

线索一

回到backtrace,#00、#01、#02行的共享库都是libc.so,这就是一个系统库。在#02行的函数名中,能够得知是调用fwrite时出现了异常。

线索二

#03 pc 000dc169 /system/lib/libstagefright.so (_ZN7android10MediaCodec21onReleaseOutputBufferERKNS_2spINS_8AMessageEEE+1432)

从这里,终于看到了我修改的代码所属模块:libstagefright.so

MediaCodec.cpp 代码目录就位于:/frameworks/av/media/libstagefright目录下

这里能给我们提供的信息有:

  1. 异常出现在libstagefright.so库中。

  2. 异常出现在_ZN7android10MediaCodec21onReleaseOutputBufferERKNS_2spINS_8AMessageEEE函数中。

从第二点那串乱码中,大致已经可以看到发生异常的函数名称了。但为什么是乱码!!!

google了一把,原来编译器在编译时,对函数名做了一定优化。

既然是编译器的事情,那一定是有一定规则,想想这规则应该也不会简单。有没有简单的方法,能把这堆乱码还原?

当然有,国外有大兄弟做了个网站专门“翻译”这堆乱码:https://demangler.com/

DEMANGLE 了一下,得到了这串乱码的实际函数名:android::MediaCodec::onReleaseOutputBuffer(android::sp<android::AMessage> const&)

)

直白的表明了,这是MediaCodec.cpp中的onReleaseOutputBuffer函数。xia! xia!

这个时候,我选择去茶水间,冲一杯…好吧,我没有咖啡。

让我们来理一理:

  • 异常出现在libstagefright.so库中

  • 异常是一个空指针。

  • 发生在fwrite调用中。

  • fwrite又被onReleaseOutputBuffer调用。

离真相已经很近了。我去代码里看了一下onReleaseOutputBuffer函数,居然只有我插入的代码里调用了fwrite。

为了让文档继续下去,我们来假装onReleaseOutputBuffer函数中,有无数的fwrite调用、或者报错的是一个第三方库,我们并不知道源码。怎么知道报错的代码,具体在文件的哪一行呢?

addr2line使用

从名字上就可以看出,这玩意儿是将一个地址转换成行号的工具。

位置

如果你有在用Android Studio,那么恭喜你,可以很方便在自带NDK目录下找到它。

比如我的是这样:

\Android\Sdk\ndk-bundle\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin\arm-linux-androideabi-addr2line.exe

没有装Android Studio的同学,可以自行到官网下载NKD,解压后就可以看到了,路径类似。

https://developer.android.com/ndk/downloads?hl=zh-cn

使用

arm-linux-androideabi-addr2line [option(s)] [addr(s)]

简单来说就是arm-linux-androideabi-addr2line + 可选项 + 异常地址

先来看看可选项都有哪些:

[option(s)] 介绍
@ 从文件中读取options(暂时不研究)
-a 在结果中显示地址addr(ps:没啥用,因为这个地址本来就是我们输入命令中的)
-b 设置二进制文件的格式(暂不研究)
-e 设置输入文件(常用:选项后面需要跟报错的共享库,用于addr2line程序分析)
-i unwind inline function(暂不研究)
-j Read section-relative offsets instead of addresses(暂不研究)
-p 让输出更易读(亲测,没啥区别,可能其它选项已经很易懂了)
-s 在输出中,剥离文件夹名称(这个还不错,前面有一长串目录确实恶心)
-f 显示函数名称(也比较有用,可以直观的看出是在哪个函数中报的错)
-C(大写的) 将输出的函数名demangle(类似于demangler.com这个网站的功能)
-h 输出帮助
-v 输出版本信息

那么多选项,在这里能用上(也是常用)的,也就是 -e、 -s、-C、-f了。

实战addr2line出现“??:?”

铺垫了那么多,是时候试验一下了:

C:\SuperLi>arm-linux-androideabi-addr2line -e \system\lib\libstagefright.so -s -f -C 000dc169
_ZN7android10MediaCodec21onReleaseOutputBufferERKNS_2spINS_8AMessageEEE
??:?

eum~ 出现:_ZN7android10MediaCodec17onMessageReceivedERKNS_2spINS_8AMessageEEE,加个-C选项再试试:

C:\SuperLi>arm-linux-androideabi-addr2line -e \system\lib\libstagefright.so -s -f -C 000dc169
android::MediaCodec::onReleaseOutputBuffer(android::sp<android::AMessage> const&)
??:?

??:? 虾米,看到这个问号,我不由想起黑人问号的表情包。

只能google了:

在使用 addr2line 过程中经常会遇到 “??:?” 或 “??:0” 这种情况,原因就是一般 C/C++ SDK 都会进行添加 map 混淆,在编译配置选项中不生成符号表 symbolic 信息,不过 AndroidStudio 会默认为 so 文件添加符号表。

也就是说,我使用了一个不带符号表的库。

google说了,如果是aosp编译的话,在out/target/product/[productname]/symbols/system/lib/****.so下面会自动生成带了符号表的共享库。找了找,真的有。于是我改了一下addr2line输入文件的位置:

C:\SuperLi>arm-linux-androideabi-addr2line -e \symbols\system\lib\libstagefright.so -s -f -C 000dc169
fwrite(void const*, unsigned int pass_object_size0, unsigned int, __sFILE*)
stdio.h:375

Bingo!!!成功的去掉了讨厌的黑人问号(??:?)。但是为什么函数名称不一样了?(哪位大神知道原因,求解惑啊)索性把和libstagefright.so相关的addr都加进来看看吧:

C:\SuperLi>arm-linux-androideabi-addr2line -e \symbols\system\lib\libstagefright.so -s -f -C 000dc169
fwrite(void const*, unsigned int pass_object_size0, unsigned int, __sFILE*)
stdio.h:375
android::MediaCodec::onMessageReceived(android::sp<android::AMessage> const&)
MediaCodec.cpp:2508

结果中可以看出,报错的地方在MediaCodec.cpp文件的2508行。

推了推鼻梁上不存在的眼镜,嘴角扯出一抹弧度:小样,我找到你了。

然鹅,在MediaCodec.cpp文件中ctrl + g 了一把。我擦,2508行代码居然只是这个:

 我是2508行:   status_t err = onReleaseOutputBuffer(msg);

而我们真实报错的地方是在3077行。

因为fwrite在onReleaseOutputBuffer函数中只有一处调用,我们能通过阅读onReleaseOutputBuffer函数的代码,将异常锁定在3077行,但这只是妥协的方法。如果onReleaseOutputBuffer函数中有无数的fwrite怎么搞?暂时能想到的只有加log一步步调试了。

好吧,目前也只能做到这一步了。

小结

1、通过实验addrline + symbolic版本的so,只能将异常定位到库中的函数级别,而不能具体到行号

2、要精准定位,还需要结合上下文才能确定。

那么,所有的库都需要这么麻烦吗?

准备使用Android studio来测试一下。

android studio编译的库

打开Android studio,通过File > New > New Project > Native C++ … 创建一个Test 工程。

在自动生成的native-lib.cpp 文件的Java_com_dali_myapplication_MainActivity_stringFromJNI函数中,故意加入了一个a = b / c的除零异常。

native-lib.cpp 代码如下:

#include <jni.h>
#include <string>
using namespace std;extern "C" JNIEXPORT jstring JNICALL
Java_com_dali_myapplication_MainActivity_stringFromJNI(JNIEnv *env,jobject /* this */) {string hello = "Hello from C++";int b = 3, c = 0;int a = b / c;return env->NewStringUTF(hello.c_str());
}

Activity代码如下:

public class MainActivity extends AppCompatActivity {static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView tv = findViewById(R.id.sample_text);tv.setText(stringFromJNI());}public native String stringFromJNI();
}

运行后,立马报错,关键日志如下:

 DEBUG   : signal 8 (SIGFPE), code -6 (SI_TKILL), fault addr -------- DEBUG   :     r0 00000000  r1 000016aa  r2 00000008  r3 8efbcbe4 DEBUG   :     r4 8efbcbe4  r5 00000001  r6 00000000  r7 0000010cDEBUG   :     r8 0000004e  r9 8dc10000  sl 9eadb790  fp 8dc10000DEBUG   :     ip 9eadb768  sp 9eadb6c8  lr 79a1ee9b  pc 8ef4c964  cpsr 00000010 DEBUG   :                                       DEBUG   : backtrace:DEBUG   :     #00 pc 0004a964  /system/lib/libc.so (tgkill+12) DEBUG   :     #01 pc 0001ce97  /data/app/com.dali.myapplication-H8qoqUb6cjv0hUDfGYRtRQ==/lib/arm/libnative-lib.soDEBUG   :     #02 pc 000064e1  /data/app/com.dali.myapplication-H8qoqUb6cjv0hUDfGYRtRQ==/lib/arm/libnative-lib.so (Java_com_dali_myapplication_MainActivity_stringFromJNI+108)

使用addr2line运行一下:

C:\SuperLi>arm-linux-androideabi-addr2line -e libnative-lib.so 000064e1 -s
native-lib.cpp:11

可以看到,Android studio中编译出来的so,完全没有各种困扰,很直接的指出的异常发生的行号。

原因是,Android studio会在编译时,自动生成附带有符号表的so。

其它

在对应库的编译目录下的Android.mk文件中,增加gcc警告和调试标志:

LOCAL_CFLAGS := -Wl,-Map=test.map -g

可能有不一样的效果,为啥说不一样的效果呢?因为俺们没验证,暂时感觉没需求。

总结

1、使用addr2line需要附带符号表的so文件。

2、addr2line工具并不是一定能将异常位置定位到行,还需要其它调试手段辅助。

3、如果c/c++库程序是使用Android studio开发,因为生成的so自带符号表,所以使用addr2line工具可以直接定位。

Android Native报错定位(addr2line工具的使用)相关推荐

  1. Android各种报错问题汇总

    一.Android调试中报错:Could not connect to remote process. Aborting debug session. 在AndroidManifext.xml中的添加 ...

  2. android override报错,android @override 报错解决方案

    android @override 报错解决方案 现象: ... java: 1801: method does not override a method from its superclass @ ...

  3. 打开Android Studio报错required plugin “Android Support” is disabled

    打开Android Studio报错 Plugin Error: required plugin "Android Support" is disabled 更详细的错误信息如下: ...

  4. the android emulator process,Android studio报错:The emulator process for AVD (xxx) was killed

    背景:重做系统后重新配置Android studio 安装虚拟机后无法启动 log中显示为启动AVD的进程被杀 控制台显示为:在虚拟机列表里没有找到所要启动的AVD的名称 首先考虑是BIOS问题:发现 ...

  5. Android Studio报错: Alternatively, to transfer the license agreements from one workstation to another

    1.Android Studio报错:Alternatively, to transfer the license agreements from one workstation to another ...

  6. Android Sutiod报错:Dx unsupported class file version 52.0(解决)

    一.Android Stuido报错 其实Dx unsupported class file version 52.0和Conversion to Dalvik format failed with ...

  7. Eclipse新建Android项目报错解决方案详细汇总

    本文记录刚接触Android开发搭建环境后新建工程各种可能的报错,并亲身经历漫长的解决过程(╥╯^╰╥),寻找各种偏方,避免大家采坑,希望能帮助到大家. 报错信息 出错一:The import and ...

  8. Android Studio报错:E/EGL_emulation: tid 3197: eglSurfaceAttrib(1199): error 0x3009 (EGL_BAD_MATCH)

    @[toc](Android Studio报错:E/EGL_emulation: tid 3197: eglSurfaceAttrib(1199): error 0x3009 (EGL_BAD_MAT ...

  9. Android程序报错:Unable to add window——token android.os.BinderProxy@431e65b8 is not valid

    http://blog.csdn.net/lvyoujt/article/details/51604746 http://blog.csdn.net/lvyoujt/article/details/5 ...

  10. 快速解决Android编译报错 : Manifest merger failed with multiple errors, see logs

    快速解决Android编译报错 : Manifest merger failed with multiple errors, see logs 编译项目的时候,遇到Android Manifest合并 ...

最新文章

  1. Nginx proxy_cache 使用示例
  2. 奇点云发布三大无人零售终端产品,“云”“端”结合赋能零售商
  3. Windows Messenger 5.1 [Download from Microsoft]
  4. Newtonsoft.Json(Json.net)的基本用法
  5. 给程序媛妹子的4个贴心建议
  6. c#結合正則表達式驗證輸入的字符串
  7. CodeForces - 1330D Dreamoon Likes Sequences(组合数学)
  8. 给初级拍摄者的十条好建议
  9. 安装ubuntu出现花屏_电脑显卡花屏怎么办?电脑独立显卡花屏的原因和解决方法...
  10. QTableWidget与QTableView的区别
  11. Android应用程序消息处理机制
  12. 运维之我的docker-Dockerfile构建镜像详情
  13. 中消协:要选择优质电子显示产品 OLED屏整体上略胜一筹
  14. 7种形式的Android Dialog使用举例
  15. Java中的指针---Unsafe应用解析
  16. 儒豹手机搜索发布2008年各频道关键词排行榜
  17. 【STM32H7的DSP教程】第48章 STM32H7的中值滤波器实现,适合噪声和脉冲过滤(支持逐个数据的实时滤波)
  18. 如何在html中插入表情包,一款支持插入表情的编辑器实现代码(简单思路挺重要)...
  19. word插入图片被文字挡住
  20. luogu P5867 【[SEERC2018]Fishermen】

热门文章

  1. 判断一个数是否为质数(素数)的4种方法
  2. 数学分析习题课讲义习题-第2章-2.4
  3. java毕业答辩会问什么,java毕业设计答辩(详细流程介绍)
  4. Java生成随机数的几种方式
  5. 达梦(DM8)数据库实例
  6. 利用t-SNE可视化Glove向量
  7. 数字信号处理——多速率信号处理
  8. 数字信号处理原理及实现一书的思维导图
  9. CSS之BFC(Block Formatting Context)
  10. c++语言题库,计算机二级c++题库软件下载