zsource 命令是什么?

美团 App 在 2015 年就已经基于 CocoaPods 完成了组件化的工作。在组件化的改造过程中,为了能够加速整体工程的构建速度,我们对需要集成进美团 App 的组件进行了二进制化,同时提供一个叫做 cocoapods-binary 的 CocoaPods 插件来支持本地工程使用二进制。因此,美团 App 的开发者在集成开发时,除了自己正在开发的组件,其他的组件都以二进制的形式存在。

使用二进制,虽然会给工程带来构建速度的提升,但是会带来一个新的问题:在调试工程时,那些使用二进制的组件,无法像源码调试那样看到足够丰富的调试信息。例如,如果程序在二进制组件的代码中崩溃,我们只能看到该组件的堆栈信息和一些不明所以的汇编代码:

和业界大多的组件化方案类似,美团 App 的组件化方案也提供了将一个组件从二进制切换到源码的机制。美团工程的开发者能够使用一系列配置和命令来切换组件的源码和二进制状态,但每次切换都需要重新执行 pod install。这种方式在组件化的初期是没有什么问题的。但随着美团 App 的组件数量不断增长,即便是只切换一个组件的状态,单次 pod install 的时间也增长到了分钟级。而且这种方式每切换一次就必须重新编译运行一次 App,在追查一些偶现崩溃问题时,开发体验非常不友好,也不利于崩溃问题的快速定位分析。

为了解决以上提到的这些问题,我们利用 CocoaPods 的插件机制,为 CocoaPods 的 pod 命令增加了 zsource 子命令,开发者可以在使用二进制构建工程的同时,非常快速地将一个组件调出源码进行调试,具体的使用效果可以看一下如下的屏幕录制:

zsource 命令的开发始末

在推出 zsource 功能后,很多同学都对 zsource 背后的技术原理十分感兴趣。其实 zsource 整个功能的开发流程也十分的有趣,就像小说一样,分为几个不同的时期:

  • 原理猜想
  • 查阅资料
  • 简单粗暴的尝试
  • 柳暗花明
  • 工程化

原理猜想

如果让我们猜想 Xcode 断点调试功能的实现原理,可能大部分人都会猜这样一种可能:Xcode 在编译 Debug 版本的二进制过程中,在二进制中某个字段存储了该二进制所对应的源码的文件地址。当我们在 Xcode 中打断点进行调试的时候,Xcode 会根据二进制中这个字段中存储的源码文件地址,打开对应的源码文件,并在 UI 上展示该源码文件。

道理好像没有什么问题,但是事实是这样吗?在某次团建回国的航班上,我们组成威和志宇两位同学在提出这种猜想后,拿出电脑,做了一个这样的小实验:

实验中,他们分别创建了两个 Xcode 工程 A 和 B,工程 A 会产出一个二进制 libA.a。工程 B 中会接将 A 的产出 libA.a 拖到工程中,然后设置 A 中代码的符号断点,然后编译运行。结果发现,当断点断在 A 中的代码时,Xcode 会直接跳转到 A 的源文件中,并且可以继续增加断点以及正常的单步调试。

通过这个实验,成威和志宇同学确定了猜想的正确性。那么接下来需要做的,就是确定二进制中,这个源文件地址信息具体藏在哪一个字段中。

查阅资料

我们都知道苹果的 Mach-O 二进制文件使用的是 DWARF 这种格式来存放调试相关的数据的。但因为我们很难从这个问题中提炼几个精确的关键词在搜索引擎中检索,所以很难通过简单的几次检索就获取到我们想要的答案:二进制这个字段的名称,在初期甚至无法确定这个字段应该是从 Mach-O 的资料中检索还是从 DWARF 的资料中检索。

在没有太好的搜索结果的情况下,我们一度曾经想尝试去从头去啃一啃找到的一些二进制相关的文档:

  • osx-abi-macho-file-format-reference
  • Introduction to the DWARF Debugging Format
  • DWARF 1.1.0 Reference

简单粗暴的尝试

然而,由于对二进制格式不是那么熟悉,也不太了解二进制相关的词汇和概念,所以阅读文档的速度就非常缓慢。

不过,技术的有趣之处就在于,有时候你可以基于我们的猜想,任意去尝试,跳过艰辛的文档阅读过程。在文档阅读遇到挫折后,我们猜想,二进制中很有可能也是用字符来存储这些源码信息的,那么如果我们就把二进制当做字符来看,是不是能搜到一些东西呢?

于是我们试着做了一个比较简单的二进制文件,二进制文件中仅仅包含一个 ZSCViewController,然后用 xxd 这个命令尝试读取二进制中的内容,考虑到 xxd 的输出会折行,我们选取了 ZSCViewController 字符串的子串进行过滤:

