memprof 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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