Android Crash 定位

本文介绍了如何在 Android 手机发生 Crash 时进行 Log 分析的方法,
它可以帮助测试人员快速定位 Android 手机 Crash 发生的原因,同时给研发人员提供有效修改 Bug 的 Log 信息。

用自动化测试工具对 Android 手机进行压力测试和稳定性测试,往往通过模拟实际使用场景中所发生的业务量来测试手机性能是否满足要求,测试过程中系统状态信息被实时记录到 Log 文件中,通过对 Log 信息的分析,可以检查到错误发生的根源和运行痕迹,因此,有效利用 Log 信息并对其进行分析与实时的监控管理,对于分析 Android 手机发生 Crash 的原因具有极为重要的作用。

自动化测试工具在运行过程中会产生多种类型的 Log 信息,而这些 Log 信息又往往分布在不同的目录中,在超大规模的 Log 数据上做分析不是一件容易的事情,需要测试人员分析大量的脚本文件和代码去寻找自己所关心的答案。基于以上问题,本文介绍了如何进行 Log 分析的方法,有了这些方法会让 Crash 的问题定位变得简单,并让复杂的分析变得可行。

1. Android Log 文件类型

由于 Android 上的应用程序千差万别,出现的问题也不尽相同。不过 Bug类型还是有规律可循的,可以根据生成的 Log 文件找到相应的错误,通常错误信息里记录了错误的大致位置,据此可以捕获到问题的关键信息。

Log 文件记录着每次操作的信息,在出现问题后可以借助 log 信息分析以达到解决问题的目的,Log 文件类型主要分为以下几种:

(1) Logcat: Main 缓存日志,通过运行 logcat 命令,可以获得系统中使用的标记和优先级的列表,也可以加上过滤器进行表达式限制,只输出测试人员及研发人员感兴趣的标记-优先级组合。
Logcat-Kernel: Kernel(Linux)信息,包括多个进程并发信息,进程所使用的内存情况,进程访问磁盘的请求等信息。
Logcat-Radio: Radio And Telephoney 信息,面向手机基本信息提供的 api,包括网络类型、连接状态、电话功能、电话号码字符串处理的实用程序等。
Logcat-Event: 系统级别的 Events,比如:垃圾回收,Activity 管理状态,系统的 Watchdogs 和其他底层 Activity;

(2) Bugreport: Java 应用程序 Crash 时会产生一个 Bugreport 文件,该文件主要包括三个方面的内容:
Dumpstate:内存信息,Cpu 信息,Procrank 信息,系统日志,Vm Trace 信息等。
Build.Prop:当前版本、当前命令、显示系统 Build 的一些属性等;
Dumpsys:Dump Of Service Meminfo(显示某个进程更详细的内存消耗情况以及 Native And Java (Dalvik)堆栈的统计数) ;

(3) Crashdump: 每次 Crash 都会产生一个 Crashdump 文件,文件包括主日志,Java 堆栈信息,本地调用堆栈,虚拟机/进程堆,Log 缓存,内存信息,进程列表,Modem 信息,Adb Log 等信息;

(4) Bratlog: 测试用例及详细信息;

(5) Logalong: 事件,如手机通讯功能信息等;

(6) Pullfs: Traces(Java 堆栈信息);

(7) Procrank: Uss(Unique Set Size) 值,进程独自占用的物理内存。

Android Crash 类型

程序员开发 android 应用程序时追逐的最终目标是要尽量避免程序 Crash 的发生。但是现实的情况是,在进行轮番测试和验证的时候,Android 应用程序几乎不可能完全杜绝 Crash 的发生。Crash 的类型主要有以下几种:

(1) Java Application Crash:不完善的 Java 应用程序很容易导致 Crash 的发生如:某个应用程序由于源代码不完善,会做一些它本不应该做的操作而导致 Crash的发生。 另外,Java 虚拟机自身的 Bug、系统的库文件、Api、第三方的库文件、系统资源的短缺都有可能造成 Java Crash 的发生。

(2) System Crash:当程序试图访问不被允许访问的内存区域、指针越界、错误的访问类型、访问不存在的内存、访问不属于进程地址空间的内存、栈溢出、函数非法跳转、非法的系统调用、数据中断等操作容易引起 System Crash。

(3) Modem Crash:当手机出现无信号、死机、网络中断等现象,通常会出现 Modem Crash。

