bleak_house 3.0.1 → 3.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,29 +1,8 @@
1
1
 
2
- v3.0.1. Rewrite. Remove Gruffs and graphs. Become a standard compiled extension. Walk the symbol table. Track object histories. Use custom CSV format instead of YAML.
2
+ BleakHouse Changelog
3
3
 
4
- v2.7. Version renumbering; apologies if this messes anyone up.
5
-
6
- v2.6. RDoc documentation; use new Echoe setup; fix for namespaced controllers.
7
-
8
- v2.5. Detect if core keys are present or not; fixes for analyzing non-Rails apps.
9
-
10
- v2.4. Log heap usage; remove pure-Ruby profiler.
11
-
12
- v2.3. Subtract Rails core counts from action counts and join paired frames.
13
-
14
- v2.2. Change smoothing algorithm to be more intuitive and so mem usage is correct.
15
-
16
- v2.1. Close file properly.
17
-
18
- v2. C instrumentation with YAML output.
19
-
20
- v1.3. Log memory usage on *nix.
21
-
22
- v1.2. Fix nil directory traversal error on Windows.
23
-
24
- v1.1. Gem version
25
-
26
- v1. Dumped Rublique; replaced with a lightweight non-delta marshalling version.
27
-
28
- v0. First version
4
+ 3.1. fix nil directory traversal error on windows
5
+ 3. gem version
6
+ 2. dumped rublique once I understood what it did; replaced with a lightweight non-delta marshalling version
7
+ 1. first version
29
8
 
data/Manifest CHANGED
@@ -1,30 +1,18 @@
1
- TODO
2
- test/unit/test_bleak_house.rb
3
- test/misc/direct.rb
4
- README
5
- Rakefile
6
- patches/parse.y.patch
7
- patches/gc.c.patch
8
- Manifest
9
- LICENSE_RUBY
10
- LICENSE_BSD
11
- LICENSE
12
- lib/vendor/lightcsv.rb
13
- lib/bleak_house.rb
14
- lib/bleak_house/support/rake.rb
15
- lib/bleak_house/support/core_extensions.rb
16
- lib/bleak_house/rails.rb
17
- lib/bleak_house/rails/dispatcher.rb
18
- lib/bleak_house/rails/bleak_house.rb
19
- lib/bleak_house/rails/action_controller.rb
20
- lib/bleak_house/logger.rb
21
- lib/bleak_house/logger/mem_usage.rb
22
- lib/bleak_house/analyzer.rb
23
- lib/bleak_house/analyzer/analyzer.rb
24
- install.rb
25
- init.rb
26
- ext/bleak_house/logger/snapshot.h
27
- ext/bleak_house/logger/snapshot.c
28
- ext/bleak_house/logger/extconf.rb
29
- CHANGELOG
30
- bin/bleak
1
+ ./CHANGELOG
2
+ ./LICENSE
3
+ ./Manifest
4
+ ./README
5
+ ./Rakefile
6
+ ./init.rb
7
+ ./install.rb
8
+ ./lib/bleak_house
9
+ ./lib/bleak_house/action_controller.rb
10
+ ./lib/bleak_house/analyze.rb
11
+ ./lib/bleak_house/bleak_house.rb
12
+ ./lib/bleak_house/dispatcher.rb
13
+ ./lib/bleak_house/gruff_hacks.rb
14
+ ./lib/bleak_house/mem_logger.rb
15
+ ./lib/bleak_house/rake_task_redefine_task.rb
16
+ ./lib/bleak_house/support_methods.rb
17
+ ./lib/bleak_house.rb
18
+ ./tasks/bleak_house_tasks.rake
data/README CHANGED
@@ -1,73 +1,20 @@
1
+ -----------------------------------------------------------------
1
2
 
2
3
  BleakHouse
4
+ You need the 'gruff' gem, and 'rmagick', and all that.
3
5
 
4
- A library for finding memory leaks.
6
+ To run:
7
+ RAILS_ENV=production BLEAK_HOUSE=true INTERVAL=5 ./script/server
8
+ Browse around in your app. Cause requests. Do stuff. Then:
9
+ RAILS_ENV=production rake bleak_house:analyze
10
+ And then look in log/bleak_house/.
5
11
 
6
- == License
7
-
8
- Copyright 2007 Cloudburst, LLC. See the included LICENSE file. Portions copyright 2006 Eric Hodel and used with permission. See the included LICENSE_BSD file. Portions copyright 2007 TOMITA Masahiro and used with permission. See the included LICENSE_RUBY file.
9
-
10
- The public certificate for this gem is at http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem.
11
-
12
- == Features
13
-
14
- * leak-proof C instrumentation
15
- * fast logging
16
- * complete tracking of object and symbol history
17
- * easy Rails integration
18
-
19
- == Requirements
20
-
21
- * A unix-like operating system
22
- * Ruby 1.8.6
23
-
24
- = Usage
25
-
26
- == Installation
27
-
28
- Install the gem:
29
- sudo gem install bleak_house -v 3.0.1
30
-
31
- You need to compile the BleakHouse patched Ruby build. In the plugin directory, run:
32
- rake ruby:build
33
-
34
- This gives you a Ruby binary named <tt>ruby-bleak-house</tt> alongside your regular binary.
35
-
36
- == Profiling a Rails app
37
-
38
- To setup a Rails app for profiling, just <tt>require 'bleak_house'</tt> in <tt>config/environment.rb</tt>.
39
-
40
- Then, to engage the logger (ideally, in a live deployment situation), start a server instance as so:
41
- RAILS_ENV=production BLEAK_HOUSE=true ruby-bleak-house ./script/server
42
-
43
- Browse around manually, thrash your entire app with a script, target troublesome controllers/actions, etc. The BleakHouse logger will dump a huge amount of data to <tt>log/bleak_house_production.dump</tt>--keep an eye on your disk space.
44
-
45
- Then, to analyze the dump:
46
- bleak path/to/log/bleak_house_production.dump
12
+ Copyright (c) 2007 Cloudburst, LLC. See included LICENSE file. Portions
13
+ copyright Tony Arcieri and Scott Barron and used under license.
47
14
 
48
- Be patient.
49
-
50
- = Extras
51
-
52
- == Using BleakHouse outside of Rails
53
-
54
- Just instantiate a BleakHouse::Logger object, and make calls to <tt>snapshot</tt> at appropriate times.
55
-
56
- == Troubleshooting
57
-
58
- If you see the error <tt>Symbol not found: _rb_gc_heaps_used</tt>, it means you installed the patched binary, but tried to profile the server with the regular Ruby binary.
59
-
60
- You may get library require errors if you install <tt>ruby-bleak-house</tt> 1.8.6 alongside a different verson of Ruby. You could try to patch your local version of Ruby instead, or you could just upgrade to 1.8.6, which has a good trackrecord of stability.
61
-
62
- == Reporting problems
63
-
64
- * http://rubyforge.org/forum/forum.php?forum_id=13983
65
-
66
- Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC.
67
-
68
- == Further resources
15
+ I was such a shy little thing that I seldom dared to open my lips, and never
16
+ dared to open my heart, to anybody else.
17
+ - Dickens, Bleak House
69
18
 
70
- * http://blog.evanweaver.com/articles/2007/05/12/let-me-hit-you-with-some-knowledge
71
- * http://blog.evanweaver.com/articles/2007/05/06/leak-proof-direct-heap-instrumentation-for-bleak_house
72
- * http://blog.evanweaver.com/articles/2007/04/28/bleak_house
19
+ -----------------------------------------------------------------
73
20
 
data/Rakefile CHANGED
@@ -1,73 +1,66 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'lib/bleak_house/rake_task_redefine_task'
1
4
 
2
- require 'lib/bleak_house/support/rake'
5
+ NAME = "bleak_house"
3
6
 
4
7
  begin
8
+ require 'rake/clean'
9
+ gem 'echoe', '>= 1.1'
5
10
  require 'echoe'
