Table of Contents [expand]
Last updated January 23, 2026
When a Ruby program doesn’t crash but hangs indefinitely or becomes very slow, it can be difficult to debug. Add a sampling profiler to see where the program hangs or slows down.
You can add a crude sampling profiler to any Ruby application. Spin up a thread in the background and report the backtrace of all executing threads. For example, if your application hangs during deployment, add this code to an initializer in config/initializers/sampling_profiler.rb:
Thread.new do
loop do
sleep 10 # seconds
puts "=" * 80;
Thread.list.each.with_index { |t, i| puts "== Thread #{i}"; puts t.backtrace }
end
end
In this example, the location of all executing code prints out every 10 seconds. The location gives you a hint of where the program hangs or slows down.
After isolating that code, you can add additional debugging statements or attempt to remediate the problem directly.
Multi-process debugging
If the problem involves multiple processes, the above code might produce interleaved output where it’s difficult to determine what each process is executing. One option is to use an advisory file mutex to coordinate output between processes:
require "tempfile"
require "stringio"
file_mutex = Tempfile.new(["print_process_output_mutex", ".lock"])
Thread.new do
loop do
sleep 10
buffer = StringIO.new
buffer.puts "=" * 80
buffer.puts "== PID: #{Process.pid}"
Thread.list.each.with_index do |t, i|
buffer.puts "== Thread #{i}"
buffer.puts t.backtrace
end
# Prevent interleaved output from multiple processes
File.open(file_mutex.path, 'w') do |f|
f.flock(File::LOCK_EX) # exclusive advisory lock, closed when file closes
puts buffer.string # print to stdout
end
end
end