热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

androidcrash没有日志_有赞crash平台符号化实践

‍‍点击关注“有赞coder”获取更多技术干货哦~作者:杨杨&姜豪部门:电商移动背景有赞在基础保障平台的实践中完成了Crash平台的建设&
‍‍501447ec68b6577b2e9205e40af6eaa6.png

点击关注“有赞coder”

获取更多技术干货哦~

593e779d1211d32948505c8be21a1a23.png

作者:杨杨 & 姜豪

部门:电商移动

背景

有赞在基础保障平台的实践中完成了 Crash平台的建设,但是iOS的崩溃日志未经符号化,排查问题比较困难。为了降低iOS App的crash率,快速排查线上crash,疑难crash的跟踪处理,符号化崩溃日志显得尤为重要!

一、crash日志的收集与分析

1.1 如何收集crash日志

1.手机上直接看,在隐私-分析与改进 -分析数据,可以找到所有崩溃日志,未符号化。2.连接电脑,通过“音乐”同步到本地 ~/Library/Logs/CrashReporter/MobileDevice/xxx的 iPhone. 缺点:日志没有符号化,需要自己手动符号化3.连接电脑,打开Xcode-window-Diveces and Simulators。

44f69e729241b0aa554ef4e11eb97ade.png

Xcode会尝试在本地查找符号表文件,自动符号化。以上3种方法都局限于拿得到设备的情况。4.查看别人手机上的crash日志 Xcode-Window-Organizer。66ec898ddacb4a4f23055c8fbbd582f1.png这种方式找符号表会有2种途径
  • 上传AppStore的时候会让你勾选上传符号表「Include App symbols for your Application…」,如果上传了,苹果自动帮你在云端做解析。
  • 如果没有上传,Xcode尝试在本地找符号表文件进行符号化。
缺点:这种方式也只能收集在手机设置中打开了上传crash开关,以及TestFlight用户的crash日志。企业分发或 AdHoc 安装,需要自行获取崩溃日志。信息不全,线程信息不够。5.自己收集crash日志,比如接入KSCrash、plcrashreporter等,但是要自己做符号化。

1.2 crash日志的结构

日志可以分成4个部分,基本信息,崩溃的原因,所有线程调用,Binary Images (二进制文件列表)。

1.2.1 基本信息

ba917d2c4441bbb5336c69db78c0141d.png

1.2.2 崩溃原因

aafe368376205344e512d642beb1f80f.png

线程

3ddfbf18f394ee7ae76ce1e98f0d363b.png

Binary Images

6e5c62ae04304e300e72f04ab8b54192.png

二、如何进行crash日志符号化

crash日志符号化通常是通过 atossymbolicatecrash 这两个工具来完成。

2.1 atos

atos 是苹果提供的符号化工具,在Mac OS系统下默认安装,他的缺点是只能一个地址一个地址逐个翻译。我们看下这个工具的使用说明:

09e4ccc5c7e17d1e2ce2d9ad331d3d84.png

使用方法:

atos -arch -o /Contents/Resources/DWARF/ -l

需要传入这几个信息:arch 架构、dSYM路径、binary image 载入内存的初始地址、崩溃的地址。

参数内容可以从crash日志中取得,如下图所示:

db79acbab401f7d15fe6762a5ebf4976.png

example

$ atos -arch arm64 -o TheElements.App.dSYM/Contents/Resources/DWARF/TheElements -l 0x1000e4000 0x00000001000effdc -[AtomicElementViewController myTransitionDidStop:finished:context:]

2.2 symbolicatecrash

symbolicatecrashXcode 自带的一个程序,他是对 atos 的封装,可以翻译整个crash文件,有赞就是选择这个工具来进行 crash 符号化的。

具体的路径可以通过以下命令搜索出来:

find /Applications/Xcode.App -name symbolicatecrash -type f

使用方法:

export DEVELOPER_DIR="/Applications/Xcode.App/Contents/Developer" /symbolicatecrash 例子: symbolicatecrash log.crash > result.log // dSYM可以跟多个 symbolicatecrash log.crash -d TheElement.App.dSYM >result.log

下文会对此工具做一个详细的原理分析。

三、symbolicatecrash符号化原理分析

通过网上找的教程来看,一般是把对应版本的crash日志,dSYM文件,App文件都放进一个目录,然后执行一下命令来进行符号化:

