しおメモ

雑多な技術系ブログです。ニッチな内容が多いです。

XcodeのLLDBデバッグでよく使う技

若干話題になって出尽くしてる感がありますが、XcodeのLLDBを絡めたデバッグでよく使う手法をまとめてみました。

特定の行をスキップ

行をスキップしたい際は、thread jumpthread returnを活用します。

# 1行スキップ
(lldb) th j --by 1
c

th jthread jumpの、ccontinueのように一意に決まれば先頭だけで短縮してよいので、こちらを利用すると便利です。

ある1行だけコメントアウトしたい際は、ブレークポイントを挟んで再度ビルドせずにXcodeのGUIを利用してこのようにすると、同様のことができます。

f:id:scior:20190703220643p:plain

thread returnは実行中の関数から即returnすることが出来ます。

(lldb) th r
c

特定の行を書き換える

書き換えたい行の手前にブレークポイントを挟んで、expressionコマンドを実行して、thread jumpすることで実現できます。

# Viewの背景色を赤に書き換える例
(lldb) e view.backgroundColor = UIColor.red
th j --by 1
c

eexpressionのエイリアスになっています。

こちらも毎回書き換えたい場合には、XcodeのGUIを利用するのが便利です。

f:id:scior:20190630233325p:plain

このようにすることで、上の3行のLLDBコマンドと同様のことを毎回実行してくれます。

ブレークポイントをon/offする

アクティブなブレークポイントは、breakpoint listで確認します。

(lldb) br l
Current breakpoints:
1: file = '/Users/hoge/iOS/ColorStock/ColorStock/Domain/Model/EditingThemeDataStore.swift', line = 18, exact_match = 0, locations = 0 (pending)


2: file = '/Users/hoge/iOS/ColorStock/ColorStock/Presentation/ViewController/ColorSelectorViewController.swift', line = 88, exact_match = 0, locations = 1, resolved = 1, hit count = 0

  2.1: where = ColorStock`ColorStock.ColorSelectorViewController.toastViewRecoveryButtonTapped(Any) -> () + 1295 at ColorSelectorViewController.swift:88:5, address = 0x00000001029bcf0f, resolved, hit count = 0

ブレークポイントを無効にしたい場合は、breakpoint disableを使います。削除の場合はbreakpoint deleteです。複数指定も可能です。

# リストの2番目と4番目のブレークポイントを無効にする
(lldb) br di 2 4
1 breakpoints disabled.
# リストの1番目のブレークポイントを削除する
(lldb) br de 1
1 breakpoints deleted; 0 breakpoint locations disabled.

disableしたブレークポイントはenableで有効にできます。

# リストの1番目のブレークポイントを有効にする
(lldb) br e 1
1 breakpoints enabled.

アドレスからオブジェクトに戻して変数に格納する

デバッグ時の出力が不足している場合、アドレスから元のオブジェクトを取り出したいケースがあります。
その場合、Swiftの文としてexpressionunsafeBitCastを利用するのが、一つの手法としてあります。

# Viewの親VCを取得する
(lldb) e -l swift -- let $view = unsafeBitCast(0x01234567, UIView.self)
e -l swift -- print(view.parentVIewController)

letを使って変数を定義したい場合は、e -l swiftのように言語を明示する必要があります。--はセパレータになります。
その上、変数名は$viewのように$で始まる必要があります。

~/.lldbinit~/.lldbinit-Xcodeなどに以下のように記載すると、LLDBのエイリアスを定義することが出来るため、

swi let $view = unsafeBitCast(0x012345678900, UIView.self)

のように書くことが出来て、記述が楽になります。

command alias swi expr -l swift --

単純に変数に格納せずにprintしたい場合は、poコマンドを利用します。

po unsafeBitCast(0x012345678900, UIView.self).parentViewController

組み込みメソッドに対してブレークポイントを仕込む

Symbolic Breakpointを使うことで、組み込みのメソッドに対してもブレークポイントを追加することが出来ます。

viewDidLoadのように、UIKitのメソッドを対象にする場合は、Obj-C記法で-[UIViewController viewDidLoad]のように書きます。

f:id:scior:20190703220948p:plain

$arg1にオブジェクトが入るので、po $arg1とすることで、どのオブジェクトの該当シンボルが呼ばれているか確認することが出来ます。

変数の変更を監視する

watchpointを使うことで、指定した変数の変更を監視ができます。
設定はwatchpoint set varで行い、ブレークポイントのようにlistで一覧表示してdisableで無効にできます。

# self.redを監視
(lldb) w s v self.red
Watchpoint created: Watchpoint 3: addr = 0x7ffee3515a28 size = 1 state = enabled type = w
    declare @ '/Users/hoge/iOS/ColorStock/ColorStock/Data/Entity/Color.swift:21'
    watchpoint spec = 'self.red'
(lldb) w l
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 2: addr = 0x7ffee3515c58 size = 8 state = disabled type = w
    declare @ '/Users/hoge/iOS/ColorStock/ColorStock/Presentation/View/ColorDetailView.swift:31'
    watchpoint spec = 'self'
    old value: 0x000000010c709300
    new value: 0x000000010c709300
Watchpoint 3: addr = 0x7ffee3515a28 size = 1 state = enabled type = w
    declare @ '/Users/hoge/iOS/ColorStock/ColorStock/Data/Entity/Color.swift:21'
    watchpoint spec = 'self.red'
(lldb) w di 2
1 watchpoints disabled.

その他のよく使うコマンド

Xcodeのボタンでも操作できますが、以下の操作はLLDBでも出来ます。

コマンド 操作
bt スタックトレース
step ステップ実行
stepi ステップイン
continue 実行の継続