heap_dump 0.0.29 → 0.0.30
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -1
- data/ext/heap_dump/extconf.rb +33 -0
- data/ext/heap_dump/heap_dump.c +133 -79
- data/ext/heap_dump/specific/ruby-2.0.0/fiber.h +67 -0
- data/ext/heap_dump/specific/ruby-2.0.0/gc_internal.h +251 -0
- data/ext/heap_dump/specific/ruby-2.0.0/internal_typed_data.h +109 -0
- data/lib/heap_dump/version.rb +1 -1
- metadata +6 -5
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Low-level ruby heap memory dump - including data and code references, useful for
|
|
4
4
|
Has no performance overhead while not active, so can be used in production environment.
|
5
5
|
|
6
6
|
Originally written across ruby 1.9.2-p290 data structures.
|
7
|
-
Does work on other 1.9.2s
|
7
|
+
Does work on other 1.9.2s, 1.9.3 and 2.0.0-preview1, but not well-tested yet(output is not proven to be as full etc.).
|
8
8
|
|
9
9
|
Currently is under development and output format may differ.
|
10
10
|
|
data/ext/heap_dump/extconf.rb
CHANGED
@@ -1,6 +1,20 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
#encoding: utf-8
|
3
3
|
|
4
|
+
# autodetect ruby headers
|
5
|
+
unless ARGV.any? {|arg| arg.include?('--with-ruby-include') }
|
6
|
+
require 'rbconfig'
|
7
|
+
bindir = RbConfig::CONFIG['bindir']
|
8
|
+
if bindir =~ %r{(^.*/\.rbenv/versions)/([^/]+)/bin$}
|
9
|
+
ruby_include = "#{$1}/#{$2}/include/ruby-1.9.1/ruby-#{$2}"
|
10
|
+
ARGV << "--with-ruby-include=#{ruby_include}"
|
11
|
+
elsif bindir =~ %r{(^.*/\.rvm/rubies)/([^/]+)/bin$}
|
12
|
+
ruby_include = "#{$1}/#{$2}/include/ruby-1.9.1/#{$2}"
|
13
|
+
ruby_include = "#{ENV['rvm_path']}/src/#{$2}" unless File.exist?(ruby_include)
|
14
|
+
ARGV << "--with-ruby-include=#{ruby_include}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
4
18
|
require 'mkmf'
|
5
19
|
require 'debugger/ruby_core_source'
|
6
20
|
|
@@ -50,13 +64,32 @@ hdrs = proc {
|
|
50
64
|
%w{
|
51
65
|
constant.h
|
52
66
|
internal.h
|
67
|
+
gc.h
|
53
68
|
}.each{|h| have_header(h)}
|
54
69
|
|
70
|
+
have_struct_member("rb_iseq_t", "filename", "vm_core.h")
|
71
|
+
have_struct_member("rb_binding_t", "filename", "vm_core.h")
|
72
|
+
have_struct_member("rb_control_frame_t", "bp", "vm_core.h")
|
73
|
+
have_struct_member("rb_thread_t", "thrown_errinfo", "vm_core.h")
|
74
|
+
have_struct_member("rb_event_hook_t", "data", "vm_core.h")
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
have_struct_member("rb_iseq_t", "location", "vm_core.h")
|
79
|
+
#have_struct_member("rb_iseq_location_t", "filename", "vm_core.h")
|
80
|
+
have_struct_member("rb_block_t", "klass", "vm_core.h")
|
81
|
+
have_struct_member("rb_block_t", "lfp", "vm_core.h")
|
82
|
+
|
55
83
|
res
|
56
84
|
}
|
57
85
|
|
58
86
|
dir_config("ruby") # allow user to pass in non-standard core include directory
|
59
87
|
|
60
88
|
if !Debugger::RubyCoreSource::create_makefile_with_core(hdrs, "heap_dump")
|
89
|
+
STDERR.print("Makefile creation failed\n")
|
90
|
+
STDERR.print("*************************************************************\n\n")
|
91
|
+
STDERR.print(" NOTE: If your headers were not found, try passing\n")
|
92
|
+
STDERR.print(" --with-ruby-include=PATH_TO_HEADERS \n\n")
|
93
|
+
STDERR.print("*************************************************************\n\n")
|
61
94
|
exit(1)
|
62
95
|
end
|
data/ext/heap_dump/heap_dump.c
CHANGED
@@ -16,6 +16,10 @@
|
|
16
16
|
// #include "atomic.h"
|
17
17
|
#include "iseq.h"
|
18
18
|
|
19
|
+
#ifdef HAVE_GC_H
|
20
|
+
#include "gc.h"
|
21
|
+
#endif
|
22
|
+
|
19
23
|
#ifdef HAVE_INTERNAL_H
|
20
24
|
#include "internal.h"
|
21
25
|
#else
|
@@ -66,9 +70,9 @@ static ID classid;
|
|
66
70
|
|
67
71
|
//shortcuts to yajl
|
68
72
|
#define YAJL ctx->yajl
|
69
|
-
#define yg_string(str,len) yajl_gen_string(YAJL, str, len)
|
70
|
-
#define yg_cstring(str) yg_string(str,
|
71
|
-
#define yg_rstring(str) yg_string(RSTRING_PTR(str),
|
73
|
+
#define yg_string(str,len) yajl_gen_string(YAJL, (const unsigned char *)(str), (unsigned int)(len))
|
74
|
+
#define yg_cstring(str) yg_string(str, strlen(str))
|
75
|
+
#define yg_rstring(str) yg_string(RSTRING_PTR(str), RSTRING_LEN(str))
|
72
76
|
#define yg_int(i) yajl_gen_integer(YAJL, i)
|
73
77
|
#define yg_double(d) (yajl_gen_double(YAJL, d)==yajl_gen_invalid_number? yg_cstring("inf|NaN") : true)
|
74
78
|
#define yg_null() yajl_gen_null(YAJL)
|
@@ -135,11 +139,6 @@ static inline const char* rb_type_str(int type){
|
|
135
139
|
}
|
136
140
|
}
|
137
141
|
|
138
|
-
static inline const char* rb_builtin_type(VALUE obj){
|
139
|
-
//NOTE: only for heap objects, on embedded use (TYPE(...))
|
140
|
-
return rb_type_str(BUILTIN_TYPE(obj));
|
141
|
-
}
|
142
|
-
|
143
142
|
#define true 1
|
144
143
|
#define false 0
|
145
144
|
|
@@ -565,9 +564,20 @@ static const char* iseq_type(VALUE type){
|
|
565
564
|
}
|
566
565
|
|
567
566
|
static void dump_iseq(const rb_iseq_t* iseq, walk_ctx_t *ctx){
|
567
|
+
int i;
|
568
|
+
#ifdef HAVE_RB_ISEQ_T_FILENAME
|
568
569
|
if(iseq->name) ygh_rstring("name", iseq->name);
|
569
570
|
if(iseq->filename) ygh_rstring("filename", iseq->filename);
|
570
571
|
ygh_int("line", FIX2LONG(iseq->line_no));
|
572
|
+
#else
|
573
|
+
#ifdef HAVE_RB_ISEQ_T_LOCATION
|
574
|
+
if(iseq->location.label) ygh_rstring("name", iseq->location.label);
|
575
|
+
if(iseq->location.path) ygh_rstring("filename", iseq->location.path);
|
576
|
+
//base_label usually(always?)==label
|
577
|
+
// if(iseq->location.base_label) ygh_rstring("base_label", iseq->location.base_label);
|
578
|
+
ygh_int("line", FIX2LONG(iseq->location.first_lineno));
|
579
|
+
#endif
|
580
|
+
#endif
|
571
581
|
|
572
582
|
//if(iseq->type != 25116) //also 28 in mark_ary
|
573
583
|
ygh_cstring("type", iseq_type(iseq->type));
|
@@ -581,12 +591,11 @@ static void dump_iseq(const rb_iseq_t* iseq, walk_ctx_t *ctx){
|
|
581
591
|
ygh_id("klass", iseq->klass);
|
582
592
|
ygh_id("cref_stack", (VALUE)iseq->cref_stack); //NODE*
|
583
593
|
|
584
|
-
ID id = iseq->defined_method_id;
|
585
594
|
yg_cstring("defined_method_id");
|
586
|
-
if(
|
587
|
-
yg_cstring(rb_id2name(
|
595
|
+
if(iseq->defined_method_id && iseq->defined_method_id != ID_ALLOCATOR){
|
596
|
+
yg_cstring(rb_id2name(iseq->defined_method_id));
|
588
597
|
} else {
|
589
|
-
yg_int(
|
598
|
+
yg_int(iseq->defined_method_id);
|
590
599
|
}
|
591
600
|
|
592
601
|
if (iseq->compile_data != 0) {
|
@@ -599,9 +608,8 @@ static void dump_iseq(const rb_iseq_t* iseq, walk_ctx_t *ctx){
|
|
599
608
|
if(iseq->local_table_size > 0){
|
600
609
|
yg_cstring("local_table");
|
601
610
|
yg_array();
|
602
|
-
int i;
|
603
611
|
for(i = 0; i < iseq->local_table_size; i++){
|
604
|
-
char* name = rb_id2name(iseq->local_table[i]);
|
612
|
+
const char* name = rb_id2name(iseq->local_table[i]);
|
605
613
|
if(name){
|
606
614
|
yg_cstring(name);
|
607
615
|
} else {
|
@@ -632,11 +640,17 @@ static void dump_block(const rb_block_t* block, walk_ctx_t *ctx){
|
|
632
640
|
|
633
641
|
ygh_id("self", block->self);
|
634
642
|
|
643
|
+
#ifdef HAVE_RB_BLOCK_T_LFP
|
635
644
|
//FIXME: these are pointers to some memory, may be dumped more clever
|
636
645
|
ygh_id("lfp", (VALUE)block->lfp);
|
637
646
|
ygh_id("dfp", (VALUE)block->dfp);
|
638
647
|
//lfp = local frame pointer? local_num elems?
|
639
648
|
// dfp = ?
|
649
|
+
#endif
|
650
|
+
#ifdef HAVE_RB_BLOCK_T_KLASS
|
651
|
+
ygh_id("class", (VALUE)block->klass);
|
652
|
+
//TODO: VALUE*ep = ?
|
653
|
+
#endif
|
640
654
|
}
|
641
655
|
|
642
656
|
|
@@ -672,7 +686,7 @@ static void dump_locations(VALUE* p, long int n, walk_ctx_t *ctx){
|
|
672
686
|
static void dump_thread(const rb_thread_t* th, walk_ctx_t *ctx);
|
673
687
|
|
674
688
|
|
675
|
-
vm_dump_each_thread_func(st_data_t key, VALUE obj, walk_ctx_t *ctx){
|
689
|
+
static int vm_dump_each_thread_func(st_data_t key, VALUE obj, walk_ctx_t *ctx){
|
676
690
|
//here stored 'self' from thread
|
677
691
|
// yg_map();
|
678
692
|
ygh_id("id", obj);
|
@@ -758,16 +772,20 @@ static void dump_data_if_known(VALUE obj, walk_ctx_t *ctx){
|
|
758
772
|
rb_binding_t *bind = RTYPEDDATA_DATA(obj);
|
759
773
|
if(!bind) return;
|
760
774
|
ygh_id("env", bind->env);
|
775
|
+
#ifdef HAVE_RB_BINDING_T_FILENAME
|
761
776
|
ygh_id("filename", bind->filename);
|
777
|
+
ygh_int("line", bind->line_no);
|
778
|
+
#else
|
779
|
+
ygh_id("filename", bind->path);
|
780
|
+
ygh_int("line", bind->first_lineno);
|
781
|
+
#endif
|
762
782
|
return;
|
763
783
|
}
|
764
784
|
|
765
785
|
if(!strcmp("VM/env", typename)){
|
766
786
|
const rb_env_t* env = RTYPEDDATA_DATA(obj);
|
767
|
-
int i = 0;
|
768
787
|
yg_cstring("env");
|
769
788
|
yajl_gen_array_open(ctx->yajl);
|
770
|
-
//for(; i < env->env_size; i++) yg_id(env->env[i]);
|
771
789
|
dump_locations(env->env, env->env_size, ctx);
|
772
790
|
yajl_gen_array_close(ctx->yajl);
|
773
791
|
|
@@ -891,7 +909,7 @@ static void dump_data_if_known(VALUE obj, walk_ctx_t *ctx){
|
|
891
909
|
// rb_gc_mark(tobj->vtm.subsecx);
|
892
910
|
// rb_gc_mark(tobj->vtm.utc_offset);
|
893
911
|
VALUE flt = rb_funcall(obj, rb_intern("to_f"), 0);
|
894
|
-
if(
|
912
|
+
if(TYPE(flt) == T_FLOAT){ ygh_double("val", NUM2DBL(flt)); }
|
895
913
|
return;
|
896
914
|
}
|
897
915
|
|
@@ -920,11 +938,13 @@ static VALUE rb_class_real_checked(VALUE cl)
|
|
920
938
|
}
|
921
939
|
|
922
940
|
static inline void walk_live_object(VALUE obj, walk_ctx_t *ctx){
|
941
|
+
//note: BUILTIN_TYPE is only for heap, for embedded use TYPE
|
942
|
+
const int bt_type = BUILTIN_TYPE(obj);
|
923
943
|
ctx->live_objects++;
|
924
944
|
yajl_gen_map_open(ctx->yajl);
|
925
945
|
|
926
946
|
ygh_int("id", NUM2LONG(rb_obj_id(obj)));
|
927
|
-
ygh_cstring("bt",
|
947
|
+
ygh_cstring("bt", rb_type_str(bt_type));
|
928
948
|
|
929
949
|
//TODO:
|
930
950
|
#ifdef GC_DEBUG
|
@@ -938,8 +958,6 @@ static inline void walk_live_object(VALUE obj, walk_ctx_t *ctx){
|
|
938
958
|
// if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj))
|
939
959
|
// return generic_ivar_get(obj, id, warn);
|
940
960
|
|
941
|
-
const int bt_type = BUILTIN_TYPE(obj);
|
942
|
-
|
943
961
|
// for generic types ivars are held separately in a table
|
944
962
|
if(bt_type != T_OBJECT && bt_type != T_CLASS && bt_type != T_MODULE && bt_type != T_ICLASS){
|
945
963
|
st_table* generic_tbl = rb_generic_ivar_table(obj);
|
@@ -1151,10 +1169,11 @@ static inline void walk_live_object(VALUE obj, walk_ctx_t *ctx){
|
|
1151
1169
|
* vend: a pointer to next to the valid heap_slot area.
|
1152
1170
|
* stride: a distance to next VALUE.
|
1153
1171
|
*/
|
1154
|
-
static int objspace_walker(void *vstart, void *vend,
|
1172
|
+
static int objspace_walker(void *vstart, void *vend, size_t stride, void* data) {
|
1173
|
+
VALUE v = (VALUE)vstart;
|
1174
|
+
walk_ctx_t *ctx = data;
|
1155
1175
|
ctx->walker_called++;
|
1156
1176
|
|
1157
|
-
VALUE v = (VALUE)vstart;
|
1158
1177
|
for (; v != (VALUE)vend; v += stride) {
|
1159
1178
|
if (RBASIC(v)->flags) { // is live object
|
1160
1179
|
walk_live_object(v, ctx);
|
@@ -1165,18 +1184,8 @@ static int objspace_walker(void *vstart, void *vend, int stride, walk_ctx_t *ctx
|
|
1165
1184
|
}
|
1166
1185
|
|
1167
1186
|
|
1168
|
-
//TODO: move to separate header
|
1169
|
-
|
1170
|
-
Bits of code taken directly from ruby gc
|
1171
|
-
Copyright (C) 1993-2007 Yukihiro Matsumoto
|
1172
|
-
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
|
1173
|
-
Copyright (C) 2000 Information-technology Promotion Agency, Japan
|
1174
|
-
*/
|
1175
|
-
// #if defined(__x86_64__) && defined(__GNUC__) && !defined(__native_client__)
|
1176
|
-
// #define SET_MACHINE_STACK_END(p) __asm__("movq\t%%rsp, %0" : "=r" (*(p)))
|
1177
|
-
// #elif defined(__i386) && defined(__GNUC__) && !defined(__native_client__)
|
1178
|
-
// #define SET_MACHINE_STACK_END(p) __asm__("movl\t%%esp, %0" : "=r" (*(p)))
|
1179
|
-
// #else
|
1187
|
+
//TODO: move to separate header?
|
1188
|
+
#ifndef SET_MACHINE_STACK_END
|
1180
1189
|
NOINLINE(static void rb_gc_set_stack_end(VALUE **stack_end_p));
|
1181
1190
|
#define SET_MACHINE_STACK_END(p) rb_gc_set_stack_end(p)
|
1182
1191
|
#define USE_CONSERVATIVE_STACK_END
|
@@ -1187,6 +1196,7 @@ rb_gc_set_stack_end(VALUE **stack_end_p)
|
|
1187
1196
|
VALUE stack_end;
|
1188
1197
|
*stack_end_p = &stack_end;
|
1189
1198
|
}
|
1199
|
+
#endif
|
1190
1200
|
|
1191
1201
|
#ifdef __ia64
|
1192
1202
|
#define SET_STACK_END (SET_MACHINE_STACK_END(&th->machine_stack_end), th->machine_register_stack_end = rb_ia64_bsp())
|
@@ -1234,7 +1244,6 @@ ruby_get_stack_grow_direction(volatile VALUE *addr)
|
|
1234
1244
|
|
1235
1245
|
#define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
|
1236
1246
|
|
1237
|
-
|
1238
1247
|
/////////////
|
1239
1248
|
|
1240
1249
|
|
@@ -1251,8 +1260,10 @@ static int
|
|
1251
1260
|
dump_backtrace(void* data, VALUE file, int line, VALUE method, int argc, VALUE* argv)
|
1252
1261
|
{
|
1253
1262
|
walk_ctx_t *ctx = data;
|
1254
|
-
yg_map();
|
1255
1263
|
const char *filename = NIL_P(file) ? "<ruby>" : RSTRING_PTR(file);
|
1264
|
+
int i;
|
1265
|
+
|
1266
|
+
yg_map();
|
1256
1267
|
|
1257
1268
|
ygh_cstring("file", filename);
|
1258
1269
|
ygh_int("line", line);
|
@@ -1268,7 +1279,6 @@ dump_backtrace(void* data, VALUE file, int line, VALUE method, int argc, VALUE*
|
|
1268
1279
|
if(argc > 0){
|
1269
1280
|
yg_cstring("argv");
|
1270
1281
|
yg_array();
|
1271
|
-
int i;
|
1272
1282
|
for(i = 0; i < argc; i++)
|
1273
1283
|
yg_id(argv[i]);
|
1274
1284
|
yg_array_end();
|
@@ -1281,11 +1291,12 @@ typedef int (rb_backtrace_iter_ext_func)(void *arg, VALUE file, int line, VALUE
|
|
1281
1291
|
|
1282
1292
|
// copied from ruby_ext_backtrace
|
1283
1293
|
static int
|
1284
|
-
vm_backtrace_each_ext(rb_thread_t *th, int lev, void (*init)(void *), rb_backtrace_iter_ext_func *iter, void *arg)
|
1294
|
+
vm_backtrace_each_ext(const rb_thread_t *th, int lev, void (*init)(void *), rb_backtrace_iter_ext_func *iter, void *arg)
|
1285
1295
|
{
|
1286
1296
|
const rb_control_frame_t *limit_cfp = th->cfp;
|
1287
1297
|
const rb_control_frame_t *cfp = (void *)(th->stack + th->stack_size);
|
1288
1298
|
VALUE file = Qnil;
|
1299
|
+
VALUE* argv;
|
1289
1300
|
int line_no = 0;
|
1290
1301
|
|
1291
1302
|
cfp -= 2;
|
@@ -1301,18 +1312,32 @@ vm_backtrace_each_ext(rb_thread_t *th, int lev, void (*init)(void *), rb_backtra
|
|
1301
1312
|
if (th->vm->progname) file = th->vm->progname;
|
1302
1313
|
|
1303
1314
|
while (cfp > limit_cfp) {
|
1315
|
+
#ifdef HAVE_RB_CONTROL_FRAME_T_BP
|
1316
|
+
VALUE* bp = cfp->bp;
|
1317
|
+
#else
|
1318
|
+
VALUE* bp = cfp->sp; //??
|
1319
|
+
#endif
|
1304
1320
|
if (cfp->iseq != 0) {
|
1305
1321
|
if (cfp->pc != 0) {
|
1306
1322
|
rb_iseq_t *iseq = cfp->iseq;
|
1307
1323
|
|
1308
1324
|
line_no = rb_vm_get_sourceline(cfp);
|
1325
|
+
#ifdef HAVE_RB_ISEQ_T_FILENAME
|
1309
1326
|
file = iseq->filename;
|
1310
|
-
|
1327
|
+
#else
|
1328
|
+
file = iseq->location.path;
|
1329
|
+
#endif
|
1311
1330
|
//arguments pushed this way: *reg_cfp->sp++ = recv; for (i = 0; i < argc; i++) *reg_cfp->sp++ = argv[i];
|
1312
1331
|
//local vars = cfp->iseq->local_size - cfp->iseq->arg_size;
|
1313
1332
|
//in memory: receiver params locals (bp(incremented))
|
1314
|
-
|
1315
|
-
if ((*iter)(arg, file, line_no,
|
1333
|
+
argv = &bp[- cfp->iseq->local_size - 1];
|
1334
|
+
if ((*iter)(arg, file, line_no,
|
1335
|
+
#ifdef HAVE_RB_ISEQ_T_LOCATION
|
1336
|
+
iseq->location.label
|
1337
|
+
#else
|
1338
|
+
iseq->name
|
1339
|
+
#endif
|
1340
|
+
, cfp->iseq->arg_size, argv)) break;
|
1316
1341
|
}
|
1317
1342
|
} else
|
1318
1343
|
if (RUBYVM_CFUNC_FRAME_P(cfp)) {
|
@@ -1321,11 +1346,11 @@ vm_backtrace_each_ext(rb_thread_t *th, int lev, void (*init)(void *), rb_backtra
|
|
1321
1346
|
if (NIL_P(file)) file = ruby_engine_name;
|
1322
1347
|
|
1323
1348
|
if (id != ID_ALLOCATOR){
|
1324
|
-
|
1349
|
+
argv = NULL;
|
1325
1350
|
// when argc==-1/-2(variable length params without/with splat) - the cfp has no info on params count :(
|
1326
1351
|
//TODO: infere from somewhere ex. find self in stack? (not guaranted btw, for example: obj.method(obj, 123, obj) - will find last param instead of self)
|
1327
1352
|
if(cfp->me->def->body.cfunc.argc >= 0){ //only fixed args
|
1328
|
-
argv = &
|
1353
|
+
argv = &bp[- cfp->me->def->body.cfunc.argc - 2]; // args+self, bp was incremented thus minus 2
|
1329
1354
|
}
|
1330
1355
|
//file+line no from previous iseq frame
|
1331
1356
|
if((*iter)(arg, file, line_no, rb_id2str(id), cfp->me->def->body.cfunc.argc, argv)) break;
|
@@ -1337,6 +1362,15 @@ vm_backtrace_each_ext(rb_thread_t *th, int lev, void (*init)(void *), rb_backtra
|
|
1337
1362
|
}
|
1338
1363
|
|
1339
1364
|
static void dump_thread(const rb_thread_t* th, walk_ctx_t *ctx){
|
1365
|
+
rb_iseq_t *iseq;
|
1366
|
+
int line_no;
|
1367
|
+
ID id;
|
1368
|
+
#ifdef HAVE_RB_EVENT_HOOK_T_DATA
|
1369
|
+
rb_event_hook_t *hook = th->event_hooks;
|
1370
|
+
#else
|
1371
|
+
struct rb_event_hook_struct *hook = th->event_hooks.hooks;
|
1372
|
+
#endif
|
1373
|
+
|
1340
1374
|
if(th->stack){
|
1341
1375
|
VALUE *p = th->stack;
|
1342
1376
|
VALUE *sp = th->cfp->sp;
|
@@ -1357,13 +1391,18 @@ static void dump_thread(const rb_thread_t* th, walk_ctx_t *ctx){
|
|
1357
1391
|
//TODO: this is kind of backtrace, but other direction plus some other info, merge it in backtrace.
|
1358
1392
|
while (cfp != limit_cfp) {
|
1359
1393
|
yajl_gen_map_open(ctx->yajl);
|
1360
|
-
|
1394
|
+
iseq = cfp->iseq;
|
1361
1395
|
ygh_id("proc", cfp->proc);
|
1362
1396
|
ygh_id("self", cfp->self);
|
1363
1397
|
if (iseq) {
|
1364
1398
|
ygh_id("iseq", RUBY_VM_NORMAL_ISEQ_P(iseq) ? iseq->self : (VALUE)iseq);
|
1365
|
-
|
1399
|
+
line_no = rb_vm_get_sourceline(cfp);
|
1400
|
+
//TODO: dry?
|
1401
|
+
#ifdef HAVE_RB_ISEQ_T_FILENAME
|
1366
1402
|
ygh_rstring("file", iseq->filename);
|
1403
|
+
#else
|
1404
|
+
ygh_rstring("file", iseq->location.path);
|
1405
|
+
#endif
|
1367
1406
|
ygh_int("line_no",line_no);
|
1368
1407
|
}
|
1369
1408
|
if (cfp->me){
|
@@ -1376,7 +1415,7 @@ static void dump_thread(const rb_thread_t* th, walk_ctx_t *ctx){
|
|
1376
1415
|
// char mark;
|
1377
1416
|
//rb_method_definition_t *def;
|
1378
1417
|
ygh_id("klass", me->klass);
|
1379
|
-
|
1418
|
+
id = me->called_id;
|
1380
1419
|
|
1381
1420
|
if(me->def){
|
1382
1421
|
id = me->def->original_id;
|
@@ -1405,8 +1444,12 @@ static void dump_thread(const rb_thread_t* th, walk_ctx_t *ctx){
|
|
1405
1444
|
ygh_id("thgroup", th->thgroup);
|
1406
1445
|
ygh_id("value", th->value);
|
1407
1446
|
ygh_id("errinfo", th->errinfo);
|
1447
|
+
|
1448
|
+
#ifdef HAVE_RB_THREAD_T_THROWN_ERRINFO
|
1408
1449
|
ygh_id("thrown_errinfo", th->thrown_errinfo);
|
1409
1450
|
ygh_id("local_svar", th->local_svar);
|
1451
|
+
#endif
|
1452
|
+
|
1410
1453
|
ygh_id("top_self", th->top_self);
|
1411
1454
|
ygh_id("top_wrapper", th->top_wrapper);
|
1412
1455
|
ygh_id("fiber", th->fiber);
|
@@ -1438,8 +1481,6 @@ static void dump_thread(const rb_thread_t* th, walk_ctx_t *ctx){
|
|
1438
1481
|
}
|
1439
1482
|
yajl_gen_map_close(ctx->yajl);
|
1440
1483
|
|
1441
|
-
// mark_event_hooks(th->event_hooks);
|
1442
|
-
rb_event_hook_t *hook = th->event_hooks;
|
1443
1484
|
yg_cstring("event_hooks");
|
1444
1485
|
yajl_gen_array_open(ctx->yajl);
|
1445
1486
|
while(hook){
|
@@ -1451,6 +1492,9 @@ static void dump_thread(const rb_thread_t* th, walk_ctx_t *ctx){
|
|
1451
1492
|
|
1452
1493
|
|
1453
1494
|
static void dump_machine_context(walk_ctx_t *ctx){
|
1495
|
+
VALUE* x;
|
1496
|
+
unsigned long n;
|
1497
|
+
|
1454
1498
|
//TODO: other threads?
|
1455
1499
|
rb_thread_t* th = GET_THREAD()->vm->main_thread; //GET_THREAD();
|
1456
1500
|
union {
|
@@ -1470,8 +1514,8 @@ static void dump_machine_context(walk_ctx_t *ctx){
|
|
1470
1514
|
yg_cstring("registers");
|
1471
1515
|
yajl_gen_array_open(ctx->yajl);
|
1472
1516
|
//mark_locations_array(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v));
|
1473
|
-
|
1474
|
-
|
1517
|
+
x = save_regs_gc_mark.v;
|
1518
|
+
n = numberof(save_regs_gc_mark.v);
|
1475
1519
|
while (n--) {
|
1476
1520
|
VALUE v = *(x++);
|
1477
1521
|
if(is_in_heap((void*)v, NULL))
|
@@ -1501,22 +1545,21 @@ static void dump_machine_context(walk_ctx_t *ctx){
|
|
1501
1545
|
// 1.9.2, rb_class_tbl fails to be linked in 1.9.3 :(
|
1502
1546
|
|
1503
1547
|
static int dump_class_tbl_entry(ID key, rb_const_entry_t* ce/*st_data_t val*/, walk_ctx_t *ctx){
|
1548
|
+
const char* id;
|
1504
1549
|
if (!rb_is_const_id(key)) return ST_CONTINUE; //?
|
1505
|
-
VALUE value = ce->value;
|
1506
1550
|
|
1507
|
-
|
1508
|
-
if(id)
|
1551
|
+
if((id = rb_id2name(key)))
|
1509
1552
|
yg_cstring(id);
|
1510
1553
|
else
|
1511
1554
|
yg_cstring("(unknown)");
|
1512
|
-
yg_id(value);
|
1555
|
+
yg_id(ce->value);
|
1513
1556
|
return ST_CONTINUE;
|
1514
1557
|
}
|
1515
1558
|
#endif
|
1516
1559
|
|
1517
1560
|
#ifdef HAVE_RB_GLOBAL_TBL
|
1518
1561
|
static int dump_global_tbl_entry(ID key, struct rb_global_entry* ge/*st_data_t val*/, walk_ctx_t *ctx){
|
1519
|
-
char* id = rb_id2name(key);
|
1562
|
+
const char* id = rb_id2name(key);
|
1520
1563
|
if(id)
|
1521
1564
|
yg_cstring(id);
|
1522
1565
|
else
|
@@ -1530,8 +1573,8 @@ static int dump_global_tbl_entry(ID key, struct rb_global_entry* ge/*st_data_t v
|
|
1530
1573
|
yg_cstring(info.dli_sname);
|
1531
1574
|
|
1532
1575
|
if(!strcmp("rb_gvar_val_getter", info.dli_sname)){
|
1533
|
-
|
1534
|
-
|
1576
|
+
yg_cstring("data");
|
1577
|
+
yg_id((VALUE)ge->var->data);
|
1535
1578
|
}
|
1536
1579
|
}
|
1537
1580
|
|
@@ -1547,11 +1590,13 @@ static int dump_global_tbl_entry(ID key, struct rb_global_entry* ge/*st_data_t v
|
|
1547
1590
|
#include <stdarg.h>
|
1548
1591
|
static bool g_verbose = false;
|
1549
1592
|
static int log_printf(const char* format, ...){
|
1593
|
+
int res = 0;
|
1550
1594
|
va_list list;
|
1551
1595
|
va_start(list, format);
|
1552
1596
|
if(g_verbose)
|
1553
|
-
vprintf(format, list);
|
1597
|
+
res = vprintf(format, list);
|
1554
1598
|
va_end(list);
|
1599
|
+
return res;
|
1555
1600
|
}
|
1556
1601
|
|
1557
1602
|
#define log log_printf
|
@@ -1569,6 +1614,14 @@ static VALUE heapdump_verbose_setter(VALUE self, VALUE verbose){
|
|
1569
1614
|
//public symbol, can be used from GDB
|
1570
1615
|
void heapdump_dump(const char* filename){
|
1571
1616
|
struct walk_ctx ctx_o, *ctx = &ctx_o;
|
1617
|
+
struct gc_list *list;
|
1618
|
+
#ifdef HAVE_RB_CLASS_TBL
|
1619
|
+
st_table *rb_class_tbl;
|
1620
|
+
#endif
|
1621
|
+
#ifdef HAVE_RB_GLOBAL_TBL
|
1622
|
+
st_table *rb_global_tbl;
|
1623
|
+
#endif
|
1624
|
+
|
1572
1625
|
memset(ctx, 0, sizeof(*ctx));
|
1573
1626
|
|
1574
1627
|
if(!filename){
|
@@ -1589,7 +1642,6 @@ void heapdump_dump(const char* filename){
|
|
1589
1642
|
flush_yajl(ctx);
|
1590
1643
|
// fprintf(ctx->file, "\n");
|
1591
1644
|
|
1592
|
-
struct gc_list *list;
|
1593
1645
|
/* mark protected global variables */
|
1594
1646
|
log("global_list\n");
|
1595
1647
|
yg_cstring("globals");
|
@@ -1602,7 +1654,7 @@ void heapdump_dump(const char* filename){
|
|
1602
1654
|
|
1603
1655
|
//TODO: rb_global_tbl
|
1604
1656
|
#ifdef HAVE_RB_GLOBAL_TBL
|
1605
|
-
|
1657
|
+
rb_global_tbl = rb_get_global_tbl();
|
1606
1658
|
if (rb_global_tbl && rb_global_tbl->num_entries > 0){
|
1607
1659
|
log("globals\n");
|
1608
1660
|
yg_cstring("global_tbl");
|
@@ -1614,7 +1666,7 @@ void heapdump_dump(const char* filename){
|
|
1614
1666
|
#endif
|
1615
1667
|
|
1616
1668
|
#ifdef HAVE_RB_CLASS_TBL
|
1617
|
-
|
1669
|
+
rb_class_tbl = rb_get_class_tbl();
|
1618
1670
|
if (rb_class_tbl && rb_class_tbl->num_entries > 0){
|
1619
1671
|
log("classes\n");
|
1620
1672
|
yg_cstring("classes");
|
@@ -1666,13 +1718,25 @@ iterate_user_type_counts(VALUE key, VALUE value, yajl_gen yajl){
|
|
1666
1718
|
|
1667
1719
|
static VALUE
|
1668
1720
|
rb_heapdump_count_objects(VALUE self, VALUE string_prefixes, VALUE do_gc){
|
1669
|
-
rb_check_array_type(string_prefixes);
|
1670
1721
|
yajl_gen_config cfg;
|
1722
|
+
yajl_gen yajl;
|
1723
|
+
VALUE cls, class_name, prefix;
|
1724
|
+
size_t counts[T_MASK+1];
|
1725
|
+
size_t freed = 0;
|
1726
|
+
size_t total = 0;
|
1727
|
+
size_t i;
|
1728
|
+
long int n;
|
1729
|
+
const unsigned char* buf;
|
1730
|
+
unsigned int len;
|
1731
|
+
VALUE hash = rb_hash_new();
|
1732
|
+
rb_objspace_t *objspace = GET_THREAD()->vm->objspace;
|
1733
|
+
|
1734
|
+
rb_check_array_type(string_prefixes);
|
1671
1735
|
memset(&cfg, 0, sizeof(cfg));
|
1672
1736
|
cfg.beautify = true;
|
1673
1737
|
cfg.htmlSafe = true;
|
1674
1738
|
cfg.indentString = " ";
|
1675
|
-
|
1739
|
+
yajl = yajl_gen_alloc(&cfg,NULL);
|
1676
1740
|
yg_map();
|
1677
1741
|
if(do_gc){
|
1678
1742
|
yg_cstring("gc_ran");
|
@@ -1680,13 +1744,6 @@ rb_heapdump_count_objects(VALUE self, VALUE string_prefixes, VALUE do_gc){
|
|
1680
1744
|
rb_gc_start();
|
1681
1745
|
}
|
1682
1746
|
|
1683
|
-
rb_objspace_t *objspace = GET_THREAD()->vm->objspace;
|
1684
|
-
size_t counts[T_MASK+1];
|
1685
|
-
size_t freed = 0;
|
1686
|
-
size_t total = 0;
|
1687
|
-
size_t i;
|
1688
|
-
VALUE hash = rb_hash_new();
|
1689
|
-
|
1690
1747
|
for (i = 0; i <= T_MASK; i++) counts[i] = 0;
|
1691
1748
|
|
1692
1749
|
FOR_EACH_HEAP_SLOT(p)
|
@@ -1696,12 +1753,11 @@ rb_heapdump_count_objects(VALUE self, VALUE string_prefixes, VALUE do_gc){
|
|
1696
1753
|
counts[type]++;
|
1697
1754
|
if(type == T_OBJECT){
|
1698
1755
|
//take class etc.
|
1699
|
-
|
1756
|
+
cls = rb_class_real_checked(CLASS_OF(p));
|
1700
1757
|
if(!cls) continue;
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
VALUE prefix = rb_check_string_type(RARRAY_PTR(string_prefixes)[n]);
|
1758
|
+
class_name = rb_class_path(cls);
|
1759
|
+
for(n = RARRAY_LEN(string_prefixes)-1; n >= 0; n--){
|
1760
|
+
prefix = rb_check_string_type(RARRAY_PTR(string_prefixes)[n]);
|
1705
1761
|
if(NIL_P(prefix)) continue;
|
1706
1762
|
rb_enc_check(class_name, prefix);
|
1707
1763
|
if (RSTRING_LEN(class_name) < RSTRING_LEN(prefix)) continue;
|
@@ -1744,11 +1800,9 @@ rb_heapdump_count_objects(VALUE self, VALUE string_prefixes, VALUE do_gc){
|
|
1744
1800
|
yg_map_end(); //all document
|
1745
1801
|
|
1746
1802
|
//flush yajl:
|
1747
|
-
const unsigned char* buf;
|
1748
|
-
unsigned int len;
|
1749
1803
|
if(yajl_gen_get_buf(yajl, &buf, &len) == yajl_gen_status_ok){
|
1750
1804
|
//fwrite(buf, len, 1, ctx->file);
|
1751
|
-
VALUE res = rb_str_new(buf, len);
|
1805
|
+
VALUE res = rb_str_new((char*)buf, len);
|
1752
1806
|
yajl_gen_clear(yajl);
|
1753
1807
|
yajl_gen_free(yajl);
|
1754
1808
|
return res;
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#define CAPTURE_JUST_VALID_VM_STACK 1
|
2
|
+
|
3
|
+
enum context_type {
|
4
|
+
CONTINUATION_CONTEXT = 0,
|
5
|
+
FIBER_CONTEXT = 1,
|
6
|
+
ROOT_FIBER_CONTEXT = 2
|
7
|
+
};
|
8
|
+
|
9
|
+
typedef struct rb_context_struct {
|
10
|
+
enum context_type type;
|
11
|
+
VALUE self;
|
12
|
+
int argc;
|
13
|
+
VALUE value;
|
14
|
+
VALUE *vm_stack;
|
15
|
+
#ifdef CAPTURE_JUST_VALID_VM_STACK
|
16
|
+
size_t vm_stack_slen; /* length of stack (head of th->stack) */
|
17
|
+
size_t vm_stack_clen; /* length of control frames (tail of th->stack) */
|
18
|
+
#endif
|
19
|
+
VALUE *machine_stack;
|
20
|
+
VALUE *machine_stack_src;
|
21
|
+
#ifdef __ia64
|
22
|
+
VALUE *machine_register_stack;
|
23
|
+
VALUE *machine_register_stack_src;
|
24
|
+
int machine_register_stack_size;
|
25
|
+
#endif
|
26
|
+
rb_thread_t saved_thread;
|
27
|
+
rb_jmpbuf_t jmpbuf;
|
28
|
+
size_t machine_stack_size;
|
29
|
+
} rb_context_t;
|
30
|
+
|
31
|
+
enum fiber_status {
|
32
|
+
CREATED,
|
33
|
+
RUNNING,
|
34
|
+
TERMINATED
|
35
|
+
};
|
36
|
+
|
37
|
+
#if FIBER_USE_NATIVE && !defined(_WIN32)
|
38
|
+
#define MAX_MAHINE_STACK_CACHE 10
|
39
|
+
static int machine_stack_cache_index = 0;
|
40
|
+
typedef struct machine_stack_cache_struct {
|
41
|
+
void *ptr;
|
42
|
+
size_t size;
|
43
|
+
} machine_stack_cache_t;
|
44
|
+
static machine_stack_cache_t machine_stack_cache[MAX_MAHINE_STACK_CACHE];
|
45
|
+
static machine_stack_cache_t terminated_machine_stack;
|
46
|
+
#endif
|
47
|
+
|
48
|
+
typedef struct rb_fiber_struct {
|
49
|
+
rb_context_t cont;
|
50
|
+
VALUE prev;
|
51
|
+
enum fiber_status status;
|
52
|
+
struct rb_fiber_struct *prev_fiber;
|
53
|
+
struct rb_fiber_struct *next_fiber;
|
54
|
+
/* If a fiber invokes "transfer",
|
55
|
+
* then this fiber can't "resume" any more after that.
|
56
|
+
* You shouldn't mix "transfer" and "resume".
|
57
|
+
*/
|
58
|
+
int transfered;
|
59
|
+
|
60
|
+
#if FIBER_USE_NATIVE
|
61
|
+
#ifdef _WIN32
|
62
|
+
void *fib_handle;
|
63
|
+
#else
|
64
|
+
ucontext_t context;
|
65
|
+
#endif
|
66
|
+
#endif
|
67
|
+
} rb_fiber_t;
|
@@ -0,0 +1,251 @@
|
|
1
|
+
#include "ruby/re.h"
|
2
|
+
|
3
|
+
#ifndef GC_PROFILE_MORE_DETAIL
|
4
|
+
#define GC_PROFILE_MORE_DETAIL 0
|
5
|
+
#endif
|
6
|
+
|
7
|
+
typedef struct gc_profile_record {
|
8
|
+
double gc_time;
|
9
|
+
double gc_invoke_time;
|
10
|
+
|
11
|
+
size_t heap_total_objects;
|
12
|
+
size_t heap_use_size;
|
13
|
+
size_t heap_total_size;
|
14
|
+
|
15
|
+
int is_marked;
|
16
|
+
|
17
|
+
#if GC_PROFILE_MORE_DETAIL
|
18
|
+
double gc_mark_time;
|
19
|
+
double gc_sweep_time;
|
20
|
+
|
21
|
+
size_t heap_use_slots;
|
22
|
+
size_t heap_live_objects;
|
23
|
+
size_t heap_free_objects;
|
24
|
+
|
25
|
+
int have_finalize;
|
26
|
+
|
27
|
+
size_t allocate_increase;
|
28
|
+
size_t allocate_limit;
|
29
|
+
#endif
|
30
|
+
} gc_profile_record;
|
31
|
+
|
32
|
+
|
33
|
+
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
|
34
|
+
#pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
|
35
|
+
#endif
|
36
|
+
|
37
|
+
typedef struct RVALUE {
|
38
|
+
union {
|
39
|
+
struct {
|
40
|
+
VALUE flags; /* always 0 for freed obj */
|
41
|
+
struct RVALUE *next;
|
42
|
+
} free;
|
43
|
+
struct RBasic basic;
|
44
|
+
struct RObject object;
|
45
|
+
struct RClass klass;
|
46
|
+
struct RFloat flonum;
|
47
|
+
struct RString string;
|
48
|
+
struct RArray array;
|
49
|
+
struct RRegexp regexp;
|
50
|
+
struct RHash hash;
|
51
|
+
struct RData data;
|
52
|
+
struct RTypedData typeddata;
|
53
|
+
struct RStruct rstruct;
|
54
|
+
struct RBignum bignum;
|
55
|
+
struct RFile file;
|
56
|
+
struct RNode node;
|
57
|
+
struct RMatch match;
|
58
|
+
struct RRational rational;
|
59
|
+
struct RComplex complex;
|
60
|
+
} as;
|
61
|
+
#ifdef GC_DEBUG
|
62
|
+
const char *file;
|
63
|
+
int line;
|
64
|
+
#endif
|
65
|
+
} RVALUE;
|
66
|
+
|
67
|
+
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
|
68
|
+
#pragma pack(pop)
|
69
|
+
#endif
|
70
|
+
|
71
|
+
struct heaps_slot {
|
72
|
+
void *membase;
|
73
|
+
RVALUE *slot;
|
74
|
+
size_t limit;
|
75
|
+
uintptr_t *bits;
|
76
|
+
RVALUE *freelist;
|
77
|
+
struct heaps_slot *next;
|
78
|
+
struct heaps_slot *prev;
|
79
|
+
struct heaps_slot *free_next;
|
80
|
+
};
|
81
|
+
|
82
|
+
struct heaps_header {
|
83
|
+
struct heaps_slot *base;
|
84
|
+
uintptr_t *bits;
|
85
|
+
};
|
86
|
+
|
87
|
+
struct sorted_heaps_slot {
|
88
|
+
RVALUE *start;
|
89
|
+
RVALUE *end;
|
90
|
+
struct heaps_slot *slot;
|
91
|
+
};
|
92
|
+
|
93
|
+
struct heaps_free_bitmap {
|
94
|
+
struct heaps_free_bitmap *next;
|
95
|
+
};
|
96
|
+
|
97
|
+
struct gc_list {
|
98
|
+
VALUE *varptr;
|
99
|
+
struct gc_list *next;
|
100
|
+
};
|
101
|
+
|
102
|
+
#define STACK_CHUNK_SIZE 500
|
103
|
+
|
104
|
+
typedef struct stack_chunk {
|
105
|
+
VALUE data[STACK_CHUNK_SIZE];
|
106
|
+
struct stack_chunk *next;
|
107
|
+
} stack_chunk_t;
|
108
|
+
|
109
|
+
typedef struct mark_stack {
|
110
|
+
stack_chunk_t *chunk;
|
111
|
+
stack_chunk_t *cache;
|
112
|
+
size_t index;
|
113
|
+
size_t limit;
|
114
|
+
size_t cache_size;
|
115
|
+
size_t unused_cache_size;
|
116
|
+
} mark_stack_t;
|
117
|
+
|
118
|
+
#ifndef CALC_EXACT_MALLOC_SIZE
|
119
|
+
#define CALC_EXACT_MALLOC_SIZE 0
|
120
|
+
#endif
|
121
|
+
|
122
|
+
typedef struct rb_objspace {
|
123
|
+
struct {
|
124
|
+
size_t limit;
|
125
|
+
size_t increase;
|
126
|
+
#if CALC_EXACT_MALLOC_SIZE
|
127
|
+
size_t allocated_size;
|
128
|
+
size_t allocations;
|
129
|
+
#endif
|
130
|
+
} malloc_params;
|
131
|
+
struct {
|
132
|
+
size_t increment;
|
133
|
+
struct heaps_slot *ptr;
|
134
|
+
struct heaps_slot *sweep_slots;
|
135
|
+
struct heaps_slot *free_slots;
|
136
|
+
struct sorted_heaps_slot *sorted;
|
137
|
+
size_t length;
|
138
|
+
size_t used;
|
139
|
+
struct heaps_free_bitmap *free_bitmap;
|
140
|
+
RVALUE *range[2];
|
141
|
+
RVALUE *freed;
|
142
|
+
size_t live_num;
|
143
|
+
size_t free_num;
|
144
|
+
size_t free_min;
|
145
|
+
size_t final_num;
|
146
|
+
size_t do_heap_free;
|
147
|
+
} heap;
|
148
|
+
struct {
|
149
|
+
int dont_gc;
|
150
|
+
int dont_lazy_sweep;
|
151
|
+
int during_gc;
|
152
|
+
rb_atomic_t finalizing;
|
153
|
+
} flags;
|
154
|
+
struct {
|
155
|
+
st_table *table;
|
156
|
+
RVALUE *deferred;
|
157
|
+
} final;
|
158
|
+
mark_stack_t mark_stack;
|
159
|
+
struct {
|
160
|
+
int run;
|
161
|
+
gc_profile_record *record;
|
162
|
+
size_t count;
|
163
|
+
size_t size;
|
164
|
+
double invoke_time;
|
165
|
+
} profile;
|
166
|
+
struct gc_list *global_list;
|
167
|
+
size_t count;
|
168
|
+
int gc_stress;
|
169
|
+
|
170
|
+
struct mark_func_data_struct {
|
171
|
+
void *data;
|
172
|
+
void (*mark_func)(VALUE v, void *data);
|
173
|
+
} *mark_func_data;
|
174
|
+
} rb_objspace_t;
|
175
|
+
|
176
|
+
#define malloc_limit objspace->malloc_params.limit
|
177
|
+
#define malloc_increase objspace->malloc_params.increase
|
178
|
+
#define heaps objspace->heap.ptr
|
179
|
+
#define heaps_length objspace->heap.length
|
180
|
+
#define heaps_used objspace->heap.used
|
181
|
+
#define lomem objspace->heap.range[0]
|
182
|
+
#define himem objspace->heap.range[1]
|
183
|
+
#define heaps_inc objspace->heap.increment
|
184
|
+
#define heaps_freed objspace->heap.freed
|
185
|
+
#define dont_gc objspace->flags.dont_gc
|
186
|
+
#define during_gc objspace->flags.during_gc
|
187
|
+
#define finalizing objspace->flags.finalizing
|
188
|
+
#define finalizer_table objspace->final.table
|
189
|
+
#define deferred_final_list objspace->final.deferred
|
190
|
+
#define global_List objspace->global_list
|
191
|
+
#define ruby_gc_stress objspace->gc_stress
|
192
|
+
#define initial_malloc_limit initial_params.initial_malloc_limit
|
193
|
+
#define initial_heap_min_slots initial_params.initial_heap_min_slots
|
194
|
+
#define initial_free_min initial_params.initial_free_min
|
195
|
+
|
196
|
+
#define is_lazy_sweeping(objspace) ((objspace)->heap.sweep_slots != 0)
|
197
|
+
|
198
|
+
#define nonspecial_obj_id(obj) (VALUE)((SIGNED_VALUE)(obj)|FIXNUM_FLAG)
|
199
|
+
|
200
|
+
#define RANY(o) ((RVALUE*)(o))
|
201
|
+
#define has_free_object (objspace->heap.free_slots && objspace->heap.free_slots->freelist)
|
202
|
+
|
203
|
+
#define HEAP_HEADER(p) ((struct heaps_header *)(p))
|
204
|
+
#define GET_HEAP_HEADER(x) (HEAP_HEADER((uintptr_t)(x) & ~(HEAP_ALIGN_MASK)))
|
205
|
+
#define GET_HEAP_SLOT(x) (GET_HEAP_HEADER(x)->base)
|
206
|
+
#define GET_HEAP_BITMAP(x) (GET_HEAP_HEADER(x)->bits)
|
207
|
+
#define NUM_IN_SLOT(p) (((uintptr_t)(p) & HEAP_ALIGN_MASK)/sizeof(RVALUE))
|
208
|
+
#define BITMAP_INDEX(p) (NUM_IN_SLOT(p) / (sizeof(uintptr_t) * CHAR_BIT))
|
209
|
+
#define BITMAP_OFFSET(p) (NUM_IN_SLOT(p) & ((sizeof(uintptr_t) * CHAR_BIT)-1))
|
210
|
+
#define MARKED_IN_BITMAP(bits, p) (bits[BITMAP_INDEX(p)] & ((uintptr_t)1 << BITMAP_OFFSET(p)))
|
211
|
+
|
212
|
+
//
|
213
|
+
#define RANY(o) ((RVALUE*)(o))
|
214
|
+
//
|
215
|
+
|
216
|
+
static inline int
|
217
|
+
is_pointer_to_heap(rb_objspace_t *objspace, void *ptr)
|
218
|
+
{
|
219
|
+
register RVALUE *p = RANY(ptr);
|
220
|
+
register struct sorted_heaps_slot *heap;
|
221
|
+
register size_t hi, lo, mid;
|
222
|
+
|
223
|
+
if (p < lomem || p > himem) return FALSE;
|
224
|
+
if ((VALUE)p % sizeof(RVALUE) != 0) return FALSE;
|
225
|
+
|
226
|
+
/* check if p looks like a pointer using bsearch*/
|
227
|
+
lo = 0;
|
228
|
+
hi = heaps_used;
|
229
|
+
while (lo < hi) {
|
230
|
+
mid = (lo + hi) / 2;
|
231
|
+
heap = &objspace->heap.sorted[mid];
|
232
|
+
if (heap->start <= p) {
|
233
|
+
if (p < heap->end)
|
234
|
+
return TRUE;
|
235
|
+
lo = mid + 1;
|
236
|
+
}
|
237
|
+
else {
|
238
|
+
hi = mid;
|
239
|
+
}
|
240
|
+
}
|
241
|
+
return FALSE;
|
242
|
+
}
|
243
|
+
|
244
|
+
//from count_objects:
|
245
|
+
#define FOR_EACH_HEAP_SLOT(p) for (i = 0; i < heaps_used; i++) {\
|
246
|
+
RVALUE *p = objspace->heap.sorted[i].start, *pend = objspace->heap.sorted[i].end;\
|
247
|
+
if(!p) continue;\
|
248
|
+
for (; p < pend; p++) {
|
249
|
+
#define FOR_EACH_HEAP_SLOT_END(total) } total += objspace->heap.sorted[i].slot->limit; }
|
250
|
+
|
251
|
+
#define NODE_OPTBLOCK 1000000 //FIXME
|
@@ -0,0 +1,109 @@
|
|
1
|
+
//FIXME: autogen this from ruby (this copied from 1.9.3p194)
|
2
|
+
|
3
|
+
// thread.c:
|
4
|
+
struct thgroup {
|
5
|
+
int enclosed;
|
6
|
+
VALUE group;
|
7
|
+
};
|
8
|
+
|
9
|
+
// enumerator.c:
|
10
|
+
struct enumerator {
|
11
|
+
VALUE obj;
|
12
|
+
ID meth;
|
13
|
+
VALUE args;
|
14
|
+
VALUE fib;
|
15
|
+
VALUE dst;
|
16
|
+
VALUE lookahead;
|
17
|
+
VALUE feedvalue;
|
18
|
+
VALUE stop_exc;
|
19
|
+
};
|
20
|
+
|
21
|
+
//
|
22
|
+
struct generator {
|
23
|
+
VALUE proc;
|
24
|
+
};
|
25
|
+
|
26
|
+
struct yielder {
|
27
|
+
VALUE proc;
|
28
|
+
};
|
29
|
+
|
30
|
+
|
31
|
+
// proc.c:
|
32
|
+
struct METHOD {
|
33
|
+
VALUE recv;
|
34
|
+
VALUE rclass;
|
35
|
+
VALUE defined_class; //FIXME: dump this
|
36
|
+
ID id;
|
37
|
+
rb_method_entry_t *me;
|
38
|
+
struct unlinked_method_entry_list_entry *ume;
|
39
|
+
};
|
40
|
+
|
41
|
+
//
|
42
|
+
#define METHOD_DEFINITIONP(m) (m->me ? m->me->def : NULL)
|
43
|
+
|
44
|
+
//class.c:
|
45
|
+
#define HAVE_RB_CLASS_TBL 1
|
46
|
+
//For som reason this fails to link directly on 1.9.3 :(
|
47
|
+
|
48
|
+
//HACK:
|
49
|
+
#include <dlfcn.h>
|
50
|
+
|
51
|
+
inline st_table * rb_get_class_tbl(){
|
52
|
+
Dl_info info;
|
53
|
+
void* image;
|
54
|
+
if(!dladdr(rb_intern, &info) || !info.dli_fname){
|
55
|
+
return NULL;
|
56
|
+
}
|
57
|
+
image = dlopen(info.dli_fname, RTLD_NOLOAD | RTLD_GLOBAL);
|
58
|
+
// printf("Image is %p, addr is %p (%p rel)\n", image, rb_intern, ((void*)rb_intern - image));
|
59
|
+
if(image)
|
60
|
+
{
|
61
|
+
void* tbl = dlsym(image, "_rb_class_tbl");
|
62
|
+
dlclose(image);
|
63
|
+
if(tbl)
|
64
|
+
return tbl;
|
65
|
+
}
|
66
|
+
|
67
|
+
//TODO: parse sym table and calculate address?
|
68
|
+
|
69
|
+
return NULL;
|
70
|
+
}
|
71
|
+
|
72
|
+
#define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
|
73
|
+
#define GET_THREAD() ruby_current_thread
|
74
|
+
|
75
|
+
//FIXME: get global const for it: rb_define_global_const("RUBY_ENGINE", ruby_engine_name = MKSTR(engine));
|
76
|
+
#define ruby_engine_name Qnil
|
77
|
+
|
78
|
+
#define ID_ALLOCATOR 0
|
79
|
+
|
80
|
+
//vm_trace.c
|
81
|
+
typedef enum {
|
82
|
+
RUBY_HOOK_FLAG_SAFE = 0x01,
|
83
|
+
RUBY_HOOK_FLAG_DELETED = 0x02,
|
84
|
+
RUBY_HOOK_FLAG_RAW_ARG = 0x04
|
85
|
+
} rb_hook_flag_t;
|
86
|
+
typedef struct rb_event_hook_struct {
|
87
|
+
rb_hook_flag_t hook_flags;
|
88
|
+
rb_event_flag_t events;
|
89
|
+
rb_event_hook_func_t func;
|
90
|
+
VALUE data;
|
91
|
+
struct rb_event_hook_struct *next;
|
92
|
+
} rb_event_hook_t;
|
93
|
+
|
94
|
+
//vm_backtrace.c
|
95
|
+
inline static int
|
96
|
+
calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
97
|
+
{
|
98
|
+
return rb_iseq_line_no(iseq, pc - iseq->iseq_encoded);
|
99
|
+
}
|
100
|
+
|
101
|
+
int rb_vm_get_sourceline(const rb_control_frame_t * cfp){
|
102
|
+
int lineno = 0;
|
103
|
+
const rb_iseq_t *iseq = cfp->iseq;
|
104
|
+
|
105
|
+
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
|
106
|
+
lineno = calc_lineno(cfp->iseq, cfp->pc);
|
107
|
+
}
|
108
|
+
return lineno;
|
109
|
+
}
|
data/lib/heap_dump/version.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.30
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: debugger-ruby_core_source
|
@@ -82,6 +82,9 @@ files:
|
|
82
82
|
- ext/heap_dump/specific/ruby-1.9.3/fiber.h
|
83
83
|
- ext/heap_dump/specific/ruby-1.9.3/gc_internal.h
|
84
84
|
- ext/heap_dump/specific/ruby-1.9.3/internal_typed_data.h
|
85
|
+
- ext/heap_dump/specific/ruby-2.0.0/fiber.h
|
86
|
+
- ext/heap_dump/specific/ruby-2.0.0/gc_internal.h
|
87
|
+
- ext/heap_dump/specific/ruby-2.0.0/internal_typed_data.h
|
85
88
|
- heap_dump.gemspec
|
86
89
|
- lib/heap_dump.rb
|
87
90
|
- lib/heap_dump/version.rb
|
@@ -103,9 +106,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
106
|
- - ! '>='
|
104
107
|
- !ruby/object:Gem::Version
|
105
108
|
version: '0'
|
106
|
-
segments:
|
107
|
-
- 0
|
108
|
-
hash: -3423213544880593547
|
109
109
|
requirements: []
|
110
110
|
rubyforge_project:
|
111
111
|
rubygems_version: 1.8.24
|
@@ -114,3 +114,4 @@ specification_version: 3
|
|
114
114
|
summary: Allows to dump heap to track reference leaks, including leaks in proc contexts
|
115
115
|
and fibers
|
116
116
|
test_files: []
|
117
|
+
has_rdoc:
|