memprof 0.1.3 → 0.2.0

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