memprof 0.3.0 → 0.3.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.
data/bin/memprof CHANGED
@@ -26,7 +26,8 @@ class MemprofUploader
26
26
  opts.on("-n", "--name <name>", "Name for your dump (required)") {|arg| @name = arg }
27
27
  opts.on("-k", "--key <key>", "Memprof.com API key (required)") {|arg| @key = arg }
28
28
  opts.on("-d", "--[no-]delete", "Delete dump file after uploading (default true)") {|arg| @delete_dump = arg }
29
- opts.on("-s", "--seconds <seconds>", "Seconds to wait for the dump (default 60)") {|arg| @secs = arg}
29
+ opts.on("-s", "--seconds <seconds>",
30
+ Integer, "Seconds to wait for the dump (default 300)") {|arg| @secs = arg }
30
31
  opts.on("-t", "--[no-]test", "Test run (don't actually upload) (default false)") {|arg| @test = arg }
31
32
  opts.on("-f", "--file <path>", "Upload specific json dump (optional)") {|arg| @file = arg }
32
33
  opts.on("--put-my-data-on-the-internet", "Confirm that you understand\n" +
@@ -48,7 +49,7 @@ class MemprofUploader
48
49
  @delete_dump = true if @delete_dump.nil?
49
50
 
50
51
  # Make this default to 60 if the user didn't pass the number of seconds.
51
- @secs = 60 if @secs.nil?
52
+ @secs ||= 300
52
53
 
53
54
  if @file
54
55
  fail_with("File not found: #{@file}") unless File.exists?(@file)
@@ -96,13 +97,14 @@ class MemprofUploader
96
97
  # We'll compare against this later to see which is the new file.
97
98
  old_files = Dir.glob("/tmp/memprof-#{pid}-*")
98
99
  signal_process(pid)
99
- puts "Waiting 5 seconds for process #{pid} to create a new dump..."
100
+ timeout = 30
101
+ puts "Waiting #{timeout} seconds for process #{pid} to create a new dump..."
100
102
 
101
103
  file = nil
102
104
 
103
- 5.times do |i|
105
+ timeout.times do |i|
104
106
  sleep 1 unless file = (Dir.glob("/tmp/memprof-#{pid}-*") - old_files).first and break
105
- fail_with("Timed out after waiting 5 seconds. Make sure you added require '#{File.expand_path('../../lib/memprof/signal', __FILE__)}' to your application.") if i+1 == 5
107
+ fail_with("Timed out after waiting #{timeout} seconds. Make sure you added require '#{File.expand_path('../../lib/memprof/signal', __FILE__)}' to your application.") if i+1 == timeout
106
108
  end
107
109
 
108
110
  puts "\nFound file #{file}"
data/ext/arch.h CHANGED
@@ -13,6 +13,8 @@
13
13
  #error "Unsupported architecture! Cannot continue compilation."
14
14
  #endif
15
15
 
16
+ #include "x86_gen.h"
17
+
16
18
  /*
17
19
  * arch_get_st2_tramp - architecture specific stage 2 trampoline getter
18
20
  *
data/ext/elf.c CHANGED
@@ -15,8 +15,8 @@
15
15
  #include <libgen.h>
16
16
  #include <limits.h>
17
17
  #include <link.h>
18
- #include <stdio.h>
19
18
  #include <stdlib.h>
19
+ #include <stdio.h>
20
20
  #include <string.h>
21
21
  #include <sysexits.h>
22
22
  #include <unistd.h>
@@ -135,6 +135,7 @@ get_got_addr(struct plt_entry *plt)
135
135
  {
136
136
  assert(plt != NULL);
137
137
  /* the jump is relative to the start of the next instruction. */
138
+ dbg_printf("PLT addr: %p, .got.plt slot: %p\n", plt, (void *)&(plt->pad) + plt->jmp_disp);
138
139
  return (void *)&(plt->pad) + plt->jmp_disp;
139
140
  }
140
141
 
@@ -151,8 +152,9 @@ overwrite_got(void *plt, const void *tramp)
151
152
  assert(tramp != NULL);
152
153
  void *ret = NULL;
153
154
 
154
- memcpy(&ret, get_got_addr(plt), sizeof(ret));
155
- memcpy(get_got_addr(plt), &tramp, sizeof(void *));
155
+ memcpy(&ret, get_got_addr(plt), sizeof(void *));
156
+ copy_instructions(get_got_addr(plt), &tramp, sizeof(void *));
157
+ dbg_printf("GOT value overwritten to: %p\n", tramp);
156
158
  return ret;
157
159
  }
158
160
 
@@ -189,7 +191,6 @@ for_each_dso_cb(struct link_map *map, void *data)
189
191
  dbg_printf("found an empty string (skipping)\n");
190
192
  return CB_CONTINUE;
191
193
  }
192
-
193
194
  memset(&curr_lib, 0, sizeof(curr_lib));
194
195
  dbg_printf("trying to open elf object: %s\n", map->l_name);
195
196
  curr_lib.filename = map->l_name;
