KGDBを使って、Android組み込みボードをリモートデバッグしよう!【前編】〜KGDBの仕組みを理解する〜実践しながら学ぶ Android USBガジェットの仕組み(2)(2/3 ページ)

» 2011年09月21日 11時50分 公開
[中垣内勇祐、森崇、中谷洋一(永和システムマネジメント),@IT MONOist]

KGDBの仕組み

 KGDBの仕組みを理解する上で、最初にKGDBでデバッグするための構成を説明します(図2)。


KGDBでのデバッグ構成 図2 KGDBでのデバッグ構成
  • ユーザーの作業 
    ユーザーはホストPC上にインストールされたGDBに対してコマンドを送り、その結果を取得することでデバッグ作業を行います。ユーザーが使う代表的なGDBのコマンドは以下の通りです(注4)。
No. ユーザーの操作 ユーザーがGDBに送るコマンド コマンドの概要
1 ブレークポイント設定 break <ブレークポイント> <ブレークポイント>には、関数名、行番号などを指定し、ブレークポイントを設定します
2 ブレークポイント解除 delete <ブレークポイント番号> <ブレークポイント番号>には、設定したブレークポイントの番を指定し、ブレークポイントを解除します
3 データ参照 info registers レジスタの値を参照します
print <変数名> <変数名>の値を参照します
4 データ変更 set $<レジスタ>=<値> <レジスタ>に<値>を設定します
set variable <変数名>=<値> <変数名>に<値>を設定します
5 コンティニュー continue プログラムの実行を再開します。
表4 ユーザーがGDBに送るコマンド

※注4:ここで挙げているコマンドおよびパラメータは一例です。詳しくは、GDBのマニュアルを参照してください。


  • ホストPC 
    ホストPCでは、GDBがユーザー操作を受け付けたとき、KGDBに対してGDBコマンド(表5)を送り、その結果を表示してユーザーに伝えます。GDBがKGDBに送るコマンドは以下の通りです。
No. ユーザーの操作 GDBがKGDBに送るコマンド コマンドの概要
1 ブレークポイント設定 Z0,<アドレス>,<アドレス長> <アドレス>番地にソフトウェアブレークポイントを設定する
2 ブレークポイント解除 z0,<アドレス>,<アドレス長> <アドレス>番地にソフトウェアブレークポイントを削除する
3 データ参照 g GDBで定義されているレジスタ値の配列で各レジスタの値を取得する
m<アドレス>,<アドレス長> <アドレス>番地のメモリを参照する
4 データ変更 G<レジスタ値> GDBで定義されているレジスタ値の配列を渡すことで、それらの値が各レジスタに設定される
M<アドレス>,<アドレス長>:<DATA> <アドレス>番地のメモリに<DATA>を設定する
5 コンティニュー c<アドレス> <アドレス>番地からプログラムを再開する。<アドレス>を省略すると、現在の実行アドレス番地から再開する
表5 GDBがKGDBに送るコマンド

  • ターゲットボード 
    ターゲットボードとホストPCとの接続はシリアルもしくはイーサーネットで行います。そして、KGDBがGDBから要求を受け付けたとき、Linuxカーネルコードに対する「デバッグ操作」を行い、その結果をGDBに伝えます。

 さて、ここでKGDBの役割として、さらっと“デバッグ操作”をすると書きましたが、具体的にどのような操作をするとCPUを強制的に停止させ、また再開させるというような魔法を実現できるのか、気になりませんか?

KGDBのデバッガ機能の実現方法

 デバッグ操作のうち、ブレークポイントの実現方法については、デバッガ機能の肝となるものだと思います。先ほどの説明で、GDBからの通信でKGDBにブレークポイントの設定要求を送ることは分かりました。では、その要求を受けたKGDBは、一体、どのようにブレークポイントの機能を実現するのでしょうか?

 KGDBがブレークポイントの機能を実現するためには、少なくとも以下の機能要件を満たす必要があります。

  1. CPUの実行が、設定されたブレークポイントに到達したとき、GDBに処理が移ること
  2. GDBからのコンティニュー要求により、CPUの処理が継続すること
  3. 設定したブレークポイントを解除できること

 これらの機能要件をどのように実現しているか、実験用に購入したAndroid搭載の組み込みボード(図3)を用い、KGDBのソース(注5)(表6)をデバッグしながら調査しました。

