あなとみー おぶ mrubyのJIT (その10)
お久しぶりです。ここんとこしばらくProcオブジェクトのサポートを作りこんでました。これが無いとイテレータとかみんなVMに戻ってしまって性能が上がらないのです。実はProcオブジェクトをサポートしてもあまり性能が上がらなかったのですが…。
で、この作業ですごくとりにくいバグがいっぱい出て数カ月デバッグ三昧という感じでした。おかげてうまく動くようになると却って落ち着かないという状態なのですが、それはそれとしてそのデバッグで作ったツールを紹介したいと思います。全国に31名くらいいると思われるmrubyでJITコンパイラを作っている人たちに参考になれば幸いです。
デバッグしていて困るのはどの命令を実行していた時にバグったのかが分からないことです。vm.cで実行していた場合は命令毎に処理が分かれているのでまだいいのですが、ネイティブコードでバグった場合(例えばセグフォしたばあいとか)、mrubyのどの命令がコンパイルされたものでバグったのか分からないわけです。
幸い、mrubyのJITではVMに処理を渡すためにmrubyのVMのプログラムカウンタ(pc)と実行中のメソッドのirepをこまめに更新しています。これらを頼りに実行位置が付きとめられます。ところが、pcとirepがつじつまが合っていないという場合があって、この場合ほぼ確実にセグフォするのですがpcに対応するirepが分からないからいろいろ不便です。また、バイナリを見てmruby VMのどの命令か判断するのは結構出来るようになったのですが、とてもむなしい作業です。
そんなこんなで次のような関数を作ってデバッグ効率を上げました。
- search_irep(mrb, pc) pcに対応するirepを探す
- disasm_irep(mrb, irep) irepを逆アセンブルする
- disasm_once(mrb, irep, 命令) 1命令を逆アセンブルする
実装は、https://github.com/miura1729/mruby/blob/95dc9d1c5596c96aae6a6814e98e09954f0c96f4/src/jit.cの398行目以降です。
こんな感じで使います
For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /home/miura/work/mruby/bin/mruby...done.
aoベンチを実行
(gdb) r benchmark/ao-render.rb Starting program: /home/miura/work/mruby/bin/mruby benchmark/ao-render.rb [New Thread 7272.0x2f00] [New Thread 7272.0x3118] P6 64 64 255
ちなみに今のバージョンはちゃんと動きます。説明用にバグを仕込ませています。
Program received signal SIGSEGV, Segmentation fault. 0x3d24418b in ?? ()
取り合えず、irep, pcがアクセスできそうな関数を探す。mrbjit_dispatchではpcはppcという変数名でpcへのポインタという形で持っている。
(gdb) where #0 0x3d24418b in ?? () #1 0x00429041 in mrbjit_dispatch (status=0x22a8c0, mrb=0x20039920) at C:\cygwin\home\miura\work\mruby\src\vm.c:682 #2 mrb_run (mrb=0x20039920, proc=0x2003b798, self=...) at C:\cygwin\home\miura\work\mruby\src\vm.c:2325 #3 0x00419ecf in load_exec (mrb=0x20039920, p=0x200c01a0, c=<optimized out>) at src/parse.y:5206 #4 0x00427474 in mrb_load_file_cxt (mrb=0x20039920, f=0x200bff34, c=0x200c0148) at src/parse.y:5215 #5 0x004017ac in main (argc=2, argv=0x22ac40) at C:\cygwin\home\miura\work\mruby\tools\mruby\mruby.c:281
mrbjit_dispatchに対象フレームを移す
(gdb) up #1 0x00429041 in mrbjit_dispatch (status=0x22a8c0, mrb=0x20039920) at C:\cygwin\home\miura\work\mruby\src\vm.c:682 682 asm volatile("call *%0\n\t"
pcに対応するirepを探す。
(gdb) p search_irep (mrb, *ppc) $1 = (mrb_irep *) 0x200f3ec8
irepの命令列を逆アセンブルする
(gdb) p disasm_irep (mrb, $1) 0 OP_ENTER 1:0:0:0:0:0:0 1 OP_GETIV R4 @x 2 OP_MOVE R5 R1 3 OP_SEND R5 :x 0 4 OP_NOP 5 OP_MUL R4 :* 1 6 OP_NOP 7 OP_GETIV R5 @y 8 OP_MOVE R6 R1 9 OP_SEND R6 :y 0 a OP_NOP b OP_MUL R5 :* 1 c OP_NOP d OP_ADD R4 :+ 1 e OP_NOP f OP_GETIV R5 @z 10 OP_MOVE R6 R1 11 OP_SEND R6 :z 0 12 OP_NOP 13 OP_MUL R5 :* 1 14 OP_NOP 15 OP_ADD R4 :+ 1 16 OP_NOP 17 OP_MOVE R3 R4 18 OP_RETURN R3 $2 = void
どこを実行していたかはこんな感じで調べられる
(gdb) p *ppc - $1->iseq $3 = 4
そんな感じです。またお会いしましょう。