之前整理过一篇linux coredump的文章,一直想把这个特性在手机上应用起来,帮助排查错误,今天终于如愿以偿,记录如下。
【1】概述
在Android系统上,java应用程序出错时很容易通过logcat获取出错信息,一般会有详细的callstack(调用栈),例如:
java.lang.NullPointerException:
atcom.android.providers.calendar.CalendarSyncAdapter.onAccountsChanged(CalendarSyncAdapter.java:1400)
atandroid.content.AbstractSyncableContentP
atandroid.accounts.AccountManager$10.run(AccountManager.java:826)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4325)
at java.lang.reflect.Method.invokeNative(Method.java:-2)
at java.lang.reflect.Method.invoke(Method.java:521)
atcom.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
atcom.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
atdalvik.system.NativeStart.main(NativeStart.java:-2)java.lang.NullPointerException:
atcom.android.providers.calendar.CalendarSyncAdapter.onAccountsChanged(CalendarSyncAdapter.java:1400)
atandroid.content.AbstractSyncableContentP
atandroid.accounts.AccountManager$10.run(AccountManager.java:826)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4325)
at java.lang.reflect.Method.invokeNative(Method.java:-2)
at java.lang.reflect.Method.invoke(Method.java:521)
atcom.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
atcom.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(NativeStart.java:-2)
该信息给出了函数调用关系及对应的源代码及行号,因此很容易解决。
但是非java程序就比较困难了,例如同样是一个空指针操作,非java程序通过logcat获取的log信息示例如下:
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
I/DEBUG
是不是很头大?
本篇文章就是探索一种方式来解决这种问题的。
【2】准备知识
先仔细阅读此篇文章:linuxcoredump 知识整理
其中的要点:
(1)使用ulimit命令开启coredump功能。
(2)修改coredump文件生成位置与名称
(3)gdb的使用方法
【3】实践
(1)adb连接手机,开启coredump
# ulimit -a
ulimit -a
time(seconds)
file(blocks)
data(kbytes)
stack(kbytes)
coredump(blocks)
memory(kbytes)
locked memory(kbytes) 64
process(processes)
nofiles(descriptors) 1024
(2)配置coredump文件生成位置与名称(没找到默认情况下放在哪里)
#echo "1" > /proc/sys/kernel/core_uses_pid
#echo "/local/log/core-%e-%p" >/proc/sys/kernel/core_pattern
把dump文件存放目录改到local/log下。
(3)示例程序
foo.c
#include <stdio.h>
static void sub(void);
int main(void)
{
sub();
return 0;
}
static void sub(void)
{
int *p = NULL;
printf("%d",*p);
}
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo.c\
include $(BUILD_EXECUTABLE)
将以上两文件放到android源码树的一个目录中,我是放到eclair/external/coredump文件下
编译(eclair目录下执行./build/envsetup.sh,然后转到coredump目录下mm命令;或者直接eclair目录下make,全编译)
android会生成两种版本的文件,一种是带符号信息的,
/homeeclair/out/target/product/generic/symbols/system/bin/foo
另一种是不带符号信息的(即strip过的)
/homeeclair/out/target/product/generic/system/bin/foo
不带符号信息的会做到system.img中去,带符号信息的我们需要保存住,以备后续调试用。
上面第二个log信息就是此程序运行的结果。
(4)运行
我们把generic/system/bin/foo文件拷贝到手机中,比如local目录下,修改权限(chmod 777foo),执行,结果如下。
#./foo
[1] + Stopped(signal)
#
[1]
#
# ls
ls
core-foo-1672
foo
etc
log
lost+found
(5)gdb调试
将core-foo-1672与generic/symbols/system/bin/foo(这个必须是带符号的)拷贝到相同目录下
运行gdb进行调试,注意这里要运行的gdb是android自带的,我这里的名称叫arm-eabi-gdb
$arm-eabi-gdb ./foo
GNU gdb 6.6
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License,and you are
welcome to change it and/or distribute copies of it under certainconditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" fordetails.
The GDB was configured as "--host=i686-unknown-linux-gnu--target=arm-elf-linux"...
(gdb)
输入core-file文件,回车
(gdb) core-file core-foo-1672
warning: core file may not match specified executable file.
Error while mapping shared library sections:
/system/bin/linker: No such file or directory.
Error while mapping shared library sections:
libc.so: Success.
Error while mapping shared library sections:
libstdc++.so: Success.
Error while mapping shared library sections:
libm.so: Success.
Symbol file not found for /system/bin/linker
Symbol file not found for libc.so
Symbol file not found for libstdc++.so
Symbol file not found for libm.so
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
Core was generated by `./foo'.
Program terminated with signal 11, Segmentation fault.
#0 0x0000836a in main () at external/coredump/foo.c:15==》看到这种信息知道该知道哪出错了把
15
(gdb)
如果函数调用关系比较复杂,可试试bt(backtrace)指令
【4】总结
上面虽然是一个小例子,但android中的其他非java可执行程序原理与此一样。
我们只需要对手机进行一定的配置,出错时就可以抓到有效的信息,然后如果对应带符号的文件没有丢失的话,就可以通过gdb精确定位到出错的位置
coredump是适用于用户空间的应用出错,对内核不适用。
经测试,java程序jni调用库文件,库文件中空指针操作,无法生成coredump。
如果可以将coredump的设置自动化的话(比如在init.rc中添加命令),还是有一定实用价值的,
所要做的就是每做一个版本的镜像时把带符号的相关文件备份一下,即可在后续出错时获取到非常有用的信息。
备注:查了下我手机init.rc中有这样的设置
# set RLIMIT_CORE to enable core dump file up to 100kB (512*)
原文地址:http://hi.baidu.com/donghaozheng/blog/item/b3f03a9b6abe16bac8eaf43d
联系客服