スレッドで遊んでたらRubyが落ちた
gcgraph(http://github.com/miura1729/gcgraph/tree/master)を拡張してスレッド毎に今どこを実行しているかグラフ表示することをたくらんでいます。そのためのテストコードを書いてみました。
ruby -r threadprof.rb thread_test.rb
とするとこんな感じで1秒間に1回表示されます。
Thread List #<Thread:0x1002fe58> : thread_test.rb:9 #<Thread:0x100300c4> : thread_test.rb:7 #<Thread:0x1002f818> : thread_test.rb:7 #<Thread:0x1002fb74> : thread_test.rb:1 #<Thread:0x1002f048> : thread_test.rb:7 #<Thread:0x1002e198> : thread_test.rb:3 #<Thread:0x10030844> : Profiler Thread #<Thread:0x1002e6ac> : thread_test.rb:7 #<Thread:0x1003e50c> : thread_test.rb:6 #<Thread:0x1002f458> : thread_test.rb:3 #<Thread:0x1002ebc0> : thread_test.rb:7 #<Thread:0x1002dc48> : thread_test.rb:5
ところが、たまにコアはいちゃいます。
[BUG] Segmentation fault ruby 1.9.0 (2008-08-28 revision 15675) [i386-cygwin] -- control frame ---------- c:0030 p:0078 s:0149 b:0148 l:001b1c d:001b1c METHOD thread_test.rb:9 c:0029 p:0070 s:0143 b:0142 l:000804 d:000804 METHOD thread_test.rb:7 c:0028 p:0070 s:0137 b:0136 l:000135 d:000135 METHOD thread_test.rb:7 c:0027 p:0053 s:0131 b:0131 l:000130 d:000130 METHOD thread_test.rb:7 c:0026 p:0053 s:0126 b:0126 l:000125 d:000125 METHOD thread_test.rb:7 c:0025 p:0053 s:0121 b:0121 l:000120 d:000120 METHOD thread_test.rb:7 c:0024 p:0053 s:0116 b:0116 l:000115 d:000115 METHOD thread_test.rb:7 c:0023 p:0053 s:0111 b:0111 l:000110 d:000110 METHOD thread_test.rb:7 c:0022 p:0053 s:0106 b:0106 l:000105 d:000105 METHOD thread_test.rb:7 c:0021 p:0053 s:0101 b:0101 l:000100 d:000100 METHOD thread_test.rb:7 c:0020 p:0070 s:0096 b:0095 l:000094 d:000094 METHOD thread_test.rb:7 c:0019 p:0053 s:0090 b:0090 l:000089 d:000089 METHOD thread_test.rb:7 c:0018 p:0053 s:0085 b:0085 l:000084 d:000084 METHOD thread_test.rb:7 c:0017 p:0070 s:0080 b:0079 l:000078 d:000078 METHOD thread_test.rb:7 c:0016 p:0053 s:0074 b:0074 l:000073 d:000073 METHOD thread_test.rb:7 c:0015 p:0053 s:0069 b:0069 l:000068 d:000068 METHOD thread_test.rb:7 c:0014 p:0053 s:0064 b:0064 l:000063 d:000063 METHOD thread_test.rb:7 c:0013 p:0070 s:0059 b:0058 l:000057 d:000057 METHOD thread_test.rb:7 c:0012 p:0053 s:0053 b:0053 l:000052 d:000052 METHOD thread_test.rb:7 c:0011 p:0053 s:0048 b:0048 l:000047 d:000047 METHOD thread_test.rb:7 c:0010 p:0053 s:0043 b:0043 l:000042 d:000042 METHOD thread_test.rb:7 c:0009 p:0053 s:0038 b:0038 l:000037 d:000037 METHOD thread_test.rb:7 c:0008 p:0053 s:0033 b:0033 l:000032 d:000032 METHOD thread_test.rb:7 c:0007 p:0070 s:0028 b:0027 l:000026 d:000026 METHOD thread_test.rb:7 c:0006 p:0070 s:0022 b:0021 l:000020 d:000020 METHOD thread_test.rb:7 c:0005 p:0070 s:0016 b:0015 l:000014 d:000014 METHOD thread_test.rb:7 c:0004 p:0053 s:0010 b:0010 l:000009 d:000009 METHOD thread_test.rb:7 c:0003 p:0021 s:0005 b:0004 l:001314 d:000003 BLOCK thread_test.rb:13 c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH c:0001 p:---- s:0002 b:0002 l:000001 d:000001 TOP ---------------------------
ruby-devに投げるべきですが、もうちょっとプログラムを切り出さないと顰蹙ですね。
とりあえず、落ちるけどプログラムを晒します。作っていて、Mutexとか条件変数とか全然理解していないことが分かりました。
本体のthreadprof.rbです
RubyVM::InstructionSequence.compile_option = { :trace_instruction => true, :specialized_instruction => false } Thread.new do while true do sleep(1) STDERR.print "\nThread List \n" profthread = Thread.current tinfo = {} tinfo[profthread] = "Profiler Thread" Thread.list.each do |th| if th != profthread then if th.status == "run" then tinfo[th] = nil else tinfo[th] = th.status end end end handler = lambda {|event, file, line, id, binding, klass| th = Thread.current if tinfo[th] == nil then tinfo[th] = "#{file}:#{line}" end if profthread.stop? then profthread.wakeup else Thread.pass end } set_trace_func handler tinfo.each do |th, value| while value == nil do Thread.stop end end set_trace_func nil tinfo.each do |th, value| STDERR.print "#{th} : #{value}\n" end end end
サンプルプログラムのthread_test.rbです
def fib(x) if x < 2 then 1 else a = 1 $a = x fib(x - 1) + fib(x - 2) end end (1..10).each do |i| Thread.new { p fib(i + 25) } end fib(40)