iOS 崩溃报告怎么交给 AI 分析?.ips 崩溃日志的取法与提示词
iOS 崩溃报告交给 AI 分析前,先拿到 .ips 文件,摘出 Exception Type、Termination Reason、崩溃线程和 Last Exception Backtrace,连同复现步骤和代码一起给。本文附提示词模板、符号化、验证修复和脱敏方法。
iOS 崩溃报告交给 AI 分析,能很快找到闪退的那一行,前提是给对材料。做法是先拿到 .ips 文件,摘出 Exception Type、Termination Reason、崩溃线程(Triggered by Thread)和 Last Exception Backtrace 这几段,连同复现步骤和相关代码一起交给 AI。修完以后按同样的步骤回归,再看新构建的崩溃数有没有降下来。下文依次讲拿报告、读字段、对照异常类型、符号化、提示词、验证修复和脱敏,Apple 的规则截至 2026 年 10 月。
iOS 崩溃报告从哪里拿?
报告的来源决定你能多快拿到它。按手机在不在你手边选一种。
| 来源 | 怎么拿 | 适合的情况 |
|---|---|---|
| 测试机接在电脑上 | kxdevice crash list 列出,kxdevice crash pull 下载 |
自己测试时崩溃,最快 |
| 用户或测试员的手机 | 手机上「设置 → 隐私与安全性 → 分析与改进 → 分析数据」,找到报告后分享给你 | 对方不在身边 |
| TestFlight | App Store Connect 的 TestFlight 页,「反馈」下的「崩溃」 | 内测阶段,测试员提交了崩溃反馈 |
| App Store 正式版 | Xcode 的 Crashes organizer | 已上架,用户开启了共享分析数据 |
测试机:用 kxdevice 下载
手机用数据线接到电脑上,先列出报告,再下载到本地目录。PATTERN 是文件名通配,不写就是全部。
1kxdevice crash list MyTodo*
2kxdevice crash pull .\crashes MyTodo*
不想记命令时执行 kxdevice web,用浏览器打开 http://localhost:8780,在网页上查看崩溃报告。命令说明见 kxdevice 命令参考。
用 F5 调试时崩溃,调试器会先停在出错的位置,系统这时还没生成报告。想要完整报告,在调试控制台输入 detach 让 App 继续崩溃完,再去下载。
用户手机:从「分析数据」里分享
让对方在 iPhone 上打开「设置 → 隐私与安全性 → 分析与改进 → 分析数据」。崩溃报告的名字以 App 的可执行文件名开头,后面跟着日期时间。内存不足被系统结束的,名字以 JetsamEvent 开头。点开报告,用分享按钮发给你,比如作为邮件附件。
TestFlight 和 App Store
TestFlight 测试员的崩溃会自动共享给你,不受手机上分析数据开关的影响。在 App Store Connect 里打开 App,进入 TestFlight 页,在侧边栏「反馈」下点「崩溃」,点开一条后用右上角的下载按钮得到 .zip,里面有崩溃报告和测试员写的说明。
崩溃报告可下载 120 天,App 因无响应被结束的那类反馈不附带报告。测试员怎么提交反馈见 TestFlight 内测分发教程。
正式版的崩溃由 Apple 汇总到 Xcode 的 Crashes organizer,只包含开启了共享分析数据的用户。Apple 说明 watchdog 超时、内存过高(Jetsam)、过热、签名无效这几类不会出现在 Crashes organizer 里,要从手机上直接取。来源见 Apple 的 Acquiring crash reports and diagnostic logs。
.ips 文件里哪些字段要看?
从 iOS 15 开始,崩溃报告是扩展名 .ips 的 JSON 文件,用文本编辑器就能打开。一个文件里有两个 JSON 对象。第一行是元数据,bug_type 为 309 表示这是崩溃报告。第二行开始是报告正文。
1{"app_name":"MyTodo","app_version":"1.2.0","build_version":"15","bug_type":"309","bundleID":"com.example.mytodo","os_version":"iPhone OS 18.6 (22G86)","name":"MyTodo"}
2{
3 "modelCode" : "iPhone14,5",
4 "exception" : {"type":"EXC_BREAKPOINT","signal":"SIGTRAP","codes":"0x0000000000000001, 0x0000000102a3c1f4"},
5 "termination" : {"namespace":"SIGNAL","code":5,"indicator":"Trace/BPT trap: 5"},
6 "faultingThread" : 0,
7 "threads" : [
8 {"triggered":true,"queue":"com.apple.main-thread","frames":[
9 {"imageIndex":0,"imageOffset":49652,"symbol":"TodoStore.toggle(id:)","symbolLocation":120,"sourceFile":"TodoStore.swift","sourceLine":15}
10 ]}
11 ],
12 "usedImages" : [
13 {"name":"MyTodo","uuid":"0b5c6f2e-1111-2222-3333-444455556666","base":4339204096}
14 ]
15}
上面是删减过的示意,真实报告还有所有线程、寄存器和几百个系统库。JSON 里的数字都是十进制,code、地址这类值要换成十六进制看,例如 2343432205 就是 0x8badf00d。
这几个字段决定了你要交给 AI 什么。左列是 JSON 里的键,中间是 Xcode、控制台这类工具翻译后显示的名字,AI 两种都认识。
| JSON 键 | 翻译后的字段名 | 看什么 |
|---|---|---|
exception.type、exception.signal |
Exception Type | Mach 异常和信号,决定排查方向 |
termination.namespace、code、indicator |
Termination Reason | 被谁、以什么原因结束,watchdog、后台任务超时都看这里 |
faultingThread、threads[].triggered |
Triggered by Thread | 哪个线程触发了崩溃,它的调用栈最重要 |
lastExceptionBacktrace |
Last Exception Backtrace | 未捕获的 Objective-C 或 C++ 异常才有,指向抛出异常的调用 |
asi |
Application Specific Information | 系统和库附加的说明,例如 watchdog 的超时描述 |
usedImages[].uuid |
Binary Images | 每个二进制的构建 UUID,用来匹配符号文件 |
bundleInfo、osVersion、modelCode |
Version、OS Version、Hardware Model | 哪个版本、哪个系统、哪种机型 |
字段定义以 Apple 的 Interpreting the JSON format of a crash report 为准。
读的顺序
- 看 Exception Type 和 Termination Reason,按下一节的表判断是哪一类崩溃。
- 有 Last Exception Backtrace 时先看它,这是语言异常,真正的出错点在这一段里,崩溃线程上只剩
abort()。 - 在崩溃线程的调用栈里,从 0 号帧往下找第一帧属于你 App 的位置,附近就是出问题的代码。
- Application Specific Information 里有文字说明时一起看,它常常直接写出了原因。
常见异常类型分别是什么原因?
先认出崩溃类型,再决定给 AI 什么。下表按 Apple 的 Understanding the exception types in a crash report 及其子页面整理。
| 报告里的样子 | 常见原因 | 排查方向 |
|---|---|---|
EXC_BAD_ACCESS (SIGSEGV) 或 (SIGBUS) |
访问了无效地址,例如野指针、已释放的对象、C 数组越界、写只读内存 | 看崩溃线程第一帧你的代码,查对象生命周期、unowned、Unsafe 指针、C/C++ 代码 |
EXC_BREAKPOINT (SIGTRAP),Termination Reason 是 SIGNAL, Code 0x5 |
Swift 运行时错误,例如对 nil 强制解包 !、as! 强制转换失败、数组下标越界,或代码里调用了 fatalError |
崩溃线程 0 号帧通常就是那一行,改成 if let、guard let、as? |
EXC_CRASH (SIGABRT),有 Last Exception Backtrace |
未捕获的 Objective-C 或 C++ 异常,例如 NSArray 越界、KVC 设了不存在的键 |
读 Last Exception Backtrace,找到抛出异常的系统方法和调用它的你的代码 |
EXC_CRASH (SIGABRT),Exception Subtype 是 LAUNCH_HANG |
App 扩展初始化太慢 | 查扩展里的静态初始化和 load() |
EXC_CRASH (SIGKILL),Code 0x8badf00d |
watchdog 结束了无响应的 App。scene-create 是启动后没及时画出第一帧,scene-update 是主线程太忙没及时更新界面 |
主线程上的同步网络、大 JSON 解析、Core Data 同步迁移,挪到后台 |
EXC_CRASH (SIGKILL),Code 0xdead10cc |
挂起时还持有文件锁或 SQLite 锁 | 写库前用 beginBackgroundTask 申请后台时间,挂起前释放锁 |
EXC_RESOURCE |
CPU、内存、磁盘写入、唤醒次数超过系统限制,Exception Subtype 写着是哪一种 | 有 NON-FATAL CONDITION 时进程没被结束,是预警 |
文件名 JetsamEvent_… |
系统内存紧张,结束了占用内存的 App。reason 是 per-process-limit 表示超过单进程上限,vm-pageshortage 表示为前台 App 回收后台内存 |
这类报告没有调用栈,在 processes 里找带 reason 的那一项,再查内存占用 |
Jetsam 报告里被结束的不是你的 App 时,说明你的崩溃和内存无关,要另找它的崩溃报告。内存怎么查见没有 Instruments 怎么在 Windows 上查 iOS 卡顿和内存。
只有地址没有函数名怎么办?
调用栈里你的 App 那几帧只显示 0x… 地址时,报告是未符号化的。不要把这种报告直接交给 AI 让它猜,地址对 AI 没有意义,它只能编一个看起来合理的原因。按下面的顺序处理。
- 用 Debug 复现。照报告里的场景操作,用 Debug 配置按 F5 运行,调试器会停在崩溃的那一行,调用栈和变量都能直接看。Objective-C 异常配合异常断点,做法见真机调试与崩溃定位。
- 自测用的 Release 包保留符号。Release 包默认剥离符号。只在自己测试时需要看到函数名,就在构建时加
-D STRIP_INSTALLED_PRODUCT=NO。 - 保留每次发布构建的符号文件。Apple 说明,二进制和它的符号文件靠构建 UUID 对应,同一份源码换了编译器版本或构建设置,UUID 就不一样。报告里
usedImages的uuid要和你保存的符号文件一致才能用,所以每个上传的构建都要存一份,不能事后重新编译来补。
1kxbuild build .\MyTodo.xcodeproj -c Release -D STRIP_INSTALLED_PRODUCT=NO
报告部分符号化,也就是系统库有函数名、你的代码只有地址时,Last Exception Backtrace 里的系统方法名往往已经够判断原因,比如看到 -[__NSArrayM objectAtIndex:] 就知道是数组越界。这时把这部分交给 AI 也有用,但要告诉它「我的代码这几帧没有符号」。
怎么摘出关键段再交给 AI 分析?
完整的 .ips 有几千行,大部分是系统线程和库列表,整份贴给 AI 既费上下文,又容易把设备信息一起发出去。下面这个 PowerShell 脚本只摘出关键字段和崩溃线程的前 15 帧,在 Windows 自带的 PowerShell 里就能运行,保存成 ipsfacts.ps1 即可。
1# ipsfacts.ps1: print the key parts of an .ips crash report
2param([Parameter(Mandatory)][string]$Path, [int]$Max = 15)
3$lines = Get-Content -LiteralPath $Path -Encoding UTF8
4$meta = $lines[0] | ConvertFrom-Json
5$r = ($lines[1..($lines.Count - 1)] -join "`n") | ConvertFrom-Json
6"App: $($meta.name) $($r.bundleInfo.CFBundleShortVersionString) ($($r.bundleInfo.CFBundleVersion))"
7"OS: $($r.osVersion.train) ($($r.osVersion.build)) Model: $($r.modelCode)"
8"Exception Type: $($r.exception.type) ($($r.exception.signal)) Subtype: $($r.exception.subtype)"
9"Termination Reason: $($r.termination.namespace), Code 0x{0:x} $($r.termination.indicator)" -f [int64]$r.termination.code
10if ($r.asi) { "Application Specific Information:"; $r.asi | ConvertTo-Json -Depth 5 }
11function Show-Frames($title, $list) {
12 $title
13 $i = 0
14 foreach ($f in $list | Select-Object -First $Max) {
15 $img = $r.usedImages[$f.imageIndex].name
16 $sym = if ($f.symbol) { "$($f.symbol) + $($f.symbolLocation)" } else { "0x{0:x}" -f [int64]$f.imageOffset }
17 $src = if ($f.sourceFile) { " ($($f.sourceFile):$($f.sourceLine))" } else { "" }
18 "{0,-3} {1,-28} {2}{3}" -f $i, $img, $sym, $src
19 $i++
20 }
21}
22if ($r.lastExceptionBacktrace) { Show-Frames "Last Exception Backtrace:" $r.lastExceptionBacktrace }
23$t = $r.threads[$r.faultingThread]
24Show-Frames "Triggered by Thread $($r.faultingThread) $($t.queue):" $t.frames
运行后得到一段几十行的文字,直接复制给 AI。
1powershell -ExecutionPolicy Bypass -File .\ipsfacts.ps1 .\crashes\MyTodo-2026-10-18-203015.ips
1App: MyTodo 1.2.0 (15)
2OS: iPhone OS 18.6 (22G86) Model: iPhone14,5
3Exception Type: EXC_BREAKPOINT (SIGTRAP) Subtype:
4Termination Reason: SIGNAL, Code 0x5 Trace/BPT trap: 5
5Triggered by Thread 0 com.apple.main-thread:
60 MyTodo TodoStore.toggle(id:) + 120 (TodoStore.swift:15)
71 MyTodo closure #1 in ContentView.body.getter + 64 (ContentView.swift:88)
82 SwiftUI specialized ButtonAction.callAsFunction() + 20
崩溃线程前十几帧都是系统库、看不到你的代码时,用 -Max 40 多摘一些。
给 AI 的提示词怎么写?
AI 修崩溃的质量取决于三样东西:报告关键段、复现步骤、相关代码。少了复现步骤,它只能猜触发条件;少了代码,它只能给泛泛的建议。
1iOS App 崩溃,请先分析原因,再给出修改方案。
2
3【环境】App 1.2.0 (15),iPhone 13(iOS 18.6),Release 包,TestFlight 安装。
4【复现步骤】
51. 新建两条待办。
62. 在第二条上左滑删除。
73. 立刻点第一条前面的勾选框,App 闪退。必现。
8【崩溃报告关键段】
9(粘贴 ipsfacts.ps1 的输出)
10【相关代码】
11(粘贴 TodoStore.swift 全文,以及 ContentView.swift 里调用 toggle 的那一段)
12
13要求:
14- 先根据 Exception Type 和崩溃线程说明是哪一类崩溃、崩在哪一行、为什么。
15- 只改导致崩溃的地方,不要顺手重构。
16- 说明修改后用什么步骤能验证不再崩溃。
17- 有不确定的地方直接说,不要猜测没给出的代码。
拿到上面这份材料,AI 一般能指出 TodoStore.swift:15 的强制解包。删除后列表还拿着旧的 id,firstIndex 返回 nil,! 触发 Swift 运行时错误。
1func toggle(id: UUID) {
2 let index = items.firstIndex(where: { $0.id == id })!
3 items[index].done.toggle()
4}
合理的改法是用 guard let 处理找不到的情况,并留一条日志,方便以后确认这条路径有没有被走到。
1import os
2
3final class TodoStore {
4 private let log = Logger(subsystem: "com.example.mytodo", category: "store")
5 var items: [TodoItem] = []
6
7 func toggle(id: UUID) {
8 guard let index = items.firstIndex(where: { $0.id == id }) else {
9 log.error("toggle: 找不到待办 \(id.uuidString, privacy: .public),当前 \(self.items.count) 条")
10 return
11 }
12 items[index].done.toggle()
13 }
14}
审查 AI 的改法时多问一句:nil 是怎么来的。把 ! 换成 guard 只是不崩了,界面拿着已删除的 id 本身可能也是个 bug,让 AI 一并查调用方。
几条提高命中率的做法。
- 一次只给一个崩溃。同一个版本有几种崩溃时,按 Exception Type 和崩溃位置分组,每组单独问。
- 同一位置的多份报告给一份就够。确认它们的崩溃线程前几帧一致即可。
- watchdog 崩溃给主线程的栈。
0x8badf00d的崩溃线程就是主线程,把 Application Specific Information 里的超时说明一起给,AI 才知道是启动慢还是界面卡住。 - 语言异常一定带 Last Exception Backtrace。只给崩溃线程,AI 看到的只有
abort()。
AI 给的修复怎么验证?
AI 说修好了不算数,要在手机上验证。
- 按复现步骤回归。用原来的步骤在 Debug 配置下操作几遍,再用 Release 构建装到手机上重复一次。Release 开了优化,有些问题只在 Release 下出现。
- 加日志确认路径。像上面的
log.error,在修复点打日志。用kxdevice ostrace按 subsystem 过滤,操作时能看到这条日志被打出来,说明你复现的确实是那条路径。用法见 iOS 真机日志怎么看。 - 确认没有新报告。回归后再执行一次
kxdevice crash list MyTodo*,看有没有新增的报告。 - TestFlight 观察。构建号加一后上传,TestFlight 页会按构建统计会话数和崩溃数。新构建跑过一段时间、会话数上来以后,对比新旧构建的崩溃数。
- 偶现崩溃拉长观察。复现不了的崩溃,修复后不要急着下结论,等新构建在测试员手里用上几天,再看同类报告还有没有出现。
1kxdevice ostrace --subsystem com.example.mytodo --levels error,fault
交给 AI 前要脱敏哪些内容?
崩溃报告里有和设备、用户相关的信息。交给第三方 AI 服务前,先确认这几项。
| 内容 | 位置 | 处理 |
|---|---|---|
| 匿名的设备标识 | crashReporterKey,同一台设备的报告值相同 |
删除 |
| 报告编号 | 元数据的 incident_id、正文的 incident |
删除 |
| 安装路径 | procPath、usedImages[].path 里的容器目录 |
用不到就删 |
| 业务数据 | Application Specific Information、异常消息里可能带上用户输入、文件名、接口地址 | 逐条看,替换成占位符 |
| App 名和 Bundle ID | 元数据和正文 | 涉及保密时替换 |
用上面的脚本只摘关键段,本身就去掉了大部分设备信息,但异常消息和 Application Specific Information 仍要自己看一遍。Apple 建议在公开分享崩溃报告时,把 App 名和 Bundle ID 替换成长度相同的字符,例如 MyApp 换成 MmVvv,这样对齐不乱,也能区分两者。
另外两条。用户发给你的 iOS 崩溃报告属于用户的数据,交给 AI 之前确认你的隐私政策允许这样处理。证书、.p12、App Store Connect API 密钥不要和代码一起贴给 AI。
常见问题
iPhone 上的崩溃日志在哪里看?
在 iPhone 上打开「设置 → 隐私与安全性 → 分析与改进 → 分析数据」,以 App 名开头的就是这个 App 的崩溃报告,以 JetsamEvent 开头的是内存不足的报告。点开后用分享按钮发出去。手机接在电脑上时,可以用 kxdevice crash pull 直接下载。
.ips 文件怎么打开?
用任意文本编辑器打开即可。文件第一行是元数据,后面是一个 JSON 对象,重点看 exception、termination、threads 里 triggered 为 true 的线程,以及 lastExceptionBacktrace。
崩溃报告里没有函数名,AI 能分析吗?
基本不能。你的代码只剩地址时,AI 只能猜。先用 Debug 配置在手机上复现,让调试器停在崩溃行;发布的构建要保留每次构建的符号文件,用报告里的 UUID 对上之后才能还原函数名。
0x8badf00d 是什么崩溃?
这是 watchdog 结束了无响应的 App,异常类型是 EXC_CRASH (SIGKILL)。说明里有 scene-create 表示启动后太久没画出第一帧,有 scene-update 表示主线程太忙没及时更新界面。把主线程上的同步网络请求、大文件解析挪到后台。
TestFlight 的崩溃报告在哪里下载?
在 App Store Connect 里打开 App,进入 TestFlight 页,在「反馈」下点「崩溃」,点开一条后用右上角的下载按钮得到 .zip,里面有崩溃报告和测试员的说明。崩溃报告可以下载 120 天。