mcpField
A
axiom-testflight-triage

axiom-testflight-triage

github.com/charleswiltgen测试与质量1.2k179 次安装今天更新
代码仓库

关于 axiom-testflight-triage

axiom-testflight-triage 是供开发者通过 Xcode Organizer 调查 TestFlight 崩溃和测试反馈的工作流技能,涵盖崩溃解读、符号化、终止报告及测试者情境。

主要功能

  1. 01在 Xcode Organizer 中查看崩溃
  2. 02使用 dSYM 文件进行符号化
  3. 03解读异常类型与调用栈
  4. 04调查没有崩溃报告的终止
  5. 05查看测试者反馈和设备信息

适用场景

  1. 01定位 Organizer 崩溃发生的位置
  2. 02分析尚未符号化的崩溃报告
  3. 03调查没有崩溃记录的应用终止
  4. 04结合测试反馈了解崩溃条件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-testflight-triage

安装后在 Claude Code 中输入/axiom-testflight-triage确认已加载。

详细介绍

TestFlight 崩溃与反馈分类处理

概述

使用 Xcode Organizer 调查 TestFlight 崩溃并审查 Beta 反馈的系统化工作流。核心原则: 编写任何修复代码之前,先弄清楚崩溃原因——花 15 分钟进行分类处理,可以避免数小时的调试。

警示信号——遇到以下情况时使用此技能

  • “Beta 测试人员说我的应用崩溃了”
  • “我在 App Store Connect 指标中看到了崩溃,但不知道该如何调查”
  • “Organizer 中的崩溃日志没有符号化”
  • “用户发来了一张崩溃截图,但我无法复现”
  • “应用被终止了,但没有崩溃——只是消失了”
  • “TestFlight 反馈中有我需要审查的截图”

决策树——从这里开始

“用户报告了崩溃”
  1. 打开 Xcode Organizer(Window → Organizer → Crashes 标签页)
  2. 在左侧边栏中选择你的应用
  3. 找到用户运行的构建版本
  4. 崩溃是否已符号化?
  5. 能否确定崩溃位置?
“应用被终止了,但没有崩溃报告”

并非所有终止都会生成崩溃报告。请检查:

  1. Jetsam 报告——系统因内存压力而终止了应用
    • Organizer 会将这些报告与崩溃报告分开显示
    • 查看较高的 pageOuts 值
  2. 看门狗终止——主线程阻塞时间过长
    • 异常代码 0x8badf00d(“吃了坏东西”)
    • 发生在启动期间(>20 秒)或后台任务期间(>10 秒)
  3. MetricKit 诊断信息——设备端的终止原因
    • 需要在应用中集成 MetricKit

→ 请参阅没有崩溃报告的终止

“我想审查 TestFlight 反馈”
  1. Xcode Organizer → Feedback 标签页(位于 Crashes 旁边)
  2. 或者 App Store Connect → My Apps → [App] → TestFlight → Feedback

→ 请参阅反馈分类处理工作流


Xcode Organizer 使用指南

打开 Organizer

Window → Organizer(或在 Xcode 中按 ⌘⇧O)

界面布局
┌─────────────────────────────────────────────────────────────────┐
│ [Toolbar: Time Period ▼] [Version ▼] [Product ▼] [Release ▼]   │
├──────────┬──────────────────────────┬───────────────────────────┤
│ Sidebar  │     Crashes List         │       Inspector           │
│          │                          │                           │
│ • Crashes│  ┌─────────────────────┐ │  Distribution Graph       │
│ • Energy │  │ syncFavorites crash │ │  ┌─────────────────────┐  │
│ • Hang   │  │ 21 devices • 7 today│ │  │ ▄ ▄▄▄ v2.0          │  │
│ • Disk   │  └─────────────────────┘ │  │ ▄▄▄▄▄ v2.0.1        │  │
│ Feedback │  ┌─────────────────────┐ │  └─────────────────────┘  │
│          │  │ Another crash...    │ │                           │
│          │  └─────────────────────┘ │  Device Distribution      │
│          │                          │  OS Distribution          │
│          ├──────────────────────────┤                           │
│          │     Log View             │  [Feedback Inspector]     │
│          │  (simplified crash view) │  Shows tester feedback    │
│          │                          │  for selected crash       │
└──────────┴──────────────────────────┴───────────────────────────┘
主要功能
功能说明
快速送达TestFlight 崩溃发生后不久即会送达(而不是每天送达)
一年历史记录按时间段筛选崩溃,查看每月趋势
产品筛选器按 App Clip、watch 应用、扩展或主应用进行筛选
版本筛选器深入查看特定构建版本
发布渠道筛选器区分 TestFlight 与 App Store 崩溃
共享按钮与团队成员共享崩溃链接
反馈检查器查看与所选崩溃相关的测试人员评论
崩溃条目徽标

