ruby/debugとエディタ
ユニットテストで問題を再現させてデバッグするといった使い方が多いので、サンプルコードをtest-unitを使ったものを用意した。
# Gemfile
source 'https://rubygems.org'
gem 'debug'
gem 'test-unit'
# アプリケーション
class Foo
def foo
:foo
end
end
# ユニットテスト
require 'test/unit'
require_relative 'foo'
class FooTest < Test::Unit::TestCase
test 'foo' do
foo = Foo.new
assert_equal :foo, foo.foo
end
end
端末でデバッグする
まずは基本的な使い方ができることを確認しておく。
デバッガrdbg
を介してデバッグ対象を実行する。すると、デバッグ対象の起動直後に動作が停止してデバッグセッションが始まる。
rdbgコマンドを直接使用する
Visual Studio Codeを起動してデバッグする
次にrdbg
コマンドを使用するが、デバッグセッションをVisual Studio Codeで開始させてみる。
VSCode rdbg Ruby Debuggerが必要だが、事前の設定は必要ないようだ。
$ PATH=$PATH:'/Applications/Visual Studio Code.app/Contents/Resources/app/bin' \
> bundle exec rdbg --open=vscode foo_test.rb
DEBUGGER: Debugger can attach via UNIX domain socket (/var/folders/25/5gm2mn2w8xlfbc008k8yhkkh0000gp/T/ruby-debug-sock-502/ruby-debug-akira-64773)
Launching: code /private/tmp/rdbg-sample/
Loaded suite foo_test
Started
DEBUGGER: wait for debugger connection...
rdbgコマンドからVisual Studio Codeを起動した
Visual Studio Codeで「Shell Command: Install 'code' command in PATH」を実行しておくか、あらかじめPATHを通しておけば環境変数の指定は必要ない。
Visual Studio Codeで開いているファイルをデバッグする
Visual Studio Codeからrdbg
コマンドを使用してデバッグセッションを開始する。事前の設定が必要となるが、VSCode rdbg Ruby Debuggerの説明に従えばよい。
設定がまだないときは「実行とデバッグ」で表示される「launch.jsonファイルを作成します」をクリックすると雛型が作成される。設定内容の選択肢が表示されたときは「rdbg」とあるものを選ぶ。
「Debug current file with rdbg」を選択し、「⏵」をクリックすると実行するコマンドの入力を求められる。ただし、この場合にはデバッグ対象を起動直後に停止させることはない(※1)ようなので、事前にブレークポイントを設定しておく。(またはコード中にbinding.b
などでブレークポイントを記述しておく。)
ブレークポイントの設定
そして「⏵」をクリック。
コマンドラインの入力
デバッグセッション開始
リモートデバッグ
rdbg
はDAPというプロトコルをサポートしていて「開発ツール ⇄ DAPアダプタ・デバッガ ⇄ デバッグ対象」という構成でデバッグできる。このうちの開発ツールとしてはDAPをサポートしているソフトウェアを使用できる。上述のVisual Studio Codeを使用した方法も実はリモートデバッグである。
ここではrdbg
コマンドでコードを実行し、開始されたデバッガに外部から接続してみる。
デバッガを起動して接続待ちにする
シンプルな方法はrdbg
コマンドに--open
を指定して実行する。接続方法にはUNIXドメインソケットを使う方法と、TCPを使う方法がある。
$ bundle exec rdbg --open --sock-path /tmp/rdbg.sock --command -- ruby foo_test.rb
DEBUGGER: Debugger can attach via UNIX domain socket (/tmp/rdbg.sock)
Loaded suite foo_test
Started
DEBUGGER: wait for debugger connection...
$ bundle exec rdbg --open --port 12345 --command -- ruby foo_test.rb
DEBUGGER: Debugger can attach via TCP/IP (127.0.0.1:12345)
Loaded suite foo_test
Started
DEBUGGER: wait for debugger connection...
デバッグセッションがすでに開始しているときはrdbg
のコマンドのopenを使用すると接続待ちにできる。
コード中にbinding.b
などを埋め込んでいるときは環境変数RUBY_DEBUG_OPEN
を設定しておくとその場で接続待ちとなる。
rdbgコマンドで接続する
接続方法に合わせて接続先を引数で指定する。
$ bundle exec rdbg --attach /tmp/rdbg.sock
$ bundle exec rdbg --attach 12345
開始されるデバッグセッションはリモートデバッグではない場合と変わりはない。
DEBUGGER (client): Connected. PID:72865, $0:foo_test.rb
[1, 10] in foo_test.rb
1| # frozen_string_literal: true
2|
=> 3| require 'test/unit'
4| require_relative 'foo'
5|
6| class FooTest < Test::Unit::TestCase
7| test 'foo' do
8| foo = Foo.new
9| assert_equal :foo, foo.foo
10| end
=>#0 <main> at foo_test.rb:3
(rdbg:remote)
Visual Studio Codeで接続する
Visual Studio Codeから接続するときは、接続先を.vscode/launch.jsonで指定する。
上のUNIXドメインソケットの例に合わせると"name": "Attach with rdbg"
のほうに"debugPort": "/tmp/rdbg.sock"
を加える。
TCP接続する場合は"debugPort": "12345"
を加え、さらに"localfs": true
も加える。(localfs
の指定をしておかないと、Visual Studio Code上で設定したブレークポイントが無視される。)
そして「Attach with rdbg」を選択して「⏵」をクリックするとデバッグセッションが始まる。
なお、このときはデバッグ対象の起動直後に停止する(※2)ため事前にブレークポイントを設定していなくてもよい。
Neovimとの連携
nvim-dapはNeovim用のDAP実装で、デバッガとの標準入出力またはTCPでの接続をサポートしている。
標準入出力はruby/debugがサポートしていないと思うのでTCPで進める。
シンプルな設定
local dap = require('dap')
dap.adapters.ruby = function(callback, config)
callback {
type = 'server',
port = '12345',
}
end
dap.configurations.ruby = {
{
type = 'ruby',
name = 'Attach with rdbg',
request = 'attach',
localfs = true, -- TCP接続のときは必要
},
}
rdbg --open
でTCP接続待ちにしておき、Neovimから:DapContinue
を実行するとデバッグセッションが始まる。
nvim-dapからの接続
この例の設定ではデバッグ対象の起動直後に停止するかどうかは実行中のrdbg
のオプション指定に従って決まる。(※1)(※2)
デバッグ対象の起動直後に停止するかどうか
基本としてrdbg
のオプションに--nonstop
があれば起動直後には停止せず、なければ起動直後に停止する。
リモートデバッグのときは、そこからさらに使用されるDAPリクエストによって動作が変わる。DAPリクエストはVisual Studio Codeやnvim-dapの設定の中のrequest
によって決まる。この値がattach
のときは停止するがlaunch
のときは停止しない。
上述の(※1)で停止せず(※2)で停止するのはこの違いによる。
起動直後の停止については--stop-at-load
という別のオプションがあり、こちらはデバッグ機能が有効になった直後に動作を停止する。
Neovimで開いているファイルをデバッグする
Visual Studio Codeの雛型と同じように、開いているファイルを実行してデバッグできるようにするには以下のように設定する。
local dap = require('dap')
dap.adapters.ruby = function(callback, config)
if config.request == 'attach' then
callback {
type = 'server',
port = '12345',
}
else
callback {
type = 'server',
port = '${port}',
executable = {
command = 'bundle',
args = {
'exec', 'rdbg', '--stop-at-load',
'--open', '--host', '127.0.0.1', '--port', '${port}',
'--command', '--', 'bundle', 'exec', config.command, config.script,
},
},
}
end
end
dap.configurations.ruby = {
{
type = 'ruby',
name = 'Attach with rdbg',
request = 'attach',
localfs = true,
},
{
type = 'ruby',
name = 'Debug current file with rdbg',
request = 'launch',
localfs = true,
command = 'ruby',
script = '${file}',
},
}
この設定の「Debug current file with rdbg」は、Visual Studio Codeの例と同じくブレークポイントでのみ停止する。そのため事前にブレークポイントを設定しておく。(またはコード中にbinding.b
などでブレークポイントを記述しておく。)
設定の選択
B
とあるのが設定したブレークポイント。
デバッグセッション開始
nvim-dap-ruby
ここでは設定を手書きしたが、環境によってはnvim-dap-rubyを利用できる。