从验证可行性跑通最小闭环,到搭建群控机架一次次为集群扩容,实属不易。
个中涉及到大量的方案谈论乃至推翻,很多思路和实现细节是业界找不到公开方案的,只能自己摸索。
本文详细分享了手 Q 防劣化系统的构建链路,相信对业界和开拓者们都有较高的借鉴意义。

作者|杨萧玉、邱少雄、张自蹊、王褚重天、姚伟斌
责编 | 梦依丹
出品 | 腾讯云开拓者

为什么要做防劣化
代码体量较大:业务涵盖 IM/空间/短视频/超级 QQ 秀等。
迭代需求紧:双周迭代,研发职员多;每版本几十条需求分支,主干每周构建出包数百次。
问题较多:需求合流新增性能问题多,根本侧人力寡不敌众,问题越堆越多,事后回溯效率低。
当业务的体量足够大,问题足够繁芜的时候,办理问题的思路也须要转变。
1.1 如何破局
盘点了下手 Q 研发流程的困局,现有的手段更着重于线上监控问题并不才个版本修复(乃至是下下个版本),如果能在开拓阶段发布前乃至合入 master 之前就把问题扼杀在摇篮之中,就可以达到防劣化的目标。
下图根据手 Q 真实惨痛案例改编,当年为了查一个严重的启动耗时劣化问题,二分法拉代码手动跑 Instruments 的痛,懂的都懂。

此谈天记录为虚构,如有雷同纯属巧合
大家开拓需求都爱赶 deadline,以是合流高峰期光靠堆人力代码 CR 和手动测试性能是不现实的,性能问题漏出事后优化也是不足的。
由于业务的繁芜性,总是优化赶不上劣化快。
手 Q 在优化性能稳定性的同时,也提前布局防劣化系统,将其作为质量三位一体中的主要一环:
1.2 我们的口号:Hold the door!
提前创造部分主路径问题,通过门禁防止性能劣化:
主干合流门禁:对付较稳定的性能指标,合流前自动检讨。
日常自动提单:针对偶现的性能问题,开拓阶段提前创造。
性能数据看板:常态化详细数据看板,上帝视角不雅观测性能。
告警机器人:自定义各性能维度告警规则,第一韶光创造问题。

QQ 客户端机能稳定性防劣化系统 Hodor 技能筹划

防劣化系统的实现
由于整套系统的实现比较繁芜,考虑到整体篇幅限定,数据采集部分仅概述 iOS 平台的方案,各平台数据的上报协议及做事真个处理逻辑是共享的。
2.1 方案设计
要做好门禁,就须要把性能数据精确到每一次 commit,并做好科学的比拟。
现实情形是很繁芜的,可能有各种各样的突发情形:

基于以上诉求,我们开拓了 feature 分支比拟 master 的算法策略。
建立全维度性能指标和科学归因方案。
Hodor 实现了性能报告、数据剖析、智能调度、提单告警、设备管理、用例管理等一系列能力,大概的运行机制如下:

此方案的优点:
性能测试和性能报告创建审批左移到开拓阶段。
覆盖场景可拓展:测试用例云端独立管理派发。
性能维度可拓展:支持 Instruments 所有模板。
静态检讨可拓展:构建数据做事端存储与比对。
2.1.1 防劣化 ≠ 自动化测试
现有测试平台和工具的不敷:测试平台站在创造问题角度,缺少办理问题思维;APM SDK 运行时采集能力受限
传统的自动化性能测试平台和工具的报告只有 metric 统计数据和 App 截图/日志,信息太少不敷以定位问题。
APM 平台的线上监控很强大,但无法动态追踪采集 time profiler 等详细数据。
以是办理问题依然须要开拓同学获取更详细信息或能够 debug 复现问题,而性能问题每每是偶现的。
自动防劣化办理方案:打通创造和解决问题全链路:
通过 Instruments 动态追踪技能采集 diagnostic 诊断数据,无侵入性。
xctrace 自动解析 trace 文件,翻译堆栈精准归因,还原『案创造场』。
每次提交构建均实行防劣化检测,精准定位问题的提交引入者。
数据可视化看板+自动提单派发+大模型 AI 剖析问题 = 测开降本增效。
总之,手 Q 的办理方案从一开始就冲破了传统思维,终极的方案也是真喷鼻香!
2.1.2 繁芜业务下如何降落性能颠簸
俗话说细节决定成败,很多事情想跑通 demo 很随意马虎,但是想达到高可用性,须要打磨很多很多细节才能运用莅临盆环境。
比如为了掌握变量降落场外成分颠簸,大到集群调度策略,小到机器零件型号,都锱铢必较。
2.2 数据采集
在运行时性能数据采集方面,我们拥有一套自研方案;在静态扫描方面,也从编译链接过程采集了干系数据。
2.2.1 动态性能数据采集
性能采集方案选型之初,我们对付性能采集紧张有以下几个诉求:
须要有详细的堆栈信息;
性能维度足够多;
最好非侵入式的;
随意马虎与 CI 流程相结合。
为了知足上述诉求,终极我们选则了当时苹果刚推出的 xctrace 方案。
运用外数据采集:
xctrace
Instruments 是 iOS & macOS 平台进行性能剖析必不可少的工具,它能采集到绝大多数的性能数据,同时拥有精美的 GUI 方便排查和剖析。
但是 Instruments一贯都只有 GUI(古从前月曾经有过导出性能数据的功能,后面也去掉了),没有 CLI,这也使自动化利用 Instruments 进行性能采集和剖析成为奢望。
直到 Xcode 12,苹果终于推出了 Instruments 的 CLI 版本:xctrace。
xctrace 供应了一系列命令,可以录制、导出性能数据,Instruments 支持哪些性能维度,xctrace 就支持哪些性能维度。
我们根据不同的性能采集需求,天生不同的性能模板,利用 xctrace 录制对应的性能数据。
录制好的 trace 文件可以通过 xctrace 导出为 XML 格式的文件,从 XML 文件中读取到性能数据。
值得一提的是,手 Q 作为早期第一批吃螃蟹利用 xctrace 的技能团队,我们一边摸着石头过河探索 workaround,一边与苹果团队互助以提升其可用性。
Xcode 14 Release Notes 中办理的多个 issue 也是手 Q 团队在防劣化开拓过程中向 Apple 提出的:

以是手 Q 团队已经默默帮大家踩过很多坑了!
Xcode Memory Graph
在排查内存透露干系问题时,利用 Instruments 内存干系模板只能看到工具的创建堆栈和引用计数的增减过程,无法展示工具间的引用关系。
面对这类问题,Xcode Memory Graph 是更好的选择,但 Xcode Memory Graph 也是一个嵌入到 Xcode 的 GUI 程序,目前为止还没有 CLI 实现。
为此,我们调研了 Xcode Memory Graph 的实现,获取到干系协议,实现分开 GUI 天生 Xcode 内存图,并利用 heap、vmmap、leaks 等工具剖析内存图,实现自动采集内存图,进行大内存占用和内存透露的剖析和监控。
Crash
Crash 的监控比较大略,我们是通过检讨测试过程中设备上有没有新天生的 ips 文件办法来监测 Crash 的。
这种办法的优点是监控范围广,SIGKILL、pre-main 阶段 Crash 等常规办法无法捕获的 Crash 也能监控到。
实践中碰着的一个小坑是单台设备每天单个 App 天生 ips 文件是有上限的,之前测试阈值是 50,崩溃超过 50 次就不再天生 ips 文件。
高频日志
剖析客户端日志是必不可少的排查问题的手段,但大型项目业务繁多,会涌现很多无效高频日志,高频日志会占用大量的 IO 和 CPU 资源造成卡顿和发热,乃至有导致日志文件过大,无法打捞起来的情形。
而且很多时候,高频日志的背后便是一段去世循环或者循环调用逻辑的存在,以是我们对高频日志也进行了监控和告警提单。
运用内数据采集:
流量监控
流量下载的数据采集,虽然 Instruments Network 模块能够监控所有的下载要求,但 Network 上显示的流量大小依赖了 Response Header的 Content-Length 或 Range 字段。
由于部分后台做事器并没有填写该字段,以是 Instruments 上无法获取总的下载流量大小,故而放弃 Instruments 上采集数据,改用 App 运行时网络数据。
业务打点
性能数据须要和业务场景进行关联,我们采取了苹果的 Signpost 方案进行打点,选择 Signpost 打点方案的缘故原由紧张是下面 3 点:
和 Instruments 高度契合,Instruments 有 os_signpost 模板,运用内利用 signpost 干系接口打的点,在 Instruments GUI 展示性能数据时,也能将业务打点一并展示,方便排查问题;
signpost 打点数据可以利用 xctrace 进行导出,可以实现业务场景和性能数据的干系联;
比较 print 打点办法,signpost 性能损耗更低。
2.2.2 静态扫描能力
符号扫描
平台有两套符号扫描工具,都是面向链接期产物 (Mach-O Image) 进行静态扫描,分别洞察产物中的 Objective-C (简称 OC) 符号问题和原生符号问题。
OC 符号扫描:
OC 符号扫描工具,帮助扫描工程产物中存在的 OC Category 同名方法覆盖和 +load 静态初始化方法。
OC Category 同名方法覆盖是指 Category 机制隐含的运行时实现覆盖问题,覆盖问题有两种环境:1. 若主类 (原类) 存在一个与 Category 扩展方法同名的方法,则运行时会选择 Category 的实现利用。
2. 若存在多个 Category 都对同一个类扩展了同名的方法,则运行时会选择个中一个 Category 的实现利用。
这两种情形都可能导致程序逻辑非预期地调用到其他库的实现,涌现功能非常或崩溃。
该问题相对暗藏不易被察觉,由于在链接期间不会产生警告。
只管代码规范哀求 Category 方法名必须加前缀来规避该问题,但该问题在大型多源项目的集成过程中,还是时有发生,只是每每由于恰好兼容没出问题而没感知。
直到某天改动后涌现莫名非常,溯源后才创造。
+load 方法在程序加载的静态初始化阶段实行,会影响运用的启动耗时。
这两类方法(符号)对稳定性和性能有全局性的影响,因此平台培植了工具来关注这些符号。
工具综合基于 class-dump 和链接器天生的 LinkMap 信息 (如果有),获取产物中的全部 OC 符号和来源,统计筛选出重名 Category 方法和 +load 方法。
并与 CI 构建检讨相结合,监控和管控这两类问题方法,设立门禁哀求业务新引入 +load 和重名方法须拉通根本侧 Review。

原生符号扫描:
原生符号扫描工具,帮助扫描工程所有依赖库中存在重复的库函数(符号) (紧张关注 C 符号重复问题)。
常日重复的库函数是 C/C++ 编写的根本实用函数,这大部分归咎于 C/C++ 短缺广泛认可的依赖管理范式,部分大型业务静态库采纳将其依赖的实用方法库也一同编译打包 (ar) 的范式而导致。
这些实用方法库常日是广泛利用的根本实用库,如 FishHook、zip、libffi 等。
若有多个业务静态库都集成了同源的根本实用库,在链接 (ld) 天生可实行程序时,链接器会选择个中一份链接 (取决于链接先后顺序等成分,可以通过 LinkMap 确认选用的实现),它们虽然具有相同的符号 (API),但版本/实现未必同等、ABI 未必兼容,以是如果链接时选取的实现不恰当,则可能涌现功能非常或崩溃。
通过原生符号扫描工具,扫描出重复的库函数,有助于标识出上述这样\"大众存在多份重复选其一不兼容\"大众的潜在风险。
工具的事情流程是解析链接 (ld) 参数,遍历每一个参与链接的静态库,利用 nm 工具等工具读取它们包含的对外导出 (External & Defined) 符号。
实践中集成到 CI,在构建完成后的现场回溯构建日志取得链接 (ld) 参数并实行,统计出重复的原生符号并根据规则登记归档。

终极的统计结果会展示在 Hodor 平台,可以查看每个 commit 的重复符号变革情形:

dyld hash 碰撞扫描
在 Hodor 防劣化系统上线了一段时候后,我们抓到了一个冷启动劣化的 case:主干上一个新提交导致冷启动 T0 阶段(pre-main)劣化了 150+ ms,但该提交只修正了一个方法名。
在打消掉外部成分、测试环境稳定性成分之后,创造真的由于一个方法名导致冷启动劣化那么多。
问题看起来很棘手,幸好,我们有详细的 trace 文件,在详细剖析比拟了劣化前后的堆栈之后,创造劣化的根源是冷启动创建启动闭包时调用了一个 perfect hash 的算法,新修正的方法名导致这个算法的碰撞次数增加了,发生碰撞的情形下,须要 rehash,于是耗时增加。
以下是天生启动闭包的简要流程:

找到了劣化的缘故原由,那如何找到发生碰撞的方法名呢?答案只能去 dyld 源码里找,还好,dyld 是开源的,我们在 dyld 源码里找到了天生启动闭包干系的部分,在发生碰撞的地方输出对应的 sel 名字,将其编译起来后,把 QQ 的 Mach-O 扫一遍,这样我们就找到了所有的碰撞点,之后我们修复了所有的碰撞点,冷启动得以降落了 700 ms(iPhone 11)。
我们将该扫描工具支配回了 Hodor 系统,监测每一个提交的碰撞情形,同时我们也将这个问题反馈给了苹果卖力 linker 的团队。
2.3 任务调度
职责在于监听 Git 事宜与自定义事宜并天生多类型的性能测试任务,在得当的韶光点将任务与得当的测试机进行匹配然后天生配置文件,末了将配置文件派发到数据采集端驱念头能测试任务在对应测试机上实行。
2.3.1 任务类型
防劣化性能测试任务紧张分为以下几大类
主流程测试:
由根本侧供应的核心测试用例组,测试流程包括手Q的几个核心场景进行测试(启动、登录、AIO、频道、短视频等),所有分支默认运行当前测试用例组。
后续性能报告也是基于当前用例组所上报的性能数据来进行比拟。
担保统一的测试用例流程与环境,性能数据的比拟才是可信任的。
专项测试:
针对某些性能维度(内存、IO、预下载流量检测等)单独进行测试。
最终生成相应性能看板。
自定义用例测试:
手 Q 功能场景十分的弘大繁芜,根本用例也无法覆盖到所有的场景,由此出身自定义测试用例功能。
如果业务同学想不雅观察自己所处业务部分详细的性能数据,防劣化系统支持由各业务来编写自定义的测试用例,测试完毕后根据上报数据与定义的场景将自动天生相应性能看板。
Crash、Monkey 测试:
在日常开拓中,发生 Crash 问题将会严重影响全体项目开拓进度。
我们希望能第一韶光将问题检测暴露出来并推动修正。
启动以及主流程 Crash 则是最为严重的,直接导致项目不可利用,影响大家日常开拓。
Master 主干的每一个 Commit 合入都会进行 Crash 测试。
如果发生 Crash,会立即拉群关照排查。
而对付非启动以及主流程 Crash 问题则会进行自动提单。
而 Monkey 测试则是仿照用户操作,无序进行操作。
能够尽可能的将 Crash 问题暴露出来。
闲时利用:
为了更充分的利用防劣化系统,在空闲韶光(深夜、周末)会对过去已经测试过的主干 Commit 再次进行测试。
用尽可能多的测试来暴露出更多的问题。
2.3.2 任务调度管理
所有天生的测试任务会根据任务类型,优先级等条件进行一轮排序,终极优先担保最紧急的任务最优先实行。
大略示意图如下:

当任务状态非常时,也会有告警。
2.3.3 设备管理
针对不同类型的任务采取不同的策略进行测试机分配。
对付 Crash 任务,为了担保能第一韶光创造问题,会分配专门的机器池进行测试。
对付性能任务,根据版本流程与任务优先级进行动态分配。
根本性能>业务自定义>=专项测试>闲时利用。
设备环境发生问题,也将及时进行告警:

2.4 数据处理
由于 Instruments 采集到的性能数据量巨大,动辄 GB 级别,无法全量上报,以是性能数据采集时会进行符号化和性能问题的剖析,比如找出卡顿堆栈、内存透露的工具等。
剖析完毕后会将数据上报给做事端,由做事端进一步处理。
职责在于将上报的数据根据不同规则进行打算存储。
不同维度性能根据不同规则打算,得出相应的性能结果并消费劣化性能数据(自动提单与告警)。
2.4.1 不同类型性能数据的处理
1. 根本性能数据
对付根本性能数据而言(CPU、内存、IO、线程数),上报的数据是原始每一次采样所得数据(大致在一秒采样一次),这里出身了两种打算办法。
1.1. 对付关注整体性能数据以及流程比较短的用例,则会整体打算出三个维度的数据:峰值数据、均匀数据、结束时数据。
1.2. 对付有定义「场景」的用例,会根据所通报的打点(Signpost)值来找到对合时光范围的数据进行打算。
同样因此上三个根本维度,其余新增一个耗时打算。
整体示意图如下:

2. 重点性能数据
对付重点关注的数据(启动韶光,启动线程状态),采集端会利用专门的模板来进行测试,上报数据后。
Server 端对多次测试结果数据进行综合打算,得出结果末了展示在相应看板上。

3. 自定义性能数据
对付自定义上报数据(重复符号变动,启动阶段函数监控),则是开放专门上报数据接口,由对应业务方自主打算上传(防劣化会向业务方供应基本数据)。
防劣化系统卖力记录数据并展示相应看板。

2.4.2 消费性能劣化数据
1、自动提单
我们会定时扫描数据库中上报的性能劣化信息。
先根据白名单以及过滤规则进行筛选,然后将须要提单的数据进行信息聚合,终极以提单的形式将问题自动分配给对应的业务卖力人。

bug 单包含了毛病的堆栈等详细信息:
2、性能告警
对付主干的性能数据进行实时监控,不同用例不同性能维度可以配置不同的告警规则。
当发生劣化时及时将对应的信息抛到对应业务群中进行告警。

2.5 管理端展示
2.5.1 防劣化看板
防劣化看板支持查看指定时间、分支、测试用例和场景下的每个 commit 的状态以及各项性能数据,并可以快速标记 commit,支持与任意 commit 的性能数据做比拟。

2.5.2 分支性能报告
防劣化系统会对所有需求分支的每一次 push 进行性能测试,然后与对应的主干 Commit 性能数据进行比拟天生性能报告。
能直不雅观的看到需求分支的性能变革,当性能发生劣化的时候也能直不雅观的看到是从哪个 Commit 开始引入劣化问题,方便问题排查。

测试报告有多种状态,比如“等待数据上报”、“自动审批通过”、“自动审批不通过” 等:

当测试报告“自动审批不通过” 时,也会标注出是哪些指标不通过,便于开拓者迅速定位问题:

2.5.3 测试用例管理
根本所供应的主流程测试用例一定是无法覆盖手 Q 所有的场景,因此供应开放能力,支持各业务方的开拓、测试自主供应测试用例。
在防劣化平台上进行配置测试,测试完毕后自动根据配置天生相应的性能看板。

同时对正在运行的测试用例进行成功率监控,低于一定的成功率将进行告警。
如业务方在一段韶光内没有处理告警,会将其临时下架避免资源摧残浪费蹂躏。
2.6 整体架构

收益与总结
Hodor 上线后收益显著,研发效率大幅提升!

通过将问题创造和解决左移到开拓阶段,可以有效防止问题漏出到线上导致大盘数据劣化。
如某次提交导致主干启动耗时上涨,基于防劣化系统可精准快速定位到代码提交者。

Hodor 系统还在不断迭代中,2024 年还拓展了 QQ 桌面客户端,并在运行效率方面持续优化。
目前防劣化系统已经落地了 QQ 各平台。
防劣化系统从 0 到 1 迭代了将近三年的韶光。
从验证可行性跑通最小闭环,到搭建群控机架一次次为集群扩容,实属不易。
个中涉及到大量的方案谈论乃至推翻,很多思路和实现细节是业界找不到公开方案的,只能自己摸索。
在培植过程中我们碰着了不少很底层的问题须要与厂商沟通,比如与 Apple 的技能专家们线上和线下互换过程中也学到了不少,在此也感谢 Apple。
客户真个性能稳定性防劣化是一个很繁芜的话题,而且只有体量足够大的业务才会面临更多的寻衅。
正由于我们面对的很多问题业界都无先例可循,以是也期待行业内后续有更多的分享和互换。
同时,我们也期望 Hodor 不仅在性能稳定性方面发挥浸染,未来也会把手 Q 研发效能的各项指标集成进来。
\"大众>