列表中的崩溃会显示徽标,指示其来源:

徽标含义
App Clip来自你的 App Clip 的崩溃
Watch来自 watchOS 配套应用的崩溃
Extension来自共享扩展、小组件等的崩溃
(无)主 iOS 应用
分类处理问题工作流

在深入查看代码之前,先问自己以下问题(摘自 WWDC):

问题 1——问题持续了多久

→ 查看右侧的检查器图表区域 → 图例会显示受影响的版本 → 找出崩溃首次出现的时间

问题 2——影响的是正式版还是仅影响 TestFlight

→ 使用工具栏中的发布渠道筛选器 → 选择“Release”仅查看 App Store 崩溃 → 选择“TestFlight”仅查看 Beta 崩溃

问题 3——用户当时在做什么

→ 打开反馈检查器(右侧面板) → 查看测试人员是否评论了他们当时的操作 → 留意上下文线索:网络状态、电池电量、磁盘空间

使用反馈检查器

当崩溃关联了 TestFlight 反馈时,崩溃列表中会显示反馈图标。点击该图标即可打开反馈检查器。

每条反馈包含:

字段重要性
版本/构建版本确认测试人员运行的确切构建版本
设备型号了解设备特有的崩溃情况(旧设备、特定屏幕尺寸)
电池电量电量低可能会影响应用行为
可用磁盘空间磁盘空间不足可能导致写入失败
网络类型蜂窝网络与 WiFi,以及连接问题
测试人员评论测试人员对所发生情况的描述

WWDC 中的示例洞察: 一位测试人员评论说:“我经过一条隧道时,点击了收藏按钮。几秒后,应用就崩溃了。”这揭示了一个网络超时问题——由于网络状况不佳,10 秒的超时时间太短,导致了崩溃。

在项目中打开崩溃
  1. 在列表中选择一条崩溃
  2. 点击 在项目中打开 按钮
  3. Xcode 将打开以下内容:
    • 显示回溯信息的调试导航器
    • 标出确切崩溃行的编辑器
分享崩溃
  1. 选择一条崩溃
  2. 点击工具栏中的 分享 按钮
  3. 可选操作:
    • 复制链接并分享给团队
    • 添加到待办事项列表
  4. 队友点击链接后,Organizer 会打开并定位到该条崩溃

符号化工作流

崩溃为何未符号化

崩溃报告会显示原始内存地址,直到与 dSYM 文件(调试符号文件)匹配。满足以下条件时,Xcode 会自动处理:

  • 你在 Xcode 中归档了构建(而非仅使用命令行)
  • 分发期间启用了“将符号上传到 Apple”
  • Spotlight 已为 dSYM 编制索引
快速检查:是否已符号化?

在 Organizer 中查看堆栈跟踪:

显示内容状态
0x0000000100abc123未符号化——需要 dSYM
MyApp.ViewController.viewDidLoad() + 45已符号化——可以开始分析
系统帧已符号化,应用帧未符号化部分符号化——缺少你的 dSYM
手动符号化

如果自动符号化失败:

# 1. Find the crash's build UUID (shown in crash report header)
#    Look for "Binary Images" section, find your app's UUID

# 2. Find matching dSYM
mdfind "com_apple_xcode_dsym_uuids == YOUR-UUID-HERE"

# 3. If not found, check Archives
ls ~/Library/Developer/Xcode/Archives/

# 4. Symbolicate a specific address
xcrun atos -arch arm64 \
  -o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp \
  -l 0x100000000 \
  0x0000000100abc123

# 5. Symbolicate an entire crash log (parse + symbolicate in one step)
# Note: crashlog is an LLDB Python script, not a compiled tool.
# Works via xcrun but may not be available in all Xcode configurations.
xcrun crashlog MyCrash.ips

atos 与 crashlog:对单个地址使用 atos(始终可用)。使用 crashlog 一次解析并符号化整份崩溃报告——它会处理完整的 Binary Images 部分并自动解析所有地址。

常见符号化失败
症状原因修复方法
系统帧正常,应用帧为十六进制缺少应用的 dSYM在 Archives 文件夹中查找 dSYM,或重新归档并包含符号
没有任何内容被符号化崩溃报告与 dSYM 的 UUID 不匹配验证 UUID 是否匹配;从完全相同的提交重新构建
atos 提示“No such file”Spotlight 索引中没有 dSYM运行 mdimport /path/to/MyApp.dSYM
到处都找不到 dSYM归档时未包含符号在构建设置中启用“Debug Information Format = DWARF with dSYM”
防止符号化问题
# Verify dSYM exists after archive
ls ~/Library/Developer/Xcode/Archives/YYYY-MM-DD/MyApp*.xcarchive/dSYMs/

# Verify UUID matches
dwarfdump --uuid MyApp.app.dSYM

阅读崩溃报告

关键字段(真正重要的信息)
字段说明
Exception Type崩溃类别(EXC_BAD_ACCESS、EXC_CRASH 等)
Exception Codes具体错误(KERN_INVALID_ADDRESS = 空指针)
Termination Reason系统终止进程的原因
Crashed Thread崩溃的线程(Thread 0 = 主线程)
Application Specific Information通常包含实际错误消息
Binary Images已加载的框架(有助于识别第三方问题来源)
阅读堆栈跟踪

崩溃线程的堆栈跟踪按从上到下的顺序显示:

  • Frame 0 = 崩溃发生的位置(最具体)
  • 较低的帧 = 调用了它的内容(调用链)
  • 查找你的代码 = 带有应用/框架名称的帧
Thread 0 Crashed:
0   libsystem_kernel.dylib    __pthread_kill + 8        ← System code
1   libsystem_pthread.dylib   pthread_kill + 288        ← System code
2   libsystem_c.dylib         abort + 128               ← System code
3   MyApp                     ViewController.loadData() ← YOUR CODE (start here)
4   MyApp                     ViewController.viewDidLoad()
5   UIKitCore                 -[UIViewController _loadView]

从 frame 3 开始——这是你的代码中的第一个帧。沿着堆栈向下查看,了解调用链。

示例:解读真实崩溃
Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000010

Thread 0 Crashed:
0   MyApp    0x100abc123 UserManager.currentUser.getter + 45
1   MyApp    0x100abc456 ProfileViewController.viewDidLoad() + 123
2   UIKitCore 0x1a2b3c4d5 -[UIViewController loadView] + 89

解读:

  • EXC_BAD_ACCESS 和 KERN_INVALID_ADDRESS = 尝试访问无效内存
  • 地址 0x10 = 地址非常低,几乎可以确定是对 nil 解引用
  • 在 currentUser.getter 中崩溃 = 访问了一个为 nil 的属性
  • 由 ProfileViewController.viewDidLoad() 调用 = 在设置视图期间

可能原因: 强制解包了值为 nil 的可选值,或访问了已释放的对象。


常见崩溃模式

EXC_BAD_ACCESS (SIGSEGV / SIGBUS)

含义: 访问了不属于你的内存。

Swift 中的常见原因:

模式示例修复方法
强制解包 niluser!.name使用 guard let 或 if let
对象已释放释放后在逃逸闭包中访问 self使用 [weak self]
数组越界array[index],其中 index >= count先检查边界
未初始化的指针使用错误指针进行 C 互操作使用前验证指针
// Before (crashes if user is nil)
let name = user!.name

// After (safe)
guard let user = user else {
    logger.warning("User was nil in ProfileViewController")
    return
}
let name = user.name
EXC_CRASH (SIGABRT)

含义: 应用主动终止了自身。

常见原因:

