heap_dump 0.0.1 → 0.0.2

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