mem_inspect 1.0.0

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.
@@ -0,0 +1,10 @@
1
+ Manifest.txt
2
+ README
3
+ Rakefile
4
+ bin/ruby_mem_dump
5
+ bin/ruby_mem_inspect_build
6
+ gc.c.patch
7
+ lib/mem_inspect.rb
8
+ lib/mem_inspect/aquaterm_viewer.rb
9
+ lib/mem_inspect/png_viewer.rb
10
+ lib/mem_inspect/viewer.rb
data/README ADDED
@@ -0,0 +1,60 @@
1
+ = mem_inspect
2
+
3
+ == About
4
+
5
+ mem_inspect is ObjectSpace.each_object on crack. mem_inspect gives you the
6
+ contents of each slot in Ruby's heap. mem_inspect also includes viewers that
7
+ let you visualize the contents of Ruby's heap.
8
+
9
+ == Installing mem_inspect
10
+
11
+ First, install the gem:
12
+
13
+ $ sudo gem install mem_inspect
14
+
15
+ Then you need to build a patched ruby:
16
+
17
+ ruby_mem_inspect_build
18
+
19
+ Optionally, you can install the patch ruby binary. It will be named
20
+ "ruby_mem_inspect".
21
+
22
+ == Using mem_inspect
23
+
24
+ === Dumping a PNG
25
+
26
+ To get a PNG of memory use:
27
+
28
+ mem_inspect_ruby_1_8/ruby_mem_inspect -S ruby_mem_dump
29
+
30
+ To get a PNG of memory use in your application:
31
+
32
+ require 'mem_inspect'
33
+ require 'mem_inspect/png_viewer'
34
+
35
+ And when you want to dump a PNG:
36
+
37
+ MemInspect::PNGViewer.new(1024, 768).draw
38
+
39
+ == Dumping via AquaTerm
40
+
41
+ First you'll need to install RubyCocoa. You can find instructions for that
42
+ here:
43
+
44
+ http://rubycocoa.sourceforge.net/doc/getting.en.html
45
+
46
+ http://rubycocoa.sourceforge.net/doc/build.en.html
47
+
48
+ Then you'll need to download AquaTerm:
49
+
50
+ http://sourceforge.net/project/showfiles.php?group_id=39915
51
+
52
+ Once you have all that installed you can dump to an AquaTerm plot:
53
+
54
+ require 'mem_inspect'
55
+ require 'mem_inspect/aquaterm_viewer'
56
+
57
+ And when you want a plot:
58
+
59
+ MemInspect::AquatermViewer.new(1024, 768).draw
60
+
@@ -0,0 +1,63 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/gempackagetask'
6
+
7
+ $VERBOSE = nil
8
+
9
+ spec = Gem::Specification.new do |s|
10
+ s.name = 'mem_inspect'
11
+ s.version = '1.0.0'
12
+ s.summary = 'ObjectSpace.each_object on crack'
13
+ s.description = 'mem_inspect walks Ruby\'s heaps giving you the contents of
14
+ each slot. mem_inspect also includes viewers that will let you visualize
15
+ the contents of Ruby\'s heap.'
16
+ s.author = 'Eric Hodel'
17
+ s.email = 'drbrain@segment7.net'
18
+
19
+ s.has_rdoc = true
20
+ s.files = File.read('Manifest.txt').split($/)
21
+ s.require_path = 'lib'
22
+
23
+ s.executables = %w[ruby_mem_dump ruby_mem_inspect_build]
24
+
25
+ s.add_dependency 'RubyInline', '>= 3.5.0'
26
+ s.add_dependency 'png', '>= 1.0.0'
27
+ end
28
+
29
+ desc 'Run tests'
30
+ task :default => [ :test ]
31
+
32
+ Rake::TestTask.new('test') do |t|
33
+ t.libs << 'test'
34
+ t.pattern = 'test/test_*.rb'
35
+ t.verbose = true
36
+ end
37
+
38
+ desc 'Update Manifest.txt'
39
+ task :update_manifest do
40
+ sh "find . -type f | sed -e 's%./%%' | egrep -v 'svn|swp|~' | egrep -v '^(doc|pkg)/' | sort > Manifest.txt"
41
+ end
42
+
43
+ desc 'Generate RDoc'
44
+ Rake::RDocTask.new :rdoc do |rd|
45
+ rd.rdoc_dir = 'doc'
46
+ rd.rdoc_files.add 'lib', 'README', 'LICENSE'
47
+ rd.main = 'README'
48
+ rd.options << '-d' if `which dot` =~ /\/dot/
49
+ rd.options << '-t mem_inspect'
50
+ end
51
+
52
+ desc 'Build Gem'
53
+ Rake::GemPackageTask.new spec do |pkg|
54
+ pkg.need_tar = true
55
+ end
56
+
57
+ desc 'Clean up'
58
+ task :clean => [ :clobber_rdoc, :clobber_package ]
59
+
60
+ desc 'Clean up'
61
+ task :clobber => [ :clean ]
62
+
63
+ # vim: syntax=Ruby
@@ -0,0 +1,8 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'mem_inspect'
4
+ require 'mem_inspect/png_viewer'
5
+
6
+ mv = MemInspect::PNGViewer.new 1024, 768
7
+ mv.draw
8
+
@@ -0,0 +1,45 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+
6
+ ruby_dir = "mem_inspect_ruby_1_8"
7
+ old_pwd = Dir.pwd
8
+
9
+ if File.exist? ruby_dir then
10
+ Dir.chdir ruby_dir do "sh cvs up" end
11
+ else
12
+ sh "cvs -z4 -d :pserver:anonymous@cvs.ruby-lang.org:/src co -d #{ruby_dir} -r ruby_1_8 ruby"
13
+ end
14
+
15
+ Dir.chdir ruby_dir do
16
+ sh "rm gc.c"
17
+ sh "cvs up gc.c"
18
+ sh "patch < #{File.join old_pwd, "gc.c.patch"}"
19
+ sh "autoconf" unless File.exist? "configure"
20
+ sh "./configure --program-suffix=_mem_inspect" unless File.exist? "Makefile"
21
+ sh "make"
22
+
23
+ puts "*" * 80
24
+ puts "Ruby has been built in #{ruby_dir}, but not installed"
25
+ puts
26
+ puts "If you run:"
27
+ puts
28
+ puts "\tcd #{ruby_dir}; sudo make install"
29
+ puts
30
+ puts "YOU WILL OVERWRITE YOUR STDLIB"
31
+ puts
32
+ puts "Generally, this is harmless, but you may not want that."
33
+ puts "So I'll let you do that all by yourself."
34
+ puts
35
+ puts "You may now run ruby_mem_dump using the patched ruby:"
36
+ puts
37
+ puts "\t#{File.join ruby_dir, "ruby_mem_inspect"} -S ruby_mem_dump"
38
+ puts
39
+ puts "If you haven't installed mem_inspect:"
40
+ puts
41
+ puts "\t#{File.join ruby_dir, "ruby_mem_inspect"} -Ilib bin/ruby_mem_dump"
42
+ puts
43
+ puts "*" * 80
44
+ end
45
+
@@ -0,0 +1,30 @@
1
+ Index: gc.c
2
+ ===================================================================
3
+ RCS file: /src/ruby/gc.c,v
4
+ retrieving revision 1.168.2.45
5
+ diff -p -u -r1.168.2.45 gc.c
6
+ --- gc.c 25 Aug 2006 08:12:46 -0000 1.168.2.45
7
+ +++ gc.c 31 Aug 2006 18:47:55 -0000
8
+ @@ -323,6 +323,22 @@ static struct heaps_slot {
9
+ static int heaps_length = 0;
10
+ static int heaps_used = 0;
11
+
12
+ +struct heaps_slot *
13
+ +rb_gc_heap_slots()
14
+ +{
15
+ + return heaps;
16
+ +}
17
+ +
18
+ +int
19
+ +rb_gc_heaps_used() {
20
+ + return heaps_used;
21
+ +}
22
+ +
23
+ +int
24
+ +rb_gc_heaps_length() {
25
+ + return heaps_length;
26
+ +}
27
+ +
28
+ #define HEAP_MIN_SLOTS 10000
29
+ static int heap_slots = HEAP_MIN_SLOTS;
30
+
@@ -0,0 +1,197 @@
1
+ begin
2
+ require 'inline'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'inline'
6
+ end
7
+
8
+ ##
9
+ # ObjectSpace.each_object on crack.
10
+ #
11
+ # MemInspect allows you to walk Ruby's heaps and gives you the contents of
12
+ # each heap slot.
13
+
14
+ class MemInspect
15
+
16
+ inline do |builder|
17
+ builder.include '"node.h"' # struct RNode
18
+ builder.include '"st.h"' # struct st_table
19
+ builder.include '"re.h"' # struct RRegexp
20
+ builder.include '"env.h"' # various structs
21
+
22
+ builder.prefix <<-EOC
23
+ typedef struct RVALUE {
24
+ union {
25
+ struct {
26
+ unsigned long flags; /* always 0 for freed obj */
27
+ struct RVALUE *next;
28
+ } free;
29
+ struct RBasic basic;
30
+ struct RObject object;
31
+ struct RClass klass;
32
+ struct RFloat flonum;
33
+ struct RString string;
34
+ struct RArray array;
35
+ struct RRegexp regexp;
36
+ struct RHash hash;
37
+ struct RData data;
38
+ struct RStruct rstruct;
39
+ struct RBignum bignum;
40
+ struct RFile file;
41
+ struct RNode node;
42
+ struct RMatch match;
43
+ struct RVarmap varmap;
44
+ struct SCOPE scope;
45
+ } as;
46
+ } RVALUE;
47
+
48
+ struct heaps_slot {
49
+ void *membase;
50
+ RVALUE *slot;
51
+ int limit;
52
+ };
53
+
54
+ struct heaps_slot * rb_gc_heap_slots();
55
+ int rb_gc_heaps_used();
56
+ int rb_gc_heaps_length();
57
+ EOC
58
+
59
+ ##
60
+ # Number of struct heaps_slots used
61
+
62
+ builder.c <<-EOC
63
+ static int
64
+ heaps_used() {
65
+ return rb_gc_heaps_used();
66
+ }
67
+ EOC
68
+
69
+ ##
70
+ # Length of the struct heaps_slots allocated (I think)
71
+
72
+ builder.c <<-EOC
73
+ static int
74
+ heaps_length() {
75
+ return rb_gc_heaps_length();
76
+ }
77
+ EOC
78
+
79
+ # [Type flag, object size, object]
80
+
81
+ types = [
82
+ ['T_NONE', 0, 'unknown'],
83
+ ['T_NIL', 0, 'Qnil'],
84
+ ['T_OBJECT', 'sizeof(struct RObject) + sizeof(struct st_table)'],
85
+ ['T_CLASS', 'sizeof(struct RClass) + sizeof(struct st_table) * 2'],
86
+ ['T_ICLASS', 'sizeof(struct RClass)', 'iclass'],
87
+ ['T_MODULE', 'sizeof(struct RObject) + sizeof(struct st_table) * 2'],
88
+ ['T_FLOAT', 'sizeof(struct RFloat)'],
89
+ ['T_STRING',
90
+ 'sizeof(struct RString) + (FL_TEST(RSTRING(p), ELTS_SHARED) ? 0 : RSTRING(p)->len)'],
91
+ ['T_REGEXP', 'sizeof(struct RRegexp) + RREGEXP(p)->len'],
92
+ ['T_ARRAY',
93
+ 'sizeof(struct RArray) + (FL_TEST(RARRAY(p), ELTS_SHARED) ? 0 : RARRAY(p)->len * 4)'],
94
+ ['T_FIXNUM', 0],
95
+ ['T_HASH', 'sizeof(struct RHash) + sizeof(struct st_table)'],
96
+ ['T_STRUCT', 'sizeof(struct RStruct) + RSTRUCT(p)->len'],
97
+ ['T_BIGNUM', 'sizeof(struct RBignum) + RBIGNUM(p)->len'],
98
+ ['T_FILE', 'sizeof(struct RFile)'],
99
+
100
+ ['T_TRUE', 0, 'Qtrue'],
101
+ ['T_FALSE', 0, 'Qfalse'],
102
+ ['T_DATA', 'sizeof(struct RData)'],
103
+ ['T_MATCH', 'sizeof(struct RMatch)'],
104
+ ['T_SYMBOL', 0],
105
+
106
+ ['T_BLKTAG', 0, 'unknown'],
107
+ ['T_UNDEF', 0, 'unknown'],
108
+ ['T_VARMAP', 'sizeof(struct RVarmap)', 'varmap'], # TODO linked-list
109
+ ['T_SCOPE',
110
+ 'sizeof(struct SCOPE) +
111
+ (((struct SCOPE *)p)->local_tbl ? ((struct SCOPE *)p)->local_tbl[0] : 0)',
112
+ 'scope'],
113
+ ['T_NODE', 'sizeof(struct RNode)', 'node'], # TODO SCOPE and ALLOCA nodes
114
+ ]
115
+
116
+ types.each do |type|
117
+ type[2] = '(VALUE)p' if type[2].nil?
118
+ end
119
+
120
+ ##
121
+ # Generates the cases for the switch statement
122
+
123
+ def self.make_switch(types)
124
+ types.map do |type, size, object|
125
+ ["case #{type}:",
126
+ " size = #{size};",
127
+ " obj = #{object};",
128
+ " break;",
129
+ nil]
130
+ end.join("\n")
131
+ end
132
+
133
+ ##
134
+ # Walks the heap slots yielding each item's address, size and value.
135
+ #
136
+ # The value may be:
137
+ # :__free:: Unassigned heap slot
138
+ # Object:: Ruby object
139
+ # :__node:: Ruby AST node
140
+ # :__iclass:: module instance (created via include)
141
+ # :__scope:: ruby interpreter scope
142
+ # :__varmap:: variable map (see eval.c, parse.y)
143
+ # :__unknown:: unknown item
144
+ #
145
+ # The size of objects may not be correct. Please fix if you find an
146
+ # error.
147
+
148
+ builder.c <<-EOC
149
+ static void
150
+ walk() {
151
+ RVALUE *p, *pend;
152
+ VALUE ary = rb_ary_new2(3);
153
+ VALUE unknown = ID2SYM(rb_intern("__unknown"));
154
+ VALUE iclass = ID2SYM(rb_intern("__iclass"));
155
+ VALUE varmap = ID2SYM(rb_intern("__varmap"));
156
+ VALUE scope = ID2SYM(rb_intern("__scope"));
157
+ VALUE node = ID2SYM(rb_intern("__node"));
158
+ VALUE free = ID2SYM(rb_intern("__free"));
159
+ VALUE obj = unknown;
160
+ long size = 0;
161
+ struct heaps_slot * heaps = rb_gc_heap_slots();
162
+ int i;
163
+
164
+ for (i = 0; i < rb_gc_heaps_used(); i++) {
165
+ p = heaps[i].slot;
166
+ pend = p + heaps[i].limit;
167
+ for (; p < pend; p++) {
168
+ size = 0;
169
+ obj = free;
170
+ if (p->as.basic.flags) { /* always 0 for freed objects */
171
+ switch (TYPE(p)) {
172
+ #{make_switch types}
173
+
174
+ default:
175
+ if (!p->as.basic.klass) {
176
+ obj = unknown;
177
+ } else {
178
+ obj = (VALUE)p;
179
+ }
180
+ }
181
+
182
+ if (FL_TEST(obj, FL_EXIVAR))
183
+ size += sizeof(struct st_table);
184
+ }
185
+ rb_ary_store(ary, 0, LONG2NUM((long)p));
186
+ rb_ary_store(ary, 1, INT2FIX(size));
187
+ rb_ary_store(ary, 2, obj);
188
+
189
+ rb_yield(ary);
190
+ }
191
+ }
192
+ }
193
+ EOC
194
+ end
195
+
196
+ end
197
+
@@ -0,0 +1,84 @@
1
+ require 'osx/cocoa'
2
+ require 'mem_inspect'
3
+ require 'mem_inspect/viewer'
4
+
5
+ ##
6
+ # Prints plots of memory to an AquaTerm window.
7
+ #
8
+ # Requires RubyCocoa and the AquaTerm framework.
9
+
10
+ class MemInspect::AquatermViewer < MemInspect::Viewer
11
+ OSX::NSBundle.bundleWithPath(File.expand_path("/Library/Frameworks/Aquaterm.framework")).load
12
+ OSX.ns_import :AQTAdapter
13
+
14
+ BLACK = 0 # unalloc
15
+ WHITE = 1
16
+ RED = 2
17
+ GREEN = 3
18
+ GRAY = 4 # free
19
+
20
+ ##
21
+ # Creates a new AquatermViewer.
22
+
23
+ def initialize(width, height)
24
+ super
25
+
26
+ @adapter = OSX::AQTAdapter.alloc.init
27
+ @adapter.openPlotWithIndex 1
28
+ @adapter.setPlotSize [@width, @height]
29
+ @adapter.setPlotTitle 'Memory Map'
30
+
31
+ @adapter.setColormapEntry_red_green_blue 0, 0.0, 0.0, 0.0 # black
32
+ @adapter.setColormapEntry_red_green_blue 1, 1.0, 1.0, 1.0 # white
33
+ @adapter.setColormapEntry_red_green_blue 2, 1.0, 0.0, 0.0 # red
34
+ @adapter.setColormapEntry_red_green_blue 3, 0.0, 1.0, 0.0 # green
35
+ @adapter.setColormapEntry_red_green_blue 4, 0.7, 0.7, 0.7 # gray
36
+ end
37
+
38
+ ##
39
+ # Draws a plot and renders it in the active plot window.
40
+
41
+ def draw
42
+ fill_background
43
+ color = BLACK
44
+ last_color = color
45
+ x = 0
46
+ y = 0
47
+
48
+ @mem_inspect.walk do |address, size, object|
49
+ x, y = coords_for address
50
+
51
+ color = case object
52
+ when :__free then GRAY
53
+ when :__node then RED
54
+ when :__varmap, :__scope, :__unknown then WHITE
55
+ else GREEN
56
+ end
57
+
58
+ @adapter.takeColorFromColormapEntry color unless color == last_color
59
+ @adapter.moveToPoint [x, y] if x == 0
60
+ @adapter.addLineToPoint [x + 1, y + 1]
61
+ last_color = color
62
+ end
63
+
64
+ ensure # coords_for raises when out-of-bounds
65
+ @adapter.renderPlot
66
+ end
67
+
68
+ ##
69
+ # Fills the background of the plot, erasing the current contents.
70
+
71
+ def fill_background
72
+ @adapter.takeColorFromColormapEntry BLACK
73
+ @adapter.addFilledRect [0, 0, @width, @height] # background
74
+ end
75
+
76
+ ##
77
+ # Close the plot
78
+
79
+ def close
80
+ @adapter.closePlot
81
+ end
82
+
83
+ end
84
+
@@ -0,0 +1,47 @@
1
+ begin
2
+ require 'png'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'png'
6
+ end
7
+ require 'mem_inspect'
8
+ require 'mem_inspect/viewer'
9
+
10
+ ##
11
+ # Prints plots of memory as a PNG.
12
+
13
+ class MemInspect::PNGViewer < MemInspect::Viewer
14
+
15
+ ##
16
+ # Draws a PNG and saves it to memory_map.PID.timestamp.png
17
+
18
+ def draw
19
+ canvas = PNG::Canvas.new @width, @height, PNG::Color::Black
20
+ x = 0
21
+ y = 0
22
+ color = nil
23
+
24
+ @mem_inspect.walk do |address, size, object|
25
+ x, y = coords_for address
26
+
27
+ color = case object
28
+ when :__free then PNG::Color::Gray
29
+ when :__node then PNG::Color::Red
30
+ when :__scope then PNG::Color::Yellow
31
+ when :__varmap then PNG::Color::Purple
32
+ when :__unknown then PNG::Color::Orange
33
+ when :__iclass then PNG::Color::White
34
+ else PNG::Color::Green
35
+ end
36
+
37
+ canvas[x, y] = color
38
+ end
39
+
40
+ ensure # coords_for raises when out-of-bounds
41
+ png = PNG.new canvas
42
+ file = "memory_map.#{$$}.#{Time.now.to_i}.png"
43
+ png.save file
44
+ end
45
+
46
+ end
47
+
@@ -0,0 +1,39 @@
1
+ require 'mem_inspect'
2
+
3
+ ##
4
+ # An abstract viewer class
5
+ #
6
+ # Use the PNGViewer as an example.
7
+
8
+ class MemInspect::Viewer
9
+
10
+ ##
11
+ # Intializes the visualization.
12
+
13
+ def initialize(width, height)
14
+ @width = width
15
+ @height = height
16
+ @max = @width * @height
17
+
18
+ @mem_inspect = MemInspect.new
19
+ end
20
+
21
+ ##
22
+ # Returns x, y coordinates for +address+ based on the width and height of
23
+ # the visualization.
24
+
25
+ def coords_for(address)
26
+ index = address / 20
27
+
28
+ if index > @max then
29
+ raise "Ran out of plot space at index %d for 0x%x" % [index, address]
30
+ end
31
+
32
+ x = index % @width
33
+ y = index / @width
34
+
35
+ return x, y
36
+ end
37
+
38
+ end
39
+
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.99
3
+ specification_version: 1
4
+ name: mem_inspect
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2006-08-31 00:00:00 -07:00
8
+ summary: ObjectSpace.each_object on crack
9
+ require_paths:
10
+ - lib
11
+ email: drbrain@segment7.net
12
+ homepage:
13
+ rubyforge_project:
14
+ description: mem_inspect walks Ruby's heaps giving you the contents of each slot. mem_inspect also includes viewers that will let you visualize the contents of Ruby's heap.
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Eric Hodel
31
+ files:
32
+ - Manifest.txt
33
+ - README
34
+ - Rakefile
35
+ - bin/ruby_mem_dump
36
+ - bin/ruby_mem_inspect_build
37
+ - gc.c.patch
38
+ - lib/mem_inspect.rb
39
+ - lib/mem_inspect/aquaterm_viewer.rb
40
+ - lib/mem_inspect/png_viewer.rb
41
+ - lib/mem_inspect/viewer.rb
42
+ test_files: []
43
+
44
+ rdoc_options: []
45
+
46
+ extra_rdoc_files: []
47
+
48
+ executables:
49
+ - ruby_mem_dump
50
+ - ruby_mem_inspect_build
51
+ extensions: []
52
+
53
+ requirements: []
54
+
55
+ dependencies:
56
+ - !ruby/object:Gem::Dependency
57
+ name: RubyInline
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Version::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 3.5.0
64
+ version:
65
+ - !ruby/object:Gem::Dependency
66
+ name: png
67
+ version_requirement:
68
+ version_requirements: !ruby/object:Gem::Version::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.0.0
73
+ version: