195 lines
5.2 KiB
Ruby
Executable File
195 lines
5.2 KiB
Ruby
Executable File
#
|
|
# File:: memory_profiler.rb
|
|
# Description::
|
|
#
|
|
# Author:: David Muir <david.muir@rockstarnorth.com>
|
|
# Date:: 9 April 2009
|
|
#
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Uses
|
|
#----------------------------------------------------------------------------
|
|
# None
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Implementation
|
|
#----------------------------------------------------------------------------
|
|
module Pipeline
|
|
module Util
|
|
|
|
#
|
|
# == Description
|
|
#
|
|
# === Example Usage
|
|
#
|
|
# === References
|
|
# The original which this profiler code is based can be found at:
|
|
# http://scottstuff.net/blog/articles/2006/08/17/memory-leak-profiling-with-rails
|
|
#
|
|
class MemoryProfiler
|
|
DEFAULTS = {
|
|
:delay => 10,
|
|
:string_debug => true,
|
|
:log_file => 'x:\memory_profiler.log',
|
|
:string_log_file => 'x:\memory_profiler_strings.log'
|
|
}
|
|
|
|
UNIT_BYTES = 1
|
|
UNIT_KILOBYTES = 1024
|
|
UNIT_MEGABYTES = (1024*1024)
|
|
|
|
def MemoryProfiler::stop( )
|
|
@@running = false
|
|
@@profiler_thread.join( )
|
|
end
|
|
|
|
def MemoryProfiler::current( )
|
|
@@current
|
|
end
|
|
|
|
def MemoryProfiler::previous( )
|
|
@@previous
|
|
end
|
|
|
|
def MemoryProfiler::display( sort_key = :size, max_num = 20, io = STDOUT, units = UNIT_KILOBYTES )
|
|
begin
|
|
io.puts "#{Time.now.strftime( '%Y/%m/%d %H:%M:%S' )}"
|
|
io.puts "Current Top #{max_num}"
|
|
io.puts
|
|
io.puts "sortkey #{sort_key.class} #{sort_key.to_s}"
|
|
current().sort_by { |k,v| -v[sort_key].abs }[0..max_num-1].sort_by { |k,v| -v[sort_key]}.each do |k,v|
|
|
io.printf "%+8d%s: %s (%d)\n", v[:size]/units, UNIT_STRINGS[units], k.name, current()[k][:count] unless v == 0
|
|
end
|
|
|
|
80.times do io.print( '-' ); end
|
|
io.puts
|
|
io.flush
|
|
rescue Exception => ex
|
|
STDERR.puts "Unhandled exception: #{ex.message}"
|
|
STDERR.puts ex.backtrace.join( "\n" )
|
|
end
|
|
end
|
|
|
|
def MemoryProfiler::capture()
|
|
current().clear( )
|
|
@@curr_strings = [] if @@opt[:string_debug]
|
|
|
|
ObjectSpace.each_object do |o|
|
|
current()[o.class] = { } unless ( current().has_key?( o.class ) )
|
|
current()[o.class][:count] = 0 unless ( current()[o.class].has_key?( :count ) )
|
|
current()[o.class][:size] = 0 unless ( current()[o.class].has_key?( :size ) )
|
|
current()[o.class][:count] += 1
|
|
current()[o.class][:size] += Marshal.dump(o).size rescue 1
|
|
if ( @@opt[:string_debug] and ( String == o.class ) )
|
|
@@curr_strings.push o
|
|
end
|
|
end
|
|
end
|
|
|
|
def MemoryProfiler::write_string_debug( )
|
|
if ( @@opt[:string_debug] )
|
|
File.open( "#{@@opt[:string_log_file]}.#{Time.now.to_i}", 'w' ) do |f|
|
|
@@curr_strings.sort.each do |s|
|
|
f.puts s
|
|
end
|
|
end
|
|
@@curr_strings.clear
|
|
end
|
|
end
|
|
|
|
def MemoryProfiler::calc_deltas( delta )
|
|
delta.clear( )
|
|
current().keys.each do |k,v|
|
|
delta[k] = { } unless ( delta.has_key?( k ) )
|
|
delta[k][:count] = 0 unless ( delta[k].has_key?( :count ) )
|
|
delta[k][:size] = 0 unless ( delta[k].has_key?( :size ) )
|
|
delta[k][:size] = ( current()[k][:size] - previous()[k][:size] ) if ( previous().has_key?( k ) )
|
|
delta[k][:size] = current()[k][:size] unless ( previous().has_key?( k ) )
|
|
delta[k][:count] = ( current()[k][:count] - previous()[k][:count] ) if ( previous().has_key?( k ) )
|
|
delta[k][:count] = current()[k][:count] unless ( previous().has_key?( k ) )
|
|
end
|
|
end
|
|
|
|
def MemoryProfiler::snapshot( sort_key = :size )
|
|
MemoryProfiler::capture( )
|
|
MemoryProfiler::display( sort_key )
|
|
end
|
|
|
|
#
|
|
#
|
|
#
|
|
def MemoryProfiler::start( opt = {} )
|
|
opt = DEFAULTS.dup.merge(opt)
|
|
|
|
@@opt = opt
|
|
@@running = true
|
|
@@profiler_thread = Thread.new do
|
|
Thread.current[:name] = PROFILER_THREAD_NAME
|
|
@@previous = { }
|
|
@@current = { }
|
|
@@curr_strings = []
|
|
delta = { }
|
|
begin
|
|
file = File::open( opt[:log_file], 'w' )
|
|
rescue Exception => err
|
|
STDERR.puts "** memory_profiler file open error: #{err}"
|
|
STDERR.puts err.backtrace.join( "\n" )
|
|
end
|
|
|
|
while ( @@running ) do
|
|
puts "Log memory to #{file.path}"
|
|
begin
|
|
GC.start( )
|
|
|
|
MemoryProfiler::capture( )
|
|
|
|
MemoryProfiler::write_string_debug( )
|
|
|
|
MemoryProfiler::calc_deltas( delta )
|
|
|
|
MemoryProfiler::display( :size, 20 , file )
|
|
|
|
delta.clear
|
|
previous().clear
|
|
previous().update current()
|
|
GC.start( )
|
|
rescue Exception => err
|
|
STDERR.puts "** memory_profiler error: #{err}"
|
|
STDERR.puts err.backtrace.join( "\n" )
|
|
end
|
|
sleep opt[:delay]
|
|
end
|
|
end
|
|
end
|
|
|
|
#--------------------------------------------------------------------
|
|
# Private
|
|
#--------------------------------------------------------------------
|
|
private
|
|
PROFILER_THREAD_NAME = '__MemoryProfiler_Thread'
|
|
UNIT_STRINGS = {
|
|
UNIT_BYTES => 'B',
|
|
UNIT_KILOBYTES => 'KB',
|
|
UNIT_MEGABYTES => 'MB'
|
|
}
|
|
end
|
|
|
|
end # Util module
|
|
end # Pipeline module
|
|
|
|
|
|
if ( __FILE__ == $0 ) then
|
|
|
|
|
|
Pipeline::Util::MemoryProfiler::start( )
|
|
s = 'STRING STRING 1'
|
|
s = 'STRIN GSTRING 2'
|
|
s = 'STRING STRING 1'
|
|
while true do
|
|
s = 'STRIN GSTRING 2'
|
|
end
|
|
Pipeline::Util::MemoryProfiler::stop( )
|
|
end
|
|
|
|
# memory_profiler.rb
|