wait2_fail_test.rb

(Uses strftime to cause failure, not Logger) - jgold.bg (Jonathan Gold), 01/15/2010 04:56 am

Download (4.5 kB)

 
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
#