memprof 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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