android crash自动化分析

什么是CrashCrash就是由于代码异常而导致App非正常退出现象,也就是我们常说的“崩溃”


Android中有哪些类型Crash

通常情况下有以下两种类型Crash

分为Java Crash、Native Crash


首先来探讨下Java Crash

Java Crash在Android上的特点

1. 这类错误一般是由Java层代码触发的

2. 一般情况下程序出错时会弹出提示框,JVM虚拟机退出

3. 一般的Crash工具都能够捕获,系统也提供了API


举个例子:




通过上面代码可以知道Button并没有被实例化,也就是没有通过id绑定到layout布局中的控件,那在程序运行的时候就会出现空指针异常Java.lang.NullPointerException

 


 

通过Crash堆栈信息定位问题


上面就是一个很简单的Crash啦,相信很多同学在开发过程中一定遇到过这种情况,万恶的空指针啊,啊,啊。我们来看看logcat控制台输出给我们输出的堆栈信息




从堆栈信息很快就定位到程序的14行有个空对象



 

我们通过adb shell logcat 获取的信息




这种在开发过程中调试运行很简单,那么产品上线后怎么办呢,通过UncaughtExceptionHandler来记录dump异常日志,具体代码这里就不贴上了,网上一大堆。这是正对开发者的,在代码中使用该API可以记录异常日志,通过网络将日志传回自己的服务器进行分析。但是对于测试人员呢,除了查看监控平台,或者在产品发布之前可以在测试过程中使用开发工具的logcat控制台,或者是adb shell logcat工具,使用这些工具是很容易的,但是当logcat日志中的信息很多,比如程序中埋点log第三方applog等,这样分析起来既耗时又耗力,那么有没有一种方法可以将异常的日志从一大堆日志中分离出来呢,方案是肯定有的,我们看到当异常发生的时候产生的日志与普通的日志是有区别的,而不同的异常日志又按照相同的规律去做记录,那么就可以根据这一规律进行分离。






信息包含了时间,进程名,进程id以及发生的异常体和普通的日志。提到自动化分析,不得不提一款测试工具,MoonLight,工具中的Logcat模块就根据这一规律将异常日志从日志流中分离出来得到单独的文件,那么我们就可以通过查看这个文件,就能快速的知道发生日志的内容,以及是由哪一个进程产生的。那么如何将异常日志做分离处理呢,我们来描述一下其原理,很简单,通过adb shell logcat 获取logcat输出流,我们首先根据正则表达式"^\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}.\\d{3}\\s.*"来判断当前的输出流是否包含了时间并且输出流的长度小于10240&&大于0,如果没有包含时间即作为子数据处理,其他的将舍弃。


然后将分离出的日志单独保存在文件中,这样logcat日志自动化分离就完成了,测试人员在测试的过程中也不用去查看那么长的日志文件了。或许你用这样的方式去做logcat日志分离的时候,你会发现有很多异常并不是自己app导致的也被分离出来了,这个时候可以通过进程名进行过滤。但这样并不推荐android系统是一个整体,可能你的app某些操作导致了第三方app应用崩溃也不是不可能,所以尽可能的去分析分离出来的日志。

 

上面介绍完android Crash java层的Crash现在来讲述Native层发生的日志,java层的crash通常可以使用crash根据捕捉到,讲述了java层的crash特点,那么native层的crash的特点又是什么呢?如何判断程序Crash是因为Native层导致的?我们怎么去分析它?


Native Crash在Android上的特点

1. 出错时界面不会弹出提示框提醒程序崩溃(Android 5.0以下)

2. 出错时会弹出提示框提醒程序崩溃(Android 5.0以上)

3. 程序会直接闪退到系统桌面

4. 这类错误一般是由C++层代码错误引起的

5. 绝大部分Crash工具不能够捕获


我们在实际Android开发的时候,可能会引入第三方的一些so库或者自己开发相应的so库供程序使用,然而so库一般是通过c或者c++开发的。Android开发者通过java层的JNI机制调用Native语言写的函数,然而Natice语言也可以调用java层的函数。


Java层定义native方法:



 

普通的java方法一样,只是多了native关键字修饰

JNI层实现Native方法




Java层调用



 

Native Crash如何分析?在分析JavaCrash的时候是通过logcat日志找到对应的出错代码,然而NativeCrash也是可以logcat日志来进行分析的




从上面的日志中我们根本看不到异常原因是啥,这个出错信息是我们调用native函数时打印出来的日志,只是简单的描述出错信号,出错地址还有进程号,我们可以在android studio的控制台中添加过滤就可以得到以下信息




进程信息:pid表示进程号,tid表示线程号,name表示进程名


- 错误信号:signal 11表示信号的数字,SIGSEGV表示信号的名字,code 1SEGV_MAPERR)表示出错代码,fault addr 00000000 表示出错的地址。


- 寄存器快照:进程收到错误信号时保存下来的寄存器快照,一共有15个寄存器。


- 堆栈信息:##00表示栈顶,##01调用#00,以此往下都是嵌套的调用关系,直至到栈顶。




引起nativecrash原因很多,通过上面日志的方式还是不能准确的像java层一样定位到异常是由什么地方产生的,并且nativeCrash复现的难度大,需要手机rootnative层的crash日志保存在/data/tombstones目录,并不是所有的手机都是root的,这样对收集nativecrash有很大的难度,然而一次偶然的机会,听到一款开源工具google breakpad,它是一个非常实用的跨平台的崩溃转储和分析模块,他支持WindowsLinuxMacSolarisandroid基于linux,如图:naitive层在图中红线区域




那么就可以基于google breakpad实时抓取线上Native crashdmp日志,并对dmp日志进行上传、解析、分类、聚合的过程。


Breakpad有三个主要组件:


1.Client 需要包含到应用程序中,它可以获取当前线程的状态和当前加载的可执行文件和共享库的ID写转储文件,控制程序发送Native崩溃时,产生minidump文件。


2.Symbol dumper读取由编译器产生的调试信息,并生成一个使用Breakpad格式的符号文件。


3.minidump processor 读取minidump文件,根据相应的符号文件,解析产生了一个可读的C/C++堆栈


基于breakpadUncaughtExceptionHandler或者shell执行adblogcat我们就可以做个监控app crash的线上监控模块。



Dmp解析参照http://blog.csdn.net/brook0344/article/details/20126351