memprof 0.2.9 → 0.3.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.
@@ -144,18 +144,41 @@ if have_header('mach-o/dyld.h')
144
144
  # XXX How to determine this properly? RUBY_PLATFORM reports "i686-darwin10.2.0" on Snow Leopard.
145
145
  add_define "_ARCH_x86_64_"
146
146
 
147
- expressions = [
148
- [:sizeof__RVALUE, "sizeof(RVALUE)"],
149
- [:sizeof__heaps_slot, "sizeof(struct heaps_slot)"],
150
- [:offset__heaps_slot__slot, "(int)&(((struct heaps_slot *)0)->slot)"],
151
- [:offset__heaps_slot__limit, "(int)&(((struct heaps_slot *)0)->limit)"]
152
- # "&add_freelist",
153
- # "&rb_newobj",
154
- # "&freelist",
155
- # "&heaps",
156
- # "&heaps_used"
147
+ sizes_of = [
148
+ 'RVALUE',
149
+ 'struct heaps_slot'
157
150
  ]
158
151
 
152
+ offsets_of = {
153
+ 'struct heaps_slot' => %w[ slot limit ],
154
+ 'struct BLOCK' => %w[ body var cref self klass wrapper block_obj orig_thread dyna_vars scope prev ],
155
+ 'struct METHOD' => %w[ klass rklass recv id oid body ]
156
+ }
157
+
158
+ addresses_of = [
159
+ # 'add_freelist',
160
+ # 'rb_newobj',
161
+ # 'freelist',
162
+ # 'heaps',
163
+ # 'heaps_used'
164
+ ]
165
+
166
+ expressions = []
167
+
168
+ sizes_of.each do |type|
169
+ name = type.sub(/^struct\s*/,'')
170
+ expressions << ["sizeof__#{name}", "sizeof(#{type})"]
171
+ end
172
+ offsets_of.each do |type, members|
173
+ name = type.sub(/^struct\s*/,'')
174
+ members.each do |member|
175
+ expressions << ["offset__#{name}__#{member}", "(size_t)&(((#{type} *)0)->#{member})"]
176
+ end
177
+ end
178
+ addresses_of.each do |name|
179
+ expressions << ["address__#{name}", "&#{name}"]
180
+ end
181
+
159
182
  pid = fork{sleep}
160
183
  output = IO.popen('gdb --interpreter=mi --quiet', 'w+') do |io|
161
184
  io.puts "attach #{pid}"
@@ -175,8 +198,8 @@ if have_header('mach-o/dyld.h')
175
198
  end
176
199
 
177
200
  values = results.map{ |l| l[/value="(.+?)"/, 1] }
178
- vars = Hash[ *expressions.map{|n,e| n }.zip(values).flatten(1) ].each do |name, val|
179
- add_define "#{name}=#{val}"
201
+ vars = Hash[ *expressions.map{|n,e| n }.zip(values).flatten ].each do |name, val|
202
+ add_define "#{name}=#{val.split.first}"
180
203
  end
181
204
  end
182
205
 
@@ -189,7 +212,10 @@ when 'i486'
189
212
  end
190
213
  add_define "_ARCH_#{arch}_"
191
214
 
192
- add_define "_MEMPROF_DEBUG" if ENV['MEMPROF_DEBUG'] == '1'
215
+ if ENV['MEMPROF_DEBUG'] == '1'
216
+ add_define "_MEMPROF_DEBUG"
217
+ $preload = ["\nCFLAGS = -Wall -Wextra -fPIC -ggdb3 -O0"]
218
+ end
193
219
 
194
220
  if is_elf or is_macho
195
221
  create_makefile('memprof')
data/ext/mach.c CHANGED
@@ -348,7 +348,7 @@ get_symtab_string(uint32_t stroff) {
348
348
  */
349
349
 
350
350
  void *
351
- bin_find_symbol(const char *symbol, size_t *size) {
351
+ bin_find_symbol(const char *symbol, size_t *size, int search_libs) {
352
352
  void *ptr = NULL;
353
353
  uint32_t i, j;
354
354
 
@@ -438,12 +438,12 @@ bin_find_symbol_name(void *symbol) {
438
438
  */
439
439
 
440
440
  int
441
- bin_update_image(const char *trampee, struct tramp_st2_entry *tramp)
441
+ bin_update_image(const char *trampee, struct tramp_st2_entry *tramp, void **orig_function)
442
442
  {
443
443
  int ret = -1;
444
444
  int i;
445
445
  int header_count = _dyld_image_count();
446
- void *trampee_addr = bin_find_symbol(trampee, NULL);
446
+ void *trampee_addr = bin_find_symbol(trampee, NULL, 0);
447
447
 
448
448
  // Go through all the mach objects that are loaded into this process
449
449
  for (i=0; i < header_count; i++) {
@@ -4,14 +4,14 @@
4
4
  #define _GNU_SOURCE
5
5
  #endif
6
6
 
7
+ #include <assert.h>
8
+ #include <err.h>
7
9
  #include <fcntl.h>
8
10
  #include <stddef.h>
9
- #include <stdio.h>
10
11
  #include <stdint.h>
12
+ #include <stdio.h>
11
13
  #include <stdlib.h>
12
14
  #include <unistd.h>
13
- #include <assert.h>
14
- #include <err.h>
15
15
  #include <sysexits.h>
16
16
 
17
17
  #include <st.h>
@@ -23,20 +23,36 @@
23
23
  #include "tramp.h"
24
24
  #include "util.h"
25
25
 
26
-
27
26
  /*
28
27
  * bleak_house stuff
29
28
  */
30
29
  static VALUE eUnsupported;
31
30
  static int track_objs = 0;
31
+ static int memprof_started = 0;
32
32
  static st_table *objs = NULL;
33
33
 
34
+ /*
35
+ * stuff needed for heap dumping
36
+ */
37
+ static VALUE (*rb_classname)(VALUE);
38
+ static double (*rb_timeofday)();
39
+ static RUBY_DATA_FUNC *rb_bm_mark;
40
+ static RUBY_DATA_FUNC *rb_blk_free;
41
+ static RUBY_DATA_FUNC *rb_thread_mark;
34
42
  struct memprof_config memprof_config;
35
43
 
44
+ /*
45
+ * memprof config struct init
46
+ */
47
+ static void init_memprof_config_base();
48
+ static void init_memprof_config_extended();
49
+
36
50
  struct obj_track {
37
51
  VALUE obj;
38
52
  char *source;
39
53
  int line;
54
+ int len;
55
+ struct timeval time[];
40
56
  };
41
57
 
42
58
  static VALUE gc_hook;
@@ -92,7 +108,7 @@ newobj_tramp()
92
108
  struct obj_track *tracker = NULL;
93
109
 
94
110
  if (track_objs && objs) {
95
- tracker = malloc(sizeof(*tracker));
111
+ tracker = malloc(sizeof(*tracker) + sizeof(struct timeval));
96
112
 
97
113
  if (tracker) {
98
114
  if (ruby_current_node && ruby_current_node->nd_file &&
@@ -108,6 +124,13 @@ newobj_tramp()
108
124
  }
109
125
 
110
126
  tracker->obj = ret;
127
+ tracker->len = 1;
128
+
129
+ /* TODO a way for the user to disallow time tracking */
130
+ if (gettimeofday(&tracker->time[0], NULL) == -1) {
131
+ perror("gettimeofday failed. Continuing anyway, error");
132
+ }
133
+
111
134
  rb_gc_disable();
112
135
  st_insert(objs, (st_data_t)ret, (st_data_t)tracker);
113
136
  rb_gc_enable();
@@ -209,6 +232,13 @@ objs_to_array(st_data_t key, st_data_t record, st_data_t arg)
209
232
  static VALUE
210
233
  memprof_start(VALUE self)
211
234
  {
235
+ if (!memprof_started) {
236
+
237
+ insert_tramp("rb_newobj", newobj_tramp);
238
+ insert_tramp("add_freelist", freelist_tramp);
239
+ memprof_started = 1;
240
+ }
241
+
212
242
  if (track_objs == 1)
213
243
  return Qfalse;
214
244
 
@@ -219,6 +249,8 @@ memprof_start(VALUE self)
219
249
  static VALUE
220
250
  memprof_stop(VALUE self)
221
251
  {
252
+ /* TODO: remove trampolines and set memprof_started = 0 */
253
+
222
254
  if (track_objs == 0)
223
255
  return Qfalse;
224
256
 
@@ -302,11 +334,215 @@ memprof_track(int argc, VALUE *argv, VALUE self)
302
334
  return Qnil;
303
335
  }
304
336
 
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
+ static VALUE
459
+ memprof_track_bytes(int argc, VALUE *argv, VALUE self)
460
+ {
461
+ if (!rb_block_given_p())
462
+ rb_raise(rb_eArgError, "block required");
463
+
464
+ memprof_start_track_bytes();
465
+ 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();
483
+ return Qnil;
484
+ }
485
+
305
486
  #include <yajl/yajl_gen.h>
306
487
  #include <stdarg.h>
307
488
  #include "env.h"
489
+ #include "rubyio.h"
308
490
  #include "re.h"
309
491
 
492
+ #ifndef RARRAY_PTR
493
+ #define RARRAY_PTR(ary) RARRAY(ary)->ptr
494
+ #endif
495
+
496
+ #ifndef RARRAY_LEN
497
+ #define RARRAY_LEN(ary) RARRAY(ary)->len
498
+ #endif
499
+
500
+ #ifndef RSTRING_PTR
501
+ #define RSTRING_PTR(str) RSTRING(str)->ptr
502
+ #endif
503
+
504
+ #ifndef RSTRING_LEN
505
+ #define RSTRING_LEN(str) RSTRING(str)->len
506
+ #endif
507
+
508
+ /* HAX: copied from internal yajl_gen.c (PATCH yajl before building instead)
509
+ */
510
+
511
+ typedef enum {
512
+ yajl_gen_start,
513
+ yajl_gen_map_start,
514
+ yajl_gen_map_key,
515
+ yajl_gen_map_val,
516
+ yajl_gen_array_start,
517
+ yajl_gen_in_array,
518
+ yajl_gen_complete,
519
+ yajl_gen_error
520
+ } yajl_gen_state;
521
+
522
+ struct yajl_gen_t
523
+ {
524
+ unsigned int depth;
525
+ unsigned int pretty;
526
+ const char * indentString;
527
+ yajl_gen_state state[YAJL_MAX_DEPTH];
528
+ yajl_print_t print;
529
+ void * ctx; /* yajl_buf */
530
+ /* memory allocation routines */
531
+ yajl_alloc_funcs alloc;
532
+ };
533
+
534
+ static void
535
+ yajl_gen_reset(yajl_gen gen)
536
+ {
537
+ yajl_gen_clear(gen);
538
+ assert (gen->state[gen->depth] == yajl_gen_complete);
539
+ gen->state[gen->depth] = yajl_gen_start;
540
+ gen->print(gen->ctx, "\n", 1);
541
+ }
542
+
543
+ /* END HAX
544
+ */
545
+
310
546
  static yajl_gen_status
311
547
  yajl_gen_cstr(yajl_gen gen, const char * str)
312
548
  {
@@ -335,6 +571,24 @@ yajl_gen_format(yajl_gen gen, char *format, ...)
335
571
  return ret;
336
572
  }
337
573
 
574
+ static yajl_gen_status
575
+ yajl_gen_id(yajl_gen gen, ID id)
576
+ {
577
+ if (id) {
578
+ if (id < 100)
579
+ return yajl_gen_format(gen, ":%c", id);
580
+ else
581
+ return yajl_gen_format(gen, ":%s", rb_id2name(id));
582
+ } else
583
+ return yajl_gen_null(gen);
584
+ }
585
+
586
+ static yajl_gen_status
587
+ yajl_gen_pointer(yajl_gen gen, void* ptr)
588
+ {
589
+ return yajl_gen_format(gen, "0x%x", ptr);
590
+ }
591
+
338
592
  static yajl_gen_status
339
593
  yajl_gen_value(yajl_gen gen, VALUE obj)
340
594
  {
@@ -347,9 +601,9 @@ yajl_gen_value(yajl_gen gen, VALUE obj)
347
601
  else if (obj == Qfalse)
348
602
  return yajl_gen_bool(gen, 0);
349
603
  else if (SYMBOL_P(obj))
350
- return yajl_gen_format(gen, ":%s", rb_id2name(SYM2ID(obj)));
604
+ return yajl_gen_id(gen, SYM2ID(obj));
351
605
  else
352
- return yajl_gen_format(gen, "0x%x", obj);
606
+ return yajl_gen_pointer(gen, (void*)obj);
353
607
  }
354
608
 
355
609
  static int
@@ -418,7 +672,20 @@ nd_type_str(VALUE obj)
418
672
  }
419
673
  }
420
674
 
421
- static VALUE (*rb_classname)(VALUE);
675
+ static inline void
676
+ obj_dump_class(yajl_gen gen, VALUE obj)
677
+ {
678
+ if (RBASIC(obj)->klass) {
679
+ yajl_gen_cstr(gen, "class");
680
+ yajl_gen_value(gen, RBASIC(obj)->klass);
681
+
682
+ VALUE name = rb_classname(RBASIC(obj)->klass);
683
+ if (RTEST(name)) {
684
+ yajl_gen_cstr(gen, "class_name");
685
+ yajl_gen_cstr(gen, RSTRING_PTR(name));
686
+ }
687
+ }
688
+ }
422
689
 
423
690
  /* TODO
424
691
  * look for FL_EXIVAR flag and print ivars
@@ -442,42 +709,307 @@ obj_dump(VALUE obj, yajl_gen gen)
442
709
  yajl_gen_cstr(gen, tracker->source);
443
710
  yajl_gen_cstr(gen, "line");
444
711
  yajl_gen_integer(gen, tracker->line);
712
+ yajl_gen_cstr(gen, "time");
713
+ yajl_gen_integer(gen, (tracker->time[0].tv_sec * 1000000) + tracker->time[0].tv_usec);
445
714
  }
446
715
 
447
716
  yajl_gen_cstr(gen, "type");
448
717
  switch (type=BUILTIN_TYPE(obj)) {
449
718
  case T_DATA:
450
719
  yajl_gen_cstr(gen, "data");
720
+ obj_dump_class(gen, obj);
451
721
 
452
- if (RBASIC(obj)->klass) {
453
- yajl_gen_cstr(gen, "class");
454
- yajl_gen_value(gen, RBASIC(obj)->klass);
722
+ if (DATA_PTR(obj)) {
723
+ yajl_gen_cstr(gen, "data");
724
+ yajl_gen_pointer(gen, DATA_PTR(obj));
725
+ }
726
+
727
+ if (RDATA(obj)->dfree == (RUBY_DATA_FUNC)rb_blk_free) {
728
+ void *val;
729
+ VALUE ptr;
730
+
731
+ val = *(void**)(DATA_PTR(obj) + memprof_config.offset_BLOCK_body);
732
+ if (val) {
733
+ yajl_gen_cstr(gen, "nd_body");
734
+ yajl_gen_pointer(gen, val);
735
+ }
736
+
737
+ val = *(void**)(DATA_PTR(obj) + memprof_config.offset_BLOCK_var);
738
+ if (val) {
739
+ yajl_gen_cstr(gen, "nd_var");
740
+ yajl_gen_pointer(gen, val);
741
+ }
742
+
743
+ val = *(void**)(DATA_PTR(obj) + memprof_config.offset_BLOCK_cref);
744
+ if (val) {
745
+ yajl_gen_cstr(gen, "nd_cref");
746
+ yajl_gen_pointer(gen, val);
747
+ }
748
+
749
+ val = *(void**)(DATA_PTR(obj) + memprof_config.offset_BLOCK_dyna_vars);
750
+ if (val) {
751
+ yajl_gen_cstr(gen, "vars");
752
+ yajl_gen_pointer(gen, val);
753
+ }
754
+
755
+ val = *(void**)(DATA_PTR(obj) + memprof_config.offset_BLOCK_scope);
756
+ if (val) {
757
+ yajl_gen_cstr(gen, "scope");
758
+ yajl_gen_pointer(gen, val);
759
+ }
760
+
761
+ ptr = *(VALUE*)(DATA_PTR(obj) + memprof_config.offset_BLOCK_self);
762
+ yajl_gen_cstr(gen, "self");
763
+ yajl_gen_value(gen, ptr);
764
+
765
+ ptr = *(VALUE*)(DATA_PTR(obj) + memprof_config.offset_BLOCK_klass);
766
+ yajl_gen_cstr(gen, "klass");
767
+ yajl_gen_value(gen, ptr);
768
+
769
+ ptr = *(VALUE*)(DATA_PTR(obj) + memprof_config.offset_BLOCK_orig_thread);
770
+ yajl_gen_cstr(gen, "thread");
771
+ yajl_gen_value(gen, ptr);
772
+
773
+ ptr = *(VALUE*)(DATA_PTR(obj) + memprof_config.offset_BLOCK_wrapper);
774
+ if (RTEST(ptr)) {
775
+ yajl_gen_cstr(gen, "wrapper");
776
+ yajl_gen_value(gen, ptr);
777
+ }
778
+
779
+ ptr = *(VALUE*)(DATA_PTR(obj) + memprof_config.offset_BLOCK_block_obj);
780
+ if (RTEST(ptr)) {
781
+ yajl_gen_cstr(gen, "block");
782
+ yajl_gen_value(gen, ptr);
783
+ }
784
+
785
+ /* TODO: is .prev actually useful? refers to non-heap allocated struct BLOCKs,
786
+ * but we don't print out any information about those
787
+ */
788
+ /*
789
+ yajl_gen_cstr(gen, "prev");
790
+ yajl_gen_array_open(gen);
791
+ val = *(void**)(DATA_PTR(obj) + memprof_config.offset_BLOCK_prev);
792
+ while (val) {
793
+ yajl_gen_pointer(gen, val);
794
+ prev = val;
795
+ val = *(void**)(ptr + memprof_config.offset_BLOCK_prev);
796
+ if (prev == val)
797
+ break;
798
+ }
799
+ yajl_gen_array_close(gen);
800
+ */
801
+
802
+ } else if (RDATA(obj)->dmark == (RUBY_DATA_FUNC)rb_bm_mark) {
803
+ VALUE ptr;
804
+ ID id, mid;
805
+
806
+ ptr = *(VALUE*)(DATA_PTR(obj) + memprof_config.offset_METHOD_klass);
807
+ if (RTEST(ptr)) {
808
+ yajl_gen_cstr(gen, "klass");
809
+ yajl_gen_value(gen, ptr);
810
+ }
811
+
812
+ ptr = *(VALUE*)(DATA_PTR(obj) + memprof_config.offset_METHOD_rklass);
813
+ if (RTEST(ptr)) {
814
+ yajl_gen_cstr(gen, "rklass");
815
+ yajl_gen_value(gen, ptr);
816
+ }
817
+
818
+ ptr = *(VALUE*)(DATA_PTR(obj) + memprof_config.offset_METHOD_recv);
819
+ if (RTEST(ptr)) {
820
+ yajl_gen_cstr(gen, "recv");
821
+ yajl_gen_value(gen, ptr);
822
+ }
823
+
824
+ ptr = *(VALUE*)(DATA_PTR(obj) + memprof_config.offset_METHOD_body);
825
+ if (RTEST(ptr)) {
826
+ yajl_gen_cstr(gen, "node");
827
+ yajl_gen_value(gen, ptr);
828
+ }
829
+
830
+ mid = *(ID*)(DATA_PTR(obj) + memprof_config.offset_METHOD_id);
831
+ if (mid) {
832
+ yajl_gen_cstr(gen, "mid");
833
+ yajl_gen_id(gen, mid);
834
+ }
835
+
836
+ id = *(ID*)(DATA_PTR(obj) + memprof_config.offset_METHOD_oid);
837
+ if (id && id != mid) {
838
+ yajl_gen_cstr(gen, "oid");
839
+ yajl_gen_id(gen, id);
840
+ }
841
+
842
+ } else if (RDATA(obj)->dmark == (RUBY_DATA_FUNC)rb_thread_mark) {
843
+ rb_thread_t th = (rb_thread_t)DATA_PTR(obj);
844
+
845
+ if (th == rb_curr_thread) {
846
+ yajl_gen_cstr(gen, "current");
847
+ yajl_gen_bool(gen, 1);
848
+ } else {
849
+ if (th->dyna_vars) {
850
+ yajl_gen_cstr(gen, "varmap");
851
+ yajl_gen_pointer(gen, th->dyna_vars);
852
+ }
853
+
854
+ yajl_gen_cstr(gen, "node");
855
+ yajl_gen_pointer(gen, th->node);
856
+
857
+ yajl_gen_cstr(gen, "cref");
858
+ yajl_gen_pointer(gen, th->cref);
859
+
860
+ char *status;
861
+ switch (th->status) {
862
+ case THREAD_TO_KILL:
863
+ status = "to_kill";
864
+ break;
865
+ case THREAD_RUNNABLE:
866
+ status = "runnable";
867
+ break;
868
+ case THREAD_STOPPED:
869
+ status = "stopped";
870
+ break;
871
+ case THREAD_KILLED:
872
+ status = "killed";
873
+ break;
874
+ default:
875
+ status = "unknown";
876
+ }
877
+
878
+ yajl_gen_cstr(gen, "status");
879
+ yajl_gen_cstr(gen, status);
880
+
881
+ #define WAIT_FD (1<<0)
882
+ #define WAIT_SELECT (1<<1)
883
+ #define WAIT_TIME (1<<2)
884
+ #define WAIT_JOIN (1<<3)
885
+ #define WAIT_PID (1<<4)
886
+
887
+ yajl_gen_cstr(gen, "wait_for");
888
+ yajl_gen_array_open(gen);
889
+ if (th->wait_for & WAIT_FD)
890
+ yajl_gen_cstr(gen, "fd");
891
+ if (th->wait_for & WAIT_SELECT)
892
+ yajl_gen_cstr(gen, "select");
893
+ if (th->wait_for & WAIT_TIME)
894
+ yajl_gen_cstr(gen, "time");
895
+ if (th->wait_for & WAIT_JOIN)
896
+ yajl_gen_cstr(gen, "join");
897
+ if (th->wait_for & WAIT_PID)
898
+ yajl_gen_cstr(gen, "pid");
899
+ yajl_gen_array_close(gen);
900
+
901
+ if (th->wait_for & WAIT_FD) {
902
+ yajl_gen_cstr(gen, "fd");
903
+ yajl_gen_integer(gen, th->fd);
904
+ }
905
+
906
+ #define DELAY_INFTY 1E30
907
+
908
+ if (th->wait_for & WAIT_TIME) {
909
+ yajl_gen_cstr(gen, "delay");
910
+ if (th->delay == DELAY_INFTY)
911
+ yajl_gen_cstr(gen, "infinity");
912
+ else
913
+ yajl_gen_double(gen, th->delay - rb_timeofday());
914
+ }
915
+
916
+ if (th->wait_for & WAIT_JOIN) {
917
+ yajl_gen_cstr(gen, "join");
918
+ yajl_gen_value(gen, th->join->thread);
919
+ }
920
+ }
921
+
922
+ yajl_gen_cstr(gen, "priority");
923
+ yajl_gen_integer(gen, th->priority);
924
+
925
+ if (th == rb_main_thread) {
926
+ yajl_gen_cstr(gen, "main");
927
+ yajl_gen_bool(gen, 1);
928
+ }
929
+
930
+ if (th->next && th->next != rb_main_thread) {
931
+ yajl_gen_cstr(gen, "next");
932
+ yajl_gen_value(gen, th->next->thread);
933
+ }
934
+ if (th->prev && th->prev != th && (th->prev == rb_main_thread || th->prev != th->next)) {
935
+ yajl_gen_cstr(gen, "prev");
936
+ yajl_gen_value(gen, th->prev->thread);
937
+ }
938
+
939
+ if (th->locals) {
940
+ yajl_gen_cstr(gen, "variables");
941
+ yajl_gen_map_open(gen);
942
+ st_foreach(th->locals, each_ivar, (st_data_t)gen);
943
+ yajl_gen_map_close(gen);
944
+ }
455
945
 
456
- yajl_gen_cstr(gen, "class_name");
457
- VALUE name = rb_classname(RBASIC(obj)->klass);
458
- if (RTEST(name))
459
- yajl_gen_cstr(gen, RSTRING(name)->ptr);
460
- else
461
- yajl_gen_cstr(gen, 0);
462
946
  }
463
947
  break;
464
948
 
465
949
  case T_STRUCT:
466
950
  yajl_gen_cstr(gen, "struct");
467
-
468
- yajl_gen_cstr(gen, "class");
469
- yajl_gen_value(gen, RBASIC(obj)->klass);
470
-
471
- yajl_gen_cstr(gen, "class_name");
472
- yajl_gen_cstr(gen, rb_obj_classname(obj));
951
+ obj_dump_class(gen, obj);
473
952
  break;
474
953
 
475
954
  case T_FILE:
476
955
  yajl_gen_cstr(gen, "file");
956
+ obj_dump_class(gen, obj);
957
+
958
+ OpenFile *file = RFILE(obj)->fptr;
959
+
960
+ if (file->f) {
961
+ yajl_gen_cstr(gen, "fileno");
962
+ yajl_gen_integer(gen, fileno(file->f));
963
+ }
964
+
965
+ if (file->f2) {
966
+ yajl_gen_cstr(gen, "fileno2");
967
+ yajl_gen_integer(gen, fileno(file->f2));
968
+ }
969
+
970
+ if (file->pid) {
971
+ yajl_gen_cstr(gen, "pid");
972
+ yajl_gen_integer(gen, file->pid);
973
+ }
974
+
975
+ if (file->path) {
976
+ yajl_gen_cstr(gen, "path");
977
+ yajl_gen_cstr(gen, file->path);
978
+ }
979
+
980
+ if (file->mode) {
981
+ yajl_gen_cstr(gen, "mode");
982
+ yajl_gen_array_open(gen);
983
+ if (file->mode & FMODE_READABLE)
984
+ yajl_gen_cstr(gen, "readable");
985
+ if (file->mode & FMODE_WRITABLE)
986
+ yajl_gen_cstr(gen, "writable");
987
+ if (file->mode & FMODE_READWRITE)
988
+ yajl_gen_cstr(gen, "readwrite");
989
+ if (file->mode & FMODE_APPEND)
990
+ yajl_gen_cstr(gen, "append");
991
+ if (file->mode & FMODE_CREATE)
992
+ yajl_gen_cstr(gen, "create");
993
+ if (file->mode & FMODE_BINMODE)
994
+ yajl_gen_cstr(gen, "binmode");
995
+ if (file->mode & FMODE_SYNC)
996
+ yajl_gen_cstr(gen, "sync");
997
+ if (file->mode & FMODE_WBUF)
998
+ yajl_gen_cstr(gen, "wbuf");
999
+ if (file->mode & FMODE_RBUF)
1000
+ yajl_gen_cstr(gen, "rbuf");
1001
+ if (file->mode & FMODE_WSPLIT)
1002
+ yajl_gen_cstr(gen, "wsplit");
1003
+ if (file->mode & FMODE_WSPLIT_INITIALIZED)
1004
+ yajl_gen_cstr(gen, "wsplit_initialized");
1005
+ yajl_gen_array_close(gen);
1006
+ }
1007
+
477
1008
  break;
478
1009
 
479
1010
  case T_FLOAT:
480
1011
  yajl_gen_cstr(gen, "float");
1012
+ obj_dump_class(gen, obj);
481
1013
 
482
1014
  yajl_gen_cstr(gen, "data");
483
1015
  yajl_gen_double(gen, RFLOAT(obj)->value);
@@ -485,6 +1017,7 @@ obj_dump(VALUE obj, yajl_gen gen)
485
1017
 
486
1018
  case T_BIGNUM:
487
1019
  yajl_gen_cstr(gen, "bignum");
1020
+ obj_dump_class(gen, obj);
488
1021
 
489
1022
  yajl_gen_cstr(gen, "negative");
490
1023
  yajl_gen_bool(gen, RBIGNUM(obj)->sign == 0);
@@ -498,6 +1031,7 @@ obj_dump(VALUE obj, yajl_gen gen)
498
1031
 
499
1032
  case T_MATCH:
500
1033
  yajl_gen_cstr(gen, "match");
1034
+ obj_dump_class(gen, obj);
501
1035
 
502
1036
  yajl_gen_cstr(gen, "data");
503
1037
  yajl_gen_value(gen, RMATCH(obj)->str);
@@ -505,6 +1039,7 @@ obj_dump(VALUE obj, yajl_gen gen)
505
1039
 
506
1040
  case T_REGEXP:
507
1041
  yajl_gen_cstr(gen, "regexp");
1042
+ obj_dump_class(gen, obj);
508
1043
 
509
1044
  yajl_gen_cstr(gen, "length");
510
1045
  yajl_gen_integer(gen, RREGEXP(obj)->len);
@@ -523,8 +1058,10 @@ obj_dump(VALUE obj, yajl_gen gen)
523
1058
  VALUE *list = &scope->local_vars[-1];
524
1059
  VALUE cur = *list++;
525
1060
 
526
- yajl_gen_cstr(gen, "node");
527
- yajl_gen_value(gen, cur);
1061
+ if (RTEST(cur)) {
1062
+ yajl_gen_cstr(gen, "node");
1063
+ yajl_gen_value(gen, cur);
1064
+ }
528
1065
 
529
1066
  if (n) {
530
1067
  yajl_gen_cstr(gen, "variables");
@@ -536,7 +1073,7 @@ obj_dump(VALUE obj, yajl_gen gen)
536
1073
  if (!rb_is_local_id(scope->local_tbl[i]))
537
1074
  continue;
538
1075
 
539
- yajl_gen_cstr(gen, scope->local_tbl[i] == 95 ? "_" : rb_id2name(scope->local_tbl[i]));
1076
+ yajl_gen_id(gen, scope->local_tbl[i]);
540
1077
  yajl_gen_value(gen, cur);
541
1078
  }
542
1079
  yajl_gen_map_close(gen);
@@ -559,8 +1096,8 @@ obj_dump(VALUE obj, yajl_gen gen)
559
1096
  yajl_gen_cstr(gen, "node_code");
560
1097
  yajl_gen_integer(gen, nd_type(obj));
561
1098
 
562
- #define PRINT_ID(sub) yajl_gen_format(gen, ":%s", rb_id2name(RNODE(obj)->sub.id));
563
- #define PRINT_VAL(sub) yajl_gen_value(gen, RNODE(obj)->sub.value);
1099
+ #define PRINT_ID(sub) yajl_gen_id(gen, RNODE(obj)->sub.id)
1100
+ #define PRINT_VAL(sub) yajl_gen_value(gen, RNODE(obj)->sub.value)
564
1101
 
565
1102
  int nd_type = nd_type(obj);
566
1103
  yajl_gen_cstr(gen, "n1");
@@ -580,9 +1117,18 @@ obj_dump(VALUE obj, yajl_gen gen)
580
1117
  case NODE_GASGN:
581
1118
  case NODE_DASGN_CURR:
582
1119
  case NODE_BLOCK_ARG:
1120
+ case NODE_CDECL:
1121
+ case NODE_VALIAS:
583
1122
  PRINT_ID(u1);
584
1123
  break;
585
1124
 
1125
+ case NODE_OP_ASGN2:
1126
+ if (RNODE(obj)->u3.id > 1000000)
1127
+ PRINT_VAL(u1);
1128
+ else
1129
+ PRINT_ID(u1);
1130
+ break;
1131
+
586
1132
  case NODE_SCOPE: {
587
1133
  ID *tbl = RNODE(obj)->nd_tbl;
588
1134
  yajl_gen_array_open(gen);
@@ -591,13 +1137,14 @@ obj_dump(VALUE obj, yajl_gen gen)
591
1137
  int i = 3;
592
1138
 
593
1139
  for (; i < size+1; i++) {
594
- yajl_gen_cstr(gen, tbl[i] == 95 ? "_" : rb_id2name(tbl[i]));
1140
+ yajl_gen_id(gen, tbl[i]);
595
1141
  }
596
1142
  }
597
1143
  yajl_gen_array_close(gen);
598
1144
  break;
599
1145
  }
600
1146
 
1147
+ case NODE_IFUNC:
601
1148
  case NODE_CFUNC: {
602
1149
  const char *name = bin_find_symbol_name((void*)RNODE(obj)->u1.value);
603
1150
  yajl_gen_format(gen, "0x%x: %s", RNODE(obj)->u1.value, name ? name : "???");
@@ -618,13 +1165,51 @@ obj_dump(VALUE obj, yajl_gen gen)
618
1165
  case NODE_VCALL:
619
1166
  case NODE_COLON2:
620
1167
  case NODE_COLON3:
1168
+ case NODE_BACK_REF:
1169
+ case NODE_DEFS:
1170
+ case NODE_VALIAS:
621
1171
  PRINT_ID(u2);
622
1172
  break;
623
1173
 
1174
+ case NODE_OP_ASGN1:
1175
+ if (RNODE(obj)->nd_mid == 0)
1176
+ yajl_gen_cstr(gen, ":||");
1177
+ else if (RNODE(obj)->nd_mid == 1)
1178
+ yajl_gen_cstr(gen, ":&&");
1179
+ else
1180
+ PRINT_ID(u2);
1181
+ break;
1182
+
1183
+ case NODE_OP_ASGN2:
1184
+ if (RNODE(obj)->u3.id > 1000000) {
1185
+ PRINT_VAL(u2);
1186
+ } else {
1187
+ if (RNODE(obj)->nd_mid == 0)
1188
+ yajl_gen_cstr(gen, ":||");
1189
+ else if (RNODE(obj)->nd_mid == 1)
1190
+ yajl_gen_cstr(gen, ":&&");
1191
+ else
1192
+ PRINT_ID(u2);
1193
+ }
1194
+ break;
1195
+
1196
+ case NODE_DREGX:
1197
+ case NODE_DREGX_ONCE:
624
1198
  case NODE_NTH_REF:
1199
+ case NODE_IFUNC:
1200
+ case NODE_CFUNC:
1201
+ case NODE_NEWLINE:
625
1202
  yajl_gen_integer(gen, RNODE(obj)->u2.argc);
626
1203
  break;
627
1204
 
1205
+ case NODE_BLOCK:
1206
+ case NODE_ARRAY:
1207
+ if (RNODE(obj)->u2.node == RNODE(obj))
1208
+ yajl_gen_null(gen);
1209
+ else
1210
+ PRINT_VAL(u2);
1211
+ break;
1212
+
628
1213
  default:
629
1214
  PRINT_VAL(u2);
630
1215
  }
@@ -635,6 +1220,13 @@ obj_dump(VALUE obj, yajl_gen gen)
635
1220
  yajl_gen_integer(gen, RNODE(obj)->u3.cnt);
636
1221
  break;
637
1222
 
1223
+ case NODE_OP_ASGN2:
1224
+ if (RNODE(obj)->u3.id > 1000000)
1225
+ PRINT_VAL(u3);
1226
+ else
1227
+ PRINT_ID(u3);
1228
+ break;
1229
+
638
1230
  default:
639
1231
  PRINT_VAL(u3);
640
1232
  }
@@ -642,9 +1234,10 @@ obj_dump(VALUE obj, yajl_gen gen)
642
1234
 
643
1235
  case T_STRING:
644
1236
  yajl_gen_cstr(gen, "string");
1237
+ obj_dump_class(gen, obj);
645
1238
 
646
1239
  yajl_gen_cstr(gen, "length");
647
- yajl_gen_integer(gen, RSTRING(obj)->len);
1240
+ yajl_gen_integer(gen, RSTRING_LEN(obj));
648
1241
 
649
1242
  if (FL_TEST(obj, ELTS_SHARED|FL_USER3)) {
650
1243
  yajl_gen_cstr(gen, "shared");
@@ -659,12 +1252,13 @@ obj_dump(VALUE obj, yajl_gen gen)
659
1252
  yajl_gen_array_close(gen);
660
1253
  } else {
661
1254
  yajl_gen_cstr(gen, "data");
662
- yajl_gen_string(gen, (unsigned char *)RSTRING(obj)->ptr, RSTRING(obj)->len);
1255
+ yajl_gen_string(gen, (unsigned char *)RSTRING_PTR(obj), RSTRING_LEN(obj));
663
1256
  }
664
1257
  break;
665
1258
 
666
1259
  case T_VARMAP:
667
1260
  yajl_gen_cstr(gen, "varmap");
1261
+ obj_dump_class(gen, obj);
668
1262
 
669
1263
  struct RVarmap *vars = (struct RVarmap *)obj;
670
1264
 
@@ -676,7 +1270,7 @@ obj_dump(VALUE obj, yajl_gen gen)
676
1270
  if (vars->id) {
677
1271
  yajl_gen_cstr(gen, "data");
678
1272
  yajl_gen_map_open(gen);
679
- yajl_gen_cstr(gen, rb_id2name(vars->id));
1273
+ yajl_gen_id(gen, vars->id);
680
1274
  yajl_gen_value(gen, vars->val);
681
1275
  yajl_gen_map_close(gen);
682
1276
  }
@@ -686,23 +1280,26 @@ obj_dump(VALUE obj, yajl_gen gen)
686
1280
  case T_MODULE:
687
1281
  case T_ICLASS:
688
1282
  yajl_gen_cstr(gen, type==T_CLASS ? "class" : type==T_MODULE ? "module" : "iclass");
1283
+ obj_dump_class(gen, obj);
689
1284
 
690
1285
  yajl_gen_cstr(gen, "name");
691
1286
  VALUE name = rb_classname(obj);
692
1287
  if (RTEST(name))
693
- yajl_gen_cstr(gen, RSTRING(name)->ptr);
1288
+ yajl_gen_cstr(gen, RSTRING_PTR(name));
694
1289
  else
695
1290
  yajl_gen_cstr(gen, 0);
696
1291
 
697
1292
  yajl_gen_cstr(gen, "super");
698
1293
  yajl_gen_value(gen, RCLASS(obj)->super);
699
1294
 
700
- yajl_gen_cstr(gen, "super_name");
701
- VALUE super_name = rb_classname(RCLASS(obj)->super);
702
- if (RTEST(super_name))
703
- yajl_gen_cstr(gen, RSTRING(super_name)->ptr);
704
- else
705
- yajl_gen_cstr(gen, 0);
1295
+ if (RTEST(RCLASS(obj)->super)) {
1296
+ yajl_gen_cstr(gen, "super_name");
1297
+ VALUE super_name = rb_classname(RCLASS(obj)->super);
1298
+ if (RTEST(super_name))
1299
+ yajl_gen_cstr(gen, RSTRING_PTR(super_name));
1300
+ else
1301
+ yajl_gen_cstr(gen, 0);
1302
+ }
706
1303
 
707
1304
  if (FL_TEST(obj, FL_SINGLETON)) {
708
1305
  yajl_gen_cstr(gen, "singleton");
@@ -716,7 +1313,7 @@ obj_dump(VALUE obj, yajl_gen gen)
716
1313
  yajl_gen_map_close(gen);
717
1314
  }
718
1315
 
719
- if (type != T_ICLASS && RCLASS(obj)->m_tbl && RCLASS(obj)->m_tbl->num_entries) {
1316
+ if (RCLASS(obj)->m_tbl && RCLASS(obj)->m_tbl->num_entries) {
720
1317
  yajl_gen_cstr(gen, "methods");
721
1318
  yajl_gen_map_open(gen);
722
1319
  st_foreach(RCLASS(obj)->m_tbl, each_ivar, (st_data_t)gen);
@@ -726,12 +1323,7 @@ obj_dump(VALUE obj, yajl_gen gen)
726
1323
 
727
1324
  case T_OBJECT:
728
1325
  yajl_gen_cstr(gen, "object");
729
-
730
- yajl_gen_cstr(gen, "class");
731
- yajl_gen_value(gen, RBASIC(obj)->klass);
732
-
733
- yajl_gen_cstr(gen, "class_name");
734
- yajl_gen_cstr(gen, rb_obj_classname(obj));
1326
+ obj_dump_class(gen, obj);
735
1327
 
736
1328
  struct RClass *klass = RCLASS(obj);
737
1329
 
@@ -745,6 +1337,7 @@ obj_dump(VALUE obj, yajl_gen gen)
745
1337
 
746
1338
  case T_ARRAY:
747
1339
  yajl_gen_cstr(gen, "array");
1340
+ obj_dump_class(gen, obj);
748
1341
 
749
1342
  struct RArray *ary = RARRAY(obj);
750
1343
 
@@ -766,6 +1359,7 @@ obj_dump(VALUE obj, yajl_gen gen)
766
1359
 
767
1360
  case T_HASH:
768
1361
  yajl_gen_cstr(gen, "hash");
1362
+ obj_dump_class(gen, obj);
769
1363
 
770
1364
  struct RHash *hash = RHASH(obj);
771
1365
 
@@ -790,6 +1384,7 @@ obj_dump(VALUE obj, yajl_gen gen)
790
1384
 
791
1385
  default:
792
1386
  yajl_gen_cstr(gen, "unknown");
1387
+ obj_dump_class(gen, obj);
793
1388
  }
794
1389
 
795
1390
  yajl_gen_cstr(gen, "code");
@@ -810,11 +1405,163 @@ extern st_table *rb_global_tbl;
810
1405
  static int
811
1406
  globals_each_dump(st_data_t key, st_data_t record, st_data_t arg)
812
1407
  {
813
- yajl_gen_cstr((yajl_gen)arg, rb_id2name((ID)key));
1408
+ yajl_gen_id((yajl_gen)arg, (ID)key);
814
1409
  yajl_gen_value((yajl_gen)arg, rb_gvar_get((void*)record));
815
1410
  return ST_CONTINUE;
816
1411
  }
817
1412
 
1413
+ static void
1414
+ memprof_dump_globals(yajl_gen gen)
1415
+ {
1416
+ yajl_gen_map_open(gen);
1417
+
1418
+ yajl_gen_cstr(gen, "_id");
1419
+ yajl_gen_cstr(gen, "globals");
1420
+
1421
+ yajl_gen_cstr(gen, "type");
1422
+ yajl_gen_cstr(gen, "globals");
1423
+
1424
+ yajl_gen_cstr(gen, "variables");
1425
+
1426
+ yajl_gen_map_open(gen);
1427
+ st_foreach(rb_global_tbl, globals_each_dump, (st_data_t)gen);
1428
+ yajl_gen_map_close(gen);
1429
+
1430
+ yajl_gen_map_close(gen);
1431
+ yajl_gen_reset(gen);
1432
+ }
1433
+
1434
+ static void
1435
+ memprof_dump_stack_frame(yajl_gen gen, struct FRAME *frame)
1436
+ {
1437
+ yajl_gen_map_open(gen);
1438
+
1439
+ yajl_gen_cstr(gen, "_id");
1440
+ yajl_gen_pointer(gen, frame);
1441
+
1442
+ yajl_gen_cstr(gen, "type");
1443
+ yajl_gen_cstr(gen, "frame");
1444
+
1445
+ yajl_gen_cstr(gen, "self");
1446
+ yajl_gen_value(gen, frame->self);
1447
+
1448
+ if (frame->last_class) {
1449
+ yajl_gen_cstr(gen, "last_class");
1450
+ yajl_gen_value(gen, frame->last_class);
1451
+ }
1452
+
1453
+ if (frame->orig_func) {
1454
+ yajl_gen_cstr(gen, "orig_func");
1455
+ yajl_gen_id(gen, frame->orig_func);
1456
+ }
1457
+
1458
+ if (frame->last_func && frame->last_func != frame->orig_func) {
1459
+ yajl_gen_cstr(gen, "last_func");
1460
+ yajl_gen_id(gen, frame->last_func);
1461
+ }
1462
+
1463
+ if (frame->node) {
1464
+ yajl_gen_cstr(gen, "node");
1465
+ yajl_gen_pointer(gen, (void*)frame->node);
1466
+ }
1467
+
1468
+ if (frame->prev) {
1469
+ yajl_gen_cstr(gen, "prev");
1470
+ yajl_gen_pointer(gen, (void*)frame->prev);
1471
+ }
1472
+
1473
+ if (frame->tmp) {
1474
+ yajl_gen_cstr(gen, "tmp");
1475
+ yajl_gen_pointer(gen, (void*)frame->tmp);
1476
+ }
1477
+
1478
+ yajl_gen_map_close(gen);
1479
+ yajl_gen_reset(gen);
1480
+
1481
+ if (frame->prev) {
1482
+ memprof_dump_stack_frame(gen, frame->prev);
1483
+ }
1484
+ }
1485
+
1486
+ static void
1487
+ memprof_dump_stack(yajl_gen gen)
1488
+ {
1489
+ memprof_dump_stack_frame(gen, ruby_frame);
1490
+ }
1491
+
1492
+ static void
1493
+ memprof_dump_lsof(yajl_gen gen)
1494
+ {
1495
+ VALUE cmd = rb_str_new2("lsof -np ");
1496
+ VALUE pid = rb_funcall(rb_mProcess, rb_intern("pid"), 0);
1497
+ rb_str_append(cmd, rb_funcall(pid, rb_intern("to_s"), 0));
1498
+
1499
+ VALUE lsof = rb_funcall(rb_cObject, '`', 1, cmd);
1500
+ if (RTEST(lsof)) {
1501
+ VALUE newline = rb_str_new2("\n");
1502
+ VALUE lines = rb_funcall(lsof, rb_intern("split"), 1, newline);
1503
+ int i;
1504
+ for (i=1; i < RARRAY_LEN(lines); i++) {
1505
+ VALUE parts = rb_funcall(RARRAY_PTR(lines)[i], rb_intern("split"), 2, Qnil, INT2FIX(9));
1506
+
1507
+ yajl_gen_map_open(gen);
1508
+
1509
+ yajl_gen_cstr(gen, "_id");
1510
+ yajl_gen_format(gen, "lsof:%d", i);
1511
+
1512
+ yajl_gen_cstr(gen, "type");
1513
+ yajl_gen_cstr(gen, "lsof");
1514
+
1515
+ yajl_gen_cstr(gen, "fd");
1516
+ yajl_gen_cstr(gen, RSTRING_PTR(RARRAY_PTR(parts)[3]));
1517
+
1518
+ yajl_gen_cstr(gen, "fd_type");
1519
+ yajl_gen_cstr(gen, RSTRING_PTR(RARRAY_PTR(parts)[4]));
1520
+
1521
+ yajl_gen_cstr(gen, "fd_name");
1522
+ yajl_gen_cstr(gen, RSTRING_PTR(RARRAY_PTR(parts)[RARRAY_LEN(parts)-1]));
1523
+
1524
+ yajl_gen_map_close(gen);
1525
+ yajl_gen_reset(gen);
1526
+ }
1527
+ }
1528
+ }
1529
+
1530
+ static void
1531
+ memprof_dump_ps(yajl_gen gen)
1532
+ {
1533
+ VALUE cmd = rb_str_new2("ps -o rss,vsize -p ");
1534
+ VALUE pid = rb_funcall(rb_mProcess, rb_intern("pid"), 0);
1535
+ rb_str_append(cmd, rb_funcall(pid, rb_intern("to_s"), 0));
1536
+
1537
+ VALUE ps = rb_funcall(rb_cObject, '`', 1, cmd);
1538
+ if (RTEST(ps)) {
1539
+ VALUE newline = rb_str_new2("\n");
1540
+ VALUE lines = rb_funcall(ps, rb_intern("split"), 1, newline);
1541
+
1542
+ if (RARRAY_LEN(lines) == 2) {
1543
+ VALUE parts = rb_funcall(RARRAY_PTR(lines)[1], rb_intern("split"), 0);
1544
+
1545
+ yajl_gen_map_open(gen);
1546
+
1547
+ yajl_gen_cstr(gen, "_id");
1548
+ yajl_gen_cstr(gen, "ps");
1549
+
1550
+ yajl_gen_cstr(gen, "type");
1551
+ yajl_gen_cstr(gen, "ps");
1552
+
1553
+ yajl_gen_cstr(gen, "rss");
1554
+ yajl_gen_cstr(gen, RSTRING_PTR(RARRAY_PTR(parts)[0]));
1555
+
1556
+ yajl_gen_cstr(gen, "vsize");
1557
+ yajl_gen_cstr(gen, RSTRING_PTR(RARRAY_PTR(parts)[1]));
1558
+
1559
+ yajl_gen_map_close(gen);
1560
+ yajl_gen_reset(gen);
1561
+ }
1562
+ }
1563
+ }
1564
+
818
1565
  static void
819
1566
  json_print(void *ctx, const char * str, unsigned int len)
820
1567
  {
@@ -868,8 +1615,8 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
868
1615
  memprof_config.heaps_used == NULL ||
869
1616
  memprof_config.sizeof_RVALUE == 0 ||
870
1617
  memprof_config.sizeof_heaps_slot == 0 ||
871
- memprof_config.offset_heaps_slot_slot == -1 ||
872
- memprof_config.offset_heaps_slot_limit == -1)
1618
+ memprof_config.offset_heaps_slot_slot == SIZE_MAX ||
1619
+ memprof_config.offset_heaps_slot_limit == SIZE_MAX)
873
1620
  rb_raise(eUnsupported, "not enough config data to dump heap");
874
1621
 
875
1622
  char *heaps = *(char**)memprof_config.heaps;
@@ -878,12 +1625,20 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
878
1625
  char *p, *pend;
879
1626
  int i, limit;
880
1627
  VALUE str;
1628
+ char *filename = NULL;
1629
+ char *in_progress_filename = NULL;
881
1630
  FILE *out = NULL;
882
1631
 
883
1632
  rb_scan_args(argc, argv, "01", &str);
884
1633
 
885
1634
  if (RTEST(str)) {
886
- out = fopen(StringValueCStr(str), "w");
1635
+ filename = StringValueCStr(str);
1636
+ size_t filename_len = strlen(filename);
1637
+ in_progress_filename = alloca(filename_len + 13);
1638
+ memcpy(in_progress_filename, filename, filename_len);
1639
+ memcpy(in_progress_filename + filename_len, ".IN_PROGRESS\0", 13);
1640
+
1641
+ out = fopen(in_progress_filename, "w");
887
1642
  if (!out)
888
1643
  rb_raise(rb_eArgError, "unable to open output file");
889
1644
  }
@@ -893,23 +1648,8 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
893
1648
 
894
1649
  track_objs = 0;
895
1650
 
896
- //yajl_gen_array_open(gen);
897
-
898
- yajl_gen_map_open(gen);
899
-
900
- yajl_gen_cstr(gen, "_id");
901
- yajl_gen_cstr(gen, "globals");
902
-
903
- yajl_gen_cstr(gen, "type");
904
- yajl_gen_cstr(gen, "globals");
905
-
906
- yajl_gen_cstr(gen, "variables");
907
-
908
- yajl_gen_map_open(gen);
909
- st_foreach(rb_global_tbl, globals_each_dump, (st_data_t)gen);
910
- yajl_gen_map_close(gen);
911
-
912
- yajl_gen_map_close(gen);
1651
+ memprof_dump_globals(gen);
1652
+ memprof_dump_stack(gen);
913
1653
 
914
1654
  for (i=0; i < heaps_used; i++) {
915
1655
  p = *(char**)(heaps + (i * memprof_config.sizeof_heaps_slot) + memprof_config.offset_heaps_slot_slot);
@@ -919,23 +1659,23 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
919
1659
  while (p < pend) {
920
1660
  if (RBASIC(p)->flags) {
921
1661
  obj_dump((VALUE)p, gen);
922
- // XXX ugh
923
- yajl_gen_clear(gen);
924
- yajl_gen_free(gen);
925
- gen = yajl_gen_alloc2((yajl_print_t)&json_print, &conf, NULL, (void*)out);
926
- while(fputc('\n', out ? out : stdout) == EOF);
1662
+ yajl_gen_reset(gen);
927
1663
  }
928
1664
 
929
1665
  p += memprof_config.sizeof_RVALUE;
930
1666
  }
931
1667
  }
932
1668
 
933
- //yajl_gen_array_close(gen);
1669
+ memprof_dump_lsof(gen);
1670
+ memprof_dump_ps(gen);
1671
+
934
1672
  yajl_gen_clear(gen);
935
1673
  yajl_gen_free(gen);
936
1674
 
937
- if (out)
1675
+ if (out) {
938
1676
  fclose(out);
1677
+ rename(in_progress_filename, filename);
1678
+ }
939
1679
 
940
1680
  track_objs = 1;
941
1681
 
@@ -945,8 +1685,8 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
945
1685
  static void
946
1686
  init_memprof_config_base() {
947
1687
  memset(&memprof_config, 0, sizeof(memprof_config));
948
- memprof_config.offset_heaps_slot_limit = -1;
949
- memprof_config.offset_heaps_slot_slot = -1;
1688
+ memprof_config.offset_heaps_slot_limit = SIZE_MAX;
1689
+ memprof_config.offset_heaps_slot_slot = SIZE_MAX;
950
1690
  memprof_config.pagesize = getpagesize();
951
1691
  assert(memprof_config.pagesize);
952
1692
  }
@@ -954,7 +1694,7 @@ init_memprof_config_base() {
954
1694
  static void
955
1695
  init_memprof_config_extended() {
956
1696
  /* If we don't have add_freelist, find the functions it gets inlined into */
957
- memprof_config.add_freelist = bin_find_symbol("add_freelist", NULL);
1697
+ memprof_config.add_freelist = bin_find_symbol("add_freelist", NULL, 0);
958
1698
 
959
1699
  /*
960
1700
  * Sometimes gc_sweep gets inlined in garbage_collect
@@ -962,27 +1702,31 @@ init_memprof_config_extended() {
962
1702
  */
963
1703
  if (memprof_config.add_freelist == NULL) {
964
1704
  memprof_config.gc_sweep = bin_find_symbol("gc_sweep",
965
- &memprof_config.gc_sweep_size);
1705
+ &memprof_config.gc_sweep_size, 0);
966
1706
  if (memprof_config.gc_sweep == NULL)
967
1707
  memprof_config.gc_sweep = bin_find_symbol("garbage_collect_0",
968
- &memprof_config.gc_sweep_size);
1708
+ &memprof_config.gc_sweep_size, 0);
969
1709
  if (memprof_config.gc_sweep == NULL)
970
1710
  memprof_config.gc_sweep = bin_find_symbol("garbage_collect",
971
- &memprof_config.gc_sweep_size);
1711
+ &memprof_config.gc_sweep_size, 0);
972
1712
 
973
1713
  memprof_config.finalize_list = bin_find_symbol("finalize_list",
974
- &memprof_config.finalize_list_size);
1714
+ &memprof_config.finalize_list_size, 0);
975
1715
  memprof_config.rb_gc_force_recycle = bin_find_symbol("rb_gc_force_recycle",
976
- &memprof_config.rb_gc_force_recycle_size);
977
- memprof_config.freelist = bin_find_symbol("freelist", NULL);
1716
+ &memprof_config.rb_gc_force_recycle_size, 0);
1717
+ memprof_config.freelist = bin_find_symbol("freelist", NULL, 0);
978
1718
  }
979
1719
 
980
- memprof_config.classname = bin_find_symbol("classname", NULL);
981
- memprof_config.rb_mark_table_add_filename = bin_find_symbol("rb_mark_table_add_filename", NULL);
1720
+ memprof_config.classname = bin_find_symbol("classname", NULL, 0);
1721
+ memprof_config.timeofday = bin_find_symbol("timeofday", NULL, 0);
1722
+ memprof_config.bm_mark = bin_find_symbol("bm_mark", NULL, 0);
1723
+ memprof_config.blk_free = bin_find_symbol("blk_free", NULL, 0);
1724
+ memprof_config.thread_mark = bin_find_symbol("thread_mark", NULL, 0);
1725
+ memprof_config.rb_mark_table_add_filename = bin_find_symbol("rb_mark_table_add_filename", NULL, 0);
982
1726
 
983
1727
  /* Stuff for dumping the heap */
984
- memprof_config.heaps = bin_find_symbol("heaps", NULL);
985
- memprof_config.heaps_used = bin_find_symbol("heaps_used", NULL);
1728
+ memprof_config.heaps = bin_find_symbol("heaps", NULL, 0);
1729
+ memprof_config.heaps_used = bin_find_symbol("heaps_used", NULL, 0);
986
1730
  #ifdef sizeof__RVALUE
987
1731
  memprof_config.sizeof_RVALUE = sizeof__RVALUE;
988
1732
  #else
@@ -1003,6 +1747,91 @@ init_memprof_config_extended() {
1003
1747
  #else
1004
1748
  memprof_config.offset_heaps_slot_slot = bin_type_member_offset("heaps_slot", "slot");
1005
1749
  #endif
1750
+ #ifdef offset__BLOCK__body
1751
+ memprof_config.offset_BLOCK_body = offset__BLOCK__body;
1752
+ #else
1753
+ memprof_config.offset_BLOCK_body = bin_type_member_offset("BLOCK", "body");
1754
+ #endif
1755
+ #ifdef offset__BLOCK__var
1756
+ memprof_config.offset_BLOCK_var = offset__BLOCK__var;
1757
+ #else
1758
+ memprof_config.offset_BLOCK_var = bin_type_member_offset("BLOCK", "var");
1759
+ #endif
1760
+ #ifdef offset__BLOCK__cref
1761
+ memprof_config.offset_BLOCK_cref = offset__BLOCK__cref;
1762
+ #else
1763
+ memprof_config.offset_BLOCK_cref = bin_type_member_offset("BLOCK", "cref");
1764
+ #endif
1765
+ #ifdef offset__BLOCK__prev
1766
+ memprof_config.offset_BLOCK_prev = offset__BLOCK__prev;
1767
+ #else
1768
+ memprof_config.offset_BLOCK_prev = bin_type_member_offset("BLOCK", "prev");
1769
+ #endif
1770
+ #ifdef offset__BLOCK__self
1771
+ memprof_config.offset_BLOCK_self = offset__BLOCK__self;
1772
+ #else
1773
+ memprof_config.offset_BLOCK_self = bin_type_member_offset("BLOCK", "self");
1774
+ #endif
1775
+ #ifdef offset__BLOCK__klass
1776
+ memprof_config.offset_BLOCK_klass = offset__BLOCK__klass;
1777
+ #else
1778
+ memprof_config.offset_BLOCK_klass = bin_type_member_offset("BLOCK", "klass");
1779
+ #endif
1780
+ #ifdef offset__BLOCK__orig_thread
1781
+ memprof_config.offset_BLOCK_orig_thread = offset__BLOCK__orig_thread;
1782
+ #else
1783
+ memprof_config.offset_BLOCK_orig_thread = bin_type_member_offset("BLOCK", "orig_thread");
1784
+ #endif
1785
+ #ifdef offset__BLOCK__wrapper
1786
+ memprof_config.offset_BLOCK_wrapper = offset__BLOCK__wrapper;
1787
+ #else
1788
+ memprof_config.offset_BLOCK_wrapper = bin_type_member_offset("BLOCK", "wrapper");
1789
+ #endif
1790
+ #ifdef offset__BLOCK__block_obj
1791
+ memprof_config.offset_BLOCK_block_obj = offset__BLOCK__block_obj;
1792
+ #else
1793
+ memprof_config.offset_BLOCK_block_obj = bin_type_member_offset("BLOCK", "block_obj");
1794
+ #endif
1795
+ #ifdef offset__BLOCK__scope
1796
+ memprof_config.offset_BLOCK_scope = offset__BLOCK__scope;
1797
+ #else
1798
+ memprof_config.offset_BLOCK_scope = bin_type_member_offset("BLOCK", "scope");
1799
+ #endif
1800
+ #ifdef offset__BLOCK__dyna_vars
1801
+ memprof_config.offset_BLOCK_dyna_vars = offset__BLOCK__dyna_vars;
1802
+ #else
1803
+ memprof_config.offset_BLOCK_dyna_vars = bin_type_member_offset("BLOCK", "dyna_vars");
1804
+ #endif
1805
+ #ifdef offset__METHOD__klass
1806
+ memprof_config.offset_METHOD_klass = offset__METHOD__klass;
1807
+ #else
1808
+ memprof_config.offset_METHOD_klass = bin_type_member_offset("METHOD", "klass");
1809
+ #endif
1810
+ #ifdef offset__METHOD__rklass
1811
+ memprof_config.offset_METHOD_rklass = offset__METHOD__rklass;
1812
+ #else
1813
+ memprof_config.offset_METHOD_rklass = bin_type_member_offset("METHOD", "rklass");
1814
+ #endif
1815
+ #ifdef offset__METHOD__recv
1816
+ memprof_config.offset_METHOD_recv = offset__METHOD__recv;
1817
+ #else
1818
+ memprof_config.offset_METHOD_recv = bin_type_member_offset("METHOD", "recv");
1819
+ #endif
1820
+ #ifdef offset__METHOD__id
1821
+ memprof_config.offset_METHOD_id = offset__METHOD__id;
1822
+ #else
1823
+ memprof_config.offset_METHOD_id = bin_type_member_offset("METHOD", "id");
1824
+ #endif
1825
+ #ifdef offset__METHOD__oid
1826
+ memprof_config.offset_METHOD_oid = offset__METHOD__oid;
1827
+ #else
1828
+ memprof_config.offset_METHOD_oid = bin_type_member_offset("METHOD", "oid");
1829
+ #endif
1830
+ #ifdef offset__METHOD__body
1831
+ memprof_config.offset_METHOD_body = offset__METHOD__body;
1832
+ #else
1833
+ memprof_config.offset_METHOD_body = bin_type_member_offset("METHOD", "body");
1834
+ #endif
1006
1835
 
1007
1836
  int heap_errors_printed = 0;
1008
1837
 
@@ -1018,10 +1847,10 @@ init_memprof_config_extended() {
1018
1847
  if (memprof_config.sizeof_heaps_slot == 0)
1019
1848
  heap_errors_printed += fprintf(stderr,
1020
1849
  "Failed to determine sizeof(heaps_slot)\n");
1021
- if (memprof_config.offset_heaps_slot_limit == -1)
1850
+ if (memprof_config.offset_heaps_slot_limit == SIZE_MAX)
1022
1851
  heap_errors_printed += fprintf(stderr,
1023
1852
  "Failed to determine offset of heaps_slot->limit\n");
1024
- if (memprof_config.offset_heaps_slot_slot == -1)
1853
+ if (memprof_config.offset_heaps_slot_slot == SIZE_MAX)
1025
1854
  heap_errors_printed += fprintf(stderr,
1026
1855
  "Failed to determine offset of heaps_slot->slot\n");
1027
1856
 
@@ -1086,6 +1915,7 @@ Init_memprof()
1086
1915
  rb_define_singleton_method(memprof, "track", memprof_track, -1);
1087
1916
  rb_define_singleton_method(memprof, "dump", memprof_dump, -1);
1088
1917
  rb_define_singleton_method(memprof, "dump_all", memprof_dump_all, -1);
1918
+ rb_define_singleton_method(memprof, "track_bytes", memprof_track_bytes, -1);
1089
1919
 
1090
1920
  objs = st_init_numtable();
1091
1921
  init_memprof_config_base();
@@ -1098,15 +1928,13 @@ Init_memprof()
1098
1928
 
1099
1929
  rb_classname = memprof_config.classname;
1100
1930
  rb_add_freelist = memprof_config.add_freelist;
1931
+ rb_timeofday = memprof_config.timeofday;
1932
+ rb_bm_mark = memprof_config.bm_mark;
1933
+ rb_blk_free = memprof_config.blk_free;
1934
+ rb_thread_mark = memprof_config.thread_mark;
1101
1935
  ptr_to_rb_mark_table_add_filename = memprof_config.rb_mark_table_add_filename;
1102
1936
 
1103
1937
  assert(rb_classname);
1104
1938
 
1105
- insert_tramp("rb_newobj", newobj_tramp);
1106
- insert_tramp("add_freelist", freelist_tramp);
1107
-
1108
- if (getenv("MEMPROF"))
1109
- track_objs = 1;
1110
-
1111
1939
  return;
1112
1940
  }