LLDB Debugging

横山祥平氏:はい、始めていきたいと思います。Githubでレポジトリを公開してるので、こちらをご覧いただけると詳細な内容がわかりやすいと思います。「#ca_swift」でツイートもしてるので、そちらからもご覧ください。

ということで、「LLDB Debugging」というタイトルで発表を始めていきたいと思います。

自己紹介ですが、横山祥平といいます。

CATSっていう事業部で新規事業の立ち上げを担当してます。その前はAbemaTVのiOS開発の担当をしてました。

さっそく内容に入っていきたいと思います。LLDB Debuggerはみなさんご存知の方も多いかと思いますが、こちらはLLVMの一部のプロジェクトとして進められているデバッガです。

処理の実行や管理、評価などができて、Xcodeのデフォルトのデバッガとして採用されてるので、みなさんも馴染みが深いものかなと思ってます。

(スライドを指して)こちらは基本的なコマンドのフォーマットで、実際に処理を実行することができます。コマンドに対してアクション、オプションと引数を指定することで、実行することができます。

例はこんな感じになりますが、こちらはhello.cファイルの1行目にブレイクポイントを設定するコマンドです。こちらのbreakpoint コマンドを含めて、コマンドの種類は「GDB to LLDB Command Map」というところに詳しく記載がありますので、そちらを見ていただければと思います。

続いてこちらは、CLIでLLDBを起動して実行した結果になります。このブレイクしている状態で、LLDBのさまざまなコマンドの実行が可能となります。

こちらは先ほどのレポジトリに内容をまとめてありますので、そちらを見ていただけると詳細がわかるかと思います。

LLDBの代表的なコマンド

WWDCで「Advanced Debugging with Xcode and LLDB」というセッションが発表されたのが、記憶に新しいかなと思います。こちら、見られた方はいらっしゃいますか?

(会場挙手)

ちらほらいる感じですね。のちほどデモで紹介していきたいと思いますが、こちらでも紹介された代表的なコマンドをいくつか紹介していきたいと思います。

まず、一番便利だなと思ったのが、このexpressionっていうコマンドで、現在のスレッドで式を評価して実行することができます。

例えば、このようにexpression label.text = “Hello”というコマンドを実行すると、そのテキストが反映されます。ただ、デバッグポイントで止まってる状態では、フレームバッファの更新が走らないので、値の更新をした後に、expressionでCATransaction.flush()という関数を呼び出す必要があります。

expressionコマンドはオプションも指定することができます。oのオプションを付けると、その言語にdescriptionのAPIがあれば、それを評価して出力することができます。Swiftの場合はCustomDebugStringConvertibleのdebugDescriptionが表示されるようになってます。

こちらはpoコマンドのエイリアスになっているので、実際にpoコマンドでも値の評価をすることができるようになってます。

こちらもサンプルを公開してますので、先ほどのレポジトリを見ていただけるといいかなと思います。

LLDBでPythonを使う

デモの前にLLDBでPythonを使う話に移っていきたいと思います。Pythonを使用することで、LLDBのフルAPIにアクセスすることができるので、コマンドをカスタムする場合などに、柔軟にコマンドを拡張することができます。

Python ファイルをインポートする場合は、cmmand script importでパスで該当のPythonファイルを指定することで、Pythonファイルを読み込むことができます。

こちらは、実際の基本的なカスタムコマンドを追加するサンプルです。ファイルの読み込みがあったときに、lldb_init_moduleという関数が呼び出されます。この関数に実際に処理を書くことになりますが、この場合はcommand script add -fというコマンドを実行すると、任意の関数を登録することができます。filename.function_nameでコマンドの名前を実行することで、そのコマンドが使えるようになります。

achooというのは、英語でくしゃみの効果音のことなんですけども、「achoo」と打つと、「Bless you」と返ってくるサンプルになります。

こちらを含めて、Configファイルに書いて使うことになると思います。CLIを使った場合は.lldbinitというファイルの読み込みが行われて、Xcodeの場合は.lldbinit-Xcodeというものが呼び出されます。

LLDBの共通の設定に関しては.lldbinit、Xcodeを使うものは.lldb-Xcodeに設定を書いて、Xcodeのほうで.lldbinitを読み込むのがいいかなと思ってます。

