wait2_fail_test.rb
| 1 |
# Sample program to repro a bug that causes ruby to crash in a call to
|
|---|---|
| 2 |
# Process.wait2. The program simulates a server controller scenario, in which
|
| 3 |
# the parent process starts a daemon process which itself manages a "server"
|
| 4 |
# process, in this case just another ruby that sleeps forever. The desire is to
|
| 5 |
# send a signal (USR1 here) to the daemon process informing it that it should
|
| 6 |
# shut down the server.
|
| 7 |
#
|
| 8 |
# What is observed is that Time.strftime is causing this. Originally it was
|
| 9 |
# thought that the problem was in the Logger class, but attempts to workaround
|
| 10 |
# that class by rolling a custom logger that used Time.strftime to do its work
|
| 11 |
# revealed the problem once again. When strftime is used from debug statements
|
| 12 |
# issued from within the trap handler, the trap handler completes but the
|
| 13 |
# subsequent call to Process.wait2 crashes the interpreter. If the log
|
| 14 |
# statements don't use strftime everything works as expected. Note also that
|
| 15 |
# the crash behavior is nondeterministic, though anecdotally it happens 70-80%
|
| 16 |
# of the time.
|
| 17 |
#
|
| 18 |
# To run this program 10 times and see what the daemon prints, including stack
|
| 19 |
# traces, here is a quick shell script:
|
| 20 |
#
|
| 21 |
# for (( i = 0; i < 10; ++i )); do if [ -e /tmp/test.log ]; then rm
|
| 22 |
# /tmp/test.log; fi; ruby /tmp/wait2_fail_test.rb ; sleep 1; kill -USR1 `cat
|
| 23 |
# /tmp/test.pid `; sleep 2; cat /tmp/test.log; done
|
| 24 |
#
|
| 25 |
# See the bottom of this script for a sample crash trace
|
| 26 |
#
|
| 27 |
STDOUT.sync = true |
| 28 |
|
| 29 |
# Set this to true to use strftime in debug statements making the interpreter
|
| 30 |
# crash; set to false to skip them and never crash
|
| 31 |
$use_strftime = true |
| 32 |
|
| 33 |
def debug( msg ) |
| 34 |
|
| 35 |
tm = $use_strftime ? Time.now.strftime( "%H:%M:%S" ) : Time.now.to_s |
| 36 |
puts "[#{tm} ##$$]: #{msg}"
|
| 37 |
end
|
| 38 |
|
| 39 |
# Here we go...
|
| 40 |
|
| 41 |
if pid = Kernel.fork |
| 42 |
Process.detach( pid )
|
| 43 |
debug( "Detached from child" )
|
| 44 |
else
|
| 45 |
Process.daemon
|
| 46 |
|
| 47 |
io = File.open( "/tmp/test.log", "a" ) |
| 48 |
STDOUT.reopen( io )
|
| 49 |
STDERR.reopen( io )
|
| 50 |
|
| 51 |
File.open( "/tmp/test.pid", "w" ) { |io| io.puts $$ } |
| 52 |
|
| 53 |
debug( "Starting server" )
|
| 54 |
server_pid = Process.spawn( "ruby", "-e", "sleep" ) |
| 55 |
debug( "Started server with pid #{server_pid}" )
|
| 56 |
|
| 57 |
trap( "USR1" ) {
|
| 58 |
debug( "Inside trap; killing server" )
|
| 59 |
Process.kill( "USR2", server_pid ) |
| 60 |
debug( "Did kill" )
|
| 61 |
} |
| 62 |
|
| 63 |
debug( "Waiting for server: #{server_pid}" )
|
| 64 |
Process.wait2( server_pid )
|
| 65 |
debug( "Wait done; exiting" )
|
| 66 |
|
| 67 |
exit |
| 68 |
end
|
| 69 |
|
| 70 |
# ----------------------------------------------------------------
|
| 71 |
# Here is a sample of one of the crash messages from /tmp/test.log
|
| 72 |
# ----------------------------------------------------------------
|
| 73 |
#
|
| 74 |
#
|
| 75 |
# /tmp/wait2_fail_test.rb:50: [BUG] rb_sys_fail() - errno == 0
|
| 76 |
# ruby 1.9.1p0 (2009-01-30 revision 21907) [i386-darwin9.6.0]
|
| 77 |
#
|
| 78 |
# -- control frame ----------
|
| 79 |
# c:0004 p:---- s:0015 b:0015 l:000014 d:000014 CFUNC :wait2
|
| 80 |
# c:0003 p:0314 s:0011 b:0011 l:002644 d:000c08 EVAL /tmp/wait2_fail_test.rb:50
|
| 81 |
# c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH
|
| 82 |
# c:0001 p:0000 s:0002 b:0002 l:002644 d:002644 TOP <main>:38964
|
| 83 |
# ---------------------------
|
| 84 |
# -- Ruby level backtrace information-----------------------------------------
|
| 85 |
# /tmp/wait2_fail_test.rb:50:in `wait2'
|
| 86 |
# /tmp/wait2_fail_test.rb:50:in `<main>'
|
| 87 |
#
|
| 88 |
# -- C level backtrace information -------------------------------------------
|
| 89 |
# 0x115fb2 0 ruby 0x00115fb2 rb_vm_bugreport + 82
|
| 90 |
# 0x2b70c 1 ruby 0x0002b70c rb_warning + 444
|
| 91 |
# 0x2b76b 2 ruby 0x0002b76b rb_bug + 43
|
| 92 |
# 0x2cd3b 3 ruby 0x0002cd3b rb_sys_fail + 75
|
| 93 |
# 0x8bcd0 4 ruby 0x0008bcd0 rb_f_exit + 672
|
| 94 |
# 0x8bcf9 5 ruby 0x0008bcf9 rb_f_exit + 713
|
| 95 |
# 0x1019f5 6 ruby 0x001019f5 rb_iseq_compile + 1125
|
| 96 |
# 0x10646d 7 ruby 0x0010646d rb_funcall2 + 1773
|
| 97 |
# 0x108bdb 8 ruby 0x00108bdb rb_funcall2 + 11867
|
| 98 |
# 0x10dfcb 9 ruby 0x0010dfcb rb_funcall2 + 33355
|
| 99 |
# 0x10e1df 10 ruby 0x0010e1df rb_iseq_eval_main + 159
|
| 100 |
# 0x2e869 11 ruby 0x0002e869 ruby_exec_node + 169
|
| 101 |
# 0x30eee 12 ruby 0x00030eee ruby_run_node + 94
|
| 102 |
# 0x184f 13 ruby 0x0000184f main + 95
|
| 103 |
# 0x17b6 14 ruby 0x000017b6 start + 54
|
| 104 |
#
|
| 105 |
# [NOTE]
|
| 106 |
# You may encounter a bug of Ruby interpreter. Bug reports are welcome.
|
| 107 |
# For details: http://www.ruby-lang.org/bugreport.html
|
| 108 |
#
|