(4) Kernel Crash:由于手机应用软件出现错误而导致系统崩溃的时候,会提示 Kernel Crash 的信息,并且错误发生时 kernel 的存储图像会保存起来,当系统重新启动后,会恢复 kernel 的存储图像,然后根据现象判断是哪类错误发生。

(5) Watch Dog Crash:当手机发生 Crash 时系统停止运行,说明预先设定的Watch Dog 发生了 Crash。

Android Crash 问题定位方法

3.1 Java Application Crash

3.1.1 Java Anr Crash

手机应用程序在预先设定的时间内不响应或响应不够灵敏,系统会向用户显示一个对话框,这个对话框被称作 anr(Application No Response) Crash。这一般都是由于应用程序错误导致的,测试人员可以在 Logcat 文件中定位是哪个进程导致 anr 的发生,为什么会发生 anr,发生 anr 之前的历史日志,事件响应间隔中Cpu 的使用状态等。

当出现 Anr Crash 后,测试人员可以在 Log 文件里查找关键字:Anr,定位到的相应的关键信息,然后根据定位的具体信息再进行分析。下述示例说明了造成了 anr Crash 的三种情况:

(1) Keydispatchingtimedout:测试人员在 Log 文件里查找关键字:Anr,定位到的关键信息如下所示:

11-02 13:00:45.878  1200    1211 I Activitymanager: Anr In Process: Com.*******.Camera
(Com. *******.Camera/Com. *******.Camera.Video.Videocamera)
11-02 13:00:45.878  1200    1211 I Activitymanager: Annotation: Keydispatchingtimedout
11-02 13:00:45.878  1200    1211 I Activitymanager: Cpu Usage:
11-02 13:00:45.878  1200    1211 I Activitymanager: Load: 11.09 / 11.24 / 10.73
11-02 13:00:45.878  1200    1211 I Activitymanager: Cpu Usage From 7739ms To 2694ms
Ago:
11-02 13:00:45.878  1200    1211 I Activitymanager: System_Server: 17% = 9% User +
7% Kernel / Faults: 149 Minor
11-02 13:00:45.878  1200    1211 I Activitymanager: Adbd: 2% = 0% User + 2% Kernel
11-02 13:00:45.878  1200    1211 I Activitymanager: Mediaserver: 1% = 0% User + 0%
Kernel
11-02 13:00:45.878  1200    1211 I Activitymanager: Logcat: 0% = 0% User + 0% Kernel
11-02 13:00:45.878  1200    1211 I Activitymanager: M.Android.Phone: 0% = 0% User +
0% Kernel / Faults: 15 Minor
11-02 13:00:45.878  1200    1211 I Activitymanager: Total: 22% = 12% User + 10% Kernel
……
11-02 13:00:46.128  1832    1832 D Jcrashcatcher: Create Bugreport
Instance(/Sdcard/Bugreport/Bugreport-004402140726351-20111102-130045)
User: The System Spent In User Mode, User Mode In Low Priority
Kernel:The System Spent In System Mode.
Iowait:The System Spent For I/O Wait
,……………..

以 上 代 码 中 com.*.Camera 应 用 程 序 包 下 的 com.
*.Camera.Video.Videocamera 出现了 keydispatchingtimedout Anr,表示输入事件(比如:按手机键盘,触摸屏幕等操作)在预设时间之内(通常是 10 秒)无响应,当这种情况发生时,在目录/Sdcard/Bugreport/下会生成一个以当前日期命
名的文本文件,例如文件名:Bugreport-004402140726351-20111102-130045,此文本文件里记录了发生 Crash 时的多个 I/O 操作,但具体是哪个主线程引起的Crash,还需要我们做进一步的分析。通过在生成的 Bugreport 文件中搜索关键字:Dalvik Threads,可以定位到本应用程序的虚拟机信息。

