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