Vdebugでデバッグする

Vdebugというデバッガを紹介します。

- [joonty/vdebug: Multi-language DBGP debugger client for Vim (PHP, Python, Perl, Ruby, etc.)](https://github.com/joonty/vdebug)

実際に動いている様子は[Debug Drupal PHP in Vim with Vdebug | Mediacurrent](http://www.mediacurrent.com/blog/debug-drupal-php-vim-vdebug)に[動画](https://youtu.be/_6LV_XgP6Fs)があったので、そちらを見て下さい。

Vdebugを使うにはVimが`+python`でコンパイルされている必要があるので、

```bash
$ vim --version
```

で`+python`になっているかを確認します。

インストールは[Shougo/dein.vim](https://github.com/Shougo/dein.vim)などを使って、インストールしてください。

Vdebugは[DBGp](https://en.wikipedia.org/wiki/DBGp)というプロトコルを使うため、デバッグしたい言語でDBGpが使えるように設定する必要があります。各言語の設定はVdebugのヘルプに載っています（`:help VdebugSetUp<言語>`）。


## PHP

まずはPHPを例にして使い方を説明していきます（`:help VdebugSetUpPHP`）。

### Xdebug

PHPでデバッグするにはXdebugが必要です。[xdebugのinstallのドキュメント](https://xdebug.org/docs/install)などを見てインストールしましょう。

Xdebugのremote debugのついての説明は[XdebugのRemote Debuggingのドキュメント](https://xdebug.org/docs/remote#browser_session)に書かれています。

XdebugとVdebugを使ったwebページのdebugは以下のように行われます。

```
+------------------+                  +------------------+
|                  | ---------------> | 80               |
|                  |   HTTP Request   |                  |
|                  |                  |                  |
|             9000 | <--------------- |                  |
|                  |   DBGP Connect   |                  |
|   Vim / Vdebug   |                  |   PHP / Xdebug   |
|                  | <--------------> |                  |
|                  |       DBGP       |                  |
|                  |                  |                  |
|                  | <--------------- |                  |
|                  |   HTTP Response  |                  |
+------------------+                  +------------------+
```

左側がVimで、右側がデバッグしたい対象です。

1. まず、Vdebugを起動して、xdebug.remote_portで設定したポート（デファルトは9000）をリッスンしておきます。
2. デバッグしたいURLにHTTPでリクエストします。このときidekeyと呼ばれる設定も送ります
3. idekeyを送られたPHPはxdebug.idekeyに設定された値と一致した場合にxdebug.remote_hostに設定されたホストのxdebug.remote_portに設定されたポートに接続します
4. デバッグが開始されます
5. デバッグが終了するとHTTPレスポンスが返ります

Xdebugから見て、デバッグクライアントはremoteなので、`xdebug.remote_...`という設定名になっています。

### PHPとVimが同じ環境で動いている場合の設定

例えば、PCでPHPを動かしてVimでデバッグする場合や、サーバでPHPを動かしてそのサーバのVimでデバッグする場合、PHPのiniファイルを以下のようの設定します。

```ini
; 1にするとxdebug.remote_hostとxdebug.remote_portで設定したホストとポートをリッスンしているデバッグクライアントに
; 接続を試みるようになる。defaultは0。
xdebug.remote_enable=1
; 設定したidekeyと同じ値がPHPに送られたときにデバッグを開始する。周りの人がPhpStormを使っているなら、PHPSTORMなど。
xdebug.idekey="PHPSTORM"
; デバッグクライアントのホスト。defaultはlocalhost。
xdebug.remote_host=localhost
; デバッグクライアントがリッスンしているポート。defaultは9000。
xdebug.remote_port=9000
; HTTPのGET/POST/COOKIEのの設定にかかわらずデバッグの開始を試みる。defaultは0。
xdebug.remote_autostart=1
```

最後の`xdebug.remote_autostart=1`は0にしても、後で入れるブラウザ拡張がうまくやって、デバッグを開始してくれるはずですが、開始しないので入れています。

### サーバ上でPHPを動かして、PC上のVimでデバッグする場合

PHPのiniファイルを以下のようの設定します。

```ini
; 1にするとxdebug.remote_hostとxdebug.remote_portで設定したホストとポートをリッスンしているデバッグクライアントに
; 接続を試みるようになる。defaultは0。
xdebug.remote_enable=1
; 設定したidekeyと同じ値がPHPに送られたときにデバッグを開始する。周りの人がPhpStormを使っているなら、PHPSTORMなど。
xdebug.idekey="PHPSTORM"
; 1にするとHTTPでリクエストして来たIPに接続を試みるようになる。defaultは0
xdebug.remote_connect_back=1
; デバッグクライアントがリッスンしているポート。defaultは9000
xdebug.remote_port=9000
; HTTPのGET/POST/COOKIEのの設定にかかわらずデバッグの開始を試みる。defaultは0。
xdebug.remote_autostart=1
```

`xdebug.remote_host`にPCのIPアドレスを設定することでもデバッグできますが、それだと一つのPCしか接続できません。`xdebug.remote_connect_back`を使うと複数のPCで同時にデバッグをすることが出来ます。

サーバ上のファイルと、PC上のファイルのマッピングは`g:vdebug_options['path_maps']`で設定します。この部分の設定だけを設定することは出来ないので、まず、デフォルトの設定をします。

```vim
let g:vdebug_options= {
\    "port" : 9000,
\    "server" : '',
\    "timeout" : 20,
\    "on_close" : 'detach',
\    "break_on_open" : 1,
\    "ide_key" : '',
\    "path_maps" : {},
\    "debug_window_level" : 0,
\    "debug_file_level" : 0,
\    "debug_file" : "",
\    "watch_window_style" : 'expanded',
\    "marker_default" : '⬦',
\    "marker_closed_tree" : '▸',
\    "marker_open_tree" : '▾'
\}
```

ide_keyは空のままでも大丈夫です。設定するとデバッグの対象をide_keyで絞り込みます。

そして、ファイルパスのマッピングの設定をします。

```vim
let g:vdebug_options['path_maps'] = {"/path/to/server/scripts": "/path/to/local/scripts"}
```

### サーバ上でPHPが動いていて、PCでデバッグしたい人と、サーバ上でデバッグしたい人がいる場合

PCでデバッグしたい人は`xdebug.remote_connect_back=1`とすることでデバッグ出来ますが、サーバ上でデバッグしたい人はlocalhostに接続してほしいので両立しません。

#### 解決策1: サーバ上でデバッグしたいしたい人がサーバ上からリクエストする

サーバ上でデバッグしたい人が、そのサーバ上で`curl`やテキストブラウザでリクエストすれば、remote_connect_back先はそのサーバになるのでデバッグできます。ただ、PCのブラウザを使いたいですよね……。

#### 解決策2: プロキシを立てる

[Remote Debuggingのドキュメント](https://xdebug.org/docs/remote#browser_session)のMultiple Users DebuggingのDBGp proxyを使い、remote_hostをproxyに設定して、proxyが振り分けるようにするとうまくいくかもしれません。

### ブラウザ拡張

ブラウザからデバッグを開始したい場合は、例えばchromeの場合、[Xdebug helper](https://chrome.google.com/webstore/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc)という拡張をインストールします[^1]。オプションでidekeyを設定します。

### デバッグの仕方

まず、VimでF5を押してデバッガを起動します。

次にwebページをデバッグしたい場合はインストールしたブラウザ拡張のDebugをonにしてアクセスします。

コマンドラインからPHPを実行する場合は、環境変数XDEBUG_CONFIGにidekeyを設定して、PHPを起動します。

```bash
XDEGUG_CONFIG="idekey=PHPSTORM" php script.php
```

PHPUnitを使ったテストもデバッグできます。ただし、関数名に日本語を使っていると落ちます。

```bash
XDEGUG_CONFIG="idekey=PHPSTORM" php /path/to/phpunit ...
```

実行後、Vimを見ると、コードの最初の行で止まっているので、F2などを押してステップ実行していきます。

デフォルトのキーは以下の通り。

```
<F5>: start/run (to next breakpoint/end of script)
<F2>: step over
<F3>: step into
<F4>: step out
<F6>: stop debugging (kills script) （すでにデバッグが終わっている場合はデバッガUIを閉じます）
<F7>: detach script from debugger
<F9>: run to cursor
<F10>: toggle line breakpoint
<F11>: show context variables (e.g. after "eval")
<F12>: evaluate variable under cursor
```

自分で設定することもできます。

```vim
let g:vdebug_keymap = {
\    "run" : "<F5>",
\    "run_to_cursor" : "<F9>",
\    "step_over" : "<F2>",
\    "step_into" : "<F3>",
\    "step_out" : "<F4>",
\    "close" : "<F6>",
\    "detach" : "<F7>",
\    "set_breakpoint" : "<F10>",
\    "get_context" : "<F11>",
\    "eval_under_cursor" : "<F12>",
\    "eval_visual" : "<Leader>e",
\}
```

デバッグ開始時にスクリプトの最初の行で止まらずにブレークポイントまで進んでほしい場合は

```vim
let g:vdebug_options["break_on_open"] = 0
```

という設定をします。

### うまくいかない場合

```ini
xdebug.remote_log=/tmp/xdebug.log
```

を設定して、ログを確認してみるといいかもしれません。

## 他の言語

Vdebugのヘルプを見ると他にもPython, Perl, Ruby, Nodejs, TClでデバッグができるようです。どれも[Komodo Remote Debugging Package](http://code.activestate.com/komodo/remotedebugging/)を使います。Vdebugのヘルプと[KomodoのDebugging your programs](http://docs.activestate.com/komodo/11/manual/debugger.html)を参照すれば設定できそうです。

実際にPythonとPerlの設定について書きます。

## Python

`:help VdebugSetUpPython`と[Komodo 11 DocumentationのDebugging Python](http://docs.activestate.com/komodo/11/manual/debugpython.html)を見てPythonのセットアップをしてみます。

[Komodo Remote Debugging Package Downloads « ActiveState Code](http://code.activestate.com/komodo/remotedebugging/)からPython Remote Debugging Clientをダウンロードして解凍します。以下を実行してOKが出るかを確認します。

```bash
$ export PYTHONPATH="/path/to/Komodo-PythonRemoteDebugging-xxx/pythonlib${PYTHONPATH:+:${PYTHONPATH}}"
$ python -c "import dbgp.client; print 'ok'"
ok
```

Vdebugを起動して、

```bash
$ /path/to/Komodo-PythonRemoteDebugging-xxx/pydbgp -d localhost:9000 script.py
```

でscript.pyに対してのデバッグを開始できます。やってみると、デバッグが終了しても上記コマンドが終了せず、Ctrl-cでも終了できないのが難点でした。

非公式ですがpipでインストールできるようにした人がいました（[dougn/komodo-pydbgp: Repackaging of the Komodo python remote debugging package for building into usable wheels](https://github.com/dougn/komodo-pydbgp)）。

試してみるとpython 2.7.14は動きましたがpython 3.6.3ではエラーがでました。

## Perl

`:help VdebugSetUpPerl`を見てPerlのセットアップをしてみます。

ヘルプ曰く、特定のバージョンからうまくいかなくなったようなので、ヘルプに書いてあるバージョンを

http://downloads.activestate.com/Komodo/releases/archive/8.x/8.0.2/remotedebugging/

からダウンロードします（Komodo-PerlRemoteDebugging-8.0.2-78971-xxx）。

展開して、適当な場所（`~/perl5/lib/perl5/`など）に配置します。

シェルの設定ファイルに以下の設定をします。

```bash
export PERL5LIB="/path/to/Komodo-PerlRemoteDebugging-xxx/${PERL5LIB:+:${PERL5LIB}}"
export PERL5DB="BEGIN { require q(/path/to/Komodo-PerlRemoteDebugging-xxx/perl5db.pl)}"
export PERLDB_OPTS="RemotePort=localhost:9000"
export DBGP_IDEKEY="PHPSTORM"
```

あとはVdebugを起動して

```bash
$ perl -d script.pl
```

でPerlを実行すればデバッグが始まります。

### webページのデバッグ

[Debugging Perl | Komodo IDE/Edit Documentation](http://docs.komodoide.com/Manual/debugperl)にやり方が載ってます。

例えば、Apache HTTP ServerでCGIを使っている場合はApache HTTP Serverのconfファイルに

```apache
SetEnv PERL5LIB "/path/to/Komodo-PerlRemoteDebugging-xxx"
SetEnv PERLDB_OPTS "RemotePort=<hostname>:<port>"
SetEnv DBGP_IDEKEY "<ide_key>"
```

と書いて、デバッグしたいスクリプトのシェバンに`-d`を加えます。

```perl
#!/usr/local/bin/perl -d
```

そして、Vdebugを起動してwebページにアクセスするとデバッグが開始されます。

## Python 3への移行

Vdebugはpython2で動きますが、[v2.0](https://github.com/joonty/vdebug/projects/1)という名前がついたプロジェクトページに[Adds Python 3 support](https://github.com/joonty/vdebug/pull/271)というissueが立ってます。[そこのコメント](https://github.com/joonty/vdebug/pull/271#issuecomment-334805571) に

> @l0rb fortunately @lucc mentioned the v2-integration branch where there is continued development but still some issues to be resolved before thinking about py3

と書いてあるので、Python 3への移行はまだ先になりそうです。


[^1]: その他のブラウザの拡張については、[Remote Debuggingのドキュメント](https://xdebug.org/docs/remote#browser_session)を見て下さい。