MRIを読む (4) Macでもデバッグしたい
マシンが非力なので仮想マシン上では動作が重たい。 Mac上でデバッグする方法を覚える。
Homebrewでいれたrbenv + ruby-buildをつかって2.1.4をインストールしている状態。
どのコンパイラが使われたのか
clangである。なのでclangのデバッガを使ってソースを追う。
$ ruby -r rbconfig -e 'puts RbConfig::CONFIG["CC"]' clang
デバッガ(lldb)でrubyを実行
lldbでrubyのデバッグ初級編 - CubicLouve
この方のブログ、よくあたる。
同じような感じで実行してみる
$ lldb `rbenv which ruby` (lldb) target create "/usr/local/var/rbenv/versions/2.1.4/bin/ruby" Current executable set to '/usr/local/var/rbenv/versions/2.1.4/bin/ruby' (x86_64). (lldb) b rb_io_puts Breakpoint 1: where = ruby`rb_io_puts, address = 0x0000000100070750 (lldb) r -e 'puts :hi' Process 87570 launched: '/usr/local/var/rbenv/versions/2.1.4/bin/ruby' (x86_64) Process 87570 stopped * thread #1: tid = 0x1ad973, 0x0000000100070750 ruby`rb_io_puts, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x0000000100070750 ruby`rb_io_puts ruby`rb_io_puts: -> 0x100070750: pushq %rbp 0x100070751: movq %rsp, %rbp 0x100070754: pushq %r15 0x100070756: pushq %r14 (lldb) bt * thread #1: tid = 0x1ad973, 0x0000000100070750 ruby`rb_io_puts, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100070750 ruby`rb_io_puts frame #1: 0x00000001001880b0 ruby`vm_call0_body + 2320 frame #2: 0x000000010017943f ruby`rb_funcallv + 207 frame #3: 0x000000010018a9d7 ruby`vm_call_cfunc + 1063 frame #4: 0x000000010018a322 ruby`vm_call_method + 866 frame #5: 0x0000000100171aad ruby`vm_exec_core + 11933 frame #6: 0x00000001001804a1 ruby`vm_exec + 129 frame #7: 0x000000010018142c ruby`rb_iseq_eval_main + 396 frame #8: 0x0000000100046744 ruby`ruby_exec_internal + 148 frame #9: 0x000000010004666e ruby`ruby_run_node + 78 frame #10: 0x000000010000143f ruby`main + 79 frame #11: 0x00007fff8e1045fd libdyld.dylib`start + 1 frame #12: 0x00007fff8e1045fd libdyld.dylib`start + 1
これでは読めないのでは。
ビルドし直す
man clang
すると
-g Generate debug information. Note that Clang debug information works best at -O0.
とあるので、この通りにビルドする。既存の設定を確認する。
$ ruby -r rbconfig -e 'puts RbConfig::CONFIG["CFLAGS"]'
-O3 -Wno-error=shorten-64-to-32 -pipe
-g
と-O0
を渡したがソースコードが表示されない。StackOverflowの以下の記事を参考にrbenv install
に-k
オプションを渡す。
ruby - How do I get rbenv to keep debugging symbols? - Stack Overflow
実行
$ RUBY_CFLAGS='-g -O0' RUBY_CONFIGURE_OPTS="--with-readline-dir=/usr/local/opt/readline --with-openssl-dir=/usr/local/opt/openssl" rbenv install 2.1.4 -k
もう一度
でたね。
$ lldb `rbenv which ruby` (lldb) target create "/usr/local/var/rbenv/versions/2.1.4/bin/ruby" Current executable set to '/usr/local/var/rbenv/versions/2.1.4/bin/ruby' (x86_64). (lldb) b rb_io_puts Breakpoint 1: where = ruby`rb_io_puts + 19 at io.c:6989, address = 0x00000001000a53f3 (lldb) r -e 'puts :hi' Process 96042 launched: '/usr/local/var/rbenv/versions/2.1.4/bin/ruby' (x86_64) Process 96042 stopped * thread #1: tid = 0x23767f, 0x00000001000a53f3 ruby`rb_io_puts(argc=1, argv=0x0000000102000028, out=4321012640) + 19 at io.c:6989, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x00000001000a53f3 ruby`rb_io_puts(argc=1, argv=0x0000000102000028, out=4321012640) + 19 at io.c:6989 6986 VALUE line; 6987 6988 /* if no argument given, print newline. */ -> 6989 if (argc == 0) { 6990 rb_io_write(out, rb_default_rs); 6991 return Qnil; 6992 } (lldb)
実行中のプロセスにアタッチする
前回と同じように実行中のプロセスにアタッチする
$ ruby -e 'loop { puts :hi; sleep 5 }' > /dev/null &
[1] 5396
$ lldb `rbenv which ruby`
(lldb) target create "/usr/local/var/rbenv/versions/2.1.4/bin/ruby"
Current executable set to '/usr/local/var/rbenv/versions/2.1.4/bin/ruby' (x86_64).
(lldb) b rb_io_puts Breakpoint 1: where = ruby`rb_io_puts + 19 at io.c:6989, address = 0x00000001000a53f3
(lldb) process attach -p 5396
Process 5396 stopped
(lldb) process continue
Process 5396 resuming
Process 5396 stopped
* thread #1: tid = 0x23fbba, 0x00000001078a63f3 ruby`rb_io_puts(argc=1, argv=0x0000000107c38050, out=140508413144000) + 19 at io.c:6989, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x00000001078a63f3 ruby`rb_io_puts(argc=1, argv=0x0000000107c38050, out=140508413144000) + 19 at io.c:6989
6986 VALUE line;
6987
6988 /* if no argument given, print newline. */
-> 6989 if (argc == 0) {
6990 rb_io_write(out, rb_default_rs);
6991 return Qnil;
6992 }
アタッチできたようだ。
lldb
の使い方についてはまた必要になったとき調べる。
実行中のプロセスのcoreをとる
(lldb) process save-core /tmp/core
とれたコアは
$ lldb `rbenv which ruby` -c /tmp/core
のような感じで実行できる。
puts
の引数の:hi
をCから引きたかったけど
(lldb) p rb_sym_to_s(argv[0])
(lldb) expr rb_sym_to_s(argv[0])
などでうまく見れない。rb_define_method
したメソッドを引く方法を調べたほうがよさそうだ。
まとめ
ruby -r rbconfig -e 'puts RbConfig::CONFIG["CC"], RbConfig::CONFIG["CFLAGS"]'
でビルド時のオプションを確認できるclang
でコンパイルしたrubyをデバッグするときはllvm
を使うrbenv
を使っている場合RUBY_CFLAGS
でビルド時のコンパイルオプションを指定できるrbenv install X -k
の-k
オプションでソースコードを取っておかないとデバッグ時にソースコードが表示できないlldb
でrubyを実行する方法、実行中のrubyプロセスにアタッチする方法がわかったlldb
で実行中のプロセスのコアを取る方法がわかった