symbolicatecrash log.crash -d TheElement.App.dSYM >result.log

但是我有几个疑问:

  1. 如果App打包出来多个dSYM怎么办?

  2. 发现把目录中的App文件删了,dSYM删了(源文件还在),执行命令的时候也没传他们,竟然也可以符号化,这怎么做到的?

  3. 怎么样知道crash日志,dSYM,App是正确的,可以正确做符号化,如果发现某个crash日志没有被正确符号化,怎么查这个问题?

  4. 把dSYM丢了,相同代码再去编译一次把dSYM拿出来可以用吗?

  5. 我们执行完后发现系统库也都符号化了,系统的dSYM在哪里,难道已经包含在App的dSYM中吗?

  6. 崩溃日志最下面的Binary Images是干嘛的?

针对以上这些问题,我们来做下源码分析一探究竟。

3.1 symbolicatecrash 源码分析

官方没有开源,但是网上有类似的实现,是用perl实现的一个脚本。

首先,一个基本原则是需要确保你的电脑上有每个 image 对应的 uuid 的符号表文件,这样crash文件才能被正确解析和符号化出来。

然后我们看下符号化一个crash文件的流程:

3.1.1 解析所有的Binary Image

这是crash日志中的Binary Image格式0x1cd997000 - 0x1cea7bfff UIKitCore arm64 <40a93e939f8635c1905c7b947c7c2305> /System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore转换为如下格式&#39;UIKitCore&#39; &#61;> { &#39;extent&#39; &#61;> &#39;0x1cea7bfff&#39;, &#39;plus&#39; &#61;> &#39;&#39;, &#39;bundlename&#39; &#61;> &#39;UIKitCore&#39;, &#39;uuid&#39; &#61;> &#39;40a93e939f8635c1905c7b947c7c2305&#39;, &#39;base&#39; &#61;> &#39;0x1cd997000&#39;, &#39;path&#39; &#61;> &#39;/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore&#39;, &#39;arch&#39; &#61;> &#39;arm64&#39;, &#39;nextID&#39; &#61;> &#39;&#39;}

把每一个Binary Image都存储为以上形式的对象。

Binary Image的作用是建立UIKitCore与uuid的关系&#xff0c;当需要符号化一个UIKitCore的地址时&#xff0c;会找到对应的uuid&#xff0c;并从文件系统中查找到这个符号表。这也解释了上面第6个问题。

3.1.2 解析所有线程

8 TheElement 0x00000001044dcfc0 0x104058000 &#43; 4739008转换为如下格式&#39;0x00000001044dcfc0 0x104058000 &#43; 4739008&#39; &#61;> { &#39;raw_address&#39; &#61;> &#39;0x00000001044dcfc0&#39;, &#39;bundle&#39; &#61;> &#39;TheElement&#39;, &#39;address&#39; &#61;> &#39;0x00000001044dcfc0&#39;}

把所有堆栈存储为以上形式的对象。

3.1.3 翻译Last Exception Backtrace

这是crash日志中的Last Exception BacktraceLast Exception Backtrace:(0x1a1a9127c 0x1a0c6b9f8 0x1a19adab8 0x1a1a96ac4 0x1a1a9875c 0x10566d498 0x10423ab84 0x1ce255040 0x1cdcfe1c8 0x1cdcfe4e8 0x1cdcfd554 0x1ce28c304 0x1ce28d52c 0x1ce26d59c 0x10437fd20 0x1ce333714 0x1ce335e40 0x1ce32f070 0x1a1a23018 0x1a1a22f98 0x1a1a22880 0x1a1a1d7bc 0x1a1a1d0b0 0x1a3c1d79c 0x1ce253978 0x104283158 0x1a14e28e0)翻译为&#xff1a;0 libsystem_kernel.dylib 0x00000001a162e0dc 0x1a160b000 &#43; 1435801 libsystem_pthread.dylib 0x00000001a16a7094 0x1a16a5000 &#43; 83402 libsystem_c.dylib 0x00000001a1587f4c 0x1a152d000 &#43; 3725563 libsystem_c.dylib 0x00000001a1587eb4 0x1a152d000 &#43; 3724044 libc&#43;&#43;abi.dylib 0x00000001a0c54788 0x1a0c53000 &#43; 60245 libc&#43;&#43;abi.dylib 0x00000001a0c54934 0x1a0c53000 &#43; 64526 libobjc.A.dylib 0x00000001a0c6be00 0x1a0c66000 &#43; 240647 TheElement 0x0000000104babb18 0x104058000 &#43; 118771448 TheElement 0x00000001044dcfc0 0x104058000 &#43; 47390089 libc&#43;&#43;abi.dylib 0x00000001a0c60838 0x1a0c53000 &#43; 5535210 libc&#43;&#43;abi.dylib 0x00000001a0c60434 0x1a0c53000 &#43; 5432411 libobjc.A.dylib 0x00000001a0c6bbc8 0x1a0c66000 &#43; 2349612 CoreFoundation 0x00000001a1a1d11c 0x1a1979000 &#43; 67202813 GraphicsServices 0x00000001a3c1d79c 0x1a3c13000 &#43; 4290814 UIKitCore 0x00000001ce253978 0x1cd997000 &#43; 916108015 TheElement 0x0000000104283158 0x104058000 &#43; 227362416 libdyld.dylib 0x00000001a14e28e0 0x1a14e1000 &#43; 6368

