Ruby 2.4.0 リリース
2.4.0がリリースされたので、
spring-mt.hatenablog.com
と同じことをやってみようかと思います。
準備
AWSのamazon Linux上で試しています。
# cat /etc/system-release
Amazon Linux AMI release 2016.09
gccは 4.8.3
、 gdbは7.6.1
を使ってます。
$ 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
---
まずは、かんたんなhttpサーバーを立ち上げてそのプロセスにアタッチしてみます。
今回は単純なhello world
を返すだけのhttpサーバーを立ててます。(pidは控えておいてください)
ソースは下記reposにあります。
rack-hello_world
アタッチしてみる
サクッと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の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がなくなった?
d.hatena.ne.jp
ここで、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周りで変更が入っていそうなので、おいおい調べられればよいなあ。。。