data/ext/extconf.rb CHANGED
@@ -160,7 +160,8 @@ if have_header('mach-o/dyld.h')
160
160
  # 'rb_newobj',
161
161
  # 'freelist',
162
162
  # 'heaps',
163
- # 'heaps_used'
163
+ # 'heaps_used',
164
+ # 'finalizer_table'
164
165
  ]
165
166
 
166
167
  expressions = []
@@ -218,7 +219,14 @@ if ENV['MEMPROF_DEBUG'] == '1'
218
219
  end
219
220
 
220
221
  if is_elf or is_macho
222
+ $objs = Dir['{.,*}/*.c'].map{ |file| file.gsub(/\.c(pp)?$/, '.o') }
221
223
  create_makefile('memprof')
224
+
225
+ makefile = File.read('Makefile')
226
+ makefile.gsub!('-c $<', '-o $(patsubst %.c,%.o,$<) -c $<')
227
+ makefile.gsub!(/(CLEANOBJS\s*=)/, '\1 */*.o')
228
+
229
+ File.open('Makefile', 'w+'){ |f| f.puts(makefile) }
222
230
  else
223
231
  raise 'unsupported platform'
224
232
  end
data/ext/mach.c CHANGED
@@ -508,7 +508,5 @@ bin_init()
508
508
  assert(mach_config.symbol_table != NULL);
509
509
  assert(mach_config.string_table != NULL);
510
510
  assert(mach_config.symbol_count > 0);
511
-
512
- free(file);
513
511
  }
514
512
  #endif
data/ext/memprof.c CHANGED
@@ -20,6 +20,7 @@
20
20
 
21
21
  #include "arch.h"
22
22
  #include "bin_api.h"
23
+ #include "tracer.h"
23
24
  #include "tramp.h"
24
25
  #include "util.h"
25
26
 
@@ -334,152 +335,17 @@ memprof_track(int argc, VALUE *argv, VALUE self)
334
335
  return Qnil;
335
336
  }
336
337
 
337
- struct memprof_malloc_stats {
338
- size_t malloc_bytes_requested;
339
- size_t calloc_bytes_requested;
340
- size_t realloc_bytes_requested;
341
-
342
- size_t malloc_bytes_actual;
343
- size_t calloc_bytes_actual;
344
- size_t realloc_bytes_actual;
345
- size_t free_bytes_actual;
346
-
347
- size_t malloc_calls;
348
- size_t calloc_calls;
349
- size_t realloc_calls;
350
- size_t free_calls;
351
- };
352
-
353
- static struct memprof_malloc_stats memprof_malloc_stats;
354
- static void *orig_malloc, *orig_realloc, *orig_calloc, *orig_free;
355
- static size_t (*malloc_usable_size)(void *ptr);
356
-
357
- static void *
358
- malloc_tramp(size_t size)
359
- {
360
- void *ret = NULL;
361
- memprof_malloc_stats.malloc_bytes_requested += size;
362
- memprof_malloc_stats.malloc_calls++;
363
- ret = malloc(size);
364
- memprof_malloc_stats.malloc_bytes_actual += malloc_usable_size(ret);
365
- return ret;
366
- }
367
-
368
- static void *
369
- calloc_tramp(size_t nmemb, size_t size)
370
- {
371
- void *ret = NULL;
372
- memprof_malloc_stats.calloc_bytes_requested += (nmemb * size);
373
- memprof_malloc_stats.calloc_calls++;
374
- ret = calloc(nmemb, size);
375
- memprof_malloc_stats.calloc_bytes_actual += malloc_usable_size(ret);
376
- return ret;
377
- }
378
-
379
- static void *
380
- realloc_tramp(void *ptr, size_t size)
381
- {
382
- /* TODO need to check malloc_usable_size of before/after i guess? */
383
- void *ret = NULL;
384
- memprof_malloc_stats.realloc_bytes_requested += size;
385
- memprof_malloc_stats.realloc_calls++;
386
- ret = realloc(ptr, size);
387
- memprof_malloc_stats.realloc_bytes_actual += malloc_usable_size(ptr);
388
- return ret;
389
- }
390
-
391
- static void
392
- free_tramp(void *ptr)
393
- {
394
- /* TODO use malloc_usable_size to track bytes freed? */
395
- memprof_malloc_stats.free_bytes_actual += malloc_usable_size(ptr);
396
- memprof_malloc_stats.free_calls++;
397
- free(ptr);
398
- }
399
-
400
- static void
401
- memprof_start_track_bytes()
402
- {
403
- struct tramp_st2_entry tmp;
404
-
405
- if (!malloc_usable_size) {
406
- if ((malloc_usable_size =
407
- bin_find_symbol("MallocExtension_GetAllocatedSize", NULL, 1)) == NULL) {
408
- malloc_usable_size = bin_find_symbol("malloc_usable_size", NULL, 1);
409
- dbg_printf("tcmalloc was not found...\n");
410
- }
411
- assert(malloc_usable_size != NULL);
412
- dbg_printf("malloc_usable_size: %p\n", malloc_usable_size);
413
- }
414
-
415
- tmp.addr = malloc_tramp;
416
- bin_update_image("malloc", &tmp, &orig_malloc);
417
- assert(orig_malloc != NULL);
418
- dbg_printf("orig_malloc: %p\n", orig_malloc);
419
-
420
- tmp.addr = realloc_tramp;
421
- bin_update_image("realloc", &tmp, &orig_realloc);
422
- dbg_printf("orig_realloc: %p\n", orig_realloc);
423
-
424
- tmp.addr = calloc_tramp;
425
- bin_update_image("calloc", &tmp, &orig_calloc);
426
- dbg_printf("orig_calloc: %p\n", orig_calloc);
427
-
428
- tmp.addr = free_tramp;
429
- bin_update_image("free", &tmp, &orig_free);
430
- assert(orig_free != NULL);
431
- dbg_printf("orig_free: %p\n", orig_free);
432
- }
433
-
434
- static void
435
- memprof_stop_track_bytes()
436
- {
437
- struct tramp_st2_entry tmp;
438
-
439
- tmp.addr = orig_malloc;
440
- bin_update_image("malloc", &tmp, NULL);
441
-
442
- tmp.addr = orig_realloc;
443
- bin_update_image("realloc", &tmp, NULL);
444
-
445
- tmp.addr = orig_calloc;
446
- bin_update_image("calloc", &tmp, NULL);
447
-
448
- tmp.addr = orig_free;
449
- bin_update_image("free", &tmp, NULL);
450
- }
451
-
452
- static void
453
- memprof_reset_track_bytes()
454
- {
455
- memset(&memprof_malloc_stats, 0, sizeof(memprof_malloc_stats));
456
- }
457
-
458
338
  static VALUE
