続・Debugger の Tips


[potatotips #1](https://github.com/potatotips/potatotips/wiki/potatotips-1) で LT した内容が好評(？)だったのでもう少し紹介してみようかなと思います。
発表した内容は「[Debugger の Tips](https://speakerdeck.com/dealforest/debugger-false-tips)」です。

当日のまとめは「[クックパッドのLT会に参加してきたのでiOSのtipsをまとめる](http://himaratsu.hatenablog.com/entry/potatotips1)」がおすすめです。

さてさて LLDB がデフォルトの Debugger となり久しいですね。
しかし Xcode から LLDB を扱う情報をあまりみかけません。かなしいですね。
というわけで今回は LLDB にフォーカスをあてて少し紹介してみようと思います。
(決して GDB もあわせて書くのが面倒くさかったというわけではありませんｗ)

## 設定ファイル
shell での .~rc ファイルみたいなものですね。
Xcode から起動した LLDB だと以下の２つのファイルのどちらかが読み込まれます。

* ~/.lldbinit-Xcode
* ~/.lldbinit

注意しないといけないのは `~/.lldbinit-Xcode` があると `~/.lldbinit` が読み込まれません。
CLI から起動すると `~/.lldbinit` のみが読み込まれます。
ということで以下のようにしておく事で奇麗に分けられます。

```lldb:~/.lldbinit-Xcode
# ここに Xcode の console で使用する特有の設定を記述
# command regex ps 's/^[[:space:]]*$/po [NSThread callStackSymbols]/' 等々
command source ~/.lldbinit
```

```lldb:~/.lldbinit
# ここに共通の設定を記述
# breakpoint や jump の alias 等々
# prompt の表示や format を変更したりもできます。
```

## できること
さてここからが本題ですね。
alias に関しては前の LT でふれているのでそれ以外のことを挙げてみます。

1. Python を実行
1. 独自コマンドの定義
1. Inspector のカスタマイズ

### 1. Python を実行

```lldb:console
(lldb) script import os
(lldb) script print os.getcwd()
/
```

### 2. 独自コマンドの定義
LLDB で独自コマンドを定義することができます。
Python で書けるので汎用性は高いと思います。

例えば動的に生成した UIImage をファイルに出力するコマンドとかは便利そうですね。

試しに `lssim` というコマンドを作ってみました。
Simulator の直下の起動している Application のディレクトリを `ls` するコマンドです。

```python:~/.lldb/ls_simulator.py
#!/user/bin/python

import lldb
import commands
import os

def ls_simulator_cmd(debugger, command, result, dict):
    app_dir = commands.getoutput('find "%s/Library/Application Support/iPhone Simulator" -type d -name *.app -print0 | xargs -0 ls -td | head -1' % os.environ.get('HOME'))
    shell_cmd = '/bin/ls -alF "%s/%s"' % (app_dir, command)
    shell_result = commands.getoutput(shell_cmd)
    result.PutCString(shell_result)

def __lldb_init_module(debugger, internal_dict):
    debugger.HandleCommand("command script add -f ls_simulator.ls_simulator_cmd lssim")
    print "lssim installed."
```

```lldb:console
(lldb) command script import ~/.lldb/ls_simulator.py
(lldb) lssim
total 240
drwxr-xr-x  9 dealforest  1469690100    306 Dec  6 04:06 ./
drwxr-xr-x@ 6 dealforest  1469690100    204 Dec  6 04:06 ../
-rw-r--r--  1 dealforest  1469690100  75320 Dec  6 04:06 Assets.car
drwxr-xr-x  3 dealforest  1469690100    102 Dec  6 04:06 Base.lproj/
-rw-r--r--  1 dealforest  1469690100   1006 Dec  6 04:06 Info.plist
-rw-r--r--  1 dealforest  1469690100  14929 Dec  6 04:06 LaunchImage-700-568h@2x.png
-rw-r--r--  1 dealforest  1469690100      8 Dec  6 04:06 PkgInfo
drwxr-xr-x  3 dealforest  1469690100    102 Dec  6 04:06 en.lproj/
-rwxr-xr-x  1 dealforest  1469690100  17280 Dec  6 04:06 hoge*
```

起動した際に import しておきたい場合は `~/.lldbinit-Xcode` に `command script import ~/.lldb/ls_simulator.py` をかいておけばいいです。


### 3. Inspector のカスタマイズ
「[Advanced Issues: Creating custom LLDB object summaries](http://stavash.wordpress.com/2013/01/06/advanced-issues-creating-custom-lldb-object-summaries/)」が分かりやすいです。

Beer のインスタンスが複数入った NSArray を Inspetor で確認すると、Beer のインスタンスの `summary` にはのアドレスが表示されているのですが、そこに自分がみたい情報を定義して視認性をあげれるので便利ですね。

## できないこと
1. Application の起動時に環境変数の設定
1. ホームディレクトリ以外の設定ファイルの読み込み

### 1. Application の起動時に環境変数の設定

設定ファイルから LLDB の環境変数を設定することはできます。

``` ~/.lldbinit-Xcode
env NSZombieEnabled=YES
```

```console
(lldb)settings show target.env-vars
target.env-vars (dictionary of strings) =
  NSZombieEnabled=YES
```

あくまで LLDB の環境変数の設定のみなので、アプリの起動時に環境変数を設定することはできないので上記のように `NSZombieEnabled=YES`と定義しても期待する挙動にはならないので Xcode の GUI 上で設定しましょう。

### 2. ホームディレクトリ以外の設定ファイルの読み込み

個人で設定して営む分には便利に使えてますけど、チーム開発で使う場合には個別に設定しないといけないのでとても不便です。
とはいえ `Inspector のカスタマイズ` はプロジェクトに固有したりするので、プロジェクト毎に管理できればいいなと思うものの、どうすればいいのかまだよくわかっていません。
できそうなんですけどね。この辺りはおいおい調べておきます。

## 総括

作業効率は確実にあがるので、騙されたと思って設定してみてはいかがでしょうか。
他にもこういうのあるよ！もしくはつっこみ等あればよろしくです。

ではでは。

## おまけ

僕が使っている設定を晒してみます

```~/.lldbinit-Xcode
command regex pv 's/^[[:space:]]*$/po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]/' 's/^(.+)$/po [%1 recursiveDescription]/'
command regex pe 's/^[[:space:]]*$/po [[NSProcessInfo processInfo] environment]/'
command regex ps 's/^[[:space:]]*$/po [NSThread callStackSymbols]/'
command regex pd 's/^(.+)$/po [[NSString alloc] initWithData:%1 encoding:4]/'

command script import ~/.lldb/ls_simulator.py

command source ~/.lldbinit
```

```~/.lldbinit
script import os, sys

command alias sp script print

# breakpoint shortcuts
# break on function/method/selector: b -n name
# break on C/C++ method: b -M method
# break on selector: b -S selector:here:
# break on address: b -a 0xfeedface
command alias b breakpoint set
command alias bd breakpoint disable
command alias be breakpoint enable
command alias bdel breakpoint delete
command alias bcommand breakpoint command add
command alias commands breakpoint command list

# jump aliases
# jump 0xfeedface
command alias jump register write pc
command alias jmp register write pc
command alias j register write pc
```