FPGA開発日記

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

ISSとGDBを接続してxv6をデバッグ

前回、GDBの挙動を確認したので、ISSの実装に移っていこう。

ブレークポイントを設定する際、PCの位置としては、

という位置で止まるのだった。これを実現すれば良い。

msyksphinz.hatenablog.com

github.com

という訳で実装した。この場合、PCで命令をフェッチする前にアドレス判定し、ブレークポイントに引っ掛かればすぐに停止する。 そして、レジュームするときはブレークポイントの判定を無視し、そのままフェッチに入ればよい。

    if (FindPCBreak (fetch_pc) &&
        !is_resume_break) {
        std::cerr << "<Break PC:" << std::hex << fetch_pc << ">\n";
        DebugPrint ("<Break PC: %08x>\n", fetch_pc);
        return ExecBreakPC;
    }

    fetch_res = FetchMemory (fetch_pc, &inst_hex);

これで、GDBでのブレークポイントの設定ができるようになった。試しに、xv6を起動して、mpmain()に入るところで止めてみよう。

ISSでxv6を起動して、mpmain()にブレークポイントを張る

gdbのコマンドとしては、以下のようなものを用意しておく。

target remote :2000
symbol-file kernel
directory .
layout src
b mpmain
c

ISS側は、ログを出しても良いし、出さなくても良い。ログを出さない高速動作モードの場合は、約60秒でmpmain()まで到達する。ちょっと遅い気がする。全体シミュレーションをするには苦痛だ。

iss-log-gdb: fs.img xv6.img
    @echo "*** Now run 'ISS'." 1>&2
    swimmer_mips -gdb 2000 --imgfile xv6.img --binfile kernel --init_pc 0x1fc00000 --debug --out xv6.debug.log --img_dump
iss-nolog-gdb: fs.img xv6.img
    @echo "*** Now run 'ISS'." 1>&2
    swimmer_mips -gdb 2000 --imgfile xv6.img --binfile kernel --init_pc 0x1fc00000 --img_dump

もちろん、ログを出しながらシミュレーションすることもできる。最初は、関数トレースを出しながらシミュレーションすると、暴走していないことを確認できる。

    swimmer_mips -gdb 2000 --imgfile xv6.img --binfile kernel --init_pc 0x1fc00000 --img_dump --trace_hier
          <Return: stosl>
        <Return: memset>
      <Return: kfree>
      <FunctionCall 930317: kfree(0x80104464)>
        <FunctionCall 930335: v2p(0x801042f8)>
        <Return: v2p>
        <FunctionCall 930357: memset(0x80107758)>
          <FunctionCall 930392: stosl(0x801076e4)>
          <Return: stosl>
        <Return: memset>
      <Return: kfree>
      <FunctionCall 944797: kfree(0x80104464)>
        <FunctionCall 944815: v2p(0x801042f8)>
        <Return: v2p>
        <FunctionCall 944837: memset(0x80107758)>

クライアント側を立ち上げると、mpmain()で停止した。

   |45      mpenter(void)                                                            |
   |46      {                                                                        |
   |47        switchkvm();                                                           |
   |48        seginit();                                                             |
   |49        lapicinit();                                                           |
   |50        mpmain();                                                              |
   |51      }                                                                        |
   |52      */                                                                       |
   |53      // Common CPU setup code.                                                |
   |54      static void                                                              |
   |55      mpmain(void)                                                             |
B+>|56      {                                                                        |
   |57        cprintf("cpu%d: starting\n", cpu->id);                                 |
   |58        atomic_swap(&cpu->started, 1); // tell startothers() we're up          |
   |59        scheduler();     // start running processes                            |
   |60      }                                                                        |
   |61                                                                               |
   |62                                                                               |
   |63                                                                               |
   |64                                                                               |

mpmain()に入った直後で停止した。

(gdb) p/x $pc
$1 = 0x8010530c

レジスタの値を表示してみる。

(gdb) info registers
          zero       at       v0       v1       a0       a1       a2       a3
 R0   00000000 00000000 80113458 00000003 8010ba2c 8010d6a8 8010d6a8 00000000
            t0       t1       t2       t3       t4       t5       t6       t7
 R8   00000000 00000000 0010c664 00010074 00010074 0010c664 00000000 00000000
            s0       s1       s2       s3       s4       s5       s6       s7
 R16  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
            t8       t9       k0       k1       gp       sp       s8       ra
 R24  1fc00124 0010000c 00000000 00000000 00000000 8010d6e8 8010d6e8 8010530c
            sr       lo       hi      bad    cause       pc
      00000000 00000000 00000000 00000000 00000000 8010530c
           fsr      fir
      00000000 00000000

成功だ!

ただし、ここからユーザプロセスに切り替えるところがうまくISSに実装できていない。このあたりは、xv6の挙動をgdbデバッグしながら何が起きているのか勉強しよう。