gdbを使ってRuby(2.3.3)のライブプロセスの情報を取得する
Rubyのプロセスに対して、非破壊的に中をのぞいてみたいことが時々ありますよね???(詰まっているプロセスとか)
今回、gdbを使ってライブプロセスにアタッチして調べてみました。
準備
# cat /etc/system-release Amazon Linux AMI release 2016.09
Rubyのバージョンは2.3.3
を使いました。
予めrbenvをインストールしておき、rbenvでRubyは管理しておきます。
ビルドオプションとして、最適化を切っておくのとgdbからデバッグ情報を使えるように指定します。
wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.3.tar.gz tar zxf ruby-2.3.3.tar.gz cd ruby-2.3.3 ./configure optflags="-O0" debugflags="-ggdb3" --prefix="${HOME}/.rbenv/versions/2.3.3-O0_g3" make make install
configureのオプションは
./configure --help
で確認できます。
ちなみに最適化しない(-O0
) + デバッグフラグつけてビルドしたRubyのバイナリ
と
最適化をかけて(-O3
) + デバッグフラグつけていないrubyのバイナリの大きさはこれくらい違います。
gdbでアタッチするRubyプロセスを作る
まずは、かんたんなhttpサーバーを立ち上げてそのプロセスにアタッチしてみます。
今回は単純なhello world
を返すだけのhttpサーバーを立ててます。(pidは控えておいてください)
ソースは下記reposにあります。
アタッチしてみる
Cの世界
では、gdbを使ってプロセスにアタッチしてみましょう。
gdbのコマンドラインオプションで使いそうなのは下記の通りです。
-nw
,-nowindows
: 強制的にコマンドラインインターフェースを使う-silent
: 起動時のバージョン番号を表示しない-x
: コマンドを指定されたファイルから読み取って実行する--args
: コマンドライン上で実行ファイルより後ろに置かれたものを引数として実行ファイルへ渡す
今回Rubyのソースの中に入っている.gdbinit
の内容にちょっと手を加えたgdbinitファイルを使いました。
手を加えた(追加のみですが)部分はこちら。
https://gist.github.com/SpringMT/dfc443f97804f61d1804d3c4a1f56625
アタッチするコマンドは下記の通りです。
gdb -nw -silent -x /PATH/TO/.gdbinit /PATH/TO/bin/ruby [PID] 例: gdb -nw -x ./ruby-2.3.3/.gdbinit -silent /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/bin/ruby 24676
アタッチした後で、
Missing separate debuginfos, use: debuginfo-install hoge
とかでたら、rootユーザーで
debuginfo-install hoge
をしましょう。
gdb経由でプロセスにアタッチしたら、setを使って設定を追加しておくと良いでしょう。
(今回はgdbinit内でやっています。)
(gdb) set print pretty on # 構造体の表示を見やすくします(一行ずつインデント付きで表示します。) (gdb) set pagination off # ページングを切ります
アタッチしたら、まず手始めにRubyのバージョンを表示させてみましょう。
(gdb) p ruby_version $1 = "2.3.3"
p
はGBDのコマンドで、print
のエイリアスです。
print
は変数、式の値を表示します。
ruby_versionはconst charで宣言されているグローバル変数です。
https://github.com/ruby/ruby/blob/202bbda2bf5f25343e286099140fb9282880ecba/version.c#L29
定義場所はlistコマンドで表示できます
(gdb) list ruby_version 24 const int ruby_api_version[] = { 25 RUBY_API_VERSION_MAJOR, 26 RUBY_API_VERSION_MINOR, 27 RUBY_API_VERSION_TEENY, 28 }; 29 const char ruby_version[] = RUBY_VERSION; 30 const char ruby_release_date[] = RUBY_RELEASE_DATE; 31 const char ruby_platform[] = RUBY_PLATFORM; 32 const int ruby_patchlevel = RUBY_PATCHLEVEL; 33 const char ruby_description[] = RUBY_DESCRIPTION;
次に、今アタッチしているプロセスの環境変数をみてみましょう。
(gdb) show environ # environなんかポインタおかしい? environ[0]がでてないような。。。 LESS_TERMCAP_mb= HOSTNAME=ip-172-30-0-109 TERM=xterm-256color SHELL=/bin/bash HISTSIZE=1000 ・ ・
スレッド情報はどうでしょう。
(gdb) info threads Id Target Id Frame 2 Thread 0x7fc266669700 (LWP 26309) "ruby-timer-thr" 0x00007fc26581b64d in poll () at ../sysdeps/unix/syscall-template.S:81 * 1 Thread 0x7fc266660740 (LWP 26300) "rackup" 0x00007fc26581d3c3 in select () at ../sysdeps/unix/syscall-template.S:81
メインスレッド(Rubyのスレッド)とタイマスレッドが見えます。
ここから、実際に実行されている実体に迫って行きます。
では、rackupのスレッドのbacktraceを情報をみてみましょう。
gdbのthreadコマンドでinfo threads
で表示されたIDを指定して、該当スレッドに移動し、backtraceコマンド(bt)を打つとCのバックトレース情報がみれます。
(gdb) thread 1 [Switching to thread 1 (Thread 0x7f416100d740 (LWP 6147))] #0 0x00007f41601ca3c3 in select () at ../sysdeps/unix/syscall-template.S:81 81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS) (gdb) backtrace #0 0x00007f41601ca3c3 in select () at ../sysdeps/unix/syscall-template.S:81 #1 0x0000556f9688b60a in rb_fd_select (n=9, readfds=0x7ffd5cbddc70, writefds=0x0, exceptfds=0x0, timeout=0x7ffd5cbdd8e0) at thread.c:3431 #2 0x0000556f96883f90 in native_fd_select (n=9, readfds=0x7ffd5cbddc70, writefds=0x0, exceptfds=0x0, timeout=0x7ffd5cbdd8e0, th=0x556f978c05d0) at thread_pthread.c:1090 #3 0x0000556f9688b86e in do_select (n=9, readfds=0x7ffd5cbddc70, writefds=0x0, exceptfds=0x0, timeout=0x7ffd5cbdd8e0) at thread.c:3570 #4 0x0000556f9688bb18 in rb_thread_fd_select (max=9, read=0x7ffd5cbddc70, write=0x0, except=0x0, timeout=0x7ffd5cbddc40) at thread.c:3643 #5 0x0000556f9676379a in select_internal (read=93937774090080, write=8, except=8, tp=0x7ffd5cbddc40, fds=0x7ffd5cbddc70) at io.c:8441 #6 0x0000556f96763bc3 in select_call (arg=140726159400016) at io.c:8511 #7 0x0000556f96727932 in rb_ensure (b_proc=0x556f96763b7d <select_call>, data1=140726159400016, e_proc=0x556f96763bc5 <select_end>, data2=140726159400016) at eval.c:914 #8 0x0000556f96764037 in rb_f_select (argc=4, argv=0x7f4160f0c188, obj=93937772561160) at io.c:8860 #9 0x0000556f96860559 in call_cfunc_m1 (func=0x556f96763f52 <rb_f_select>, recv=93937772561160, argc=4, argv=0x7f4160f0c188) at vm_insnhelper.c:1463 #10 0x0000556f9686107f in vm_call_cfunc_with_frame (th=0x556f978c05d0, reg_cfp=0x7f416100bd90, calling=0x7ffd5cbde930, ci=0x556f98160320, cc=0x556f98161240) at vm_insnhelper.c:1642 #11 0x0000556f9686119c in vm_call_cfunc (th=0x556f978c05d0, reg_cfp=0x7f416100bd90, calling=0x7ffd5cbde930, ci=0x556f98160320, cc=0x556f98161240) at vm_insnhelper.c:1737 #12 0x0000556f96865fcc in vm_exec_core (th=0x556f978c05d0, initial=0) at insns.def:994 #13 0x0000556f96877843 in vm_exec (th=0x556f978c05d0) at vm.c:1650 #14 0x0000556f96878455 in rb_iseq_eval (iseq=0x556f97acc3b0) at vm.c:1882 #15 0x0000556f9672ace7 in rb_load_internal0 (th=0x556f978c05d0, fname=93937774417160, wrap=0) at load.c:619 #16 0x0000556f9672ade6 in rb_load_internal (fname=93937774417160, wrap=0) at load.c:649 #17 0x0000556f9672b0d4 in rb_f_load (argc=1, argv=0x7f4160f0c048) at load.c:717 #18 0x0000556f96860559 in call_cfunc_m1 (func=0x556f9672aff4 <rb_f_load>, recv=93937772668480, argc=1, argv=0x7f4160f0c048) at vm_insnhelper.c:1463 #19 0x0000556f9686107f in vm_call_cfunc_with_frame (th=0x556f978c05d0, reg_cfp=0x7f416100bf90, calling=0x7ffd5cbdfe50, ci=0x556f97b9cdb0, cc=0x556f97b9cf60) at vm_insnhelper.c:1642 #20 0x0000556f9686119c in vm_call_cfunc (th=0x556f978c05d0, reg_cfp=0x7f416100bf90, calling=0x7ffd5cbdfe50, ci=0x556f97b9cdb0, cc=0x556f97b9cf60) at vm_insnhelper.c:1737 #21 0x0000556f96861fd0 in vm_call_method_each_type (th=0x556f978c05d0, cfp=0x7f416100bf90, calling=0x7ffd5cbdfe50, ci=0x556f97b9cdb0, cc=0x556f97b9cf60) at vm_insnhelper.c:2026 #22 0x0000556f968626f6 in vm_call_method (th=0x556f978c05d0, cfp=0x7f416100bf90, calling=0x7ffd5cbdfe50, ci=0x556f97b9cdb0, cc=0x556f97b9cf60) at vm_insnhelper.c:2161 #23 0x0000556f96862844 in vm_call_general (th=0x556f978c05d0, reg_cfp=0x7f416100bf90, calling=0x7ffd5cbdfe50, ci=0x556f97b9cdb0, cc=0x556f97b9cf60) at vm_insnhelper.c:2193 #24 0x0000556f96865fcc in vm_exec_core (th=0x556f978c05d0, initial=0) at insns.def:994 #25 0x0000556f96877843 in vm_exec (th=0x556f978c05d0) at vm.c:1650 #26 0x0000556f96878495 in rb_iseq_eval_main (iseq=0x556f97ad7d00) at vm.c:1893 #27 0x0000556f96725f97 in ruby_exec_internal (n=0x556f97ad7d00) at eval.c:245 #28 0x0000556f967260c0 in ruby_exec_node (n=0x556f97ad7d00) at eval.c:310 #29 0x0000556f96726093 in ruby_run_node (n=0x556f97ad7d00) at eval.c:302 #30 0x0000556f96723d55 in main (argc=2, argv=0x7ffd5cbe0668) at main.c:36
select()
が呼ばれているところで止まっているところまではわかりました。
ここまでは、Cの世界ですが、Cの世界だけではわかりにくいので、今度はRubyの世界に降りていきましょう。
Rubyの世界へ
まずは簡単にRubyのcallbackを表示してみます。
rubyのrb_backtrace()を使って表示できます。
https://github.com/ruby/ruby/blob/9cbd6ee09770be3d73a17ab1195a094c59c9f9ee/vm_backtrace.c#L776
gdbのcallコマンドを使って、rb_backtrace()をよびます。
(gdb) call rb_backtrace()
そうすると、プロセスの標準エラー出力(今回はrackupで立ち上げたプロセス)にrubyのbacktraceが表示されます。
from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/bin/rackup:22:in `<main>' from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/bin/rackup:22:in `load' from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/bin/rackup:4:in `<top (required)>' from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/server.rb:147:in `start' from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/server.rb:296:in `start' from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb:34:in `run' from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:164:in `start' from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:33:in `start' from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:177:in `block in start' from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:177:in `select'
更にRubyとCの関数をあわせたbacktrace情報もみてましょう。
rb_backtrace()
ではでてきませんでしたが、rb_vmdebug_stack_dump_raw_current()
を使えばCの関数呼出しも含まれて標準エラー出力に表示されます。
https://github.com/ruby/ruby/blob/202bbda2bf5f25343e286099140fb9282880ecba/vm_dump.c#L178
実際にはRubyのコールスタックを見やすい形で全てdumpしてくれます。
(gdb) call rb_vmdebug_stack_dump_raw_current()
-- Control frame information ----------------------------------------------- c:0011 p:---- s:0053 e:000052 CFUNC :select c:0010 p:0117 s:0046 e:000045 BLOCK /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:177 c:0009 p:0006 s:0037 e:000036 METHOD /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:33 c:0008 p:0068 s:0034 e:000033 METHOD /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:164 c:0007 p:0161 s:0029 e:000028 METHOD /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb:34 c:0006 p:0231 s:0022 E:000558 METHOD /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/server.rb:296 c:0005 p:0016 s:0016 e:000015 METHOD /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/server.rb:147 c:0004 p:0023 s:0012 e:000011 TOP /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/bin/rackup:4 [FINISH] c:0003 p:---- s:0010 e:000009 CFUNC :load c:0002 p:0136 s:0006 E:000370 EVAL /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/bin/rackup:22 [FINISH] c:0001 p:0000 s:0002 E:000aa0 (none) [FINISH]
CFUNCやMTHODとあるのが、スタックフレームのタイプとなり、CFUNCはC コードで書かれた関数のことになります。
MTHEODはRubyのメソッド呼出し、BLOCKはブロックの呼出しとなります。
次は各コールスタックの内容を見ていきましょう。
Rubyのコールスタックはrb_control_frame_t構造体に格納されています。
最新のコールスタックの中身をみてみます。
https://gist.github.com/SpringMT/dfc443f97804f61d1804d3c4a1f56625
のruby_thread
とvm_frame_type
コマンドを使います。
(gdb) ruby_thread running_thread set $ruby_thread (gdb) vm_frame_type $ruby_thread->cfp vm_frame_type 61 CFUNC
CFUNCが返ってくるので、このコールスタックはCの関数呼出しをしていることがわかります。
Cの関数の場合、どういう関数が呼ばれているかはrb_vm_frame_method_entry()
を使います。
https://github.com/ruby/ruby/blob/7bbab207cbe0c92777fcb30e21cde787ca9a3863/vm_insnhelper.c#L488
rp
はrubyの.gdbinitに定義されているコマンドでよしなに値をRubyレベルの表示にしてくれます。
(gdb) rp rb_id2str(rb_vm_frame_method_entry($ruby_thread->cfp)->called_id) [PROMOTED] T_STRING: "select" bytesize:6 (embed) encoding:2 coderange:7bit $19 = (struct RString *) 0x5644ed0393b8
これでselect
が呼ばれていることがわかります。
では、ちょっと意図的ですが4つ前のコールスタック(rubyのメソッド呼び出し)のコールスタックも見てみましょう。
下記に定義してある、rb_iseq_disasmも使ってYARV命令列も表示してみます。(標準出力にでます。)
https://github.com/ruby/ruby/blob/4e60f99803e0b9ab49fb368f3660438934bf1a4e/iseq.c#L1402
iseq(instruction sequence、命令列)の部分はRubyの2.3系以降で大きな変更が入っているので、2.3未満のバージョンだと少し挙動が違う可能性があります。
make rb_iseq_t T_IMEMO object (type is imemo_iseq).
また、環境データは環境ポインタで示されるデータのindex 1に入っています。(下記参照)
https://github.com/ruby/ruby/blob/472959f9c20e7aff1776312d6c8e2236a52e29a7/vm_core.h#L994
(gdb) set $cfp = $ruby_thread->cfp + 4 # 4つ前のコールスタック (gdb) vm_frame_type $cfp vm_frame_type 11 METHOD # タイプ (gdb) rp $cfp->iseq->body->location.path [PROMOTED] T_STRING: "/home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb" bytesize:109 encoding:2 coderange:7bit $9 = (struct RString *) 0x56236cde86a0 # ファイルパス (gdb) p/d rb_vm_get_sourceline($cfp) $6 = 34 # ファイルの行数 (gdb) rb_p rb_iseq_disasm($cfp->iseq) # YARV命令列 (gdb) rb_p $cfp->ep[1] # 環境ポインタのデータの中身をdumpする
YARV命令列はこちら
== disasm: #<ISeq:run@/home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb> local table (size: 5, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) [ 5] app<Arg> [ 4] options<Opt=0>[ 3] environment[ 2] default_host 0000 newhash 0 ( 25) 0002 setlocal_OP__WC__0 4 0004 trace 8 0006 trace 1 ( 26) 0008 getinlinecache 15, <is:0> 0011 getconstant :ENV 0013 setinlinecache <is:0> 0015 opt_aref_with <callinfo!mid:[], argc:1, ARGS_SIMPLE>, <callcache>, \"RACK_ENV\" 0019 dup 0020 branchif 25 0022 pop 0023 putstring \"development\" 0025 setlocal_OP__WC__0 3 0027 trace 1 ( 27) 0029 getlocal_OP__WC__0 3 0031 putstring \"development\" 0033 opt_eq <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache> 0036 branchunless 42 0038 putstring \"localhost\" 0040 jump 43 0042 putnil 0043 setlocal_OP__WC__0 2 0045 trace 1 ( 29) 0047 getlocal_OP__WC__0 4 0049 putobject :BindAddress 0051 getlocal_OP__WC__0 4 0053 putobject :Host 0055 opt_send_without_block <callinfo!mid:delete, argc:1, ARGS_SIMPLE>, <callcache> 0058 dup 0059 branchif 64 0061 pop 0062 getlocal_OP__WC__0 2 0064 opt_aset <callinfo!mid:[]=, argc:2, ARGS_SIMPLE>, <callcache> 0067 pop 0068 trace 1 ( 30) 0070 getlocal_OP__WC__0 4 0072 putobject :Port 0074 dupn 2 0076 opt_aref <callinfo!mid:[], argc:1, ARGS_SIMPLE>, <callcache> 0079 dup 0080 branchif 91 0082 pop 0083 putobject 8080 0085 opt_aset <callinfo!mid:[]=, argc:2, ARGS_SIMPLE>, <callcache> 0088 pop 0089 jump 93 0091 adjuststack 3 0093 trace 1 ( 31) 0095 getinlinecache 107, <is:1> 0098 pop 0099 putobject Object 0101 getconstant :WEBrick 0103 getconstant :HTTPServer 0105 setinlinecache <is:1> 0107 getlocal_OP__WC__0 4 0109 opt_send_without_block <callinfo!mid:new, argc:1, ARGS_SIMPLE>, <callcache> 0112 setinstancevariable :@server, <is:2> 0115 trace 1 ( 32) 0117 getinstancevariable :@server, <is:3> 0120 putstring \"/\" 0122 getinlinecache 133, <is:4> 0125 getconstant :Rack 0127 getconstant :Handler 0129 getconstant :WEBrick 0131 setinlinecache <is:4> 0133 getlocal_OP__WC__0 5 0135 opt_send_without_block <callinfo!mid:mount, argc:3, ARGS_SIMPLE>, <callcache> 0138 pop 0139 trace 1 ( 33) 0141 putself 0142 opt_send_without_block <callinfo!mid:block_given?, argc:0, FCALL|ARGS_SIMPLE>, <callcache> 0145 branchunless 153 0147 getinstancevariable :@server, <is:5> 0150 invokeblock <callinfo!argc:1, ARGS_SIMPLE> 0152 pop 0153 trace 1 ( 34) 0155 getinstancevariable :@server, <is:6> 0158 opt_send_without_block <callinfo!mid:start, argc:0, ARGS_SIMPLE>, <callcache> 0161 trace 16 ( 35) 0163 leave ( 34)
データはこちら。
#<WEBrick::HTTPServer:0x0055e7bb2e6960 @config={:ServerName=>"ip-172-30-0-109", :BindAddress=>"localhost", :Port=>"8000", :MaxClients=>100, :ServerType=>nil, :Logger=>#<WEBrick::Log:0x0055e7bb2e6550 @level=4, @log=#<IO:<STDERR>>, @time_format="[%Y-%m-%d %H:%M:%S]">, :ServerSoftware=>"WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21)", :TempDir=>"/tmp", :DoNotListen=>false, :StartCallback=>nil, :StopCallback=>nil, :AcceptCallback=>nil, :DoNotReverseLookup=>nil, :ShutdownSocketWithoutClose=>false, :RequestTimeout=>30, :HTTPVersion=>#<WEBrick::HTTPVersion:0x0055e7bb36c240 @minor=1, @major=1>, :AccessLog=>[], :MimeTypes=>{"ai"=>"application/postscript", "asc"=>"text/plain", "avi"=>"video/x-msvideo", "bin"=>"application/octet-stream", "bmp"=>"image/bmp", "class"=>"application/octet-stream", "cer"=>"application/pkix-cert", "crl"=>"application/pkix-crl", "crt"=>"application/x-x509-ca-cert", "css"=>"text/css", "dms"=>"application/octet-stream", "doc"=>"application/msword", "dvi"=>"application/x-dvi", "eps"=>"application/postscript", "etx"=>"text/x-setext", "exe"=>"application/octet-stream", "gif"=>"image/gif", "htm"=>"text/html", "html"=>"text/html", "jpe"=>"image/jpeg", "jpeg"=>"image/jpeg", "jpg"=>"image/jpeg", "js"=>"application/javascript", "lha"=>"application/octet-stream", "lzh"=>"application/octet-stream", "mov"=>"video/quicktime", "mpe"=>"video/mpeg", "mpeg"=>"video/mpeg", "mpg"=>"video/mpeg", "pbm"=>"image/x-portable-bitmap", "pdf"=>"application/pdf", "pgm"=>"image/x-portable-graymap", "png"=>"image/png", "pnm"=>"image/x-portable-anymap", "ppm"=>"image/x-portable-pixmap", "ppt"=>"application/vnd.ms-powerpoint", "ps"=>"application/postscript", "qt"=>"video/quicktime", "ras"=>"image/x-cmu-raster", "rb"=>"text/plain", "rd"=>"text/plain", "rtf"=>"application/rtf", "sgm"=>"text/sgml", "sgml"=>"text/sgml", "svg"=>"image/svg+xml", "tif"=>"image/tiff", "tiff"=>"image/tiff", "txt"=>"text/plain", "xbm"=>"image/x-xbitmap", "xhtml"=>"text/html", "xls"=>"application/vnd.ms-excel", "xml"=>"text/xml", "xpm"=>"image/x-xpixmap", "xwd"=>"image/x-xwindowdump", "zip"=>"application/zip"}, :DirectoryIndex=>["index.html", "index.htm", "index.cgi", "index.rhtml"], :DocumentRoot=>nil, :DocumentRootOptions=>{:FancyIndexing=>true}, :RequestCallback=>nil, :ServerAlias=>nil, :InputBufferSize=>65536, :OutputBufferSize=>65536, :ProxyAuthProc=>nil, :ProxyContentHandler=>nil, :ProxyVia=>true, :ProxyTimeout=>true, :ProxyURI=>nil, :CGIInterpreter=>nil, :CGIPathEnv=>nil, :Escape8bitURI=>false, :environment=>"development", :pid=>nil, :config=>"/home/ec2-user/rack-hell_world/config.ru"}, @status=:Running, @logger=#<WEBrick::Log:0x0055e7bb2e6550 @level=4, @log=#<IO:<STDERR>>, @time_format="[%Y-%m-%d %H:%M:%S]">, @tokens=#<Thread::SizedQueue:0x0055e7bb2e64d8>, @listeners=[#<TCPServer:fd 7>], @shutdown_pipe=[#<IO:fd 8>, #<IO:fd 9>], @http_version=#<WEBrick::HTTPVersion:0x0055e7bb36c240 @minor=1, @major=1>, @mount_tab=#<WEBrick::HTTPServer::MountTable:0x0055e7bb2e3968 @tab={""=>[Rack::Handler::WEBrick, [#<Rack::ContentLength:0x0055e7bb2e8d00 @app=#<Rack::Chunked:0x0055e7bb2e8da0 @app=#<Rack::CommonLogger:0x0055e7bb2e8e68 @app=#<Rack::ShowExceptions:0x0055e7bb7efe48 @app=#<Rack::Lint:0x0055e7bb7efe98 @app=#<Rack::TempfileReaper:0x0055e7bb7efee8 @app=#<Application:0x0055e7bb17e280>>, @content_length=nil>>, @logger=#<IO:<STDERR>>>>>]]}, @scanner=/^()(?=\/|$)/>, @virtual_hosts=[]>
ここまでくれば、処理の流れも追えますし、データもある程度見えているので、ここから大分デバッグもしやすくなるのではないでしょうか。
ライブプロセスの調査を非破壊的にやるにはいろいろ難しい面がありますが、その時の参考になればと思います。