459
- memprof_track_bytes(int argc, VALUE *argv, VALUE self)
339
+ memprof_track_fun(int argc, VALUE *argv, VALUE self)
460
340
  {
461
341
  if (!rb_block_given_p())
462
342
  rb_raise(rb_eArgError, "block required");
463
343
 
464
- memprof_start_track_bytes();
344
+ trace_invoke_all(TRACE_START);
465
345
  rb_yield(Qnil);
466
- fprintf(stderr, "================ Requested ====================\n");
467
- fprintf(stderr, "Malloced: %zd, Realloced: %zd, Calloced: %zd\n",
468
- memprof_malloc_stats.malloc_bytes_requested, memprof_malloc_stats.realloc_bytes_requested,
469
- memprof_malloc_stats.calloc_bytes_requested);
470
- fprintf(stderr, "================ Actual ====================\n");
471
- fprintf(stderr, "Malloced: %zd, Realloced: %zd, Calloced: %zd, Freed: %zd\n",
472
- memprof_malloc_stats.malloc_bytes_actual, memprof_malloc_stats.realloc_bytes_actual,
473
- memprof_malloc_stats.calloc_bytes_actual, memprof_malloc_stats.free_bytes_actual);
474
- fprintf(stderr, "================ Call count ====================\n");
475
- fprintf(stderr, "Calls to malloc: %zd, realloc: %zd, calloc: %zd, free: %zd\n",
476
- memprof_malloc_stats.malloc_calls,
477
- memprof_malloc_stats.realloc_calls,
478
- memprof_malloc_stats.calloc_calls,
479
- memprof_malloc_stats.free_calls);
480
-
481
- memprof_reset_track_bytes();
482
- memprof_stop_track_bytes();
346
+ trace_invoke_all(TRACE_DUMP);
347
+ trace_invoke_all(TRACE_STOP);
348
+ trace_invoke_all(TRACE_RESET);
483
349
  return Qnil;
484
350
  }
485
351
 
@@ -1410,6 +1276,17 @@ globals_each_dump(st_data_t key, st_data_t record, st_data_t arg)
1410
1276
  return ST_CONTINUE;
1411
1277
  }
1412
1278
 
1279
+ static int
1280
+ finalizers_each_dump(st_data_t key, st_data_t val, st_data_t arg)
1281
+ {
1282
+ yajl_gen gen = (yajl_gen)arg;
1283
+ yajl_gen_array_open(gen);
1284
+ yajl_gen_value(gen, (VALUE)key);
1285
+ yajl_gen_value(gen, (VALUE)val);
1286
+ yajl_gen_array_close(gen);
1287
+ return ST_CONTINUE;
1288
+ }
1289
+
1413
1290
  static void
1414
1291
  memprof_dump_globals(yajl_gen gen)
1415
1292
  {
@@ -1562,6 +1439,29 @@ memprof_dump_ps(yajl_gen gen)
1562
1439
  }
1563
1440
  }
1564
1441
 
