BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル Rubyのデバッガを調査

Rubyのデバッガを調査

Rubyには、Rubyコミュニティの内外で広く知られている誤解が一つある。Rubyにはデバッガがないという誤解だ。この誤解は、Rubyにとって問題だと唱える人もいる。このデバッギングツールが欠けていることは、賢明で素晴らしいことだと解釈する者もいる(参考記事)

しかし、Rubyにデバッガが無いということは誤解なのだ。実際のところ、Rubyにはデバッガ用のツールがある。それも実に沢山のツールがある。様々なRubyの実装で利用出来るツール、GUIのデバッグツール、デバッグの実装、そしてデバッグのサポートを見ていきたいと思う。

デバッガって何?

第一に、実際デバッガに含まれるものをハッキリさせよう。

デバッガGUIとインタフェース

もちろん、インタラクティブなデバッガのもっとも大切な部分、少なくともユーザにとって大切な部分は、ユーザインタフェースです。Ruby標準ライブラリやRubiniusデバッガに付属しているデバッガのようなRubyデバッガのコマンドラインインタフェース版が、利用可能だ。コマンドライン版のデバッガを使えば、コードをデバッグ出来るが、ブレークポイントを設定したり、デバッグ作業そのものは非常に退屈だ。

IDEは、Rubyコミュニティで時々中傷されるが、確かにIDEを使うとデバッグ作業は簡単になります。結局IDEというのは、統合開発環境なのだ。統合開発環境の、統合という部分が、デバッグに関して重要なところで、IDEはコード編集とデバッグツールを統合している。コードの行番号を取得し、コマンドラインデバッガへ移り、取得した行番号のところにブレークポイントを設定することと比較して、統合開発環境の場合、ソースエディタで、直接ブレークポイントを管理することが出来る。行ベースのステップ実行のような機能は、的確な行で現在のスタックフレームのファイルを開くIDEで、より役立つことだろう。

組み込み済みのスクリプトをサポートしたIDEを利用すると、デバッグ作業をスクリプト化出来る。例えば、EclipseにはJRubyで書かれたスクリプトを実行可能にするエクステンションとして、EclipseMonkeyエクステンションがある(参考記事)。これらのスクリプトは、Eclipse IDEと同じJVM内で実行されているので、デバッガインスタンスにアクセスし、コントロールすることが可能だ。

デバッガプロトコルやバックエンドへの接続

デバッガ・バックエンドがあるIDEのようなデバッガユーザインターフェースへ接続する簡単な方法は、コマンドラインインターフェースを使い、標準入力、標準出力、標準エラー出力ストリームを通してデバッガをコントロールすることだ。これを使用して、エディタやIDEのデバッガサポートは、デバッガをコントロールすることが出来る。さらに、ユーザがブレイクポイントを管理するのを容易にする。


デバッガユーザインタフェースへ接続する他の方法は、ワイアプロトコルだ。ワイアプロトコルを使って、プロセス間通信(IPC)の幾つかのモード経由、または最近ではTCP/IP経由でデバッガへ接続することが出来る。またネットワークベースのプロトコルを使うと、ローカルユーザインタフェースを使用してリモートマシーンのデバッグを行えるように、GUIとデバッガが異なる端末に存在していてもデバッグすることが可能になる。

簡単で、テキストベース又は少なくともドキュメント化された、デバッグプロトコルを使って、いろいろな言語でデバッグプロセスをスクリプト化出来る。実際、TelnetでRubyのデバッガへ接続するのは簡単だ。debug-commons(リンク)及びDBGp(リンク)プロトコルは、単一行の文字列やXMLのレスポンスを含んでいる。

VMサポートまたはデバッギングバックエンド

ブレークポイントのような機能が動作するように、少なくともランゲージランタイムは、プログラムの実行をモニターしコントロールする最小のサポートを提供しなくてはいけない。これは、Rubyのトレース機能と同じくらい最小限でありうる。一連のRubyコードが実行される前に、Rubyは、set_trace_funcに呼び出しをセットされたコールバック機能を呼び出す。この関数に渡される引数は、Rubyが実行しようとしている一連のコードの環境に関する情報を含む。例えば、それは行番号であったり、ファイル名、クラス等である。これは、ブレークポイントの機能を実装するのに十分だ。ファイル名と行番号があれば、もしある行にブレークポイントが登録されていたとしたら、登録されたブレークポイントをチェックすることが可能だ。


ブレークポイントが、ヒットされたとしたら、実行は単に停止され、コールバックから戻らないことによって、一旦コールバックが戻されたならば、Rubyのランタイムを継続させることのみ可能だ。このことを元にして、ステッピングや他の機能を実装出来る。

トレース機能を使っているデバッガを構築することが可能な間、以前実行されたトーレスコールバックのオーバーヘッドと共に、どれかの行が実行されるので非常に遅い。ブレークポイントの行が実際に実行されたときに、この理想的な解決策は、ブレークポイントのコストを招くだけである。ランタイムは、ロードしたコード修正することによって、これを実装することが出来る。ブレークポイントを設定した行では、それは、抽象構文木または命令コードだ。ランゲージランタイムの中には、ビルトインデバッギングサポートを特徴とするものもあり、それらは実行メカニズムを統合している。Javaと.NETのバイナリは両方とも、デバッギング情報と一緒に出荷可能だ。Javaの世界では、たとえば、この機能にアクセスするためのJVM Tool Interface(JVM TI)(リンク)とJVMに接続するためのJava Debug Wire Protocol(JDWP)(リンク)が付いてくる。

もう一つのアプローチは、Rubiniusデバッガによって使用される。それはアクセス出来て修正可能なRubyコードの命令コードを使用している。(Rubiniusは、Rubyのソースコードを実行前にコンパイルする)ブレークポイントは、現在のスレッドを停止して、デバッガスタックのより高い層に知らせる特別な命令コードを持った通常の命令コードによって設定される。多くの基盤と管理データ構造が言語へアクセス出来るようにすることによって、言語そのものをデバッギングメカニズムを構築するのに用いることが出来る。

Ruby実装毎のデバッガとIDEのサポート

基本的なセットアップで、どのようなデバッガが利用可能か見てみよう。まず最初に最も使われていて、そしてサポートされているMatz Ruby(MRI)を見てみる。その後は、JRuby、RubiniusそしてIronRubyの順に、これらのRuby実装に対するツールのサポート、ツールサポートやパフォーマンスの点などで、どのような点がMRIと異なるのかを見ていこう。

Ruby/MRI

デバッギングバックエンド

Ruby 1.8.xまたはMRIは、C言語で書かれた公式のRubyインタープリタだ。このバージョンのRubyに対して、デバッグオプションのホストが利用可能だ。標準ライブラリが付属して出荷されたRubyで、トレースデバッガは利用出来る。しかし、より早い実装のデバッガが利用出来る。一つは、ネイティブ拡張を利用して実装されているruby-debug(リンク)だ。

もうひとつは、SapphireSteelのRuby in Steel IDEに(リンク)同梱されている、Cylon debuggerだ(リンク)。Cylon debuggerも、メソッドインヴォケーションのようなイベントに関して通知を受けるためにRubyのフックの利用を備えた機能を実装するのにネイティブコードを使って作られている。SapphireSteelのベンチマークによれば(リンク)、Cylon debuggerは、Rubyで書かれたデバッガやruby-debugよりも著しく早いということだ。

GUI

Ruby IDEの多くが、デバッグサポートを提供している。EclipseをベースとしたRDT(現在はAptanaとRadRailsの一部となっている)は(参考記事)、長い間デバッグサポートを行ってきた。最初はRubyベースのトレースデバッガへ接続し、後にruby-debugのサポートするようになった。RDTのデバッグプロトコルは、debug-commonsプロジェクトへ移された(リンク)。debug-commonsプロジェクトは、Netbeans Rubyでデバッグを提供するのに使われている。Ruby IDE界の別の古顔に、ActiveStateのKomodoがある(リンク)。Komodoは、DBGp(リンク)プロトコルへ接続するためにDBGpプロトコル上に作られている。Eclipse DLTK Rubyは(参考記事)、CodeGear 3dRailの(参考記事)ベースとなっており、EclipseのデバッガGUIを活用しているもう一つのIDEだ。DLTKもまた、DBGpを使ってバックエンドへ接続する。SapphireSteelのRuby in Steelには、デバッガGUIが含まれている。Cylon debuggerを利用することによって、デバッグが早くなるのだ。

IDEの特定の機能セットは、変化するかもしれないが、それらは少なくとも、ブレークポイントの設定、コードのステップ実行、変数の内容を表示する機能を提供している。注意:IntelliJが、自分たちのIDEでRubyの編集機能をサポートすると同時に、IntelliJ Ruby roadmandは、将来のプロジェクトとしてデバッグ機能のサポートをリストに並べている(リンク)

JRuby

デバッギングバックエンド

通常のトレースベースのRubyデバッガは(リンク)、JRubyでも動作する。さらに、動作が速いデバッガとして、jruby-debugも利用可能だ(debug-commonsプロジェクトにホストされている)jruby-debugは実装言語としてRubyの代わりにJavaを使用しており、実行行毎のオーバヘッドを軽減している。


新しいJRubyのデバッグバックエンドが、SapphireSteelからリリースされる(参考記事)。MRI向けのカスタム化された動作が速いCylonデバッガについて同社のことは既に触れた。jruby-debugとは違って、SapphireSteelの解決策は、Javaとネイティブコード(JNI)の両方を、デバッガバックエンドで使うというものだ。

GUI

JRubyでも同様に動作するset_trace_func debugging機能をサポートしているNetbeansとApatanaでも、jruby-debugのサポートも含めて出荷されている。クロスランゲージのデバッグサポートは、単にRubyの実行環境としてだけでなく、Rubyと一緒にJavaのクラスをスクリプト化するのにJRubyを使う全ての人にとって、メリットがあるのは明らかだ。この場合、クロスランゲージのデバッギングは有効だ。RubyとJavaのスタックと変数を表示すること、RubyのコードがJavaのコードへコールされるとき、RubyとJavaのスタックと変数を表示する際に有効だ。SapphireSteel IDEは、バックエンドとコミュニケーションプロトコルの独自の実装を使用しており、ruby-debugやjruby-debugに基づいていない。即ち、そのことは、Steel IDEのRubyに束縛されているということを意味する。

Rubinius

Debugging backends

Rubiniusは(参考記事リンク)、確実に大きく進歩した。特にここ数ヶ月間の間に、デバッギングのサポートが全くないところから、デバッギングパフォーマンスの点において、他のRuby実装と比較して最前部へ躍り出た。Full Speed Ruby Debuggerは(参考記事・英語)、上記又はリンク記事で説明されているように、デバッギングが有効の状態で、他のソリューションのパフォーマンスに関してオーバーヘッド無く、Rubyプログラムを実行することが出来る。

デバッギングは、Rubiniusのデザイン決定に役立ち、それは、実行時の通常のRubyに対して沢山のVMインフラとメタデータを利用可能にする。スタックトレース同様に命令コード又は、ロードされたRubyコードのパースツリーは(参考記事リンク)、アクセス可能だ。イントロスペクション能力は更に進んでおり、例えば、SendSitesだ(参考記事)。SendSitesは、send("method invocation")のようにメッセージのサイトを表し、メソッドと関連づけることが出来る。SendSitesを使うと、実行時にロードされたコードの設定を把握することが出来るが、同様にプロファイリングやコードカバリッジツールとしても機能する。全てのメッセージ送信が、SendSiteのカウンターを増加する。この情報が、通常のRubyコードで利用出来るので、単純なプロファイラーや、少なくともコードカバリッジツールを書くとしても、2、3行のコードで実現出来てしまう。

