手游频繁崩溃”闪退”? 从程序上找原因

手游频繁崩溃”闪退”? 从程序上找原因

 作为玩家,当游戏crash的时候是什么心情,如果这个游戏玩起来还不错的话,那我可能还会打开第二次,如果这个游戏一般的话我可能直接怒删了。当多次出现闪退crash的时候,这种糟糕的体验很容易让用户流失,造成很大的损失。但是作为测试人员,面对如此棘手的事情,首先要做的是协助开发组解决问题。没错,第一件要做的事情就是去定位crash发生的代码逻辑,到底是哪个文件的哪一段函数逻辑导致了这个crash问题。因此,我们需要去尽量重现crash场景,收集解析crash日志,以此定位到具体到游戏代码逻辑中寻找导致crash的原因,改善项目的质量和体验。本文阐述在App crash产生的原理,收集和解析过程,旨在经验积累,与大家分享。

  一.crash产生的原因

  当iOS/Android设备上的App应用闪退时,操作系统会生成一个crash日志,保存在设备上。crash日志上有很多有用的信息,比如每个正在执行线程的完整堆栈跟踪信息和内存映像,这样就能够通过解析这些信息进而定位crash发生时的代码逻辑,从而找到App闪退的原因。通常来说,crash产生来源于两种问题:违反iOS系统规则导致的crash和App代码逻辑BUG导致的crash,下面分别对他们进行分析。

  1.1违反iOS系统规则包括三种类型:

  (1) 内存报警闪退

  当iOS检测到内存过低时,它的VM系统会发出低内存警告通知,尝试回收一些内存;如果情况没有得到足够的改善,iOS会终止后台应用以回收更多内存;最后,如果内存还是不足,那么正在运行的应用可能会被终止掉。在Debug模式下,可以主动将客户端执行的动作逻辑写入一个log文件中,这样程序童鞋可以将内存预警的逻辑写入该log文件,当发生如下截图中的内存报警时,就是提醒当前客户端性能内存吃紧,可以通过Instruments工具中的Allocations 和 Leaks模块库来发现内存分配问题和内存泄漏问题。

手游频繁崩溃”闪退”? 从程序上找原因 ...


  (2) 响应超时

  当应用程序对一些特定的事件(比如启动、挂起、恢复、结束)响应不及时,苹果的Watchdog机制会把应用程序干掉,并生成一份相应的crash日志。这些事件与下列UIApplicationDelegate方法相对应,当遇到Watchdog日志时,可以检查上图中的几个方法是否有比较重的阻塞UI的动作。

  application:didFinishLaunchingWithOptions:

  applicationWillResignActive:

  applicationDidEnterBackground:

  applicationWillEnterForeground:

  applicationDidBecomeActive:

  applicationWillTerminate:

  (3) 用户强制退出

  一看到“用户强制退出”,首先可能想到的双击Home键,然后关闭应用程序。不过这种场景一般是不会产生crash日志的,因为双击Home键后,所有的应用程序都处于后台状态,而iOS随时都有可能关闭后台进程,当应用阻塞界面并停止响应时这种场景才会产生crash日志。

  这里指的“用户强制退出”场景,是稍微比较复杂点的操作:先按住电源键,直到出现“滑动关机”的界面时,再按住Home键,这时候当前应用程序会被终止掉,并且产生一份相应事件的crash日志。

  1.2应用逻辑的Bug

  大多数闪退崩溃日志的产生都是因为应用中的Bug,这种Bug的错误种类有很多,比如

  SEGV:(Segmentation Violation,段违例),无效内存地址,比如空指针,未初始化指针,栈溢出等;

  SIGABRT:收到Abort信号,可能自身调用abort()或者收到外部发送过来的信号;

  SIGBUS:总线错误。与SIGSEGV不同的是,SIGSEGV访问的是无效地址(比如虚存映射不到物理内存),而SIGBUS访问的是有效地址,但总线访问异常(比如地址对齐问题);

  SIGILL:尝试执行非法的指令,可能不被识别或者没有权限;

  SIGFPE:Floating Point Error,数学计算相关问题(可能不限于浮点计算),比如除零操作;

  SIGPIPE:管道另一端没有进程接手数据;

  常见的崩溃原因基本都是代码逻辑问题或资源问题,比如数组越界,访问野指针或者美术资源不存在,或美术资源大小写错误等,这种问题的类型有很多,不再详细介绍。

  二.crash的收集

  上文提到crash日志是操作系统层产生并保存在设备上的,那如果我的一台设备在运行某App的时候crash了,可以通过什么方式拿到crash日志呢。如果是在windows上你可以通过itools或pp助手等辅助工具查看系统产生的历史crash日志,然后再根据app来查看。如果是在Mac 系统上,只需要打开xcode->windows->organizer->devices,选择device logs进行查看,如下图,这些crash文件都可以导出来,然后再单独对这个crash文件做处理分析。

手游频繁崩溃”闪退”? 从程序上找原因 ...


  以上这些是针对能够拿到真机设备的情况下才能收集crash日志的。如果是针对玩家的话,当App在玩家的设备上crash的时候如何收集呢。先来看下市场上已有的商业软件提供crash收集服务,他们这些软件基本都提供了日志存储,日志符号化解析和服务端可视化管理等服务:

  Crashlytics (www.crashlytics.com)

  Crittercism (www.crittercism.com)

  Bugsense (www.bugsense.com)

  TestFlight (www.testflightapp.com)

  HockeyApp (www.hockeyapp.net)

  Flurry(www.flurry.com)

  具体这些商业软件有哪些优缺点,有人做了如下统计:

手游频繁崩溃”闪退”? 从程序上找原因 ...


  除了上述所说的这些商业软件外,还有一些开源的软件也可以拿来收集crash日志,比如Razor,QuincyKit(git链接)等,这些软件收集crash的原理其实大同小异,都是根据系统产生的crash日志进行了一次提取或封装,然后将封装后的crash文件上传到对应的服务端进行解析处理。很多商业软件都采用了Plcrashreporter这个开源工具来上传和解析crash,比如HockeyApp,Flurry和crittercism等,下图是笔者利用这一开源框架制作的一个收集crash的样例。

手游频繁崩溃”闪退”? 从程序上找原因 ...


  通过这种方式就可以很好的支持开发人员收集crash日志的需求,进而定位和解决App产品存在的问题。如果有需要或者感兴趣的可以深入的调研一下。

  但是有个很重要的问题就是这种方式只能收集游戏引擎层(c++或object c代码)的逻辑,如果是脚本逻辑问题产生的crash就无能无力了。而现在手游项目基本都是引擎(cocos2dx或Neox)+脚本(lua或javascript)的开发模式,几乎所有的业务逻辑都在脚本层,游戏App时常发生的crash几乎都是由脚本逻辑bug导致的,这该怎么处理呢?平时在开发阶段,程序童鞋在Debug模式下开通了客户端运行日志功能,当出现crash或者traceback等问题的时候直接去查看log文件的输出即可知道原因了,但是在Release模式下一切log输出均被屏蔽,逻辑运行的log消息输出也就无法查看了。这种情况该又该如何处理呢?方法总比问题多,iOS/Android系统提供了异常发生时的处理API,只需要在程序启动的地方加入对应的处理逻辑,当异常发生时就可以触发对应的回调函数将必要的信息进行处理上传,适时地反馈给开发组。比如,下图是某项目组在iOS平台收集crash的一个截图:

手游频繁崩溃”闪退”? 从程序上找原因 ...


  其实,它具体的实现原理是这样的:首先,在游戏应用程序启动的地方需要开启异常处理逻辑的handler:

手游频繁崩溃”闪退”? 从程序上找原因 ...


  最后需要当crash发生时,需要调用的回调函数处理具体如下:

手游频繁崩溃”闪退”? 从程序上找原因 ...


  这样在当玩家在Release游戏版本中出现逻辑异常导致crash时,就会把对应的脚本层的异常(traceback或error等)以类似dump文件的形式发送到指定的服务端,方便运营维护人员进行快速定位分析。这些脚本层异常日志收集后的显示效果如下:

  以具体某一个异常日志文件为例,具体上传的内容如下图。这是一种直接可读的文本,里面记录着crash发生时代码逻辑的traceback,通过阅读代码逻辑就可以直接定位到或推断导致crash

