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