美团 iOS 工程 zsource 命令背后的那些事儿

zsource 命令是什么?

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

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

程序断点在二进制组件的代码中时的样子

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

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

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'

果真得到了一些结果:

xxd 命令的输出结果

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

MacOViewer 的结果

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

柳暗花明

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

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

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

dwarfdump ./libZSource.a | grep 'ZSCViewContro'

果然有了更好的输出:

dwarfdump 的输出

这里我们注意到了 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 的三个命令:

pod zsource 命令

总结

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

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

作者简介

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

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

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

相关文章

互联网大厂CTR预估前沿进展

文 | Ruhjkg编 | 小鹿鹿lulu源 | 知乎前言CTR(click through rate)预估模型是广告推荐领域的核心问题。早期主要是使用LR(线性回归)人工特征工程的机器学习方法,但是存在人工组合特征工程成本较高,不同任务…

以史为鉴 | 为什么要将「知识图谱」追溯到1956年?

本文转载自公众号:AI科技评论。作者 | Claudio Gutierrez 编译 | MrBear编辑 | Tokai以史为鉴,可以知兴替。纵观近期包括 AAAI、NeurIPS、IJCAI 在内的AI顶级会议,对图结构模型的研究是一个绕不开的话题,大量学者涌入这个赛道&…

Android官方开发文档Training系列课程中文版:添加ActionBar之ActionBar浮层效果

原文地址 : http://android.xsoftlab.net/training/basics/actionbar/overlaying.html 浮层效果的ActionBar 默认情况下,ActionBar总是会出现在Activity窗口的顶部,这样会稍微的减少Activity布局的剩余空间。如果需要在用户使用的时候隐藏和显示Action…

美团大规模微服务通信框架及治理体系OCTO核心组件开源

微服务通信框架及治理平台OCTO作为美团基础架构设施的重要组成部分,目前已广泛应用于公司技术线,稳定承载上万应用、日均支撑千亿级的调用。业务基于OCTO提供的标准化技术方案,能够轻松实现服务注册/发现、负载均衡、容错处理、降级熔断、灰度…

领域应用 | 知识结构化在阿里小蜜中的应用

本文转载自公众号:DataFunTalk。分享嘉宾:李凤麟 阿里巴巴 算法专家文章整理:付一韬内容来源:2019知识图谱前沿技术论坛出品社区:DataFun导读:阿里小蜜是阿里巴巴服务领域的重要人工智能产品,是…

内卷的世界,我们是否可以换一种思维生活?

文 | Flood Sung源 | 知乎前言今年最热门的词汇之一当属内卷了。似乎很多行业都由于份额有限而陷入内卷当中。最火的或许是清华学生的这张图,“骑车写代码”:图片来自网络虽然后来知道是这位同学怕关了屏幕程序就断了,但这不禁让人思考&#…

LeetCode 513. 找树左下角的值(按层遍历 queue)

1. 题目 给定一个二叉树,在树的最后一行找到最左边的值。 2. 解题 利用队列按层次遍历顺序,根右左,要求最左边的一个,所以根右左,最后一个队列元素就是答案 class Solution { public:int findBottomLeftValue(TreeN…

Hadoop YARN:调度性能优化实践

背景 YARN作为Hadoop的资源管理系统,负责Hadoop集群上计算资源的管理和作业调度。 美团的YARN以社区2.7.1版本为基础构建分支。目前在YARN上支撑离线业务、实时业务以及机器学习业务。 离线业务主要运行的是Hive on MapReduce, Spark SQL为主的数据仓库作…

LeetCode 39. 组合总和(排列组合 回溯)

1. 题目 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取。 说明: 所有数字(包括 target)都是正整数。 解集不能包…

深度学习平台的未来:谁会赢得下半场?

今天这篇文章无意引战,只想从历史发展的角度来谈谈深度学习大背景下的开发工具变迁,以及对未来发展趋势的想象。TensorFlow:无力回天的深度学习里程碑不知道有多少小伙伴是2017年以前入坑深度学习的,那时候人工智能概念火热&#…

论文浅尝 | 基于属性嵌入的知识图谱实体对齐

论文笔记整理:王中昊,天津大学硕士,方向:自然语言处理。来源:AAAI2019论文链接: https://doi.org/10.1609/aaai.v33i01.3301297概述知识图谱之间的实体对齐的任务目标是去找到那些在两个不同的知识图谱上表…

基本功 | Litho的使用及原理剖析

1. 什么是Litho? Litho是Facebook推出的一套高效构建Android UI的声明式框架,主要目的是提升RecyclerView复杂列表的滑动性能和降低内存占用。下面是Litho官网的介绍: Litho is a declarative framework for building efficient user interfa…

论文浅尝 | 基于深度强化学习将图注意力机制融入知识图谱推理

论文笔记整理:陈名杨,浙江大学直博生。Introduction知识图谱(KGs)在很多NLP的下游应用中起着越来越重要的作用。但是知识图谱常常是不完整的,所以解决知识图谱补全的任务也非常重要。主要有三种方法来完成知识图谱补全…

聊聊如何提升推荐系统的结果多样性

文 | 洪九(李戈)源 | 知乎个性化推荐系统的出现为处理信息过载问题提供了一个有效的工具,已经成为互联网各大平台(电商、信息流等)的标配,并在技术(个性化召回、个性化排序等)上取得了长足的发展,逐渐从传统模型过度到深度学习时代。但是&…

论文浅尝 | GNN with Generated Parameters for Relation Extraction

论文笔记整理:申时荣,东南大学博士生。地址:https://arxiv.org/pdf/1902.00756.pdf来源:ACL2019在许多自然语言处理任务(例如关系提取)中,多跳关系推理是必不可少的,而图神经网络&am…

大众点评信息流基于文本生成的创意优化实践

1. 引言 信息流是目前大众点评除搜索之外的第二大用户获取信息的入口,以优质内容来辅助用户消费决策并引导发现品质生活。整个大众点评信息流(下文简称点评信息流)围绕个性化推荐去连接用户和信息,把更好的内容推荐给需要的用户。…

LeetCode 701. 二叉搜索树中的插入操作(二叉查找树/插入)

1. 题目 给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 保证原始二叉搜索树中不存在新值。 注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索…

docker的简单操作和端口映射

docker的简单操作和端口映射:https://www.cnblogs.com/lixaingyang/p/11976827.html docker的简单操作和端口映射 一:简介 Docker镜像 在Docker中容器是基于镜像启动的 镜像是启动容器的核心 镜像采用分层设计,最顶层为读写层 使用快照COW技…

Android官方开发文档Training系列课程中文版:管理Activity的生命周期之启动一个Activity

原文地址 : http://android.xsoftlab.net/training/basics/activity-lifecycle/index.html 导言 用户通过导航退出或者返回应用的时候,应用中Activity的生命周期会在不同的状态之间变换。举个例子,当Activity初次启动的时候,它会来到系统的…

论文浅尝 | 利用多语言 wordnet 上随机游走实现双语 embeddings

论文笔记整理:谭亦鸣,东南大学博士生,研究方向为知识图谱问答。来源:Knowledge Based System链接:https://www.sciencedirect.com/science/article/abs/pii/S0950705118301412?via%3Dihub双语word embedding将两种语言…