File 01-railsbench-gc-patch.patch of Package ruby-railsexpress

diff --git a/gc.c b/gc.c
index 564d260..27d9177 100644
--- gc.c.orig
+++ gc.c
@@ -78,6 +78,17 @@ void *alloca ();
 #define GC_MALLOC_LIMIT 8000000
 #endif
 
+#ifndef HAVE_LONG_LONG
+#define LONG_LONG long
+#endif
+
+static size_t heap_free_min = 4096;
+static int heap_min_slots = 10000;
+static int heap_slots_increment = 10000;
+static int initial_heap_slots_increment = 10000;
+static double heap_slots_growth_factor = 1.8;
+static size_t initial_malloc_limit = GC_MALLOC_LIMIT;
+
 #define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory]
 
 #define MARK_STACK_MAX 1024
@@ -267,7 +278,7 @@ typedef struct RVALUE {
 	struct RComplex complex;
     } as;
 #ifdef GC_DEBUG
-    const char *file;
+    VALUE file;
     int   line;
 #endif
 } RVALUE;
@@ -314,6 +325,8 @@ typedef struct rb_objspace {
     struct {
 	int dont_gc;
 	int during_gc;
+        int gc_statistics;
+        int verbose_gc_stats;
     } flags;
     struct {
 	st_table *table;
@@ -334,6 +347,14 @@ typedef struct rb_objspace {
     struct gc_list *global_list;
     unsigned int count;
     int gc_stress;
+    long heap_size;
+    unsigned LONG_LONG gc_time_accumulator;
+    FILE* gc_data_file;
+    long gc_collections;
+    unsigned LONG_LONG gc_allocated_size;
+    unsigned LONG_LONG gc_num_allocations;
+    unsigned long live_objects;
+    unsigned LONG_LONG allocated_objects;
 } rb_objspace_t;
 
 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
@@ -357,6 +378,16 @@ int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
 #define heaps_freed		objspace->heap.freed
 #define dont_gc 		objspace->flags.dont_gc
 #define during_gc		objspace->flags.during_gc
+#define gc_statistics           objspace->flags.gc_statistics
+#define verbose_gc_stats        objspace->flags.verbose_gc_stats
+#define heap_size               objspace->heap_size
+#define gc_time_accumulator     objspace->gc_time_accumulator
+#define gc_data_file            objspace->gc_data_file
+#define gc_collections          objspace->gc_collections
+#define gc_allocated_size       objspace->gc_allocated_size
+#define gc_num_allocations      objspace->gc_num_allocations
+#define live_objects            objspace->live_objects
+#define allocated_objects       objspace->allocated_objects
 #define finalizer_table 	objspace->final.table
 #define deferred_final_list	objspace->final.deferred
 #define mark_stack		objspace->markstack.buffer
@@ -375,7 +406,8 @@ rb_objspace_alloc(void)
 {
     rb_objspace_t *objspace = malloc(sizeof(rb_objspace_t));
     memset(objspace, 0, sizeof(*objspace));
-    malloc_limit = GC_MALLOC_LIMIT;
+    malloc_limit = initial_malloc_limit;
+
     ruby_gc_stress = ruby_initial_gc_stress;
 
     return objspace;
@@ -409,23 +441,8 @@ rb_objspace_free(rb_objspace_t *objspace)
 }
 #endif
 
-/* tiny heap size */
-/* 32KB */
-/*#define HEAP_SIZE 0x8000 */
-/* 128KB */
-/*#define HEAP_SIZE 0x20000 */
-/* 64KB */
-/*#define HEAP_SIZE 0x10000 */
-/* 16KB */
-#define HEAP_SIZE 0x4000
-/* 8KB */
-/*#define HEAP_SIZE 0x2000 */
-/* 4KB */
-/*#define HEAP_SIZE 0x1000 */
-/* 2KB */
-/*#define HEAP_SIZE 0x800 */
-
-#define HEAP_OBJ_LIMIT (HEAP_SIZE / sizeof(struct RVALUE))
+#define HEAP_OBJ_LIMIT 8000
+#define HEAP_SIZE (HEAP_OBJ_LIMIT * sizeof(struct RVALUE))
 
 extern VALUE rb_cMutex;
 extern st_table *rb_class_tbl;
@@ -673,6 +690,11 @@ vm_xmalloc(rb_objspace_t *objspace, size_t size)
     mem = (size_t *)mem + 1;
 #endif
 
+    if (gc_statistics) {
+        gc_allocated_size += size;
+	gc_num_allocations += 1;
+    }
+
     return mem;
 }
 
@@ -780,6 +802,92 @@ ruby_xfree(void *x)
 	vm_xfree(&rb_objspace, x);
 }
 
