2.4.0がリリースされたので、
と同じことをやってみようかと思います。
準備
# cat /etc/system-release Amazon Linux AMI release 2016.09
$ wget https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.0.tar.gz $ tar zxf ruby-2.4.0.tar.gz $ cd ruby-2.4.0 $ ./configure optflags="-O0" debugflags="-ggdb3" --prefix="${HOME}/.rbenv/versions/2.4.0-O0_g3" $ make $ make install
configureのサマリは下記の通りです。
--- Configuration summary for ruby version 2.4.0 * Installation prefix: /home/ec2-user/.rbenv/versions/2.4.0-O0_g3 * exec prefix: ${prefix} * arch: x86_64-linux * site arch: ${arch} * RUBY_BASE_NAME: ruby * ruby lib prefix: ${libdir}/${RUBY_BASE_NAME} * site libraries path: ${rubylibprefix}/${sitearch} * vendor path: ${rubylibprefix}/vendor_ruby * target OS: linux * compiler: gcc * with pthread: yes * enable shared libs: no * dynamic library ext: so * CFLAGS: ${optflags} ${debugflags} ${warnflags} * LDFLAGS: -L. -fstack-protector -rdynamic \ -Wl,-export-dynamic * optflags: -O0 -fno-fast-math * debugflags: -ggdb3 * warnflags: -Wall -Wextra -Wno-unused-parameter \ -Wno-parentheses -Wno-long-long \ -Wno-missing-field-initializers \ -Wno-tautological-compare \ -Wno-parentheses-equality \ -Wno-constant-logical-operand -Wno-self-assign \ -Wunused-variable -Wimplicit-int -Wpointer-arith \ -Wwrite-strings -Wdeclaration-after-statement \ -Wimplicit-function-declaration \ -Wdeprecated-declarations \ -Wno-packed-bitfield-compat \ -Wsuggest-attribute=noreturn \ -Wsuggest-attribute=format * strip command: strip -S -x * install doc: yes * man page type: doc ---
gdbでアタッチするRubyプロセスを作る
まずは、かんたんなhttpサーバーを立ち上げてそのプロセスにアタッチしてみます。
今回は単純なhello world
を返すだけのhttpサーバーを立ててます。(pidは控えておいてください)
ソースは下記reposにあります。
アタッチしてみる
サクッとgdbでアタッチした体でいきます。
Cの世界
(gdb) p ruby_version $1 = "2.4.0"
スレッド情報はどうでしょう。
(gdb) info threads Id Target Id Frame 2 Thread 0x7fe410aad700 (LWP 17067) "ruby-timer-thr" 0x00007fe40fc5f64d in poll () at ../sysdeps/unix/syscall-template.S:81 * 1 Thread 0x7fe410aa4740 (LWP 17058) "rackup" 0x00007fe40fc613c3 in select () at ../sysdeps/unix/syscall-template.S:81
メインスレッド(Rubyのスレッド)とタイマスレッドがいますね。
ここいらでRubyの世界にいきます。
Rubyの世界へ
まずは簡単にRubyのcallbackを表示してみます。
(gdb) call rb_vmdebug_stack_dump_raw_current()
そうすると、プロセスの標準出力(今回はrackupで立ち上げたプロセス)にrubyのCの関数呼出しも含まれて標準出力に表示されます。
from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/bin/rackup:22:in `<main>' from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/bin/rackup:22:in `load' from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/bin/rackup:4:in `<top (required)>' from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/server.rb:147:in `start' from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/server.rb:296:in `start' from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb:34:in `run' from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:158:in `start' from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:33:in `start' from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:171:in `block in start' from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:171:in `select' -- Control frame information ----------------------------------------------- c:0011 p:---- s:0064 e:000063 CFUNC :select c:0010 p:0117 s:0056 e:000055 BLOCK /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:171 c:0009 p:0006 s:0046 e:000045 METHOD /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:33 c:0008 p:0068 s:0042 e:000041 METHOD /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:158 c:0007 p:0161 s:0036 e:000035 METHOD /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb:34 c:0006 p:0231 s:0028 E:0015a8 METHOD /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/server.rb:296 c:0005 p:0016 s:0021 e:000020 METHOD /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/server.rb:147 c:0004 p:0023 s:0016 e:000015 TOP /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/bin/rackup:4 [FINISH] c:0003 p:---- s:0013 e:000012 CFUNC :load c:0002 p:0136 s:0008 E:0020a0 EVAL /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/bin/rackup:22 [FINISH] c:0001 p:0000 s:0003 E:001850 (none) [FINISH]
最新のコールスタックの中身も見てみます。
(gdb) ruby_thread running_thread set $ruby_thread (gdb) vm_frame_type $ruby_thread->cfp There is no member named flag.
ん、cfp構造体の中からflagがなくなった?
ここで、rb_control_frame_t::flagのメンバをep[0]に移したとのこと。 https://github.com/ruby/ruby/blob/1407e52ba4915ed7f251dec548b5e68f2a5a6a6e/vm_core.h#L994
あと、vmのframe_typeの持ち方も変わっているので、前に定義したgdbのvm_frame_type関数も変更しておきます。 ruby/vm_core.h at 1407e52ba4915ed7f251dec548b5e68f2a5a6a6e · ruby/ruby · GitHub
Ruby 2.4.0 for gdbinit · GitHub 再度アタッチ。 ついでにrb_vm_frame_method_entry()も一緒に見てみます。
(gdb) ruby_thread running_thread set $ruby_thread (gdb) vm_frame_type $ruby_thread->cfp vm_frame_type 55550001 CFUNC (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 $1 = (struct RString *) 0x5635ba9a5010
ここまでくれば、2.3.3とほとんど変わってないですね。
前と同じように、4つ前のコールスタック(rubyのメソッド呼び出し)のコールスタックも見てみます。
(gdb) set $cfp = $ruby_thread->cfp + 4 # 4つ前のコールスタック (gdb) vm_frame_type $cfp vm_frame_type 11110001 METHOD (gdb) rp $cfp->iseq->body->location.path # ファイルパス [PROMOTED] T_STRING: "/home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb" bytesize:106 encoding:2 coderange:7bit $2 = (struct RString *) 0x5635ba951028 (gdb) p/d rb_vm_get_sourceline($cfp) # ファイルの行数 $3 = 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.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb> local table (size: 4, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) [ 4] app<Arg> [ 3] options<Opt=0>[ 2] environment[ 1] default_host 0000 newhash 0 ( 25) 0002 setlocal_OP__WC__0 5 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 4 0027 trace 1 ( 27) 0029 getlocal_OP__WC__0 4 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 3 0045 trace 1 ( 29) 0047 getlocal_OP__WC__0 5 0049 putobject :BindAddress 0051 getlocal_OP__WC__0 5 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 3 0064 opt_aset <callinfo!mid:[]=, argc:2, ARGS_SIMPLE>, <callcache> 0067 pop 0068 trace 1 ( 30) 0070 getlocal_OP__WC__0 5 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 5 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:2> 0120 putstring \"/\" 0122 getinlinecache 133, <is:3> 0125 getconstant :Rack 0127 getconstant :Handler 0129 getconstant :WEBrick 0131 setinlinecache <is:3> 0133 getlocal_OP__WC__0 6 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:2> 0150 invokeblock <callinfo!argc:1, ARGS_SIMPLE> 0152 pop 0153 trace 1 ( 34) 0155 getinstancevariable :@server, <is:2> 0158 opt_send_without_block <callinfo!mid:start, argc:0, ARGS_SIMPLE>, <callcache> 0161 trace 16 ( 35) 0163 leave ( 34)
データはこちら。
#<WEBrick::HTTPServer:0x005635baae7978 @config={:BindAddress=>"localhost", :Port=>9292, :MaxClients=>100, :ServerType=>nil, :Logger=>#<WEBrick::Log:0x005635baae77e8 @level=4, @log=#<IO:<STDERR>>, @time_format="[%Y-%m-%d %H:%M:%S]">, :ServerSoftware=>"WEBrick/1.3.1 (Ruby/2.4.0/2016-12-24)", :TempDir=>"/tmp", :DoNotListen=>false, :StartCallback=>nil, :StopCallback=>nil, :AcceptCallback=>nil, :DoNotReverseLookup=>true, :ShutdownSocketWithoutClose=>false, :RequestTimeout=>30, :HTTPVersion=>#<WEBrick::HTTPVersion:0x005635bafca738 @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:0x005635baae77e8 @level=4, @log=#<IO:<STDERR>>, @time_format="[%Y-%m-%d %H:%M:%S]">, @tokens=#<Thread::SizedQueue:0x005635baae76d0>, @listeners=[#<TCPServer:fd 7>], @shutdown_pipe=[#<IO:fd 8>, #<IO:fd 9>], @http_version=#<WEBrick::HTTPVersion:0x005635bafca738 @minor=1, @major=1>, @mount_tab=#<WEBrick::HTTPServer::MountTable:0x005635baae67a8 @tab={""=>[Rack::Handler::WEBrick, [#<Rack::ContentLength:0x005635baaeb488 @app=#<Rack::Chunked:0x005635baaeb730 @app=#<Rack::CommonLogger:0x005635baaeb938 @app=#<Rack::ShowExceptions:0x005635bab2dfb8 @app=#<Rack::Lint:0x005635bab2e058 @app=#<Rack::TempfileReaper:0x005635bab2e120 @app=#<Application:0x005635bab3dda0>>, @content_length=nil>>, @logger=#<IO:<STDERR>>>>>]]}, @scanner=/^()(?=\/|$)/>, @virtual_hosts=[]>
こちらも2.3.3のときと同じ感じでいけました。
cfp周りで変更が入っていそうなので、おいおい調べられればよいなあ。。。