WWDC Quick Look 💓 By SwiftGGTeam
Run, Break, Inspect: Explore effective debugging in LLDB

Run, Break, Inspect: Explore effective debugging in LLDB

元の動画を見る

ハイライト

LLDB はデバッグを Run → Break → Inspect のループとしてモデル化:各停止後に状態を検査し、結果に応じて続行または再実行——問題範囲を狭めてバグを特定。


主要内容

print デバッグは多くの人が最初に学ぶ方法:print 挿入、再コンパイル、実行、出力確認、不満なら繰り返し。3 つの問題——print 変更のたび再コンパイル、本番残留リスク、事前に思いついた状態しか検査できない。LLDB はより効率的:実行時にブレークポイント設定、変数検査、式評価——再コンパイル不要。

本セッションはデバッグを探索問題として捉える。ある時点で誤動作、バグは起動からその瞬間の間のどこか。目標はそのコードを見つけること。異なる時点で状態を検査、各検査がバグに近づける。LLDB の 4 核心ツール:backtrace、変数検査、ブレークポイント、式評価。

マルチプラットフォーム Destination Video App を例に実シナリオ:「Watch Later」追加、SwiftUI クロージャ実行タイミング理解、JSON デコードエラー排查、カスタム型のデバッガー表示。各々が LLDB 核心能力に対応。


詳細

crashlog オフライン分析

クラッシュ時 Apple プラットフォームは状態を収集し crashlog 生成。LLDB は crashlog を消費してデバッグセッションを模擬——プログラム実行なしで問題特定(04:37)。

Xcode で crashlog を右クリック、対応プロジェクトに関連付けて開く。Xcode が LLDB セッション作成、クラッシュ行をハイライト。backtrace で完全呼び出しチェーン:現在フレームは JSON 読み込み、親フレームは動画メタデータインポート、その上は初期化。crashlog 1 つでバグ特定も。

前提:プロジェクトを crashlog と同一 commit に checkout、対応 dSYM bundle 利用可能。

SwiftUI の 1 行ブレークポイントが複数位置に解決

SwiftUI Button イニシャライザの行ブレークポイントは複数独立位置に解決(08:29):

Button(action: { watchLater.toggle(video: video) }) {
  let inList = watchLater.isInList(video: video)
  Label(inList ? "In Watch Later" : "Add to Watch Later",
  systemImage: inList ? "checkmark" : "plus")
}

キーポイント:

  • LLDB はこの行を 3 ブレークポイント位置(1.1、1.2、1.3)に解決
  • 1.1 は Button イニシャライザ呼び出し——UI 作成時
  • 1.2 は action クロージャ(watchLater.toggle(video: video))——タップ時のみ
  • 1.3 は trailing closure(label)——イニシャライザ自身が呼び出し
  • SwiftUI 等宣言型フレームワークで一般的:同一行、異なるコードパス
  • breakpoint list で各行・列番号確認
  • 各位置を個別に有効/無効化可能

ブレークポイントアクションとコマンドライン操作

Break/Inspect ループで同じコマンド手動繰り返しは煩雑。ブレークポイントアクションはヒット時に自動実行(13:17):

p "last video is \(watchLater.last?.name)"

Xcode:ブレークポイント右クリック → Edit Breakpoint → Debugger Command action 追加。「Continue after evaluation」で自動続行。

コマンドラインでも同様(14:42):

b DetailView.swift:70
break command add
p "last video is \(watchLater.last?.name)"
continue
DONE

キーポイント:

  • bbreakpoint set の短縮
  • break command add は直近ブレークポイントにアクション追加。DONE で終了
  • コマンドライン設定は Xcode 設定を上書き
  • helpapropos で LLDB 全コマンド検索

条件ブレークポイントと高頻度ヒット

ループ内でブレークポイントが頻繁に発火する場合、3 つのフィルタ(16:09):

  1. 条件ブレークポイントbreak modify --condition で Boolean 式、例 video.duration > 60
  2. 一時ブレークポイント(tbreak):アクション内で 1 回限り作成——「A から B まで実行後に停止」
  3. 無視カウントbreak modify --ignore-count 10 で最初 N 回スキップ

1 行が数百万回実行される場合、LLDB は毎回条件評価で停止し実行遅延。コードに if を追加し内部で raise(SIGSTOP) を呼びデバッガーに制御移譲。

p コマンドと式評価

Xcode 15 から p は「do what I mean」print のエイリアスに再定義——大多数の変数検査と式評価をカバー(19:57)。

JSON デコードエラー排查では p で段階的に複雑な式を構築(24:06):

p watchLater.count
p watchLater.last!.name

キーポイント:

  • p は任意 backtrace フレームで式評価
  • 段階的構築、中間結果確認、再コンパイル不要
  • 変数は Xcode 変数ビューア/ソースホバーでも確認
  • 一部型(URL、Image)は Quick Look で追加可視化
  • Swift Error ブレークポイントで Swift Error throw 時自動停止

@DebugDescription マクロ

Swift 6 で @DebugDescription 追加——カスタム型がデバッガーで可読サマリー表示(26:46):

@DebugDescription
struct WatchLaterItem {
    let video: Video
    let name: String
    let addedOn: Date

    var debugDescription: String {
        "\(name) - \(addedOn)"
    }
}

キーポイント:

  • @DebugDescription 注釈+debugDescription 計算プロパティ
  • debugDescription は文字列補間と stored property で構築必須
  • 変数ビューアと p でサマリー直接表示——各フィールド展開不要
  • CustomDebugStringConvertible + po で文字列補間/計算プロパティのみなら @DebugDescription へ移行推奨

重要ポイント

  • 何をするか:print デバッグの代わりにブレークポイントアクション。なぜ価値があるか:再コンパイル不要、動的追加/変更、本番残留リスク回避。始め方:Xcode ブレークポイント右クリック → Edit Breakpoint → Debugger Command action、p 式、自動続行。

  • 何をするか:コレクションに頻出するコアデータ型に @DebugDescriptionなぜ価値があるか:変数ビューアで各エントリ展開なしにサマリー表示。始め方:型に注釈、debugDescription で重要フィールドを補間出力。

  • 何をするか:CI で各ビルドの dSYM bundle をアーカイブ。なぜ価値があるか:crashlog オフライン分析は dSYM シンボル化に依存。なければアドレスのみ、ソース行特定不可。始め方:Archive 時 dSYM 生成確認。App Store Connect は自動処理、内部配布は自行管理。

  • 何をするか:高頻度ヒットに条件ブレークポイントと無視カウント——ブレークポイント諦めない。なぜ価値があるか:ループ内は数千回発火、手動続行非現実。条件は必要時のみ停止、無視カウントは既知無関係反復をスキップ。始め方:Edit Breakpoint → Condition/Ignore Count。CLI: break modify --condition/--ignore-count


関連セッション

コメント

GitHub Issues · utterances