mem_inspect 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: