heap_dump 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,6 +6,8 @@
6
6
  #include "yarv-headers/constant.h"
7
7
  #include "yarv-headers/node.h"
8
8
  #include "yarv-headers/vm_core.h"
9
+ #include "yarv-headers/atomic.h"
10
+ #include "yarv-headers/iseq.h"
9
11
 
10
12
  //#undef RCLASS_IV_TBL
11
13
  //#include "yarv-headers/internal.h"
@@ -36,7 +38,7 @@ static ID classid;
36
38
  #define yg_cstring(str) yg_string(str, (unsigned int)strlen(str))
37
39
  #define yg_rstring(str) yg_string(RSTRING_PTR(str), (unsigned int)RSTRING_LEN(str))
38
40
  #define yg_int(i) yajl_gen_integer(ctx->yajl, i)
39
- #define yg_double(d) yajl_gen_double(ctx->yajl, d)
41
+ #define yg_double(d) (yajl_gen_double(ctx->yajl, d)==yajl_gen_invalid_number? yg_cstring("inf|NaN") : true)
40
42
 
41
43
  //#define yg_id(obj) yg_int(NUM2LONG(rb_obj_id(obj)))
42
44
  #define yg_id(obj) yg_id1(obj,ctx)
@@ -92,9 +94,16 @@ static inline const char* rb_builtin_type(VALUE obj){
92
94
 
93
95
  //FIXME: handle non-ids?
94
96
  static void yg_id1(VALUE obj, walk_ctx_t* ctx){
95
- if(!obj) return;
97
+ if(!obj) {
98
+ yajl_gen_null(ctx->yajl);
99
+ return;
100
+ }
96
101
  if (IMMEDIATE_P(obj)) {
97
- if (FIXNUM_P(obj)) { /*ignore immediate fixnum*/ return; }
102
+ //printf("immediate\n");
103
+ if (FIXNUM_P(obj)) { /*ignore immediate fixnum*/
104
+ //fixme: output some readable info
105
+ yajl_gen_null(ctx->yajl);
106
+ return; }
98
107
  if (obj == Qtrue){ yajl_gen_bool(ctx->yajl, true); return; }
99
108
  if (SYMBOL_P(obj)) {
100
109
  //printf("symbol\n");
@@ -103,22 +112,43 @@ static void yg_id1(VALUE obj, walk_ctx_t* ctx){
103
112
  return;
104
113
  }
105
114
  if (obj == Qundef) { yg_cstring("(undef)"); return; }
106
- printf("immediate p\n");
107
- } else /*non-immediate*/ if (!RTEST(obj)) {
108
- if (obj == Qnil){
109
- yajl_gen_null(ctx->yajl);
110
- return;
111
- }
112
- if (obj == Qfalse) {
113
- yajl_gen_bool(ctx->yajl, false);
114
- return;
115
+
116
+ printf("immediate p %p?\n", obj);
117
+ yg_cstring("(unknown)");
118
+ return;
119
+ } else /*non-immediate*/ {
120
+ if (!RTEST(obj)) {
121
+ if (obj == Qnil){
122
+ yajl_gen_null(ctx->yajl);
123
+ return;
124
+ }
125
+ if (obj == Qfalse) {
126
+ yajl_gen_bool(ctx->yajl, false);
127
+ return;
128
+ }
129
+ //printf("non r-test\n");
115
130
  }
116
131
  }
117
132
 
133
+ //25116(0x621c aka 0b110001000011100), 28(0x) - wtf?
134
+ //28 = 0x1c
135
+ //1c= TNODE? or other flags combination
136
+
137
+ //also 30(x1e) ? - some internal symbols?
138
+
139
+ // if((obj & ~(~(VALUE)0 << RUBY_SPECIAL_SHIFT)) == 0x1c){
140
+ // printf("!!!!! special shift flags is 0x1c: %p\n", obj);
141
+ // yg_cstring("(unknown or internal 1c)");
142
+ // return;
143
+ // }
144
+
118
145
  if(BUILTIN_TYPE(obj) == T_STRING && (!(RBASIC(obj)->flags & RSTRING_NOEMBED))){
119
146
  //printf("embedded string\n");
147
+ //yajl_gen_null(ctx->yajl);
148
+ yg_rstring(obj);
120
149
  return;
121
150
  }
151
+
122
152
  yg_int(NUM2LONG(rb_obj_id(obj)));
123
153
  }
124
154
 
@@ -217,9 +247,8 @@ static void dump_node_refs(NODE* obj, walk_ctx_t* ctx){
217
247
  //printf("node scope\n");
218
248
  if(obj->nd_tbl){
219
249
  ID *tbl = RNODE(obj)->nd_tbl;
220
- int size = tbl[0];
250
+ unsigned long i = 0, size = tbl[0];
221
251
  tbl++;
222
- int i = 0;
223
252
  for (; i < size; i++) {
224
253
  //TODO: dump local var names?
225
254
  //printf("%s\n", rb_id2name(tbl[i]));
@@ -312,7 +341,7 @@ static void dump_node_refs(NODE* obj, walk_ctx_t* ctx){
312
341
 
313
342
  //iteration func - blocks,procs,lambdas etc:
314
343
  case NODE_IFUNC: //NEN_CFNC, NEN_TVAL, NEN_STATE? / u2 seems to be data for func(context?)
315
- printf("IFUNC NODE: %p %p %p\n", obj->nd_cfnc, obj->u2.node, obj->nd_aid /*u3 - aid id- - aka frame_this_func?*/);
344
+ // printf("IFUNC NODE: %p %p %p\n", obj->nd_cfnc, obj->u2.node, (void*)obj->nd_aid /*u3 - aid id- - aka frame_this_func?*/);
316
345
  //FIXME: closures may leak references?
317
346
  break;
318
347
 
@@ -323,7 +352,7 @@ static void dump_node_refs(NODE* obj, walk_ctx_t* ctx){
323
352
 
324
353
  {const Node_Type_Descrip* descrip = node_type_descrips[nd_type(obj)];
325
354
 
326
- printf("UNKNOWN NODE TYPE %d(%s): %p %p %p\n", nd_type(obj), descrip ? descrip->name : "unknown", obj->u1.node, obj->u2.node, obj->u3.node);
355
+ printf("UNKNOWN NODE TYPE %d(%s): %p %p %p\n", nd_type(obj), descrip ? descrip->name : "unknown", (void*)obj->u1.node, (void*)obj->u2.node, (void*)obj->u3.node);
327
356
  }
328
357
 
329
358
  // if (is_pointer_to_heap(objspace, obj->as.node.u1.node)) { gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev); }
@@ -425,6 +454,106 @@ static int dump_const_entry_i(ID key, const rb_const_entry_t *ce, walk_ctx_t *ct
425
454
  return ST_CONTINUE;
426
455
  }
427
456
 
457
+ const char* iseq_type(VALUE type){
458
+ switch(type){
459
+ case ISEQ_TYPE_TOP: return "top";
460
+ case ISEQ_TYPE_METHOD: return "method";
461
+ case ISEQ_TYPE_BLOCK: return "block";
462
+ case ISEQ_TYPE_CLASS: return "class";
463
+ case ISEQ_TYPE_RESCUE: return "rescue";
464
+ case ISEQ_TYPE_ENSURE: return "ensure";
465
+ case ISEQ_TYPE_EVAL: return "eval";
466
+ case ISEQ_TYPE_MAIN: return "main";
467
+ case ISEQ_TYPE_DEFINED_GUARD: return "defined_guard";
468
+ }
469
+ printf("unknown iseq type %d!\n", type);
470
+ return "unknown";
471
+ }
472
+
473
+ void dump_iseq(const rb_iseq_t* iseq, walk_ctx_t *ctx){
474
+ if(iseq->name) ygh_rstring("name", iseq->name);
475
+ if(iseq->filename) ygh_rstring("filename", iseq->filename);
476
+ ygh_int("line", iseq->line_no);
477
+
478
+ //if(iseq->type != 25116) //also 28 in mark_ary
479
+ ygh_cstring("type", iseq_type(iseq->type));
480
+ //see isec.c: iseq_data_to_ary(rb_iseq_t* )
481
+
482
+ //28 is what?
483
+ ygh_id("refs_array_id", iseq->mark_ary);
484
+
485
+
486
+ ygh_id("coverage", iseq->coverage);
487
+ ygh_id("klass", iseq->klass);
488
+ ygh_id("cref_stack", (VALUE)iseq->cref_stack); //NODE*
489
+
490
+ //TODO: resolve id into str?
491
+ ygh_int("defined_method_id", iseq->defined_method_id);
492
+
493
+ if (iseq->compile_data != 0) {
494
+ struct iseq_compile_data *const compile_data = iseq->compile_data;
495
+ ygh_id("cd_marks_ary", compile_data->mark_ary);
496
+ ygh_id("cd_err_info", compile_data->err_info);
497
+ ygh_id("cd_catch_table_ary", compile_data->catch_table_ary);
498
+ }
499
+ }
500
+
501
+ void dump_data_if_known(VALUE obj, walk_ctx_t *ctx){
502
+
503
+ // VM
504
+ // VM/env
505
+ // VM/thread
506
+ // autoload
507
+ // binding
508
+ // encoding
509
+ // iseq <-
510
+ // method <-
511
+ // mutex
512
+ // proc <-
513
+ // thgroup
514
+ // time
515
+
516
+ const char* typename = RTYPEDDATA_TYPE(obj)->wrap_struct_name;
517
+
518
+ if(!strcmp("iseq", typename)){
519
+ const rb_iseq_t* iseq = RTYPEDDATA_DATA(obj);
520
+ dump_iseq(iseq, ctx);
521
+ return;
522
+ }
523
+
524
+ if(!strcmp("proc", typename)){
525
+ const rb_proc_t *proc = RTYPEDDATA_DATA(obj);
526
+ ygh_int("is_lambda", proc->is_lambda);
527
+ ygh_id("blockprocval", proc->blockprocval);
528
+ ygh_id("envval", proc->envval);
529
+ //TODO: dump refs from env here (they're dumped in env itself, but just to make analysis easier)?
530
+
531
+ //TODO: is this proc->block.iseq bound somewhere else? probably not
532
+ // if(proc->block.iseq && !RUBY_VM_IFUNC_P(proc->block.iseq)) {
533
+ // yg_cstring("iseq");
534
+ // yajl_gen_map_open(ctx->yajl);
535
+ // ygh_id("id", proc->block.iseq);
536
+ // dump_iseq(proc->block.iseq, ctx);
537
+ // yajl_gen_map_close(ctx->yajl);
538
+ // } else {
539
+ ygh_id("iseq", proc->block.iseq);
540
+ // }
541
+ return;
542
+ }
543
+
544
+ if(!strcmp("VM/env", typename)){
545
+ const rb_env_t* env = RTYPEDDATA_DATA(obj);
546
+ int i = 0;
547
+ yg_cstring("refs");
548
+ yajl_gen_array_open(ctx->yajl);
549
+ for(; i > env->env_size; i++)
550
+ yg_id(env->env[i]);
551
+ yajl_gen_array_close(ctx->yajl);
552
+ return;
553
+ }
554
+
555
+ }
556
+
428
557
  static VALUE rb_class_real_checked(VALUE cl)
429
558
  {
430
559
  if (cl == 0)
@@ -443,9 +572,14 @@ static inline void walk_live_object(VALUE obj, walk_ctx_t *ctx){
443
572
  ctx->live_objects++;
444
573
  yajl_gen_map_open(ctx->yajl);
445
574
 
446
- ygh_int("id", NUM2LONG(rb_obj_id(obj))); //TODO: object_id is value>>2 ?
575
+ ygh_int("id", NUM2LONG(rb_obj_id(obj)));
447
576
  ygh_cstring("bt", rb_builtin_type(obj));
448
577
 
578
+ //TODO:
579
+ #ifdef GC_DEBUG
580
+ //RVALUE etc. has file/line info in this case
581
+ #endif
582
+
449
583
  switch(BUILTIN_TYPE(obj)){ // no need to call TYPE(), as value is on heap
450
584
  case T_NODE:
451
585
  dump_node(RNODE(obj), ctx);
@@ -568,11 +702,45 @@ static inline void walk_live_object(VALUE obj, walk_ctx_t *ctx){
568
702
  case T_FLOAT:
569
703
  ygh_double("val", RFLOAT_VALUE(obj));
570
704
  break;
571
- // T(T_BIGNUM);
572
- // T(T_RATIONAL); // refs too (num/den)...
573
- // T(T_COMPLEX);
705
+ case T_RATIONAL:
706
+ //TODO: dump value for immediate components
707
+ yg_cstring("refs");
708
+ yajl_gen_array_open(ctx->yajl);
709
+ yg_id(RRATIONAL(obj)->num);
710
+ yg_id(RRATIONAL(obj)->den);
711
+ yajl_gen_array_close(ctx->yajl);
712
+ break;
713
+ case T_COMPLEX:
714
+ yg_cstring("refs");
715
+ yajl_gen_array_open(ctx->yajl);
716
+ yg_id(RCOMPLEX(obj)->real);
717
+ yg_id(RCOMPLEX(obj)->imag);
718
+ yajl_gen_array_close(ctx->yajl);
719
+ break;
720
+
721
+ case T_BIGNUM:
722
+ {
723
+ long len = RBIGNUM_LEN(obj), i;
724
+ BDIGIT* digits = RBIGNUM_DIGITS(obj);
725
+ yg_cstring("digits");
726
+ yajl_gen_array_open(ctx->yajl);
727
+ for(i = 0; i < len; i++)
728
+ yg_int(digits[i]);
729
+ yajl_gen_array_close(ctx->yajl);
730
+ }
731
+ break;
732
+
733
+ case T_DATA: // data of extensions + raw bytecode etc., refs undumpable? maybe in some way mess with mark callback? (need to intercept rb_gc_mark :( )
734
+ if(RTYPEDDATA_P(obj)){
735
+ ygh_cstring("type_name", RTYPEDDATA_TYPE(obj)->wrap_struct_name);
736
+ if(RTYPEDDATA_TYPE(obj)->dsize){
737
+ ygh_int("size", RTYPEDDATA_TYPE(obj)->dsize(RTYPEDDATA_DATA(obj)));
738
+ }
739
+
740
+ dump_data_if_known(obj, ctx);
741
+ }
742
+ break;
574
743
 
575
- // T(T_DATA); // data of extensions, undumpable? maybe in some way mess with mark callback? (need to intercept rb_gc_mark :( )
576
744
  // T(T_UNDEF);
577
745
  default: break;
578
746
  }
@@ -601,34 +769,476 @@ static int objspace_walker(void *vstart, void *vend, int stride, walk_ctx_t *ctx
601
769
  }
602
770
 
603
771
 
772
+ //TODO: move to separate header.
773
+ /*
774
+ Bits of code taken directly from ruby gc
775
+ Copyright (C) 1993-2007 Yukihiro Matsumoto
776
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
777
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
778
+ */
779
+ // #if defined(__x86_64__) && defined(__GNUC__) && !defined(__native_client__)
780
+ // #define SET_MACHINE_STACK_END(p) __asm__("movq\t%%rsp, %0" : "=r" (*(p)))
781
+ // #elif defined(__i386) && defined(__GNUC__) && !defined(__native_client__)
782
+ // #define SET_MACHINE_STACK_END(p) __asm__("movl\t%%esp, %0" : "=r" (*(p)))
783
+ // #else
784
+ NOINLINE(void rb_gc_set_stack_end(VALUE **stack_end_p));
785
+ #define SET_MACHINE_STACK_END(p) rb_gc_set_stack_end(p)
786
+ #define USE_CONSERVATIVE_STACK_END
787
+ // #endif
788
+ void
789
+ rb_gc_set_stack_end(VALUE **stack_end_p)
790
+ {
791
+ VALUE stack_end;
792
+ *stack_end_p = &stack_end;
793
+ }
794
+
795
+ #ifdef __ia64
796
+ #define SET_STACK_END (SET_MACHINE_STACK_END(&th->machine_stack_end), th->machine_register_stack_end = rb_ia64_bsp())
797
+ #else
798
+ #define SET_STACK_END SET_MACHINE_STACK_END(&th->machine_stack_end)
799
+ #endif
800
+
801
+ #define STACK_START (th->machine_stack_start)
802
+ #define STACK_END (th->machine_stack_end)
803
+ #define STACK_LEVEL_MAX (th->machine_stack_maxsize/sizeof(VALUE))
804
+
805
+ #if STACK_GROW_DIRECTION < 0
806
+ # define STACK_LENGTH (size_t)(STACK_START - STACK_END)
807
+ #elif STACK_GROW_DIRECTION > 0
808
+ # define STACK_LENGTH (size_t)(STACK_END - STACK_START + 1)
809
+ #else
810
+ # define STACK_LENGTH ((STACK_END < STACK_START) ? (size_t)(STACK_START - STACK_END) \
811
+ : (size_t)(STACK_END - STACK_START + 1))
812
+ #endif
813
+ #if !STACK_GROW_DIRECTION
814
+ int ruby_stack_grow_direction;
815
+ int
816
+ ruby_get_stack_grow_direction(volatile VALUE *addr)
817
+ {
818
+ VALUE *end;
819
+ SET_MACHINE_STACK_END(&end);
820
+
821
+ if (end > addr) return ruby_stack_grow_direction = 1;
822
+ return ruby_stack_grow_direction = -1;
823
+ }
824
+ #endif
825
+
826
+ #if STACK_GROW_DIRECTION < 0
827
+ #define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_END, (end) = STACK_START)
828
+ #elif STACK_GROW_DIRECTION > 0
829
+ #define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_START, (end) = STACK_END+(appendix))
830
+ #else
831
+ #define GET_STACK_BOUNDS(start, end, appendix) \
832
+ ((STACK_END < STACK_START) ? \
833
+ ((start) = STACK_END, (end) = STACK_START) : ((start) = STACK_START, (end) = STACK_END+(appendix)))
834
+ #endif
835
+
836
+ #define rb_setjmp(env) RUBY_SETJMP(env)
837
+ #define rb_jmp_buf rb_jmpbuf_t
838
+
839
+ #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
840
+
841
+ extern st_table *rb_class_tbl;
842
+
843
+
844
+ /////////////
845
+ #define MARK_STACK_MAX 1024
846
+
847
+ #ifndef CALC_EXACT_MALLOC_SIZE
848
+ #define CALC_EXACT_MALLOC_SIZE 0
849
+ #endif
850
+ #include "ruby/re.h"
851
+
852
+ #ifndef FALSE
853
+ # define FALSE 0
854
+ #elif FALSE
855
+ # error FALSE must be false
856
+ #endif
857
+ #ifndef TRUE
858
+ # define TRUE 1
859
+ #elif !TRUE
860
+ # error TRUE must be true
861
+ #endif
862
+
863
+ //FIXME: this should be autoextracted from ruby
864
+ typedef struct RVALUE {
865
+ union {
866
+ struct {
867
+ VALUE flags; /* always 0 for freed obj */
868
+ struct RVALUE *next;
869
+ } free;
870
+ struct RBasic basic;
871
+ struct RObject object;
872
+ struct RClass klass;
873
+ struct RFloat flonum;
874
+ struct RString string;
875
+ struct RArray array;
876
+ struct RRegexp regexp;
877
+ struct RHash hash;
878
+ struct RData data;
879
+ struct RTypedData typeddata;
880
+ struct RStruct rstruct;
881
+ struct RBignum bignum;
882
+ struct RFile file;
883
+ struct RNode node;
884
+ struct RMatch match;
885
+ struct RRational rational;
886
+ struct RComplex complex;
887
+ } as;
888
+ #ifdef GC_DEBUG
889
+ const char *file;
890
+ int line;
891
+ #endif
892
+ } RVALUE;
893
+
894
+ typedef struct gc_profile_record {
895
+ double gc_time;
896
+ double gc_mark_time;
897
+ double gc_sweep_time;
898
+ double gc_invoke_time;
899
+
900
+ size_t heap_use_slots;
901
+ size_t heap_live_objects;
902
+ size_t heap_free_objects;
903
+ size_t heap_total_objects;
904
+ size_t heap_use_size;
905
+ size_t heap_total_size;
906
+
907
+ int have_finalize;
908
+ int is_marked;
909
+
910
+ size_t allocate_increase;
911
+ size_t allocate_limit;
912
+ } gc_profile_record;
913
+
914
+ struct heaps_slot {
915
+ void *membase;
916
+ RVALUE *slot;
917
+ size_t limit;
918
+ uintptr_t *bits;
919
+ RVALUE *freelist;
920
+ struct heaps_slot *next;
921
+ struct heaps_slot *prev;
922
+ struct heaps_slot *free_next;
923
+ };
924
+
925
+ struct heaps_header {
926
+ struct heaps_slot *base;
927
+ uintptr_t *bits;
928
+ };
929
+
930
+ struct sorted_heaps_slot {
931
+ RVALUE *start;
932
+ RVALUE *end;
933
+ struct heaps_slot *slot;
934
+ };
935
+
936
+ struct heaps_free_bitmap {
937
+ struct heaps_free_bitmap *next;
938
+ };
939
+
940
+ struct gc_list {
941
+ VALUE *varptr;
942
+ struct gc_list *next;
943
+ };
944
+
945
+
946
+ // typedef struct rb_objspace {
947
+ // struct {
948
+ // size_t limit;
949
+ // size_t increase;
950
+ // //FIXME: this should match ruby settings
951
+ // //#if CALC_EXACT_MALLOC_SIZE
952
+ // size_t allocated_size;
953
+ // size_t allocations;
954
+ // //#endif
955
+ // } malloc_params;
956
+
957
+ // struct {
958
+ // size_t increment;
959
+ // struct heaps_slot *ptr;
960
+ // struct heaps_slot *sweep_slots;
961
+ // struct heaps_slot *free_slots;
962
+ // struct sorted_heaps_slot *sorted;
963
+ // size_t length;
964
+ // size_t used;
965
+ // struct heaps_free_bitmap *free_bitmap;
966
+ // RVALUE *range[2];
967
+ // RVALUE *freed;
968
+ // size_t live_num;
969
+ // size_t free_num;
970
+ // size_t free_min;
971
+ // size_t final_num;
972
+ // size_t do_heap_free;
973
+ // } heap;
974
+
975
+ // struct {
976
+ // int dont_gc;
977
+ // int dont_lazy_sweep;
978
+ // int during_gc;
979
+ // rb_atomic_t finalizing;
980
+ // } flags;
981
+
982
+ // struct {
983
+ // st_table *table;
984
+ // RVALUE *deferred;
985
+ // } final;
986
+
987
+ // struct {
988
+ // VALUE buffer[MARK_STACK_MAX];
989
+ // VALUE *ptr;
990
+ // int overflow;
991
+ // } markstack;
992
+
993
+ // struct {
994
+ // int run;
995
+ // gc_profile_record *record;
996
+ // size_t count;
997
+ // size_t size;
998
+ // double invoke_time;
999
+ // } profile;
1000
+
1001
+ // struct gc_list *global_list;
1002
+ // size_t count;
1003
+ // int gc_stress;
1004
+ // } rb_objspace_t;
1005
+
1006
+
1007
+ // 1.9.2-p290:
1008
+ typedef struct rb_objspace {
1009
+ struct {
1010
+ size_t limit;
1011
+ size_t increase;
1012
+ #if CALC_EXACT_MALLOC_SIZE
1013
+ size_t allocated_size;
1014
+ size_t allocations;
1015
+ #endif
1016
+ } malloc_params;
1017
+ struct {
1018
+ size_t increment;
1019
+ struct heaps_slot *ptr;
1020
+ size_t length;
1021
+ size_t used;
1022
+ RVALUE *freelist;
1023
+ RVALUE *range[2];
1024
+ RVALUE *freed;
1025
+ } heap;
1026
+ struct {
1027
+ int dont_gc;
1028
+ int during_gc;
1029
+ } flags;
1030
+ struct {
1031
+ st_table *table;
1032
+ RVALUE *deferred;
1033
+ } final;
1034
+ struct {
1035
+ VALUE buffer[MARK_STACK_MAX];
1036
+ VALUE *ptr;
1037
+ int overflow;
1038
+ } markstack;
1039
+ struct {
1040
+ int run;
1041
+ gc_profile_record *record;
1042
+ size_t count;
1043
+ size_t size;
1044
+ double invoke_time;
1045
+ } profile;
1046
+ struct gc_list *global_list;
1047
+ unsigned int count;
1048
+ int gc_stress;
1049
+ } rb_objspace_t;
1050
+
1051
+ #define malloc_limit objspace->malloc_params.limit
1052
+ #define malloc_increase objspace->malloc_params.increase
1053
+ #define heaps objspace->heap.ptr
1054
+ #define heaps_length objspace->heap.length
1055
+ #define heaps_used objspace->heap.used
1056
+ #define lomem objspace->heap.range[0]
1057
+ #define himem objspace->heap.range[1]
1058
+ #define heaps_inc objspace->heap.increment
1059
+ #define heaps_freed objspace->heap.freed
1060
+ #define dont_gc objspace->flags.dont_gc
1061
+ #define during_gc objspace->flags.during_gc
1062
+ #define finalizing objspace->flags.finalizing
1063
+ #define finalizer_table objspace->final.table
1064
+ #define deferred_final_list objspace->final.deferred
1065
+ #define mark_stack objspace->markstack.buffer
1066
+ #define mark_stack_ptr objspace->markstack.ptr
1067
+ #define mark_stack_overflow objspace->markstack.overflow
1068
+ #define global_List objspace->global_list
1069
+
1070
+ #define RANY(o) ((RVALUE*)(o))
1071
+
1072
+ static inline int
1073
+ is_pointer_to_heap(void *ptr, rb_objspace_t *objspace)
1074
+ {
1075
+ if(!ptr) return false;
1076
+ if(!objspace) objspace = GET_THREAD()->vm->objspace;
1077
+
1078
+ register RVALUE *p = RANY(ptr);
1079
+ //register struct sorted_heaps_slot *heap;
1080
+ register size_t hi, lo, mid;
1081
+
1082
+ if (p < lomem || p > himem) {
1083
+ //printf("not in range %p - %p (objspace %p, l%u used %u)\n", lo, hi, objspace, heaps_length, heaps_used);
1084
+ return FALSE;
1085
+ }
1086
+ //printf("ptr in range\n");
1087
+ if ((VALUE)p % sizeof(RVALUE) != 0) return FALSE;
1088
+ //printf("ptr %p align correct\n", ptr);
1089
+
1090
+ //1.9.2-p290
1091
+ /* check if p looks like a pointer using bsearch*/
1092
+ lo = 0;
1093
+ hi = heaps_used;
1094
+ while (lo < hi) {
1095
+ mid = (lo + hi) / 2;
1096
+ register struct heaps_slot *heap;
1097
+ heap = &heaps[mid];
1098
+ if (heap->slot <= p) {
1099
+ if (p < heap->slot + heap->limit)
1100
+ return TRUE;
1101
+ lo = mid + 1;
1102
+ }
1103
+ else {
1104
+ hi = mid;
1105
+ }
1106
+ }
1107
+ //printf("not found");
1108
+ return FALSE;
1109
+ }
1110
+
1111
+
1112
+ static void dump_machine_context(walk_ctx_t *ctx){
1113
+ //TODO: other threads?
1114
+ rb_thread_t* th = GET_THREAD()->vm->main_thread; //GET_THREAD();
1115
+ union {
1116
+ rb_jmp_buf j;
1117
+ VALUE v[sizeof(rb_jmp_buf) / sizeof(VALUE)];
1118
+ } save_regs_gc_mark;
1119
+ VALUE *stack_start, *stack_end;
1120
+
1121
+
1122
+ yg_cstring("stack_and_registers");
1123
+ yajl_gen_array_open(ctx->yajl);
1124
+
1125
+ FLUSH_REGISTER_WINDOWS;
1126
+ /* This assumes that all registers are saved into the jmp_buf (and stack) */
1127
+ rb_setjmp(save_regs_gc_mark.j);
1128
+
1129
+ SET_STACK_END;
1130
+ GET_STACK_BOUNDS(stack_start, stack_end, 1);
1131
+
1132
+ //mark_locations_array(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v));
1133
+ VALUE* x = save_regs_gc_mark.v;
1134
+ unsigned long n = numberof(save_regs_gc_mark.v);
1135
+ //printf("registers\n");
1136
+ while (n--) {
1137
+ VALUE v = *(x++);
1138
+ if(is_pointer_to_heap((void*)v, NULL))
1139
+ yg_id(v);
1140
+ }
1141
+
1142
+ //printf("stack: %p %p\n", stack_start, stack_end);
1143
+
1144
+ //rb_gc_mark_locations(stack_start, stack_end);
1145
+ if(stack_start < stack_end){
1146
+ n = stack_end - stack_start;
1147
+ x = stack_start;
1148
+ while (n--) {
1149
+ VALUE v = *(x++);
1150
+ //printf("val: %p\n", (void*)v);
1151
+ //FIXME: other objspace (not default one?)
1152
+ if(is_pointer_to_heap((void*)v, NULL)) {
1153
+ //printf("ON heap\n");
1154
+ yg_id(v);
1155
+ }
1156
+ }
1157
+ }
1158
+
1159
+ yajl_gen_array_close(ctx->yajl);
1160
+ }
1161
+
1162
+ static int dump_iv_entry1(ID key, rb_const_entry_t* ce/*st_data_t val*/, walk_ctx_t *ctx){
1163
+ if (!rb_is_const_id(key)) return ST_CONTINUE; //?
1164
+ //rb_const_entry_t* ce = val;
1165
+ VALUE value = ce->value;
1166
+
1167
+ //printf("cls %p\n", (void*)value);
1168
+ //printf("id: %s\n", rb_id2name(key));
1169
+
1170
+ //val - damaged in some way?
1171
+
1172
+ //printf("name %s\n", RSTRING_PTR(rb_class_path(rb_class_real_checked(value))));
1173
+
1174
+ //if(is_pointer_to_heap(value, NULL)){
1175
+ //printf("on heap\n");
1176
+ yg_id(value);
1177
+ //}
1178
+
1179
+ return ST_CONTINUE;
1180
+ }
1181
+
604
1182
  static VALUE
605
1183
  rb_heapdump_dump(VALUE self, VALUE filename)
606
1184
  {
607
- struct walk_ctx ctx;
608
- memset(&ctx, 0, sizeof(ctx));
1185
+ struct walk_ctx ctx_o, *ctx = &ctx_o;
1186
+ memset(ctx, 0, sizeof(*ctx));
609
1187
 
610
1188
  Check_Type(filename, T_STRING);
611
1189
 
612
1190
  printf("Dump should go to %s\n", RSTRING_PTR(filename));
613
- ctx.file = fopen(RSTRING_PTR(filename), "wt");
614
- ctx.yajl = yajl_gen_alloc(NULL,NULL);
615
- yajl_gen_array_open(ctx.yajl);
1191
+ ctx->file = fopen(RSTRING_PTR(filename), "wt");
1192
+ ctx->yajl = yajl_gen_alloc(NULL,NULL);
1193
+ yajl_gen_array_open(ctx->yajl);
616
1194
 
617
- rb_objspace_each_objects(objspace_walker, &ctx);
1195
+ //dump origins:
1196
+ yajl_gen_map_open(ctx->yajl);
1197
+ ygh_cstring("id", "_ROOTS_");
1198
+
1199
+ printf("machine context\n");
1200
+
1201
+ dump_machine_context(ctx);
1202
+ flush_yajl(ctx);
1203
+
1204
+ struct gc_list *list;
1205
+ /* mark protected global variables */
1206
+ printf("global_List\n");
1207
+ for (list = GET_THREAD()->vm->global_List; list; list = list->next) {
1208
+ VALUE v = *list->varptr;
1209
+ //printf("global %p\n", v);
1210
+ }
1211
+
1212
+ yg_cstring("classes");
1213
+ yajl_gen_array_open(ctx->yajl);
1214
+ printf("classes\n");
1215
+ if (rb_class_tbl && rb_class_tbl->num_entries > 0)
1216
+ st_foreach(rb_class_tbl, dump_iv_entry1, (st_data_t)ctx);
1217
+ else printf("no classes\n");
1218
+ yajl_gen_array_close(ctx->yajl);
1219
+ flush_yajl(ctx);
1220
+
1221
+ //TODO: other gc entry points - symbols, encodings, etc.
1222
+
1223
+ yajl_gen_map_close(ctx->yajl); //id:roots
1224
+ flush_yajl(ctx);
1225
+
1226
+ //now dump all live objects
1227
+ printf("starting objspace walk\n");
1228
+ rb_objspace_each_objects(objspace_walker, ctx);
618
1229
 
619
- yajl_gen_array_close(ctx.yajl);
620
- flush_yajl(&ctx);
621
- yajl_gen_free(ctx.yajl);
622
- fclose(ctx.file);
1230
+ yajl_gen_array_close(ctx->yajl);
1231
+ flush_yajl(ctx);
1232
+ yajl_gen_free(ctx->yajl);
1233
+ fclose(ctx->file);
623
1234
 
624
- printf("Walker called %d times, seen %d live objects.\n", ctx.walker_called, ctx.live_objects);
1235
+ printf("Walker called %d times, seen %d live objects.\n", ctx->walker_called, ctx->live_objects);
625
1236
 
626
1237
  return Qnil;
627
1238
  }
628
1239
 
629
1240
 
630
1241
  void Init_heap_dump(){
631
- printf("heap_dump extension loading\n");
632
1242
  //ruby-internal need to be required before linking us, but just in case..
633
1243
  rb_require("internal/node");
634
1244
  rb_require("yajl");
data/heap_dump.gemspec CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = HeapDump::VERSION
17
17
 
18
- gem.required_ruby_version = '>=1.9.2'
18
+ gem.required_ruby_version = '~>1.9.2p290' # heap structure changed in 1.9.3, support later
19
19
  #gem.platform = Gem::Platform::CURRENT # other than osx - maybe later
20
20
 
21
21
  gem.extensions = "ext/heap_dump/extconf.rb"
@@ -1,3 +1,3 @@
1
1
  module HeapDump
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/heap_dump.rb CHANGED
@@ -9,7 +9,8 @@ require 'heap_dump.bundle'
9
9
 
10
10
  module HeapDump
11
11
  # Dumps ruby object space to file
12
- def self.dump filename='dump.json'
12
+ def self.dump filename='dump.json', gc_before_dump=true
13
+ GC.start if gc_before_dump
13
14
  return dump_ext(filename)
14
15
  end
15
16
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heap_dump
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-25 00:00:00.000000000Z
12
+ date: 2012-05-29 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ruby-internal
16
- requirement: &70288827505440 !ruby/object:Gem::Requirement
16
+ requirement: &70106505219640 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.8.5
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70288827505440
24
+ version_requirements: *70106505219640
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: yajl-ruby
27
- requirement: &70288827504740 !ruby/object:Gem::Requirement
27
+ requirement: &70106505219140 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '1.1'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70288827504740
35
+ version_requirements: *70106505219140
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rake-compiler
38
- requirement: &70288827504140 !ruby/object:Gem::Requirement
38
+ requirement: &70106505218720 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70288827504140
46
+ version_requirements: *70106505218720
47
47
  description: dump ruby 1.9 heap contents
48
48
  email:
49
49
  - vasilyfedoseyev@gmail.com
@@ -72,9 +72,9 @@ require_paths:
72
72
  required_ruby_version: !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
- - - ! '>='
75
+ - - ~>
76
76
  - !ruby/object:Gem::Version
77
- version: 1.9.2
77
+ version: 1.9.2p290
78
78
  required_rubygems_version: !ruby/object:Gem::Requirement
79
79
  none: false
80
80
  requirements: