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