+static void set_gc_parameters(rb_objspace_t *objspace)
+{
+    char *envp;
+
+    gc_data_file = stderr;
+
+    envp = getenv("RUBY_GC_STATS");
+    if (envp != NULL) {
+        int i = atoi(envp);
+        if (i > 0) {
+            verbose_gc_stats = 1;
+        }
+        /* child processes should not inherit RUBY_GC_STATS */
+        ruby_unsetenv("RUBY_GC_STATS");
+    }
+
+    envp = getenv("RUBY_GC_DATA_FILE");
+    if (envp != NULL) {
+        FILE* data_file = fopen(envp, "w");
+        if (data_file != NULL) {
+            gc_data_file = data_file;
+        }
+        else {
+            fprintf(stderr, "can't open gc log file %s for writing, using default\n", envp);
+        }
+        /* child processes should not inherit RUBY_GC_DATA_FILE to avoid clobbering */
+        ruby_unsetenv("RUBY_GC_DATA_FILE");
+    }
+
+    envp = getenv("RUBY_HEAP_MIN_SLOTS");
+    if (envp != NULL) {
+        int i = atoi(envp);
+        if (verbose_gc_stats) {
+            fprintf(gc_data_file, "RUBY_HEAP_MIN_SLOTS=%s\n", envp);
+        }
+        if (i > 0) {
+            heap_min_slots = i;
+        }
+    }
+
+    envp = getenv("RUBY_HEAP_FREE_MIN");
+    if (envp != NULL) {
+        int i = atoi(envp);
+        if (verbose_gc_stats) {
+            fprintf(gc_data_file, "RUBY_HEAP_FREE_MIN=%s\n", envp);
+        }
+        if (i > 0) {
+            heap_free_min = i;
+        }
+    }
+
+    envp = getenv("RUBY_HEAP_SLOTS_INCREMENT");
+    if (envp != NULL) {
+        int i = atoi(envp);
+        if (verbose_gc_stats) {
+            fprintf(gc_data_file, "RUBY_HEAP_SLOTS_INCREMENT=%s\n", envp);
+        }
+        heap_slots_increment = i;
+        initial_heap_slots_increment = heap_slots_increment;
+    }
+
+    envp = getenv("RUBY_HEAP_SLOTS_GROWTH_FACTOR");
+    if (envp != NULL) {
+        double d = atof(envp);
+        if (verbose_gc_stats) {
+            fprintf(gc_data_file, "RUBY_HEAP_SLOTS_GROWTH_FACTOR=%s\n", envp);
+        }
+        if (d > 0) {
+            heap_slots_growth_factor = d;
+        }
+    }
+
+    envp = getenv("RUBY_GC_MALLOC_LIMIT");
+    if (envp != NULL) {
+        long l = atol(envp);
+        if (verbose_gc_stats) {
+            fprintf(gc_data_file, "RUBY_GC_MALLOC_LIMIT=%s\n", envp);
+        }
+        if (l > 0) {
+            initial_malloc_limit = l;
+            malloc_limit = initial_malloc_limit;
+        }
+    }
+
+    fflush(gc_data_file);
+}
 
 /*
  *  call-seq:
@@ -826,6 +934,454 @@ rb_gc_disable(void)
     return old ? Qtrue : Qfalse;
 }
 
+/*
+ *  call-seq:
+ *     GC.enable_stats    => true or false
+ *
+ *  Enables garbage collection statistics, returning <code>true</code> if garbage
+ *  collection statistics was already enabled.
+ *
+ *     GC.enable_stats   #=> false or true
+ *     GC.enable_stats   #=> true
+ *
+ */
+
+VALUE
+rb_gc_enable_stats()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    int old = gc_statistics;
+    gc_statistics = 1;
+    return old ? Qtrue : Qfalse;
+}
+
+/*
+ *  call-seq:
+ *     GC.disable_stats    => true or false
+ *
+ *  Disables garbage collection statistics, returning <code>true</code> if garbage
+ *  collection statistics was already disabled.
+ *
+ *     GC.disable_stats   #=> false or true
+ *     GC.disable_stats   #=> true
+ *
+ */
+
+VALUE
+rb_gc_disable_stats()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    int old = gc_statistics;
+    gc_statistics = 0;
+    return old ? Qtrue : Qfalse;
+}
+
+/*
+ *  call-seq:
+ *     GC.stats_enabled?    => true or false
+ *
+ *  Check whether GC stats have been enabled.
+ *
+ *     GC.stats_enabled?   #=> false or true
+ *
+ */
+
+VALUE
+rb_gc_stats_enabled()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    return gc_statistics ? Qtrue : Qfalse;
+}
+
+
+/*
+ *  call-seq:
+ *     GC.clear_stats    => nil
+ *
+ *  Clears garbage collection statistics, returning nil. This resets the number
+ *  of collections (GC.collections) and the time used (GC.time) to 0.
+ *
+ *     GC.clear_stats    #=> nil
+ *
+ */
+
+VALUE
+rb_gc_clear_stats()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    gc_collections = 0;
+    gc_time_accumulator = 0;
+    gc_allocated_size = 0;
+    gc_num_allocations = 0;
+    return Qnil;
+}
+
+/*
+ *  call-seq:
+ *     GC.allocated_size    => Integer
+ *
+ *  Returns the size of memory (in bytes) allocated since GC statistics collection
+ *  was enabled.
+ *
+ *     GC.allocated_size    #=> 35
+ *
+ */
+
+VALUE
+rb_gc_allocated_size()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+#if HAVE_LONG_LONG
+    return ULL2NUM(gc_allocated_size);
+#else
+    return ULONG2NUM(gc_allocated_size);
+#endif
+}
+
+/*
+ *  call-seq:
+ *     GC.num_allocations    => Integer
+ *
+ *  Returns the number of memory allocations since GC statistics collection
+ *  was enabled.
+ *
+ *     GC.num_allocations    #=> 150
+ *
+ */
+VALUE
+rb_gc_num_allocations()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+#if HAVE_LONG_LONG
+    return ULL2NUM(gc_num_allocations);
+#else
+    return ULONG2NUM(gc_num_allocations);
+#endif
+}
+
+/*
+ *  call-seq:
+ *     GC.enable_trace    => true or false
+ *
+ *  Enables garbage collection tracing, returning <code>true</code> if garbage
+ *  collection tracing was already enabled.
+ *
+ *     GC.enable_trace   #=> false or true
+ *     GC.enable_trace   #=> true
+ *
+ */
+
+VALUE
+rb_gc_enable_trace()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    int old = verbose_gc_stats;
+    verbose_gc_stats = 1;
+    return old ? Qtrue : Qfalse;
+}
+
+/*
+ *  call-seq:
+ *     GC.disable_trace    => true or false
+ *
+ *  Disables garbage collection tracing, returning <code>true</code> if garbage
+ *  collection tracing was already disabled.
+ *
+ *     GC.disable_trace   #=> false or true
+ *     GC.disable_trace   #=> true
+ *
+ */
+
+VALUE
+rb_gc_disable_trace()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    int old = verbose_gc_stats;
+    verbose_gc_stats = 0;
+    return old ? Qtrue : Qfalse;
+}
+
+/*
+ *  call-seq:
+ *     GC.trace_enabled?    => true or false
+ *
+ *  Check whether GC tracing has been enabled.
+ *
+ *     GC.trace_enabled?   #=> false or true
+ *
+ */
+
+VALUE
+rb_gc_trace_enabled()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    return verbose_gc_stats ? Qtrue : Qfalse;
+}
+
+
+const char* GC_LOGFILE_IVAR = "@gc_logfile_name";
+
+/*
+ *  call-seq:
+ *     GC.log_file(filename=nil, mode="w")    => boolean
+ *
+ *  Changes the GC data log file. Closes the currently open logfile.
+ *  Returns true if the file was successfully opened for
+ *  writing. Returns false if the file could not be opened for
+ *  writing. Returns the name of the current logfile (or nil) if no
+ *  parameter is given. Restores logging to stderr when given nil as
+ *  an argument.
+ *
+ *     GC.log_file                  #=> nil
+ *     GC.log_file "/tmp/gc.log"    #=> true
+ *     GC.log_file                  #=> "/tmp/gc.log"
+ *     GC.log_file nil              #=> true
+ *
+ */
+
+VALUE
+rb_gc_log_file(int argc, VALUE *argv, VALUE self)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    VALUE filename = Qnil;
+    VALUE mode_str = Qnil;
+    FILE* f = NULL;
+    const char* mode = "w";
+
+    VALUE current_logfile_name = rb_iv_get(rb_mGC, GC_LOGFILE_IVAR);
+
+    if (argc==0)
+        return current_logfile_name;
+
+    rb_scan_args(argc, argv, "02", &filename, &mode_str);
+
+    if (filename == Qnil) {
+        /* close current logfile and reset logfile to stderr */
+        if (gc_data_file != stderr) {
+            fclose(gc_data_file);
+            gc_data_file = stderr;
+            rb_iv_set(rb_mGC, GC_LOGFILE_IVAR, Qnil);
+        }
+        return Qtrue;
+    }
+
+    /* we have a real logfile name */
+    filename = StringValue(filename);
+
+    if (rb_equal(current_logfile_name, filename) == Qtrue) {
+        /* do nothing if we get the file name we're already logging to */
+        return Qtrue;
+    }
+
+    /* get mode for file opening */
+    if (mode_str != Qnil)
+    {
+      mode = RSTRING_PTR(StringValue(mode_str));
+    }
+
+    /* try to open file in given mode */
+    if (f = fopen(RSTRING_PTR(filename), mode)) {
+        if (gc_data_file != stderr) {
+            fclose(gc_data_file);
+        }
+        gc_data_file = f;
+        rb_iv_set(rb_mGC, GC_LOGFILE_IVAR, filename);
+    } else {
+        return Qfalse;
+    }
+    return Qtrue;
+}
+
+/*
+ *  call-seq:
+ *     GC.log String  => String
+ *
+ *  Logs string to the GC data file and returns it.
+ *
+ *     GC.log "manual GC call"    #=> "manual GC call"
+ *
+ */
+
+VALUE
+rb_gc_log(self, original_str)
+     VALUE self, original_str;
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    if (original_str == Qnil) {
+        fprintf(gc_data_file, "\n");
+    }
+    else {
+        VALUE str = StringValue(original_str);
+        char *p = RSTRING_PTR(str);
+        fprintf(gc_data_file, "%s\n", p);
+    }
+    return original_str;
+}
+
+/*
+ *  call-seq:
+ *     GC.dump    => nil
+ *
+ *  dumps information about the current GC data structures to the GC log file
+ *
+ *     GC.dump    #=> nil
+ *
+ */
+
+VALUE
+rb_gc_dump()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    size_t i;
+
+    for (i = 0; i < heaps_used; i++) {
+        size_t limit = heaps[i].limit;
+        fprintf(gc_data_file, "HEAP[%2lu]: size=%7lu\n", (unsigned long)i, (unsigned long)limit);
+    }
+
+    return Qnil;
+}
+
+static const char* obj_type(VALUE tp);
+
+#ifdef GC_DEBUG
+/*
+ *  call-seq:
+ *     GC.dump_file_and_line_info(String, boolean)    => nil
+ *
+ *  dumps information on which currently allocated object was created by which file and on which line
+ *
+ *     GC.dump_file_and_line_info(String, boolean)    #=> nil
+ *
+ *  The second parameter specifies whether class names should be included in the dump.
+ *  Note that including class names will allocate additional string objects on the heap.
+ *
+ */
+
+VALUE
+rb_gc_dump_file_and_line_info(int argc, VALUE *argv)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    VALUE filename, str, include_classnames = Qnil;
+    char *fname = NULL;
+    char *klass = NULL;
+    FILE* f = NULL;
+    size_t i = 0;
+
+    rb_scan_args(argc, argv, "11", &filename, &include_classnames);
+
+    str = StringValue(filename);
+    fname = RSTRING_PTR(str);
+    f = fopen(fname, "w");
+
+    for (i = 0; i < heaps_used; i++) {
+        RVALUE *p, *pend;
+
+        p = heaps[i].slot; pend = p + heaps[i].limit;
+        for (;p < pend; p++) {
+            if (p->as.basic.flags) {
+                const char *src_filename = (p->file && p->file != Qnil )? RSTRING_PTR(p->file) : "";
+                fprintf(f, "%s:%s:%d", obj_type(p->as.basic.flags & T_MASK), src_filename, (int)p->line);
+                // rb_obj_classname will create objects on the heap, we need a better solution
+                if (include_classnames == Qtrue) {
+                    /* write the class */
+                    fprintf(f, ":");
+                    switch (BUILTIN_TYPE(p)) {
+                    case T_NONE:
+                        fprintf(f, "__none__");
+                        break;
+                    case T_UNDEF:
+                        fprintf(f, "__undef__");
+                        break;
+                    case T_NODE:
+                        fprintf(f, "__node__");
+                        break;
+                    default:
+                        if (!p->as.basic.klass) {
+                            fprintf(f, "__unknown__");
+                        } else {
+                            fprintf(f, "%s", rb_obj_classname((VALUE)p));
+                        }
+                    }
+                    /* print object size for some known object types */
+                    switch (BUILTIN_TYPE(p)) {
+                    case T_STRING:
+                        fprintf(f, ":%lu", RSTRING_LEN(p));
+                        break;
+                    case T_ARRAY:
+                        fprintf(f, ":%lu", RARRAY_LEN(p));
+                        break;
+                    case T_HASH:
+                        fprintf(f, ":%lu", (long unsigned int)RHASH_SIZE(p));
+                        break;
+                    }
+                }
+                fprintf(f, "\n");
+            }
+        }
+    }
+    fclose(f);
+    return Qnil;
+}
+#endif
+
+/*
+ *  call-seq:
+ *     GC.heap_slots    => Integer
+ *
+ *  Returns the number of heap slots available for object allocations.
+ *
+ *     GC.heap_slots    #=> 10000
+ *
+ */
+VALUE
+rb_gc_heap_slots()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    return LONG2NUM(heap_size);
+}
+
+
+/*
+ *  call-seq:
+ *     GC.collections    => Integer
+ *
+ *  Returns the number of garbage collections performed while GC statistics collection
+ *  was enabled.
+ *
+ *     GC.collections    #=> 35
+ *
+ */
+
+VALUE
+rb_gc_collections()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    return LONG2NUM(gc_collections);
+}
+
+/*
+ *  call-seq:
+ *     GC.time    => Integer
+ *
+ *  Returns the time spent during garbage collection while GC statistics collection
+ *  was enabled (in micro seconds).
+ *
+ *     GC.time    #=> 20000
+ *
+ */
+
+VALUE
+rb_gc_time()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+#if HAVE_LONG_LONG
+    return LL2NUM(gc_time_accumulator);
+#else
+    return LONG2NUM(gc_time_accumulator);
+#endif
+}
+
 VALUE rb_mGC;
 
 void