11
+ require 'fileutils'
12
+
13
+ AUTHOR = "Evan Weaver"
14
+ EMAIL = "evan at cloudbur dot st"
15
+ DESCRIPTION = "BleakHouse is a Rails plugin for finding memory leaks. It tracks ObjectSpace for your entire app, and produces charts of references by controller, by action, and by object class."
16
+ CHANGES = `cat CHANGELOG`[/^([\d\.]+\. .*)/, 1]
17
+ RUBYFORGE_NAME = "fauna"
18
+ GEM_NAME = "bleak_house"
19
+ HOMEPATH = "http://blog.evanweaver.com"
20
+ RELEASE_TYPES = ["gem"]
21
+ REV = nil
22
+ VERS = `cat CHANGELOG`[/^([\d\.]+)\. /, 1]
23
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
24
+ RDOC_OPTS = ['--quiet', '--title', "bleak_house documentation", "--opname", "index.html", "--line-numbers", "--main", "README", "--inline-source"]
25
+
26
+ include FileUtils
27
+
28
+ taskmsg = File.open(File.dirname(__FILE__) + "/tasks/bleak_house_tasks.rake").readlines
29
+ taskmsg = taskmsg[0..3] + [taskmsg[7][2..-1]] + taskmsg[9..-1]
6
30
 
7
- Echoe.new("bleak_house") do |p|
8
- p.author = "Evan Weaver"
9
- p.project = "fauna"
10
- p.summary = "A library for finding memory leaks."
11
- p.url = "http://blog.evanweaver.com/files/doc/fauna/bleak_house/"
12
- p.docs_host = 'blog.evanweaver.com:~/www/bax/public/files/doc/'
13
- p.rdoc_pattern = /^ext.*\.c|lib.*logger.*rb|analyzer|rails\/bleak_house|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
14
- p.dependencies = ['rake']
15
- p.require_signed = true
16
- p.include_gemspec = false
17
- p.include_rakefile = true
31
+ echoe = Echoe.new(GEM_NAME, VERS) do |p|
32
+ p.author = AUTHOR
33
+ p.rubyforge_name = RUBYFORGE_NAME
34
+ p.name = NAME
35
+ p.description = DESCRIPTION
36
+ p.changes = CHANGES
37
+ p.email = EMAIL
38
+ p.summary = DESCRIPTION
39
+ p.url = HOMEPATH
40
+ p.need_tar = false
41
+ p.need_tar_gz = true
42
+ p.test_globs = ["*_test.rb"]
43
+ p.clean_globs = CLEAN
44
+ p.spec_extras = {:post_install_message =>
45
+ "
46
+ Thanks for installing Bleak House #{VERS}.
47
+
48
+ For each Rails app you want to profile, you will need to add the following
49
+ rake task in RAILS_ROOT/lib/tasks/bleak_house_tasks.rake to be able to run
50
+ the analyzer:
51
+ " + taskmsg.join(" ") + "\n"}
18
52
  end
19
-
20
- rescue LoadError
53
+
54
+ rescue LoadError => boom
55
+ puts "You are missing a dependency required for meta-operations on this gem."
56
+ puts "#{boom.to_s.capitalize}."
57
+
58
+ desc 'Run the default tasks'
59
+ task :default => :test
21
60
  end
22
61
 
23
- desc 'Run tests.'
62
+ desc 'Do nothing.'
24
63
  Rake::Task.redefine_task("test") do
25
- system "ruby-bleak-house -Ibin:lib:test test/unit/test_bleak_house.rb #{ENV['METHOD'] ? "--name=#{ENV['METHOD']}" : ""}"
64
+ puts "There are no tests. You could totally write some, though."
65
+ # system "ruby -Ibin:lib:test test/unit/polymorph_test.rb #{ENV['METHOD'] ? "--name=#{ENV['METHOD']}" : ""}"
26
66
  end
27
-
28
- desc 'Build a patched binary.'
29
- namespace :ruby do
30
- task :build do
31
- if RUBY_PLATFORM =~ /win32|windows/
32
- puts "ERROR: windows not supported."
33
- exit
34
- end
35
- require 'fileutils'
36
- puts "building patched Ruby binary"
37
- tmp = "/tmp/"
38
- Dir.chdir(tmp) do
39
- build_dir = "bleak_house"
40
- binary_dir = File.dirname(`which ruby`)
41
- FileUtils.rm_rf(build_dir) rescue nil
42
- Dir.mkdir(build_dir)
43
- begin
44
- Dir.chdir(build_dir) do
45
- puts " downloading Ruby source"
46
- bz2 = "ruby-1.8.6.tar.bz2"
47
- system("wget 'http://rubyforge.org/frs/download.php/18434/ruby-1.8.6.tar.bz2' &> wget.log")
48
- puts " extracting"
49
- system("tar xjf #{bz2} &> tar.log")
50
- File.delete bz2
51
- Dir.chdir("ruby-1.8.6") do
52
- puts " patching"
53
- system("patch -p0 < \'#{File.dirname(__FILE__)}/patches/gc.c.patch\' &> ../gc.c.patch.log")
54
- system("patch -p0 < \'#{File.dirname(__FILE__)}/patches/parse.y.patch\' &> ../parse.y.patch.log")
55
- puts " configuring"
56
- system("./configure --prefix=#{binary_dir[0..-5]} &> ../configure.log") # --with-static-linked-ext
57
- puts " making"
58
- system("make &> ../make.log")
59
-
60
- binary = "#{binary_dir}/ruby-bleak-house"
61
- puts " installing as #{binary}"
62
- FileUtils.cp("./ruby", binary)
63
- FileUtils.chmod(0755, binary)
64
- puts " done"
65
- end
66
- end
67
- rescue Object => e
68
- puts "ERROR: please see the last modified log file in #{tmp}#{build_dir}, perhaps\nit will contain a clue."
69
- end
70
- end
71
- end
72
- end
73
-
data/init.rb CHANGED
@@ -1,3 +1,2 @@
1
1
 
2
- puts "Please use the gem version of BleakHouse from now on."
3
- exit!
2
+ require 'bleak_house'
data/install.rb CHANGED
@@ -1,2 +1 @@
1
-
2
- puts "Please use the gem version of BleakHouse from now on."
1
+ puts File.open("#{File.dirname(__FILE__)}/README").read
@@ -1,15 +1,14 @@
1
1
 
2
- # Override ActionController::Base.process and process_with_exception to make sure the request tag for the snapshot gets set as a side-effect of request processing.
3
2
  class ActionController::Base
4
3
  class << self
5
4
  def process_with_bleak_house(request, *args)
6
- BleakHouse::Rails.set_request_name request
5
+ BleakHouse.set_request_name request
7
6
  process_without_bleak_house(request, *args)
8
7
  end
9
8
  alias_method_chain :process, :bleak_house
10
9
 
11
10
  def process_with_exception_with_bleak_house(request, *args)
12
- BleakHouse::Rails.set_request_name request, "/error"
11
+ BleakHouse.set_request_name request, "/error"
13
12
  process_with_exception_without_bleak_house(request, *args)
14
13
  end
15
14
  alias_method_chain :process_with_exception, :bleak_house
@@ -0,0 +1,139 @@
1
+
2
+ require 'rubygems'
3
+ require 'fileutils'
4
+ require 'base64'
5
+
6
+ gem 'gruff', '= 0.2.8'
7
+ require 'gruff'
8
+
9
+ # require, but make rdoc not whine
10
+ load "#{File.dirname(__FILE__)}/gruff_hacks.rb"
11
+ load "#{File.dirname(__FILE__)}/support_methods.rb"
12
+
13
+ Gruff::Base::LEFT_MARGIN = 200
14
+ Gruff::Base::NEGATIVE_TOP_MARGIN = 30
15
+ Gruff::Base::MAX_LEGENDS = 28
16
+
17
+ class BleakHouse
18
+ class Analyze
19
+
20
+ DIR = "#{RAILS_ROOT}/log/bleak_house/"
21
+
22
+ def initialize(data, increments, name)
23
+ @data = data
24
+ @increments = increments
25
+ @name = name
26
+ end
27
+
28
+ def draw
29
+ g = Gruff::Line.new("1024x768")
30
+ g.title = @name
31
+ g.x_axis_label = "time"
32
+ g.legend_font_size = g.legend_box_size = 14
33
+ g.title_font_size = 24
34
+ g.marker_font_size = 14
35
+
36
+ @data.map do |key, values|
37
+ name = if key.to_s == ""
38
+ '[Unknown]'
39
+ else
40
+ # elsif key =~ Regexp.new(@specials.keys.join('|'))
41
+ # name = "#{key} (#{values.to_i.max / (2**10)} MB)"
42
+ # @specials.each do |regex, scale|
43
+ # values.map! {|x| x * scale} if key =~ regex
44
+ # end
45
+ # else
46
+ "#{key.gsub(/.*::/, '')} (#{values.to_i.max})"
47
+ end
48
+ [name, values]
49
+ end.sort_by do |key, values|
50
+ 0 - key[/.*?([\d]+)\)$/, 1].to_i
51
+ end.each do |key, values|
52
+ g.data(key, values.to_i)
53
+ end
54
+
55
+ labels = {}
56
+ mod = (@increments.size / 4.0).ceil
57
+ @increments.each_with_index do |increment, index|
58
+ labels[index] = increment.split(" ").last if (index % mod).zero?
59
+ end
60
+ g.labels = labels
61
+
62
+ g.minimum_value = 0
63
+ # g.maximum_value = @maximum
64
+
65
+ g.write(@name.to_filename + ".png")
66
+ end
67
+
68
+ def self.aggregate(data, selector, namer)
69
+ aggregate_data = {}
70
+ increments = []
71
+ data.each_with_index do |frameset, index|
72
+ increments << frameset.time
73
+ frameset.data.keys.select do |key|
74
+ key =~ selector #or key =~ Regexp.new(specials.keys.join('|'))
75
+ end.each do |key|
76
+ aggregate_data[key.to_s[namer, 1]] ||= []
77
+ aggregate_data[key.to_s[namer, 1]][index] += frameset.data[key].to_i
78
+ end
79
+ end
80
+ [aggregate_data, increments]
81
+ end
82
+
83
+ def self.build_all(filename)
84
+ unless File.exists? filename
85
+ puts "No data file found: #{filename}"
86
+ exit
87
+ end
88
+ FileUtils.rm_r(DIR) rescue nil
89
+ Dir.mkdir(DIR)
90
+
91
+ Dir.chdir(DIR) do
92
+
93
+ puts "Parsing data"
94
+ data = File.open(filename).readlines.map do |line|
95
+ Marshal.load Base64.decode64(line)
96
+ end
97
+
98
+ # data_maximum = data.flatten.inject(0) do |max, el|
99
+ # if el.is_a? Hash
100
+ # current_max = el.merge({:"real memory" => 0, :"virtual memory" => 0}).values.to_i.max
101
+ # current_max if max < current_max
102
+ # end or max
103
+ # end
104
+ # mem_maximum = data.flatten.inject(0) do |max, el| # only real memory (RSS) for now
105
+ # (el["real memory"] if el.is_a?(Hash) and max < el["real memory"]) or max
106
+ # end
107
+ # mem_scale = data_maximum / mem_maximum.to_f
108
+ # scales = {/memory$/ => mem_scale}
109
+
110
+ puts "By controller"
111
+ controller_data, increments = aggregate(data, //, /^(.*?)($|\/|::::)/)
112
+ Analyze.new(controller_data, increments, "objects by controller").draw
113
+
114
+ # in each controller, by action
115
+ puts "By action"
116
+ controller_data.keys.each do |controller|
117
+ puts " ...in #{controller} controller"
118
+ Dir.descend(controller) do
119
+ action_data, increments = aggregate(data, /^#{controller}($|\/|::::)/, /\/(.*?)($|\/|::::)/)
120
+ Analyze.new(action_data, increments, "objects by action in /#{controller}").draw
121
+
122
+ # in each action, by object class
123
+ action_data.keys.each do |action|
124
+ action = "unknown" if action.to_s == ""
125
+ puts " ...in #{action} action"
126
+ Dir.descend(action) do
127
+ class_data, increments = aggregate(data, /^#{controller}#{"\/#{action}" unless action == "unknown"}($|\/|::::)/,
128
+ /::::(.*)/)
129
+ Analyze.new(class_data, increments, "objects by class in /#{controller}/#{action}").draw
130
+ end
131
+ end
132
+
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,33 @@
1
+
2
+ class BleakHouse
3
+ cattr_accessor :last_request_name
4
+ cattr_accessor :dispatch_count
5
+ cattr_accessor :log_interval
6
+
7
+ self.dispatch_count = 0
8
+ self.log_interval = (ENV['INTERVAL'] and ENV['INTERVAL'].to_i or 10)
9
+
10
+ def self.set_request_name request, other = nil
11
+ self.last_request_name = "#{request.parameters['controller']}/#{request.parameters['action']}/#{request.request_method}#{other}"
12
+ end
13
+
14
+ def self.debug s
15
+ s = "#{name.underscore}: #{s}"
16
+ ::ActiveRecord::Base.logger.debug s if ::ActiveRecord::Base.logger
17
+ end
18
+
19
+ def self.warn s
20
+ s = "#{name.underscore}: #{s}"
21
+ if ::ActiveRecord::Base.logger
22
+ ::ActiveRecord::Base.logger.warn s
23
+ else
24
+ $stderr.puts s
25
+ end
26
+ end
27
+
28
+ LOGFILE = "#{RAILS_ROOT}/log/bleak_house_#{RAILS_ENV}.dump"
29
+ if File.exists?(LOGFILE)
30
+ File.rename(LOGFILE, "#{LOGFILE}.old")
31
+ warn "renamed old logfile"
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+
2
+ # crazyness
3
+
4
+ class Dispatcher
5
+ class << self
6
+ def prepare_application_with_bleak_house
7
+ prepare_application_without_bleak_house
8
+ BleakHouse.dispatch_count += 1
9
+ BleakHouse::MemLogger.snapshot('core rails')
10
+ end
11
+ alias_method_chain :prepare_application, :bleak_house
12
+
13
+ def reset_after_dispatch_with_bleak_house
14
+ BleakHouse::MemLogger.snapshot(BleakHouse.last_request_name || 'unknown')
15
+ if (BleakHouse.dispatch_count % BleakHouse.log_interval).zero?
16
+ BleakHouse.warn "wrote frameset (#{BleakHouse.dispatch_count} dispatches)"
17
+ BleakHouse::MemLogger.log(BleakHouse::LOGFILE)
18
+ end
19
+ reset_after_dispatch_without_bleak_house
20
+ end
21
+ alias_method_chain :reset_after_dispatch, :bleak_house
22
+ end
23
+ end
@@ -0,0 +1,56 @@
1
+
2
+ class Gruff::Base
3
+
4
+ def draw_legend
5
+ @legend_labels = @data.collect {|item| item[DATA_LABEL_INDEX] }
6
+
7
+ legend_square_width = @legend_box_size # small square with color of this item
8
+ legend_left = 10
9
+
10
+ current_x_offset = legend_left
11
+ current_y_offset = TOP_MARGIN + TITLE_MARGIN + @title_caps_height + LEGEND_MARGIN
12
+
13
+ debug { @d.line 0.0, current_y_offset, @raw_columns, current_y_offset }
14
+
15
+ @legend_labels.each_with_index do |legend_label, index|
16
+
17
+ next if index > MAX_LEGENDS
18
+ legend_label = "Some not shown" if index == MAX_LEGENDS
19
+
20
+ # Draw label
21
+ @d.fill = @font_color
22
+ @d.font = @font if @font
23
+ @d.pointsize = scale_fontsize(@legend_font_size)
24
+ @d.stroke('transparent')
25
+ @d.font_weight = NormalWeight
26
+ @d.gravity = WestGravity
27
+ @d = @d.annotate_scaled( @base_image,
28
+ @raw_columns, 1.0,
29
+ current_x_offset + (legend_square_width * 1.7), current_y_offset,
30
+ legend_label.to_s, @scale)
31
+
32
+ if index < MAX_LEGENDS
33
+ # Now draw box with color of this dataset
34
+ @d = @d.stroke('transparent')
35
+ @d = @d.fill @data[index][DATA_COLOR_INDEX]
36
+ @d = @d.rectangle(current_x_offset,
37
+ current_y_offset - legend_square_width / 2.0,
38
+ current_x_offset + legend_square_width,
39
+ current_y_offset + legend_square_width / 2.0)
40
+ end
41
+
42
+ @d.pointsize = @legend_font_size
43
+ metrics = @d.get_type_metrics(@base_image, legend_label.to_s)
44
+ current_y_offset += metrics.height * 1.05
45
+ end
46
+ @color_index = 0
47
+ end
48
+
49
+ alias :setup_graph_measurements_without_top_margin :setup_graph_measurements
50
+ def setup_graph_measurements
51
+ setup_graph_measurements_without_top_margin
52
+ @graph_height += NEGATIVE_TOP_MARGIN
53
+ @graph_top -= NEGATIVE_TOP_MARGIN
54
+ end
55
+
56
+ end
@@ -0,0 +1,54 @@
1
+
2
+ require 'base64'
3
+
4
+ class BleakHouse::MemLogger
5
+ class << self
6
+
7
+ SEEN = {}
8
+ CURRENT = {}
9
+ TAGS = Hash.new(0)
10
+ TIMEFORMAT = '%Y-%m-%d %H:%M:%S'
11
+ VSZ = "virtual memory"
12
+ RSS = "real memory"
13
+
14
+ def log(path, with_mem = false)
15
+ File.open(path, 'a+') do |log|
16
+ log.sync = true
17
+ TAGS[VSZ], TAGS[RSS] = mem_usage if with_mem
18
+ log.puts Base64.encode64(Marshal.dump([Time.now.strftime(TIMEFORMAT), TAGS])).gsub("\n", '')
19
+ end
20
+ GC.start
21
+ end
22
+
23
+ def snapshot(tag)
24
+ # puts "bleak_house: seen: #{SEEN.size}"
25
+ CURRENT.clear
26
+ ObjectSpace.each_object do |obj|
27
+ CURRENT[obj_id = obj._bleak_house_object_id] = true
28
+ unless SEEN[obj_id]
29
+ # symbols will rapidly stabilize; don't worry
30
+ SEEN[obj_id] = "#{tag}::::#{obj._bleak_house_class}".to_sym
31
+ TAGS[SEEN[obj_id]] += 1
32
+ end
33
+ end
34
+ # puts "bleak_house: current: #{CURRENT.size}"
35
+ SEEN.keys.each do |obj_id|
36
+ TAGS[SEEN.delete(obj_id)] -= 1 unless CURRENT[obj_id]
37
+ end
38
+ # puts "bleak_house: done"
39
+ CURRENT.clear
40
+ GC.start
41
+ end
42
+
43
+ def mem_usage
44
+ `ps -x -o 'rss vsz' -p #{Process.pid}`.split(/\s+/)[-2..-1].map{|el| el.to_i}
45
+ end
46
+
47
+ end
48
+ end
49
+
50
+ class Object
51
+ alias :_bleak_house_object_id :object_id
52
+ alias :_bleak_house_class :class
53
+ end
54
+
@@ -1,6 +1,4 @@
1
1
 
2
- require 'set'
3
-
4
2
  class Array
5
3
  alias :time :first
6
4
  alias :data :last
@@ -15,24 +13,6 @@ class Array
15
13
 
16
14
  end
17
15
 
18
- class Hash
19
-
20
- # Similar to the ActiveSupport methods in Rails
21
- def slice(keys)
22
- keys = Set.new(keys)
23
- reject do |key,|
24
- !keys.include?(key)
25
- end
26
- end
27
-
28
- def unslice(keys)
29
- keys = Set.new(keys)
30
- reject do |key,|
31
- keys.include?(key)
32
- end
33
- end
34
- end
35
-
36
16
  class Dir
37
17
  def self.descend path, &block
38
18
  path = path.split("/") unless path.is_a? Array
@@ -64,7 +44,4 @@ class Symbol
64
44
  def =~ regex
65
45
  self.to_s =~ regex
66
46
  end
67
- def [](*args)
68
- self.to_s[*args]
69
- end
70
47
  end