summaryrefslogtreecommitdiff
path: root/data/rbot/plugins/debugger.rb
blob: fe000324e8383118dd898d54588e05f3172321ba (plain)
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 + @prev.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'