前回、GDBの挙動を確認したので、ISSの実装に移っていこう。
ブレークポイントを設定する際、PCの位置としては、
- ブレークポイントを設定した位置で停止する
- ただし当該命令は実行されていない
という位置で止まるのだった。これを実現すれば良い。
という訳で実装した。この場合、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でデバッグしながら何が起きているのか勉強しよう。