CDT デバッガーとのインターフェース: 第 1 回 C/C++ デバッガーのインターフェースを理解する

CDT デバッガー・フレームワークにカスタム・ツールを追加する

Eclipse の CDT (C/C++ Development Tooling) は C/C++ 開発用のオープンソース環境として最もよく知られたものの 1 つであり、その人気を支える大きな要素が CDT の持つ本格的なデバッガーです。一方、あまり知られていませんが、カスタムのデバッガーをサポートするために CDT フレームワークを拡張することができます。適切なプラグインを利用することにより、カスタムのデバッガーは CDT のグラフィカルなデバッグ環境のあらゆる機能を利用することができます (ステップごとのコード実行、ウォッチポイントの設定、ブレークポイントの設定、レジスターやメモリーの内容、そして変数の表示など)。こうしたプラグインの作成方法を、CDI (C/C++ Debugging Interface) に焦点を当てながら学びましょう。

Matthew Scarpino, Java Developer, Eclipse Engineering, LLC

Matthew Scarpino は、Eclipse Engineering LLC のプロジェクト・マネージャーであり、Java 開発者です。彼は SWT/JFace in Action の主執筆者であり、また SWT (Standard Widget Toolkit) に対して、小さいながらも重要な貢献をしました。彼が好きなものはアイルランドの民族音楽やマラソン、William Blake の詩、そして GEF (Graphical Editing Framework) です。



2008年 6月 10日

デバッグにコマンドライン・インターフェースを使うこともできますが、本格的な使用に耐える洗練されたものとして、適切に作成されたグラフィカルな環境に勝るものはありません。フル機能のデバッグ環境をゼロから作成するにはかなりの時間と努力が必要ですが、実は 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 をカスタマイズする方法について説明します。

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 ウィンドウ
CDT Launch Configuration window

新しいデバッガーと 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. 起動構成タブの例
Example launch-configuration tab

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.CDebuggerid フィールドに対応しています。リスト 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 つのメソッドが空のままになっています。


ターゲット、イベント、そして CDI モデル

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 モデルと呼ばれる階層構造が形成されます。これらのオブジェクトには、ICDIRegisterICDIVariableICDIMemoryBlockICDIThread など、多数あります。ICDITarget はこの階層構造のルートであり、CDI モデル・オブジェクトのコンテナーです。すべての hICDIObject が実装しなければならないメソッドは、getTarget() のみです。


CDI のブレークポイントとウォッチポイント

デバッガーの実行を制御する最も重要な 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() を使用します。多くの場合、このマネージャーにアクセスして IBreakpointsICDIBreakpoints に変換する必要があります。


まとめ

グラフィカルなデバッグ環境をゼロから作成しようとすると、非常に大きな困難を伴います。対象のプロセッサーを詳細に知る必要があるだけではなく、デバッガーの出力がグラフィカルなユーザー環境に出力されるようにデバッガーと通信を行う必要もあります。あまり困難な思いをしたくないという人達のために、Eclipse にはカスタムのデバッガーを CDT フレームワークに追加するための API が用意されています。この API は CDI (C/C++ Debugger Interface) と呼ばれ、この記事ではこの CDI の動作と使い方の基本について説明しました。この「CDT デバッガーとのインターフェース">」シリーズ第 2 回では、CDT が CDI をどう使ってオープンソースのデバッガーの中で最高傑作の gdb (GNU Debugger) とインターフェースを取るかを説明します。


ダウンロード

内容ファイル名サイズ
Sample codeos-eclipse-cdt-debug-ex-debugger-plugin.zip15KB

参考文献

学ぶために

  • 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 製品でオープンソース技術を使用するためのハウ・ツー情報やツール、プロジェクトの更新情報など、豊富な情報が用意されています。

製品や技術を入手するために

議論するために

  • Eclipse に関する質問を議論するための最初の場所として、Eclipse Platform newsgroups があります (このリンクをクリックすると、デフォルトの Usenet ニュース・リーダー・アプリケーションが起動し、eclipse.platform が開きます)。
  • Eclipse newsgroups には、Eclipse を利用し、拡張することに関心を持つ人達のために、さまざまなリソースが用意されています。
  • developerWorks blogs から developerWorks のコミュニティーに加わってください。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=318683
ArticleTitle=CDT デバッガーとのインターフェース: 第 1 回 C/C++ デバッガーのインターフェースを理解する
publish-date=06102008