Android Native crash处理案例分享

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"一 背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前 "},{"type":"link","attrs":{"href":"https://www.aliyun.com/product/mpaas","title":null},"content":[{"type":"text","text":"mPaas"}]},{"type":"text","text":" Android是使用的是Crash SDK对闪退进行的处理,CrashSDK 是 Android 平台上一款功能强大的崩溃日志收集 SDK,有着极高的崩溃收集率和完整、全面的崩溃日志信息,生成的日志内容非常利于问题的跟进和解决。在我们的日常运维中,经常遇到一些闪退,无法直接从闪退堆栈看到原因,尤其是一些非Java的Native的闪退,这里分享下在mPaas框架下怎么使用Crash SDK对闪退进行分析。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"二 闪退报文分析工具介绍"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于mPaas的用户,从MAS上闪退分析平台导出的一般是原始的闪退信息,闪退信息比较多,如果直接阅读会比较困难,使用者可以通过下载Chrome的插件"},{"type":"link","attrs":{"href":"https://chrome.google.com/webstore/detail/loganalyzer/foigdjdmacffmdeneaohncefpaiiobjk","title":null},"content":[{"type":"text","text":"LogAnalyzer"}]},{"type":"text","text":",LogAnalyzer会将Crash SDK生成的日志文本内容转化成可视效果较强的 HTML 页面展现,功能还是很强大的,主要包含:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1. 高亮显示日志中重点信息,并使用不同颜色区分;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2. 支持日志内容整体结构预览,快速定位重点内容;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3. 常见崩溃原因提醒;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"安装好chrome插件后,还需要做以下配置"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1. 修改闪退文件后缀为 .txt"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于MAS上默认下载的文件后缀是.dat,需要改为.txt,否则 LogAnalyzer 会不识别"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2. 修改插件配置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于 Chrome 默认权限限制,任何 Chrome 插件默认都不能访问文件网址,需要在 Chrome 插件中进行如下操作。"}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"打开 Chrome 插件管理页面 chrome://extensions/"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"找到 LogAnalyzer 插件,点击 “详细信息\" 进入设置:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d7/d7bd2637be4c5dfe507d821b7b4bc54b.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"4","normalizeStart":"4"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"找到允许访问文件网址选项,并勾选:"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"打开或者刷新日志页面,LogAnalyzer 就生效了。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3. 生效效果"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把日志文件直接拖到chrome后,可以看到右边插件生效后,可以通过不同颜色显示闪退信息的各个字段"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首次打开后的使用说明如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7b/7b7d3461d2f8c17a75c0412d1a4fa452.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正常查看闪退截图如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/13/13806202e6345303995bc9d5246df914.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"三 闪退分析举例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们经常在日常运维中遇到一些非Java的Native模块闪退,比如UC。这种时候很多时候只能去联系UC团队进行支撑,其实很多场景下,闪退的根因并不是UC,只是最后的闪退点在UC。以我最近日常运维中比较常遇到的UC内核的闪退为例,对一些案例的处理分享如下。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1. java空指针导致UC闪退"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们在闪退点上可以看到以下闪退(已经隐藏客户apk相关信息),如果只是从这看我们暂时没有任何线索,我们继续往下看日志"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f1/f14b953ad06012b48d03ccc4f73f619a.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当看到logcat节点信息的时候,我们发现了线索,首先我们看到关键字:begin to generate native report, 表示当前是闪退日志上报的日志,我们在往前看,logcat节点里打印了异常堆栈信息,从堆栈信息可以看到,是由于precreate操作触发了底层的空指针,从而导致初始化异常,最后触发了闪退。解决方案就是临时关闭预创建,从而规避了闪退。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/28/28d40a08c1b22e86262873e4f717813b.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从上面的案例我们可以看出,"}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"Native的闪退不一定是Native模块的原因导致的,有可能是由于java导致的异常,从而导致Native闪退"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"begin to generate native report 附近可以看闪退相关的logcat信息,协助定位闪退的一些上下文日志。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2. 上层OOM导致UC闪退"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先我们看上报的闪退点的日志如下图所示,闪退在了RenderThread里,也是毫无头绪。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/65/652a0ce707e99741086683d146c34d18.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们继续硬着头皮往下看,在logcat节点里查找begin to generate native report上报节点,我们看到了大量的底层OOM的异常日志,基本大概率确定是OOM的原因了。剩下的就是查找OOM是哪里触发的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/66/66e8fb1ab573bbebb4f93f515f9b90c7.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"点击闪退里的内存节点,基本原因就比较清晰了,当前手机的vmsize基本已经到最大了,我们知道对于 32 位的进程,APP 可使用的 VmSize 最大为 3GB,不过当运行在 64 位 CPU 上时,VmSize 最大可超过 3GB,接近 4GB。但是由于内核需要占据一部分,以及不同的ROM版本的差别,我们发现有以下规律:android  8.1.0 及之后的系统,大部分 native oom crash 发生时 vmSize 分布在 3.5 - 3.9 G 的位置,相对较为集中。所以下面的案例的解决思路就变成了怎么解决OOM了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f0/f0ceec1544c789fd5f604f19cf1ce230.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3. FD误关导致UC闪退"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上报的日志如下图所示,我们大概只能看出SIGILL有可能是主动崩溃,崩溃ILL_ILLOPC表示非法操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4e/4e10e94237630b987152ac0cbaf238fb.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后我们继续看logcat节点的begin to generate native report, 基本确认原因是因为uc使用的FD对象被其他程序关闭。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1e/1e14d01dad29cd0683f6e5f8ab19f363.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"随后UC提供了带FDscan的工具包,通过我们复现后发现,是由于UC调用shouldIntercept回调的输入流对象被其他模块close掉了,导致UC使用的时候发现FD对象已经被关闭,从而做了崩溃处理。最后的处理方案就变成了用户解决其他模块的误关FD的问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7a/7a50efbd7d1f67ea45e055b17d9d4444.png","alt":"image.png","title":"image.png","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"四 总结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"综合以上的case分析,在遇到Native模块闪退的时候,一般如果从直接的闪退堆栈看不出原因的时候,不要心急,可以搜索begin to generate native report 找到崩溃上下文,多看看logcat闪退上下文的日志,会有一些收获,同时对于oom类型的问题,可以结合当前内存统计来看。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章