模式崩溃报告中的线索
fatalError() / preconditionFailure()Application Specific Info 中的断言消息
未捕获的 Objective-C 异常报告中的 NSException 类型和原因
Swift 运行时错误"Fatal error: ..." 消息
检测到死锁对当前队列执行 dispatch_sync

调试提示: 查看 "Application Specific Information" 部分,那里通常包含实际错误消息。

看门狗终止(0x8badf00d)

含义: 主线程阻塞时间过长,系统因此结束了应用。

时间限制:

场景限制
应用启动~20 秒
后台任务~10 秒
应用进入后台~5 秒

常见原因:

  • 在主线程上执行同步网络请求
  • 在主线程上执行同步文件 I/O
  • 队列之间发生死锁
  • 耗时计算阻塞 UI
// Before (blocks main thread — will trigger watchdog)
let data = try Data(contentsOf: largeFileURL)
processData(data)

// After (offload to background)
Task.detached {
    let data = try Data(contentsOf: largeFileURL)
    await MainActor.run {
        self.processData(data)
    }
}
Jetsam(内存压力导致的终止)

含义: 系统为释放内存而终止了应用。没有崩溃报告,应用就这样消失了。

症状:

  • 应用毫无崩溃迹象地“消失”
  • Organizer 中有 Jetsam 报告(与崩溃报告分开)
  • 报告中的 pageOuts 值较高
  • 常发生于照片/视频处理或大型数据操作期间

调查方法:

  1. 使用 Instruments → Allocations 进行分析
  2. 查找报告所述操作期间的内存峰值
  3. 检查是否存在没有大小限制的图像缓存
  4. 查找保留在内存中的大型数据结构

常见修复方法:

  • 使用 autoreleasepool 进行批处理
  • 实现带内存限制的图像缓存
  • 以流式方式处理大型文件,而不是一次性全部加载
  • 进入后台时释放对大型对象的引用

没有崩溃报告的终止

当用户报告“应用只是关闭了”,但你找不到崩溃记录时:

Terminations Organizer

Terminations Organizer(与 Crashes 分开)会显示非编程错误导致的应用终止趋势:

Window → Organizer → Terminations(位于侧边栏)

终止类别含义
启动超时应用启动耗时过长
内存限制达到系统内存上限
CPU 限制(后台)应用在后台时 CPU 使用量过高
后台任务超时后台任务超出时间限制

关键要点: 将终止率与先前版本进行比较,以发现回归问题。发布后内存终止次数激增,说明可能存在内存泄漏或内存占用增加。

检查 Jetsam
  1. Organizer → 选择应用 → 查找 "Disk Write Diagnostics" 或 "Hang Diagnostics"
  2. 这类情况不是崩溃,而是由系统发起的终止
检查后台终止

应用可能因以下原因在后台被终止:

  • 内存压力(jetsam)
  • CPU 使用量过高(应用在后台时)
  • 后台任务超时
询问用户

如果没有报告:

  1. “应用关闭时是在前台吗?”
  2. “你看到任何错误消息了吗?”
  3. “问题发生前你正在做什么?”
  4. “应用已经打开多长时间了?”
使用 MetricKit 启用更完善的诊断

MetricKit 崩溃诊断现在会在下次启动应用时送达(而不是每天汇总)。这样你就能更快获取崩溃数据。

import MetricKit

class MetricsManager: NSObject, MXMetricManagerSubscriber {

    static let shared = MetricsManager()

    func startListening() {
        MXMetricManager.shared.add(self)
    }

    func didReceive(_ payloads: [MXMetricPayload]) {
        // Process performance metrics
    }

    func didReceive(_ payloads: [MXDiagnosticPayload]) {
        for payload in payloads {
            // Crash diagnostics — delivered on next launch
            if let crashDiagnostics = payload.crashDiagnostics {
                for crash in crashDiagnostics {
                    // Process crash diagnostic
                    print("Crash: \(crash.callStackTree)")
                }
            }

            // Hang diagnostics
            if let hangDiagnostics = payload.hangDiagnostics {
                for hang in hangDiagnostics {
                    print("Hang duration: \(hang.hangDuration)")
                }
            }
        }
    }
}

何时使用 MetricKit,何时使用 Organizer:

使用场景工具
快速排查 TestFlight 崩溃Organizer(更快、可视化)
通过程序分析崩溃MetricKit
集成自定义崩溃报告MetricKit
跨版本查看终止趋势Terminations Organizer

Claude 辅助解读

使用崩溃分析器 Agent

要自动分析崩溃,请使用 crash-analyzer Agent:

/axiom:analyze-crash

或者自然地触发:

  • “分析这份崩溃日志”
  • “解析这个 .ips 文件:~/Library/Logs/DiagnosticReports/MyApp.ips”
  • “我的应用为什么崩溃了?这是报告……”

Agent 将会:

  1. 解析崩溃报告(JSON .ips 或文本 .crash 格式)
  2. 检查符号化状态
  3. 按崩溃模式分类(空指针、Swift 运行时、看门狗、jetsam 等)
  4. 生成可执行的分析结果,并提供具体的后续步骤
有效提示词

基础解读:

Here's a crash report from my iOS app. Help me understand:
1. What type of crash is this?
2. Where in my code did it crash?
3. What's the likely cause?

[paste full crash report]

提供上下文(效果更好):

My TestFlight app crashed. Here's what I know:

- User was [describe action, e.g., "tapping the save button"]
- iOS version: [from crash report]
- Device: [from crash report]

Crash report:
[paste full crash report]

The relevant code is in [file/class name]. Help me understand the cause.
应包含的内容
内容原因
完整的崩溃报告不完整的报告会丢失上下文
用户当时正在做什么有助于缩小代码路径范围
相关代码片段如果你知道崩溃发生的代码区域
iOS 版本和设备某些崩溃与特定设备或操作系统有关
Claude 可以提供的帮助
  • 解读异常类型和代码
  • 根据堆栈跟踪找出可能的原因
  • 解释不熟悉的系统栈帧
  • 建议在哪里添加日志
  • 提出修复模式
需要你自行判断的事项
  • 建议的修复是否适合你的架构
  • 如何在本地重现崩溃
  • 相较于其他 Bug 的优先级
  • 这是回归问题还是长期存在的问题

反馈分流工作流

在哪里查找反馈

Xcode Organizer(推荐): 窗口 → Organizer → 选择应用 → Feedback 标签页

App Store Connect: 我的 App → [App] → TestFlight → 反馈

每条反馈包含的内容
组成部分说明
截图用户看到的内容(通常最有价值)
文本评论用户对问题的描述
设备/操作系统iPhone 型号和 iOS 版本
应用版本对应的 TestFlight 构建版本
时间戳提交时间
分流工作流
  1. 按时间排序 — 优先查看最新反馈,除非正在调查特定问题
  2. 浏览截图 — 视觉问题通常一目了然
  3. 阅读评论 — 了解用户的描述和上下文
  4. 检查版本 — 更新版本中是否已修复?
  5. 分类:
类别操作
🐛 Bug调查、创建问题并优先修复
💡 功能请求如果有价值,加入待办列表
❓ 不明确缺少更多上下文,无法采取行动
✅ 符合预期的行为可能表明用户对 UX 存在困惑
局限性
  • 无法直接回复 — TestFlight 不支持回复反馈
  • 仅支持截图 — 不支持视频录制
  • 上下文有限 — 用户通常不会说明他们原本想做什么
MCP 驱动的反馈访问

如果已配置 asc-mcp,你可以通过编程方式访问崩溃诊断信息和测试者数据:

任务asc-mcp 工具Worker
列出近期构建版本builds_listbuilds
查看谁测试了某个构建版本builds_get_beta_testersbuild_beta
获取某个构建版本的崩溃特征metrics_build_diagnosticsmetrics
下载崩溃日志metrics_get_diagnostic_logsmetrics
向测试者分发修复版本beta_groups_add_buildsbeta_groups
通知测试者有新构建版本builds_send_beta_notificationbuild_beta

限制:通过 App Store Connect API 无法获取 TestFlight 文本反馈和屏幕截图。请使用 Xcode Organizer 或 ASC Web 控制面板查看反馈内容。

设置:/skill axiom-asc-mcp

获取更多上下文

如果反馈不明确,且可以联系到测试者:

  • 通过 TestFlight 群组邮件联系
  • 添加应用内反馈机制,以便捕获更多详情
  • 在 TestFlight 备注中加入复现步骤提示