GUI

現在、Rubiniusデバッガーへのユーザインタフェースは、コマンド行インタフェースであり、それを使ってブレークポイント、ステップ実行、それだけでなく、実行中のRubyコードやソースコードの命令コードを見ることを管理出来てしまう。役に立つ命令は、sexpだ。それは、抽象構文木に関して、s式のパースツリーを返却する。(引数を省略することによって、パースツリーのs式として現在のメソッドのASTを示している)これは非常に有益な情報だ。特に、メタプログラミングを使ったコードには非常に有益だ。実行時にコード生成されたものは、ソースコードにはないのは明らかだ。生成そしてロードされたコードを見ることが可能だと、メタプログラミングしているコードをデバッギングするのに役立てることが出来る。S 式を見てみると、生成されたコードが行う部分を推測しようと試みることから進歩したということが分かる。Ruby2Ruby(リンク)のようなパースツリーベースのツールへ後退することにより一層便利になる。そして、それはs式を取得し、Rubyのソースコードへフォーマットし直す。

RubiniusのデバッガGUIへの接続は、この記事を書いている時点では存在しない。しかし、この状況は直ぐに変わるだろう。理由は、デバッギングプロトコルの実装が、現時点で取り交わされていることだ。デバッギングサポートが実装された期間から判断すると、デバッガーGUIサポートは、そう遠い未来の話ではない(デバッギングプロトコルの実装は、デバッガ実装の中でも簡単な部分である。)一度、debug-commonsやDBGpプロトコルが、サポートされれば、これらのプロトコルを使用したIDEは、Rubiniusを使うことが出来るかもしれない。

IronRuby

IronRubyは(参考記事リンク)、MSILコードを生成することによって、.NETプラットフォームをターゲットとしている。IronRubyは、DLRを利用している。DLRとは、例えば、expression treeなどからMSILを生成するというような、複数の言語実装向けに共通の機能を集めて作ったシステムである。

Debugging backends

DLRは、.NET MS ILを生成し、同様にMS ILのデバッギング情報も生成することも出来る。このことは、IronRubyが、.NETのデバッギング機能とVisual StudioのデバッガGUIの両方を使えることを意味する。

GUI

Visual Studioを使えるが、SapphireSteelのRuby in Steel IDEは、Visual Studioをベースとしており、IronRuby開発のサポートをしている。デバッギング機能は、将来的には利用可能になるだろう。

The rest of the field

この記事では、完全性を求めることなく現在Rubyで利用可能なデバッギングツールを紹介してきた。ActiveState KomodoやRubyの実装やデバッギング機能に対して、さまざまなレベルのサポートをしているバックエンドようなIDEで、他のGUIやバックエンドが利用可能だ。Ruby実装のXRubyは(リンク)、デバッギングをサポートしている(リンク)にも関わらず、記事では扱わなかった。Ruby 1.9は、公式にリリースされてはいるが、依然として主要部分は開発中と思えたので、同様にこの記事では扱わなかった。Ruby 1.9のVMは同様にバイトコードのインタープリタを使用しているので、Rubiniusと同じようなスキーマが可能だろう。


最後に、免責事項:既存の代わりとなる新しいRubyの実装やデバッギングサポートの開発は、今も尚足早に進められている。従って、Rubyのデバッギングサポートの概観を知るためにこの記事を読んで欲しい。読者であるあなたが、これを読む頃には、Rubyの実装やツールの実際のデバッギングサポートは
変更され、改善されているであろう。

 

原文はこちらです:http://www.infoq.com/articles/ruby-debuggers-survey
(このArticleは2008年4月19日に原文が掲載されました)

この記事に星をつける

おすすめ度
スタイル

BT