WWDC Quick Look 💓 By SwiftGGTeam
Discover breakpoint improvements

Discover breakpoint improvements

观看原视频

Highlight

Xcode 13 为断点加入列断点、未解析断点提示和运行时问题断点,让开发者能在具体表达式、符号解析失败点和运行时问题发生现场暂停程序。

核心内容

调试时,开发者真正需要的是在错误发生前停住程序。停住以后,才能检查进程状态,才能一步一步验证逻辑。

以前最常用的是行断点。它适合停在某一行,但一行代码里可能有多个表达式。编译器会为这些表达式生成多个可停止位置。你想进入 convertedToVolume,调试器可能先进入 adjustedDensity。然后你要 step out,再 step in。重复几次以后,调试节奏就断了。

Xcode 13 解决的是这个粒度问题。它加入了列断点(Column Breakpoint)。开发者可以把断点放在同一行里的某个表达式上。程序暂停时,Xcode 还会用列 PC(Column PC)在表达式下方画绿色下划线,告诉你下一步将执行哪一个表达式。

符号断点也有类似的痛点。你输入一个函数名,LLDB 会在进程加载的所有库里匹配它。函数名太常见时,命中位置可能有几百个。函数名拼错时,断点一直不命中,你只能猜原因。

Xcode 13 给未解析断点加了虚线图标和提示。它会指出常见原因:符号名是否拼错、符号是否存在、库是否已经加载、源码行是否参与编译、模块是否生成调试信息。

运行时问题是第三类场景。比如在后台线程修改 UI 状态。默认情况下,Xcode 会把调用栈记录到 Issue navigator,但不会暂停程序。问题已经发生以后,再看当前进程状态通常帮助有限。运行时问题断点可以让程序在问题发生当下停住。

详细内容

列断点:停在一行里的具体表达式

02:27)行断点按源代码行暂停。遇到一行里有多个表达式时,LLDB 会根据编译器生成的 line table(行表)选择停止位置。Xcode 13 的列断点把停止点推进到表达式级别。

关键操作如下。

Command-click expression
Select "Set Column Breakpoint"
Continue execution
Check the green underscore from Column PC
Step into the highlighted expression

关键点:

  • Command-click expression:在目标表达式上打开 Actions popover。
  • Set Column Breakpoint:把断点绑定到这一列的表达式。
  • Continue execution:继续运行,直到命中列断点。
  • Column PC:绿色下划线标记调试器下一步要执行的表达式。
  • Step into:确认下划线在目标表达式下方以后,再执行单步进入。

04:15)这个能力对 Swift closure(闭包)和 Objective-C block 更实用。一个 Swift 行里可能有多个闭包。以前只能在整行打断点,然后反复 step in 和 step out。现在可以直接在最后一个 $0 上设置列断点。

关键操作如下。

Command-click the last $0
Select "Set Column Breakpoint"
Inspect $0 when the debugger pauses

关键点:

  • the last $0:目标是同一行中最后一个闭包的匿名参数。
  • Set Column Breakpoint:让 LLDB 暂停在该闭包对应的 line table entry。
  • Inspect $0:暂停位置已经落在目标闭包内,可以直接查看匿名参数。

符号断点:按函数名暂停,并限制模块范围

05:27)符号断点(Symbolic Breakpoint)按函数名暂停。它适合两种情况:没有源码文件,或者很多子类实现了同一个函数,不想逐个文件设置断点。

在 Xcode 里创建符号断点的路径如下。

Breakpoint Navigator
Add button
Symbolic Breakpoint
Symbol: toggle
Module: Fruta

关键点:

  • Breakpoint Navigator:断点管理入口。
  • Add button:打开可创建的断点类型列表。
  • Symbolic Breakpoint:选择按符号名暂停。
  • Symbol: toggle:输入要匹配的函数名。
  • Module: Fruta:把匹配范围限制在应用主二进制,避免系统库里的同名符号一起命中。

06:34)函数名是常见词时,要限制模块。LLDB 会在进程加载的所有 library(库)里匹配符号。限制到 Fruta 后,演示中的命中位置从潜在的大量结果缩小到 3 个。

未解析断点:用图标和 LLDB 找出拼写错误

07:59)Xcode 13 会把没有被 LLDB 解析到任何位置的断点显示为虚线图标。把鼠标悬停在图标上,Xcode 会给出常见解释。

