Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyプログラムのデバッグと修正方法を徹底解説|スタックトレースの読み方からPry・Byebugまで

書いたプログラムが、初回実行で思い通りに動くことってどれくらいありますか?

多くの場合、プログラムは期待どおりには動いてくれないもの。そんなときに頼りになるのがRubyのデバッグという技術です。原因を突き止めるための頼れる相棒といえるでしょう。

次のようなエラーメッセージを見たことはありませんか?

undefined method 'some_method' for nil:NilClass

これは、nil値がコードの中に紛れ込んでしまったことを意味します。

本記事で紹介するテクニックを身につければ、この問題や似たようなトラブルにも自信を持って対処できるようになりますよ!

エラーとスタックトレースの読み方

Rubyインタプリタからエラーが出たり、プログラムが期待した動作をしなかったりしたときは、いよいよデバッグの出番です。

プログラムがクラッシュするような場合は、まずエラーメッセージに注目しましょう。何が起きているのかの手がかりが、たいていそこに含まれています。

具体例を見てみましょう:

def method1
  method2
end

def method2
  puts invalid_variable
end

method1

このコードを実行すると、次のようなエラーが表示されます。

/tmp/stack.rb:6:in 'method2': undefined local variable or method 'invalid_variable' for main:Object (NameError)
    from /tmp/stack.rb:2:in 'method1'
    from /tmp/stack.rb:9:in '
'

これがいわゆるスタックトレース(バックトレース)です。

一緒に分析してみましょう!

まずは一番上の行から見ていきます。

ここは実際にエラーが発生した場所ですが、必ずしもエラーの原因がここで生まれたとは限りません。とはいえ、調査を始めるには最適な出発点です。

ポイントはこちら:

表示内容 意味
/tmp/stack.rb:6 ファイル名と行番号
in `method2 メソッド名
undefined local variable or method ‘invalid_variable エラーメッセージ
main:Object クラス名
(NameError) 例外名

このように分解してみると、エラーメッセージは決して怖いものではないことが分かりますね。

ちなみに、Rubyの標準例外の一覧は公式ドキュメントで確認できます。

さて

スタックトレースの2行目以降は、「どうやってここに到達したか」を示しています。

基本的にはメソッドの呼び出しチェーンなので、下へ辿っていけば最終的にアプリの起点となるメソッドにたどり着きます。

スタックトレースに向き合うときの一般的な手順は以下のとおりです。

  1. スタックトレースの先頭行を読む
  2. そのファイルが自分のプロジェクトの一部なら、指定された行番号を開いて確認する。外部のファイルなら、見覚えのあるファイルへの参照が現れるまで下へ辿る
  3. 明らかに怪しい箇所がないか探して修正する(エラーメッセージに書かれている内容に注目)
  4. それでも解決しなければ、変数の値など、より詳しい情報を集める必要がある

Rubyの基本的なデバッグ手法

おそらく誰もが知っている最もシンプルな(=決して悪いわけではない)デバッグ手法が、怪しい変数の値を出力して確認する方法です。

Rubyではputspを使えばそれが可能です。

pputs variable.inspectと書くのと同じ意味で、オブジェクトの中身を詳しく見たいときに便利です。

例を見てみましょう:

Book = Struct.new(:title)

def find_book(title)
  books = []
  books << Book.new('Eloquent Ruby')

  books.find { |b| b.title == title }
end

book = find_book('Eloquent Ruby')
p book # 本オブジェクトが出力される

book = find_book('POODR')
p book # nilが出力される

book.name # さて、この後どうなるでしょう!

Pryでもっと深く調べる

確認したい変数が大量にある場合、あちこちにputsを仕込むのはあまり現実的ではありません。

そんなときにおすすめなのがpryです。

pryを使うと、コードの任意の場所(ブレークポイントとも呼ばれます)でプログラムを停止させられます。停止するとirbに似た環境に入り、プロジェクトのコンテキスト上でRubyのコードを実行したり、pryの豊富な便利コマンドを利用したりできるのです。

pryの使い方はとても簡単

ブレークポイントを設置したい場所にbinding.pryと書くだけです。

また、プロジェクトでpryを読み込む必要があります(require 'pry')。

一時的に試すだけであれば、スクリプトを次のように実行することもできます。

ruby -rpry app.rb

ただしRailsアプリではこれは不便なので、Gemfileにpryを追加しておくのがおすすめです。

私のお気に入りの工夫は、エディタのスニペット(マクロ)にrequire文とブレークポイントを同じ行にまとめて登録しておくこと。削除するときに両方同時に消せるので、後片付けが楽になります。

pryセッションに入ると、画面はこのようになります。

Rubyプログラムのデバッグと修正方法を徹底解説|スタックトレースの読み方からPry・Byebugまで

pryセッションを完全に終了したい場合はexit!と入力します。通常のexitだと、プログラムは次のブレークポイントまで実行を続けるので注意してください。

pryの威力はこれだけではありません。たとえばlsコマンドを使えば、オブジェクトがアクセスできるメソッドやインスタンス変数を一覧表示できます。

Rubyプログラムのデバッグと修正方法を徹底解説|スタックトレースの読み方からPry・Byebugまで

すべての便利コマンドを知りたいときは、忘れずにhelpコマンドを実行してみましょう!

もうひとつのデバッガー:Byebug

Byebugは、pryの代わりとしても、gdbのようなステップ実行型デバッガーとしても使えるツールです。

前者の用途なら、コードを止めたい場所にbinding.pryの代わりにbyebugと書くだけです。ただし、pryと比べるとシンタックスハイライトがない点が難点です。

では、byebugの中でブレークポイントを設定してコードをデバッグする方法を見ていきましょう!

通常はhelpコマンドを呼ぶところですが、byebugの場合は情報が少し少なめです。

Rubyプログラムのデバッグと修正方法を徹底解説|スタックトレースの読み方からPry・Byebugまで

そのため、ドキュメントを参照するのが確実です。

breakコマンドに行番号を組み合わせれば、任意の場所にブレークポイントを設定できます。

設定済みのブレークポイント一覧を見るにはinfo breakpointを使います。

ブレークポイントを設置したら、次のコマンドでプログラムの実行を進められます。

  • step(1命令ずつ進む。メソッド呼び出しの中にも入り込む)
  • next(1命令ずつ進む。メソッドの中までは入らない)
  • continue(終端または次のブレークポイントまで実行する)

ちなみに、コマンドなしでEnterキーを押すと直前のコマンドが繰り返されるので、コードを一行ずつ追いかけていくときに非常に便利です。

どうしても解決できないときは

長時間取り組んでも答えが見えてこないときは、思い切って休憩を取りましょう。頭をリフレッシュして戻ってくると、答えがずっと目の前にあったことに気づくことがよくあります。また、問題を他の人に説明してみるのも効果的です(アヒル隊長に説明するのもアリです)。

そもそも問題がどこにあるのか分からないこともありますよね。そんなときでも選択肢はたくさんあります。

たとえば、コードブロックをコメントアウトして、問題の切り分けを試す方法があります。

コメントアウトによって問題が消えたら、今度はコメントを解除する範囲を絞りながら戻していきます。

非常に原始的なやり方ですが、まさにこれが必要なケースもあるのです。

ここまで試しても何もうまくいかない場合

いよいよ「大砲」を持ち出すときです。

役立つことが多いシステムツールをいくつか紹介します。

まずWireshark。ネットワーク通信の内容を詳細に調査できるツールです。

SSL暗号化された通信が相手の場合は、mitmproxyのような中間者(MITM)プロキシが助けになるかもしれません。

また、ターミナルからcurlでHTTPリクエストを送信すれば、サーバーの不正なレスポンスのデバッグに役立ちます。

もうひとつ知っておくと便利なのがstrace(Linux専用)です。

straceを使うと、アプリが発行しているすべてのシステムコールを確認できます。-eオプションで特定のシステムコールに絞り込むことも可能です。なお、straceのよりモダンな代替手段としてはsysdigがあります。

注意! straceは対象システムのパフォーマンスを著しく低下させるため、本番環境での使用は避けましょう。

最後に、問題の原因が外部のgemにあると思われる場合は、gemのソースコードを直接調べてみるのが手っ取り早い解決策です。

gem open <gem名>コマンドを使えば、設定済みのエディタでソースコードを開けます。

まとめ

デバッグが最も楽しい作業だとは言いませんが、作業を楽にしてくれるツールやテクニックはたくさんあります。ぜひそれらを味方につけてください。

この記事が役に立ったら、SNSなどでシェアして、より多くの人の学びにつながると嬉しいです!🙂

ありがとうございました。


  1. 「ERR_CONTENT_DECODING_FAILED」エラーの原因と9つの解決方法を徹底解説

    「ERR_CONTENT_DECODING_FAILED」というエラーは、ほぼすべてのブラウザで発生が確認されています。特定のウェブサイトを読み込む際に表示されることもあれば、新しいサーバーへ移行した後に発生し始めることもあります。ページを何度更新してもエラーが消えず、多くのユーザーを悩ませています。この記事では、このエラーが発生する主な原因を解説し、完全に解消するための具体的な対処法を順番にご紹介します。 「ERR_CONTENT_DECODING_FAILED」エラーの原因 残念ながら、このエラーの原因を単一の要因に特定することはできません。ただし、以下のような原因がよく知られています。

  2. Windows PCで発生するエラー1722の原因と修正方法を徹底解説

    エラーコード1722は、Windows 2003、2000、XPなどの環境で表示されるエラーです。PC上で動作しているアプリケーションがNetUserGetLocalGroup関数にアクセスしようとした際に発生します。この関数は、Windows NTベースのオペレーティングシステムに搭載されている「グループポリシー」の一部であり、ユーザーアカウントやコンピュータアカウントの作業環境を管理する役割を担っています。 PCがこの関数を使用する際、別のドメインのActive Directoryを照会するケースがあります。その場合、ユーザー名は「ドメイン名/ユーザー名」の形式で指定されます。こうした処