memprof 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,11 @@
1
+ if RUBY_PLATFORM =~ /darwin/
2
+ STDERR.puts "\n\n"
3
+ STDERR.puts "***************************************************************************************"
4
+ STDERR.puts "**************************** osx is not supported (yet) =( ****************************"
5
+ STDERR.puts "***************************************************************************************"
6
+ exit(1)
7
+ end
8
+
1
9
  if RUBY_VERSION >= "1.9"
2
10
  STDERR.puts "\n\n"
3
11
  STDERR.puts "***************************************************************************************"
@@ -14,23 +22,65 @@ CWD = File.expand_path(File.dirname(__FILE__))
14
22
  def sys(cmd)
15
23
  puts " -- #{cmd}"
16
24
  unless ret = xsystem(cmd)
17
- raise "#{cmd} failed, please report to http://github.com/ice799/memprof/issues with pastie.org link to #{CWD}/mkmf.log"
25
+ raise "#{cmd} failed, please report to memprof@tmm1.net with pastie.org link to #{CWD}/mkmf.log"
18
26
  end
19
27
  ret
20
28
  end
21
29
 
30
+ ###
31
+ # yajl
32
+
33
+ yajl = File.basename('yajl-1.0.8.tar.gz')
34
+ dir = File.basename(yajl, '.tar.gz')
35
+
36
+ unless File.exists?("#{CWD}/dst/lib/libyajl_ext.a")
37
+ puts "(I'm about to compile yajl.. this will definitely take a while)"
38
+
39
+ Dir.chdir('src') do
40
+ FileUtils.rm_rf(dir) if File.exists?(dir)
41
+
42
+ sys("tar zxvf #{yajl}")
43
+ Dir.chdir("#{dir}/src") do
44
+ FileUtils.mkdir_p "api/yajl"
45
+ %w[ common parse gen ].each do |f|
46
+ FileUtils.cp "api/yajl_#{f}.h", 'api/yajl/'
47
+ end
48
+
49
+ File.open("extconf.rb",'w') do |f|
50
+ f.puts "require 'mkmf'; $INCFLAGS[0,0] = '-I./api/ '; create_makefile 'libyajl'"
51
+ end
52
+ sys("#{Config::CONFIG['bindir']}/#{Config::CONFIG['ruby_install_name']} extconf.rb")
53
+
54
+ sys("make")
55
+ sys("ar rv libyajl_ext.a #{Dir['*.o'].join(' ')}")
56
+
57
+ FileUtils.mkdir_p "#{CWD}/dst/lib"
58
+ FileUtils.cp 'libyajl_ext.a', "#{CWD}/dst/lib"
59
+ FileUtils.mkdir_p "#{CWD}/dst/include"
60
+ FileUtils.cp_r 'api/yajl', "#{CWD}/dst/include/"
61
+ end
62
+ end
63
+ end
64
+
65
+ $LIBPATH.unshift "#{CWD}/dst/lib"
66
+ $INCFLAGS[0,0] = "-I#{CWD}/dst/include "
67
+
68
+ unless have_library('yajl_ext') and have_header('yajl/yajl_gen.h')
69
+ raise 'Yajl build failed'
70
+ end
71
+
22
72
  def add_define(name)
23
73
  $defs.push("-D#{name}")
24
74
  end
25
75
 
26
- ###
27
- # libelf
28
-
29
76
  if RUBY_PLATFORM =~ /linux/
77
+ ###
78
+ # libelf
79
+
30
80
  libelf = File.basename('libelf-0.8.13.tar.gz')
31
81
  dir = File.basename(libelf, '.tar.gz')
32
82
 
33
- unless File.exists?("#{CWD}/dst/lib/libelf_ext.so")
83
+ unless File.exists?("#{CWD}/dst/lib/libelf_ext.a")
34
84
  puts "(I'm about to compile libelf.. this will definitely take a while)"
35
85
 
36
86
  Dir.chdir('src') do
@@ -38,26 +88,58 @@ if RUBY_PLATFORM =~ /linux/
38
88
 
39
89
  sys("tar zxvf #{libelf}")
40
90
  Dir.chdir(dir) do
41
- sys("./configure --prefix=#{CWD}/dst")
91
+ ENV['CFLAGS'] = '-fPIC'
92
+ sys("./configure --prefix=#{CWD}/dst --disable-nls --disable-shared")
42
93
  sys("make")
43
94
  sys("make install")
44
95
  end
45
96
  end
46
97
 
47
98
  Dir.chdir('dst/lib') do
48
- FileUtils.ln_s 'libelf.so', 'libelf_ext.so'
99
+ FileUtils.ln_s 'libelf.a', 'libelf_ext.a'
49
100
  end
50
101
  end
51
102
 
52
103
  $LIBPATH.unshift "#{CWD}/dst/lib"
53
104
  $INCFLAGS[0,0] = "-I#{CWD}/dst/include "
54
105
 
55
- unless have_library('elf', 'gelf_getshdr')
106
+ unless have_library('elf_ext', 'gelf_getshdr')
56
107
  raise 'libelf build failed'
57
108
  end
58
109
 
110
+ ###
111
+ # libdwarf
112
+
113
+ libdwarf = File.basename('libdwarf-20091118.tar.gz')
114
+ dir = File.basename(libdwarf, '.tar.gz').sub('lib','')
115
+
116
+ unless File.exists?("#{CWD}/dst/lib/libdwarf_ext.a")
117
+ puts "(I'm about to compile libdwarf.. this will definitely take a while)"
118
+
119
+ Dir.chdir('src') do
120
+ FileUtils.rm_rf(dir) if File.exists?(dir)
121
+
122
+ sys("tar zxvf #{libdwarf}")
123
+ Dir.chdir("#{dir}/libdwarf") do
124
+ ENV['CFLAGS'] = "-fPIC -I#{CWD}/dst/include"
125
+ ENV['LDFLAGS'] = "-L#{CWD}/dst/lib"
126
+ sys("./configure")
127
+ sys("make")
128
+
129
+ FileUtils.cp 'libdwarf.a', "#{CWD}/dst/lib/libdwarf_ext.a"
130
+ FileUtils.cp 'dwarf.h', "#{CWD}/dst/include/"
131
+ FileUtils.cp 'libdwarf.h', "#{CWD}/dst/include/"
132
+ end
133
+ end
134
+ end
135
+
136
+ unless have_library('dwarf_ext')
137
+ raise 'libdwarf build failed'
138
+ end
139
+
59
140
  is_elf = true
60
141
  add_define 'HAVE_ELF'
142
+ add_define 'HAVE_DWARF'
61
143
  end
62
144
 
63
145
  if have_header('mach-o/dyld')
@@ -65,6 +147,12 @@ if have_header('mach-o/dyld')
65
147
  add_define 'HAVE_MACH'
66
148
  end
67
149
 
150
+ arch = RUBY_PLATFORM[/(.*)-linux/,1]
151
+ if arch == 'universal'
152
+ arch = 'x86_64'
153
+ end
154
+ add_define "_ARCH_#{arch}_"
155
+
68
156
  if is_elf or is_macho
69
157
  create_makefile('memprof')
70
158
  else
@@ -0,0 +1,106 @@
1
+ #if defined (_ARCH_i386_) || defined(_ARCH_i686_)
2
+
3
+ #include <stdint.h>
4
+ #include <string.h>
5
+
6
+ #include <sys/mman.h>
7
+
8
+ #include "arch.h"
9
+ #include "x86_gen.h"
10
+
11
+ /* This is the stage 1 inline trampoline for hooking the inlined add_freelist
12
+ * function .
13
+ *
14
+ * NOTE: The original instruction mov %reg, freelist is 7 bytes wide,
15
+ * whereas jmpq $displacement is only 5 bytes wide. We *must* pad out
16
+ * the next two bytes. This will be important to remember below.
17
+ */
18
+ struct inline_st1_tramp {
19
+ unsigned char jmp;
20
+ uint32_t displacement;
21
+ unsigned char pad;
22
+ } __attribute__((__packed__)) inline_st1_tramp = {
23
+ .jmp = '\xe9',
24
+ .displacement = 0,
25
+ .pad = '\x90',
26
+ };
27
+
28
+ struct inline_st1_base_short {
29
+ unsigned char mov;
30
+ void *addr;
31
+ } __attribute__((__packed__)) inline_st1_short = {
32
+ .mov = '\xa3',
33
+ .addr = 0,
34
+ };
35
+
36
+ struct inline_st1_base_long {
37
+ unsigned char mov;
38
+ unsigned char src_reg;
39
+ void *addr;
40
+ } __attribute__((__packed__)) inline_st1_long = {
41
+ .mov = '\x89',
42
+ .src_reg = 0,
43
+ .addr = 0
44
+ };
45
+
46
+ static int
47
+ arch_check_ins(unsigned char *base)
48
+ {
49
+
50
+ /* if the byte is 0xa3 then we're moving from %eax, so
51
+ * the length is only 5, so we don't need the pad.
52
+ *
53
+ * otherwise, we're moving from something else, so the
54
+ * length is going to be 6 and we need a NOP.
55
+ */
56
+
57
+ /* is it a mov instruction? */
58
+ if (*base == 0xa3)
59
+ return 0;
60
+ else if (*base == 0x89)
61
+ return 1;
62
+
63
+ return -1;
64
+ }
65
+
66
+ int
67
+ arch_insert_inline_st2_tramp(void *addr, void *marker, void *trampoline, void *table_entry)
68
+ {
69
+ struct inline_st1_base_long *long_base = addr;
70
+ struct inline_st1_base_short *short_base = addr;
71
+ struct inline_st1_tramp *st1_tramp = addr;
72
+ void *mov_target = NULL;
73
+ size_t pad_length = 0;
74
+
75
+ if ((pad_length = arch_check_ins(addr)) == -1)
76
+ return 1;
77
+
78
+ if (pad_length == 0) {
79
+ mov_target = short_base->addr;
80
+ default_inline_st2_tramp.mov = 0x90;
81
+ default_inline_st2_tramp.src_reg = 0xa3;
82
+ inline_st1_tramp.displacement = table_entry - (void *)(short_base + 1);
83
+ default_inline_st2_tramp.jmp_displacement = (void *)(short_base + 1) - (table_entry + sizeof(default_inline_st2_tramp));
84
+ } else {
85
+ mov_target = long_base->addr;
86
+ default_inline_st2_tramp.mov = long_base->mov;
87
+ default_inline_st2_tramp.src_reg = long_base->src_reg;
88
+ inline_st1_tramp.displacement = table_entry - (void *)(long_base + 1) + 1;
89
+ default_inline_st2_tramp.jmp_displacement = (void *)(long_base + 1) - (table_entry + sizeof(default_inline_st2_tramp));
90
+ }
91
+
92
+ if (marker == mov_target) {
93
+ default_inline_st2_tramp.mov_addr= default_inline_st2_tramp.frame.freelist = marker;
94
+ default_inline_st2_tramp.frame.fn_addr = trampoline;
95
+ if (pad_length) {
96
+ copy_instructions(addr, &inline_st1_tramp, sizeof(inline_st1_tramp));
97
+ } else {
98
+ copy_instructions(addr, &inline_st1_tramp, sizeof(inline_st1_tramp) - 1);
99
+ }
100
+ memcpy(table_entry, &default_inline_st2_tramp, sizeof(default_inline_st2_tramp));
101
+ return 0;
102
+ }
103
+
104
+ return 1;
105
+ }
106
+ #endif
@@ -0,0 +1,75 @@
1
+ #if !defined(_i386_h_)
2
+ #define _i386_h_
3
+
4
+ #include <stdint.h>
5
+ #include "arch.h"
6
+
7
+ /*
8
+ * This is the "normal" stage 2 trampoline with a default entry pre-filled
9
+ */
10
+ static struct tramp_st2_entry {
11
+ unsigned char ebx_save;
12
+ unsigned char mov;
13
+ void *addr;
14
+ unsigned char call[2];
15
+ unsigned char ebx_restore;
16
+ unsigned char ret;
17
+ } __attribute__((__packed__)) default_st2_tramp = {
18
+ .ebx_save = 0x53, /* push ebx */
19
+ .mov = 0xbb, /* mov addr into ebx */
20
+ .addr = 0, /* this is filled in later */
21
+ .call = {0xff, 0xd3}, /* calll *ebx */
22
+ .ebx_restore = 0x5b, /* pop ebx */
23
+ .ret = 0xc3, /* ret */
24
+ };
25
+
26
+
27
+ /*
28
+ * This is the inline stage 2 trampoline with a default entry pre-filled
29
+ */
30
+ static struct inline_tramp_st2_entry {
31
+
32
+ /* this block will be filled in at runtime to replicate the overwritten
33
+ * instruction.
34
+ */
35
+ unsigned char mov;
36
+ unsigned char src_reg;
37
+ void *mov_addr;
38
+
39
+ /* this frame will arrange freelist to be passed as an argument to
40
+ * the third and final trampoline (C level).
41
+ */
42
+ struct {
43
+ unsigned char push_ebx;
44
+ unsigned char pushl[2];
45
+ void * freelist;
46
+ unsigned char mov_ebx;
47
+ void * fn_addr;
48
+ unsigned char call[2];
49
+ unsigned char pop_ebx;
50
+ unsigned char restore_ebx;
51
+ } __attribute__((__packed__)) frame;
52
+
53
+ /* this block jumps back to resume execution */
54
+ unsigned char jmp;
55
+ uint32_t jmp_displacement;
56
+ } __attribute__((__packed__)) default_inline_st2_tramp = {
57
+ .mov = 0x89,
58
+ .src_reg = 0,
59
+ .mov_addr = 0,
60
+
61
+ .frame = {
62
+ .push_ebx = 0x53,
63
+ .pushl = {0xff, 0x35},
64
+ .freelist = 0,
65
+ .mov_ebx = 0xbb,
66
+ .fn_addr = 0,
67
+ .call = {0xff, 0xd3},
68
+ .pop_ebx = 0x5b,
69
+ .restore_ebx = 0x5b,
70
+ },
71
+
72
+ .jmp = 0xe9,
73
+ .jmp_displacement = 0,
74
+ };
75
+ #endif
data/ext/mach.c CHANGED
@@ -104,6 +104,18 @@ bin_find_symbol(char *sym, size_t *size) {
104
104
  return ptr;
105
105
  }
