heap_dump 0.0.29 → 0.0.30
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/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:
|