デバッグにコマンドライン・インターフェースを使うこともできますが、本格的な使用に耐える洗練されたものとして、適切に作成されたグラフィカルな環境に勝るものはありません。フル機能のデバッグ環境をゼロから作成するにはかなりの時間と努力が必要ですが、実は Eclipse の CDT (C/C++ Development Tooling という代替手段があるのです。CDT の拡張性を利用すると、CDT の持つグラフィカルなデバッグ機能をカスタムのデバッガーに追加することができます。そのために大量のコードを作成する必要はありませんが、CDT の拡張ポイントと CDI を理解する必要があります。
CDI は Java™ ベースの API (Application Programming Interface) であり、CDI のクラスとインターフェースを利用すると CDT のデバッグ・フレームワークを利用できるようになります。CDI を利用する Eclipse プラグインによって CDT に新しいデバッガーを追加することができ、またデバッグの結果を Eclipse/CDT のデバッグ・パースペクティブに表示することができます。この記事では CDI の詳細について説明します。この「CDT デバッガーとのインターフェース」シリーズの第 2 回では、CDI の MI (Machine Interface) を利用して gdb (GNU Debugger) とインターフェースを取れるように CDI をカスタマイズする方法について説明します。
CDT のデバッガーの動作について理解するためには、実際のコードを調べ、実験してみるのが一番です。この記事では、CDT を拡張して基本的なデバッグ機能を持つ、機能の限定されたプラグインを作成する方法を説明します。実際のデバッガーの実行可能ファイルは用意していませんが、ここで紹介するコードは独自のカスタム・デバッガーを CDT に追加するための基礎として利用することができます。
このサンプル・プラグインは、CDT と Eclipse Debug Framework に対する次の 3 つの拡張機能で構成されています。
-
org.eclipse.debug.core.launchConfigurationTypes - C/C++ アプリケーションをデバッグするための、別のランチャーを作成します。
-
org.eclipse.debug.ui.launchConfigurationTabGroups - デバッグを構成するためのパラメーターをユーザーから受け取ります。
-
org.eclipse.cdt.debug.core.CDebugger - 起動された C/C++ アプリケーション用にデバッグ・セッションを作成します。
この記事では、この 3 つの拡張機能について説明するとともに、これらの動作がわかるサンプル・コードを示します。次に CDI の動作を詳しく調べ、CDI モデルの説明と CDI でブレークポイントとウォッチポイントがどのように実現されているかの検証を行います。
Eclipse では、アプリケーションを起動するプロセスは「アプリケーションを実行モードで起動する (launching the application in Run mode)」という言い方をします。同様にデバッグ・セッションは「デバッグ・モードで起動する (a launch in Debug mode)」という言い方をします。起動モードを選択したら、次のステップとして起動構成タイプを選択し、アプリケーションの実行方法あるいはデバッグ方法を Eclipse に正確に指示します。例えばデバッグ・モード用の起動構成タイプでは、デバッガーの実行可能ファイルやデフォルトのデバッグ・オプション、Eclipse でのデバッグ出力の表示方法を定義します。図 1 は構成タイプが CDT の Debug Configurations ウィンドウに表示されている様子を示しています。
図 1. CDT の Debug Configurations ウィンドウ
新しいデバッガーと Eclipse とのインターフェースを取るための最初のステップは、起動構成タイプを作成することです。このためには org.eclipse.debug.core.launchConfigurationTypes 拡張ポイントを継承するプラグインが必要です。図 1 では、左側のウィンドウに構成タイプのサンプル ( Example Configuration Type) が表示されています。リスト 1 はこの新しいタイプを定義する拡張機能を示しています。
リスト 1.
LaunchConfigurationType 拡張機能の例
<extension
point="org.eclipse.debug.core.launchConfigurationTypes">
<launchConfigurationType
name="Example Configuration Type"
delegate="org.dworks.debugexample.ExampleConfigurationDelegate"
modes="debug"
public="true"
sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator"
sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer"
id="org.dworks.debug.example.ExampleLaunch">
</launchConfigurationType>
</extension>
|
Example Configuration Type が Debug ウィンドウに置かれる理由は、modes フィールドの値が debug に、public フィールドの値が true になっているためです。最も重要なフィールドは、起動プロセスを管理するクラスを指定する delegate です。ここに指定されたクラスは ILaunchConfigurationDelegate インターフェースを実装する必要がありますが、delegate クラスをゼロからコーディングする作業は複雑です。幸いなことに、CDT には既に作成された 4 つの delegate クラス (下記) が用意されているため、容易に作業を行うことができます。
-
LocalRunLaunchDelegate - C/C++ のローカル・アプリケーションを実行します。
-
LocalAttachLaunchDelegate - ローカル・アプリケーションをデバッグします。
-
CoreFileLaunchDelegate - 実行後にコア・ファイルを分析します。
-
LocalCDILaunchDelegate - ローカルの CDI デバッガーとのインターフェースを取ります。
CDT はローカル・ソフトウェアを起動するために、上記のうちの最初の 3 つを使います。最後の delegate クラスは CDT 外部のツール用に作成されたものであり、この記事の説明の中心となるものです。デバッグ・モードで起動するために LocalCDILaunchDelegate が呼び出されると、ILaunchConfiguration はデバッグ対象の実行可能ファイルに関する情報と、デバッガーに送信されるパラメーターを取得します。この情報は ILaunchConfiguration オブジェクトの中にパッケージする必要があり、またこの情報をユーザーから取得するために適切なUI が必要です。
Eclipse アプリケーションをデバッグする前に、起動構成タイプを選択してダブル・クリックまたは右クリックすることにより、新しい起動構成を作成する必要があります。この時点でグラフィカル・パネルが右側に表示されているので、このパネルに起動に関する情報を入力する必要があります。このパネルは起動構成タブ・グループ (Launch Configuration Tab Group) と呼ばれ、サンプルのタブ・グループが図 1 の右側に表示されています。このタブ・グループは複数のタブで構成され、図 1 に表示されているタブでは、デバッグ対象の実行可能ファイルの名前と、そのプロジェクト名を入力します。
各起動構成は独自のタブ・グループを持つ必要があり、このタブ・グループを plug-in.xml の中で org.eclipse.debug.ui.launchConfigurationTabGroups の拡張機能として指定する必要があります。図 2 は、この拡張機能が (この例では) どのようなものかを示しています。
リスト 2. サンプルのタブ・グループを宣言する
<extension
point="org.eclipse.debug.ui.launchConfigurationTabGroups">
<launchConfigurationTabGroup
type="org.dworks.debug.example.ExampleLaunch"
class="org.dworks.debug.example.ExampleTabGroup"
id="org.dworks.debug.example.ExampleTabGroup">
</launchConfigurationTabGroup>
</extension>
|
この拡張機能は容易に理解することができます。type フィールドは起動構成を指定し、class はこのタブ・グループを作成するクラスを指定し、id はこのタブ・グループに一意に決まる名前を指定しています。class によって指定されるクラスは ILaunchConfigurationTabGroup インターフェースを実装する必要があり、またこのクラスの仕事は 1 つ以上の ILaunchConfigurationTab を作成することです。これらのタブによって、デバッガーの動作に必要な情報がデバッガーに提供されます (例えばデバッグ・フラグやソースコードの場所、メモリーのアドレスなど)。そうした情報を提供できるように ILaunchConfigurationTab を構成するためには、下記の重要な 2 つのメソッドを実装する必要があります。
-
createControl(Composite parent)— デバッガーのタブ用のユーザー・インターフェースを作成します。 -
performApply(ILaunchConfigurationWorkingCopy configuration)— デバッガーのパラメーターを構成します。
2 番目のメソッドは特に重要です。このメソッドは、 LaunchConfigurationWorkingCopy の中の属性に値を割り当てます。このデータ・オブジェクトはデバッグに必要な情報を保持し、デバッグ・セッションが開始されるとデバッガーに送信されます。属性の名前は ICDTLaunchConfigurationConstants インターフェースの中にリストされています。重要な属性には次のようなものがあります。
-
ATTR_DEBUGGER_ID - 起動されるデバッガーの ID
-
ATTR_DEBUGGER_SPECIFIC_ATTRS_MAP - デバッガー・セッションに提供される属性
-
ATTR_PROJECT_NAME - デバッグされるプロジェクトの名前
-
ATTR_PROGRAM_NAME - デバッグされるプログラムの名前
-
ATTR_PROGRAM_ARGUMENTS - 実行中のプログラムに提供されるパラメーター
-
ATTR_PLATFORM - プログラムを実行中のオペレーティング・システム
ExampleConfigurationTabGroup コードは ExampleTab という 1 つの構成タブを作成します。このタブは、既に初期化されたテキスト・ボックスを 6 個表示します (各テキスト・ボックスは上記で説明した各属性に対応します)。図 2 には、この ExampleTab を示しています。通常は、さまざまなコントロールを持つ複数のタブを作成する必要があります。CDT には C/C++ デバッガーを構成するための CDebuggerTab クラスが用意されています。
図 2. 起動構成タブの例
ExampleTab のコードはデバッグの属性を setDefaults() メソッドによって初期化します。このメソッドは、使用するデバッガーとしてこのサンプル・デバッガーを指定し、また選択された CDT リソースを検索してデバッグ対象のプロジェクトとプログラムを見つけます。ExampleConfigurationTab は CDT の CLaunchConfigurationTab クラスを継承しているため、この検索を行うために新しいコードは必要ありません。この抽象クラスは 2 つの重要なメソッドを提供します。1 つは CDT リソースを返す getContext()、もう 1 つは ATTR_PROJECT_NAME 属性をプロジェクトの名前で初期化する initializeCProject() です。
Debug をクリックすると、Eclipse は ATTR_DEBUGGER_ID 属性で指定されるデバッガーを検索します。このサンプル・プロジェクトでは、このデバッガーは org.dworks.debugexample.ExampleDebugger に設定されています。これはサンプル・プロジェクトの拡張機能である org.eclipse.cdt.debug.core.CDebugger の id フィールドに対応しています。リスト 3 はこの拡張機能の全体を示しています。
リスト 3. サンプル・デバッガーの拡張ポイント
<extension point="org.eclipse.cdt.debug.core.CDebugger">
<debugger
platform="*"
name="Example Debugger"
modes="run"
cpu="*"
class="org.dworks.debugexample.ExampleDebugger"
id="org.dworks.debugexample.ExampleDebugger">
</debugger>
</extension>
|
デバッガーが起動すると、Eclipse はデバッガーの platform 要素と cpu 要素の値がその処理環境に対応しているかどうかをチェックします。対応している場合には、Eclipse はデバッガーの mode 要素が現在の起動モードに対応しているかどうかをチェックします。すべてのチェックにパスすると、Eclipse は class フィールドの中で指定されているクラスを探し出します。このクラスは ICDIDebugger2 インターフェースを実装する必要があり、従って createSession() メソッドの実装を提供する必要があります。
createSession() の目的は ICDISession インターフェースを実装したオブジェクトを作成することです。このオブジェクトによって、CDT は動作中のデバッガーにアクセスすることができます。このアクセスはいくつかのメソッドによって実現されますが、そのうち最も重要なものが次の 3 つです。
-
getSessionProcess()— 実行中のデバッガーのプロセスを返します。 -
getTargets()— デバッグ中のターゲットの配列を返します。 -
getEventManager()— イベント・リスナーを追加、削除するマネージャー・オブジェクトを返します。
これらのメソッドのうち、最初のメソッドは単純であり、デバッガーの実行可能ファイルに対応する Process オブジェクトを返します。次の 2 つのメソッド、getTargets() と getEventManager() は、もっと複雑です。この記事のこれから先では、この 2 つのメソッドについて説明します。この 2 つのメソッドはデバッガーの実行可能ファイルに依存するため、またサンプルのプラグインには実行可能ファイルがないため、ExampleDebugger クラスではこの 2 つのメソッドが空のままになっています。
CDT が getTargets() を呼び出す場合、そのセッションはデバッグ対象の各プロセスに対する ICDITarget を提供する必要があります。ICDITarget の仕事は、デバッグ・コマンドを受信し、それらのコマンドをデバッガー用に変換してデバッガーに送信することです。例えば CDT のデバッグ・パースペクティブで Step ボタンまたは Resume ボタンをクリックすると、CDT はそのターゲットの stepOver() メソッドまたは resume() メソッドを呼び出します。CDI はデバッガーのターゲット・インターフェースの動作に対して何も要求しませんが、第 2 回では gdb が MI プロトコルを使ってどのようにターゲットとインターフェースを取るのかを説明します。
ICDITarget はデバッガーに対してコマンドを送信する他に、デバッガーの出力を受信しようとするすべてのオブジェクトに対してデバッガーの出力を送信します。これは CDI イベントによって実現されますが、ここにそのセッションの getEventManager() メソッドが登場します。任意の CDI オブジェクト (例えば RegisterView など) がデバッガーのイベントの通知を受けたい場合には、そのオブジェクトは getEventManager() を呼び出し、そのセッションの ICDIEventManager にアクセスします。次にそのオブジェクトは ICDIEventManager の addEventListener() メソッドを呼び出し、そのオブジェクト自身をリスナーとして追加します。デバッガーが出力を生成するとターゲットは ICDIEventManager を呼び出し、そのデバッガーのすべてのリスナーに対して、出力が生成されたことを知らせます。ICDIEventManager はこれを、各リスナーの handleDebugEvents() メソッドを呼び出すことで行うため、このメソッドはすべての ICDIEventListener に実装されている必要があります。
CDI には ICDIEvent の標準セットが用意されています。デバッグ環境における、デバッガーのすべての応答または変更は、次のうちの 1 つの中にパッケージされる必要があります。
-
ICDIBreakpointMovedEvent - ユーザーが新しいブレークポイントを選択すると呼び出されます。
-
ICDIChangedEvent - ターゲットの、ある側面 (変数やレジスター、メモリーの一部など) の値が変更されると呼び出されます。
-
ICDICreationEvent/ICDIDestroyedEvent - ブレークポイントなどのオブジェクトが作成、あるいは削除されると呼び出されます。
-
ICDIExitedEvent - プログラムが終了すると呼び出されます。
-
ICDIRestartedEvent/ICDIDisconnectedEvent - ターゲットが再起動、あるいは切断されると呼び出されます。
-
ICDIExecutableReloadedEvent - プログラムが (新しいビルドの後などに) リロードされると呼び出されます。
-
ICDISuspendedEvent/ICDIRestartedEvent/ICDIResumedEvent - 実行可能ファイルが実行停止、再度実行、あるいは実行再開されると呼び出されます。
第 2 回では、gdb の出力がどのように MIEvent に変換され、さらに MIEvent がどのようにして ICDIEvent になるのかの詳細を説明します。
ICDIChangedEvent は、ターゲットが変更されるたびに起動されるため、特に重要です。ターゲットの持つ変更可能な一つひとつの側面は、それぞれが ICDIObject で表現され、これらのオブジェクトによって CDI モデルと呼ばれる階層構造が形成されます。これらのオブジェクトには、ICDIRegister、ICDIVariable、ICDIMemoryBlock、ICDIThread など、多数あります。ICDITarget はこの階層構造のルートであり、CDI モデル・オブジェクトのコンテナーです。すべての hICDIObject が実装しなければならないメソッドは、getTarget() のみです。
デバッガーの実行を制御する最も重要な 2 つの方法として、ブレークポイントとウォッチポイントを使う方法があります。ブレークポイントは、デバッガーの実行がコードの特定の場所に達するか、ある条件が満たされると、デバッガーを停止させます。ウォッチポイントは、特定の変数が読み取られたり、書き込まれたりすると、デバッガーを停止させます。CDI はこの 2 つを表現するために、次の 2 つのインターフェースを提供しています。1 つは ICDIBreakpoint、もう 1 つは ICDIBreakpoint のサブインターフェースである ICDIWatchpoint です。ICDIBreakpoint では、通常のブレークポイントとして設定することも、一時ブレークポイント、あるいはハードウェア・ブレークポイントとして設定することも可能です。また ICDIWatchpoint では、読み取りウォッチポイント、書き込みウォッチポイント、あるいはその両方に設定することができます。
ブレークポイントとウォッチポイントの作成、削除には ICDIBreakpointManagement のインスタンスを使います。ICDIBreakpointManagement には多くの関連メソッドがあります (setLineBreakpoint() や deleteBreakpoints()、setWatchpoint() など)。ただし、org.eclipse.debug.core プラグインのプラグイン・クラスには独自のブレークポイント・マネージャーである IBreakpointManager が用意されていることは重要ですので覚えておいてください。 IBreakpointManager を利用するためには DebugPlugin.getDefault().getBreakpointManager() を使用します。多くの場合、このマネージャーにアクセスして IBreakpoints を ICDIBreakpoints に変換する必要があります。
グラフィカルなデバッグ環境をゼロから作成しようとすると、非常に大きな困難を伴います。対象のプロセッサーを詳細に知る必要があるだけではなく、デバッガーの出力がグラフィカルなユーザー環境に出力されるようにデバッガーと通信を行う必要もあります。あまり困難な思いをしたくないという人達のために、Eclipse にはカスタムのデバッガーを CDT フレームワークに追加するための API が用意されています。この API は CDI (C/C++ Debugger Interface) と呼ばれ、この記事ではこの CDI の動作と使い方の基本について説明しました。この「CDT デバッガーとのインターフェース">」シリーズ第 2 回では、CDT が CDI をどう使ってオープンソースのデバッガーの中で最高傑作の gdb (GNU Debugger) とインターフェースを取るかを説明します。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Sample code | os-eclipse-cdt-debug-ex-debugger-plugin.zip | 15KB | HTTP |
学ぶために
- C/C++ Development Tooling を使い始めるために、Eclipse.org の CDT のページを訪れてください。
-
CDT プロジェクトのリーダーによるブログでは CDT の内部に関する話題を取り上げています。
- 「Eclipse の推奨読み物リスト」を調べてみてください。
- developerWorks には他にも Eclipse に関する話題が豊富に用意されています。
- Eclipse が初めての人は、developerWorks の記事「Eclipse Platform 入門」を読んでください。Eclipse の起源やアーキテクチャー、またプラグインを使って Eclipse を拡張する方法などを学ぶことができます。
- IBM developerWorks の Eclipse project resources を利用して Eclipse のスキルを磨いてください。
-
developerWorks podcasts では、ソフトウェア開発者のための興味深いインタビューや議論を聞くことができます。
- developerWorks の Technical events and webcasts で最新情報を入手してください。
- IBM とオープンソース技術、そして製品機能を調べ、学ぶために、無料の developerWorks On demand demos をご覧ください。
- IBM オープンソース開発者にとって関心のある、世界中で今後開催される会議や業界展示会、ウェブキャスト、その他のイベントについて調べてみてください。
-
developerWorks の Open source ゾーンをご覧ください。オープンソース技術を使った開発や、IBM 製品でオープンソース技術を使用するためのハウ・ツー情報やツール、プロジェクトの更新情報など、豊富な情報が用意されています。
製品や技術を入手するために
- IBM alphaWorks に用意された最新の Eclipse technology downloads を調べてください。
- Eclipse Foundation から Eclipse Platform やその他のプロジェクトをダウンロードしてください。
-
IBM 製品の試用版をダウンロードし、DB2® や Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品をお試しください。
- 皆さんの次期オープンソース開発プロジェクトを IBM trial software で革新してください。ダウンロード、あるいは DVD で入手することができます。
議論するために
- Eclipse に関する質問を議論するための最初の場所として、Eclipse Platform newsgroups があります (このリンクをクリックすると、デフォルトの Usenet ニュース・リーダー・アプリケーションが起動し、eclipse.platform が開きます)。
-
Eclipse newsgroups には、Eclipse を利用し、拡張することに関心を持つ人達のために、さまざまなリソースが用意されています。
-
developerWorks blogs から developerWorks のコミュニティーに加わってください。