xxd ./libZSource.a | grep -C 5 'ZSCViewControlle'

果真得到了一些结果:

通过这个实验,我们确定了二进制中源码文件的路径确实是用普通的字符来存储的;紧接着,我们用 MachOViewer 来查看二进制文件,以获取到更友好的二进制信息。利用 MachOViewer,我们可以发现这些信息都存在了二进制的 “__debug_str” Section 中。

虽然还是不确定这个地址所对应的字段叫什么,但研究到这里,我们还是有所进展的,最起码我们可以假定这个路径一定是紧跟在 “Apple LLVM version 10.0.0 ” 字符后面的,然后利用一些读取 Mach-O 的 Ruby 库,比如 ruby-macho,基于这个假定来读取这个路径,为这个特性的工具化提供一丝可能性。

柳暗花明

简单的尝试没有得到想要的答案,但透过 Section 的名字,可以确定源码文件的路径信息和 DWARF 有关。

长时间和 CI 打交道的经验告诉我们,对于每一种二进制格式,苹果公司都会提供一个可以专门用于解析的命令行工具,所以我们就尝试找了找有没有解析二进制中 DWARF 格式的命令行工具。

功夫不负有心人,我们找到了 dwarfdump,那么用它来看看之前的那个二进制文件:

dwarfdump ./libZSource.a | grep 'ZSCViewContro'

果然有了更好的输出:

这里我们注意到了 AT_name 这个字段名。拿着这个字段名,去前面给出的 DWARF 1.1.0 Reference 文档中查阅,我们可以得知:

An AT_name attribute whose value is a null-terminated string containing the full or relative path name of the primary source file from which the compilation unit was derived.

进一步查询,我们可以找到另一个和他类似的字段 —— AT_comp_dir:

An AT_comp_dir attribute whose value is a null-terminated string containing the current working directory of the compilation command that produced this compilation unit in whatever form makes sense Forelax the host system.

看起来,这两个字段就是我们所苦苦追寻的答案了。

工程化

通过实验,以及找到的这两个字段的描述,我们基本可以确定,即便工程是使用二进制构建,只要二进制 AT_name 字段中的路径存在对应的源码文件,App 一样可以使用源码进行断点调试。这种调试方式除了修改源码再次构建不能生效以外,其他的调试场景都和直接使用源码构建无异。考虑到我们日常的调试场景绝大多数都只需要查看其他组件的源码,并不需要修改,把这个功能工程化还是非常有意义的。

那接下来的事情就比较简单了:

  1. 首先,我们需要确定大部分美团使用的组件二进制的编译目录是相同的。这样就方便我们在本地某个路径下统一管理下载的源码文件。
  2. 接下来,我们通过 dwarfdump 这个命令获取源码文件应该在的路径,然后通过给 CocoaPods 增加命令,将源码文件下载并放置在对应的路径中。

幸运的是,查看完美团 App 的几百个组件后,我们发现只有少数近一年内没有制作过二进制的组件路径比较不同,其他都相同,因此可以先忽略这一小部分组件。如果这部分组件需要支持该功能,只要再制作一次二进制即可。

确定方案以后,写代码就很简单了,最终我们利用 CocoaPods,提供了 zsource 的三个命令:

总结

zsource 功能整体的开发过程基本上都是基于一个个的猜想和实验来完成的,整体的开发上线过程实际上只花了两个晚上。但如果在没有基础知识的情况下,选择把上文中提到的参考资料都看懂后再动手,可能会花费更多的时间。这一个有趣的验证过程也充分说明,有时候我们可以不拘泥于冗长的文档以及资料,通过类似逆向工程的方式,非常快速地拿到我们需要的答案。此时我们再回过头去看文档,可能会获得比直接看文档更好的效果。

最后,非常感谢成威老师和志宇同学对技术的崇高追求,即便在飞机上,也愿意拿出电脑验证自己的猜想,为 zsource 后续的工程化落地提供了更多的可能。

作者简介

  • 宇杰,美团 iOS 工程师,2016 年加入美团,先后参与美团 App 持续集成平台建设、美团 App ReactNative 平台化等工作。目前在参与美团 App 工程效率提升和 Flutter 应用的相关工作。

