Skip to content

Commit

Permalink
Dump heap from a fork
Browse files Browse the repository at this point in the history
Dumping the heap can lock the VM for a very long time.
If you attempt to do this on a webserver process with a timeout
it's very likely you'll hit it and the process might get killed
before the dump completes.

Worse, if the process was processing some requests or jobs, they might
timeout because of it.

Using fork we can make a snapshot of the heap in a very short time
(relative to the dump itself) and savely dump without disrupting the
original process.

If you are familiar with how Redis does sanopshoting, it's very similar.
  • Loading branch information
byroot committed Feb 1, 2023
1 parent 326800f commit 3e03fd3
Showing 1 changed file with 26 additions and 2 deletions.
28 changes: 26 additions & 2 deletions lib/rbtrace/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,19 @@ def self.run
temp.unlink
end

tracer.eval("file = File.open('#{filename}', 'w'); ObjectSpace.dump_all(output: file); file.close")
tracer.eval(<<-RUBY)
Thread.new do
Thread.current.name = '__RBTrace__'
pid = ::Process.fork do
file = File.open('#{filename}.tmp', 'w')
ObjectSpace.dump_all(output: file)
file.close
File.rename('#{filename}.tmp', '#{filename}')
exit!(0)
end
Process.waitpid(pid)
end
RUBY
puts "Heapdump being written to #{filename}"

elsif opts[:shapesdump_given]
Expand All @@ -517,7 +529,19 @@ def self.run
temp.unlink
end

tracer.eval("file = File.open('#{filename}', 'w'); ObjectSpace.dump_shapes(output: file); file.close")
tracer.eval(<<-RUBY)
Thread.new do
Thread.current.name = '__RBTrace__'
pid = ::Process.fork do
file = File.open('#{filename}.tmp', 'w')
ObjectSpace.dump_shapes(output: file)
file.close
File.rename('#{filename}.tmp', '#{filename}')
exit!(0)
end
Process.waitpid(pid)
end
RUBY
puts "Shapes dump being written to #{filename}"

elsif opts[:eval_given]
Expand Down

0 comments on commit 3e03fd3

Please sign in to comment.