这里为什么可以翻译&#xff0c;因为第一步已经把所有Binary Image存储起来&#xff0c;上面的每一个地址&#xff0c;都可以找到对应的Binary Image&#xff0c;从而获得Binary Image的名称&#xff0c;基地址&#xff0c;以及偏移量。

3.1.4 删除不需要的image

因为crash日志把App用到的所有Binary Image都列举出来了&#xff0c;而崩溃堆栈中只用到了一小部分&#xff0c;所以这里把没有用到的Binary Image删除。后续要遍历所有images&#xff0c;去找到每个二进制对应的dSYM&#xff0c;这样做提高了效率。

3.1.5 查找Binary Image的符号表

符号表的类型
  • App编译出来的dSYM ( 一般输入命令时指定在哪里&#xff0c;如果没有会自动去查找)

  • 系统库的符号表 (自动查找)&#xff0c;这也解释了第五个问题&#xff0c;系统符号表和APP符号表是分开的。在 ~/Library/Developer/Xcode/iOS DeviceSupport/os/Symbols 这个路径再拼上image中的path&#xff0c;就是完整路径 比如 ~/Library/Developer/Xcode/iOS DeviceSupport/os/Symbols/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore

  1. 从search path中找 (包括命令行输入的几个目录 和 系统符号表所在目录)

  2. mdfind搜索uuid相同的符号表&#xff0c;这就解释了上面第1个和第2个问题&#xff0c;会使用uuid去查找&#xff0c;所以命令行中不传也没关系。

  3. 如果还没找到 返回空 并删除这个image&#xff0c;与这个image相关的都不能被符号化

判断匹配的条件
  1. lipo -info 判断架构是否一致

  2. otool 命令打出来macho信息&#xff0c;找到uuid 并 判断是否一致&#xff0c;这解答了上面第3个和第4个问题&#xff0c;只有uuid相同&#xff0c;才可以被符号化出来。相同代码重新打一个包出来也不能符号化&#xff0c;因为uuid不同。

3.1.6 执行atos进行符号化

  1. 遍历所有线程

  2. 取到每一条的bundle 还有地址 在images中找到符号表路径

  3. 执行命令 并记录符号化后的内容

&#39;0x00000001044dcfc0 0x104058000 &#43; 4739008&#39; &#61;> { &#39;symbolled&#39; &#61;> &#39;CPPExceptionTerminate() (SentryCrashMonitor_CPPException.cpp:179)&#39;, &#39;raw_address&#39; &#61;> &#39;0x00000001044dcfc0&#39;, &#39;bundle&#39; &#61;> &#39;TheElement&#39;, &#39;address&#39; &#61;> &#39;0x00000001044dcfc0&#39; }

3.1.7 字符串替换 生成最终的报告

逐行开始替换

比如将&#39;0x00000001044dcfc0 0x104058000 &#43; 4739008&#39;替换为&#39;CPPExceptionTerminate() (SentryCrashMonitor_CPPException.cpp:179)&#39;

四、有赞符号化方案

通过上面的原理分析&#xff0c;我们基本掌握了 crash 符号化的步骤&#xff0c;下面介绍下我们有赞是如何做符号化的。

4.1 dSYM符号表保存

首先&#xff0c;进行符号化必不可少的一个文件就是 dSYM 符号表&#xff0c;我们需要保存每次正式发布的App版本对应的符号表文件。如下图所示&#xff1a;

ecf5efdee11b151fe6a0362ad5c3b777.png

  1. 打包机(gitlab runner)&#xff1a;有赞目前有自己的持续构建平台 MBD&#xff0c;业务方在 MBD 上发起打包构建任务后系统会根据算法分配到不同的打包机上。更多关于有赞移动 CI/CD 我们在之前做过一次技术沙龙&#xff0c;详细内容见这里。

  2. 项目打包完成后会执行一个保存符号表的脚本&#xff0c;会保存符号表到本地&#xff0c;并且上传到云端做备份。备份完成后调用MBD接口&#xff0c;上报符号表uuid&#xff0c;bundleId&#xff0c;版本号&#xff0c;build号&#xff0c;打包机唯一标识。

  3. 由于有多台打包机导致每次打包产出的符号表分布在不同的打包机上&#xff0c;我们需要建立dSYM文件与打包机的关系。第一步中的保存符号表脚本会上报信息到MBD&#xff0c;MBD把dSYM符号表uuid和打包机唯一标识做一个映射关系。

  4. 当发生一个crash时&#xff0c;crash日志中包含符号表uuid&#xff0c;通过uuid查表&#xff0c;就能定位到执行构建的打包机。

4.2 crash上报

dSYM符号表已经保存下来了&#xff0c;接下来就是crash的上报和解析&#xff0c;crash上报大致流程见下图&#xff1a;

32470400898e2c09ebff74edf556c9a0.png

  1. crash信息通过SDK上报到埋点平台&#xff0c;我们通过Flink监听到crash信息的上报&#xff0c;并把它写入数据库。

  2. Flink是实时计算平台提供的用来实时消费上报的数据的程序&#xff0c;支持大并发量的数据。

更多关于crash平台的建设我们近期也发表过一篇文章&#xff0c;详情见 这里。

4.3 crash文件符号化

步骤二中已经上报了crash信息并展示在了我们的内部平台中&#xff0c;接下来我们需要对此crash文件结合对应的dSYM进行符号化解析&#xff0c;具体流程如下&#xff1a;

4eefe58d9e98e40b35be23c8fedeb30c.png

  1. 在 Crash前端页面&#xff0c;点击符号化按钮会发起 MBD 的一次符号化构建&#xff0c;并将 crash 的信息传递给 MBD。

  2. MBD把crash的uuid拿出来&#xff0c;根据uuid去查 dSYM文件所在的 打包机&#xff0c;并把任务给到这个打包机。

  3. 打包机运行脚本&#xff0c;这个脚本的作用是使用symbolicatecrash程序符号化crash日志&#xff0c;并把符号化后的结果通知到MBD。

  4. MBD 把符号化结果写入数据库&#xff0c;并通知Crash后端。

  5. Crash前端页面收到通知后刷新页面&#xff0c;展示符号化后的结果。

至此&#xff0c;我们完成了crash文件的符号化解析工作&#xff0c;但是使用过程中暴露出了一些问题&#xff1a;

  1. 目前每次打包都会产生dSYM文件并直接保存在打包机上&#xff0c;MBD每天的打包任务有很多&#xff0c;导致占用空间浪费资源。我们计划只维护符号表的cdn链接&#xff0c;用到时再去下载符号表。

  2. 这种方案下线一台打包机后&#xff0c;会造成一部分crash日志无法符号化&#xff0c;目前我们正在优化&#xff0c;计划统一把符号表放到一台打包机上&#xff0c;这样就能解决这个问题。

  3. 系统符号表的维护也是一个问题&#xff0c;我们需要在每台打包机上都要加上系统符号表&#xff0c;而且每次苹果发布新版都需要拿新的系统符号表过来&#xff0c;维护起来挺麻烦的。目前的解决方案是人工放到打包机上。

总结

至此&#xff0c;我们了解了如何收集crash日志&#xff0c;明白了crash日志中每个部分的意思&#xff0c;符号化的工具&#xff0c;以及如何对crash日志进行符号化。已经可以解答出来上面提出的问题&#xff0c;对符号化的原理有了非常清晰的认识。

我们的符号化方案对于有赞多台打包机环境而言&#xff0c;非常合适&#xff0c;下线一台或者新增一台打包机&#xff0c;可以无缝支持。另外&#xff0c;整套方案非常轻量&#xff0c;能够快速集成符号化功能&#xff0c;符号化链路清晰。

Crash平台拥有符号化crash日志的能力后&#xff0c;极大的提高了大家排查、解决线上问题的效率&#xff0c;提升了App的稳定性。

扩展阅读&#xff1a;
  1. 有赞移动Crash平台建设
  2. 有赞移动 App 一键切换网关实践

  3. 有赞零售小票打印图片二值化方案

  4. 有赞 Android 崩溃保护的探索及实践

  5. 有赞移动 iOS 组件化(模块化)架构设计实践

  6. 有赞Flutter插件开发与发布

  7. 有赞移动如何做到并行灰度的复杂场景&#xff1f;

Vol.334

3b1f762ed807bd9d97cb62eeafbfa6ea.png‍‍



推荐阅读
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • 本文介绍了如何利用 `matplotlib` 库中的 `FuncAnimation` 类将 Python 中的动态图像保存为视频文件。通过详细解释 `FuncAnimation` 类的参数和方法,文章提供了多种实用技巧,帮助用户高效地生成高质量的动态图像视频。此外,还探讨了不同视频编码器的选择及其对输出文件质量的影响,为读者提供了全面的技术指导。 ... [详细]
  • 本文介绍如何在 Android 中自定义加载对话框 CustomProgressDialog,包括自定义 View 类和 XML 布局文件的详细步骤。 ... [详细]
  • 如何在Linux服务器上配置MySQL和Tomcat的开机自动启动
    在Linux服务器上部署Web项目时,通常需要确保MySQL和Tomcat服务能够随系统启动而自动运行。本文将详细介绍如何在Linux环境中配置MySQL和Tomcat的开机自启动,以确保服务的稳定性和可靠性。通过合理的配置,可以有效避免因服务未启动而导致的项目故障。 ... [详细]
  • 本文介绍了如何使用 Node.js 和 Express(4.x 及以上版本)构建高效的文件上传功能。通过引入 `multer` 中间件,可以轻松实现文件上传。首先,需要通过 `npm install multer` 安装该中间件。接着,在 Express 应用中配置 `multer`,以处理多部分表单数据。本文详细讲解了 `multer` 的基本用法和高级配置,帮助开发者快速搭建稳定可靠的文件上传服务。 ... [详细]
  • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
  • 在探讨如何在Android的TextView中实现多彩文字与多样化字体效果时,本文提供了一种不依赖HTML技术的解决方案。通过使用SpannableString和相关的Span类,开发者可以轻松地为文本添加丰富的样式和颜色,从而提升用户体验。文章详细介绍了实现过程中的关键步骤和技术细节,帮助开发者快速掌握这一技巧。 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 解决Only fullscreen opaque activities can request orientation错误的方法
    本文介绍了在使用PictureSelectorLight第三方框架时遇到的Only fullscreen opaque activities can request orientation错误,并提供了一种有效的解决方案。 ... [详细]
  • 本文详细介绍了如何使用Python中的smtplib库来发送带有附件的邮件,并提供了完整的代码示例。作者:多测师_王sir,时间:2020年5月20日 17:24,微信:15367499889,公司:上海多测师信息有限公司。 ... [详细]
  • 详解 Qt 串口通信程序全程图文 (4)
    Qt串口通信程序全程图文是本文介绍的内容,本文一开始先讲解对程序的改进,在文章最后将要讲解一些重要问题。1、在窗口中加入一些组合框ComboBox&# ... [详细]
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • 装饰者模式(Decorator):一种灵活的对象结构设计模式
    装饰者模式(Decorator)是一种灵活的对象结构设计模式,旨在为单个对象动态地添加功能,而无需修改原有类的结构。通过封装对象并提供额外的行为,装饰者模式比传统的继承方式更加灵活和可扩展。例如,可以在运行时为特定对象添加边框或滚动条等特性,而不会影响其他对象。这种模式特别适用于需要在不同情况下动态组合功能的场景。 ... [详细]
  • 技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统
    技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统 ... [详细]
author-avatar
拉桑357_584
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有