ハイライト
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
キーポイント:
bはbreakpoint setの短縮break command addは直近ブレークポイントにアクション追加。DONEで終了- コマンドライン設定は Xcode 設定を上書き
helpとaproposで LLDB 全コマンド検索
条件ブレークポイントと高頻度ヒット
ループ内でブレークポイントが頻繁に発火する場合、3 つのフィルタ(16:09):
- 条件ブレークポイント:
break modify --conditionで Boolean 式、例video.duration > 60 - 一時ブレークポイント(tbreak):アクション内で 1 回限り作成——「A から B まで実行後に停止」
- 無視カウント:
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。
関連セッション
- Symbolication: Beyond the basics — dSYM シンボル化の深掘り、crashlog の正確なソース行表示
- Debug with structured logging —
pコマンドの内部機構と構造化ログのベストプラクティス - What’s new in Swift — Swift 6 総覧、@DebugDescription 等マクロ
- Analyze heap memory — Instruments ヒープ分析、メモリリークと性能ボトルネック特定
コメント
GitHub Issues · utterances