----- Pid 1368 At 2011-11-16 21:39:00
Cmd Line: Com.*******.Camera
Dalvik Threads:
"Main" Prio=5 Tid=3 Vmwait
| Group="Main" Scount=1 Dscount=0 S=N Obj=0x40026240 Self=0xbda8
| Systid=1368 Nice=0 Sched=0/0 Cgrp=Unknown Handle=-1344001376
At Dalvik.System.Vmruntime.Trackexternalallocation(Native Method)
At Android.Graphics.Bitmap.Nativecreate(Native Method)
At Com.Android.Camera. Video.Videocamera (Bitmap.Java:468)
At Android.View.View.Builddrawingcache(View.Java:6324)
At Android.View.View.Getdrawingcache(View.Java:6178)
At Android.View.Viewgroup.Drawchild(Viewgroup.Java:1541)
At Android.View.Viewgroup.Dispatchdraw(Viewgroup.Java:1343)
At Android.Widget.Abslistview.Dispatchdraw(Abslistview.Java:1474)
At Android.Widget.Listview.Dispatchdraw(Listview.Java:2978)
At Android.View.View.Draw(View.Java:6733)
At Android.Widget.Abslistview.Draw(Abslistview.Java:2327)
At Android.View.Viewgroup.Drawchild(Viewgroup.Java:1616)
At Android.View.Viewgroup.Dispatchdraw(Viewgroup.Java:1343)
At Android.View.Viewgroup.Drawchild(Viewgroup.Java:1614)
At Android.View.Viewgroup.Dispatchdraw(Viewgroup.Java:1343)
At Android.View.View.Draw(View.Java:6630)

从 上 面 的 代 码 中 可 以 看 到 关 键 问 题 出 现 在 java 代 码 的 468 行 : At
Com.Android.Camera. Video.Videocamera (Bitmap.Java:468)
至此,测试人员和研发人员就可以在 eclipse 开发环境中相应的 java 代码块
中去定位问题类型,然后去进一步寻找解决问题的具体方法。

(2) 在 Log 文件里查找关键字:Anr,定位到的关键信息如下所示:

[ 11-26 23:01:57.169    8353:0x20ac W/Activitymanager ]
Timeout Of Broadcast Broadcastrecord{45ec73c8 Android.Intent.Action.Media_Mounted} -
Receiver=Android.Os.Binderproxy@45f76d38
[ 11-26 23:01:57.169    8353:0x20ac W/Activitymanager ]
Receiver During Timeout:
Resolveinfo{45e24348    Com.Android.Providers.Media.Mediascannerreceiver    P=0 O=0
M=0x208000}
[ 11-26 23:01:57.231    8353:0x20ac I/Activitymanager ]
Anr In Process: Android.Process.Media
Annotation: Broadcast   Of  Intent  {   Act=Android.Intent.Action.Media_Mounted
Dat=File:///Sdcard Cmp=Com.Android.Providers.Media/.Mediascannerreceiver (Has Extras) }
Cpu Usage:
Load: 10.11 / 8.02 / 6.84
……

上 述 代 码 说 明 com.Android.Providers.Media 应 用 程 序 包 下 的
mediascannerreceiver 发生了 broadcast Timeout Anr,广播事件在 20 秒内未完成执
行。当这种情况发生时,在目录/Sdcard/Bugreport/下会生成一个以当前日期命名
的文本文件,测试人员可用类似于 keydispatchingtimedout Anr 的思路去找到问题
的所在,也可以在目录/Data/Anr/Traces.txt 下查找 Crash Log 的关键信息,最终
定位到 Java 代码行中。

(3) 在 log 文件里查找关键字 Anr,定位到的关键信息类似如下代码:

10-01   05:01:42.938: Warn/Activitymanager(1149): Timeout Executing Service:
Servicerecord{45c06118com.*******.Android.Debug.Jcrasher/.Jcrasherservice}
10-01   05:01:42.948:   Info/Activitymanager(1149): Anr In  Process:
Com.*******.Android.Debug.Jcrasher
10-01   05:01:42.948:   Info/Activitymanager(1149): Annotation: Executing   Service
omponentinfo{Com.*******.Android.Debug.Jcrasher/Com.*******.Android.Debug.Jcrashe
r.Jcrasherservice}
……

以 上 代 码 说 明 com.*.Android.Debug.Jcrasher 应 用 程 序 包 下 的Jcrasherservice 发生了 Service Timeout Anr,程序对一个服务进程未完全响应。可以在/Sdcard/Bugreport/下生成的文本文件或者在/Data/Anr/Traces.Txt 文件中找到问题所在。

3.1.2 Java Uncaught Exception

还有其他一些 Java Crash 由 Java Uncaught 异常引起。对于这些异常的产生原因,又有多种情况,以下列出常见的四种:

(1)在 Bugreport 文件里查找关键字:Uncaught Exception,定位到的关键

01-01 00:19:45.764: Error/Androidruntime(2251): Uncaught Handler: Thread Main Exiting Due
To Uncaught Exception
00:19:45.764:Error/Androidruntime(2251):Java.Lang.Nullpointerexception
01-01   00:19:45.764:   Error/Androidruntime(2251): At
om.*******.Android.Debug.Jcrasher.Jcrasheractivity.Causeexception(Jcrasheractivity.Java:86
……

信息类似如下代码:
以上内容说明问题是由 java.Lang.Nullpointerexception 引起的主线程异常退出所造成。一般情况下,Uncaught 异常发生在 androidruntime,还有很多原因使得系统杀死应用程序进程,Uncaught 异常时 sig 数值一般是 3,象以下代码中显示 Sig 数值为 9,可以断定这不是一个 uncaught Exception Crash。

At Android.Os.Binderproxy.Senddeathnotice(Binder.Java:353)
At Dalvik.System.Nativestart.Run(Native Method)
[ 12-08 04:05:35.451 1189:0x4a9 I/Process ]
Sending Signal. Pid: 30392 Sig: 9

(2)在 Bugreport 文件里查找关键字:Uncaught Exception,定位到的关键信息类似如下代码:

11-07 17:19:05.733 18621 18621 W Dalvikvm: Threadid=3: Thread Exiting With Uncaught
Exception (Group=0x4001e160)
11-07 17:19:05.733 18621 18621 E Androidruntime: Uncaught Handler: Thread Main Exiting
Due To Uncaught Exception
11-07 17:19:05.743 18621 18621 E Androidruntime: Java.Lang.Illegalstateexception: The
Content Of The Adapter Has Changed But Listview Did Not Receive A Notification. Make
Sure The Content Of Your Adapter Is Not Modified From A Background Thread, But Only
From The Ui Thread. [In Listview(16908298, Class Android.Widget.Listview) With
Adapter(Class Android.Widget.Headerviewlistadapter)]
……

以上代码说明 crash 是由 java.Lang.Illegalstateexception 引起。根据相应的应用程序包可以准确定位到 java 的代码行中。

(3)Java Heap Leak 内存泄漏。如果应用程序内存超过了所允许的限度

(24m)而导致了泄漏,在 Log 文件里会出现如下代码:

890 22613 22613 W Dalvikvm: Threadid=3: Thread Exiting With Uncaught Exception
(Group=0x40023160)
11-24 20:40:10.890 22613 22613 E Androidruntime: Uncaught Handler: Thread Main Exiting
Due To Uncaught Exception
11-24 20:40:10.900 22613 22613 E Androidruntime: Java.Lang.Outofmemoryerror: Bitmap
Size Exceeds Vm Budget
……

(4)native Heap Leak 内存泄漏。在应用程序占用的内存超过分配的限度

(Gref)(2000). Native Crash 时,异常会发生,这时会在 log 文件里出现如下类似代码:

01-01 00:05:55.414 D/Dalvikvm( 1330): Gref Has Increased To 2001
01-01 00:05:55.414 W/Dalvikvm( 1330): Last 10 Entries In Jni Global Reference Table:
00:05:55.414 W/Dalvikvm( 1330): 1991: 0x457b2510
Cls=Landroid/Database/Cursortobulkcursoradaptor; (44 Bytes)
……

需要说明的是 Procrank 文件可以捕获当前系统中各进程的内存使用情况,其中的 uss 值非常重要,如果 uss 值总是上升而没有下降,说明容易发生内存泄露,如果 uss 有时上升然后会下降到一个正常值,说明将不会有内存泄露发生如图 1 所示:

Android Crash 定位_第1张图片
图 1 Uss 波动图

3.2 System Crash

3.2.1 Sigsegv

Sigsegv: Segmentation Fault 意味着进程执行了一个无效的内存引用,没有物理内存对应该地址,程序可能抛出 sigsegv 异常,会在/Data/Tombstone/下找到这些信息。

(1) 搜索关键字 Sigsegv,如果定位到类似下面代码,说明发生了 data Abort异 常 , 若 发 生 这 类 问 题 , 一 般 都 是 深 层 的 so 在 调 用 时 由 于 深 层 代 码 的nullpointerreference 所导致。

01-01   00:41:52.751:   Info/Debug(1061):   Build   Fingerprint:
'Semc/X10i_1232-9897/X10i/Es209ra:2.1-Update1/2.0.A.0.468/1afa:Eng/Release-Keys'
01-01   00:41:52.751:   Info/Debug(1061):   Pid:    1896,   Tid:    1896    >>>
Com.*******.Android.Debug.Jcrasher <<<
01-01 00:41:52.751: Info/Debug(1061): Signal 11 (Sigsegv), Fault Addr Deadcafe
00:41:52.751: Info/Debug(1061): R0 Deadcafa R1 00000001 R2 Afe3db7c R3
00000000
01-01 00:41:52.761: Info/Debug(1061):   #00 Pc 0000ae74 /System/Lib/Libc.So
01-01   00:41:52.761:   Info/Debug(1061):   #01 Pc  000003ba
/Data/Data/Com.*******.Android.Debug.Jcrasher/Lib/Libjnicrasher.So
……

(2) 搜索关键字 Sigsegv,如果定位于类似以下代码,说明地址 0xdeadd00d发生了 sigsegv 异常,这个地址发生了 dvmabort。

01-01   00:35:47.551:   Info/Debug(1061):   Pid:    1296,   Tid:    1305    >>>
Android.Process.Acore <<<
00:35:47.551: Info/Debug(1061): Signal 11 (Sigsegv), Fault Addr Deadd00d
……

3.2.2 Sigill

Sigill: 非法指令异常是 cpu 处理非法指令之后所发生的一个异常,程序收到了 sigill 信号,一般就报告 illegalinstruction 错误信息。下面的代码说明地址

01-01   00:44:41.246:   Info/Debug(1061):   Pid:    1914,   Tid:    1914    >>>
Com.*******.Android.Debug.Jcrasher <<<
01-01 00:44:41.246: Info/Debug(1061): Signal 4 (Sigill), Fault Addr 810003c4
00:44:41.246: Info/Debug(1061): R0 0000ab50 R1 45689080 R2 810003a8 R3
810010b0
……

810003c4 发现了非法指令。

3.3 Modem Crash

顾名思义,Modem Crash 往往是由调制解调器所引起。调制解调器可以提供快速可靠的连接,使得手机能够连接到互联网,但是有些操作可能导致 ModemCrash 的发生,如:软件版本与硬件型号不兼容。当发生 Modem Crash 时,可以在生成的 kdumppdf 文件里定位到关键信息。如以下代码说明是调制解调器发生了 crash。

07-12 17:15:01.321 <3>[ 110.120141] Arm9 Has Crashed
07-12 17:15:01.322 <6>[ 110.120283] Smem: DiagMod Gsdi:Pfault 00000 ’
07-12 17:15:01.322 <3>[ 110.121191] Smem: Crash Log
7:15:01.434 <0>[ 110.232524] Kernel Panic - Not Syncing: Modem Has
Crashed...

3.4 Kernel Crash

一旦手机发生了 kernel Crash,相关的日志信息很少,而且又很难重现,因此遇到这类问题比较难解决,比较简单的方法是:如果手机彻底被锁定,不能再使用;数字键(Num Lock)、大写锁定键(Caps Lock)、滚动锁定键(Scroll Lock)不停闪烁;手机蓝屏并且在手机屏幕上看到内核 Cp Assert 出来的信息,如出现以上任何一种现象则可以断定发生了 Kernel Crash。也可以在 Crash Dump 文件里搜索关键字:Kernel Panic,然后检查 Call Stack 信息,将会看到 Backtrace 信息。
在手机/Proc/Kpanic 目录或者 /Data/Cp_Panic.Bin 中可以找到 panic Logs(),通过 adb 工具可以导出所需要的 log 信息,需要时可以将该信息提交给研发人员。

3.5 Watch Dog Crash

如测试人员在 crash Dump 文件和 event Log 文件里的最后几行代码中看到2802 标识,意味着发生了 Watch Dog Crash。

12-15 16:36:14.076 I/Process ( 1150): Sending Signal. Pid: 1150 Sig: 3
12-15 16:36:14.076 I/Dalvikvm( 1150): Threadid=7: Reacting To Signal 3
12-15 16:36:14.058 I/[2802] ( 1150) (Or Watchdog)
Com.Android.Server.Am.Activitymanagerservice    (Or Null)

4 结论

Android 程序测试过程中会遇到很多 Crash 的问题,一般出现的问题都可以通过本文所述的这种方法查看代码得到解决。该方法抽取出易于理解,精度高的代码分析规则集,在理论上是合理的。该方法已经通过实际案例验证,可以用于解决一些比较深层的定位问题,具有一定的理论和现实意义。

你可能感兴趣的:(性能调优)