block_given?

ジャングル ブロック ナイ アルノハ jump ダケ。CC-BY-SA 3.0

MRIを読む (3) デバッグ方法の入り口を覚える

MRIの中、Cでのデバッグをするための入り口を覚えたい。

gdbを試したい

gdbがない

$ gdb rubies/bin/ruby-2.1.4
The program 'gdb' can be found in the following packages:
 * gdb
 * gdb-minimal
Ask your administrator to install one of them

gdbを入れる

apt-get install -y gdb

gdbで止める

putsしたところを止める。

$ gdb rubies/bin/ruby-2.1.4
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from rubies/bin/ruby-2.1.4...done.
(gdb) b rb_io_puts
Breakpoint 1 at 0x51970: rb_io_puts. (2 locations)
(gdb) r -e 'puts :hi'
Starting program: /home/vagrant/rubies/bin/ruby-2.1.4 -e 'puts :hi'
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff7ff7700 (LWP 2593)]

Breakpoint 1, rb_io_puts (argc=1, argv=0x7ffff7eec038, out=93824997809280) at io.c:6984
6984    {
(gdb) l
6979     *     test
6980     */
6981
6982    VALUE
6983    rb_io_puts(int argc, VALUE *argv, VALUE out)
6984    {
6985        int i;
6986        VALUE line;
6987
6988        /* if no argument given, print newline. */
(gdb)

gdbでいろいろできそう。 gdbの使い方を覚えねば。 rubyの値を表示してくれるようなマクロを探したい。

coreを取ってみる

実行中のプロセスにコアをダンプする。

$ rubies/bin/ruby-2.1.4 -e 'loop { p :hi; sleep 5 }' &
$ gcore 2081 # ここは適宜読み替えてください

とっただけではどうしようもない

gdbrubyを使ってバックトレースを見る

CoreからRubyのバックトレースを表示するgdbruby.rbを作った

を参考に表示させてみる

$ rubies/bin/gem-2.1.4 install gdbruby
$ rubies/bin/gdbruby.rb core.2081  rubies/bin/ruby-2.1.4

environ:
XDG_SESSION_ID=2
TERM=xterm-256color
SHELL=/bin/bash
SSH_CLIENT=10.0.2.2 54405 22
SSH_TTY=/dev/pts/0
USER=vagrant
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31
MAIL=/var/mail/vagrant
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
PWD=/home/vagrant
LANG=en_US.UTF-8
SHLVL=1
HOME=/home/vagrant
LOGNAME=vagrant
SSH_CONNECTION=10.0.2.2 54405 10.0.2.15 22
LC_CTYPE=ja_JP.UTF-8
LESSOPEN=| /usr/bin/lesspipe %s
XDG_RUNTIME_DIR=/run/user/1000
LESSCLOSE=/usr/bin/lesspipe %s %s
_=rubies/bin/ruby-2.1.4

ruby_version:
"2.1.4"

c_backtrace:
#0  pthread_cond_timedwait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:238
#1  0x00007fd6eb996e6f in native_cond_timedwait (ts=<optimized out>, mutex=<optimized out>, cond=<optimized out>) at thread_pthread.c:352
#2  native_sleep (th=0x7fd6edcc95f0, timeout_tv=0x7fff40dffe10) at thread_pthread.c:1061
#3  0x00007fd6eb99877b in sleep_timeval (th=0x7fd6edcc95f0, tv=..., spurious_check=1) at thread.c:1049
#4  0x00007fd6eb99bf35 in rb_thread_wait_for (time=...) at thread.c:1118
#5  0x00007fd6eb8aeb70 in rb_f_sleep (argc=1, argv=0x7fd6eb6f2060) at process.c:4230
#6  0x00007fd6eb975174 in vm_call_cfunc_with_frame (ci=<optimized out>, reg_cfp=0x7fd6eb7f1ed0, th=0x7fd6edcc95f0) at vm_insnhelper.c:1489
#7  vm_call_cfunc (th=0x7fd6edcc95f0, reg_cfp=0x7fd6eb7f1ed0, ci=<optimized out>) at vm_insnhelper.c:1579
#8  0x00007fd6eb979f24 in vm_exec_core (th=th@entry=0x7fd6edcc95f0, initial=initial@entry=0) at insns.def:1028
#9  0x00007fd6eb97db8f in vm_exec (th=th@entry=0x7fd6edcc95f0) at vm.c:1398
#10 0x00007fd6eb9810da in invoke_block_from_c (th=0x7fd6edcc95f0, block=<optimized out>, self=<optimized out>, argc=argc@entry=0, argv=argv@entry=0x0, blockptr=blockptr@entry=0x0, cref=cref@entry=0x0, defined_class=8) at vm.c:817
#11 0x00007fd6eb981d44 in vm_yield (argc=0, argv=0x0, th=<optimized out>) at vm.c:856
#12 rb_yield_0 (argv=0x0, argc=0) at vm_eval.c:938
#13 loop_i () at vm_eval.c:1008
#14 0x00007fd6eb829b0e in rb_rescue2 (b_proc=b_proc@entry=0x7fd6eb981cf0 <loop_i>, data1=data1@entry=0, r_proc=r_proc@entry=0x0, data2=data2@entry=0) at eval.c:754
#15 0x00007fd6eb973a3e in rb_f_loop (self=140561089356800) at vm_eval.c:1042
#16 0x00007fd6eb9839a1 in vm_call_cfunc_with_frame (ci=<optimized out>, reg_cfp=0x7fd6eb7f1f70, th=0x7fd6edcc95f0) at vm_insnhelper.c:1489
#17 vm_call_cfunc (ci=<optimized out>, reg_cfp=0x7fd6eb7f1f70, th=0x7fd6edcc95f0) at vm_insnhelper.c:1579
#18 vm_call_method (th=0x7fd6edcc95f0, cfp=0x7fd6eb7f1f70, ci=<optimized out>) at vm_insnhelper.c:1767
#19 0x00007fd6eb97a865 in vm_exec_core (th=th@entry=0x7fd6edcc95f0, initial=initial@entry=0) at insns.def:999
#20 0x00007fd6eb97db8f in vm_exec (th=th@entry=0x7fd6edcc95f0) at vm.c:1398
#21 0x00007fd6eb989996 in rb_iseq_eval_main (iseqval=iseqval@entry=140561093547920) at vm.c:1662
#22 0x00007fd6eb825a8f in ruby_exec_internal (n=0x7fd6ee0cd790) at eval.c:253
#23 0x00007fd6eb8293ad in ruby_exec_node (n=0x7fd6ee0cd790) at eval.c:318
#24 ruby_run_node (n=<optimized out>) at eval.c:310
#25 0x00007fd6eb82566b in main (argc=3, argv=0x7fff40e00ff8) at main.c:36

ruby_backtrace:
[4] sleep() <- -e:1
[3] block in <main>() <- -e:1
[2] loop() <- -e:1
[1] <main>() <- -e:1

バックトレースが出た。

デバッグシンボルを調べる。

rubyをビルドしたときのconfigureオプションを表示する - Qiitaのコマンドを打ってgcc-gオプション渡しているか確認してみた。 最初から入ってるシステムのrubyだとついてる。

$ rubies/bin/ruby-2.1.4 -r rbconfig -e 'puts RbConfig::CONFIG["configure_args"]'
 '--prefix=/home/vagrant/rubies' '--program-suffix=-2.1.4'

あれ、ないけどデバッグシンボルついてるのかな。

coreを取ってみる2

man coreすると、コアダンプするシグナルはsignal(7)にあると書かれているので、man 7 signalで見る。 SIGQUITSIGABRTもcoreを吐くとあったので試しにkill -3 PIDkill -ABRT PIDなどで殺してみるが、コアを吐かない。

Ubuntu 12.10 で core ファイルが作成されない - @tmtms のメモ

のようなブログを見つけたのでulimit -aで確認(coreだけ見るならulimit -cでとれるとman bashに書いてあった)すると、0に設定されている。

もう一度man coreをよく見ると、コアファイルが作成されない状況について、ulimitにもしっかり触れられていた。

上限を引き上げる。

$ ulimit -c 100000

もういちどABRTしてみると、しっかりコアはいてくれた。

まとめ

  • gdbrubyのc関数の中で止まる方法を覚えた
  • gcoreで実行中のRubyプロセスのコアをダンプする方法を覚えた
  • killでコアをダンプするシグナルを送り、コアをダンプする方法を覚えた
  • coreがでない状況について調べる方法を覚えた

実際に使いこなすためには

実例で学べないので難しそう。Rubyじゃなくてもいいので実例で学ぶ方法を探したい。

  • MRIの値をRubyのように表示するためのgdbのマクロが必要
  • デバッガのコマンドを体に刻み込むための素振りが必要
  • デバッガを使うような状況に自分を追い込む

試したい

ここで紹介されているツールを試してみたい。

Ruby プロセスを追いかけるツール9選 - sonots:blog

つぎ

環境構築や、読む下準備ばかりしているので明日こそMRIを読みはじめるぞ。