@@ -944,6 +1500,7 @@ assign_heap_slot(rb_objspace_t *objspace)
     if (lomem == 0 || lomem > p) lomem = p;
     if (himem < pend) himem = pend;
     heaps_used++;
+    heap_size += objs;
 
     while (p < pend) {
 	p->as.free.flags = 0;
@@ -958,7 +1515,7 @@ init_heap(rb_objspace_t *objspace)
 {
     size_t add, i;
 
-    add = HEAP_MIN_SLOTS / HEAP_OBJ_LIMIT;
+    add = heap_min_slots / HEAP_OBJ_LIMIT;
 
     if (!add) {
         add = 1;
@@ -979,7 +1536,7 @@ init_heap(rb_objspace_t *objspace)
 static void
 set_heaps_increment(rb_objspace_t *objspace)
 {
-    size_t next_heaps_length = (size_t)(heaps_used * 1.8);
+    size_t next_heaps_length = (size_t)(heaps_used * heap_slots_growth_factor);
 
     if (next_heaps_length == heaps_used) {
         next_heaps_length++;
@@ -1005,6 +1562,22 @@ heaps_increment(rb_objspace_t *objspace)
 
 #define RANY(o) ((RVALUE*)(o))
 
+#ifdef GC_DEBUG
+static VALUE
+_rb_sourcefile(void)
+{
+    rb_thread_t *th = GET_THREAD();
+    rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
+
+    if (cfp) {
+	return cfp->iseq->filename;
+    }
+    else {
+	return Qnil;
+    }
+}
+#endif
+
 static VALUE
 rb_newobj_from_heap(rb_objspace_t *objspace)
 {
@@ -1022,9 +1595,11 @@ rb_newobj_from_heap(rb_objspace_t *objspace)
 
     MEMZERO((void*)obj, RVALUE, 1);
 #ifdef GC_DEBUG
-    RANY(obj)->file = rb_sourcefile();
+    RANY(obj)->file = _rb_sourcefile();
     RANY(obj)->line = rb_sourceline();
 #endif
+    live_objects++;
+    allocated_objects++;
 
     return obj;
 }
@@ -1525,6 +2100,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
 {
     register RVALUE *obj = RANY(ptr);
 
+#ifdef GC_DEBUG
+    if (obj->file && obj->file != Qnil && is_pointer_to_heap(objspace, (void*)obj->file)) {
+        gc_mark(objspace, obj->file, lev);
+    }
+#endif
+
     goto marking;		/* skip */
 
   again:
@@ -1534,6 +2115,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
     if (obj->as.basic.flags & FL_MARK) return;  /* already marked */
     obj->as.basic.flags |= FL_MARK;
 
+#ifdef GC_DEBUG
+    if (obj->file && obj->file != Qnil && is_pointer_to_heap(objspace, (void*)obj->file)) {
+        gc_mark(objspace, obj->file, lev);
+    }
+#endif
+
   marking:
     if (FL_TEST(obj, FL_EXIVAR)) {
 	rb_mark_generic_ivar(ptr);
@@ -1851,6 +2438,39 @@ free_unused_heaps(rb_objspace_t *objspace)
     }
 }
 
+static const char* obj_type(VALUE type)
+{
+    switch (type) {
+        case T_NIL    : return "NIL";
+        case T_OBJECT : return "OBJECT";
+        case T_CLASS  : return "CLASS";
+        case T_ICLASS : return "ICLASS";
+        case T_MODULE : return "MODULE";
+        case T_FLOAT  : return "FLOAT";
+        case T_COMPLEX: return "COMPLEX";
+        case T_RATIONAL: return "RATIONAL";
+        case T_STRING : return "STRING";
+        case T_REGEXP : return "REGEXP";
+        case T_ARRAY  : return "ARRAY";
+        case T_FIXNUM : return "FIXNUM";
+        case T_HASH   : return "HASH";
+        case T_STRUCT : return "STRUCT";
+        case T_BIGNUM : return "BIGNUM";
+        case T_FILE   : return "FILE";
+
+        case T_TRUE   : return "TRUE";
+        case T_FALSE  : return "FALSE";
+        case T_DATA   : return "DATA";
+        case T_MATCH  : return "MATCH";
+        case T_SYMBOL : return "SYMBOL";
+        case T_ZOMBIE : return "ZOMBIE";
+
+        case T_UNDEF  : return "UNDEF";
+        case T_NODE   : return "NODE";
+        default: return "____";
+    }
+}
+
 static void
 gc_sweep(rb_objspace_t *objspace)
 {
@@ -1859,12 +2479,27 @@ gc_sweep(rb_objspace_t *objspace)
     size_t i;
     size_t live = 0, free_min = 0, do_heap_free = 0;
 
+    long max_blocks_to_free = heaps_used - (heap_min_slots / HEAP_OBJ_LIMIT);
+    int freed_blocks = 0;
+
+    unsigned long processed = 0;
+    unsigned long freelist_size = 0;
+    unsigned long zombies = 0;
+    unsigned long free_counts[T_MASK];
+    unsigned long live_counts[T_MASK];
+    int do_gc_stats = gc_statistics & verbose_gc_stats;
+
+    if (do_gc_stats) {
+        MEMZERO((void*)free_counts, unsigned long, T_MASK);
+        MEMZERO((void*)live_counts, unsigned long, T_MASK);
+    }
+
     do_heap_free = (size_t)((heaps_used * HEAP_OBJ_LIMIT) * 0.65);
     free_min = (size_t)((heaps_used * HEAP_OBJ_LIMIT)  * 0.2);
 
-    if (free_min < FREE_MIN) {
+    if (free_min < heap_free_min) {
 	do_heap_free = heaps_used * HEAP_OBJ_LIMIT;
-        free_min = FREE_MIN;
+        free_min = heap_free_min;
     }
 
     freelist = 0;
@@ -1881,10 +2516,15 @@ gc_sweep(rb_objspace_t *objspace)
 	p = heaps[i].slot; pend = p + heaps[i].limit;
 	while (p < pend) {
 	    if (!(p->as.basic.flags & FL_MARK)) {
+                if (do_gc_stats && !p->as.basic.flags) {
+                    /* slot was free before GC */
+                    freelist_size++;
+                }
 		if (p->as.basic.flags &&
 		    ((deferred = obj_free(objspace, (VALUE)p)) ||
 		     ((FL_TEST(p, FL_FINALIZE)) && need_call_final))) {
 		    if (!deferred) {
+                        if (do_gc_stats) zombies++;
 			p->as.free.flags = T_ZOMBIE;
 			RDATA(p)->dfree = 0;
 		    }
@@ -1894,6 +2534,10 @@ gc_sweep(rb_objspace_t *objspace)
 		    final_num++;
 		}
 		else {
+                    if (do_gc_stats) {
+                       VALUE obt = p->as.basic.flags & T_MASK;
+                       if (obt) free_counts[obt]++;
+                    }
 		    add_freelist(objspace, p);
 		    free_num++;
 		}
@@ -1901,16 +2545,23 @@ gc_sweep(rb_objspace_t *objspace)
 	    else if (BUILTIN_TYPE(p) == T_ZOMBIE) {
 		/* objects to be finalized */
 		/* do nothing remain marked */
+                if (do_gc_stats) zombies++;
 	    }
 	    else {
 		RBASIC(p)->flags &= ~FL_MARK;
 		live++;
+                if (do_gc_stats) {
+                    live_counts[p->as.basic.flags & T_MASK]++;
+                }
 	    }
 	    p++;
 	}
-	if (final_num + free_num == heaps[i].limit && freed > do_heap_free) {
+	if (final_num + free_num == heaps[i].limit && freed > do_heap_free && freed_blocks < max_blocks_to_free) {
 	    RVALUE *pp;
 
+            freed_blocks += 1;
+            heap_size -= final_num + free_num;
+
 	    for (pp = final_list; pp != final; pp = pp->as.free.next) {
 		RDATA(pp)->dmark = (void (*)())(VALUE)&heaps[i];
 		pp->as.free.flags |= FL_SINGLETON; /* freeing page mark */
@@ -1922,11 +2573,13 @@ gc_sweep(rb_objspace_t *objspace)
 	else {
 	    freed += free_num;
 	}
+        processed += heaps[i].limit;
     }
     GC_PROF_SET_MALLOC_INFO;
     if (malloc_increase > malloc_limit) {
 	malloc_limit += (size_t)((malloc_increase - malloc_limit) * (double)live / (live + freed));
-	if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
+        if (malloc_limit < initial_malloc_limit) malloc_limit = initial_malloc_limit;
+
     }
     malloc_increase = 0;
     if (freed < free_min) {
@@ -1934,6 +2587,25 @@ gc_sweep(rb_objspace_t *objspace)
         heaps_increment(objspace);
     }
     during_gc = 0;
+    live_objects = live;
+
+    /* log gc stats if requested */
+    if (do_gc_stats) {
+        fprintf(gc_data_file, "objects processed: %.7lu\n", (unsigned long)processed);
+	fprintf(gc_data_file, "live objects     : %.7lu\n", (unsigned long)live);
+	fprintf(gc_data_file, "freelist objects : %.7lu\n", (unsigned long)freelist_size);
+	fprintf(gc_data_file, "freed objects    : %.7lu\n", (unsigned long)freed);
+	fprintf(gc_data_file, "zombies          : %.7lu\n", (unsigned long)zombies);
+        for(i=0; i<T_MASK; i++) {
+            if (free_counts[i]>0 || live_counts[i]>0) {
+                fprintf(gc_data_file,
+                        "kept %.7lu / freed %.7lu objects of type %s\n",
+                        (unsigned long)live_counts[i], (unsigned long)free_counts[i], obj_type((int)i));
+            }
+        }
+        rb_gc_dump();
+        fflush(gc_data_file);
+    }
 
     /* clear finalization list */
     if (final_list) {
@@ -2140,6 +2812,7 @@ void rb_gc_mark_encodings(void);
 static int
 garbage_collect(rb_objspace_t *objspace)
 {
+    struct timeval gctv1, gctv2;
     struct gc_list *list;
     rb_thread_t *th = GET_THREAD();
     INIT_GC_PROF_PARAMS;
@@ -2162,6 +2835,14 @@ garbage_collect(rb_objspace_t *objspace)
     during_gc++;
     objspace->count++;
 
+    if (gc_statistics) {
+        gc_collections++;
+        gettimeofday(&gctv1, NULL);
+        if (verbose_gc_stats) {
+            fprintf(gc_data_file, "Garbage collection started\n");
+        }
+    }
+
     GC_PROF_TIMER_START;
     GC_PROF_MARK_TIMER_START;
     SET_STACK_END;
@@ -2216,6 +2897,19 @@ garbage_collect(rb_objspace_t *objspace)
 
     GC_PROF_TIMER_STOP;
     if (GC_NOTIFY) printf("end garbage_collect()\n");
+
+    if (gc_statistics) {
+        unsigned LONG_LONG musecs_used;
+        gettimeofday(&gctv2, NULL);
+        musecs_used = ((unsigned LONG_LONG)(gctv2.tv_sec - gctv1.tv_sec) * 1000000) + (gctv2.tv_usec - gctv1.tv_usec);
+        gc_time_accumulator += musecs_used;
+
+        if (verbose_gc_stats) {
+            fprintf(gc_data_file, "GC time: %lu msec\n", (unsigned long)(musecs_used / 1000));
+            fflush(gc_data_file);
+        }
+    }
+
     return TRUE;
 }
 
@@ -2298,6 +2992,7 @@ Init_stack(volatile VALUE *addr)
 void
 Init_heap(void)
 {
+    set_gc_parameters(&rb_objspace);
     init_heap(&rb_objspace);
 }
 
@@ -2982,6 +3677,49 @@ count_objects(int argc, VALUE *argv, VALUE os)
     return hash;
 }
 
+/* call-seq:
+ *  ObjectSpace.live_objects => number
+ *
+ * Returns the count of objects currently allocated in the system. This goes
+ * down after the garbage collector runs.
+ */
+static
+VALUE os_live_objects(VALUE self)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    return ULONG2NUM(live_objects);
+}
+
+unsigned long rb_os_live_objects()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    return live_objects;
+}
+
+/* call-seq:
+ *  ObjectSpace.allocated_objects => number
+ *
+ * Returns the count of objects allocated since the Ruby interpreter has
+ * started.  This number can only increase. To know how many objects are
+ * currently allocated, use ObjectSpace::live_objects
+ */
+static
+VALUE os_allocated_objects(VALUE self)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+#if defined(HAVE_LONG_LONG)
+    return ULL2NUM(allocated_objects);
+#else
+    return ULONG2NUM(allocated_objects);
+#endif
+}
+
+unsigned LONG_LONG rb_os_allocated_objects()
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    return allocated_objects;
+}
+
 /*
  *  call-seq:
  *     GC.count -> Integer
@@ -3188,6 +3926,28 @@ Init_GC(void)
     rb_define_singleton_method(rb_mGC, "count", gc_count, 0);
     rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
 
+    rb_define_singleton_method(rb_mGC, "enable_stats", rb_gc_enable_stats, 0);
+    rb_define_singleton_method(rb_mGC, "disable_stats", rb_gc_disable_stats, 0);
+    rb_define_singleton_method(rb_mGC, "stats_enabled?", rb_gc_stats_enabled, 0);
+    rb_define_singleton_method(rb_mGC, "clear_stats", rb_gc_clear_stats, 0);
+    rb_define_singleton_method(rb_mGC, "allocated_size", rb_gc_allocated_size, 0);
+    rb_define_singleton_method(rb_mGC, "num_allocations", rb_gc_num_allocations, 0);
+    rb_define_singleton_method(rb_mGC, "heap_slots", rb_gc_heap_slots, 0);
+    rb_define_const(rb_mGC, "HEAP_SLOT_SIZE", INT2FIX(sizeof(RVALUE)));
+
+    rb_define_singleton_method(rb_mGC, "log", rb_gc_log, 1);
+    rb_define_singleton_method(rb_mGC, "log_file", rb_gc_log_file, -1);
+    rb_define_singleton_method(rb_mGC, "enable_trace", rb_gc_enable_trace, 0);
+    rb_define_singleton_method(rb_mGC, "disable_trace", rb_gc_disable_trace, 0);
+    rb_define_singleton_method(rb_mGC, "trace_enabled?", rb_gc_trace_enabled, 0);
+
+    rb_define_singleton_method(rb_mGC, "collections", rb_gc_collections, 0);
+    rb_define_singleton_method(rb_mGC, "time", rb_gc_time, 0);
+    rb_define_singleton_method(rb_mGC, "dump", rb_gc_dump, 0);
+#ifdef GC_DEBUG
+    rb_define_singleton_method(rb_mGC, "dump_file_and_line_info", rb_gc_dump_file_and_line_info, -1);
+#endif
+
     rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler");
     rb_define_singleton_method(rb_mProfiler, "enabled?", gc_profile_enable_get, 0);
     rb_define_singleton_method(rb_mProfiler, "enable", gc_profile_enable, 0);
@@ -3201,6 +3961,9 @@ Init_GC(void)
     rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
     rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);
 
+    rb_define_module_function(rb_mObSpace, "live_objects", os_live_objects, 0);
+    rb_define_module_function(rb_mObSpace, "allocated_objects", os_allocated_objects, 0);
+
     rb_define_module_function(rb_mObSpace, "define_finalizer", define_final, -1);
     rb_define_module_function(rb_mObSpace, "undefine_finalizer", undefine_final, 1);
 
diff --git sample/test.rb.orig sample/test.rb
old mode 100644
new mode 100755
openSUSE Build Service is sponsored by