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.
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