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.
- data/ext/heap_dump/heap_dump.c +643 -33
- data/heap_dump.gemspec +1 -1
- data/lib/heap_dump/version.rb +1 -1
- data/lib/heap_dump.rb +2 -1
- metadata +10 -10
data/ext/heap_dump/heap_dump.c
CHANGED
@@ -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)
|
97
|
+
if(!obj) {
|
98
|
+
yajl_gen_null(ctx->yajl);
|
99
|
+
return;
|
100
|
+
}
|
96
101
|
if (IMMEDIATE_P(obj)) {
|
97
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
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
|
-
|
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)));
|
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
|
-
|
572
|
-
|
573
|
-
|
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(
|
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
|
614
|
-
ctx
|
615
|
-
yajl_gen_array_open(ctx
|
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
|
-
|
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
|
620
|
-
flush_yajl(
|
621
|
-
yajl_gen_free(ctx
|
622
|
-
fclose(ctx
|
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
|
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 = '
|
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"
|
data/lib/heap_dump/version.rb
CHANGED
data/lib/heap_dump.rb
CHANGED
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.
|
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-
|
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: &
|
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: *
|
24
|
+
version_requirements: *70106505219640
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: yajl-ruby
|
27
|
-
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: *
|
35
|
+
version_requirements: *70106505219140
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake-compiler
|
38
|
-
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: *
|
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.
|
77
|
+
version: 1.9.2p290
|
78
78
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
79
|
none: false
|
80
80
|
requirements:
|