Unity3D 异常处理机制剖析

发表于2015-04-29
评论2 4.8k浏览

在收集Unity3D crash过程我们分2步出发,1个人负责对上线的3款产品做crash的收集和分析,一个人负责对u3d c#脚本进行扫描工作,扫描某游戏的结果如下:

 

我们在工具的指引下,用毕业生的miniGame做相关规则测试,测试项空引用、除0、内存泄漏、堆栈溢出等常异常,发现除了内存泄漏(OOM)和堆栈溢出(SOF)会导致u3d minigame crash外,其他的异常都不会导致crash, 有木有抓狂,以前的c/c++相关异常在u3d中完全不适应。

 

进一步分析(空引用、越界和除零)发现异常在u3d的编辑器中有相关信息提示,在真机上运行,logcat中也会有相应的异常LOG提示,就是不CRASH,从另外同事那分析的数据发现基本crash都是在java和native, 未能找到c#相关的丝马迹。

 

跪求度娘和谷哥才发现4.5.1版本unity3d对异常做了很好的封装, 框架层会自动捕获异常而不会导致进程的crash (OOM和 SOF不会捕获),实际游戏中我们对碰撞检查位置进行除零操作,发现脚本运行到除零后就自动结束不会再向下运行(表现上是功能不正确,但是不会导致游戏进程crash)。

 

在思考人生中突然灵感一现,既然unity3d c#脚本会主动throw异常,那么数据统计功能组件(代号:Duka是否能把异常crash并上报,接入Duka虽然简单,但是坑仍然还是有,折腾了1天半才搞定,主要原因是太懒只看了文档无视了DEMO,面壁下。

1.       注册Duka项目(以为只接入单独的Duka SDK无需RDM)

2.       注册RDM项目

3.       配置RDM相关数据

4.       引入JAR包到工程并初始化

5.       初始化JAVA端CRASH抓取

6.       初始化NATIVE CRASH抓取

 

接入后发现c#层的异常(非OOM和SOF外)仍然不会导致U3D游戏进程crash,,在RDM上并未相关的CRASH信息,为验证Duka接入的功能是否正确,分别手动在java层和native层抛出异常,在RDM上看有相应的CRASH信息,C#异常未被Duka捕获的原因还是因为unity本身对异常进行了捕获并很好的处理了异常。

 

通过GOOGLE 搜索了下Andriod的程序架构,并通过APKTOOL对unity editer生成的pkg进行反编译,结果如下:

Andriod的程序架构( google搜索):

http://km.oa.com/files/photos/captures/201409/1411462653_67.jpg

 

Unity3d miniGame 利用APKTOOL反编译结果:

http://km.oa.com/files/photos/captures/201409/1411462769_85.jpg

疑问: unity 的C#脚本是运行在java层还是Native层

经过查阅资料和动手验证发现,在解开的APK中,asserts/bin/data/managed下的所有DLL为C#脚本和相关系统API文件(后续统称脚本), 脚本是通过libmono.so来加载并执行,libmono.so为native层,所以如果由于C#脚本导致的CRASH(目前只发现2种OOM和SOF)一定会在native层。

 

疑问:unity3d 游戏c# 脚本本身异常如何定位

C#异常并不一定会导致游戏进程crash, 在实际运行过程中,只是停止当前的脚本继续向下执行, 游戏表现上是当前的功能不正确,在底层上可能会导致资源泄漏最终可能会出现OOM情况,如不能正确的处理会给游戏功能缺陷定位和OOM crash定位带来不少问题。

 

查询资料发现untiy本身由于非常好的封装了异常,可以利用u3d的日志类来很好的获取异常,并通过jni调用java异常抛出函数,在java异常抛出函数上可以做相应的日志输出和处理,如果确实需要修改的异常点可以继续抛出异常,接入Duka的项目就可以抓取相应的JAVA异常。

 

C#异常处理步骤:

1.       注册日志回调函数

Application.RegisterLogCallback(LogCallback);

2.       在日志函数中判断type哪些是异常

void LogCallback( string strLog, string stackTrace, LogType type)

3.       判断是异常后保存相关信息在脚本变量中

4.       在Update操作中调用异常上报函数

ThrowCShapException(reason,stack);

5.       在ThrowCShapException中通过JNI调用JAVA层的异常处理函数把相应的reason和stack通过给JAVA层

6.       在JAVA层实现个JAVA异常处理函数,专门处理c#上报的异常信息,JAVA异常函数可以进一步throw异常或者自己默默处理掉,进一步上报的异常就可以被Duka捕获到。

 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引