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 CHANGED
@@ -1,7 +1,29 @@
1
1
 
2
- BleakHouse Changelog
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
- 3. gem version
5
- 2. dumped Rublique once I understood what it did; replaced with a lightweight non-delta marshalling version
6
- 1. first version
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
- ./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
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
- 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/.
4
+ A library for finding memory leaks.
11
5
 
12
- Copyright (c) 2007 Cloudburst, LLC. See included LICENSE file. Portions
13
- copyright Tony Arcieri and Scott Barron and used under license.
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
- 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
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
- NAME = "bleak_house"
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
- 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"}
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 => 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
19
+
20
+ rescue LoadError
60
21
  end
61
22
 
62
- desc 'Do nothing.'
23
+ desc 'Run tests.'
63
24
  Rake::Task.redefine_task("test") do
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']}" : ""}"
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
@@ -0,0 +1,2 @@
1
+
2
+ * Per-object memory usage. This may be impossible to do accurately.
data/bin/bleak ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless ARGV[0]
4
+ puts "Please specify a BleakHouse logfile"
5
+ exit
6
+ else
7
+ $LOAD_PATH << "#{File.dirname(__FILE__)}/../lib/"
8
+ require 'bleak_house/analyzer'
9
+ BleakHouse::Analyzer.run(ARGV[0])
10
+ end
@@ -0,0 +1,4 @@
1
+
2
+ require 'mkmf'
3
+ dir_config('snapshot')
4
+ create_makefile('bleak_house/logger/snapshot')
@@ -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
@@ -1,2 +1,3 @@
1
1
 
2
- require 'bleak_house'
2
+ puts "Please use the gem version of BleakHouse from now on."
3
+ exit!
data/install.rb CHANGED
@@ -1 +1,2 @@
1
- puts File.open("#{File.dirname(__FILE__)}/README").read
1
+
2
+ puts "Please use the gem version of BleakHouse from now on."