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.
- data/Manifest.txt +10 -0
- data/README +60 -0
- data/Rakefile +63 -0
- data/bin/ruby_mem_dump +8 -0
- data/bin/ruby_mem_inspect_build +45 -0
- data/gc.c.patch +30 -0
- data/lib/mem_inspect.rb +197 -0
- data/lib/mem_inspect/aquaterm_viewer.rb +84 -0
- data/lib/mem_inspect/png_viewer.rb +47 -0
- data/lib/mem_inspect/viewer.rb +39 -0
- metadata +73 -0
data/Manifest.txt
ADDED
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
|
+
|
data/Rakefile
ADDED
@@ -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
|
data/bin/ruby_mem_dump
ADDED
@@ -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
|
+
|
data/gc.c.patch
ADDED
@@ -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
|
+
|
data/lib/mem_inspect.rb
ADDED
@@ -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:
|