手游频繁崩溃”闪退”? 从程序上找原因 ...


  以上就是收集crash的方法和原理,通过这种方式收集到crash日志后接下来就可以具体根据日志的内容进行解析来定位到底是什么原因导致的crash。

  三.crash日志的解析

  如果是脚本层逻辑导致的crash,如上所述,这种情况是可以直接根据收集的日志内容来定位导致crash的逻辑的。如果是引擎层发生了问题,该如何定位解析呢。先来看一个crash的栗子:

手游频繁崩溃”闪退”? 从程序上找原因 ...


  如上图所示,

  1)crash标识是应用进程产生crash时的一些标识信息,它描述了该crash的唯一标识(E838FEFB-ECF6-498C-8B35-D40F0F9FEAE4),所发生的硬件设备类型(iphone3,1代表iphone4),以及App进程相关的信息等;

  2)基本信息描述的是crash发生的时间和系统版本;

  3)异常类型描述的是crash发生时抛出的异常类型和错误码;

  4)线程回溯描述了crash发生时所有线程的回溯信息,每个线程在每一帧对应的函数调用信息(这里由于空间限制没有全部列出);

  5)二进制映像是指crash发生时已加载的二进制文件。以上就是一份crash日志包含的所有信息,接下来就需要根据这些信息去解析定位导致crash发生的代码逻辑, 这就需要用到符号化解析的过程(洋名叫:symbolication)。

  符号化解析过程有三种方法:

  xcode可视化查看,

  symbolicatecrash工具,

  atos工具;但是这三种方法都需要用到构建app时生成的.app文件和.app.dsym这两个文件,第一种方式已经在第二章节提到过,不再赘述,下面介绍第二种和第三种解析的方式。

  3.1 symbolicatecrash解析

  symbolicatecrash是xcode自带的一个命令行工具,在xcode5.0以前的位置是/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/,xcode5.0以后路径就变成了/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/

  比如以上述提到的TestFlight App为例,将TestFlight .crash?TestFlight .app和TestFlight .app.dsym三个文件放在同一个目录下,然后运行 symbolicatecrash?TestFlight.crash TestFlight.app.dsym>?TestFlight .log,查看TestFlight.log文件的内容:

手游频繁崩溃”闪退”? 从程序上找原因 ...


  从图中连线可以看出具体出现问题的逻辑代码是在那个文件的哪一行,这样就根据解析出来的指定函数来定位crash的发生原因。

  3.2 atos方法

  atos是一个BSD平台的通用指令,通过它可以将数字地址转换为对应的二进制映像或者进程的符号,通过该指令进行符号化解析的时候需要说明一点的是只有当.app文件、crash文件和.app.dsym文件三者的UUID都是一致的时候,该crash文件才能被正确解析,否则解析失败。(注:uuid是app应用在移动设备上的唯一标识)可以通过以下方式来查看.app和.app.dsym文件的uuid,以上述提到的TestFlight应用为例:

手游频繁崩溃”闪退”? 从程序上找原因 ...


  而crash日志文件的uuid在二进制映像中的第一行:

手游频繁崩溃”闪退”? 从程序上找原因 ...


  由此可见armv7架构下三者保持一致,都是4a42d422a466338aa56e88840b74de3d,那接下来开始进行符号化解析。

  从上文crash日志文件的线程回溯可以发现闪退时函数的回溯列表里格式不是完全一致,比如下图中的方式1和方式2在第2列的表达方式上不太一样,方式1是用的库函数名,方式2则是一个基本地址。其实这两种方式都可以用一种通用的解析方式来搞定:

手游频繁崩溃”闪退”? 从程序上找原因 ...


  首先计算加载地址(load address):

  以方式1中的0x333d8049 UIApplicationMain + 1137 为例,这一帧对应的 load address=0x333d8049 -1137=0x333d7bd8

  也就是UIApplicationMain的地址是0x333df12;方式2的0x00068b19 0x36000 + 207641,通过上述方式的计算就是load address=0x00068b19 -207641=0x36000 ,可以发现结果与第二列的值是相同的,也就是它的加载地址就是第二列的值0x36000? 然后用xcrun atos -arch armv7 -o TestFlight.app/TestFlight? 0x36000 0x00068b19 的指令来解析crash日志线程0和线程3中带有TestFlight模块的地址,结果发现TestFlight程序的代码回溯过程:

手游频繁崩溃”闪退”? 从程序上找原因 ...


  可以看出base

  address(基地址)是4000,函数的回溯过程是main.m文件的第16行的某个函数出现问题,然后该函数在逻辑调用中会调用到AFURLConnnectionOperation.m文件的第162行的某个函数,这个逻辑的调用与第一种方法解析的TestFlight.log文件作对比,crash的解析完全一致,由此就可以定位到crash的原因所在,接下来去解决crash文件也就水到渠成了。

  四.小结

  以上是根据自己的经验和理解对iOS平台下的crash问题(包括原理、收集和解析过程)进行的一次剖析,虽然苹果的沙盒系统对iOS平台的下的很多应用信息的提取有较多的限制,但是要相信方法总比问题多。对于crash问题的理解和收集过程可以很好地辅助项目组来提高项目的质量,同时对于更深入地理解iOS平台知识和crash原理有很好的帮助。当然,本文更多的涉及iOS平台下的crash问题,对于Android平台的crash问题涉及较少。虽然细节的实现上可能有差异,但是内部的原理逻辑应该是相同或者相似的,后续笔者还将继续关注关于Android平台相关问题的调研学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246370.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汇编语言属于C语言吧,汇编语言和c语言的区别是什么

区别:汇编语言的效率高,对硬件的可操控性更强,体积小,不易维护,可移植性很差;c语言的效率比较低,硬件可操控性比较差,目标代码体积大,容易维护,可移植性很好。…

c语言某一行不被优化,C语言优化小技巧

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼在编写C语言程序后,经常需要对源码进行优化,以提高程序的运行效率,下面简述几个常用的优化技巧以供大家参考:1.C于代码在程序中的优化现在的C编译器会自动对代码进行优化,…

一款已上市MMO手游地图同步方案总结

1. 客户端地图格子的相关知识在2.5D的MMO游戏里,角色是通过3D的方式渲染,2D的地图是通过2D的方式显示,所以在客户端一般会有三个坐标系: a) 3D坐标系:所有需要3D渲染的角色和光效,都以3D坐标系中定位。 b) …

策略模式与简单工厂模式区别(转)

最近一直在抽时间研究设计模式,之前对设计模式也有一定的了解,但是都没有平心静气的去研究过,只是了解了一些皮毛,最近打算再深入研究一下,重新打开了设计模式的数据,对之前的疑问一个个的刨根问底&#xf…

c语言中枚举类型的长度,C中的枚举类型数据的大小是多少?

这是一个C面试测试问题不是家庭作业。#include using namespace std;enum months_t { january, february, march, april, may, june, july, august, september,october, november, december} y2k;int main (){cout << "sizeof months_t is " << sizeof(m…

求平方根sqrt()函数的底层算法效率问题

我们平时经常会有一些数据运算的操作&#xff0c;需要调用sqrt&#xff0c;exp&#xff0c;abs等函数&#xff0c;那么时候你有没有想过&#xff1a;这个些函数系统是如何实现的&#xff1f;就拿最常用的sqrt函数来说吧&#xff0c;系统怎么来实现这个经常调用的函数呢&#xf…

android espresso跨程序,Android Espresso:依次运行多个测试

我正在尝试使用Espresso forAndroid进行一系列测试.运行之间似乎没有关闭活动.一次测试后,无论应用程序状态如何,左侧为下一次测试.如何用Espresso来实现&#xff1f;错误报告中提供的修复程序的问题是,这将仅在整个套件的完成时执行.如果你想在每次测试后都有一个干净的活动堆…

C#中字符串的内存分配与驻留池

刚开始学习C#的时候&#xff0c;就听说CLR对于String类有一种特别的内存管理机制&#xff1a;有时候&#xff0c;明明声明了两个String类的对象&#xff0c;但是他们偏偏却指向同一个实例。如下&#xff1a; String s1 “Hello”; String s2 “Hello”; //s2和s1的实际值都…

signature=f2388c4f1bce538cc797410d9560f03b,KSHV strategies for host dsDNA sensing machinery

摘要&#xff1a;The innate immune system utilizes pattern recognition receptors cyclic GMP-AMP synthase(cGAS)to sense cytosolic double-stranded(ds) DNA and initiate type 1 interferon signaling and autophagy pathway, which collaborate to limit pathogen infe…

Unity 内存管理

一&#xff1a;资源类型 GameObject, Transform, Mesh, Texture, Material, Shader, noxss和各种其他Assets。 二&#xff1a;AssetBundle文件内存镜像 来自文件就用CreateFromFile(注意这种方法只能用于standalone程序&#xff09;这是最快的加载方法。也可以来自Memory,用…

1.逐帧动画shader

最近项目压力不大&#xff0c;抽时间看了些关于shader和游戏引擎的书籍&#xff0c;准备开始shader的学习。 在网上看到这位前辈&#xff08;http://blog.sina.com.cn/s/articlelist_2312702844_6_1.html&#xff09;写的博客&#xff0c;觉得很不错&#xff0c;学习曲线应该很…

panel中html怎么写,panel控件有什么用 请写出Panel控件的用途

当多个panel控件重叠时,把一个panel的visible/设置为true,其它的设置为f例如有3个panel&#xff0c;分别为panel1,panel2,panel3 如果重叠的话&#xff0c;把他们的大小设置为相同大小&#xff0c;直接通过上下层来控制即可&#xff0c;例如想要panel2显示出来&#xff0c;就用…

html页面如何实现搜索功能,使用jQuery怎么实现一个页面关键字搜索功能

使用jQuery怎么实现一个页面关键字搜索功能发布时间&#xff1a;2021-01-04 16:46:38来源&#xff1a;亿速云阅读&#xff1a;136作者&#xff1a;Leah这篇文章给大家介绍使用jQuery怎么实现一个页面关键字搜索功能&#xff0c;内容非常详细&#xff0c;感兴趣的小伙伴们可以参…

html相对定位向上偏移,使用CSS的相对定位和偏移量

这是利用CSS相对定位和偏移写成的表单输入框效果&#xff0c;不是以前用的表格那种&#xff0c;也没有用UL li&#xff0c;觉得借此示例可练习一下CSS,特别是CSS的定位和布局方面的知识&#xff0c;了解top,bottom,left,right这些偏移量属性的用法。CSS相对定位和偏移写成的表单…

2.转动的地球shader

原理很简单&#xff0c;根据时间对uv中的x轴进行位移&#xff0c;对于地面和云层&#xff0c;取不同的移动速度&#xff0c;分别计算对应的uv坐标&#xff0c;再根据uv坐标从地面和云层的纹理中分别取出对应的值&#xff0c;最后把两者用lerp函数进行混合作为最终结果。 下边的…

3.顶点外扩方法实现的描边shader

描边shader的实现有很多种&#xff0c;顶点外扩是其中之一。顶点外扩的原理是用2个Pass 渲染物体2次第一遍:描边&#xff0c;顶点沿法线方向外拓后用黑色渲染。外扩这一步的实现是在投影空间&#xff0c;也就是2D的&#xff0c;根绝法线的x和y值进行外扩&#xff0c;因为是沿着…

为此计算机所有用户安装加载项,安装Office 2013后,无法在计算机上安装Outlook加载项...

我使用Outlook Add in模板和VSTO在visual studio 2010中实现了Outlook添加&#xff0c;我使用MSI安装程序(在visual studio 2010中创建)为它创建了一个安装..我能够在所有机器上安装它(x86和x64) ) .最近我尝试在安装了Office 2013的计算机上安装相同的安装程序并收到以下错误&…

4.边缘光照的描边shader

【思路】&#xff1a;面向摄像机的物体&#xff0c;它的表面法线【normal】和视角向量【viewDir】的【夹角】越靠近边缘就越大。那么就可以根据这个夹角进行处理&#xff0c;夹角越大&#xff0c;那么发射光越强&#xff0c;就可以实现我们想要的效果。 Shader "Study/4_O…

html 怎么设置cooki,怎么设置浏览器接受cookie

怎么设置浏览器接受cookie把浏览器设置成接受cookie方法&#xff0c;以IE11为例&#xff1a; 1&#xff0c;打开IE浏览器&#xff0c;在菜单栏上点击“工具”&#xff0c;弹出下拉菜单点击“Internet选项”。 2&#xff0c;点击“隐私”选项&#xff0c;在页面中点击“高级”。…

5.Lambert光照Diffuse Shader

http://blog.csdn.net/candycat1992/article/details/17355629 这篇文章写的很好&#xff0c;这里就直接上代码了 Shader "Study/5_LambertDiffuse" {Properties{_Color("Main Color", Color) (1,1,1,1)_MainTex("Base (RGB)", 2D) "whit…