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 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 and 1.9.3, but not well-tested yet(output is not proven to be as full etc.).
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
 
@@ -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
@@ -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, (unsigned int)strlen(str))
71
- #define yg_rstring(str) yg_string(RSTRING_PTR(str), (unsigned int)RSTRING_LEN(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(id && id != ID_ALLOCATOR){
587
- yg_cstring(rb_id2name(id)); // symbol=ID2SYM(id);
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(id);
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(BUILTIN_TYPE(flt) == T_FLOAT){ ygh_double("val", RFLOAT_VALUE(flt)); }
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", rb_builtin_type(obj));
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, int stride, walk_ctx_t *ctx) {
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
- VALUE* argv = &cfp->bp[- cfp->iseq->local_size - 1];
1315
- if ((*iter)(arg, file, line_no, iseq->name, cfp->iseq->arg_size, argv)) break;
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
- VALUE* argv = NULL;
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 = &cfp->bp[- cfp->me->def->body.cfunc.argc - 2]; // args+self, bp was incremented thus minus 2
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
- rb_iseq_t *iseq = cfp->iseq;
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
- int line_no = rb_vm_get_sourceline(cfp);
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
- ID id = me->called_id;
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
- VALUE* x = save_regs_gc_mark.v;
1474
- unsigned long n = numberof(save_regs_gc_mark.v);
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
- char* id = rb_id2name(key);
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
- yg_cstring("data");
1534
- yg_id(ge->var->data);
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
- st_table *rb_global_tbl = rb_get_global_tbl();
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
- st_table *rb_class_tbl = rb_get_class_tbl();
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
- yajl_gen yajl = yajl_gen_alloc(&cfg,NULL);
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
- VALUE cls = rb_class_real_checked(CLASS_OF(p));
1756
+ cls = rb_class_real_checked(CLASS_OF(p));
1700
1757
  if(!cls) continue;
1701
- VALUE class_name = rb_class_path(cls);
1702
- long int n = RARRAY_LEN(string_prefixes)-1;
1703
- for(; n >= 0; n--){
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
+ }
@@ -1,3 +1,3 @@
1
1
  module HeapDump
2
- VERSION = "0.0.29"
2
+ VERSION = "0.0.30"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heap_dump
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.29
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-19 00:00:00.000000000 Z
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: