Unity中Android和Ios平台的crashreporter(崩溃报告采集与上
传)
CrashReport,这在⼤型软件开发领域是很常见的功能,就是能够当程序崩溃退出后,能够将崩溃时的信息,最好是携带dmp⽂件发送给
服务器,这样开发⼈员既可以获得分发出去的客户端的崩溃率统计,也可以针对出现的错误进⾏及时的纠正,之前在PC的端游时代,这是
很常见的做法,最近进⾏了在⼿游上的关于crashreport的相关研究,并且为项⽬编写了⼀个相对完善的CrashReport模块。
这个模块的来源于⼿游项⽬正式上线,但是很多玩家反馈闪退,但是我们只能听到反馈闪退,却不能找到原因,只能凭脑袋去猜,是不
是内存不够,机器配置太差,然后去尽可能优化性能,于是⽼⼤开始喊我们需要⼀个CrashReport,于是就花2个星期完善了⼀个可以正
式使⽤的CrashRepoter,项⽬基于Unity3D,在Android和Ios上做crashreport对我还是第⼀次,所以还是抱有了极⼤的兴趣。
1Android平台。
其实CrashReport也不应该是只有Crash了才Report,各种错误和潜在会导致Crash的问题也应该report上去。对于基于Unity3D的
Android应⽤来说,⾃底到上可以分为三层:C++,Java和C#。android是基于linux的系统,最底下的各种soa库就是C++的部
分,android系统本⾝的相关逻辑则是java,U3D则使⽤了C#开发逻辑,所以我们采集问题也要从这三块分别着⼿。
C
#c#的错误很好处理,这层U3D完全封装好了,C#层会出现warningerror和exception,在android下这⼏种情况都不会导致crash,都会
被UNITY3d接住,但是我们需要知道并报告给服务器,U3D有接⼝erLogCallback(),可以让C#层发⽣上⾯的问题
时被我知道,我们只要写这个callback,然后在⾥⾯给服务器就⾏了。
Java
java层的错误就是各种javaexception,对于java,如果对于我们catch了的exception,不会导致crash,会按照我们的catch⾏为执⾏,
对于那些我们没有catch的exception,是会crash的,还会在adblog上打印出来,我们需要获知这些exception,我们可以采⽤java中的接
⼝aultUncaughtExceptionHandler来重新设置这个对未catch的exeption的处理,在我们⾃⼰的handler中基本做的事情
就是⾸先把这个exceptio报告给服务器,然后并不让程序退出,让程序尽可能活下去。
C++
C++中出现的问题通常就是很严重的了,这⾥也分两种,⼀种是普通的⼀些异常,这取决于你是否catch了,如果没有catch,默认就是
abort的,也就是crash了,还有⼀些⽐如对内存的⾮法访问,就直接在linux中产⽣了⼀个结束信号,把进程结束了,也是crash。对于
C++我们先后尝试了两种⽅案,第⼀种就是采⽤捕获linux的信号量,程序异常退出总是有信号的,可以使⽤linux下的sigaction来设置对
这些信号的捕获处理,⽐如我们捕获了SIGILLSIGABRTSIGFPESIGSEGVSIGPIPESIGBUSSIGSTKFLT,这样对于异常的程序退出我
们是知道的,可以在下次进⼊游戏时告知服务器,但是这样做有⼀个明显的问题就是我们只是知道程序crash了,但是没有traceback,不
知道在哪挂了,我们想要dump⽂件。于是后来采取的⽅法就是使⽤了google的breakpad框架,关于googlebreakpad,这是它的主
页,/breakpad/breakpad/,关于他的基本原理,⼤家可以去看他的wiki和⽂档,很长,基本来说
它是⼀个平台⽆关的C++的crashreporter,可以在crash后,⽣成dmp⽂件,然后利⽤它的⼀些⼯具获取堆栈的符号信息。
googlebreakpad在android的简单集成⽅法如下:
1.从/svn/trunk拿到源码
2.建⽴你⾃⼰的jni⼯程
3.将googlebreakpad的android和src两个⽂件夹放到你的⼯程⾥
4.配置你的,⾥⾯要加⼊
APP_STL:=stlport_static
APP_CPPFLAGS:=-std=gnu++11-D__STDC_LIMIT_MACROS
5.配置你的,⾥⾯要加⼊以下的src⽂件
google_breakpad/src/client/linux/crash_generation/crash_generation_
google_breakpad/src/client/linux/handler/exception_
google_breakpad/src/client/linux/handler/minidump_
google_breakpad/src/client/linux/log/
google_breakpad/src/client/linux/dump_writer_common/thread_
google_breakpad/src/client/linux/dump_writer_common/ucontext_
google_breakpad/src/client/linux/microdump_writer/microdump_
google_breakpad/src/client/linux/minidump_writer/linux_
google_breakpad/src/client/linux/minidump_writer/linux_ptrace_
google_breakpad/src/client/linux/minidump_writer/minidump_
google_breakpad/src/client/minidump_file_
google_breakpad/src/common/android/breakpad_getcontext.S
google_breakpad/src/common/convert_UTF.c
google_breakpad/src/common/
google_breakpad/src/common/string_
google_breakpad/src/common/linux/
google_breakpad/src/common/linux/file_
google_breakpad/src/common/linux/guid_
google_breakpad/src/common/linux/linux_libc_
google_breakpad/src/common/linux/memory_mapped_
google_breakpad/src/common/linux/safe_
还要加⼊LOCAL_STATIC_LIBRARIES+=breakpad_client
以及includegoogle_breakpad/android/google_breakpad/
6.在你的crashreport模块初始化中(当然通常也可以在JNI_OnLoad中)初始化googlebreakpad,
google_breakpad::MinidumpDescriptordescriptor(path);
handler=newgoogle_breakpad::ExceptionHandler(descriptor,NULL,NULL,NULL,true,-1);
这⾥的path是你⼿机上存放dmp⽂件的⽂件夹,crash发⽣后,它会在这个⽂件夹内⽣成以UUID命名的dmp⽂件,当然前提你要保证这个⽂
件夹真实存在。
7.最后编出你的so库,给程序使⽤。
这样我们就通过googlebreakpad实现了C++层的dump⽂件⽣成,当发⽣后,我们把dump⽂件传给服务器就⾏了。
最后关于这个dmp⽂件的解读,这⾥⼤家可以参考这份⽂档:/developers/decoding-crash-dumps。这个
解析要在linux环境下做,没有win的⼯具,基本就是两步骤,第⼀步是⽤⼯具将dmp的⼆进制⽂件转成可以看懂的⽂本格式,可以看到出错
的地址,但是如果要想详细知道这些地址所代表的符号,还需要⽤⾥⾯的⼀个⼯具以及带有符号版本的so库才能知道,unity⾃⼰的库应该
没有这种so库,但是也能⼤致看出问题处在哪了。
其他
似乎到这⾥我们能够堵到android上所有可能崩溃的地⽅,然⽽并不是。⾄少有两种情况是还不⾏的,⼀是watchdog超时,⼆是内存资源
不⾜。
watchdog超时:当你的主线程超过⼀段时间没有相应,android系统会将你的程序退出,内存资源不⾜:当android系统认为这个程序使⽤
的内存过⾼时,会选择将这个退出,以释放内存。
这两种情况都是android系统的管理器按照⼀定的策略调度的,虽然玩家看到闪退了,但是这两种情况逻辑上都不属于异常退出,和你⾃⼰
退出android进程是⼀样的,只不过是系统帮你退出了,所以⽤googlebreakpad或者信号都不能知道,因为这其实是正常退出,但是对于
我们程序设计来说,这是异常。所以从捕获异常退出来说没有办法(当然也许真的有,我不知道,那欢迎⼤家批评指正),所以对这两种情
况我们退⽽求其次使⽤当感知到有潜在的退出危险时报告给服务器警告的策略。
对于watchdog超时,我们在java层开⼀个新的线程,不断的去探测主线程,当较长时间发现主线程没回应,我们给⼀个警告给服务器,
并带上现在的内存情况,告知服务器这台机器主线程卡住很久了,很可能⼀会就被系统退出了,但是也可能运⽓好⼀会⼜好了。这种探测的
⽅法我们可以正好⽤unity的在native层的UnitySendMessage机制,因为这个就是异步的,我们⽤另⼀个线程不断的给主线程的Unity⽤
这个发送⼼跳包,unity收到后回复,很久没回复就是主线程卡住了(原因多了,⽐如某个逻辑特别特别耗时。。。)
对于内存太⾼,我们会在程序⾥定期检测⼀下内存,当发现使⽤的内存明显⾼过我们的设计时发给服务器,⽐如说我们认为PSS内存超过
600M,当然如果有机器每到这个就崩了,那也不是我们的⽬标机型。在android系统下动态得到系统的总内和当前可⽤内存可以⽤
oryInfo(),获取当前的进程的使⽤内存的接⼝可以⽤cessMemoryInfo(newint[]
{()})
通过这两种策略我们可以预防式的得到这两种情况的⼀个crash统计。
IOS
IOS只有C++和C#两层,对于C#来说,和Android是⼀模⼀样的,不⽤多说。
对于C++这层,我们当然还是可以继续使⽤googlebreakpad,因为它是跨平台的,但是其实Unity(⾄少从4.6开始)为ios已经提供了
⼀个crashreport模块,他需要我们将⼯程⽣成好后,将Crashreport.h⾥⾯的ENABLE_CUSTOM_CRASH_REPORTER设置为1,或者
你可以直接在unity安装路径下找到这个⽂件直接改。这样在c++crash后,unity会为我们⽣存crash⽂件,等下次启动后,可以通过
crashreport这个模块访问这些dmp⽂件,这些dmp⽂件都是ios上的标准dmp⽂件,可以使⽤ios的开发⼯具symbolicatecrash来查看。unity
内置的crashreport其实也是采⽤了第三⽅的库plcrashreport来实现的,这个库在ios上的应⽤很多。
另外对于ios,其实也提供了⼀个函数NSSetUncaughtExceptionHandler,⽤来当那些未捕捉的异常发⽣时,进⼊这个处理,可以拦截⼀些
东西,但是⼀些⽐如内存访问错误直接就退出了,不会进到这⾥,另外进到这⾥之后程序还是会退出,只是让我们可以记录⼀下,不过有了
unity⾃带的crashreport这个也就没啥⽤了。
还有在unity对ios的c#运⾏中,在playertting⾥⾯有个对代码的运⾏优化,选择slowbutsafe还是fastbutnoexception,如果选择前者,
所有c#的执⾏错误都会像脚本⼀样被catch住,会报c#的exception,如果后者就不会,就直接不catch了,会造成程序退出,但是运⾏效率是
⾼的,我们通常选择slowbutsafe。这是mono架构的特性。
通过对ios和android的崩溃的采集和报告,有助于了解我们程序在⽤户⼿中的稳定性和及时改进。
本文发布于:2022-12-27 00:05:43,感谢您对本站的认可!
本文链接:http://www.wtabcd.cn/fanwen/fan/90/36912.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |