WWDC Quick Look 💓 By SwiftGGTeam
What's new in MetricKit

What's new in MetricKit

观看原视频

Highlight

iOS 13 引入了 MetricKit,允许开发者收集用户设备的聚合性能指标。但当时只有 MXMetricPayload,你能看到 hang duration histogram 里出现异常或长时间挂死,却不知道根因在哪里。iOS 14 新增了 MXDiagnosticPayload,带来了崩溃诊断(crash diagnostics)和挂死诊断(hang diagnostics),每种诊断都包含完整的调用栈信息。

核心内容

MetricKit 的定位很清楚:它补上开发者看不到真实用户设备的阶段。App 进入 TestFlight 或 App Store 后,你通常不能拿到用户手里的设备,也不能让用户打开 Instruments。MetricKit 在设备上被动收集电量和性能数据,按 24 小时聚合成 payload,再把匿名数据交给 App。

iOS 13 的 MetricKit 已经能回答”这个版本有没有变慢”。它给出启动时间、CPU 时间、内存等聚合指标,适合做 build-to-build 的趋势比较。问题在于,聚合指标只能指出症状。比如冷启动次数异常增多,或者 hang duration histogram 里出现长时间挂死,你仍然缺少根因现场。

iOS 14 的 MetricKit 2.0 扩展了两个方向。第一是新增 metrics:CPU instructions、scroll hitches、application exits。它们让开发者更准确地衡量工作量、滚动流畅度和退出原因。第二是新增 diagnostics:hang、CPU exception、disk write exception、crash。diagnostic payload 与 metric payload 同一天送达,开发者可以把趋势和调用栈放在同一条排查链路里。

这场 session 的核心价值不在于多了几个指标名。它把线上性能问题从”看到异常曲线”推进到”拿到足够定位的信息”。MetricKit 仍然是延迟的、聚合的、保护隐私的数据源,但它开始提供调用栈树、异常原因和诊断 metadata,足以支撑发布后的性能回归 triage。

详细内容

接入 MetricKit 仍然是订阅模型

02:11)MetricKit 的基础接入流程没有改变。App 链接并导入 MetricKit,创建 MXMetricManager.shared,让自定义对象遵守 MXMetricManagerSubscriber,再实现接收 metric payload 的方法。

import MetricKit

class MySubscriber: NSObject, MXMetricManagerSubscriber {

    var metricManager: MXMetricManager?

    override init() {
        super.init()
        metricManager = MXMetricManager.shared
        metricManager?.add(self)
    }

    override deinit() {
        metricManager?.remove(self)
    }

    func didReceive(_ payload: [MXMetricPayload]) {
        for metricPayload in payload {
            // Do something with metricPayload.
        }
    }

}

关键点:

  • MXMetricManager.shared 是 App 与 MetricKit 框架交互的入口。
  • metricManager?.add(self) 开始订阅系统聚合的 metric payload。
  • metricManager?.remove(self) 在对象释放时取消订阅,避免 manager 持有无效 subscriber。
  • didReceive(_ payload: [MXMetricPayload]) 接收按天聚合的性能指标数组。
  • transcript 明确说明 payload 是匿名数据,用于保护用户隐私。

新 metrics 补足工作量、流畅度和退出原因

05:21MXCPUMetric 新增 CPU instructions。它统计 App 每天累计 retired instructions。CPU time 会受到时钟频率影响,CPU instructions 是硬件和频率无关的工作量指标,更适合判断一次改动是否真的让 CPU 工作变多。

05:48)scroll hitches 面向图形性能。session 把 hitch 定义为滚动时某一帧没有在预期时间出现在屏幕上,用户看到的结果就是动画不顺。MetricKit 提供的是 App 在 UIScrollView 滚动期间发生 hitch 的时间占比。

06:28)application exits 给出前台和后台退出原因的每日汇总。它不只统计”退出了几次”,还按原因和次数拆分。Apple 特别提到,这类数据有助于排查启动问题,以及使用后台运行框架时遇到的常见终止问题。

关键点:

  • CPU instructions 用来衡量 App 在 CPU 上做了多少实际工作。
  • scroll hitches 把滚动卡顿变成线上可聚合的比例指标。
  • application exits 把前台和后台退出原因纳入同一天的性能 payload。
  • 这些 metrics 适合和版本号、设备类型、发布批次一起存储,形成回归趋势。

diagnostics 与 metrics 走同一条管线

08:14)iOS 14 新增的 diagnostics 不需要重写采集架构。subscriber 继续使用同一个协议,只要再实现一个接收 MXDiagnosticPayload 的方法。