美团 iOS 工程 zsource 命令背后的那些事儿相关推荐

  1. IOS工程自动打包并发布脚本实现

    文章首发地址:http://webfrogs.me/2013/02/18/ios-automation/ 作者:webfrogs 转载请注明出处. 前言 IOS的开发过程中,当需要给测试人员发布测试包 ...

  2. 使用脚本删除ios工程中未使用图片

    使用脚本删除ios工程中未使用图片 最近在读唐巧大神的<iOS开发进阶>,学到了一个大招:使用脚本删除ios中未使用的图片(纸书上有点小问题,参考github上的issue:使用脚本删除i ...

  3. ios 自动打包命令_通过命令行xcodebuild编译打包iOS应用

    点击上方"软件测试精品"关注我们 为什么要自动化打包? iOS编译打包需要签名,测试包又需要连接不同后台服务器,开发人员就需要不断地打开Xcode编译打包成ipa,然后上传到ftp ...

  4. 【IOS】IOS工程自动打包并发布脚本实现

    网上看到一个build ios工程并打包的脚本,写的很不错,学习了下,并添加部分注释,方便理解 http://blog.csdn.net/ccf0703/article/details/8588667 ...

  5. Alibaba iOS 工程架构腐化治理实践

    " 业务开发遇到环境问题越来越多,严重影响开发效率,有些表面看似打包问题,背后却是工程架构的腐化." 背景 近年来,iOS工程复杂度高的负面影响逐渐暴露,很多同学都受到了iOS打包 ...

  6. linux怎么编译ipa,关于自动编译iOS工程,生成app及ipa文件的方法

    关于自动编译iOS工程,生成app及ipa文件的方法1.所需语句(可直接在命令行中执行) xcodebuild -configuration Release 进入所在工程的根目录文件夹,执行上面的语句 ...

  7. 如何将 iOS 工程打包速度提升十倍以上

    如何将 iOS 工程打包速度提升十倍以上 过慢的编译速度有非常明显的副作用.一方面,程序员在等待打包的过程中可能会分心,比如刷刷朋友圈,看条新闻等等.这种认知上下文的切换会带来很多隐形的时间浪费.另一 ...

  8. 在iOS工程中用Cordova加载远程网页

    1.让前端同事创建一个Cordova项目,将需要的各平台添加进去.从他那里获取一个文件和一个文件夹 2.将获取文件和文件夹引入iOS工程 3.编译解决问题 添加pch文件 将所有报错的#import ...

  9. 计算机网络工程应用,计算机网络工程网络命令的应用

    计算机网络安全是计算机网络工程当中比较重要的环节,以下是小编搜集整理的一篇探究计算机网络工程发展现状的论文范文,供大家阅读参考. 摘要:处在当前的时代发展背景下,网络技术的发展已经进入到新的阶段,计算 ...

最新文章

  1. 香港居民换领新智能身份证 市民对办理过程表满意
  2. 分段埃尔米特插值Python实现并检查误差
  3. 修改Oracle数据库的连接数
  4. avframe转byte数组_C# amp; VB6.0 图像与二维数组 互转
  5. python的功能名称_ML获取功能选择后的功能名称SelectPercentile,python
  6. c++真的比java难吗_开课吧Java课程怎么样?开课吧的Java课程真的那么好吗?
  7. 【华为云技术分享】云图说|应用编排服务AOS,助力应用上云自动化
  8. 微信小程序获取用户的头像和昵称
  9. 我身边的那些有钱人。。
  10. spss-多元线性回归分析
  11. 九位SEO专家分享他们对Google核心更新的看法
  12. PESSIMISTIC_READ PESSIMISTIC_WRITE 与 共享锁 排它锁
  13. BitCoke测评报告 | TokenInsight
  14. Kafka SASL 安全认证
  15. Visual SLAM 笔记——李群和李代数详解
  16. Windows11电脑触摸屏不能用怎么修复
  17. 1-106兔子繁衍问题
  18. 2021年最佳知识库软件指南
  19. arcgis新建图层信息复制_arcgis如何创建图层要素 专家详解
  20. C#学习之web网站制作入门篇(ASP.NET)

热门文章

  1. 从一个视图控制器切换到另一个视图控制器的几种方式
  2. 毕业这几年的嵌入式开发之路
  3. android mysql 图片_android sqlite添加图片到数据库
  4. linux cocos环境变量,Linux开发cocos2dx程序环境搭建
  5. cython python3_30倍!使用Cython加速Python代码
  6. javascript mysql php_HTML、CSS、JavaScript、PHP、 MySQL 的学习顺序是什么?
  7. uasset python_Unreal Python 结合 C++ 开发蓝图库插件
  8. 智慧交通day02-车流量检测实现05:小车匀加速案例
  9. LeetCode 1602. 找到二叉树中最近的右侧节点(BFS)
  10. 奇异值分解(Singular Value Decomposition,SVD)