使用rb_eval_string写入任何IO后,在gdb中暂停的Ruby程序无法恢复

时间:2014-09-18 00:05:15

标签: ruby gdb

我正在使用gdb尝试追踪ruby程序中的内存泄漏。 我正在尝试打印一些调试数据(或将其写入文件),看来任何时候任何打印到任何IO的程序,程序都会在我分离后无法恢复。

简单的测试用例是一个程序如下:

#!/usr/bin/env ruby

counter = 0
loop do
  puts "#{counter}\n"
  counter += 1
  sleep 1
end

然后我通过gdb -p PID将gdb附加到进程,并在gdb控制台上运行p rb_eval_string("$stderr.puts(\"hi\\n\")")。这导致分离后失败。如果我运行p rb_eval_string("a = 1")并分离,则ruby进程将恢复正常。 如果我尝试写入文件或$stdout而发生同样的问题。

程序无法恢复时的回溯如下所示:

(gdb) bt
#0  0x00007fcfb6617414 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib/x86_64-linux-gnu/libpthread.so.0
#1  0x0000000000543773 in native_cond_wait (mutex=0x15fcf30, cond=<optimized out>) at thread_pthread.c:309
#2  gvl_acquire_common (vm=0x15fcf20) at thread_pthread.c:64
#3  gvl_acquire (th=0x15fd520, vm=0x15fcf20) at thread_pthread.c:82
#4  native_sleep (timeout_tv=<synthetic pointer>, th=0x15fd520) at thread_pthread.c:918

我在Ubuntu Trusty上使用gdb版本7.7和ruby 1.9.3。

有人可以建议如何让程序恢复吗?

谢谢!

1 个答案:

答案 0 :(得分:0)

我还不明白发生了什么,但可能是因为Ruby的sleep以及rb_thread_sleep使用native_sleep的方式(我可以在本地重现您的问题)。

而不是使用sleep我只考虑使用高成本的方法,这将消耗几乎一秒钟,所以我决定只使用未经优化的fibonacci: - )

以下是代码:

# fibo.rb
def fibonacci(n)
  return n if (0..1).include?(n)
  fibonacci(n-1) + fibonacci(n-2)
end

i = 0
loop do
  fibonacci(30)
  i += 1
  puts "#### i = #{i}"
end

现在,你应该能够(请注意我使用的是lldb而不是gdb,但它是一样的(几乎是:-)):

➜  ~  lldb ruby
Current executable set to 'ruby' (x86_64).
(lldb) r foo.rb
Process 41134 launched: '/Users/xxx/.rubies/ruby-2.1.0/bin/ruby' (x86_64)
#### i = 1
Process 41134 stopped
* thread #1: tid = 0x72502, 0x00000001000c4820 ruby`range_include + 256, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
    frame #0: 0x00000001000c4820 ruby`range_include + 256
ruby`range_include + 256:
-> 0x1000c4820:  movq   0x16a691(%rip), %rsi      ; id_cmp
   0x1000c4827:  movq   %rbx, %rdi
   0x1000c482a:  movl   $0x1, %edx
   0x1000c482f:  movq   %r12, %rcx
(lldb) call (void)rb_eval_string("puts 42")
42
(lldb) c
Process 41134 resuming
#### i = 2
#### i = 3
...