module Sigdump VERSION = "0.2.0" def self.setup(signal=ENV['SIGDUMP_SIGNAL'] || 'SIGCONT', path=ENV['SIGDUMP_PATH']) Kernel.trap(signal) do begin dump(path) rescue end end end def self.dump(path=ENV['SIGDUMP_PATH']) _open_dump_path(path) do |io| io.write "Sigdump at #{Time.now} process #{Process.pid} (#{$0})\n" dump_all_thread_backtrace(io) dump_object_count(io) dump_gc_profiler_result(io) end end def self.dump_all_thread_backtrace(io) Thread.list.each do |thread| dump_backtrace(thread, io) end nil end def self.dump_backtrace(thread, io) status = thread.status if status == nil status = "finished" elsif status == false status = "error" end io.write " Thread #{thread} status=#{status} priority=#{thread.priority}\n" thread.backtrace.each {|bt| io.write " #{bt}\n" } io.flush nil end def self.dump_object_count(io) io.write " Built-in objects:\n" ObjectSpace.count_objects.sort_by {|k,v| -v }.each {|k,v| io.write "%10s: %s\n" % [_fn(v), k] } string_size = 0 array_size = 0 hash_size = 0 cmap = {} ObjectSpace.each_object {|o| c = o.class cmap[c] = (cmap[c] || 0) + 1 if c == String string_size += o.bytesize elsif c == Array array_size = o.size elsif c == Hash hash_size = o.size end } io.write " All objects:\n" cmap.sort_by {|k,v| -v }.each {|k,v| io.write "%10s: %s\n" % [_fn(v), k] } io.write " String #{_fn(string_size)} bytes\n" io.write " Array #{_fn(array_size)} elements\n" io.write " Hash #{_fn(hash_size)} pairs\n" io.flush nil end def self.dump_gc_profiler_result(io) return unless defined?(GC::Profiler) && GC::Profiler.enabled? io.write " GC profiling result:\n" io.write " Total garbage collection time: %f\n" % GC::Profiler.total_time io.write GC::Profiler.result GC::Profiler.clear io.flush nil end def self._fn(num) s = num.to_s if formatted = s.gsub!(/(\d)(?=(?:\d{3})+(?!\d))/, "\\1,") formatted else s end end private_class_method :_fn def self._open_dump_path(path, &block) case path when nil, "" path = "/tmp/sigdump-#{Process.pid}.log" File.open(path, "a", &block) when IO yield path when "-" yield STDIN when "+" yield STDERR else File.open(path, "a", &block) end end private_class_method :_open_dump_path end