func didReceive(_ payload: [MXDiagnosticPayload]) {
    for diagnosticPayload in payload {
        // Consume diagnosticPayload.
    }
}

关键点:

  • 方法签名和接收 MXMetricPayload 的版本几乎一致。
  • transcript 明确说 diagnostics 的语义和 metrics 基本一致。
  • 系统在一天内被动收集诊断信息,最后打包成 daily diagnostic payload。
  • diagnostic payload 会和同一天的 metric payload 对应,适合复用已有上报和后端处理管线。

MXDiagnosticPayload 提供可离线处理的调用栈树

09:18)diagnostic interface 由几类基础对象组成:MXDiagnostic 是诊断基类,MXDiagnosticPayload 是一天内所有诊断的承载对象,MXCallStackTree 封装回归发生时的 backtrace。每个 diagnostic 还包含发生问题时的 App metadata,例如具体 build version。

10:08MXCallStackTree 里的 backtrace 是未符号化数据,设计目标是离线处理。transcript 说明 JSON 中包含 ATOS 符号化需要的信息,包括 binary UUID、offset、name 和 frame address。这个结构也会出现在 Apple 同年发布的其他性能工具里。

关键点:

  • MXDiagnosticPayload 让 App 拿到具体诊断,而不只是聚合数值。
  • MXCallStackTree 保留 regression 发生时的调用栈树。
  • 调用栈默认未符号化,需要在设备外结合符号表处理。
  • 后端可以把 metric payload 的趋势和 diagnostic payload 的调用栈按日期、版本和 build 关联起来。

四类诊断覆盖 hang、CPU、磁盘写入和 crash

11:15)hang diagnostics 记录 App 长时间不响应用户输入的情况。session 明确把原因指向主线程被阻塞或繁忙,payload 提供主线程不响应的时长,以及主线程的 backtrace。

11:35)CPU exception diagnostics 对应 Xcode Organizer 里的 energy logs。它包含高 CPU 使用期间消耗的 CPU time、总采样时间,以及消耗 CPU time 的线程 backtrace。它适合处理本地难以复现的 CPU 回归。

11:59)disk write exception diagnostics 和 CPU exception diagnostics 很像。它记录触发异常的总写入次数,以及造成 excessive writes 的线程 backtrace。Apple 给出的触发条件是 App 突破每天 1 GB 的磁盘写入阈值。

12:19)crash diagnostics 由 MXCrashDiagnostic 表达。每次 App crash 时,MetricKit 会提供 exception information、termination reason、bad access crash 下的 virtual memory region information,以及 backtrace。

关键点:

  • hang 诊断指向主线程长时间不可响应。
  • CPU exception 诊断指向高 CPU 使用期间的线程调用栈。
  • disk write exception 诊断指向突破每日写入阈值的写入来源。
  • crash 诊断包含异常信息、终止原因、部分内存区域信息和调用栈。

核心启发

  • 做什么:给 App 做一个 MetricKit 上报层。 为什么值得做:metrics 和 diagnostics 都通过 MXMetricManagerSubscriber 下发,App 端接入点很少。 怎么开始:在启动阶段创建 subscriber,分别实现 didReceive(_ payload: [MXMetricPayload])didReceive(_ payload: [MXDiagnosticPayload]),把 payload 转成后端能存储的 JSON。

  • 做什么:建立发布后性能回归看板。 为什么值得做:CPU instructions、scroll hitches 和 application exits 都是按天聚合的线上指标,适合观察新版本相对旧版本的变化。 怎么开始:按 build version 存储 metric payload,先画 CPU instructions、scroll hitch ratio、foreground/background exit reasons 三组趋势。

  • 做什么:把 diagnostics 变成 triage 队列。 为什么值得做:hang、CPU exception、disk write exception 和 crash 都带有可离线处理的 backtrace,比单纯的百分比指标更接近根因。 怎么开始:后端按 build version 和 diagnostic 类型分组,把调用栈树符号化后聚合,优先处理出现次数高或影响新版本的栈。

  • 做什么:把线上 scroll hitch 指标和 XCTest 性能测试连起来。 为什么值得做:MetricKit 提供真实用户滚动期间的 hitch 比例,XCTest 可以在 CI 中提前测量动画回归。 怎么开始:用本场提供的 MetricKit 数据监控线上趋势,再参考 10077 为核心 UIScrollView 场景补充可重复运行的性能测试。

关联 Session

评论

GitHub Issues · utterances