Created
March 4, 2009 02:27
-
-
Save tmm1/73674 to your computer and use it in GitHub Desktop.
simple heap dumper for ruby 1.8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ ./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] | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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