これまで、自作ISSに様々な機能を組み込んできた。Luaのインタフェースを使ってインタラクティブなデバッガを実現したり、elfを読み込むことで関数トレースや、関数フローを表示できるような改良を施してきた。 ベンチマークを実行したときに、関数をどのような順序でトレースしていったのかを、簡単にトレースできるようになった。
これでも十分便利なのだが、やはりソースコードを見ながらインタラクティブにデバッグできるほうが便利ん決まっている。 この機能を実現するためには、もちろんGDBだ。GDBは実行バイナリとソースコードを読み込み、実行結果をソースコード上にインタラクティブに表示させることができる。 これは何も物理的なx86のプロセッサの実行結果のデバッグだけでなく、例えばQEMUでシミュレーションを回しながら、GDBに接続してデバッグを行うこともできる。 冷静に考えれば、QEMUと接続できるならば、自作ISSにだって接続できるはずだ。
という訳で、GDBインタフェースとISSを接続するための、GDBプロトコルについて暫く調査していた。 これが実装できれば、xv6などのOSのデバッグなども格段に簡単になるに違いない。
実現したいことは以下だ。x86上で実行されているプログラムをデバッグするのが普通のGDBだが、GDBリモートデバッグの機能を利用してISSとGDBフロントエンドを接続し、ソースコードを見ながらステップ実行やブレークポイントを貼れるようになりたい。
しばらく調査して分かったのは、GDBにはRemote Serial Protocolというものが定義されており、このプロトコルに則れば良いということだ。
以下のサイトが非常に参考になった。このサイトでは、やはりオープンソースのプロセッサであるOpenRISCのシミュレータを例にとり、GDBと接続するときのリンク確立の方法、プロトコルの詳細について解説している。 また実際のソースコードもgithubに全て上がっており、非常に参考になった。
Howto: GDB Remote Serial Protocol
GDBと接続するためのポートを開放する
GDBと接続するといっても、特段特殊なことをする訳ではないようだ。通常のようにポートを開放し、接続するのを待っていれば良いらしい。 一度ポートを開放して接続してしまうと、あとは全てパケット通信に落とし込むことができる。 例えば、ターゲットとの接続は以下のようなフローで実現できるらしい。この図は上記の解説ページから拝借した。
実際のORシミュレータを眺めてみる
実際のORシミュレータでは、debug/rsp-server.c というファイルがデバッガとの接続を担っているようだ。 githubからクローンして、ソースコードを確認しよう。
- or1ksim/debug/rsp-server.c
static void rsp_get_client () { int tmp_fd; /* Temporary descriptor for socket */ int optval; /* Socket options */ struct sockaddr_in sock_addr; /* Socket address */ socklen_t len; /* Size of the socket address */ /* 0 is used as the RSP port number to indicate that we should use the service name instead. */ if (0 == config.debug.rsp_port) { struct servent *service = getservbyname (OR1KSIM_RSP_SERVICE, "tcp"); if (NULL == service) { fprintf (stderr, "Warning: RSP unable to find service \"%s\": %s\n", OR1KSIM_RSP_SERVICE, strerror (errno)); return; } config.debug.rsp_port = ntohs (service->s_port); } ...
まずはポートを取得する。最初は、OR1KSIM_RSP_SERVICEって何だ?と思ったのだが、以下のように定義されている。
#define OR1KSIM_RSP_SERVICE "or1ksim-rsp"
このサービス名のポート番号をgetservbynameで逆引きするのだが、当然ながら引っ掛かる訳がない。実際にはGDBリモートデバッグを行ないたいときには、シミュレータの引数からちゃんとポート番号を入力しないとだめだ。
$ ./sim --srv=2000 Seeding random generator with value 0x074922b6 Or1ksim 2012-04-27 Building automata... done, num uncovered: 0/215. Parsing operands data... done. Resetting PIC. Startup Handle_Rsp() Listening for RSP on port 2000
ああー、なるほど。これでgdbインタフェース側から接続すると、通信が開始されるわけか。
通信の仕組みは何となく分かってきた気がする。次回は、これを自作ISSに実装しよう。