2006-03-25 16:15:30 -05:00
|
|
|
require 'logger'
|
|
|
|
require 'set'
|
2006-03-26 15:01:50 -05:00
|
|
|
require 'socket'
|
2006-03-25 16:15:30 -05:00
|
|
|
|
|
|
|
$mongrel_debugging=true
|
|
|
|
|
|
|
|
module MongrelDbg
|
|
|
|
SETTINGS = { :tracing => {}}
|
|
|
|
LOGGING = { }
|
|
|
|
|
2006-03-26 15:01:50 -05:00
|
|
|
def MongrelDbg::configure(log_dir = "log/mongrel_debug")
|
2006-03-25 16:15:30 -05:00
|
|
|
Dir.mkdir(log_dir) if not File.exist?(log_dir)
|
|
|
|
@log_dir = log_dir
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def MongrelDbg::trace(target, message)
|
2006-03-26 15:01:50 -05:00
|
|
|
if SETTINGS[:tracing][target] and LOGGING[target]
|
2006-03-25 16:15:30 -05:00
|
|
|
LOGGING[target].log(Logger::DEBUG, message)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def MongrelDbg::begin_trace(target)
|
|
|
|
SETTINGS[:tracing][target] = true
|
|
|
|
if not LOGGING[target]
|
|
|
|
LOGGING[target] = Logger.new(File.join(@log_dir, "#{target.to_s}.log"))
|
|
|
|
end
|
|
|
|
MongrelDbg::trace(target, "TRACING ON #{Time.now}")
|
|
|
|
end
|
|
|
|
|
|
|
|
def MongrelDbg::end_trace(target)
|
|
|
|
SETTINGS[:tracing][target] = false
|
|
|
|
MongrelDbg::trace(target, "TRACING OFF #{Time.now}")
|
|
|
|
LOGGING[target].close
|
|
|
|
LOGGING[target] = nil
|
|
|
|
end
|
2006-03-26 15:01:50 -05:00
|
|
|
|
|
|
|
def MongrelDbg::tracing?(target)
|
|
|
|
SETTINGS[:tracing][target]
|
|
|
|
end
|
2006-03-25 16:15:30 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
module ObjectTracker
|
|
|
|
@active_objects = nil
|
2006-03-26 15:01:50 -05:00
|
|
|
@live_object_tracking = true
|
2006-03-25 16:15:30 -05:00
|
|
|
|
|
|
|
def ObjectTracker.configure
|
|
|
|
@active_objects = Set.new
|
2006-03-26 15:01:50 -05:00
|
|
|
|
2006-03-25 16:15:30 -05:00
|
|
|
ObjectSpace.each_object do |obj|
|
|
|
|
@active_objects << obj.object_id
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2006-03-26 15:01:50 -05:00
|
|
|
|
2006-03-25 16:15:30 -05:00
|
|
|
def ObjectTracker.start
|
|
|
|
@live_object_tracking = true
|
|
|
|
end
|
|
|
|
|
|
|
|
def ObjectTracker.stop
|
|
|
|
@live_object_tracking = false
|
|
|
|
end
|
|
|
|
|
|
|
|
def ObjectTracker.sample
|
2006-03-26 15:01:50 -05:00
|
|
|
Class.stopit do
|
|
|
|
ospace = Set.new
|
|
|
|
counts = {}
|
|
|
|
|
|
|
|
# Strings can't be tracked easily and are so numerous that they drown out all else
|
|
|
|
# so we just ignore them in the counts.
|
|
|
|
ObjectSpace.each_object do |obj|
|
|
|
|
if not obj.kind_of? String
|
|
|
|
ospace << obj.object_id
|
|
|
|
counts[obj.class] ||= 0
|
|
|
|
counts[obj.class] += 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
dead_objects = @active_objects - ospace
|
|
|
|
new_objects = ospace - @active_objects
|
|
|
|
live_objects = ospace & @active_objects
|
|
|
|
|
|
|
|
MongrelDbg::trace(:objects, "COUNTS: #{dead_objects.length},#{new_objects.length},#{live_objects.length}")
|
|
|
|
|
|
|
|
if MongrelDbg::tracing? :objects
|
|
|
|
top_20 = counts.sort{|a,b| b[1] <=> a[1]}[0..20]
|
|
|
|
MongrelDbg::trace(:objects,"TOP 20: #{top_20.inspect}")
|
|
|
|
end
|
|
|
|
|
|
|
|
@active_objects = live_objects + new_objects
|
|
|
|
|
|
|
|
[@active_objects, top_20]
|
2006-03-25 16:15:30 -05:00
|
|
|
end
|
2006-03-26 15:01:50 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
$open_files = {}
|
2006-03-25 16:15:30 -05:00
|
|
|
|
2006-03-26 15:01:50 -05:00
|
|
|
class IO
|
|
|
|
alias_method :orig_open, :open
|
|
|
|
alias_method :orig_close, :close
|
|
|
|
|
|
|
|
def open(*arg, &blk)
|
|
|
|
$open_files[self] = args.inspect
|
|
|
|
orig_open(*arg,&blk)
|
2006-03-25 16:15:30 -05:00
|
|
|
end
|
|
|
|
|
2006-03-26 15:01:50 -05:00
|
|
|
def close(*arg,&blk)
|
|
|
|
$open_files.delete self
|
|
|
|
orig_close(*arg,&blk)
|
|
|
|
end
|
2006-03-25 16:15:30 -05:00
|
|
|
end
|
|
|
|
|
2006-03-26 15:01:50 -05:00
|
|
|
|
|
|
|
module Kernel
|
|
|
|
alias_method :orig_open, :open
|
|
|
|
|
|
|
|
def open(*arg, &blk)
|
|
|
|
$open_files[self] = arg[0]
|
|
|
|
orig_open(*arg,&blk)
|
|
|
|
end
|
|
|
|
|
|
|
|
def log_open_files
|
|
|
|
Class.stopit do
|
|
|
|
open_counts = {}
|
|
|
|
$open_files.each do |f,args|
|
|
|
|
open_counts[args] ||= 0
|
|
|
|
open_counts[args] += 1
|
|
|
|
end
|
|
|
|
MongrelDbg::trace(:files, open_counts.to_yaml)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-03-25 16:15:30 -05:00
|
|
|
class Class
|
|
|
|
alias_method :orig_new, :new
|
|
|
|
|
|
|
|
@@count = 0
|
2006-03-26 15:01:50 -05:00
|
|
|
@@stopit = false
|
2006-03-25 16:15:30 -05:00
|
|
|
@@class_caller_count = Hash.new{|hash,key| hash[key] = Hash.new(0)}
|
|
|
|
|
|
|
|
def new(*arg,&blk)
|
2006-03-26 15:01:50 -05:00
|
|
|
unless @@stopit
|
|
|
|
@@stopit = true
|
2006-03-25 16:15:30 -05:00
|
|
|
@@count += 1
|
2006-03-26 15:01:50 -05:00
|
|
|
@@class_caller_count[self][caller.join("\n\t")] += 1
|
|
|
|
@@stopit = false
|
2006-03-25 16:15:30 -05:00
|
|
|
end
|
|
|
|
orig_new(*arg,&blk)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2006-03-26 15:01:50 -05:00
|
|
|
def Class.report_object_creations(out=$stderr, more_than=20)
|
|
|
|
Class.stopit do
|
|
|
|
out.puts "Number of objects created = #{@@count}"
|
|
|
|
|
|
|
|
total = Hash.new(0)
|
|
|
|
|
|
|
|
@@class_caller_count.each_key do |klass|
|
|
|
|
caller_count = @@class_caller_count[klass]
|
|
|
|
caller_count.each_value do |count|
|
|
|
|
total[klass] += count
|
|
|
|
end
|
2006-03-25 16:15:30 -05:00
|
|
|
end
|
2006-03-26 15:01:50 -05:00
|
|
|
|
|
|
|
klass_list = total.keys.sort{|klass_a, klass_b|
|
|
|
|
a = total[klass_a]
|
|
|
|
b = total[klass_b]
|
|
|
|
if a != b
|
|
|
|
-1* (a <=> b)
|
|
|
|
else
|
|
|
|
klass_a.to_s <=> klass_b.to_s
|
|
|
|
end
|
|
|
|
}
|
|
|
|
|
|
|
|
below_count = 0
|
|
|
|
|
|
|
|
klass_list.each do |klass|
|
|
|
|
below_calls = 0
|
|
|
|
if total[klass] > more_than
|
|
|
|
out.puts "#{total[klass]}\t#{klass} objects created."
|
|
|
|
caller_count = @@class_caller_count[ klass]
|
|
|
|
caller_count.keys.sort_by{|call| -1*caller_count[call]}.each do |call|
|
|
|
|
if caller_count[call] > more_than
|
|
|
|
out.puts "\t** #{caller_count[call]} #{klass} objects AT:"
|
|
|
|
out.puts "\t#{call}\n\n"
|
|
|
|
else
|
|
|
|
below_calls += 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
out.puts "\t#{below_calls} more objects had calls less that #{more_than} limit.\n\n" if below_calls > 0
|
|
|
|
else
|
|
|
|
below_count += 1
|
|
|
|
end
|
2006-03-25 16:15:30 -05:00
|
|
|
end
|
2006-03-26 15:01:50 -05:00
|
|
|
|
|
|
|
out.puts "\t** #{below_count} More objects were created but the count was below the #{more_than} limit." if below_count > 0
|
2006-03-25 16:15:30 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def Class.reset_object_creations
|
2006-03-26 15:01:50 -05:00
|
|
|
Class.stopit do
|
|
|
|
@@count = 0
|
|
|
|
@@class_caller_count = Hash.new{|hash,key| hash[key] = Hash.new(0)}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def Class.stopit
|
2006-03-25 16:15:30 -05:00
|
|
|
@@stopit = true
|
2006-03-26 15:01:50 -05:00
|
|
|
yield
|
|
|
|
@@stopit = false
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
module RequestLog
|
|
|
|
class Files < GemPlugin::Plugin "/handlers"
|
|
|
|
include Mongrel::HttpHandlerPlugin
|
|
|
|
|
|
|
|
def process(request, response)
|
|
|
|
MongrelDbg::trace(:files, "#{Time.now} FILES OPEN BEFORE REQUEST #{request.params['PATH_INFO']}")
|
|
|
|
log_open_files
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
class Objects < GemPlugin::Plugin "/handlers"
|
|
|
|
include Mongrel::HttpHandlerPlugin
|
|
|
|
|
|
|
|
def process(request, response)
|
|
|
|
MongrelDbg::trace(:objects, "#{Time.now} OBJECT STATS BEFORE REQUEST #{request.params['PATH_INFO']}")
|
|
|
|
ObjectTracker.sample
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
class Params < GemPlugin::Plugin "/handlers"
|
|
|
|
include Mongrel::HttpHandlerPlugin
|
|
|
|
|
|
|
|
def process(request, response)
|
|
|
|
MongrelDbg::trace(:rails, "#{Time.now} REQUEST #{request.params['PATH_INFO']}")
|
|
|
|
MongrelDbg::trace(:rails, request.params.to_yaml)
|
|
|
|
end
|
|
|
|
|
2006-03-25 16:15:30 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2006-03-26 15:01:50 -05:00
|
|
|
END {
|
|
|
|
open("log/mongrel_debug/object_tracking.log", "w") {|f| Class.report_object_creations(f) }
|
|
|
|
MongrelDbg::trace(:files, "FILES OPEN AT EXIT")
|
|
|
|
log_open_files
|
|
|
|
}
|