memprof 0.1.0 → 0.1.1
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/README +20 -4
- data/ext/mach.c +10 -9
- data/ext/memprof.c +38 -7
- data/memprof.gemspec +1 -1
- metadata +1 -1
data/README
CHANGED
@@ -5,6 +5,14 @@ What is memprof?
|
|
5
5
|
Memprof is a memory profiler for Ruby that requires no patches to the Ruby VM.
|
6
6
|
It can help you find Ruby level memory leaks in your application.
|
7
7
|
|
8
|
+
Required to install
|
9
|
+
===================
|
10
|
+
If you are using the Linux version, you need to install libelf:
|
11
|
+
|
12
|
+
apt-get install libelfg0-dev
|
13
|
+
|
14
|
+
The experimental OSX version needs no additional libraries.
|
15
|
+
|
8
16
|
How to use
|
9
17
|
==========
|
10
18
|
|
@@ -13,16 +21,23 @@ Memprof.start
|
|
13
21
|
|
14
22
|
# ruby code
|
15
23
|
|
24
|
+
Memprof.stats
|
25
|
+
|
26
|
+
# more ruby code
|
27
|
+
|
28
|
+
Memprof.stats
|
16
29
|
Memprof.stop
|
17
|
-
Memprof.dump
|
18
30
|
|
19
|
-
That will monitor allocations and frees that happen between "start" and "stop"
|
20
|
-
and will output information to standard error.
|
21
31
|
|
22
|
-
|
32
|
+
The above code will output 2 summaries, allowing you to compare which objects were
|
33
|
+
destroyed and which are still around.
|
34
|
+
|
35
|
+
Memprof.stats also takes an (optional) file name to write the output to a file.
|
23
36
|
|
24
37
|
Supported systems
|
25
38
|
=================
|
39
|
+
This only works on unstripped binaries.
|
40
|
+
|
26
41
|
Currently supporting:
|
27
42
|
|
28
43
|
Linux:
|
@@ -33,6 +48,7 @@ Experimental support:
|
|
33
48
|
|
34
49
|
Snow Leopard:
|
35
50
|
x86_64 builds of MRI (both enable-shared and disable-shared)
|
51
|
+
OSX system Ruby, distributed with Snow Leopard
|
36
52
|
|
37
53
|
Coming soon:
|
38
54
|
|
data/ext/mach.c
CHANGED
@@ -12,9 +12,9 @@
|
|
12
12
|
#include <mach-o/ldsyms.h>
|
13
13
|
|
14
14
|
static void
|
15
|
-
set_text_segment(struct mach_header *header, const char *sectname)
|
15
|
+
set_text_segment(const struct mach_header *header, const char *sectname)
|
16
16
|
{
|
17
|
-
text_segment = getsectdatafromheader_64((struct mach_header_64*)header, "__TEXT", sectname, (uint64_t*)&text_segment_len);
|
17
|
+
text_segment = getsectdatafromheader_64((const struct mach_header_64*)header, "__TEXT", sectname, (uint64_t*)&text_segment_len);
|
18
18
|
if (!text_segment)
|
19
19
|
errx(EX_UNAVAILABLE, "Failed to locate the %s section", sectname);
|
20
20
|
}
|
@@ -28,8 +28,8 @@ update_dyld_stubs(int entry, void *trampee_addr)
|
|
28
28
|
for(; count < text_segment_len; count++) {
|
29
29
|
if (*byte == '\xff') {
|
30
30
|
int off = *(int *)(byte+2);
|
31
|
-
if (trampee_addr ==
|
32
|
-
*(
|
31
|
+
if (trampee_addr == *((void**)(byte + 6 + off))) {
|
32
|
+
*((void**)(byte + 6 + off)) = tramp_table[entry].addr;
|
33
33
|
}
|
34
34
|
}
|
35
35
|
byte++;
|
@@ -55,17 +55,18 @@ bin_allocate_page()
|
|
55
55
|
void
|
56
56
|
bin_update_image(int entry, void *trampee_addr)
|
57
57
|
{
|
58
|
-
// Modify any callsites residing inside the text segment of the executable itself
|
59
|
-
set_text_segment((struct mach_header*)&_mh_execute_header, "__text");
|
60
|
-
update_callqs(entry, trampee_addr);
|
61
|
-
|
62
|
-
// Modify all dyld stubs in shared libraries that have been loaded
|
63
58
|
int i, j, k;
|
64
59
|
int header_count = _dyld_image_count();
|
65
60
|
|
66
61
|
// Go through all the mach objects that are loaded into this process
|
67
62
|
for (i=0; i < header_count; i++) {
|
68
63
|
const struct mach_header *current_hdr = _dyld_get_image_header(i);
|
64
|
+
|
65
|
+
// Modify any callsites residing inside the text segment
|
66
|
+
set_text_segment(current_hdr, "__text");
|
67
|
+
text_segment += _dyld_get_image_vmaddr_slide(i);
|
68
|
+
update_callqs(entry, trampee_addr);
|
69
|
+
|
69
70
|
int lc_count = current_hdr->ncmds;
|
70
71
|
|
71
72
|
// this as a char* because we need to step it forward by an arbitrary number of bytes
|
data/ext/memprof.c
CHANGED
@@ -98,7 +98,15 @@ freelist_tramp(unsigned long rval)
|
|
98
98
|
}
|
99
99
|
|
100
100
|
static int
|
101
|
-
|
101
|
+
objs_free(st_data_t key, st_data_t record, st_data_t arg)
|
102
|
+
{
|
103
|
+
struct obj_track *tracker = (struct obj_track *)record;
|
104
|
+
free(tracker->source);
|
105
|
+
return ST_DELETE;
|
106
|
+
}
|
107
|
+
|
108
|
+
static int
|
109
|
+
objs_tabulate(st_data_t key, st_data_t record, st_data_t arg)
|
102
110
|
{
|
103
111
|
st_table *table = (st_table *)arg;
|
104
112
|
struct obj_track *tracker = (struct obj_track *)record;
|
@@ -121,7 +129,7 @@ memprof_tabulate(st_data_t key, st_data_t record, st_data_t arg)
|
|
121
129
|
type = "__node__"; break;
|
122
130
|
default:
|
123
131
|
if (RBASIC(tracker->obj)->klass) {
|
124
|
-
type = rb_obj_classname(tracker->obj);
|
132
|
+
type = (char*) rb_obj_classname(tracker->obj);
|
125
133
|
} else {
|
126
134
|
type = "__unknown__";
|
127
135
|
}
|
@@ -133,8 +141,7 @@ memprof_tabulate(st_data_t key, st_data_t record, st_data_t arg)
|
|
133
141
|
free(source_key);
|
134
142
|
}
|
135
143
|
|
136
|
-
|
137
|
-
return ST_DELETE;
|
144
|
+
return ST_CONTINUE;
|
138
145
|
}
|
139
146
|
|
140
147
|
struct results {
|
@@ -172,6 +179,7 @@ memprof_stop(VALUE self)
|
|
172
179
|
return Qfalse;
|
173
180
|
|
174
181
|
track_objs = 0;
|
182
|
+
st_foreach(objs, objs_free, (st_data_t)0);
|
175
183
|
return Qtrue;
|
176
184
|
}
|
177
185
|
|
@@ -184,7 +192,7 @@ memprof_strcmp(const void *obj1, const void *obj2)
|
|
184
192
|
}
|
185
193
|
|
186
194
|
static VALUE
|
187
|
-
|
195
|
+
memprof_stats(int argc, VALUE *argv, VALUE self)
|
188
196
|
{
|
189
197
|
st_table *tmp_table;
|
190
198
|
struct results res;
|
@@ -203,7 +211,7 @@ memprof_dump(int argc, VALUE *argv, VALUE self)
|
|
203
211
|
track_objs = 0;
|
204
212
|
|
205
213
|
tmp_table = st_init_strtable();
|
206
|
-
st_foreach(objs,
|
214
|
+
st_foreach(objs, objs_tabulate, (st_data_t)tmp_table);
|
207
215
|
|
208
216
|
res.num_entries = 0;
|
209
217
|
res.entries = malloc(sizeof(char*) * tmp_table->num_entries);
|
@@ -222,6 +230,27 @@ memprof_dump(int argc, VALUE *argv, VALUE self)
|
|
222
230
|
return Qnil;
|
223
231
|
}
|
224
232
|
|
233
|
+
static VALUE
|
234
|
+
memprof_stats_bang(int argc, VALUE *argv, VALUE self)
|
235
|
+
{
|
236
|
+
memprof_stats(argc, argv, self);
|
237
|
+
st_foreach(objs, objs_free, (st_data_t)0);
|
238
|
+
return Qnil;
|
239
|
+
}
|
240
|
+
|
241
|
+
static VALUE
|
242
|
+
memprof_track(int argc, VALUE *argv, VALUE self)
|
243
|
+
{
|
244
|
+
if (!rb_block_given_p())
|
245
|
+
rb_raise(rb_eArgError, "block required");
|
246
|
+
|
247
|
+
memprof_start(self);
|
248
|
+
rb_yield(Qnil);
|
249
|
+
memprof_stats(argc, argv, self);
|
250
|
+
memprof_stop(self);
|
251
|
+
return Qnil;
|
252
|
+
}
|
253
|
+
|
225
254
|
static void
|
226
255
|
create_tramp_table()
|
227
256
|
{
|
@@ -458,7 +487,9 @@ Init_memprof()
|
|
458
487
|
VALUE memprof = rb_define_module("Memprof");
|
459
488
|
rb_define_singleton_method(memprof, "start", memprof_start, 0);
|
460
489
|
rb_define_singleton_method(memprof, "stop", memprof_stop, 0);
|
461
|
-
rb_define_singleton_method(memprof, "
|
490
|
+
rb_define_singleton_method(memprof, "stats", memprof_stats, -1);
|
491
|
+
rb_define_singleton_method(memprof, "stats!", memprof_stats_bang, -1);
|
492
|
+
rb_define_singleton_method(memprof, "track", memprof_track, -1);
|
462
493
|
|
463
494
|
pagesize = getpagesize();
|
464
495
|
objs = st_init_numtable();
|
data/memprof.gemspec
CHANGED