常见原因如下。

Symbol name must be spelled correctly
Symbol must exist in its library
Library for the breakpoint must be loaded
Source line must be compiled
Module must have debug info

关键点:

  • Symbol name:符号断点的函数名需要拼写正确。
  • Symbol must exist:符号需要存在于指定库中。
  • Library ... loaded:库可能在用户操作后才加载,加载后 LLDB 会自动解析。
  • Source line ... compiled:源码断点所在行需要参与编译。
  • debug info:模块需要生成调试信息,否则源码断点无法解析。

09:15)演示中,开发者把 convertedToMass 拼成了 convertToMass。直接在源码里搜索 convert 会得到多条结果。更快的方法是在 Xcode console 里用 LLDB 搜索进程中的符号名。

image lookup -rn convert Fruta

关键点:

  • image:在 LLDB 中也表示 module(模块)。
  • lookup:查找模块里的符号信息。
  • -r:使用 regular expression(正则表达式)匹配。
  • -n:按 name(名称)查找。
  • convert:要搜索的符号名片段。
  • Fruta:把搜索限制在应用模块中。

这个命令在演示里只返回 4 个匹配项。结果显示正确函数名是 convertedToMass。把它复制回断点编辑器后,LLDB 成功解析到 1 个位置。

源码断点未解析:检查编译条件和调试信息

10:22)未解析断点也会出现在源码断点上。演示中的断点放在第 23 行,但这一行位于 compiler condition(编译条件)的 else 分支,当前构建没有编译它。

排查路径如下。

Check whether the source line is compiled
Check build settings for debug info

关键点:

  • source line is compiled:断点所在行必须进入当前构建产物。
  • build settings:如果模块没有生成调试信息,需要检查构建设置。
  • debug info:调试器依赖调试信息把源码位置映射到已编译地址。

运行时问题断点:在问题发生当下暂停

10:49)运行时问题(Runtime Issue)是在运行阶段出现的问题。演讲给出的例子是从后台线程修改 UI 状态。默认行为是记录 backtrace(调用栈)并显示在 Issue navigator。

如果想在问题发生时检查进程状态,可以创建运行时问题断点。

Add Runtime Issue Breakpoint
Choose a specific type in the type popup
Open Scheme Editor
Diagnostics tab
Enable Main Thread Checker

关键点:

  • Runtime Issue Breakpoint:让调试器在运行时问题出现时暂停。
  • type popup:选择具体问题类型。
  • Scheme Editor:某些运行时问题断点需要先开启对应诊断能力。
  • Diagnostics tab:诊断开关所在位置。
  • Main Thread Checker:演示中用于捕获主线程相关运行时问题的诊断功能。

核心启发

  • 做什么:给复杂 Swift 链式调用建立“表达式级调试清单”。 为什么值得做:列断点能停在同一行里的具体表达式,适合调试多闭包、多函数调用的 Swift 代码。 怎么开始:找出项目中包含多个 closure 的长行,在关键 $0 或目标调用上 Command-click,设置列断点,再用 Column PC 确认暂停位置。

  • 做什么:为协议或基类的同名实现建立符号断点。 为什么值得做:符号断点可以一次覆盖多个类中的同名函数,省去逐个文件打断点。 怎么开始:在 Breakpoint Navigator 新建 Symbolic Breakpoint,Symbol 填函数名,Module 填应用二进制名称。

  • 做什么:把“断点不命中”排查流程写进团队调试文档。 为什么值得做:Xcode 13 会显示未解析断点,LLDB 的 image lookup -rn 能快速验证符号名。 怎么开始:在 Xcode console 里运行 image lookup -rn <name> <module>,确认真实符号名,再修正断点编辑器里的 Symbol。

  • 做什么:为 UI 线程问题创建专门的运行时问题断点。 为什么值得做:Issue navigator 只能显示已经发生的问题;运行时问题断点能让你在现场检查变量和调用栈。 怎么开始:在 Scheme Editor 的 Diagnostics tab 开启 Main Thread Checker,再添加 Runtime Issue Breakpoint。

  • 做什么:在条件编译代码旁边保留调试检查步骤。 为什么值得做:源码断点可能因为当前构建没有编译该行而无法解析。 怎么开始:遇到虚线断点时,先检查该行是否落在当前 compiler condition 内,再检查模块是否生成 debug info。

关联 Session

评论

GitHub Issues · utterances