debug.gemを使ったことがない方に向けて、いくつかの機能を紹介する

こんにちは、@yuskuboです。
本記事は [ビビッドガーデン Advent Calendar 2021](https://qiita.com/advent-calendar/2021/vivid-garden) の13日目の記事です。

# 概要
RubyKaigi 2021でRubyの新しいdebuggerであるdebug.gemが紹介されましたね！Ruby 3.1からは標準ライブラリになる予定とのことです。
ビビッドガーデンでは、これまでbyebug gemとpry gemを利用していましたが、今後のバージョンアップ対応のことも考慮してdebug.gemへ移行し、日々の業務で使っています。
本記事では、私がよく使っているdebug.gemの機能をいくつかピックアップして紹介していきます。「それ知らなかった！」や「そんなこともできたんだ！」ということを1つでもお届けできれば嬉しいです。

# 対象読者
- debug.gemをまだ使ったことないから基本的な使用方法を知りたいという方
- debug.gemは既に使っているけど`binding.break`以外は知らないから、他のコマンドも知りたいという方

# 前提事項
- 本記事では、Ruby on Railsのサンプルアプリケーションでdebug.gemを使いながらご紹介します。
- サンプルアプリケーションを用意する手順も書いていますが、ご自身のPCでRuby on Railsを利用できる状態であることを前提としています。Ruby on Railsの環境構築から行いたい場合は、以下のRailsガイドなどを参照して行ってみてください。

https://railsguides.jp/getting_started.html

- debug.gemを使うには、Ruby 2.6以降のバージョンである必要があります。
- RubyやRuby on Rails自体の解説は行っていません。

# 事前準備
Ruby on Railsのサンプルアプリケーションを用意していきます。既に手元にRuby on Railsを動かせる環境がある方は、スキップして問題ありません。
### Railsプロジェクトを新規作成

```bash:bash
$ rails new debug_sample_app
```
### Gemfileにdebug.gemを追加

```:Gemfile
group :development, :test do
  gem 'debug'
end
```
### 各コマンドの実行
Gemのインストール、マイグレーションの実行、コンパイルを行い、起動します。
debug.gemを使うことが目的なので、scaffoldを使ってUserというリソース一式を作成しています。

```bash:bash
$ bundle install

$ bin/rails generate scaffold User name:string email:string

$ bin/rails db:migrate

$ bin/rails webpacker:compile

$ bin/rails s
```

### 画面へのアクセス
ユーザー一覧画面のhttp://127.0.0.1:3000/users にアクセスし、以下の画面が表示されればサンプルアプリケーションの準備は完了です。問題なく起動できたら、「New User」から1名ユーザーを作成しておきましょう。
![new_user.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/02cf68d6-c0f9-4af1-3158-f3eacbb940bc.png)

### デバッグ用のメソッド追加
メソッド呼び出しがあった方がデバッグを試しやすいので、userモデルに`has_email?`メソッドを追加し、usersコントローラーから呼び出しておきます。あくまでデバッグを試すためのものなので、処理自体は何でも大丈夫です。ここではuserのメールアドレスが存在するかどうかをチェックしています。
また、showアクションの中で処理を止めて色々試したいので、showアクションをbefore_actionの対象から外しておきます。

```.rb:app/controllers/users_controller.rb
class UsersController < ApplicationController
  # onlyに指定した配列からshowを削除
  before_action :set_user, only: %i[ edit update destroy ]

  # GET /users or /users.json
  def index
    @users = User.all
  end

  # GET /users/1 or /users/1.json
  def show
    # 以下の2行を追加
    @user = User.find(params[:id])
    @has_email = @user.has_email?
  end
# ~~ 以下省略 ~~
```
```.rb:app/models/user.rb
class User < ApplicationRecord
  # 以下のメソッドを追加
  def has_email?
    self.email.present?
  end
end
```

# デバッグコマンドを使ってみる
それでは早速デバッグコマンドを使ってみましょう！

### binding.break / binding.b / debugger
プログラムを止めたい箇所に`binding.break`を記述すると、ブレークポイントとして処理を止めることができます。`binding.break`のエイリアスとして、`binding.b`と`debugger`も用意されているので、好きなものを使いましょう。
試しに、users_controllerのshowアクションに`binding.break`を追記して処理を止めてみます。

```.rb:app/models/user.rb
def show
  @user = User.find(params[:id])
  # ブレークポイントを追加
  binding.break
  @has_email = @user.has_email?
end
```
http://127.0.0.1:3000/users/1 へアクセスすると、以下のようにデバッグコンソールが表示されます。12行目で処理が止まっていることがわかります。
![binding_break.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/30038814-aba2-a027-42e9-6512b9c093eb.png)

例えば、ここで`@user`と入力すると、`@user`変数の中身を見ることができます。
![binding_break_user.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/17f94219-e3e8-fc33-6ad6-0c096d42bc1e.png)

また、ここで`1 + 1`と入力すると、rubyの式として評価され`2`という結果を得ることができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/d80f1ec2-739e-beb7-b224-5c7ca6432141.png" width="200" alt="デバッグコンソールで1+1を実行">
何も入力せずにEnterを押した場合は、最後に実行したコマンドや式（ここでは1 + 1）が繰り返し実行されます。何度もステップオーバーやステップインする時に使うと同じコマンドを繰り返し入力する必要がないので楽ですね！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/1c4a0b6b-7437-5597-2f3e-4ac6473dd1ba.png" width="200" alt="デバッグコンソールでEnterを実行">
ブレークポイントでの確認が終わったら`continue`（この後ご紹介するコマンド）を実行してください。`continue`を実行すると、最後まで処理が実行されて、ブラウザにユーザー詳細画面が表示されます。

### binding.break do: 'debug_command'
binding.breakは、doキーワードにデバッグコマンドを指定して使うこともできます。例えば、以下のように指定すれば、ActiveRecord::RecordNotFoundが発生した時のみブレークポイントで止めることができます。

```.rb:app/controllers/users_controller.rb
def show
  @user = User.find(params[:id])
  binding.break do: 'catch ActiveRecord::RecordNotFound'
  @has_email = @user.has_email?
end
```

上記のように指定するとユーザーが存在するhttp://127.0.0.1:3000/users/1 へアクセスした時は処理は止まりません。
しかし、ユーザーが存在しないhttp://127.0.0.1:3000/users/2 へアクセスすると、ActiveRecord::RecordNotFoundが発生して処理が止まります。
![binding_break_do_option.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/ea41fdd3-b12e-2d67-851e-ad29b73809c9.png)
このように特定の条件で処理を止めてデバッグしたい時に使うと便利です！

### next (n) / step (s) / continue (c)
以下の3つのコマンドはデバッグコンソール上での基本操作となるコマンドです。それぞれ意味は以下の通りです。

- `next`：ステップオーバー（同じメソッド内で次の行へ進む）
- `step`：ステップイン（別のメソッド呼び出しである場合、呼び出したメソッド内へ進む。メソッド呼び出しでない場合は、同じメソッド内で次の行へ進む。）
- `continue`：後続の処理を実行

なお、それぞれ省略形も用意されていて、頭文字だけでコマンドを実行できます。例えば、`n`だけ入力すると右側に「# next command」と表示され、`next`であることがわかります。細かい部分がとても親切ですね！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/d02a1ee7-5ef2-0dbf-4e08-97f3f54a4c95.png" width="250" alt="デバッグコンソールで頭文字を入力">

### info (i)
現在実行中のフレームのローカル変数、インスタンス変数、定数を表示することができます。ざっと変数や定数を確認したい時に便利です。省略形の`i`でも実行できます。
![binding_break_info.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/ed79e320-8884-77ab-e904-49d342fa2387.png)

### outline (o) / ls
現在のスコープで利用可能なメソッド、定数、ローカル変数、インスタンス変数を表示できます。「ここであのメソッド使えるっけ？」となった時に役立ちます。省略形の`o`でも実行できますし、エイリアスとして`ls`も用意されています。
![binding_break_outline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/f32278de-38db-7263-f7a9-354a0e778b02.png)

### frame (f) / list (l)
デバッグ中に変数の中身を見たり、式を実行したりしているうちに、「あれ？今どこで何してたんだっけ？」となることがあります。`frame`を実行すると、現在実行中のフレーム情報を表示することができます。これでusers_controllerの12行目でデバッグしていたことを確認できます。省略形の`f`でも実行できます。
![binding_break_frame.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/8c4e4587-4bbf-29de-e1f0-e6ec462a7479.png)
さらに、「ブレークポイントを設定した周辺のソースコードはどんなものだったっけ？」を確認したい時は、`list`を実行します。省略形の`l`でも実行できます。
`list`を実行すると、現在実行中のフレーム情報周辺のソースコードを表示することができます。複数回実行すると、実行する度に後続の行を表示します。`list -`のように`-`オプションを付けると1つ前のフレーム情報を表示することができます。また、`list 10-15`のように表示する行数を指定することもできます。
![binding_break_list.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/cfb1a29c-8b5e-4b52-5bb2-4ab86dab537a.png)

### record
これはすごく便利だなと思ったのが、`record`です。`record`を有効にすると実行した処理を記録して、後から処理を遡って確認することができます。例えば、「5行前ではこの変数の値はどうなってるんだっけ？」のように前の状態を確認したいときに重宝します。
処理の記録を開始する時は、以下のようにブレークポイントで止めた後、`record on`で記録を開始します。その後、任意の場所まで`next`で処理を進めます。
![binding_break_record_on.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/8e89409d-ec0b-e08f-2996-77987ba629f7.png)
処理を進めた後に`step back`すると、実行した処理を遡ることができます。`step back`している間は、「replay」と表示されます。複数回`step back`していると、userモデルやusersコントローラー内の見覚えのある処理まで遡ってきます。なお、戻り過ぎた時は、`step`で処理を進めることもできます。
![binding_break_step_back.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/fe83e8a6-ab1c-b7b9-9ac9-cc9494b092da.png)
確認が終わったら`step reset`で、replayを終了し、ブレークポイントまで戻れます。
また、処理の記録を終了する時は、`record off`で記録が終了となります。
![binding_break_record_off.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/dabf5eb1-13ec-2da6-1e27-48c9db6cddfe.png)

### trace
`trace`コマンドを使うと、トレース情報を表示することができます。`trace line`を実行するとLineTracerが有効になり、トレース情報が表示されるようになります。
![binding_break_trace_line.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/13c5cff5-2d97-c24f-fa61-ee0264fe3a3c.png)
この状態で処理を進めてみると、以下のようにトレース情報が表示されます。赤枠の部分を見ると、usersコントローラーの13行目の次にuserモデルの3行目（`has_email?`メソッドの処理）が実行されていることがわかります。
![binding_break_trace.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/f4ba0f96-affa-c782-5a6b-0d49f7aba2db.png)
トレース情報の表示が不要になったら、`trace off line`で表示を終了することができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/f43bf4a6-7cee-a08c-ea2e-8fdfa02dc534.png" width="300" alt="デバッグコンソールでtraceを終了">

### backtrace (bt)
`backtrace`コマンドを使うと、バックトレース情報を表示することができます。省略形の`bt`でも実行できます。ここではモデル側に用意したメソッド内にブレークポイントを設定して、バックトレース情報を確認してみます。

```.rb:app/models/user.rb
class User < ApplicationRecord
  def has_email?
    # ブレークポイントを設定する
    binding.break
    self.email.present?
  end
end
```
`backtrace`を実行すると多くの情報が出てくるので、今回は`backtrace 5`として、5行だけ表示してみます。`has_email?`メソッドがusersコントローラーから呼び出されたことがわかります。
![binding_break_record_backtrace.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/ba43e93e-2f98-5c37-b694-2fd6e5d89ddd.png)

### config
`config`を使うとdebug.gemの設定内容を確認することができます。例えば、`binding.break`で処理を止めた時に表示されるソースの行数（show_src_lines）はデフォルトでは10行であることがわかります。
![binding_break_config.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/9b4adc00-0723-6a5e-d517-b758c517e9d0.png)
さらに`config set`を使うと設定内容を変更することができます。試しに表示する行数を変更してみましょう。`config set show_src_lines 20`と実行すると、表示を20行に変更することができます。
![binding_break_config_set.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/7cc2e942-4ef6-e764-97b8-987c1a4140c9.png)
表示行数の設定変更後に処理を止めてみると、20行表示されるようになっています。
![binding_break_config_set_lines.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/2d2122de-a281-0f62-b31a-e9bc54389316.png)

なお、デバッグコンソールで変更した設定はRailsアプリケーションを再起動するとデフォルト値へ戻ってしまいます。常に反映させたい場合は、`~/.rdbgrc`という初期化スクリプトに記述する必要があります。初期化スクリプトはデバッグセッション開始時に読み込まれて記述した設定が反映されます。初期化スクリプトに以下のように記述しておくと、`binding.break`で処理を止めた時に表示されるソースの行数は常に20行となります。

```.rdbgrc:~/.rdbgrc
config set show_src_lines 20
```

### help (h)
「あのコマンドどうやって使うんだっけ？」や「他にどんなコマンドがあるだろう？」というのが知りたくなったら、`help`コマンドを使いましょう。全てのコマンドの使い方を表示することができます。省略形の`h`でも実行できます。また、`help record`のようにコマンドを指定すれば、指定したコマンドの使い方だけ表示することもできます。
![binding_break_help.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/bd457197-7306-815f-fe07-36bf66500f56.png)

### その他補足事項
各コマンドの紹介時に`i`、`n`、`s`のような省略形もご紹介しました。これらは1文字のデバッグコマンドであるため、ローカル変数として`i`、`n`、`s`が使われていたとしてもデバッグコマンドとして実行されます。ローカル変数の中身を確認したい場合は、`p i`とすることで表示することができます。
以下のようにローカル変数`i`を用意して、デバッグコンソールから確認してみます。

```.rb:app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    # ローカル変数iを用意
    i = 'test'
    binding.break
    @has_email = @user.has_email?
  end
```
`i`だと`info`コマンドが実行されていますが、`p i`とするとローカル変数`i`の中身が表示されていることがわかります。
![binding_break_p.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174425/08442ef0-9d5f-8656-87e8-b4dfa93fed02.png)

# まとめ
いかがだったでしょうか？「それ知らなかった」、「明日からそのコマンド使ってみよう」といったものが1つでもあったなら幸いです。debug.gemには便利で魅力的なコマンドがたくさん用意されているので、私もどんどん使い込んで行きたいと思います。

なお、本記事は、以下のdebug.gemのREADMEを元にいくつかの内容をピックアップしてご紹介したものです。最新の仕様や機能・コマンドを網羅的に知りたい場合は、debug.gemのREADMEをご確認ください。

https://github.com/ruby/debug

また、RubyKaigi 2021のスライド資料や動画では、使用方法に加えて「開発された背景」や「どうやって動いているのか」なども解説されているので、ぜひこちらも合わせてご確認ください。

https://rubykaigi.org/2021-takeout/presentations/ko1.html

# 最後に
ビビッドガーデンが運営する食べチョクのバックエンドは、Ruby on Railsで開発されています。ビビッドガーデンではエンジニアを絶賛募集中ですので、気になった方はこちらもぜひ覗いてみてください！！

https://www.tabechoku.com/

https://herp.careers/v1/vividgarden/KpLywERTT_Wh