1442
+ static void
1443
+ memprof_dump_finalizers(yajl_gen gen)
1444
+ {
1445
+ st_table *finalizer_table = *(st_table **)memprof_config.finalizer_table;
1446
+ if (finalizer_table) {
1447
+ yajl_gen_map_open(gen);
1448
+
1449
+ yajl_gen_cstr(gen, "_id");
1450
+ yajl_gen_cstr(gen, "finalizers");
1451
+
1452
+ yajl_gen_cstr(gen, "type");
1453
+ yajl_gen_cstr(gen, "finalizers");
1454
+
1455
+ yajl_gen_cstr(gen, "data");
1456
+ yajl_gen_array_open(gen);
1457
+ st_foreach(finalizer_table, finalizers_each_dump, (st_data_t)gen);
1458
+ yajl_gen_array_close(gen);
1459
+
1460
+ yajl_gen_map_close(gen);
1461
+ yajl_gen_reset(gen);
1462
+ }
1463
+ }
1464
+
1565
1465
  static void
1566
1466
  json_print(void *ctx, const char * str, unsigned int len)
1567
1467
  {
@@ -1648,6 +1548,7 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
1648
1548
 
1649
1549
  track_objs = 0;
1650
1550
 
1551
+ memprof_dump_finalizers(gen);
1651
1552
  memprof_dump_globals(gen);
1652
1553
  memprof_dump_stack(gen);
1653
1554
 
@@ -1727,6 +1628,8 @@ init_memprof_config_extended() {
1727
1628
  /* Stuff for dumping the heap */
1728
1629
  memprof_config.heaps = bin_find_symbol("heaps", NULL, 0);
1729
1630
  memprof_config.heaps_used = bin_find_symbol("heaps_used", NULL, 0);
1631
+ memprof_config.finalizer_table = bin_find_symbol("finalizer_table", NULL, 0);
1632
+
1730
1633
  #ifdef sizeof__RVALUE
1731
1634
  memprof_config.sizeof_RVALUE = sizeof__RVALUE;
1732
1635
  #else
@@ -1869,21 +1772,21 @@ init_memprof_config_extended() {
1869
1772
  }
1870
1773
  if (memprof_config.gc_sweep_size == 0)
1871
1774
  errors_printed += fprintf(stderr,
1872
- "Failed to determine the size of gc_sweep/garbage_collect_0/garbage_collect: %ld\n",
1775
+ "Failed to determine the size of gc_sweep/garbage_collect_0/garbage_collect: %zd\n",
1873
1776
  memprof_config.gc_sweep_size);
1874
1777
  if (memprof_config.finalize_list == NULL)
1875
1778
  errors_printed += fprintf(stderr,
1876
1779
  "Failed to locate finalize_list\n");
1877
1780
  if (memprof_config.finalize_list_size == 0)
1878
1781
  errors_printed += fprintf(stderr,
1879
- "Failed to determine the size of finalize_list: %ld\n",
1782
+ "Failed to determine the size of finalize_list: %zd\n",
1880
1783
  memprof_config.finalize_list_size);
1881
1784
  if (memprof_config.rb_gc_force_recycle == NULL)
1882
1785
  errors_printed += fprintf(stderr,
1883
1786
  "Failed to locate rb_gc_force_recycle\n");
1884
1787
  if (memprof_config.rb_gc_force_recycle_size == 0)
1885
1788
  errors_printed += fprintf(stderr,
1886
- "Failed to determine the size of rb_gc_force_recycle: %ld\n",
1789
+ "Failed to determine the size of rb_gc_force_recycle: %zd\n",
1887
1790
  memprof_config.rb_gc_force_recycle_size);
1888
1791
  if (memprof_config.freelist == NULL)
1889
1792
  errors_printed += fprintf(stderr,
@@ -1915,7 +1818,7 @@ Init_memprof()
1915
1818
  rb_define_singleton_method(memprof, "track", memprof_track, -1);
1916
1819
  rb_define_singleton_method(memprof, "dump", memprof_dump, -1);
1917
1820
  rb_define_singleton_method(memprof, "dump_all", memprof_dump_all, -1);
1918
- rb_define_singleton_method(memprof, "track_bytes", memprof_track_bytes, -1);
1821
+ rb_define_singleton_method(memprof, "track_fun", memprof_track_fun, -1);
1919
1822
 
1920
1823
  objs = st_init_numtable();
1921
1824
  init_memprof_config_base();
@@ -1923,6 +1826,8 @@ Init_memprof()
1923
1826
  init_memprof_config_extended();
1924
1827
  create_tramp_table();
1925
1828
 
1829
+ install_malloc_tracer();
1830
+
1926
1831
  gc_hook = Data_Wrap_Struct(rb_cObject, sourcefile_marker, NULL, NULL);
1927
1832
  rb_global_variable(&gc_hook);
1928
1833
 
data/ext/tracer.c ADDED
@@ -0,0 +1,99 @@
1
+ #include <assert.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+
5
+ #include "tracer.h"
6
+ #include "util.h"
7
+
8
+ /*
9
+ XXX if we ever need a linked list for anything else ever, remove this crap
10
+ and switch to a generic macro-based linked list implementation
11
+ */
12
+ struct tracer_list {
13
+ struct tracer *tracer;
14
+ struct tracer_list *next;
15
+ };
16
+
17
+ static struct tracer_list *tracer_list = NULL;
18
+
19
+ int
20
+ trace_insert(struct tracer *trace)
21
+ {
22
+ assert(trace != NULL);
23
+
24
+ struct tracer_list *entry = malloc(sizeof(*entry));
25
+ entry->tracer = trace;
26
+
27
+ entry->next = tracer_list;
28
+ tracer_list = entry;
29
+ return 0;
30
+ }
31
+
32
+ int
33
+ trace_remove(const char *id)
34
+ {
35
+ struct tracer_list *tmp, *prev;
36
+ tmp = prev = tracer_list;
37
+
38
+ while (tmp) {
39
+ if (strcmp(id, tmp->tracer->id) == 0) {
40
+ tmp->next = tmp->next;
41
+ free(tmp->tracer->id);
42
+ free(tmp->tracer);
43
+ free(tmp);
44
+ return 0;
45
+ }
46
+ prev = tmp;
47
+ tmp = tmp->next;
48
+ }
49
+
50
+ return 1;
51
+ }
52
+
53
+ static void
54
+ do_trace_invoke(struct tracer *trace, trace_fn fn)
55
+ {
56
+ switch (fn) {
57
+ case TRACE_START:
58
+ trace->start();
59
+ break;
60
+ case TRACE_STOP:
61
+ trace->stop();
62
+ break;
63
+ case TRACE_RESET:
64
+ trace->reset();
65
+ break;
66
+ case TRACE_DUMP:
67
+ trace->dump();
68
+ break;
69
+ default:
70
+ dbg_printf("invoked a non-existant trace function type: %d", fn);
71
+ assert(1==0);
72
+ }
73
+
74
+ return;
75
+ }
76
+
77
+ int
78
+ trace_invoke_all(trace_fn fn)
79
+ {
80
+ struct tracer_list *tmp = tracer_list;
81
+ while (tmp) {
82
+ do_trace_invoke(tmp->tracer, fn);
83
+ tmp = tmp->next;
84
+ }
85
+ return 0;
86
+ }
87
+
88
+ int
89
+ trace_invoke(const char *id, trace_fn fn)
90
+ {
91
+ struct tracer_list *tmp = tracer_list;
92
+ while (tmp) {
93
+ if (strcmp(id, tmp->tracer->id) == 0) {
94
+ do_trace_invoke(tmp->tracer, fn);
95
+ }
96
+ tmp = tmp->next;
97
+ }
98
+ return 0;
99
+ }
data/ext/tracer.h ADDED
@@ -0,0 +1,33 @@
1
+ #if !defined(__TRACER__H_)
2
+ #define __TRACER__H_
3
+
4
+ struct tracer {
5
+ char *id;
6
+ void (*start)();
7
+ void (*stop)();
8
+ void (*reset)();
9
+ void (*dump)();
10
+ };
11
+
12
+ typedef enum {
13
+ TRACE_START,
14
+ TRACE_STOP,
15
+ TRACE_RESET,
16
+ TRACE_DUMP,
17
+ } trace_fn;
18
+
19
+ int
20
+ trace_insert(struct tracer *trace);
21
+
22
+ int
23
+ trace_remove(const char *id);
24
+
25
+ int
26
+ trace_invoke_all(trace_fn fn);
27
+
28
+ int
29
+ trace_invoke(const char *id, trace_fn fn);
30
+
31
+ /* for now, these will live here */
32
+ extern void install_malloc_tracer();
33
+ #endif
@@ -0,0 +1,163 @@
1
+ #include <assert.h>
2
+ #include <stdio.h>
3
+ #include <stdlib.h>
4
+ #include <string.h>
5
+
6
+ #include "arch.h"
7
+ #include "bin_api.h"
8
+ #include "tracer.h"
9
+ #include "util.h"
10
+
11
+ struct memprof_malloc_stats {
12
+ size_t malloc_bytes_requested;
13
+ size_t calloc_bytes_requested;
14
+ size_t realloc_bytes_requested;
15
+
16
+ size_t malloc_bytes_actual;
17
+ size_t calloc_bytes_actual;
18
+ size_t realloc_bytes_actual;
19
+ size_t free_bytes_actual;
20
+
21
+ size_t malloc_calls;
22
+ size_t calloc_calls;
23
+ size_t realloc_calls;
24
+ size_t free_calls;
25
+ };
26
+
27
+ static struct tracer tracer;
28
+ static struct memprof_malloc_stats memprof_malloc_stats;
29
+ static void *(*orig_malloc)(size_t), *(*orig_realloc)(void *, size_t),
30
+ *(*orig_calloc)(size_t, size_t), (*orig_free)(void *);
31
+ static size_t (*malloc_usable_size)(void *ptr);
32
+
33
+ static void *
34
+ malloc_tramp(size_t size)
35
+ {
36
+ void *ret = NULL;
37
+ memprof_malloc_stats.malloc_bytes_requested += size;
38
+ memprof_malloc_stats.malloc_calls++;
39
+ ret = orig_malloc(size);
40
+ memprof_malloc_stats.malloc_bytes_actual += malloc_usable_size(ret);
41
+ return ret;
42
+ }
43
+
44
+ static void *
45
+ calloc_tramp(size_t nmemb, size_t size)
46
+ {
47
+ void *ret = NULL;
48
+ memprof_malloc_stats.calloc_bytes_requested += (nmemb * size);
49
+ memprof_malloc_stats.calloc_calls++;
50
+ ret = (*orig_calloc)(nmemb, size);
51
+ memprof_malloc_stats.calloc_bytes_actual += malloc_usable_size(ret);
52
+ return ret;
53
+ }
54
+
55
+ static void *
56
+ realloc_tramp(void *ptr, size_t size)
57
+ {
58
+ /* TODO need to check malloc_usable_size of before/after i guess? */
59
+ void *ret = NULL;
60
+ memprof_malloc_stats.realloc_bytes_requested += size;
61
+ memprof_malloc_stats.realloc_calls++;
62
+ ret = orig_realloc(ptr, size);
63
+ memprof_malloc_stats.realloc_bytes_actual += malloc_usable_size(ptr);
64
+ return ret;
65
+ }
66
+
67
+ static void
68
+ free_tramp(void *ptr)
69
+ {
70
+ memprof_malloc_stats.free_bytes_actual += malloc_usable_size(ptr);
71
+ memprof_malloc_stats.free_calls++;
72
+ orig_free(ptr);
73
+ }
74
+
75
+ static void
76
+ malloc_trace_stop()
77
+ {
78
+ struct tramp_st2_entry tmp;
79
+
80
+ tmp.addr = orig_malloc;
81
+ bin_update_image("malloc", &tmp, NULL);
82
+
83
+ tmp.addr = orig_realloc;
84
+ bin_update_image("realloc", &tmp, NULL);
85
+
86
+ if (orig_calloc) {
87
+ tmp.addr = orig_calloc;
88
+ bin_update_image("calloc", &tmp, NULL);
89
+ }
90
+
91
+ tmp.addr = orig_free;
92
+ bin_update_image("free", &tmp, NULL);
93
+ }
94
+
95
+ static void
96
+ malloc_trace_reset()
97
+ {
98
+ memset(&memprof_malloc_stats, 0, sizeof(memprof_malloc_stats));
99
+ }
100
+
101
+ static void
102
+ malloc_trace_dump()
103
+ {
104
+ fprintf(stderr, "================ Requested ====================\n");
105
+ fprintf(stderr, "Malloced: %zd, Realloced: %zd, Calloced: %zd\n",
106
+ memprof_malloc_stats.malloc_bytes_requested, memprof_malloc_stats.realloc_bytes_requested,
107
+ memprof_malloc_stats.calloc_bytes_requested);
108
+ fprintf(stderr, "================ Actual ====================\n");
109
+ fprintf(stderr, "Malloced: %zd, Realloced: %zd, Calloced: %zd, Freed: %zd\n",
110
+ memprof_malloc_stats.malloc_bytes_actual, memprof_malloc_stats.realloc_bytes_actual,
111
+ memprof_malloc_stats.calloc_bytes_actual, memprof_malloc_stats.free_bytes_actual);
112
+ fprintf(stderr, "================ Call count ====================\n");
113
+ fprintf(stderr, "Calls to malloc: %zd, realloc: %zd, calloc: %zd, free: %zd\n",
114
+ memprof_malloc_stats.malloc_calls,
115
+ memprof_malloc_stats.realloc_calls,
116
+ memprof_malloc_stats.calloc_calls,
117
+ memprof_malloc_stats.free_calls);
118
+ }
119
+
120
+ static void
121
+ malloc_trace_start()
122
+ {
123
+ struct tramp_st2_entry tmp;
124
+
125
+ if (!malloc_usable_size) {
126
+ if ((malloc_usable_size =
127
+ bin_find_symbol("MallocExtension_GetAllocatedSize", NULL, 1)) == NULL) {
128
+ malloc_usable_size = bin_find_symbol("malloc_usable_size", NULL, 1);
129
+ dbg_printf("tcmalloc was not found...\n");
130
+ }
131
+ assert(malloc_usable_size != NULL);
132
+ dbg_printf("malloc_usable_size: %p\n", malloc_usable_size);
133
+ }
134
+
135
+ tmp.addr = malloc_tramp;
136
+ bin_update_image("malloc", &tmp, (void **)(&orig_malloc));
137
+ assert(orig_malloc != NULL);
138
+ dbg_printf("orig_malloc: %p\n", orig_malloc);
139
+
140
+ tmp.addr = realloc_tramp;
141
+ bin_update_image("realloc", &tmp,(void **)(&orig_realloc));
142
+ dbg_printf("orig_realloc: %p\n", orig_realloc);
143
+
144
+ tmp.addr = calloc_tramp;
145
+ bin_update_image("calloc", &tmp, (void **)(&orig_calloc));
146
+ dbg_printf("orig_calloc: %p\n", orig_calloc);
147
+
148
+ tmp.addr = free_tramp;
149
+ bin_update_image("free", &tmp, (void **)(&orig_free));
150
+ assert(orig_free != NULL);
151
+ dbg_printf("orig_free: %p\n", orig_free);
152
+ }
153
+
154
+ void install_malloc_tracer()
155
+ {
156
+ tracer.start = malloc_trace_start;
157
+ tracer.stop = malloc_trace_stop;
158
+ tracer.reset = malloc_trace_reset;
159
+ tracer.dump = malloc_trace_dump;
160
+ tracer.id = strdup("malloc_tracer");
161
+
162
+ trace_insert(&tracer);
163
+ }
data/ext/util.h CHANGED
@@ -2,7 +2,11 @@
2
2
  #define __util_h__
3
3
 
4
4
  #if defined(_MEMPROF_DEBUG)
5
- #define dbg_printf(...) fprintf(stderr, __VA_ARGS__)
5
+ #include <stdio.h>
6
+ #define dbg_printf(...) do {\
7
+ fprintf(stderr, "%s:%d ", __FILE__, __LINE__);\
8
+ fprintf(stderr, __VA_ARGS__);\
9
+ } while (0)
6
10
  #else
7
11
  #define dbg_printf(...)
8
12
  #endif
@@ -33,6 +37,7 @@ struct memprof_config {
33
37
 
34
38
  void *heaps;
35
39
  void *heaps_used;
40
+ void *finalizer_table;
36
41
 
37
42
  size_t sizeof_RVALUE;
38
43
  size_t sizeof_heaps_slot;
data/ext/x86_gen.c ADDED
@@ -0,0 +1,73 @@
1
+ #include <assert.h>
2
+ #include <stdint.h>
3
+ #include "arch.h"
4
+ #include "x86_gen.h"
5
+
6
+ /*
7
+ * arch_insert_st1_tramp - architecture specific stage 1 trampoline insert
8
+ *
9
+ * Given:
10
+ * - a start address (start),
11
+ * - the absolute address of the function to intercept (trampee),
12
+ * - the absolute address of the code to execute instead (tramp),
13
+ *
14
+ * This function will:
15
+ * - interpret address start as a struct st1_base,
16
+ * - check that the instruction at call is actually a call
17
+ * - if so, check that the target of the call is trampee
18
+ * - and change the target to tramp
19
+ *
20
+ * Returns 0 on success, 1 otherwise.
21
+ */
22
+ int
23
+ arch_insert_st1_tramp(void *start, void *trampee, void *tramp)
24
+ {
25
+ assert(start != NULL);
26
+ assert(trampee != NULL);
27
+ assert(tramp != NULL);
28
+
29
+ int32_t fn_addr = 0;
30
+ struct st1_base *check = start;
31
+
32
+ if (check->call == 0xe8) {
33
+ fn_addr = check->displacement;
34
+ if ((trampee - (void *)(check + 1)) == fn_addr) {
35
+ WRITE_INSTRUCTIONS(&check->displacement,
36
+ sizeof(*check),
37
+ (check->displacement = (tramp - (void *)(check + 1))));
38
+ return 0;
39
+ }
40
+ }
41
+
42
+ return 1;
43
+ }
44
+
45
+ /*
46
+ * arch_get_st2_tramp - architecture specific stage 2 tramp accessor. This
47
+ * function returns a pointer to the default stage 2 trampoline setting size
48
+ * if a non-NULL pointer was passed in.
49
+ */
50
+ void *
51
+ arch_get_st2_tramp(size_t *size)
52
+ {
53
+ if (size) {
54
+ *size = sizeof(default_st2_tramp);
55
+ }
56
+
57
+ return &default_st2_tramp;
58
+ }
59
+
60
+ /*
61
+ * arch_get_inline_st2_tramp - architecture specific inline stage 2 tramp
62
+ * accessor. This function returns a pointer to the default inline stage 2
63
+ * trampoline setting size if a non-NULL pointer was passed in.
64
+ */
65
+ void *
66
+ arch_get_inline_st2_tramp(size_t *size)
67
+ {
68
+ if (size) {
69
+ *size = sizeof(default_inline_st2_tramp);
70
+ }
71
+
72
+ return &default_inline_st2_tramp;
73
+ }
data/ext/x86_gen.h CHANGED
@@ -2,8 +2,10 @@
2
2
  #define _x86_gen_
3
3
 
4
4
  #include <assert.h>
5
- #include <sys/mman.h>
6
5
  #include <stdint.h>
6
+ #include <stdlib.h>
7
+ #include <string.h>
8
+ #include <sys/mman.h>
7
9
  #include "arch.h"
8
10
 
9
11
  /*
@@ -15,7 +17,7 @@
15
17
  *
16
18
  * For example: callq <0xdeadbeef> #rb_newobj
17
19
  */
18
- struct st1_base {
20
+ static struct st1_base {
19
21
  unsigned char call;
20
22
  int32_t displacement;
21
23
  } __attribute__((__packed__)) st1_mov = {
@@ -53,8 +55,24 @@ copy_instructions(void *dest, void *src, size_t count)
53
55
 
54
56
  mprotect(aligned_addr, (dest - aligned_addr) + count, PROT_READ|PROT_WRITE|PROT_EXEC);
55
57
  memcpy(dest, src, count);
56
- mprotect(aligned_addr, (dest - aligned_addr) + count, PROT_READ|PROT_EXEC);
57
58
 
59
+ /*
60
+ * XXX This has to be commented out because setting certian sections to
61
+ * readonly (.got.plt, et al.) will cause the rtld to die.
62
+ *
63
+ * There is no way to get the current permissions bits for a page.
64
+ *
65
+ * The way to solve this is:
66
+ *
67
+ * 1.) copy_instructions can take a final_permissions mask and each
68
+ * overwrite site can put in the 'Right Thing'
69
+ *
70
+ * 2.) Each overwrite site can look up the 'Right Thing' in the object
71
+ * header and pass it in, ensuring the desired permissions are
72
+ * set after.
73
+ *
74
+ * mprotect(aligned_addr, (dest - aligned_addr) + count, PROT_READ|PROT_EXEC);
75
+ */
58
76
  return;
59
77
  }
60
78
 
@@ -70,72 +88,4 @@ copy_instructions(void *dest, void *src, size_t count)
70
88
  mprotect(aligned_addr, count, PROT_READ | PROT_EXEC); \
71
89
  } while (0)
72
90
 
73
- /*
74
- * arch_insert_st1_tramp - architecture specific stage 1 trampoline insert
75
- *
76
- * Given:
77
- * - a start address (start),
78
- * - the absolute address of the function to intercept (trampee),
79
- * - the absolute address of the code to execute instead (tramp),
80
- *
81
- * This function will:
82
- * - interpret address start as a struct st1_base,
83
- * - check that the instruction at call is actually a call
84
- * - if so, check that the target of the call is trampee
85
- * - and change the target to tramp
86
- *
87
- * Returns 0 on success, 1 otherwise.
88
- */
89
- int
90
- arch_insert_st1_tramp(void *start, void *trampee, void *tramp)
91
- {
92
- assert(start != NULL);
93
- assert(trampee != NULL);
94
- assert(tramp != NULL);
95
-
96
- int32_t fn_addr = 0;
97
- struct st1_base *check = start;
98
-
99
- if (check->call == 0xe8) {
100
- fn_addr = check->displacement;
101
- if ((trampee - (void *)(check + 1)) == fn_addr) {
102
- WRITE_INSTRUCTIONS(&check->displacement,
103
- sizeof(*check),
104
- (check->displacement = (tramp - (void *)(check + 1))));
105
- return 0;
106
- }
107
- }
108
-
109
- return 1;
110
- }
111
-
112
- /*
113
- * arch_get_st2_tramp - architecture specific stage 2 tramp accessor. This
114
- * function returns a pointer to the default stage 2 trampoline setting size
115
- * if a non-NULL pointer was passed in.
116
- */
117
- void *
118
- arch_get_st2_tramp(size_t *size)
119
- {
120
- if (size) {
121
- *size = sizeof(default_st2_tramp);
122
- }
123
-
124
- return &default_st2_tramp;
125
- }
126
-
127
- /*
128
- * arch_get_inline_st2_tramp - architecture specific inline stage 2 tramp
129
- * accessor. This function returns a pointer to the default inline stage 2
130
- * trampoline setting size if a non-NULL pointer was passed in.
131
- */
132
- void *
133
- arch_get_inline_st2_tramp(size_t *size)
134
- {
135
- if (size) {
136
- *size = sizeof(default_inline_st2_tramp);
137
- }
138
-
139
- return &default_inline_st2_tramp;
140
- }
141
91
  #endif
data/memprof.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'memprof'
3
- s.version = '0.3.0'
4
- s.date = '2010-03-15'
3
+ s.version = '0.3.1'
4
+ s.date = '2010-04-13'
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"
7
7
  s.homepage = "http://github.com/ice799/memprof"
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 3
8
- - 0
9
- version: 0.3.0
8
+ - 1
9
+ version: 0.3.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Joe Damato
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2010-03-15 00:00:00 -07:00
20
+ date: 2010-04-13 00:00:00 -04:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency
@@ -73,12 +73,16 @@ files:
73
73
  - ext/src/libdwarf-20091118.tar.gz
74
74
  - ext/src/libelf-0.8.13.tar.gz
75
75
  - ext/src/yajl-1.0.9.tar.gz
76
+ - ext/tracer.c
77
+ - ext/tracer.h
78
+ - ext/tracers/malloc.c
76
79
  - ext/tramp.c
77
80
  - ext/tramp.h
78
81
  - ext/util.c
79
82
  - ext/util.h
80
83
  - ext/x86_64.c
81
84
  - ext/x86_64.h
85
+ - ext/x86_gen.c
82
86
  - ext/x86_gen.h
83
87
  - lib/memprof/middleware.rb
84
88
  - lib/memprof/signal.rb