106
106
 
107
+ int
108
+ bin_type_size(char *type)
109
+ {
110
+ return -1;
111
+ }
112
+
113
+ int
114
+ bin_type_member_offset(char *type, char *member)
115
+ {
116
+ return -1;
117
+ }
118
+
107
119
  void
108
120
  bin_init()
109
121
  {
@@ -15,27 +15,27 @@
15
15
  #include <intern.h>
16
16
  #include <node.h>
17
17
 
18
+ #include "arch.h"
18
19
  #include "bin_api.h"
19
20
 
20
21
  size_t pagesize;
21
- void *text_segment = NULL;
22
- unsigned long text_segment_len = 0;
23
22
 
24
23
  /*
25
24
  trampoline specific stuff
26
25
  */
27
- struct tramp_tbl_entry *tramp_table = NULL;
26
+ struct tramp_st2_entry *tramp_table = NULL;
28
27
  size_t tramp_size = 0;
29
28
 
30
29
  /*
31
30
  inline trampoline specific stuff
32
31
  */
33
32
  size_t inline_tramp_size = 0;
34
- struct inline_tramp_tbl_entry *inline_tramp_table = NULL;
33
+ struct inline_tramp_st2_entry *inline_tramp_table = NULL;
35
34
 
36
35
  /*
37
36
  * bleak_house stuff
38
37
  */
38
+ static VALUE eUnsupported;
39
39
  static int track_objs = 0;
40
40
  static st_table *objs = NULL;
41
41
 
@@ -45,13 +45,6 @@ struct obj_track {
45
45
  int line;
46
46
  };
47
47
 
48
- static void
49
- error_tramp()
50
- {
51
- printf("WARNING: NO TRAMPOLINE SET.\n");
52
- return;
53
- }
54
-
55
48
  static VALUE
56
49
  newobj_tramp()
57
50
  {
@@ -87,7 +80,6 @@ static void
87
80
  freelist_tramp(unsigned long rval)
88
81
  {
89
82
  struct obj_track *tracker = NULL;
90
-
91
83
  if (track_objs) {
92
84
  st_delete(objs, (st_data_t *) &rval, (st_data_t *) &tracker);
93
85
  if (tracker) {
@@ -157,7 +149,7 @@ objs_to_array(st_data_t key, st_data_t record, st_data_t arg)
157
149
  unsigned long count = (unsigned long)record;
158
150
  char *source = (char *)key;
159
151
 
160
- asprintf(&(res->entries[res->num_entries++]), "%7d %s", count, source);
152
+ asprintf(&(res->entries[res->num_entries++]), "%7li %s", count, source);
161
153
 
162
154
  free(source);
163
155
  return ST_DELETE;
@@ -231,6 +223,9 @@ memprof_stats(int argc, VALUE *argv, VALUE self)
231
223
  }
232
224
  free(res.entries);
233
225
 
226
+ if (out)
227
+ fclose(out);
228
+
234
229
  track_objs = 1;
235
230
  return Qnil;
236
231
  }
@@ -256,46 +251,518 @@ memprof_track(int argc, VALUE *argv, VALUE self)
256
251
  return Qnil;
257
252
  }
258
253
 
254
+ #include <yajl/yajl_gen.h>
255
+ #include <stdarg.h>
256
+ #include "env.h"
257
+ #include "re.h"
258
+
259
+ yajl_gen_status
260
+ yajl_gen_cstr(yajl_gen gen, const char * str)
261
+ {
262
+ if (!str || str[0] == 0)
263
+ return yajl_gen_null(gen);
264
+ else
265
+ return yajl_gen_string(gen, (unsigned char *)str, strlen(str));
266
+ }
267
+
268
+ yajl_gen_status
269
+ yajl_gen_format(yajl_gen gen, char *format, ...)
270
+ {
271
+ va_list args;
272
+ char *str;
273
+ yajl_gen_status ret;
274
+
275
+ va_start(args, format);
276
+ vasprintf(&str, format, args);
277
+ va_end(args);
278
+
279
+ ret = yajl_gen_string(gen, (unsigned char *)str, strlen(str));
280
+ free(str);
281
+ return ret;
282
+ }
283
+
284
+ yajl_gen_status
285
+ yajl_gen_value(yajl_gen gen, VALUE obj)
286
+ {
287
+ if (FIXNUM_P(obj))
288
+ return yajl_gen_integer(gen, FIX2INT(obj));
289
+ else if (NIL_P(obj) || obj == Qundef)
290
+ return yajl_gen_null(gen);
291
+ else if (obj == Qtrue)
292
+ return yajl_gen_bool(gen, 1);
293
+ else if (obj == Qfalse)
294
+ return yajl_gen_bool(gen, 0);
295
+ else if (SYMBOL_P(obj))
296
+ return yajl_gen_format(gen, ":%s", rb_id2name(SYM2ID(obj)));
297
+ else
298
+ return yajl_gen_format(gen, "0x%x", obj);
299
+ }
300
+
301
+ static int
302
+ each_hash_entry(st_data_t key, st_data_t record, st_data_t arg)
303
+ {
304
+ yajl_gen gen = (yajl_gen)arg;
305
+ VALUE k = (VALUE)key;
306
+ VALUE v = (VALUE)record;
307
+
308
+ yajl_gen_value(gen, k);
309
+ yajl_gen_value(gen, v);
310
+
311
+ return ST_CONTINUE;
312
+ }
313
+
314
+ static int
315
+ each_ivar(st_data_t key, st_data_t record, st_data_t arg)
316
+ {
317
+ yajl_gen gen = (yajl_gen)arg;
318
+ ID id = (ID)key;
319
+ VALUE val = (VALUE)record;
320
+
321
+ yajl_gen_cstr(gen, rb_id2name(id));
322
+ yajl_gen_value(gen, val);
323
+
324
+ return ST_CONTINUE;
325
+ }
326
+
327
+ char *
328
+ nd_type_str(VALUE obj)
329
+ {
330
+ switch(nd_type(obj)) {
331
+ #define ND(type) case NODE_##type: return #type;
332
+ ND(METHOD); ND(FBODY); ND(CFUNC); ND(SCOPE);
333
+ ND(BLOCK); ND(IF); ND(CASE); ND(WHEN);
334
+ ND(OPT_N); ND(WHILE); ND(UNTIL); ND(ITER);
335
+ ND(FOR); ND(BREAK); ND(NEXT); ND(REDO);
336
+ ND(RETRY); ND(BEGIN); ND(RESCUE); ND(RESBODY);
337
+ ND(ENSURE); ND(AND); ND(OR); ND(NOT);
338
+ ND(MASGN); ND(LASGN); ND(DASGN); ND(DASGN_CURR);
339
+ ND(GASGN); ND(IASGN); ND(CDECL); ND(CVASGN);
340
+ ND(CVDECL); ND(OP_ASGN1); ND(OP_ASGN2); ND(OP_ASGN_AND);
341
+ ND(OP_ASGN_OR); ND(CALL); ND(FCALL); ND(VCALL);
342
+ ND(SUPER); ND(ZSUPER); ND(ARRAY); ND(ZARRAY);
343
+ ND(HASH); ND(RETURN); ND(YIELD); ND(LVAR);
344
+ ND(DVAR); ND(GVAR); ND(IVAR); ND(CONST);
345
+ ND(CVAR); ND(NTH_REF); ND(BACK_REF); ND(MATCH);
346
+ ND(MATCH2); ND(MATCH3); ND(LIT); ND(STR);
347
+ ND(DSTR); ND(XSTR); ND(DXSTR); ND(EVSTR);
348
+ ND(DREGX); ND(DREGX_ONCE); ND(ARGS); ND(ARGSCAT);
349
+ ND(ARGSPUSH); ND(SPLAT); ND(TO_ARY); ND(SVALUE);
350
+ ND(BLOCK_ARG); ND(BLOCK_PASS); ND(DEFN); ND(DEFS);
351
+ ND(ALIAS); ND(VALIAS); ND(UNDEF); ND(CLASS);
352
+ ND(MODULE); ND(SCLASS); ND(COLON2); ND(COLON3)
353
+ ND(CREF); ND(DOT2); ND(DOT3); ND(FLIP2);
354
+ ND(FLIP3); ND(ATTRSET); ND(SELF); ND(NIL);
355
+ ND(TRUE); ND(FALSE); ND(DEFINED); ND(NEWLINE);
356
+ ND(POSTEXE); ND(ALLOCA); ND(DMETHOD); ND(BMETHOD);
357
+ ND(MEMO); ND(IFUNC); ND(DSYM); ND(ATTRASGN);
358
+ ND(LAST);
359
+ default:
360
+ return "unknown";
361
+ }
362
+ }
363
+
364
+ static VALUE (*rb_classname)(VALUE);
365
+
366
+ /* TODO
367
+ * look for FL_EXIVAR flag and print ivars
368
+ * print more detail about Proc/struct BLOCK in T_DATA if freefunc == blk_free
369
+ * add Memprof.dump_all for full heap dump
370
+ * print details on different types of nodes (nd_next, nd_lit, nd_nth, etc)
371
+ */
372
+
373
+ void
374
+ obj_dump(VALUE obj, yajl_gen gen)
375
+ {
376
+ int type;
377
+ yajl_gen_map_open(gen);
378
+
379
+ yajl_gen_cstr(gen, "address");
380
+ yajl_gen_value(gen, obj);
381
+
382
+ struct obj_track *tracker = NULL;
383
+ if (st_lookup(objs, (st_data_t)obj, (st_data_t *)&tracker)) {
384
+ yajl_gen_cstr(gen, "source");
385
+ yajl_gen_format(gen, "%s:%d", tracker->source, tracker->line);
386
+ }
387
+
388
+ yajl_gen_cstr(gen, "type");
389
+ switch (type=BUILTIN_TYPE(obj)) {
390
+ case T_DATA:
391
+ yajl_gen_cstr(gen, "data");
392
+
393
+ if (RBASIC(obj)->klass) {
394
+ yajl_gen_cstr(gen, "class");
395
+ yajl_gen_value(gen, RBASIC(obj)->klass);
396
+
397
+ yajl_gen_cstr(gen, "class_name");
398
+ VALUE name = rb_classname(RBASIC(obj)->klass);
399
+ if (RTEST(name))
400
+ yajl_gen_cstr(gen, RSTRING(name)->ptr);
401
+ else
402
+ yajl_gen_cstr(gen, 0);
403
+ }
404
+ break;
405
+
406
+ case T_FILE:
407
+ yajl_gen_cstr(gen, "file");
408
+ break;
409
+
410
+ case T_FLOAT:
411
+ yajl_gen_cstr(gen, "float");
412
+
413
+ yajl_gen_cstr(gen, "data");
414
+ yajl_gen_double(gen, RFLOAT(obj)->value);
415
+ break;
416
+
417
+ case T_BIGNUM:
418
+ yajl_gen_cstr(gen, "bignum");
419
+
420
+ yajl_gen_cstr(gen, "negative");
421
+ yajl_gen_bool(gen, RBIGNUM(obj)->sign == 0);
422
+
423
+ yajl_gen_cstr(gen, "length");
424
+ yajl_gen_integer(gen, RBIGNUM(obj)->len);
425
+
426
+ yajl_gen_cstr(gen, "data");
427
+ yajl_gen_string(gen, RBIGNUM(obj)->digits, RBIGNUM(obj)->len);
428
+ break;
429
+
430
+ case T_MATCH:
431
+ yajl_gen_cstr(gen, "match");
432
+
433
+ yajl_gen_cstr(gen, "data");
434
+ yajl_gen_value(gen, RMATCH(obj)->str);
435
+ break;
436
+
437
+ case T_REGEXP:
438
+ yajl_gen_cstr(gen, "regexp");
439
+
440
+ yajl_gen_cstr(gen, "length");
441
+ yajl_gen_integer(gen, RREGEXP(obj)->len);
442
+
443
+ yajl_gen_cstr(gen, "data");
444
+ yajl_gen_cstr(gen, RREGEXP(obj)->str);
445
+ break;
446
+
447
+ case T_SCOPE:
448
+ yajl_gen_cstr(gen, "scope");
449
+
450
+ struct SCOPE *scope = (struct SCOPE *)obj;
451
+ if (scope->local_tbl) {
452
+ int i = 1;
453
+ int n = scope->local_tbl[0];
454
+ VALUE *list = &scope->local_vars[-1];
455
+ VALUE cur = *list++;
456
+
457
+ yajl_gen_cstr(gen, "node");
458
+ yajl_gen_value(gen, cur);
459
+
460
+ if (n) {
461
+ yajl_gen_cstr(gen, "variables");
462
+ yajl_gen_map_open(gen);
463
+ while (n--) {
464
+ cur = *list++;
465
+ yajl_gen_cstr(gen, scope->local_tbl[i] == 95 ? "_" : rb_id2name(scope->local_tbl[i]));
466
+ yajl_gen_value(gen, cur);
467
+ i++;
468
+ }
469
+ yajl_gen_map_close(gen);
470
+ }
471
+ }
472
+ break;
473
+
474
+ case T_NODE:
475
+ yajl_gen_cstr(gen, "node");
476
+
477
+ yajl_gen_cstr(gen, "node_type");
478
+ yajl_gen_cstr(gen, nd_type_str(obj));
479
+
480
+ yajl_gen_cstr(gen, "file");
481
+ yajl_gen_cstr(gen, RNODE(obj)->nd_file);
482
+
483
+ yajl_gen_cstr(gen, "line");
484
+ yajl_gen_integer(gen, nd_line(obj));
485
+
486
+ yajl_gen_cstr(gen, "node_code");
487
+ yajl_gen_integer(gen, nd_type(obj));
488
+
489
+ switch (nd_type(obj)) {
490
+ case NODE_SCOPE:
491
+ break;
492
+ }
493
+ break;
494
+
495
+ case T_STRING:
496
+ yajl_gen_cstr(gen, "string");
497
+
498
+ yajl_gen_cstr(gen, "length");
499
+ yajl_gen_integer(gen, RSTRING(obj)->len);
500
+
501
+ if (FL_TEST(obj, ELTS_SHARED|FL_USER3)) {
502
+ yajl_gen_cstr(gen, "shared");
503
+ yajl_gen_value(gen, RSTRING(obj)->aux.shared);
504
+
505
+ yajl_gen_cstr(gen, "flags");
506
+ yajl_gen_array_open(gen);
507
+ if (FL_TEST(obj, ELTS_SHARED))
508
+ yajl_gen_cstr(gen, "elts_shared");
509
+ if (FL_TEST(obj, FL_USER3))
510
+ yajl_gen_cstr(gen, "str_assoc");
511
+ yajl_gen_array_close(gen);
512
+ } else {
513
+ yajl_gen_cstr(gen, "data");
514
+ yajl_gen_string(gen, (unsigned char *)RSTRING(obj)->ptr, RSTRING(obj)->len);
515
+ }
516
+ break;
517
+
518
+ case T_VARMAP:
519
+ yajl_gen_cstr(gen, "varmap");
520
+
521
+ struct RVarmap *vars = (struct RVarmap *)obj;
522
+
523
+ if (vars->next) {
524
+ yajl_gen_cstr(gen, "next");
525
+ yajl_gen_value(gen, (VALUE)vars->next);
526
+ }
527
+
528
+ if (vars->id) {
529
+ yajl_gen_cstr(gen, "data");
530
+ yajl_gen_map_open(gen);
531
+ yajl_gen_cstr(gen, rb_id2name(vars->id));
532
+ yajl_gen_value(gen, vars->val);
533
+ yajl_gen_map_close(gen);
534
+ }
535
+ break;
536
+
537
+ case T_CLASS:
538
+ case T_MODULE:
539
+ case T_ICLASS:
540
+ yajl_gen_cstr(gen, type==T_CLASS ? "class" : type==T_MODULE ? "module" : "iclass");
541
+
542
+ yajl_gen_cstr(gen, "name");
543
+ VALUE name = rb_classname(obj);
544
+ if (RTEST(name))
545
+ yajl_gen_cstr(gen, RSTRING(name)->ptr);
546
+ else
547
+ yajl_gen_cstr(gen, 0);
548
+
549
+ yajl_gen_cstr(gen, "super");
550
+ yajl_gen_value(gen, RCLASS(obj)->super);
551
+
552
+ yajl_gen_cstr(gen, "super_name");
553
+ VALUE super_name = rb_classname(RCLASS(obj)->super);
554
+ if (RTEST(super_name))
555
+ yajl_gen_cstr(gen, RSTRING(super_name)->ptr);
556
+ else
557
+ yajl_gen_cstr(gen, 0);
558
+
559
+ if (FL_TEST(obj, FL_SINGLETON)) {
560
+ yajl_gen_cstr(gen, "singleton");
561
+ yajl_gen_bool(gen, 1);
562
+ }
563
+
564
+ if (RCLASS(obj)->iv_tbl && RCLASS(obj)->iv_tbl->num_entries) {
565
+ yajl_gen_cstr(gen, "ivars");
566
+ yajl_gen_map_open(gen);
567
+ st_foreach(RCLASS(obj)->iv_tbl, each_ivar, (st_data_t)gen);
568
+ yajl_gen_map_close(gen);
569
+ }
570
+
571
+ if (type != T_ICLASS && RCLASS(obj)->m_tbl && RCLASS(obj)->m_tbl->num_entries) {
572
+ yajl_gen_cstr(gen, "methods");
573
+ yajl_gen_map_open(gen);
574
+ st_foreach(RCLASS(obj)->m_tbl, each_ivar, (st_data_t)gen);
575
+ yajl_gen_map_close(gen);
576
+ }
577
+ break;
578
+
579
+ case T_OBJECT:
580
+ yajl_gen_cstr(gen, "object");
581
+
582
+ yajl_gen_cstr(gen, "class");
583
+ yajl_gen_value(gen, RBASIC(obj)->klass);
584
+
585
+ yajl_gen_cstr(gen, "class_name");
586
+ yajl_gen_cstr(gen, rb_obj_classname(obj));
587
+
588
+ struct RClass *klass = RCLASS(obj);
589
+
590
+ if (klass->iv_tbl && klass->iv_tbl->num_entries) {
591
+ yajl_gen_cstr(gen, "ivars");
592
+ yajl_gen_map_open(gen);
593
+ st_foreach(klass->iv_tbl, each_ivar, (st_data_t)gen);
594
+ yajl_gen_map_close(gen);
595
+ }
596
+ break;
597
+
598
+ case T_ARRAY:
599
+ yajl_gen_cstr(gen, "array");
600
+
601
+ struct RArray *ary = RARRAY(obj);
602
+
603
+ yajl_gen_cstr(gen, "length");
604
+ yajl_gen_integer(gen, ary->len);
605
+
606
+ if (FL_TEST(obj, ELTS_SHARED)) {
607
+ yajl_gen_cstr(gen, "shared");
608
+ yajl_gen_value(gen, ary->aux.shared);
609
+ } else if (ary->len) {
610
+ yajl_gen_cstr(gen, "data");
611
+ yajl_gen_array_open(gen);
612
+ int i;
613
+ for(i=0; i < ary->len; i++)
614
+ yajl_gen_value(gen, ary->ptr[i]);
615
+ yajl_gen_array_close(gen);
616
+ }
617
+ break;
618
+
619
+ case T_HASH:
620
+ yajl_gen_cstr(gen, "hash");
621
+
622
+ struct RHash *hash = RHASH(obj);
623
+
624
+ yajl_gen_cstr(gen, "length");
625
+ if (hash->tbl)
626
+ yajl_gen_integer(gen, hash->tbl->num_entries);
627
+ else
628
+ yajl_gen_integer(gen, 0);
629
+
630
+ yajl_gen_cstr(gen, "default");
631
+ yajl_gen_value(gen, hash->ifnone);
632
+
633
+ if (hash->tbl && hash->tbl->num_entries) {
634
+ yajl_gen_cstr(gen, "data");
635
+ yajl_gen_map_open(gen);
636
+ st_foreach(hash->tbl, each_hash_entry, (st_data_t)gen);
637
+ yajl_gen_map_close(gen);
638
+ }
639
+ break;
640
+
641
+ default:
642
+ yajl_gen_cstr(gen, "unknown");
643
+ }
644
+
645
+ yajl_gen_cstr(gen, "code");
646
+ yajl_gen_integer(gen, BUILTIN_TYPE(obj));
647
+
648
+ yajl_gen_map_close(gen);
649
+ }
650
+
651
+ static int
652
+ objs_each_dump(st_data_t key, st_data_t record, st_data_t arg)
653
+ {
654
+ obj_dump((VALUE)key, (yajl_gen)arg);
655
+ return ST_CONTINUE;
656
+ }
657
+
658
+ void
659
+ json_print(void *ctx, const char * str, unsigned int len)
660
+ {
661
+ FILE *out = (FILE *)ctx;
662
+ fwrite(str, sizeof(char), len, out ? out : stdout);
663
+ }
664
+
665
+ static VALUE
666
+ memprof_dump(int argc, VALUE *argv, VALUE self)
667
+ {
668
+ VALUE str;
669
+ FILE *out = NULL;
670
+
671
+ if (!track_objs)
672
+ rb_raise(rb_eRuntimeError, "object tracking disabled, call Memprof.start first");
673
+
674
+ rb_scan_args(argc, argv, "01", &str);
675
+
676
+ if (RTEST(str)) {
677
+ out = fopen(StringValueCStr(str), "w");
678
+ if (!out)
679
+ rb_raise(rb_eArgError, "unable to open output file");
680
+ }
681
+
682
+ yajl_gen_config conf = { .beautify = 1, .indentString = " " };
683
+ yajl_gen gen = yajl_gen_alloc2((yajl_print_t)&json_print, &conf, NULL, (void*)out);
684
+
685
+ track_objs = 0;
686
+
687
+ yajl_gen_array_open(gen);
688
+ st_foreach(objs, objs_each_dump, (st_data_t)gen);
689
+ yajl_gen_array_close(gen);
690
+ yajl_gen_free(gen);
691
+
692
+ if (out)
693
+ fclose(out);
694
+
695
+ track_objs = 1;
696
+
697
+ return Qnil;
698
+ }
699
+
700
+ static VALUE
701
+ memprof_dump_all(int argc, VALUE *argv, VALUE self)
702
+ {
703
+ int sizeof_RVALUE = bin_type_size("RVALUE");
704
+ char *heaps = *(char**)bin_find_symbol("heaps",0);
705
+ int heaps_used = *(int*)bin_find_symbol("heaps_used",0);
706
+ int sizeof_heaps_slot = bin_type_size("heaps_slot");
707
+ int offset_limit = bin_type_member_offset("heaps_slot", "limit");
708
+ int offset_slot = bin_type_member_offset("heaps_slot", "slot");
709
+ char *p, *pend;
710
+ int i, limit;
711
+
712
+ if (sizeof_RVALUE < 0 || sizeof_heaps_slot < 0)
713
+ rb_raise(eUnsupported, "could not find internal heap");
714
+
715
+ VALUE str;
716
+ FILE *out = NULL;
717
+
718
+ rb_scan_args(argc, argv, "01", &str);
719
+
720
+ if (RTEST(str)) {
721
+ out = fopen(StringValueCStr(str), "w");
722
+ if (!out)
723
+ rb_raise(rb_eArgError, "unable to open output file");
724
+ }
725
+
726
+ yajl_gen_config conf = { .beautify = 1, .indentString = " " };
727
+ yajl_gen gen = yajl_gen_alloc2((yajl_print_t)&json_print, &conf, NULL, (void*)out);
728
+
729
+ track_objs = 0;
730
+
731
+ yajl_gen_array_open(gen);
732
+
733
+ for (i=0; i < heaps_used; i++) {
734
+ p = *(char**)(heaps + (i * sizeof_heaps_slot) + offset_slot);
735
+ limit = *(int*)(heaps + (i * sizeof_heaps_slot) + offset_limit);
736
+ pend = p + (sizeof_RVALUE * limit);
737
+
738
+ while (p < pend) {
739
+ if (RBASIC(p)->flags)
740
+ obj_dump((VALUE)p, gen);
741
+
742
+ p += sizeof_RVALUE;
743
+ }
744
+ }
745
+
746
+ yajl_gen_array_close(gen);
747
+ yajl_gen_free(gen);
748
+
749
+ if (out)
750
+ fclose(out);
751
+
752
+ track_objs = 1;
753
+
754
+ return Qnil;
755
+ }
756
+
259
757
  static void
260
758
  create_tramp_table()
261
759
  {
262
760
  int i = 0;
263
761
  void *region = NULL;
762
+ size_t tramp_sz = 0, inline_tramp_sz = 0;
264
763
 
265
- struct tramp_tbl_entry ent = {
266
- .rbx_save = {'\x53'}, // push rbx
267
- .mov = {'\x48', '\xbb'}, // mov addr into rbx
268
- .addr = error_tramp, // ^^^
269
- .callq = {'\xff', '\xd3'}, // callq rbx
270
- .rbx_restore = {'\x5b'}, // pop rbx
271
- .ret = {'\xc3'}, // ret
272
- };
273
-
274
- struct inline_tramp_tbl_entry inline_ent = {
275
- .rex = {'\x48'},
276
- .mov = {'\x89'},
277
- .src_reg = {'\x05'},
278
- .mov_displacement = 0,
279
-
280
- .frame = {
281
- .push_rdi = {'\x57'},
282
- .mov_rdi = {'\x48', '\x8b', '\x3d'},
283
- .rdi_source_displacement = 0,
284
- .push_rbx = {'\x53'},
285
- .push_rbp = {'\x55'},
286
- .save_rsp = {'\x48', '\x89', '\xe5'},
287
- .align_rsp = {'\x48', '\x83', '\xe4', '\xf0'},
288
- .mov = {'\x48', '\xbb'},
289
- .addr = error_tramp,
290
- .callq = {'\xff', '\xd3'},
291
- .leave = {'\xc9'},
292
- .rbx_restore = {'\x5b'},
293
- .rdi_restore = {'\x5f'},
294
- },
295
-
296
- .jmp = {'\xe9'},
297
- .jmp_displacement = 0,
298
- };
764
+ void *ent = arch_get_st2_tramp(&tramp_sz);
765
+ void *inline_ent = arch_get_inline_st2_tramp(&inline_tramp_sz);
299
766
 
300
767
  if ((region = bin_allocate_page()) == MAP_FAILED) {
301
768
  fprintf(stderr, "Failed to allocate memory for stage 1 trampolines.\n");
@@ -305,159 +772,72 @@ create_tramp_table()
305
772
  tramp_table = region;
306
773
  inline_tramp_table = region + pagesize/2;
307
774
 
308
- for (i = 0; i < (pagesize/2)/sizeof(struct tramp_tbl_entry); i++) {
309
- memcpy(tramp_table + i, &ent, sizeof(struct tramp_tbl_entry));
775
+ for (i = 0; i < (pagesize/2)/tramp_sz; i++) {
776
+ memcpy(tramp_table + i, ent, tramp_sz);
310
777
  }
311
778
 
312
- for (i = 0; i < (pagesize/2)/sizeof(struct inline_tramp_tbl_entry); i++) {
313
- memcpy(inline_tramp_table + i, &inline_ent, sizeof(struct inline_tramp_tbl_entry));
779
+ for (i = 0; i < (pagesize/2)/inline_tramp_sz; i++) {
780
+ memcpy(inline_tramp_table + i, inline_ent, inline_tramp_sz);
314
781
  }
315
782
  }
316
783
 
317
- void
318
- update_callqs(int entry, void *trampee_addr)
784
+ #define FREELIST_INLINES (3)
785
+
786
+ static void
787
+ hook_freelist(int entry)
319
788
  {
320
- char *byte = text_segment;
321
- size_t count = 0;
322
- int fn_addr = 0;
323
- void *aligned_addr = NULL;
324
-
325
- for(; count < text_segment_len; count++) {
326
- if (*byte == '\xe8') {
327
- fn_addr = *(int *)(byte+1);
328
- if (((void *)trampee_addr - (void *)(byte+5)) == fn_addr) {
329
- aligned_addr = (void*)(((long)byte+1)&~(0xffff));
330
- mprotect(aligned_addr, (((void *)byte+1) - aligned_addr) + 10, PROT_READ|PROT_WRITE|PROT_EXEC);
331
- *(int *)(byte+1) = (uint32_t)((void *)(tramp_table + entry) - (void *)(byte + 5));
332
- mprotect(aligned_addr, (((void *)byte+1) - aligned_addr) + 10, PROT_READ|PROT_EXEC);
333
- }
789
+ size_t sizes[FREELIST_INLINES], i = 0;
790
+ void *freelist_inliners[FREELIST_INLINES];
791
+ void *freelist = NULL;
792
+ unsigned char *byte = NULL;
793
+
794
+ freelist_inliners[0] = bin_find_symbol("gc_sweep", &sizes[0]);
795
+ /* sometimes gc_sweep gets inlined in garbage_collect */
796
+ if (!freelist_inliners[0]) {
797
+ freelist_inliners[0] = bin_find_symbol("garbage_collect", &sizes[0]);
798
+ if (!freelist_inliners[0]) {
799
+ /* couldn't find garbage_collect either. */
800
+ fprintf(stderr, "Couldn't find gc_sweep or garbage_collect!\n");
801
+ return;
334
802
  }
335
- byte++;
336
803
  }
337
- }
338
804
 
805
+ freelist_inliners[1] = bin_find_symbol("finalize_list", &sizes[1]);
806
+ if (!freelist_inliners[1]) {
807
+ fprintf(stderr, "Couldn't find finalize_list!\n");
808
+ /* XXX continue or exit? */
809
+ }
339
810
 
340
- static void
341
- hook_freelist(int entry)
342
- {
343
- long sizes[] = { 0, 0, 0 };
344
- void *sym1 = bin_find_symbol("gc_sweep", &sizes[0]);
811
+ freelist_inliners[2] = bin_find_symbol("rb_gc_force_recycle", &sizes[2]);
812
+ if (!freelist_inliners[2]) {
813
+ fprintf(stderr, "Couldn't find rb_gc_force_recycle!\n");
814
+ /* XXX continue or exit? */
815
+ }
345
816
 
346
- if (sym1 == NULL) {
347
- /* this is MRI ... */
348
- sym1 = bin_find_symbol("garbage_collect", &sizes[0]);
817
+ freelist = bin_find_symbol("freelist", NULL);
818
+ if (!freelist) {
819
+ fprintf(stderr, "Couldn't find freelist!\n");
820
+ return;
349
821
  }
350
822
 
351
- void *sym2 = bin_find_symbol("finalize_list", &sizes[1]);
352
- void *sym3 = bin_find_symbol("rb_gc_force_recycle", &sizes[2]);
353
- void *freelist_callers[] = { sym1, sym2, sym3 };
354
- int max = 3;
355
- size_t i = 0;
356
- char *byte = freelist_callers[0];
357
- void *freelist = bin_find_symbol("freelist", NULL);
358
- uint32_t mov_target = 0;
359
- void *aligned_addr = NULL;
360
- size_t count = 0;
361
-
362
- /* This is the stage 1 trampoline for hooking the inlined add_freelist
363
- * function .
364
- *
365
- * NOTE: The original instruction mov %reg, freelist is 7 bytes wide,
366
- * whereas jmpq $displacement is only 5 bytes wide. We *must* pad out
367
- * the next two bytes. This will be important to remember below.
368
- */
369
- struct tramp_inline tramp = {
370
- .jmp = {'\xe9'},
371
- .displacement = 0,
372
- .pad = {'\x90', '\x90'},
373
- };
374
-
375
- struct inline_tramp_tbl_entry *inl_tramp_st2 = NULL;
376
-
377
- for (;i < max;) {
378
- /* make sure it is a mov instruction */
379
- if (byte[1] == '\x89') {
380
-
381
- /* Read the REX byte to make sure it is a mov that we care about */
382
- if ((byte[0] == '\x48') ||
383
- (byte[0] == '\x4c')) {
384
-
385
- /* Grab the target of the mov. REMEMBER: in this case the target is
386
- * a 32bit displacment that gets added to RIP (where RIP is the adress of
387
- * the next instruction).
388
- */
389
- mov_target = *(uint32_t *)(byte + 3);
390
-
391
- /* Sanity check. Ensure that the displacement from freelist to the next
392
- * instruction matches the mov_target. If so, we know this mov is
393
- * updating freelist.
394
- */
395
- if ((freelist - (void *)(byte+7)) == mov_target) {
396
- /* Before the stage 1 trampoline gets written, we need to generate
397
- * the code for the stage 2 trampoline. Let's copy over the REX byte
398
- * and the byte which mentions the source register into the stage 2
399
- * trampoline.
400
- */
401
- inl_tramp_st2 = inline_tramp_table + entry;
402
- inl_tramp_st2->rex[0] = byte[0];
403
- inl_tramp_st2->src_reg[0] = byte[2];
404
-
405
- /* Setup the stage 1 trampoline. Calculate the displacement to
406
- * the stage 2 trampoline from the next instruction.
407
- *
408
- * REMEMBER!!!! The next instruction will be NOP after our stage 1
409
- * trampoline is written. This is 5 bytes into the structure, even
410
- * though the original instruction we overwrote was 7 bytes.
411
- */
412
- tramp.displacement = (uint32_t)((void *)(inl_tramp_st2) - (void *)(byte+5));
413
-
414
- /* Figure out what page the stage 1 tramp is gonna be written to, mark
415
- * it WRITE, write the trampoline in, and then remove WRITE permission.
416
- */
417
- aligned_addr = (void*)(((long)byte)&~(0xffff));
418
- mprotect(aligned_addr, (((void *)byte) - aligned_addr) + 10, PROT_READ|PROT_WRITE|PROT_EXEC);
419
- memcpy(byte, &tramp, sizeof(struct tramp_inline));
420
- mprotect(aligned_addr, (((void *)byte) - aligned_addr) + 10, PROT_READ|PROT_EXEC);
421
-
422
- /* Finish setting up the stage 2 trampoline. */
423
-
424
- /* calculate the displacement to freelist from the next instruction.
425
- *
426
- * This is used to replicate the original instruction we overwrote.
427
- */
428
- inl_tramp_st2->mov_displacement = freelist - (void *)&(inl_tramp_st2->frame);
429
-
430
- /* fill in the displacement to freelist from the next instruction.
431
- *
432
- * This is to arrange for the new value in freelist to be in %rdi, and as such
433
- * be the first argument to the C handler. As per the amd64 ABI.
434
- */
435
- inl_tramp_st2->frame.rdi_source_displacement = freelist - (void *)&(inl_tramp_st2->frame.push_rbx);
436
-
437
- /* jmp back to the instruction after stage 1 trampoline was inserted
438
- *
439
- * This can be 5 or 7, it doesn't matter. If its 5, we'll hit our 2
440
- * NOPS. If its 7, we'll land directly on the next instruction.
441
- */
442
- inl_tramp_st2->jmp_displacement = (uint32_t)((void *)(byte + 7) -
443
- (void *)(inline_tramp_table + entry + 1));
444
-
445
- /* write the address of our C level trampoline in to the structure */
446
- inl_tramp_st2->frame.addr = freelist_tramp;
447
-
448
- /* track the new entry and new trampoline size */
449
- entry++;
450
- inline_tramp_size++;
451
- }
452
- }
823
+ /* start the search for places to insert the inline tramp */
824
+
825
+ byte = freelist_inliners[i];
826
+
827
+ while (i < FREELIST_INLINES) {
828
+ if (arch_insert_inline_st2_tramp(byte, freelist, freelist_tramp,
829
+ &inline_tramp_table[entry]) == 0) {
830
+ /* insert occurred, so increment internal counters for the tramp table */
831
+ entry++;
832
+ inline_tramp_size++;
453
833
  }
454
834
 
455
- if (count >= sizes[i]) {
456
- count = 0;
457
- i ++;
458
- byte = freelist_callers[i];
835
+ /* if we've looked at all the bytes in this function... */
836
+ if (((void *)byte - freelist_inliners[i]) >= sizes[i]) {
837
+ /* move on to the next function */
838
+ i++;
839
+ byte = freelist_inliners[i];
459
840
  }
460
- count++;
461
841
  byte++;
462
842
  }
463
843
  }
@@ -466,13 +846,14 @@ static void
466
846
  insert_tramp(char *trampee, void *tramp)
467
847
  {
468
848
  void *trampee_addr = bin_find_symbol(trampee, NULL);
849
+ void *plt_addr = NULL;
469
850
  int entry = tramp_size;
470
851
  int inline_ent = inline_tramp_size;
852
+ void *info;
471
853
 
472
854
  if (trampee_addr == NULL) {
473
855
  if (strcmp("add_freelist", trampee) == 0) {
474
856
  /* XXX super hack */
475
- inline_tramp_table[inline_tramp_size].frame.addr = tramp;
476
857
  inline_tramp_size++;
477
858
  hook_freelist(inline_ent);
478
859
  } else {
@@ -480,8 +861,8 @@ insert_tramp(char *trampee, void *tramp)
480
861
  }
481
862
  } else {
482
863
  tramp_table[tramp_size].addr = tramp;
864
+ bin_update_image(entry, trampee, &tramp_table[tramp_size]);
483
865
  tramp_size++;
484
- bin_update_image(entry, trampee_addr);
485
866
  }
486
867
  }
487
868
 
@@ -489,11 +870,14 @@ void
489
870
  Init_memprof()
490
871
  {
491
872
  VALUE memprof = rb_define_module("Memprof");
873
+ eUnsupported = rb_define_class_under(memprof, "Unsupported", rb_eStandardError);
492
874
  rb_define_singleton_method(memprof, "start", memprof_start, 0);
493
875
  rb_define_singleton_method(memprof, "stop", memprof_stop, 0);
494
876
  rb_define_singleton_method(memprof, "stats", memprof_stats, -1);
495
877
  rb_define_singleton_method(memprof, "stats!", memprof_stats_bang, -1);
496
878
  rb_define_singleton_method(memprof, "track", memprof_track, -1);
879
+ rb_define_singleton_method(memprof, "dump", memprof_dump, -1);
880
+ rb_define_singleton_method(memprof, "dump_all", memprof_dump_all, -1);
497
881
 
498
882
  pagesize = getpagesize();
499
883
  objs = st_init_numtable();
@@ -507,5 +891,10 @@ Init_memprof()
507
891
  insert_tramp("add_freelist", freelist_tramp);
508
892
  #endif
509
893
 
894
+ rb_classname = bin_find_symbol("classname", 0);
895
+
896
+ if (getenv("MEMPROF"))
897
+ track_objs = 1;
898
+
510
899
  return;
511
900
  }