Android組み込みボード 図3 Android組み込みボード
No. ファイルパス 説明
1 kernel/kgdb.c KGDBのメイン処理
2 include/linux/kgdb.h KGDBのヘッダファイル
3 drivers/serial/kgdboc.c ホストPCとシリアル通信するためのドライバ
4 drivers/net/kgdboe.c ホストPCとイーサーネット通信するためのドライバ
5 arch/arm/kernel/kgdb.c KGDBのCPU依存(ARM)処理
6 arch/arm /include/asm/kgdb.h KGDBのCPU依存(ARM)ヘッダファイル
表6 KGDBソース

※注5:Androidカーネルソースは、http://www.csun.co.jp/hpgen/HPB/entries/22.htmlの「idea6410共通資料」の「Android2.1関連資料」から取得しました。


機能要件1.:CPUが設定されたブレークポイントに到達したとき、GDBに処理が移ること
 まず、1.の機能要件に対しては、ソフトウェア割り込みを利用することで実現しています。GDBからブレークポイントの設定要求があったときに、KGDBはブレークポイント設定アドレスに対して、特殊な命令を埋め込みます(図4)。

特殊な命令を埋め込むイメージ 図4 特殊な命令を埋め込むイメージ(アドレス0x08に埋め込む)

 そして、CPUがそのアドレスの命令を実行すると、ソフトウェア割り込みが発生します(図5)。

特殊な命令を実行することでソフトウェア割り込みが発生 図5 特殊な命令を実行することでソフトウェア割り込みが発生

 ARMアーキテクチャのCPUでは、割り込みが発生すると、その種別に応じた割り込み処理を実行します。ここで、KGDBが埋め込む特殊な命令とは、「未定義命令(0xe7ffdeff)」と呼ばれるもので、CPUが未定義命令を実行すると、自動的に未定義命令用の割り込み処理が実行されます。KGDBは、この割り込み処理を実装しており、この割り込み処理でGDBとの通信が可能となるわけです。

KGDBの割り込み処理が実行される 図6 KGDBの割り込み処理が実行される

 ここで、既存のアドレスに特殊な命令を埋め込むと説明しましたが、元の命令が消えてしまって大丈夫でしょうか? ある意味、プログラムが破壊された状態といえますよね……。この点に気付かれた方、とても鋭いです。実は、KGDBがブレークポイントを設定するとき、元の命令はちゃんと退避しているのです。そして、特殊命令を実行したときに、KGDBの割り込み処理において退避していたコードを元に戻しているのです。

機能要件2.:GDBからコンティニュー要求により、CPUの処理が継続すること
 次に、2.の機能要件に対しては、GDBがコンティニュー要求を送ったときに、KGDBがソフトウェア割り込み処理を終了させることで実現できます。割り込み処理は、その処理が終了すると、割り込みが発生する前の箇所から処理を継続させますので、単に割り込み処理を終了させるだけでよいのです(注6)(図7)。

コンティニューの実現 図7 コンティニューの実現
※注6:正確には、割り込み発生時にCPUのプログラムカウンタを一命令分元に戻しておくことで、元の場所から処理が再開されるようにしています。


あれ? でも、元のコードには未定義命令が入っていたので、処理を再開するとまた割り込みが発生するのでは?

と思われるかもしれませんが、これについては先述の通り、割り込み発生時に未定義命令は消え、元のコードに戻っているため、問題はありません。

うーん。でも、設定していたブレークポイントが消えてしまって困るのでは?

と思われた方、とても鋭いです! この辺の仕組みは、KGDBを実際に動作させながら、実際に見ていきましょう。

機能要件3.:設定したブレークポイントを解除できること
 最後に、3.の機能要件に対しては、既に説明していますが、KGDBが退避していたコードを元に戻すことで実現できます。

 これで、ブレークポイントの設定に関するKGDBの仕組みの概要説明は終わりです。ここまでの情報は、KGDBを実際に動作させてまとめたものでしたが、以降では、KGDBの仕組みを理解するために、試行錯誤しながら行った調査内容になります。

Copyright © ITmedia, Inc. All Rights Reserved.