Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

patches for 1.9.2pl320

  • Loading branch information...
commit 0c8037ec0ba57f35221519b86dd8512a09ad44bf 1 parent 9958fa6
@skaes skaes authored
View
3  README.rdoc
@@ -59,7 +59,8 @@ there's no guarantee. The following versions are currently supported:
1.8.7-p358 # current rvm default for MRI-ruby 1.8.7
1.9.2-p180 #
1.9.2-p290 #
- 1.9.2-p318 # current rvm default for MRI-ruby 1.9.2
+ 1.9.2-p318 #
+ 1.9.2-p320 # current rvm default for MRI-ruby 1.9.2
1.9.3-p125 #
1.9.3-p194 # current rvm default for MRI-ruby 1.9.3
View
1,081 patches/ruby/1.9.2/p320/railsexpress/01-railsbench-gc-patch.patch
@@ -0,0 +1,1081 @@
+diff --git a/configure.in b/configure.in
+index 3f94ac8..591aab9 100644
+--- a/configure.in
++++ b/configure.in
+@@ -2209,6 +2209,14 @@ if test "$EXEEXT" = .exe; then
+ AC_SUBST(EXECUTABLE_EXTS)
+ fi
+
++dnl enable gc debugging
++AC_ARG_ENABLE(gcdebug,
++ [ --enable-gcdebug build garbage collector with debugging enabled. ],
++ [enable_gcdebug=$enableval])
++if test "$enable_gcdebug" = 'yes'; then
++ AC_DEFINE(GC_DEBUG, 1)
++fi
++
+ dnl }
+ dnl build section {
+
+diff --git a/gc.c b/gc.c
+index 564d260..27d9177 100644
+--- a/gc.c
++++ b/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 a/sample/test.rb b/sample/test.rb
+old mode 100644
+new mode 100755
View
15 patches/ruby/1.9.2/p320/railsexpress/02-display-more-detailed-stack-trace.patch
@@ -0,0 +1,15 @@
+diff --git a/eval_error.c b/eval_error.c
+index 41fcbb0..9e065f9 100644
+--- a/eval_error.c
++++ b/eval_error.c
+@@ -166,8 +166,8 @@ error_print(void)
+ int skip = eclass == rb_eSysStackError;
+
+ #define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5)
+-#define TRACE_HEAD 8
+-#define TRACE_TAIL 5
++#define TRACE_HEAD 100
++#define TRACE_TAIL 100
+
+ for (i = 1; i < len; i++) {
+ if (TYPE(ptr[i]) == T_STRING) {
View
68 patches/ruby/1.9.2/p320/railsexpress/03-fork-support-for-gc-logging.patch
@@ -0,0 +1,68 @@
+diff --git a/gc.c b/gc.c
+index 27d9177..7673687 100644
+--- a/gc.c
++++ b/gc.c
+@@ -1193,6 +1193,34 @@ rb_gc_log_file(int argc, VALUE *argv, VALUE self)
+ }
+
+ /*
++ * Called from process.c before a fork. Flushes the gc log file to
++ * avoid writing the buffered output twice (once in the parent, and
++ * once in the child).
++ */
++void
++rb_gc_before_fork()
++{
++ rb_objspace_t *objspace = &rb_objspace;
++ fflush(gc_data_file);
++}
++
++/*
++ * Called from process.c after a fork in the child process. Turns off
++ * logging, disables GC stats and resets all gc counters and timing
++ * information.
++ */
++void
++rb_gc_after_fork()
++{
++ rb_objspace_t *objspace = &rb_objspace;
++ rb_gc_disable_stats();
++ rb_gc_clear_stats();
++ rb_gc_disable_trace();
++ gc_data_file = stderr;
++ rb_iv_set(rb_mGC, GC_LOGFILE_IVAR, Qnil);
++}
++
++/*
+ * call-seq:
+ * GC.log String => String
+ *
+diff --git a/include/ruby/intern.h b/include/ruby/intern.h
+index f0cff67..a374c7e 100644
+--- a/include/ruby/intern.h
++++ b/include/ruby/intern.h
+@@ -390,6 +390,8 @@ void rb_gc_call_finalizer_at_exit(void);
+ VALUE rb_gc_enable(void);
+ VALUE rb_gc_disable(void);
+ VALUE rb_gc_start(void);
++void rb_gc_before_fork _((void));
++void rb_gc_after_fork _((void));
+ #define Init_stack(addr) ruby_init_stack(addr)
+ /* hash.c */
+ void st_foreach_safe(struct st_table *, int (*)(ANYARGS), st_data_t);
+diff --git a/process.c b/process.c
+index c180492..d20d44c 100644
+--- a/process.c
++++ b/process.c
+@@ -2617,9 +2617,11 @@ rb_f_fork(VALUE obj)
+ rb_pid_t pid;
+
+ rb_secure(2);
++ rb_gc_before_fork();
+
+ switch (pid = rb_fork(0, 0, 0, Qnil)) {
+ case 0:
++ rb_gc_after_fork();
+ rb_thread_atfork();
+ if (rb_block_given_p()) {
+ int status;
View
69 patches/ruby/1.9.2/p320/railsexpress/04-track-live-dataset-size.patch
@@ -0,0 +1,69 @@
+diff --git a/gc.c b/gc.c
+index 7673687..4d9ca88 100644
+--- a/gc.c
++++ b/gc.c
+@@ -248,7 +248,6 @@ getrusage_time(void)
+ } while(0)
+ #endif
+
+-
+ #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
+ #pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
+ #endif
+@@ -355,6 +354,7 @@ typedef struct rb_objspace {
+ unsigned LONG_LONG gc_num_allocations;
+ unsigned long live_objects;
+ unsigned LONG_LONG allocated_objects;
++ unsigned long heap_slots_live_after_last_gc;
+ } rb_objspace_t;
+
+ #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
+@@ -388,6 +388,7 @@ int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
+ #define gc_num_allocations objspace->gc_num_allocations
+ #define live_objects objspace->live_objects
+ #define allocated_objects objspace->allocated_objects
++#define heap_slots_live_after_last_gc objspace->heap_slots_live_after_last_gc
+ #define finalizer_table objspace->final.table
+ #define deferred_final_list objspace->final.deferred
+ #define mark_stack objspace->markstack.buffer
+@@ -1410,6 +1411,24 @@ rb_gc_time()
+ #endif
+ }
+
++/*
++ * call-seq:
++ * GC.heap_slots_live_after_last_gc => Integer
++ *
++ * Returns the number of heap slots which were live after the last garbage collection.
++ *
++ * GC.heap_slots_live_after_last_gc #=> 231223
++ *
++ */
++VALUE
++rb_gc_heap_slots_live_after_last_gc()
++{
++ rb_objspace_t *objspace = &rb_objspace;
++ return ULONG2NUM(heap_slots_live_after_last_gc);
++}
++
++
++
+ VALUE rb_mGC;
+
+ void
+@@ -2616,6 +2635,7 @@ gc_sweep(rb_objspace_t *objspace)
+ }
+ during_gc = 0;
+ live_objects = live;
++ heap_slots_live_after_last_gc = live;
+
+ /* log gc stats if requested */
+ if (do_gc_stats) {
+@@ -3961,6 +3981,7 @@ Init_GC(void)
+ 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_singleton_method(rb_mGC, "heap_slots_live_after_last_gc", rb_gc_heap_slots_live_after_last_gc, 0);
+ rb_define_const(rb_mGC, "HEAP_SLOT_SIZE", INT2FIX(sizeof(RVALUE)));
+
+ rb_define_singleton_method(rb_mGC, "log", rb_gc_log, 1);
View
4 patchsets/ruby/1.9.2/p320/railsexpress
@@ -0,0 +1,4 @@
+railsexpress/01-railsbench-gc-patch.patch
+railsexpress/02-display-more-detailed-stack-trace.patch
+railsexpress/03-fork-support-for-gc-logging.patch
+railsexpress/04-track-live-dataset-size.patch
Please sign in to comment.
Something went wrong with that request. Please try again.