Debugger plugin, which can be used to profile memory usage in rbot and detect memory...
[rbot] / data / rbot / plugins / debugger.rb
1 # Debugging/profiling for rbot
2 #
3 # (c) 2006 Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
4 # Licensed under GPL V2.
5
6 class DebugPlugin < Plugin
7   BotConfig.register BotConfigIntegerValue.new('debug.interval',
8     :default => 10, :validate => Proc.new{|v| v > 0},
9     :desc => "Number of seconds between memory profile dumps")
10   BotConfig.register BotConfigBooleanValue.new('debug.dump_strings',
11     :default => false,
12     :desc => "Set to true if you want the profiler to dump strings, false otherwise")
13   BotConfig.register BotConfigStringValue.new('debug.logdir',
14     :default => "",
15     :desc => "Directory where profile/string dumps are to be stored")
16
17   def initialize
18     super
19     @prev = Hash.new(0)
20     @curr = Hash.new(0)
21     @delta = Hash.new(0)
22     @file = File.open("#{@bot.botclass}/#{@bot.config['debug.logdir']}/memory_profiler.log",'w')
23     @thread = @bot.timer.add(@bot.config['debug.interval']) {
24         begin
25           GC.start
26           @curr.clear
27
28           curr_strings = []
29
30           ObjectSpace.each_object do |o|
31             @curr[o.class] += 1 #Marshal.dump(o).size rescue 1
32             if @bot.config['debug.dump_strings'] and o.class == String
33               curr_strings.push o
34             end
35           end
36
37           if @bot.config['debug.dump_strings']
38             File.open("#{@bot.botclass}/#{@bot.config['debug.logdir']}/memory_profiler_strings.log.#{Time.now.to_i}",'w') do |f|
39               curr_strings.sort.each do |s|
40                 f.puts s
41               end
42             end
43             curr_strings.clear
44           end
45
46           @delta.clear
47           (@curr.keys + @delta.keys).uniq.each do |k,v|
48             @delta[k] = @curr[k]-@prev[k]
49           end
50
51           @file.puts "Top 20"
52           @delta.sort_by { |k,v| -v.abs }[0..19].sort_by { |k,v| -v}.each do |k,v|
53             @file.printf "%+5d: %s (%d)\n", v, k.name, @curr[k] unless v == 0
54           end
55           @file.flush
56
57           @delta.clear
58           @prev.clear
59           @prev.update @curr
60           GC.start
61         rescue Exception => err
62           error "** memory_profiler error: #{err}"
63         end
64     }
65     @bot.timer.block(@thread)
66   end
67
68   def help( plugin, topic="" )
69       "debug start => start the periodic profiler; " + \
70       "debug stop => stops the periodic profiler; " + \
71       "debug dumpstrings => dump all of the strings"
72   end
73
74   def start_it(m, params)
75     begin
76       @bot.timer.unblock(@thread)
77       m.reply "profile dump started"
78     rescue Exception => err
79       m.reply "couldn't start profile dump"
80       error "couldn't start profile dump: #{err}"
81     end
82   end
83
84   def stop_it(m, params)
85     begin
86       @bot.timer.block(@thread)
87       m.reply "profile dump stop"
88     rescue Exception => err
89       m.reply "couldn't stop profile dump"
90       error "couldn't stop profile dump: #{err}"
91     end
92   end
93
94   def dump_strings(m, params)
95     curr_strings = []
96
97     m.reply "Dumping strings ..."
98     begin
99       GC.start
100       ObjectSpace.each_object do |o|
101         if o.class == String
102           curr_strings.push o
103         end
104       end
105
106       File.open("#{@bot.botclass}/#{@bot.config['debug.logdir']}/memory_profiler_strings.log.#{Time.now.to_i}",'w') do |f|
107         curr_strings.sort.each do |s|
108           f.puts s
109         end
110       end
111       GC.start
112       m.reply "... done"
113     rescue Exception => err
114       m.reply "dumping strings failed"
115       error "dumping strings failed: #{err}"
116     end
117   end
118
119 end
120
121
122 plugin = DebugPlugin.new
123 plugin.register( "debug" )
124 plugin.default_auth( 'start', false )
125 plugin.default_auth( 'stop', false )
126 plugin.default_auth( 'dumpstrings', false )
127
128 plugin.map 'debug start', :action => 'start_it'
129 plugin.map 'debug stop', :action => 'stop_it'
130 plugin.map 'debug dumpstrings', :action => 'dump_strings'
131
132