デモの前に……けっこう便利なツールがありますのでそちらを紹介できればと思います。

これはFacebookが開発してるchiselというツールで、iOS用に最適化されたPythonのカスタムコマンドが使えるツールになります。こちらが使えると非常に便利だと思うので、デモで紹介していきたいと思います。

LLDB Debuggerのデモ

CLIのデモもやろうと思ったんですけど、時間が足りないので、Xcodeのデモをやっていきたいと思います。

(以下、デモ)

こちらはリポジトリに公開してる簡単なサンプルです。本当に簡単なサンプルで、ボタンをタップするとこんな感じににオブジェクトが動くようなものになってます(デモ)。Directionというものが定義されていて、.clockwiseの場合は時計回りに移動するというものです。こちらを使って、LLDBで処理の実行を変えたいと思います。

まず、LLDBを起動する方法はいくつかあって、単純にブレイクポイントで止める方法や、ビューデバッギングで処理を止める方法があります。。あとは単純にポーズすることでLLDBが起動するようになってます。

先ほどのカスタムコマンドとかも読み込んであるので、例えば、「achoo」と打つと、「Bless you」って返ってきたりします。

さっそく試してみたいと思います。こちらは簡単な例で、ボタンをタップすると、時計回りの次のポイントを取得して、フレームを更新するというのと、テキストを更新するという処理です。

例えば、poコマンドで、currentPointを出力するといまrightBottomとなっていますが、expr currentPoint = .leftBottomと実行すると(exprはexpression)、実際にleftBottomに値が変わってることがわかるかと思います。

これで実行すると、こちらに移動しましたね。

もう1回やってみます。次は、(poコマンドで)currentDirectionと出力してみると、いまはclockwiseになっていますが、こちらを変えてみようと思います。こちらをrandomとかに変更すると(expr currentDirection = .random)、実際に処理が実行されて、こちらrandomに変わってるのがわかると思います。

この状態で実行すると、こちらrandomにボタンが移動するのがわかるかと思います。

あと、こちらはアニメーションを実行する処理になってるんですけども、現在はフレームの更新だけを行ってるところです。ここで、例えば、コメントに記載がありますが、ボタンのTransformを変換する処理を追加します。

expr self.button.transform = self.button.transform.rotated(by: 90 * (.pi / 180))

このコマンドを実行すると、回転しながら移動したのがわかるかと思います。いまは1回だけ実行しただけなんで、このままになってるんですけど、もしこの箇所で常に実行したい場合は、ブレイクポイントを、ダブルクリックをすることで、詳細な設定ができるようになります。

コンディションを加えることによって、条件だけブレイクポイントを止めることができたり、あとはこのActionを設定すると、ブレイクポイントでブレイクしたときに、任意のアクションを実行するとかできます。

先ほどのコマンドを入力して、このOptionをtrueにすると(Automatically continue after evaluating actions)、アクションを実行して、ブレイクしないで処理が実行されます。。そうすると、回転のアニメーションが加わって実行されるかと思います。

最後に、先ほどのchiselのコマンド も少し試したいと思います。先ほど変数を使用してましたけど、ビューデバッギングでビューのアドレスを見ることができて、ここのアドレスを入力することで、評価することができます。

Chiselにいくつかコマンドが用意されていて。例えば、このボタンのアドレスを引数に取って、hideコマンドを実行すると、実際にボタンが消えたのがわかると思います。続いてshowコマンドを実行すると、またボタンが表示されます。borderコマンドでは、color blue、width 10のoptionを付けると、ボーダーが付いたのがわかるかと思います。

unborderとやると、実際にボーダーが消えます。あとpvcコマンドを実行すると、現在のキーウィンドウを基準に、ViewControllerの階層をデバッグしてくれます。

pviewsとやると、キーウィンドウを基準に、ビューの階層構造、ログを出してくれたりします。

あとはタップしたときにブレイクして、ログを出してくれるような、taplogという便利なコマンドもあります。

それ以外にもいろんなコマンドが用意されているので、chiselのレポジトリを参考にしていただけるといいかなと思います。

デモは以上になります。LLDBで評価ができると再ビルドなしに色々な処理を実行できるので、みなさん試してみてください。発表は以上になります。ご清聴ありがとうございました。

(会場拍手)