memprof 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README +20 -4
  2. data/ext/mach.c +10 -9
  3. data/ext/memprof.c +38 -7
  4. data/memprof.gemspec +1 -1
  5. 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
- Memprof.dump also takes an (optional) file name to write the output to a file.
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 == (void*)(*(long long*)(byte + 6 + off))) {
32
- *(long long*)(byte + 6 + off) = tramp_table[entry].addr;
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
- memprof_tabulate(st_data_t key, st_data_t record, st_data_t arg)
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
- free(tracker->source);
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
- memprof_dump(int argc, VALUE *argv, VALUE self)
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, memprof_tabulate, (st_data_t)tmp_table);
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, "dump", memprof_dump, -1);
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
@@ -1,6 +1,6 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'memprof'
3
- s.version = '0.1.0'
3
+ s.version = '0.1.1'
4
4
  s.date = '2009-12-10'
5
5
  s.summary = 'Ruby Memory Profiler'
6
6
  s.description = "Ruby memory profiler similar to bleak_house, but without patches to the Ruby VM"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memprof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Damato