压力场景

场景 1:“VIP 用户说应用一直崩溃,但我找不到任何崩溃报告”

压力:重要利益相关者、没有证据,很容易用“我这里没问题”敷衍了事

正确做法:

  1. 确认对方使用的是 TestFlight 版本(而非 App Store 版本或开发版)
  2. 确认对方已同意分享诊断信息(设置 → 隐私 → 分析)
  3. 检查 jetsam 报告(应用被终止,但没有崩溃报告)
  4. 检查是否有与其特定设备和 OS 组合相匹配的崩溃报告
  5. 请对方提供具体的复现步骤
  6. 如果仍然没有线索:请对方提供问题发生时的屏幕录制

回复模板:

“我检查了崩溃报告,目前还没有发现与你描述相符的崩溃。为了帮助调查:(1) 你能确认正在运行 TestFlight 版本吗?(2) 具体发生了什么情况——应用突然关闭、冻结,还是显示错误?(3) 出现问题之前你在做什么?这些信息有助于我找到问题。”

重要原因:“我这里没问题”会摧毁信任。不要急于否定,先彻底调查。

场景 2:“最新 TestFlight 构建版本发布后,崩溃率激增,需要尽快修复”

压力:时间紧迫,很容易根据代码改动猜测修复方案

正确做法:

  1. 打开 Organizer → Crashes → 筛选新构建版本
  2. 按异常类型对崩溃进行分组(找出主要特征)
  3. 找出发生频率最高的崩溃
  4. 符号化并完整阅读崩溃报告
  5. 编写任何修复之前,先弄清原因
  6. 如果可行,在本地复现
  7. 修复已确认的原因,而不是猜测

**重要原因:**仓促猜测往往会引入新错误或漏掉真正的问题。花 15 分钟妥善分诊,可以避免数小时的无效调试。

场景 3:“崩溃报告已经符号化,但我还是看不懂”

压力:很容易忽略问题,或随意改动,寄希望于能有所帮助

正确做法:

  1. 将完整崩溃报告连同上下文一起粘贴到 Claude
  2. 请它解释报告,而不只是说“修复这个”
  3. 如果不熟悉异常类型,先进行调查
  4. 如果调查后仍不清楚,在崩溃位置附近添加日志:
func suspectFunction() {
    logger.debug("Entering suspectFunction, state: \(debugDescription)")
    defer { logger.debug("Exiting suspectFunction") }

    // ... existing code ...
}
  1. 将加入日志的构建版本发布到 TestFlight
  2. 等待问题再次出现,以获取更充分的上下文

**重要原因:**理解胜过猜测,日志胜过臆测。与其发布随机改动,不如坦诚表示“我需要更多信息”。


快速参考

Organizer 键盘快捷键
操作快捷键
打开 Organizer⌘⇧O(在 Xcode 中)
刷新⌘R
常见异常代码
代码含义
KERN_INVALID_ADDRESS空指针/无效内存访问
KERN_PROTECTION_FAILURE内存保护违规
0x8badf00d看门狗超时(主线程阻塞)
0xdead10cc检测到死锁
0xc00010ff热事件(设备过热)
崩溃报告章节
章节内容
标头应用信息、设备、操作系统、日期
异常信息崩溃类型和代码
终止原因系统为何终止进程
触发线程崩溃的线程
应用专属信息错误消息、断言
线程回溯所有线程的堆栈跟踪
二进制映像已加载的框架和地址

资源

WWDC: 2018-414、2020-10076、2020-10078、2020-10081、2021-10203、2021-10258

文档: /xcode/diagnosing-issues-using-crash-reports-and-device-logs、/xcode/examining-the-fields-in-a-crash-report、/xcode/adding-identifiable-symbol-names-to-a-crash-report、/xcode/identifying-the-cause-of-common-crashes、/xcode/identifying-high-memory-use-with-jetsam-event-reports

技能:axiom-memory-debugging、axiom-xcode-debugging、axiom-swift-concurrency、axiom-lldb(用于交互式复现和调查)、axiom-asc-mcp(以编程方式访问 ASC)

Agent: crash-analyzer(自动解析和分析崩溃日志)

评论

支持 Markdown