# # monitor.rb - performance monitor # # Copyright (C) 2004-2005 Satoru Takabayashi # All rights reserved. # This is free software with ABSOLUTELY NO WARRANTY. # # You can redistribute it and/or modify it under the terms of # the GNU General Public License version 2. # module Gonzui class PerformanceMonitor @@performance_counters = {} def initialize @start_time = Time.now @elapsed_time = nil @finished_p = false end private def finish @elapsed_time = Time.now - @start_time @finished_p = true end def build_label(m, name) case m when Class m.to_s + "#" + name.to_s when Module m.to_s + "." + name.to_s end end public def self.[] (label) @@performance_counters[label] end def empty? @@performance_counters.empty? end def profile(m, name) label = build_label(m, name) if !@@performance_counters[label] @@performance_counters[label] = PerformanceCounter.new case m when Class eval <<"End" class #{m} alias orig_#{name} #{name} @@pc_#{name} = Gonzui::PerformanceMonitor["#{label}"] # redefine with profiler def #{name}(*args, &block) @@pc_#{name}.enter begin orig_#{name}(*args, &block) ensure @@pc_#{name}.leave end end end End when Module eval <<"End" module #{m} alias orig_#{name} #{name} module_function :orig_#{name} # redefine with profiler def #{name}(*args, &block) pc = Gonzui::PerformanceMonitor["#{label}"] pc.enter begin orig_#{name}(*args, &block) ensure pc.leave end end module_function :#{name} end End end end end def heading return Gonzui::PerformanceCounter.heading end def format(primary_label, *labels) finish unless @finished_p label = build_label(*primary_label) primary_pc = Gonzui::PerformanceMonitor[label] return "" if primary_pc.nil? summary = primary_pc.summary(label, 1, @elapsed_time) labels.each {|label| label = build_label(*label) pc = Gonzui::PerformanceMonitor[label] next if pc.nil? summary << pc.summary(label, 2, @elapsed_time) primary_pc.exclude(pc) } summary << primary_pc.rest_summary(:other, 2, @elapsed_time) summary << "\n" return summary end end class PerformanceCounter def initialize @count = 0 @time_enter = 0 @time_total = 0 @time_subtotal = 0 @times_enter = 0 @times_total = Struct::Tms.new(0, 0, 0, 0) @times_subtotal = Struct::Tms.new(0, 0, 0, 0) end def enter @time_enter = Time.now @times_enter = Process.times @count += 1 end def leave @time_total += Time.now - @time_enter times = Process.times @times_total.utime += times.utime - @times_enter.utime @times_total.stime += times.stime - @times_enter.stime end def time @time_total end def utime @times_total.utime end def stime @times_total.stime end def exclude(pc) @time_subtotal += pc.time @times_subtotal.utime += pc.utime @times_subtotal.stime += pc.stime end def self.heading sprintf("%-32s %8s %6s %6s %6s\n", "", "count", "utime", "stime", "real") end def summary(label, indent, elapsed) sprintf("%-32s %8d %6.2fs %6.2fs %6.2fs (%6.2f%%)\n", " " * indent + label, @count, @times_total.utime, @times_total.stime, @time_total, @time_total * 100 / elapsed) end def rest_summary(label, indent, elapsed) time = @time_total - @time_subtotal utime = @times_total.utime - @times_subtotal.utime stime = @times_total.stime - @times_subtotal.stime sprintf("%-32s %8s %6.2fs %6.2fs %6.2fs (%6.2f%%)\n", " " * indent + label.to_s, '', utime, stime, time, time * 100 / elapsed) end end end