module Sigdump

Constants

VERSION

Public Class Methods

dump(path=ENV['SIGDUMP_PATH']) click to toggle source
# File lib/sigdump.rb, line 13
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_gc_stat(io)
    dump_object_count(io)
    dump_gc_profiler_result(io)
  end
end
dump_all_thread_backtrace(io) click to toggle source
# File lib/sigdump.rb, line 23
def self.dump_all_thread_backtrace(io)
  use_java_bean = defined?(Thread.current.to_java.getNativeThread.getId)
  if use_java_bean
    begin
      bean = java.lang.management.ManagementFactory.getThreadMXBean
      java_stacktrace_map = Hash[bean.getThreadInfo(bean.getAllThreadIds, true, true).map {|t| [t.getThreadId, t.toString] }]
    rescue
      # security error may happen
    end
  end
  Thread.list.each do |thread|
    dump_backtrace(thread, io)
    if java_stacktrace_map
      io.write "    In Java " + java_stacktrace_map[thread.to_java.getNativeThread.getId]
      io.flush
    end
  end
  nil
end
dump_backtrace(thread, io) click to toggle source
# File lib/sigdump.rb, line 43
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
dump_gc_profiler_result(io) click to toggle source
# File lib/sigdump.rb, line 110
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
dump_gc_stat(io) click to toggle source
# File lib/sigdump.rb, line 99
def self.dump_gc_stat(io)
  io.write "  GC stat:\n"

  GC.stat.each do |key, val|
    io.write "      #{key}: #{val}\n"
  end

  io.flush
  nil
end
dump_object_count(io) click to toggle source
# File lib/sigdump.rb, line 60
def self.dump_object_count(io)
  if defined?(ObjectSpace.count_objects)
    # ObjectSpace doesn't work in JRuby

    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
  end
  nil
end
setup(signal=ENV['SIGDUMP_SIGNAL'] || 'SIGCONT', path=ENV['SIGDUMP_PATH']) click to toggle source
# File lib/sigdump.rb, line 4
def self.setup(signal=ENV['SIGDUMP_SIGNAL'] || 'SIGCONT', path=ENV['SIGDUMP_PATH'])
  Kernel.trap(signal) do
    begin
      dump(path)
    rescue
    end
  end
end

Private Class Methods

_fn(num) click to toggle source
# File lib/sigdump.rb, line 122
def self._fn(num)
  s = num.to_s
  if formatted = s.gsub!(/(\d)(?=(?:\d{3})+(?!\d))/, "\\1,")
    formatted
  else
    s
  end
end
_open_dump_path(path) { |path| ... } click to toggle source
# File lib/sigdump.rb, line 132
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 STDOUT
  when "+"
    yield STDERR
  else
    File.open(path, "a", &block)
  end
end