bleak_house 3 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +26 -4
- data/LICENSE_BSD +10 -0
- data/LICENSE_RUBY +53 -0
- data/Manifest +30 -18
- data/README +66 -13
- data/Rakefile +63 -56
- data/TODO +2 -0
- data/bin/bleak +10 -0
- data/ext/bleak_house/logger/extconf.rb +4 -0
- data/ext/bleak_house/logger/snapshot.c +153 -0
- data/ext/bleak_house/logger/snapshot.h +99 -0
- data/init.rb +2 -1
- data/install.rb +2 -1
- data/lib/bleak_house/analyzer/analyzer.rb +143 -0
- data/lib/bleak_house/analyzer.rb +5 -0
- data/lib/bleak_house/logger/mem_usage.rb +13 -0
- data/lib/bleak_house/logger.rb +3 -0
- data/lib/bleak_house/{action_controller.rb → rails/action_controller.rb} +3 -2
- data/lib/bleak_house/rails/bleak_house.rb +58 -0
- data/lib/bleak_house/rails/dispatcher.rb +19 -0
- data/lib/bleak_house/rails.rb +6 -0
- data/lib/bleak_house/{support_methods.rb → support/core_extensions.rb} +24 -1
- data/lib/bleak_house/{rake_task_redefine_task.rb → support/rake.rb} +0 -0
- data/lib/bleak_house.rb +4 -10
- data/lib/vendor/lightcsv.rb +168 -0
- data/patches/gc.c.patch +27 -0
- data/patches/parse.y.patch +16 -0
- data/test/misc/direct.rb +13 -0
- data/test/unit/test_bleak_house.rb +34 -0
- data.tar.gz.sig +3 -0
- metadata +77 -50
- metadata.gz.sig +0 -0
- data/lib/bleak_house/analyze.rb +0 -139
- data/lib/bleak_house/bleak_house.rb +0 -33
- data/lib/bleak_house/dispatcher.rb +0 -23
- data/lib/bleak_house/gruff_hacks.rb +0 -56
- data/lib/bleak_house/mem_logger.rb +0 -54
- data/tasks/bleak_house_tasks.rake +0 -13
data/CHANGELOG
CHANGED
@@ -1,7 +1,29 @@
|
|
1
1
|
|
2
|
-
|
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.
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
7
29
|
|
data/LICENSE_BSD
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
Copyright 2006, Eric Hodel. All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
8
|
+
3. Neither the names of the authors nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
9
|
+
|
10
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS?? AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/LICENSE_RUBY
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
$BK\%W%m%0%i%`$O%U%j!<%=%U%H%&%'%"$G$9!%(BGPL(the GNU General
|
2
|
+
Public License)$B$^$?$O0J2<$K<($9>r7o$GK\%W%m%0%i%`$r:FG[I[$G(B
|
3
|
+
$B$-$^$9!%(BGPL$B$K$D$$$F$O(BCOPYING.txt$B%U%!%$%k$r;2>H$7$F2<$5$$!%(B
|
4
|
+
|
5
|
+
1. $BJ#@=$O@)8B$J$/<+M3$G$9!%(B
|
6
|
+
|
7
|
+
2. $B0J2<$N>r7o$N$$$:$l$+$rK~$?$9;~$KK\%W%m%0%i%`$N%=!<%9$r(B
|
8
|
+
$B<+M3$KJQ99$G$-$^$9!%(B
|
9
|
+
|
10
|
+
(a) $B%M%C%H%K%e!<%:$K%]%9%H$7$?$j!$:n<T$KJQ99$rAwIU$9$k(B
|
11
|
+
$B$J$I$NJ}K!$G!$JQ99$r8x3+$9$k!%(B
|
12
|
+
|
13
|
+
(b) $BJQ99$7$?K\%W%m%0%i%`$r<+J,$N=jB0$9$kAH?%FbIt$@$1$G(B
|
14
|
+
$B;H$&!%(B
|
15
|
+
|
16
|
+
(c) $BJQ99E@$rL@<($7$?$&$(!$%=%U%H%&%'%"$NL>A0$rJQ99$9$k!%(B
|
17
|
+
$B$=$N%=%U%H%&%'%"$rG[I[$9$k;~$K$OJQ99A0$NK\%W%m%0%i(B
|
18
|
+
$B%`$bF1;~$KG[I[$9$k!%$^$?$OJQ99A0$NK\%W%m%0%i%`$N%=!<(B
|
19
|
+
$B%9$NF~<jK!$rL@<($9$k!%(B
|
20
|
+
|
21
|
+
(d) $B$=$NB>$NJQ99>r7o$r:n<T$H9g0U$9$k!%(B
|
22
|
+
|
23
|
+
3. $B0J2<$N>r7o$N$$$:$l$+$rK~$?$9;~$KK\%W%m%0%i%`$r%3%s%Q%$(B
|
24
|
+
$B%k$7$?%*%V%8%'%/%H%3!<%I$d<B9T7A<0$G$bG[I[$G$-$^$9!%(B
|
25
|
+
|
26
|
+
(a) $B%P%$%J%j$r<u$1<h$C$??M$,%=!<%9$rF~<j$G$-$k$h$&$K!$(B
|
27
|
+
$B%=!<%9$NF~<jK!$rL@<($9$k!%(B
|
28
|
+
|
29
|
+
(b) $B5!3#2DFI$J%=!<%9%3!<%I$rE:IU$9$k!%(B
|
30
|
+
|
31
|
+
(c) $BJQ99$r9T$C$?%P%$%J%j$OL>A0$rJQ99$7$?$&$(!$%*%j%8%J(B
|
32
|
+
$B%k$N%=!<%9%3!<%I$NF~<jK!$rL@<($9$k!%(B
|
33
|
+
|
34
|
+
(d) $B$=$NB>$NG[I[>r7o$r:n<T$H9g0U$9$k!%(B
|
35
|
+
|
36
|
+
4. $BB>$N%W%m%0%i%`$X$N0zMQ$O$$$+$J$kL\E*$G$"$l<+M3$G$9!%$?(B
|
37
|
+
$B$@$7!$K\%W%m%0%i%`$K4^$^$l$kB>$N:n<T$K$h$k%3!<%I$O!$$=(B
|
38
|
+
$B$l$>$l$N:n<T$N0U8~$K$h$k@)8B$,2C$($i$k>l9g$,$"$j$^$9!%(B
|
39
|
+
|
40
|
+
$B6qBNE*$K$O(Bgc.c($B0lIt(B)$B!$(Butil.c($B0lIt(B)$B!$(Bst.[ch]$B!$(Bregex.[ch]
|
41
|
+
$B$*$h$S(B ./missing$B%G%#%l%/%H%j2<$N%U%!%$%k72$,3:Ev$7$^$9!%(B
|
42
|
+
$B$=$l$>$l$NG[I[>r7o$J$I$KIU$$$F$O3F%U%!%$%k$r;2>H$7$F$/(B
|
43
|
+
$B$@$5$$!%(B
|
44
|
+
|
45
|
+
5. $BK\%W%m%0%i%`$X$NF~NO$H$J$k%9%/%j%W%H$*$h$S!$K\%W%m%0%i(B
|
46
|
+
$B%`$+$i$N=PNO$N8"Mx$OK\%W%m%0%i%`$N:n<T$G$O$J$/!$$=$l$>(B
|
47
|
+
$B$l$NF~=PNO$r@8@.$7$??M$KB0$7$^$9!%$^$?!$K\%W%m%0%i%`$K(B
|
48
|
+
$BAH$_9~$^$l$k$?$a$N3HD%%i%$%V%i%j$K$D$$$F$bF1MM$G$9!%(B
|
49
|
+
|
50
|
+
6. $BK\%W%m%0%i%`$OL5J]>Z$G$9!%:n<T$OK\%W%m%0%i%`$r%5%]!<%H(B
|
51
|
+
$B$9$k0U;V$O$"$j$^$9$,!$%W%m%0%i%`<+?H$N%P%0$"$k$$$OK\%W(B
|
52
|
+
$B%m%0%i%`$N<B9T$J$I$+$iH/@8$9$k$$$+$J$kB;32$KBP$7$F$b@U(B
|
53
|
+
$BG$$r;}$A$^$;$s!%(B
|
data/Manifest
CHANGED
@@ -1,18 +1,30 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
data/README
CHANGED
@@ -1,20 +1,73 @@
|
|
1
|
-
-----------------------------------------------------------------
|
2
1
|
|
3
2
|
BleakHouse
|
4
|
-
You need the 'gruff' gem, and 'rmagick', and all that.
|
5
3
|
|
6
|
-
|
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/.
|
4
|
+
A library for finding memory leaks.
|
11
5
|
|
12
|
-
|
13
|
-
|
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
|
14
47
|
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
18
69
|
|
19
|
-
|
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
|
20
73
|
|
data/Rakefile
CHANGED
@@ -1,66 +1,73 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'rake'
|
3
|
-
require 'lib/bleak_house/rake_task_redefine_task'
|
4
1
|
|
5
|
-
|
2
|
+
require 'lib/bleak_house/support/rake'
|
6
3
|
|
7
4
|
begin
|
8
|
-
require 'rake/clean'
|
9
|
-
gem 'echoe', '>= 1.1'
|
10
5
|
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]
|
30
6
|
|
31
|
-
|
32
|
-
p.author =
|
33
|
-
p.
|
34
|
-
p.
|
35
|
-
p.
|
36
|
-
p.
|
37
|
-
p.
|
38
|
-
p.
|
39
|
-
p.
|
40
|
-
p.
|
41
|
-
p.
|
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"}
|
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
|
52
18
|
end
|
53
|
-
|
54
|
-
rescue LoadError
|
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
|
19
|
+
|
20
|
+
rescue LoadError
|
60
21
|
end
|
61
22
|
|
62
|
-
desc '
|
23
|
+
desc 'Run tests.'
|
63
24
|
Rake::Task.redefine_task("test") do
|
64
|
-
|
65
|
-
# system "ruby -Ibin:lib:test test/unit/polymorph_test.rb #{ENV['METHOD'] ? "--name=#{ENV['METHOD']}" : ""}"
|
25
|
+
system "ruby-bleak-house -Ibin:lib:test test/unit/test_bleak_house.rb #{ENV['METHOD'] ? "--name=#{ENV['METHOD']}" : ""}"
|
66
26
|
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/TODO
ADDED
data/bin/bleak
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
|
2
|
+
#include "snapshot.h"
|
3
|
+
|
4
|
+
static VALUE rb_mBleakHouse;
|
5
|
+
static VALUE rb_cC;
|
6
|
+
|
7
|
+
/* Number of struct heaps_slots used */
|
8
|
+
static VALUE heaps_used(VALUE self) {
|
9
|
+
return INT2FIX(rb_gc_heaps_used());
|
10
|
+
}
|
11
|
+
|
12
|
+
/* Length of the struct heaps_slots allocated */
|
13
|
+
static VALUE heaps_length(VALUE self) {
|
14
|
+
return INT2FIX(rb_gc_heaps_length());
|
15
|
+
}
|
16
|
+
|
17
|
+
/* Count the live objects on the heap and in the symbol table and write a CSV frame to <tt>_logfile</tt>. Set <tt>_specials = true</tt> if you also want to count AST nodes and var scopes; otherwise, use <tt>false</tt>. Note that common classes in the CSV output are hashed to small integers in order to save space.*/
|
18
|
+
static VALUE snapshot(VALUE self, VALUE _logfile, VALUE tag, VALUE _specials) {
|
19
|
+
Check_Type(_logfile, T_STRING);
|
20
|
+
Check_Type(tag, T_STRING);
|
21
|
+
|
22
|
+
RVALUE *obj, *obj_end;
|
23
|
+
st_table_entry *sym, *sym_end;
|
24
|
+
|
25
|
+
struct heaps_slot * heaps = rb_gc_heap_slots();
|
26
|
+
struct st_table * sym_tbl = rb_parse_sym_tbl();
|
27
|
+
|
28
|
+
int specials = RTEST(_specials);
|
29
|
+
int hashed;
|
30
|
+
|
31
|
+
/* see if the logfile exists already */
|
32
|
+
FILE *logfile = fopen(StringValueCStr(_logfile), "r");
|
33
|
+
int is_new;
|
34
|
+
if (!(is_new = (logfile == NULL)))
|
35
|
+
fclose(logfile);
|
36
|
+
|
37
|
+
/* reopen for writing */
|
38
|
+
if ((logfile = fopen(StringValueCStr(_logfile), "a+")) == NULL)
|
39
|
+
rb_raise(rb_eRuntimeError, "couldn't open snapshot file");
|
40
|
+
|
41
|
+
/* write the time */
|
42
|
+
fprintf(logfile, "-1,%i\n", time(0));
|
43
|
+
|
44
|
+
/* get and write the memory usage */
|
45
|
+
VALUE mem = rb_funcall(self, rb_intern("mem_usage"), 0);
|
46
|
+
fprintf(logfile, "-2,%i\n", NUM2INT(RARRAY_PTR(mem)[0]));
|
47
|
+
fprintf(logfile, "-3,%i\n", NUM2INT(RARRAY_PTR(mem)[1]));
|
48
|
+
|
49
|
+
int current_pos = 0;
|
50
|
+
int filled_slots = 0;
|
51
|
+
int free_slots = 0;
|
52
|
+
|
53
|
+
/* write the tag header */
|
54
|
+
fprintf(logfile, "-4,%s\n", StringValueCStr(tag));
|
55
|
+
|
56
|
+
int i, j;
|
57
|
+
|
58
|
+
/* walk the heap */
|
59
|
+
for (i = 0; i < rb_gc_heaps_used(); i++) {
|
60
|
+
obj = heaps[i].slot;
|
61
|
+
obj_end = obj + heaps[i].limit;
|
62
|
+
for (; obj < obj_end; obj++) {
|
63
|
+
if (obj->as.basic.flags) { /* always 0 for freed objects */
|
64
|
+
filled_slots ++;
|
65
|
+
switch (TYPE(obj)) {
|
66
|
+
case T_NONE:
|
67
|
+
hashed = BUILTINS_SIZE + 0; break;
|
68
|
+
case T_BLKTAG:
|
69
|
+
hashed = BUILTINS_SIZE + 1; break;
|
70
|
+
case T_UNDEF:
|
71
|
+
hashed = BUILTINS_SIZE + 2; break;
|
72
|
+
case T_VARMAP:
|
73
|
+
hashed = BUILTINS_SIZE + 3; break;
|
74
|
+
case T_SCOPE:
|
75
|
+
hashed = BUILTINS_SIZE + 4; break;
|
76
|
+
case T_NODE:
|
77
|
+
hashed = BUILTINS_SIZE + 5; break;
|
78
|
+
default:
|
79
|
+
if (!obj->as.basic.klass) {
|
80
|
+
hashed = BUILTINS_SIZE + 6;
|
81
|
+
} else {
|
82
|
+
hashed = lookup_builtin(rb_obj_classname((VALUE)obj));
|
83
|
+
}
|
84
|
+
}
|
85
|
+
/* write to log */
|
86
|
+
if (hashed < 0) {
|
87
|
+
/* regular classname */
|
88
|
+
fprintf(logfile, "%s,%lu\n", rb_obj_classname((VALUE)obj), FIX2ULONG(rb_obj_id((VALUE)obj)));
|
89
|
+
} else {
|
90
|
+
/* builtins key */
|
91
|
+
if (specials || hashed < BUILTINS_SIZE) {
|
92
|
+
/* 0 is not used for 'hashed' because Ruby's to_i turns any String into 0 */
|
93
|
+
fprintf(logfile, "%i,%lu\n", hashed + 1, FIX2ULONG(rb_obj_id((VALUE)obj)));
|
94
|
+
}
|
95
|
+
}
|
96
|
+
} else {
|
97
|
+
free_slots ++;
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
/* walk the symbol table */
|
103
|
+
hashed = lookup_builtin("Symbol");
|
104
|
+
for (i = 0; i < sym_tbl->num_bins; i++) {
|
105
|
+
for (sym = sym_tbl->bins[i]; sym != 0; sym = sym->next) {
|
106
|
+
fprintf(logfile, "%i,%lu\n", hashed + 1, sym->record);
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
fprintf(logfile, "-5,%i\n", filled_slots);
|
111
|
+
fprintf(logfile, "-6,%i\n", free_slots);
|
112
|
+
fclose(logfile);
|
113
|
+
|
114
|
+
rb_funcall(rb_mGC, rb_intern("start"), 0); /* request GC run */
|
115
|
+
return Qtrue;
|
116
|
+
}
|
117
|
+
|
118
|
+
int lookup_builtin(char * name) {
|
119
|
+
int i;
|
120
|
+
for (i = 0; i < BUILTINS_SIZE; i++) {
|
121
|
+
if (!strcmp(builtins[i], name)) return i;
|
122
|
+
}
|
123
|
+
return -1;
|
124
|
+
}
|
125
|
+
|
126
|
+
/*
|
127
|
+
|
128
|
+
This class performs the actual object logging of BleakHouse. To use it directly, you need to make calls to BleakHouse::Logger#snapshot.
|
129
|
+
|
130
|
+
== Example
|
131
|
+
|
132
|
+
At the start of your app, put:
|
133
|
+
require 'rubygems'
|
134
|
+
require 'bleak_house'
|
135
|
+
$memlogger = BleakHouse::Logger.new
|
136
|
+
File.delete($logfile = "/path/to/logfile") rescue nil
|
137
|
+
|
138
|
+
Now, at the points of interest, put:
|
139
|
+
$memlogger.snapshot($logfile, "tag/subtag", false)
|
140
|
+
|
141
|
+
Run your app. Once you are done, analyze your data:
|
142
|
+
bleak /path/to/logfile
|
143
|
+
|
144
|
+
*/
|
145
|
+
void
|
146
|
+
Init_snapshot()
|
147
|
+
{
|
148
|
+
rb_mBleakHouse = rb_define_module("BleakHouse");
|
149
|
+
rb_cC = rb_define_class_under(rb_mBleakHouse, "Logger", rb_cObject);
|
150
|
+
rb_define_method(rb_cC, "snapshot", snapshot, 3);
|
151
|
+
rb_define_method(rb_cC, "heaps_used", heaps_used, 0);
|
152
|
+
rb_define_method(rb_cC, "heaps_length", heaps_length, 0);
|
153
|
+
}
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "env.h"
|
3
|
+
#include "node.h"
|
4
|
+
#include "st.h"
|
5
|
+
#include "re.h"
|
6
|
+
#include "util.h"
|
7
|
+
#include "intern.h"
|
8
|
+
#include "string.h"
|
9
|
+
|
10
|
+
/* Histogram of most common Rails classes */
|
11
|
+
static char * builtins[] = {
|
12
|
+
"String",
|
13
|
+
"Array",
|
14
|
+
"Hash",
|
15
|
+
"Class",
|
16
|
+
"Regexp",
|
17
|
+
"Proc",
|
18
|
+
"ActionController::Routing::DividerSegment",
|
19
|
+
"Gem::Version",
|
20
|
+
"Gem::Version::Requirement",
|
21
|
+
"Bignum",
|
22
|
+
"Symbol",
|
23
|
+
"Time",
|
24
|
+
"MatchData",
|
25
|
+
"Gem::Specification",
|
26
|
+
"ActionController::Routing::StaticSegment",
|
27
|
+
"Gem::Dependency",
|
28
|
+
"Module",
|
29
|
+
"ActionController::Routing::DynamicSegment",
|
30
|
+
"Range",
|
31
|
+
"ActionController::Routing::Route",
|
32
|
+
"Float",
|
33
|
+
"HashWithIndifferentAccess",
|
34
|
+
"Method",
|
35
|
+
"Enumerable",
|
36
|
+
"Comparable",
|
37
|
+
"Set",
|
38
|
+
"File",
|
39
|
+
"Object",
|
40
|
+
"NameError",
|
41
|
+
"Thread",
|
42
|
+
"_node",
|
43
|
+
"_none",
|
44
|
+
"_blktag",
|
45
|
+
"_undef",
|
46
|
+
"_varmap",
|
47
|
+
"_scope",
|
48
|
+
"_unknown"
|
49
|
+
};
|
50
|
+
|
51
|
+
#define BUILTINS_SIZE 30
|
52
|
+
#define SPECIALS_SIZE 7
|
53
|
+
|
54
|
+
typedef struct RVALUE {
|
55
|
+
union {
|
56
|
+
struct {
|
57
|
+
unsigned long flags; /* always 0 for freed obj */
|
58
|
+
struct RVALUE *next;
|
59
|
+
} free;
|
60
|
+
struct RBasic basic;
|
61
|
+
struct RObject object;
|
62
|
+
struct RClass klass;
|
63
|
+
struct RFloat flonum;
|
64
|
+
struct RString string;
|
65
|
+
struct RArray array;
|
66
|
+
struct RRegexp regexp;
|
67
|
+
struct RHash hash;
|
68
|
+
struct RData data;
|
69
|
+
struct RStruct rstruct;
|
70
|
+
struct RBignum bignum;
|
71
|
+
struct RFile file;
|
72
|
+
struct RNode node;
|
73
|
+
struct RMatch match;
|
74
|
+
struct RVarmap varmap;
|
75
|
+
struct SCOPE scope;
|
76
|
+
} as;
|
77
|
+
} RVALUE;
|
78
|
+
|
79
|
+
struct heaps_slot {
|
80
|
+
void *membase;
|
81
|
+
RVALUE *slot;
|
82
|
+
int limit;
|
83
|
+
};
|
84
|
+
|
85
|
+
typedef struct st_table_entry st_table_entry;
|
86
|
+
|
87
|
+
struct st_table_entry {
|
88
|
+
unsigned int hash;
|
89
|
+
st_data_t key;
|
90
|
+
st_data_t record;
|
91
|
+
st_table_entry *next;
|
92
|
+
};
|
93
|
+
|
94
|
+
struct st_table * rb_parse_sym_tbl();
|
95
|
+
struct heaps_slot * rb_gc_heap_slots();
|
96
|
+
int rb_gc_heaps_used();
|
97
|
+
int rb_gc_heaps_length();
|
98
|
+
|
99
|
+
int lookup_builtin(char *);
|
data/init.rb
CHANGED
data/install.rb
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
puts "Please use the gem version of BleakHouse from now on."
|