RubyKaigi 2021で発表された新しいdebug.gemを試してみた
Leaner Technologies の ころちゃん(@corocn) です。
RubyKaigi Takeout 2021 に業務で参加してきました。一緒に参加した同僚が型や RBS 周りの記事を書いてくれるようなので、デバッガ周りの話を振り返っておきます。デバッガは日々お世話になっていて最近関心の高いトピックなので選びました。
The Art of Execution Control for Ruby's Debugger
Agenda
- なぜ新しい debug.gem か?
- インストールと使い方
- Record and replay を試す
- VSCode 拡張と Remote Debugging を試す
- Rails 7 と Kernel#debugger の話
なぜ新しいdebug.gem?
パフォーマンスを改善したい
発表動画14:00あたりから引用
既存のデバッガの場合、ブレークポイント起因でかなりパフォーマンスが落ちてしまうことを知りました。
新しい debug.gem だとオーバーヘッドなしに実行できるようです。lib/debug.rb と比較すると300倍高速化されているように見えます。物凄いですね。RubyMine と比較しても 20 倍です。
自分の場合、Ruby 歴は実は浅いほうで Rails を書きはじめたときは既に IntelliJ 系 IDE が台頭していたのもあり[1]、現在は RubyMine の統合デバッグツール(debase & ruby-debug-ide)と binding.irb を使い分けるスタイルで開発しています。
以前は binding.pry を使っていましたが、Pryはもう古い、時代はIRB - k0kubun's blog を見て binding.irb を使うようになりました。
最近困っていた問題として、RubyMine でブレークポイントを貼ったときに非常に動作が重く、昨今は Docker を利用して開発しているのも相まって開発体験が悪くて非常にストレスが溜まっていましたが、原因がはっきりしましたね。
個人的には RubyMine のデバッグツールが debug.gem に対応してくると嬉しいです。
Ractorに対応したい
Ractor は Ruby3 で導入された並列処理の仕組みですが、Ractor 間でオブジェクトの共有ができないため、オブジェクトを直接触る既存のデバッガでは Ractor をデバッグできないよ〜というふわっとした理解をしています。
発表内では Ractor Programming は難しいと説明されていましたが、Ractor が難しいのではなく並列プログラミングが全般的に難しいという感想を持ちました。
なお、Ractor 対応は進行中とこのことです。リリースが楽しみですね。
弊社でもプロダクトの一部パフォーマンスが必要とされる箇所で Ractor の採用を検討しており、デバッグ含む gem が対応しはじめると利用の現実味がでてきそうです。
インストール
debug.gem は手元ですぐに試せます。
$ gem install debug
または Gemfile に以下を記述して bundle install でも動きます。
gem "debug"
使い方
デバッグの仕方は 2 種類あります。
- binding.break または binding.b
- rdbg コマンド
前者は binding.irb や binding.pry と同様、明示的に差し込むパターンです。プログラムを改変したくない場合は後者の rdbg コマンドを使用します。例えば Rails をデバッグしたい場合は次のように実行します。
$ rdbg --command -- rails server
詳しいコマンドは README にすべて載っているので参考にすると良さそうです。
Record and replay
debug.gem の面白い機能の 1 つを試してみます。デバッグ中に record コマンドを使用してポイントを保存しておくと、実行後に履歴を遡ることができる機能です。
まずは、デバッグコンソールで record on
コマンドを利用して機能を有効にします。
次に step
コマンドを使用して、実行を 7 行目の puts x
まで進めてみましょう。
ここで step back
コマンドを使うと、実行状態を遡ることができます。
4 行目まで戻って、変数 a の内容を出力してみましょう。
上書きされる前の a の値が出力されましたね。すごい。
遡ってる最中は"replay"の表示が出るようです。
VSCode + Remote Debugging
debug.rem には Remote Debugging 機能があり、なんと VSCode の拡張も用意されています。今回は Rails を VSCode でデバッグできるか試してみます。
環境は次のとおりです。
- Ruby 3.0.2
- Rails 6.1.4.1
Extensions から次の拡張をインストールします。"rdbg" で検索するとすぐ見つかります。
デバッガの設定のために .vscode/launch.json を作成します。左ペインの "create a launch json file" をクリックすると、"Ruby(rdbg)" がサジェストされるので、クリックで生成されます。
次のようなファイルが作成されました。
{
"version": "0.2.0",
"configurations": [
{
"type": "rdbg",
"name": "Debug current file with rdbg",
"request": "launch",
"script": "${file}",
"args": [],
"askParameters": true
},
{
"type": "rdbg",
"name": "Attach with rdbg",
"request": "attach"
}
]
}
Gemfile に debug.gem を追加して bundle install しておきます。
group :development do
gem 'debug'
end
rdbg 経由で Rails を起動します。
$ rdbg --open --command -- bundle exec rails server
DEBUGGER: can not load newer irb for coloring. Write 'gem "debug" in your Gemfile.
DEBUGGER: Debugger can attach via UNIX domain socket (/Users/corocn/.ruby-debug-sock/ruby-debug-corocn-42873)
DEBUGGER: wait for debugger connection...
デバッガの attach with rdbg を実行して、ソケットを選択します。
DEBUGGER: Debugger can attach via UNIX domain socket (/Users/corocn/.ruby-debug-sock/ruby-debug-corocn-42873)
DEBUGGER: wait for debugger connection...
DEBUGGER: Connected.
DEBUGGER: Debugger can attach via UNIX domain socket (/Users/corocn/.ruby-debug-sock/ruby-debug-corocn-42873)
DEBUGGER: wait for debugger connection...
なぜか DEBUGGER: Connected になってもデバッガを待ち続けてサーバーが起動しない様子。Rails で動かないというIssueを見つけましたが、現象が異なったので新しく Issue を立てました。bootsnap と Spring を外す必要があるようで、色々試行錯誤してみましたがまだ動いていません。
不完全燃焼なので rspec ならいけるんじゃない?と思って試したら無事動きました。
$ rdbg --open --command -- bundle exec rspec spec/requests/health_spec.rb
DEBUGGER: can not load newer irb for coloring. Write 'gem "debug" in your Gemfile.
DEBUGGER: Debugger can attach via UNIX domain socket (/Users/corocn/.ruby-debug-sock/ruby-debug-corocn-49313)
DEBUGGER: wait for debugger connection...
DEBUGGER: Connected.
Randomized with seed 41240
Healths
GET /health
DEBUGGER: BP - Line XXX/app/controllers/health_controller.rb:7 (call) is activated.
ちゃんとブレークできたぞ!
Rails 7の話
ruby/debug に DHH が降臨していましたね。
ruby-jp でも話題になってましたが Rails 7 では debug.gem が標準になるようです。
これに関連して、デバッグの際に binding.break と同じように使えるインターフェイスとして debugger を用意するという話もあるようです。
要約すると次のような話になります。
- Rails では byebug が 7 年間デフォルトだった。
- byebug は debugger で呼び出せるようになっている。
- binding.break より debugger のほうがより直感的だと思う。
- debug gem 側で対応しなくても、Rails 側でなんとでもできるので些細な点ではある。
今まで知らずに binding.irb を使っていましたが、byebug は Kernel#debugger
を生やしてるみたいですね。
module Kernel
def byebug
require_relative "core"
Byebug.attach unless Byebug.mode == :off
end
def remote_byebug(host = "localhost", port = nil)
Byebug.spawn(host, port)
Byebug.attach
end
alias debugger byebug
end
力強いな〜と思いました。
JavaScript も debugger ですし、debugger に統一されていたほうが嬉しいと感じました。
まとめ
今回の RubyKaigi はツール系のアップデートが多くて(周辺ツール好きとしても)興味深いセッションが多くて楽しかったです。高速なデバッガーはハッピーな開発体験をもたらすと思っているので Ractor 対応含めて今後の動向をチェックしていきたいです。
宣伝
Leaner Technologies ではツール好きなエンジニアを募集しています!
-
VSCode がなかったのと、PHPer で PHPStorm を愛用していました。 ↩︎
Discussion
参考にさせていただいております。
私もVSCode + Remote Debuggingを試しているところです。
リモートはdocker環境なのですが、この記事でのリモート環境はどのような環境でしょうか?
docker上のrailsにvscodeからrdbg attachするところで詰まっています・・・(汗)