FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

ISSとGDBを接続してリモートデバッグを実現するための調査

これまで、自作ISSに様々な機能を組み込んできた。Luaのインタフェースを使ってインタラクティブなデバッガを実現したり、elfを読み込むことで関数トレースや、関数フローを表示できるような改良を施してきた。 ベンチマークを実行したときに、関数をどのような順序でトレースしていったのかを、簡単にトレースできるようになった。

msyksphinz.hatenablog.com

これでも十分便利なのだが、やはりソースコードを見ながらインタラクティブデバッグできるほうが便利ん決まっている。 この機能を実現するためには、もちろんGDBだ。GDBは実行バイナリとソースコードを読み込み、実行結果をソースコード上にインタラクティブに表示させることができる。 これは何も物理的なx86のプロセッサの実行結果のデバッグだけでなく、例えばQEMUでシミュレーションを回しながら、GDBに接続してデバッグを行うこともできる。 冷静に考えれば、QEMUと接続できるならば、自作ISSにだって接続できるはずだ。

という訳で、GDBインタフェースとISSを接続するための、GDBプロトコルについて暫く調査していた。 これが実装できれば、xv6などのOSのデバッグなども格段に簡単になるに違いない。

実現したいことは以下だ。x86上で実行されているプログラムをデバッグするのが普通のGDBだが、GDBリモートデバッグの機能を利用してISSGDBフロントエンドを接続し、ソースコードを見ながらステップ実行やブレークポイントを貼れるようになりたい。

f:id:msyksphinz:20160119014128p:plain

しばらく調査して分かったのは、GDBにはRemote Serial Protocolというものが定義されており、このプロトコルに則れば良いということだ。

以下のサイトが非常に参考になった。このサイトでは、やはりオープンソースのプロセッサであるOpenRISCのシミュレータを例にとり、GDBと接続するときのリンク確立の方法、プロトコルの詳細について解説している。 また実際のソースコードgithubに全て上がっており、非常に参考になった。

Howto: GDB Remote Serial Protocol

github.com

GDBと接続するためのポートを開放する

GDBと接続するといっても、特段特殊なことをする訳ではないようだ。通常のようにポートを開放し、接続するのを待っていれば良いらしい。 一度ポートを開放して接続してしまうと、あとは全てパケット通信に落とし込むことができる。 例えば、ターゲットとの接続は以下のようなフローで実現できるらしい。この図は上記の解説ページから拝借した。

http://embecosm.com/appnotes/ean4/images/exchange-target-remote.png

実際の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に実装しよう。