Skip to content

Instantly share code, notes, and snippets.

@tmm1
Created March 4, 2009 02:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmm1/73674 to your computer and use it in GitHub Desktop.
Save tmm1/73674 to your computer and use it in GitHub Desktop.
simple heap dumper for ruby 1.8
$ ./miniruby -e 'class T; end; a = 1..1; b = "hi"; c = []; d = Hash.new; e = T.new; p GC.dump_heap'
0x00154750 allocated @ -e:1 is an OBJECT of type: T
0x0015476c allocated @ -e:1 is a HASH which has data
0x00154788 allocated @ -e:1 is an ARRAY of len: 0.
0x001547c0 allocated @ -e:1 is a STRING (ELTS_SHARED) which has len: 2 and val: hi
0x001547dc allocated @ -e:1 is a STRING which has len: 1 and val: T
0x001547f8 allocated @ -e:1 which is a CLASS no name - maybe anon class?
0x00154814 allocated @ -e:1 which is a CLASS named: T inherits from Object
0x00154a98 allocated @ -e:1 is a STRING which has len: 2 and val: hi
0x00154b40 allocated @ -e:1 is an OBJECT of type: Range
0x00154c04 allocated @ -e:1 is a VARMAP
[3643, 6357, 10000, 1]
diff --git a/eval.c b/eval.c
index 3be7d98..4bbfa1a 100644
--- a/eval.c
+++ b/eval.c
@@ -1149,7 +1149,7 @@ static VALUE trace_func = 0;
static int tracing = 0;
static void call_trace_func _((rb_event_t,NODE*,VALUE,ID,VALUE));
-#if 0
+#if 1
#define SET_CURRENT_SOURCE() (ruby_sourcefile = ruby_current_node->nd_file, \
ruby_sourceline = nd_line(ruby_current_node))
#else
diff --git a/gc.c b/gc.c
index cefca95..891c213 100644
--- a/gc.c
+++ b/gc.c
@@ -411,7 +411,8 @@ rb_gc_unregister_address(addr)
}
}
-#undef GC_DEBUG
+// #undef GC_DEBUG
+#define GC_DEBUG 1
void
rb_global_variable(var)
@@ -1077,6 +1078,10 @@ gc_mark(ptr, lev)
if (obj->as.basic.flags == 0) return; /* free cell */
if (obj->as.basic.flags & FL_MARK) return; /* already marked */
obj->as.basic.flags |= FL_MARK;
+#ifdef GC_DEBUG
+ /* mark our new reference point for sourcefile objects */
+ mark_source_filename(RANY(obj)->file);
+#endif
if (lev > GC_LEVEL_MAX || (lev == 0 && ruby_stack_check())) {
if (!mark_stack_overflow) {
@@ -1115,6 +1120,10 @@ gc_mark_children(ptr, lev)
if (obj->as.basic.flags == 0) return; /* free cell */
if (obj->as.basic.flags & FL_MARK) return; /* already marked */
obj->as.basic.flags |= FL_MARK;
+#ifdef GC_DEBUG
+ /* mark our new reference point for sourcefile objects */
+ mark_source_filename(RANY(obj)->file);
+#endif
marking:
if (FL_TEST(obj, FL_EXIVAR)) {
@@ -1906,6 +1915,190 @@ ruby_set_stack_size(size)
#endif
}
+static void
+dump_obj(VALUE ptr, FILE *out)
+{
+ RVALUE *obj = (RVALUE *)RANY(ptr);
+ RVALUE *kobj = NULL;
+ unsigned int type = 0;
+ int i = 0;
+ char *classname = NULL, *superclass = NULL;
+
+ if (ptr == 0) {
+ // fprintf(heap_dump_file, " found a NULL obj, skipping\n");
+ return;
+ }
+
+ if(obj->as.free.flags == 0) {
+ // fprintf(heap_dump_file, " is marked as FREE ");
+ return;
+ }
+
+ type = obj->as.basic.flags & T_MASK;
+
+ // don't print internal objects (with no file/line associated)
+ if (!obj->file || obj->line==0) return;
+
+ // ignore nodes
+ if (type == T_NODE) return;
+
+ fprintf(out, "0x%.8x allocated @ %s:%d", ptr, obj->file, obj->line);
+
+ switch (type) {
+
+ case T_NIL:
+ fprintf(out, " is a T_NIL, and nothing else to do.\n");
+ break;
+
+ case T_OBJECT:
+ fprintf(out, " is an OBJECT");
+ // classname = rb_mod_name_str(obj->as.basic.klass);
+ classname = rb_obj_classname((VALUE)obj);
+ if (!classname || classname[0] == '\0')
+ fprintf(out, "\n");
+ else
+ fprintf(out, " of type: %s\n", classname);
+ break;
+
+ case T_FIXNUM:
+ fprintf(out, " is a T_FIXNUM, has value: %lu\n", FIX2LONG(ptr));
+ break;
+
+ case T_NODE:
+ fprintf(out, " is a NODE\n");
+ break;
+
+ case T_ARRAY:
+ if (FL_TEST(obj, ELTS_SHARED)) {
+ fprintf(out, " is an ARRAY that has ELTS_SHARED marked.\n");
+ break;
+ } else {
+ long len = obj->as.array.len;
+ fprintf(out, " is an ARRAY of len: %ld.\n", len);
+ }
+ break;
+
+ case T_STRING:
+ fprintf(out, " is a STRING");
+
+ if (FL_TEST(obj, ELTS_SHARED))
+ fprintf(out, " (ELTS_SHARED)");
+ if (FL_TEST(obj, FL_USER3))
+ fprintf(out, " (STR_ASSOC)");
+
+ fprintf(out, " which has len: %ld and val: %s\n", obj->as.string.len, obj->as.string.ptr);
+ break;
+
+ case T_MODULE:
+ case T_CLASS:
+ case T_ICLASS:
+ classname = rb_mod_name_str(ptr);
+ superclass = rb_mod_name_str(obj->as.klass.super);
+
+ if (type==T_MODULE)
+ fprintf(out, " is a MODULE");
+ else if (type==T_ICLASS)
+ fprintf(out, " is an ICLASS");
+ else
+ fprintf(out, " is a CLASS");
+
+ if (classname && classname[0] != '\0')
+ fprintf(out, " named: %s", classname);
+ else
+ fprintf(out, " no name - maybe anon class?");
+
+ if (superclass && superclass[0] != '\0')
+ fprintf(out, " inherits from %s", superclass);
+
+ fprintf(out, "\n");
+ break;
+
+ case T_HASH:
+ if (obj->as.hash.tbl != NULL)
+ fprintf(out, " is a HASH which has data\n");
+ else
+ fprintf(out, " is a HASH which DOES NOT have data\n");
+ break;
+
+ case T_REGEXP:
+ fprintf(out, " is a REGEXP val: %s\n", obj->as.regexp.str);
+ break;
+
+ case T_FILE:
+ fprintf(out, " is a FILE\n");
+ break;
+
+ case T_DATA:
+ fprintf(out, " is a DATA\n");
+ break;
+
+ case T_VARMAP:
+ fprintf(out, " is a VARMAP\n");
+ break;
+
+ default:
+ fprintf(out, " is a type I don't know: %d\n", type);
+ break;
+ }
+}
+
+VALUE
+rb_gc_dump_heap(int argc, VALUE *argv, VALUE self)
+{
+ RVALUE *p, *pend;
+ int i;
+ int open = 0, closed = 0;
+ FILE *out = NULL;
+ VALUE str;
+
+ rb_scan_args(argc, argv, "01", &str);
+
+ if (NIL_P(str))
+ out = stderr;
+ else
+ out = fopen(RSTRING_PTR(str), "a+");
+
+ for (i = 0; i < heaps_used; i++) {
+ p = heaps[i].slot;
+ pend = p + heaps[i].limit;
+ while (p < pend) {
+ if(RBASIC(p)->flags != 0 && out)
+ dump_obj((VALUE)p, out);
+
+ if((VALUE)p->as.free.flags == 0)
+ open++;
+ else
+ closed++;
+
+ p++;
+ }
+ }
+
+ if (out) fclose(out);
+
+ return rb_ary_new3(4, INT2FIX(closed), INT2FIX(open), INT2FIX(open+closed), INT2FIX(heaps_used));
+}
+
+VALUE
+rb_gc_live_object_count()
+{
+ int i,n;
+ RVALUE *p, *pend;
+ n = 0;
+ for (i = 0; i < heaps_used; i++) {
+ p = heaps[i].slot; pend = p + heaps[i].limit;
+ for (;p < pend; p++)
+ if (p->as.basic.flags) n++;
+ }
+ return INT2FIX(n);
+}
+
+VALUE
+rb_gc_malloc_limit()
+{
+ return LONG2FIX(malloc_limit);
+}
+
void
Init_stack(addr)
VALUE *addr;
@@ -2493,6 +2686,9 @@ Init_GC()
VALUE rb_mObSpace;
rb_mGC = rb_define_module("GC");
+ rb_define_singleton_method(rb_mGC, "dump_heap", rb_gc_dump_heap, -1);
+ rb_define_singleton_method(rb_mGC, "malloc_limit", rb_gc_malloc_limit, 0);
+ rb_define_singleton_method(rb_mGC, "num_objects", rb_gc_live_object_count, 0);
rb_define_singleton_method(rb_mGC, "start", rb_gc_start, 0);
rb_define_singleton_method(rb_mGC, "enable", rb_gc_enable, 0);
rb_define_singleton_method(rb_mGC, "disable", rb_gc_disable, 0);
diff --git a/variable.c b/variable.c
index 50ecf04..7efc2a9 100644
--- a/variable.c
+++ b/variable.c
@@ -183,6 +183,16 @@ rb_mod_name(mod)
return rb_str_new(0,0);
}
+const char *
+rb_mod_name_str(mod)
+ VALUE mod;
+{
+ VALUE path = classname(mod);
+
+ if (!NIL_P(path)) return RSTRING_PTR(path);
+ return NULL;
+}
+
VALUE
rb_class_path(klass)
VALUE klass;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment