bleak_house 3.0.1 → 3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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