Android Native程序crash的一些定位方法简介

经常,避免不了,我们的代码会崩溃。如果crash在native代码上,Android会和其他Linux一样,生成一份core dump,将程序运行时的内存,寄存器状态,堆栈指针,内存管理信息以及各种函数调用堆栈信息等存到一个文件中,供调试者使用分析。

Core Dump的生成

Android的Core dump叫tombstone,墓碑文件。由专门的一个daemon服务debuggerd来搜集,保存到/data/tombstone/目录下[logcat中也会打印一份简要信息,基本够分析用]。一般生成过程为:

  1. 系统异常,内核丢出来signal信号
  2. 应用中,bionic中实现的默认signal处理过程,通过socket将pid/tid和异常退出信息,发给debuggerd进程
  3. debuggerd得到pid/tid信息,通过ptrace挂到异常进程,得到异常进程的各种信息,然后生成墓碑文件,保存起来。

debuggerd的具体实现和ptrace就不展开讲了,以前研究挂钩子和benchmark分析处理的时候,参考了这里不少代码。有兴趣同学可以好好学习一下这个,也可以玩出花来的。比如以前做老化测试,就是改了捕鱼达人的金钱,脚本一直放炮整周末自动老化,不过现在好多apk都防着ptrace了,你想调试它它就自杀,越来越没意思了…

Tombstone文件的基本信息:

Tombstone信息通常由如下几部分组成:
1. 系统信息
包括编译信息、版本号、进程信息等。
2. 异常信息
包括产生的异常的原因,如signal 11(即段错误,内存访问异常)、异常所在位置等。
3. 寄存器信息
Dump当前线程栈中的寄存器值,包括通用寄存器和浮点寄存器的值。
4. backtrace信息
Dump当前进程栈中的调用栈。这个信息对异常分析非常有用。Backtrace信息已经做过符号定位和demangle,PC地址已经减去基地址,基本可以直接和so文件中的偏移地址对应。
5. 栈帧数据
以ascii形式,dump当前线程栈的栈帧信息。
6. 通用寄存器周边数据信息
通用寄存器很可能存储的是指针等信息,Android的tombstone会将通用寄存器周边数据也dump出来供分析。
7. 进程的logcat信息
Tombstone中还会dump在发生异常前,该进程输出的logcat信息。

其中,3,4,5,6,7每个线程信息都有对应信息。也不展开详说了,基本玩Android的都见过,除了墓碑文件中保存,在dropbox下,以及logcat的DEBUG TAG打出来的log中也有。

错误大体定位

简单说一下tombstone文件的阅读方式,大体定位错误方法。
一般对tombstone文件,除了知道是哪个进程挂了外,我们首要要知道的,是下面画圈圈的几个信息:

第一个,是signum,一般debuggerd关注的是SIGILL,SIGBUS,SIGABRT,SIGFPE,SIGSEGV,SIGPIPE等。而这里,估计九成都是SIGSEGV (即signal 11),段错误,和非法内存访问等价。
第二个是sigcode。对于段错误来说,sigcode一般就两个:SEGV_MAPERR和SEGV_ACCERR,字面意思,一个是map错误,一个是访问错误。
第三个是错误地址。

简单定位

错误九成以上都是段错误,主要关注这个,其他像非法指令、浮点出错等,也可以分析一下,一般要么是代码越界改写,要么是系统硬件不稳定造成的
对于段错误,基本上大多是SEGV_MAPERR。先说一下段错误的两种类型:
SEGV_MAPERR:这个说明访问出错的地址,压根就没内map到进程地址空间来,这种情况,通常就是野指针,或者越界访问,当然访问空指针也是属于这类。
SEGV_ACCERR:这个说明访问出错地址,被map到地址空间来了,但是没访问权限。基本上是指针越界或野指针,比如写只读map的内存地址。
这里的tombstone中的错误地址,就是非法操作的地址。现在Adroid已经使用了ALSR(地址空间随机布局),错误地址也一般不容易猜出大约哪里出问题,不过有一类地址,还是比较容易猜出来的:即地址的值比较小,基本上都是空指针访问了。地址值是小于4K的地址,可以认为就是解引用空指针了。

根据错误地址,错误类型,再通过tombstone给出的backtrace信息,使用编译时带符号的那份库,用addr2line查PC,直接得到出错的行号,结合错误原因,基本上可以定位错误了,修复即可。

几个错误例子

SEGV_MAPERR,错误的虚拟地址。怀疑野指针或越界。可以通过addr2line定位到行号。
一般我们看backtrace,并不是死板的看栈帧最顶上的。一般最顶上的都是C库,C库通常是被调用的底层函数库,不会做太多容错处理,一般认为是调用它的code给出的参数等有问题,再回溯几个栈帧看一下。这里很明显可以看到,是栈帧号3的地方出问题了,调用的是realloc,可能是越界或野指针,错的代码在这:

SharedBuffer* SharedBuffer::editResize(size_t newSize) const{    if (onlyOwner()) {        SharedBuffer* buf = const_cast(this);        if (buf->mSize == newSize) return buf;        buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);  //错在这        if (buf != NULL) {            buf->mSize = newSize;            return buf;        }    }    SharedBuffer* sb = alloc(newSize);    if (sb) {        const size_t mySize = mSize;        memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);        release();    }    return sb;    }

看一下代码逻辑,基本先怀疑传入的参数newSize有异常,加一些trace,复现看看。


Dalvik虚拟机中有空指针, addr2line看一下具体位置,再看上下文查逻辑


这种错误,已经报说heap有异常,corrupt了。一般是有人越界写,或者double free都有可能,这个要查。很多时候还不是必现的,主要查一下临界区的保护问题。


主动自杀abort的,这个查到代码调用的地方,看一下逻辑就好。其实tombstone上,这类问题,同时会打印abort的原因,解决就好。

更多相关文章

  1. 万字长文带你了解最常用的开源 Squid 代理服务器
  2. android log 分析(一)
  3. Android之DDMS初探
  4. 给Android封装的一个简单网络请求框架
  5. gradle常用命令和查看错误
  6. Android(安卓)导入android源码有错,R.java文件不能自动生成解决方
  7. 开发手机刷机工具箱的过程
  8. Android(安卓)adb网络连接Offline和 adb断开连接
  9. Android(安卓)的20个超强应用

随机推荐

  1. Android待调研基础知识
  2. Android安装或者卸载应用APK
  3. Android单元测试-javaeye
  4. Debug native code using addr2line on A
  5. Android Loader详解
  6. 自定义Android editText
  7. android各种触摸事件的处理,touchEvent
  8. Android之日期及时间选择对话框
  9. Android 基本控件
  10. Android 动态布局