For some reason generating a thread dump for running Ruby scripts is not a built-in feature and you have to write a few lines of your own code to get that functionality. Fortunately it is quite straightforward and easy (as most of the things in Ruby).

Thread Dump? Why?

Finding out the problem why some script eats away all your CPU and does not end might be tricky if not using proper tools. Thread dump might be your lifesaver in these situations. Thread dump shows you basically what every thread does at any given moment giving you a rough overview of where your script is executing. Consider the following contrived script as an example:

def condition true end def looping while condition # do something end end looping

It’s not hard to notice that this script won’t ever end. In real life the situation might not be that easy and it would be helpful to know somehow during runtime what that script is actually doing.

Dumping All The Threads

When it comes to dumping all the threads then the easiest way would be to set up a signal handler which dumps out all the information for threads. This code can do that when pressing ctrl+c or issuing `kill -INT ` on the command line:

trap 'INT' do Thread.list.each do |thread| STDERR.puts "Thread-#{thread.object_id.to_s(36)}" STDERR.puts thread.backtrace.join("

\\_ ") end end

Just put that script into the top of your script and press ctrl+c whenever you want to see the backtrace. This is what i’m seeing for the script above:

Thread-9jnxc looping.rb:17:in `block (2 levels) in ' \_ looping.rb:15:in `each' \_ looping.rb:15:in `block in ' \_ looping.rb:22:in `call' \_ looping.rb:22:in `condition' \_ looping.rb:26:in `looping' \_ looping.rb:31:in `'

From this output it’s relatively easy to see where that script is blocking. Don’t forget to take a thread dump multiple times to see if the script is always doing similar things.

NoMethodError for Thread#backtrace

If you’re using Ruby 1.8 then the script above won’t work for you, because Thread does not have #backtrace method. This method exists in 1.9. For older Ruby versions there is a different solution, which is not as great because it works only for the current thread. Since majority of Ruby programs are single-threaded then this might not be a big problem. Here’s the script for Ruby 1.8:

trap 'INT' do STDERR.puts "Current thread: #{Thread.inspect}" STDERR.puts caller.join("

\\_ ") end

One For All Solution

Combining these two scripts into one is a straightforward process:

trap 'INT' do if RUBY_VERSION =~ /^1\.8\./ STDERR.puts "Current thread: #{Thread.inspect}" STDERR.puts caller.join("

\\_ ") else Thread.list.each do |thread| STDERR.puts "Thread-#{thread.object_id.to_s(36)}" STDERR.puts thread.backtrace.join("

\\_ ") end end end

Wait! Exiting Script Does Not Work Anymore!

Since we’re handling INT (interrupt) signal then it’s not possible to exit with ctrl+c anymore. No worries we can restore that functionality support relatively easily too. Something like this will do the trick:

interrupted = false trap 'INT' do exit Signal.list["INT"] if interrupted interrupted = true if RUBY_VERSION =~ /^1\.8\./ STDERR.puts "Current thread: #{Thread.inspect}" STDERR.puts caller.join("

\\_ ") else Thread.list.each do |thread| STDERR.puts "Thread-#{thread.object_id.to_s(36)}" STDERR.puts thread.backtrace.join("

\\_ ") end end puts "Press Ctrl+C again to exit..." sleep 1 interrupted = false end

Pressing ctrl+c twice in one second will exit your script as before. Happy Thread-Dumping!