axiom-lldb
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLLDB Debugging
LLDB 调试
Interactive debugging with LLDB. The debugger freezes time so you can interrogate your running app — inspect variables, evaluate expressions, navigate threads, and understand exactly why something went wrong.
Core insight: "LLDB is useless" really means "I don't know which command to use for Swift types." This is a knowledge-gap problem, not a tool problem.
使用LLDB进行交互式调试。调试器会冻结程序运行状态,让你可以探查正在运行的应用——检查变量、计算表达式、查看线程,精准定位问题原因。
核心认知: 所谓“LLDB没用”其实是“我不知道针对Swift类型该用哪个命令”。这是知识缺口问题,而非工具本身的问题。
Red Flags — Check This Skill When
适用场景警示——遇到以下情况请使用本技能
| Symptom | This Skill Applies |
|---|---|
| Need to inspect a variable at runtime | Yes — breakpoint + inspect |
| Crash you can reproduce locally | Yes — breakpoint before crash site |
| Wrong value at runtime but code looks correct | Yes — step through and inspect |
| Need to understand thread state during hang | Yes — pause + thread backtrace |
| Yes — Playbook 3 has alternatives |
| Crash log analyzed, need to reproduce | Yes — set breakpoints from crash context |
| Need to test a fix without rebuilding | Yes — expression evaluation |
| Want to break on all exceptions | Yes — exception breakpoints |
| App feels slow but responsive | No — use axiom-performance-profiling |
| Memory grows over time | No — use axiom-memory-debugging first |
| App completely frozen | Maybe — use axiom-hang-diagnostics first, then LLDB for thread inspection |
| Crash in production, no local repro | No — use axiom-testflight-triage first |
| 症状 | 是否适用本技能 |
|---|---|
| 需要在运行时检查变量 | 是——设置断点+检查 |
| 可在本地复现的崩溃 | 是——在崩溃位置前设置断点 |
| 代码逻辑正确但运行时值异常 | 是——单步执行并检查 |
| 需要排查程序挂起时的线程状态 | 是——暂停程序+查看线程回溯 |
| 是——手册3提供了替代方案 |
| 已分析崩溃日志,需要复现问题 | 是——根据崩溃上下文设置断点 |
| 需要在不重新构建的情况下测试修复方案 | 是——使用表达式计算 |
| 希望捕获所有异常 | 是——设置异常断点 |
| 应用响应缓慢但未完全冻结 | 否——使用axiom-performance-profiling |
| 内存占用持续增长 | 否——优先使用axiom-memory-debugging |
| 应用完全冻结 | 可能——优先使用axiom-hang-diagnostics,再用LLDB检查线程 |
| 生产环境崩溃,无法在本地复现 | 否——优先使用axiom-testflight-triage |
LLDB vs Other Tools
LLDB与其他工具对比
dot
digraph tool_selection {
"What do you need?" [shape=diamond];
"axiom-testflight-triage" [shape=box];
"axiom-hang-diagnostics" [shape=box];
"axiom-memory-debugging" [shape=box];
"axiom-performance-profiling" [shape=box];
"LLDB (this skill)" [shape=box, style=bold];
"What do you need?" -> "axiom-testflight-triage" [label="Crash log from field,\ncan't reproduce locally"];
"What do you need?" -> "axiom-hang-diagnostics" [label="App frozen,\nneed diagnosis approach"];
"What do you need?" -> "axiom-memory-debugging" [label="Memory growing,\nneed leak pattern"];
"What do you need?" -> "axiom-performance-profiling" [label="Need to measure\nCPU/memory over time"];
"What do you need?" -> "LLDB (this skill)" [label="Need to inspect state\nat a specific moment"];
}Rule of thumb: Instruments measures. LLDB inspects. If you need to understand what's happening at a specific moment in time, use LLDB. If you need to understand trends over time, use Instruments.
dot
digraph tool_selection {
"What do you need?" [shape=diamond];
"axiom-testflight-triage" [shape=box];
"axiom-hang-diagnostics" [shape=box];
"axiom-memory-debugging" [shape=box];
"axiom-performance-profiling" [shape=box];
"LLDB (this skill)" [shape=box, style=bold];
"What do you need?" -> "axiom-testflight-triage" [label="Crash log from field,\ncan't reproduce locally"];
"What do you need?" -> "axiom-hang-diagnostics" [label="App frozen,\nneed diagnosis approach"];
"What do you need?" -> "axiom-memory-debugging" [label="Memory growing,\nneed leak pattern"];
"What do you need?" -> "axiom-performance-profiling" [label="Need to measure\nCPU/memory over time"];
"What do you need?" -> "LLDB (this skill)" [label="Need to inspect state\nat a specific moment"];
}经验法则: Instruments用于测量,LLDB用于探查。如果你需要了解某个特定时刻的程序状态,使用LLDB;如果你需要分析一段时间内的趋势,使用Instruments。
Response Format
响应格式
When helping with LLDB debugging, structure your output as:
- Immediate diagnosis (1-3 bullets, confidence-tagged: HIGH/MEDIUM/LOW)
- Commands to run (numbered, copy-paste ready, with prefix)
(lldb) - What to look for (command → expected output → interpretation)
- Likely root causes (ranked by probability)
- Next breakpoint plan (catch it earlier next time)
- If no debugger attached (crash-log-only fallback path)
在协助进行LLDB调试时,请按以下结构输出内容:
- 即时诊断(1-3条要点,标注置信度:HIGH/MEDIUM/LOW)
- 待执行命令(编号格式,可直接复制粘贴,前缀为)
(lldb) - 检查要点(命令→预期输出→解读)
- 可能的根因(按概率排序)
- 后续断点计划(提前捕获问题)
- 未附加调试器时的方案(仅依赖崩溃日志的 fallback 路径)
Playbook 1: Crash Triage
手册1:崩溃排查
Goal: Understand why the app crashed, starting from the stop point.
目标: 从程序停止的位置入手,理解应用崩溃的原因。
Step 1: Read the Stop Reason
步骤1:查看停止原因
When the debugger stops, the first thing to check:
(lldb) thread infoThis shows the stop reason. Common stop reasons:
| Stop Reason | Meaning | Next Step |
|---|---|---|
| Accessed invalid memory (null pointer, dangling reference) | Check the address — |
| Misaligned or invalid address | Usually C interop or unsafe pointer issue |
| Hit a trap — Swift runtime check failed | Check for |
| Deliberate abort — assertion or uncaught exception | Look at "Application Specific Information" for the message |
| Your breakpoint was hit | Normal — inspect state |
当调试器暂停程序时,首先执行以下命令:
(lldb) thread info该命令会显示程序停止的原因。常见停止原因如下:
| 停止原因 | 含义 | 下一步操作 |
|---|---|---|
| 访问了无效内存(空指针、野指针) | 检查地址—— |
| 内存地址对齐错误或无效 | 通常是C语言交互或不安全指针导致的问题 |
| 触发了陷阱——Swift运行时检查失败 | 检查是否存在 |
| 主动终止程序——断言失败或未捕获异常 | 查看“Application Specific Information”中的错误信息 |
| 命中了你设置的断点 | 正常情况——检查程序状态 |
Step 2: Get the Backtrace
步骤2:获取回溯信息
(lldb) btRead top-to-bottom. Find the first frame in YOUR code (not system frameworks). That's where to start investigating.
(lldb) bt 10Limit to 10 frames if the full trace is noisy.
(lldb) bt从上到下阅读回溯信息,找到属于你的代码的第一个栈帧(而非系统框架),这就是你需要开始排查的位置。
(lldb) bt 10如果完整回溯信息过于冗长,可以限制只显示前10个栈帧。
Step 3: Navigate to Your Frame
步骤3:跳转到目标栈帧
(lldb) frame select 3Jump to frame 3 (or whichever frame is in your code).
(lldb) frame select 3跳转到第3个栈帧(或属于你的代码的任意栈帧)。
Step 4: Inspect State
步骤4:检查程序状态
(lldb) v
(lldb) v self.someProperty
(lldb) v localVariableUse (not ) for reliable Swift value inspection. See Playbook 3 for details.
vpo(lldb) v
(lldb) v self.someProperty
(lldb) v localVariable使用(而非)来可靠地检查Swift类型的值。详情请参考手册3。
vpoStep 5: Classify and Fix
步骤5:分类问题并修复
| Exception Type | Typical Cause | Fix Pattern |
|---|---|---|
| Force-unwrap nil optional | |
| Use-after-free / dangling pointer | Check object lifetime, |
| Swift runtime trap (bounds, unwrap, precondition) | Fix the violated precondition |
| Uncaught ObjC exception or | Read the exception message, fix the root cause |
| 异常类型 | 典型原因 | 修复方案 |
|---|---|---|
| 强制解包空可选值 | 使用 |
| 内存释放后使用/野指针 | 检查对象生命周期,使用 |
| Swift运行时陷阱(越界、解包、前置条件失败) | 修复违反的前置条件 |
| 未捕获的OC异常或 | 读取异常信息,修复根因 |
Step 6: Set a Conditional Breakpoint to Catch It Earlier
步骤6:设置条件断点提前捕获问题
(lldb) breakpoint set -f MyFile.swift -l 42 -c "value == nil"This breaks only when is nil at line 42 — catches the problem before the crash.
value(lldb) breakpoint set -f MyFile.swift -l 42 -c "value == nil"该断点仅会在第42行的为空时触发——在崩溃发生前就捕获问题。
valuePlaybook 2: Hang/Deadlock Diagnosis
手册2:挂起/死锁排查
Goal: Understand why the app is frozen by inspecting all thread states.
目标: 通过检查所有线程状态,理解应用冻结的原因。
Step 1: Pause the App
步骤1:暂停应用
If the app is hung, press the pause button in Xcode (⌃⌘Y) or:
(lldb) process interrupt如果应用挂起,点击Xcode中的暂停按钮(⌃⌘Y)或执行以下命令:
(lldb) process interruptStep 2: Get All Thread Backtraces
步骤2:获取所有线程的回溯信息
(lldb) thread backtrace allOr the shorthand:
(lldb) bt all(lldb) thread backtrace all或使用简写:
(lldb) bt allStep 3: Classify Thread States
步骤3:分类线程状态
Look at Thread 0 (main thread) — it processes all UI events. If it's blocked, the app is frozen.
Main thread blocked on synchronous wait:
frame #0: libsystem_kernel.dylib`__psynch_mutexwait
frame #1: libsystem_pthread.dylib`_pthread_mutex_firstfit_lock_wait
...
frame #5: MyApp`ViewController.viewDidLoad()Translation: Main thread is waiting for a mutex lock. Something else holds it.
Main thread blocked on dispatch_sync:
frame #0: libdispatch.dylib`_dispatch_sync_f_slow
...
frame #3: MyApp`DataManager.fetchData()Translation: called from background → classic deadlock.
DispatchQueue.main.syncMain thread busy (CPU-bound):
frame #0: MyApp`ImageProcessor.processAllImages()
frame #1: MyApp`ViewController.viewDidLoad()Translation: Expensive work on main thread. Move to background.
重点查看线程0(主线程)——它负责处理所有UI事件。如果主线程被阻塞,应用就会冻结。
主线程因同步等待被阻塞:
frame #0: libsystem_kernel.dylib`__psynch_mutexwait
frame #1: libsystem_pthread.dylib`_pthread_mutex_firstfit_lock_wait
...
frame #5: MyApp`ViewController.viewDidLoad()解读:主线程正在等待一个互斥锁,而该锁被其他线程持有。
主线程因dispatch_sync被阻塞:
frame #0: libdispatch.dylib`_dispatch_sync_f_slow
...
frame #3: MyApp`DataManager.fetchData()解读:在后台线程调用了——典型的死锁场景。
DispatchQueue.main.sync主线程繁忙(CPU密集型任务):
frame #0: MyApp`ImageProcessor.processAllImages()
frame #1: MyApp`ViewController.viewDidLoad()解读:主线程执行了耗时操作,需要将其移至后台线程。
Step 4: Check for Deadlocks
步骤4:检查死锁
If two threads are both waiting on something the other holds:
(lldb) thread listLook for multiple threads with state that reference each other's locks.
waiting如果两个线程都在等待对方持有的资源:
(lldb) thread list查找多个状态为且引用了对方锁的线程。
waitingStep 5: Inspect Specific Thread
步骤5:检查特定线程
(lldb) thread select 3
(lldb) bt
(lldb) vSwitch to another thread to inspect its state.
Cross-reference: For fix patterns once you've identified the hang cause →
/skill axiom-hang-diagnostics(lldb) thread select 3
(lldb) bt
(lldb) v切换到其他线程检查其状态。
交叉参考: 当你定位到挂起原因后,可参考修复方案 →
/skill axiom-hang-diagnosticsPlaybook 3: Swift Value Inspection
手册3:Swift值探查
This is the core value of this skill. Most developers abandon LLDB because doesn't work reliably with Swift types. Here's what actually works.
po这是本技能的核心价值。 大多数开发者放弃LLDB是因为命令无法可靠地处理Swift类型。以下是真正有效的方法。
poThe Four Print Commands
四个打印命令
| Command | Full Form | What It Does | Best For |
|---|---|---|---|
| | Reads memory directly, no compilation | Swift structs, enums, locals — your default |
| | Compiles expression, shows formatted result | Computed properties, function calls |
| | Calls | Classes with |
| | Evaluates arbitrary code | Calling methods, modifying state |
| 命令 | 全称 | 功能 | 最佳适用场景 |
|---|---|---|---|
| | 直接读取内存,无需编译 | Swift结构体、枚举、局部变量——默认首选 |
| | 编译表达式,显示格式化结果 | 计算属性、函数调用 |
| | 调用 | 实现了 |
| | 执行任意代码 | 调用方法、修改状态 |
When to Use Each
各命令的使用时机
Start with — it's fastest and most reliable for stored properties:
v(lldb) v self.userName
(lldb) v self.items[0]
(lldb) v localStructvvlazy var$bindingvpUse when can't reach it:
pv(lldb) p self.computedProperty
(lldb) p self.items.count
(lldb) p someFunction()pUse for class descriptions:
po(lldb) po myObject
(lldb) po error
(lldb) po notificationpodebugDescription优先使用——它速度最快,且对存储属性的探查最可靠:
v(lldb) v self.userName
(lldb) v self.items[0]
(lldb) v localStructvvlazy var$bindingvp当无法探查时使用:
vp(lldb) p self.computedProperty
(lldb) p self.items.count
(lldb) p someFunction()p使用查看类的描述信息:
po(lldb) po myObject
(lldb) po error
(lldb) po notificationpodebugDescriptionThe "LLDB Is Broken" Moments
"LLDB失效"场景处理
| What You See | Why | Fix |
|---|---|---|
| | Use |
| Swift expression parser can't resolve the type | Try |
| Compiler optimized it out (Release build) | Rebuild with Debug, per-file |
| Expression had side effects LLDB couldn't reverse | Try a simpler expression; avoid mutating state |
| Object doesn't conform to | Use |
| Breakpoint is in a context without | Use |
| Different compilation paths | Use |
| 现象 | 原因 | 修复方案 |
|---|---|---|
| | 改用 |
| Swift表达式解析器无法识别类型 | 对于OC对象,尝试 |
| 编译器将变量优化掉(Release构建) | 重新构建为Debug版本,为特定文件设置 |
| 表达式存在LLDB无法回滚的副作用 | 尝试更简单的表达式;避免修改状态 |
| 对象未实现 | 使用 |
| 断点处于无 | 使用 |
| 编译路径不同 | 当 |
Inspecting Optionals
探查可选值
(lldb) v optionalValueShows: or
(String?) some = "hello"(String?) noneDon't use — it may show just which is less useful.
po optionalValueOptional("hello")(lldb) v optionalValue显示结果: 或
(String?) some = "hello"(String?) none不要使用——它可能只显示,信息价值较低。
po optionalValueOptional("hello")Inspecting Collections
探查集合类型
(lldb) v myArray
(lldb) v myArray[2]
(lldb) v myDictFor large collections, limit output:
(lldb) p Array(myArray.prefix(5))(lldb) v myArray
(lldb) v myArray[2]
(lldb) v myDict对于大型集合,限制输出内容:
(lldb) p Array(myArray.prefix(5))Inspecting SwiftUI State
探查SwiftUI状态
SwiftUI is backed by stored properties with underscore prefix:
@State(lldb) v self._isPresented
(lldb) v self._itemsFor models:
@Observable(lldb) v self.viewModel.propertyNameDiagnosing "view doesn't update": If a property changes (confirmed with ) but the SwiftUI view doesn't re-render, check which thread the mutation happens on with . mutations must happen on for SwiftUI to observe them — mutations on a background actor won't trigger view updates. Use inside a view body to see which property triggered (or didn't trigger) a re-render:
vbt@Observable@MainActorSelf._printChanges()(lldb) expr Self._printChanges()For the full observation diagnostic tree →
/skill axiom-swiftui-debuggingSwiftUI的由带下划线前缀的存储属性支持:
@State(lldb) v self._isPresented
(lldb) v self._items对于模型:
@Observable(lldb) v self.viewModel.propertyName排查“视图未更新”问题: 如果确认属性已修改(通过验证)但SwiftUI视图未重新渲染,使用检查修改操作所在的线程。的修改必须在上执行,SwiftUI才能观测到——在后台actor上的修改不会触发视图更新。在视图体中使用查看哪个属性触发(或未触发)了重渲染:
vbt@Observable@MainActorSelf._printChanges()(lldb) expr Self._printChanges()如需完整的观测诊断树 →
/skill axiom-swiftui-debuggingInspecting Actors
探查Actor
Actor state is best inspected with , which reads memory directly without isolation concerns:
v(lldb) v actorShows all stored properties. This works because LLDB pauses the entire process — you can read any memory regardless of actor isolation (which is a compile-time concept).
使用探查Actor状态是最佳选择,它直接读取内存,无需考虑隔离限制:
v(lldb) v actor显示所有存储属性。这是因为LLDB会暂停整个进程——你可以读取任意内存,不受Actor隔离(这是编译时概念)的限制。
Modifying Values at Runtime
运行时修改值
(lldb) expr self.debugFlag = true
(lldb) expr myArray.append("test")
(lldb) expr self.view.backgroundColor = UIColor.redModify values without rebuilding. Useful for testing theories.
(lldb) expr self.debugFlag = true
(lldb) expr myArray.append("test")
(lldb) expr self.view.backgroundColor = UIColor.red无需重新构建即可修改值,适用于验证假设。
Referencing Previous Results
引用之前的结果
LLDB assigns result variables (, , etc.):
$R0$R1(lldb) p someValue
$R0 = 42
(lldb) p $R0 + 10
$R1 = 52LLDB会为结果分配变量(、等):
$R0$R1(lldb) p someValue
$R0 = 42
(lldb) p $R0 + 10
$R1 = 52Playbook 4: Breakpoint Strategies
手册4:断点策略
Source Breakpoints (Basic)
源码断点(基础)
(lldb) breakpoint set -f ViewController.swift -l 42
(lldb) b ViewController.swift:42Short form works for simple cases.
b(lldb) breakpoint set -f ViewController.swift -l 42
(lldb) b ViewController.swift:42简写适用于简单场景。
bConditional Breakpoints
条件断点
Break only when a condition is true:
(lldb) breakpoint set -f MyFile.swift -l 42 -c "index > 100"
(lldb) breakpoint set -f MyFile.swift -l 42 -c "name == \"test\""Iteration-based: Break after N hits:
(lldb) breakpoint set -f MyFile.swift -l 42 -i 50Ignores the first 50 hits, then breaks.
仅当条件满足时触发断点:
(lldb) breakpoint set -f MyFile.swift -l 42 -c "index > 100"
(lldb) breakpoint set -f MyFile.swift -l 42 -c "name == \"test\""基于迭代次数: 跳过前N次触发,第N+1次触发:
(lldb) breakpoint set -f MyFile.swift -l 42 -i 50忽略前50次触发,第51次触发断点。
Logpoints (Action + Auto-Continue)
日志断点(动作+自动继续)
Log without stopping — like a print statement but no rebuild needed:
(lldb) breakpoint set -f MyFile.swift -l 42
(lldb) breakpoint command add 1
> v self.value
> continue
> DONEOr in Xcode: Edit breakpoint → Add Action → "Log Message" → use token syntax → Check "Automatically continue"
@self.value@无需暂停程序即可记录日志——类似print语句,但无需重新构建:
(lldb) breakpoint set -f MyFile.swift -l 42
(lldb) breakpoint command add 1
> v self.value
> continue
> DONE在Xcode中操作:编辑断点 → 添加动作 → "Log Message" → 使用令牌语法 → 勾选"Automatically continue"
@self.value@Symbolic Breakpoints
符号断点
Break on ANY call to a method by name:
(lldb) breakpoint set -n viewDidLoad
(lldb) breakpoint set -n "MyClass.myMethod"Break on all ObjC messages to a selector:
(lldb) breakpoint set -S "layoutSubviews"按方法名在任意调用位置触发断点:
(lldb) breakpoint set -n viewDidLoad
(lldb) breakpoint set -n "MyClass.myMethod"为OC选择器的所有消息触发断点:
(lldb) breakpoint set -S "layoutSubviews"Exception Breakpoints
异常断点
Swift errors (break on throw):
(lldb) breakpoint set -E swiftObjective-C exceptions (break on throw):
(lldb) breakpoint set -E objcIn Xcode: Breakpoint Navigator → + → Swift Error Breakpoint / Exception Breakpoint
This is the single most useful breakpoint for crash debugging. It stops at the throw site instead of the catch/crash site.
Swift错误(在throw时触发):
(lldb) breakpoint set -E swiftObjective-C异常(在throw时触发):
(lldb) breakpoint set -E objc在Xcode中操作: 断点导航器 → + → Swift Error Breakpoint / Exception Breakpoint
这是崩溃调试中最有用的断点类型,它会在抛出异常的位置暂停,而非在捕获/崩溃的位置。
Watchpoints
观察点
Break when a variable's value changes:
(lldb) watchpoint set variable self.count
(lldb) watchpoint set variable -w read_write myGlobalWatchpoints are hardware-backed — limited to ~4 per process but very fast.
当变量值改变时触发断点:
(lldb) watchpoint set variable self.count
(lldb) watchpoint set variable -w read_write myGlobal观察点由硬件支持——每个进程最多支持约4个,但速度极快。
One-Shot Breakpoints
一次性断点
Break once, then auto-delete:
(lldb) breakpoint set -f MyFile.swift -l 42 -o触发一次后自动删除:
(lldb) breakpoint set -f MyFile.swift -l 42 -oManaging Breakpoints
断点管理
(lldb) breakpoint list
(lldb) breakpoint disable 3
(lldb) breakpoint enable 3
(lldb) breakpoint delete 3
(lldb) breakpoint delete(lldb) breakpoint list
(lldb) breakpoint disable 3
(lldb) breakpoint enable 3
(lldb) breakpoint delete 3
(lldb) breakpoint deletePlaybook 5: Async/Concurrency Debugging
手册5:异步/并发调试
Identifying Async Frames
识别异步栈帧
Swift concurrency backtraces are noisy — expect , , and executor internals mixed in with your code. Don't be discouraged by 40+ frames of runtime noise. Focus on frames from YOUR module.
swift_task_switch_dispatch_call_block_and_releaseIn Swift concurrency backtraces, look for frames:
swift-taskThread 3:
frame #0: MyApp`MyActor.doWork()
frame #1: swift_task_switch
frame #2: MyApp`closure #1 in ViewController.loadData()The frame indicates an async suspension point. Your code frames are the ones prefixed with your module name ( above).
swift_task_switchMyAppSwift并发的回溯信息较为冗长——会包含、以及执行器内部代码。不要被40+帧的运行时信息吓到,重点关注属于你的模块的栈帧。
swift_task_switch_dispatch_call_block_and_release在Swift并发的回溯信息中,寻找包含的栈帧:
swift_taskThread 3:
frame #0: MyApp`MyActor.doWork()
frame #1: swift_task_switch
frame #2: MyApp`closure #1 in ViewController.loadData()swift_task_switchMyAppInspecting Task State
探查任务状态
(lldb) thread backtrace allLook for threads with in their frames. Each represents an active Swift task.
swift_task(lldb) thread backtrace all查找包含的线程,每个这样的线程代表一个活跃的Swift任务。
swift_taskActor-Isolated Code
Actor隔离代码
When stopped inside an actor:
(lldb) v selfShows all actor state. This works because LLDB pauses the entire process — actor isolation is a compile-time concept, not a runtime lock (for default actors).
当在Actor内部暂停时:
(lldb) v self显示所有Actor状态。这是因为LLDB会暂停整个进程——Actor隔离是编译时概念,而非运行时锁(默认Actor)。
Task Group Inspection
任务组探查
When debugging task groups, break inside the group closure and inspect:
(lldb) v
(lldb) btEach child task runs on its own thread. Use to see them.
bt allCross-reference: For Swift concurrency patterns and fix strategies → . For profiling async performance →
/skill axiom-swift-concurrency/skill axiom-concurrency-profiling调试任务组时,在组闭包内设置断点并执行以下命令:
(lldb) v
(lldb) bt每个子任务在独立线程运行,使用查看所有子任务。
bt all交叉参考: Swift并发模式及修复策略 → 。异步性能分析 →
/skill axiom-swift-concurrency/skill axiom-concurrency-profilingPressure Scenarios
压力场景处理
Scenario 1: "Release-Only Crash — LLDB Is Useless in Release"
场景1:"仅Release构建崩溃——LLDB在Release中无用"
Situation: Crash happens in Release builds but not Debug. Team says "we can't debug it."
Why this fails: Release optimizations change timing, memory layout, and can eliminate variables — making the crash non-reproducible in Debug.
Correct approach:
- Build with Debug configuration but Release-like settings:
- Optimization Level: (not
-O)-Onone - Still include debug symbols ()
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
- Optimization Level:
- Enable Address Sanitizer () — catches memory errors with 2-3x overhead
-fsanitize=address - Use the crash report to set breakpoints at the crash site
- Set exception breakpoints to catch the error before the crash:
(lldb) breakpoint set -E swift (lldb) breakpoint set -E objc - If variable shows , reduce optimization for that one file:
<optimized out>- Build Settings → Per-file flags → for the specific file
-Onone
- Build Settings → Per-file flags →
- Last resort — read register values directly (variables live in registers before being optimized out):
On ARM64:
(lldb) register read (lldb) register read x0 x1 x2= self,x0-x1= first 7 arguments. Checkx7Part 1 for details./skill axiom-lldb-ref
情况: 崩溃仅发生在Release构建,而非Debug构建。团队认为“无法调试”。
失败原因: Release构建的优化会改变程序时序、内存布局,甚至移除变量——导致崩溃在Debug构建中无法复现。
正确方案:
- 使用Debug配置但启用类Release的设置:
- 优化级别:(而非
-O)-Onone - 仍保留调试符号()
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
- 优化级别:
- 启用Address Sanitizer()——以2-3倍的性能开销为代价,捕获内存错误
-fsanitize=address - 根据崩溃报告在崩溃位置设置断点
- 设置异常断点提前捕获错误:
(lldb) breakpoint set -E swift (lldb) breakpoint set -E objc - 如果变量显示,降低该文件的优化级别:
<optimized out>- 构建设置 → 逐文件标志 → 为特定文件设置
-Onone
- 构建设置 → 逐文件标志 → 为特定文件设置
- 最后手段——直接读取寄存器值(变量在被优化前存储在寄存器中):
在ARM64架构中:
(lldb) register read (lldb) register read x0 x1 x2= self,x0-x1= 前7个参数。详情请参考x7第1部分。/skill axiom-lldb-ref
Scenario 2: "Just Add Print Statements"
场景2:"直接加Print语句"
Situation: Developer adds calls to debug, rebuilds, runs, reads console. Repeat.
print()Why this fails: Each print-debug cycle costs 3-5 minutes (edit → build → run → navigate to state → read output). An LLDB breakpoint costs 30 seconds.
Correct approach:
- Set a breakpoint at the line you'd add a :
print()(lldb) b MyFile.swift:42 - Add a logpoint for "print-like" behavior without rebuilding:
- Edit breakpoint → Add Action → Log Message → Check "Auto continue"
- Inspect variables directly:
v self.someValue - Modify variables at runtime to test theories:
expr self.debugMode = true - One breakpoint session replaces 5-10 print-debug cycles.
Time comparison (typical control-flow debugging):
| Approach | Per investigation | 5 variables |
|---|---|---|
| print() statements | 3-5 min (build + run) | 15-25 min |
| LLDB breakpoint | 30 sec (set + inspect) | 2.5 min |
Exception: In tight loops (thousands of hits/sec), logpoints add per-hit overhead. Use to skip to the iteration you care about, or use a temporary for that specific loop.
-iprint()情况: 开发者添加语句进行调试,重新构建、运行、查看控制台,重复该流程。
print()失败原因: 每次print调试循环耗时3-5分钟(编辑→构建→运行→导航到目标状态→读取输出),而LLDB断点仅需30秒。
正确方案:
- 在你想要添加的位置设置断点:
print()(lldb) b MyFile.swift:42 - 添加日志断点实现“类似print”的行为,无需重新构建:
- 编辑断点 → 添加动作 → Log Message → 勾选"Auto continue"
- 直接探查变量:
v self.someValue - 运行时修改变量验证假设:
expr self.debugMode = true - 一次断点会话可替代5-10次print调试循环。
时间对比(典型控制流调试):
| 方案 | 单次排查耗时 | 5个变量排查耗时 |
|---|---|---|
| print()语句 | 3-5分钟(构建+运行) | 15-25分钟 |
| LLDB断点 | 30秒(设置+探查) | 2.5分钟 |
例外情况: 在高频循环(每秒数千次触发)中,日志断点会带来额外开销。使用跳过无关的迭代,或为该特定循环临时添加。
-iprint()Scenario 3: "po Doesn't Work So LLDB Is Broken"
场景3:"po无效所以LLDB对Swift无用"
Situation: Developer types and gets garbage. Concludes LLDB is broken for Swift. Goes back to print debugging.
po myStructThis is the #1 reason developers abandon LLDB.
Why fails with Swift structs: calls which requires compiling an expression in the debugger context. For Swift structs, this compilation often fails due to missing type metadata, generics, or module resolution issues.
popodebugDescriptionCorrect approach:
- Use instead of
v— reads memory directly, no compilation:po(lldb) v myStruct (lldb) v myStruct.propertyName - Use for computed properties:
p(lldb) p myStruct.computedValue - Use only for classes with
poCustomDebugStringConvertible - If also fails, try specifying the language:
p(lldb) expr -l objc -- (id)0x12345 - If everything fails, always works inside a method.
v self
情况: 开发者执行得到乱码,得出LLDB对Swift无用的结论,回到print调试。
po myStruct这是开发者放弃LLDB的头号原因。
popodebugDescription正确方案:
- 使用替代
v——直接读取内存,无需编译:po(lldb) v myStruct (lldb) v myStruct.propertyName - 使用探查计算属性:
p(lldb) p myStruct.computedValue - 仅对实现了的类使用
CustomDebugStringConvertiblepo - 如果也失效,尝试指定语言:
p(lldb) expr -l objc -- (id)0x12345 - 如果所有命令都失效,在方法内部执行始终有效。
v self
Anti-Patterns
反模式
| Anti-Pattern | Why It's Wrong | Better Alternative |
|---|---|---|
| Fails for Swift structs, enums, optionals | |
| Print-debug cycles | 3-5 min per cycle vs 30 sec breakpoint | Breakpoints with logpoint actions |
| "LLDB doesn't work with Swift" | It does — wrong command choice | |
| Ignoring backtraces | Jumping to guesses instead of reading the trace | |
| Conditional breakpoints on every hit | Slows execution if condition is expensive | Use |
| Debugging optimized (Release) builds | Variables missing, code reordered | Debug configuration, or per-file |
| Force-continuing past exceptions | Hides the real error | Fix the exception, don't suppress it |
| No exception breakpoints set | Crashes land in system code, not throw site | Always add Swift Error + ObjC Exception breakpoints |
| 反模式 | 错误原因 | 更好的替代方案 |
|---|---|---|
所有场景都用 | 对Swift结构体、枚举、可选值失效 | 用 |
| Print调试循环 | 每次循环耗时3-5分钟,而断点仅需30秒 | 使用带日志动作的断点 |
| "LLDB对Swift无用" | 实际是命令选择错误 | |
| 忽略回溯信息 | 直接猜测原因而非读取回溯 | 先执行 |
| 对每次触发都设置条件断点 | 条件判断开销大,拖慢程序执行 | 尽可能使用 |
| 调试优化后的(Release)构建 | 变量缺失、代码重排 | 使用Debug配置,或为特定文件设置 |
| 强制跳过异常 | 隐藏真实错误 | 修复异常,而非抑制异常 |
| 未设置异常断点 | 崩溃发生在系统代码中,而非抛出位置 | 始终启用Swift Error + ObjC Exception断点 |
Debugging Checklist
调试检查清单
Before starting a debug session:
- Debug build configuration (not Release)
- Exception breakpoints enabled (Swift Error + ObjC Exception)
- Breakpoint set before suspected problem area
- Know which command to use: for values,
vfor computed,pfor descriptionspo
During debug session:
- Read stop reason () before anything else
thread info - Get backtrace () — find your frame
bt - Navigate to your frame ()
frame select N - Inspect relevant state (,
v self)v localVar - Understand the cause before writing any fix
After finding the issue:
- Set conditional breakpoint to catch recurrence
- Consider adding assertion/precondition for this case
- Remove temporary breakpoints
开始调试会话前:
- 使用Debug构建配置(而非Release)
- 启用异常断点(Swift Error + ObjC Exception)
- 在疑似问题区域前设置断点
- 明确使用的命令:探查值,
v探查计算属性,p查看描述po
调试会话中:
- 先读取停止原因()
thread info - 获取回溯信息()——找到你的栈帧
bt - 跳转到你的栈帧()
frame select N - 探查相关状态(,
v self)v localVar - 在编写修复代码前理解问题原因
找到问题后:
- 设置条件断点防止问题复发
- 考虑添加断言/前置条件
- 删除临时断点
Resources
资源
WWDC: 2019-429, 2018-412, 2022-110370
Docs: /xcode/stepping-through-code-and-inspecting-variables-to-isolate-bugs, /xcode/setting-breakpoints-to-pause-your-running-app, /xcode/diagnosing-memory-thread-and-crash-issues-early
Skills: axiom-lldb-ref, axiom-testflight-triage, axiom-hang-diagnostics, axiom-memory-debugging, axiom-swift-concurrency, axiom-concurrency-profiling
WWDC: 2019-429, 2018-412, 2022-110370
文档: /xcode/stepping-through-code-and-inspecting-variables-to-isolate-bugs, /xcode/setting-breakpoints-to-pause-your-running-app, /xcode/diagnosing-memory-thread-and-crash-issues-early
技能: axiom-lldb-ref, axiom-testflight-triage, axiom-hang-diagnostics, axiom-memory-debugging, axiom-swift-concurrency, axiom-concurrency-profiling