Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #1457 from skaes/patches-for-193p374

added railsexpress patches for the latest 1.9.3 patch level (374)
  • Loading branch information...
commit 4d26302e57f43d2b8c6e9ae3960c43e8ee05c11f 2 parents a7fa0f1 + 5c6fe91
@richo richo authored
Showing with 7,824 additions and 88 deletions.
  1. +1 −1  patches/ruby/1.9.3/head/railsexpress/01-fix-make-clean.patch
  2. +41 −41 patches/ruby/1.9.3/head/railsexpress/02-railsbench-gc.patch
  3. +2 −2 patches/ruby/1.9.3/head/railsexpress/04-fork-support-for-gc-logging.patch
  4. +4 −4 patches/ruby/1.9.3/head/railsexpress/05-track-live-dataset-size.patch
  5. +11 −11 patches/ruby/1.9.3/head/railsexpress/07-export-a-few-more-symbols-for-ruby-prof.patch
  6. +5 −5 patches/ruby/1.9.3/head/railsexpress/08-thread-variables.patch
  7. +4 −4 patches/ruby/1.9.3/head/railsexpress/09-faster-loading.patch
  8. +10 −16 patches/ruby/1.9.3/head/railsexpress/10-falcon-st-opt.patch
  9. +4 −4 patches/ruby/1.9.3/head/railsexpress/11-falcon-sparse-array.patch
  10. +13 −0 patches/ruby/1.9.3/p374/railsexpress/01-fix-make-clean.patch
  11. +1,235 −0 patches/ruby/1.9.3/p374/railsexpress/02-railsbench-gc.patch
  12. +15 −0 patches/ruby/1.9.3/p374/railsexpress/03-display-more-detailed-stack-trace.patch
  13. +68 −0 patches/ruby/1.9.3/p374/railsexpress/04-fork-support-for-gc-logging.patch
  14. +45 −0 patches/ruby/1.9.3/p374/railsexpress/05-track-live-dataset-size.patch
  15. +13 −0 patches/ruby/1.9.3/p374/railsexpress/06-webrick_204_304_keep_alive_fix.patch
  16. +84 −0 patches/ruby/1.9.3/p374/railsexpress/07-export-a-few-more-symbols-for-ruby-prof.patch
  17. +319 −0 patches/ruby/1.9.3/p374/railsexpress/08-thread-variables.patch
  18. +747 −0 patches/ruby/1.9.3/p374/railsexpress/09-faster-loading.patch
  19. +2,302 −0 patches/ruby/1.9.3/p374/railsexpress/10-falcon-st-opt.patch
  20. +2,588 −0 patches/ruby/1.9.3/p374/railsexpress/11-falcon-sparse-array.patch
  21. +301 −0 patches/ruby/1.9.3/p374/railsexpress/12-falcon-array-queue.patch
  22. +12 −0 patchsets/ruby/1.9.3/p374/railsexpress
View
2  patches/ruby/1.9.3/head/railsexpress/01-fix-make-clean.patch
@@ -1,5 +1,5 @@
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
-index 08053a9..361fd08 100644
+index 7c706e8..c381a38 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -2191,7 +2191,7 @@ def init_mkmf(config = CONFIG, rbconfig = RbConfig::CONFIG)
View
82 patches/ruby/1.9.3/head/railsexpress/02-railsbench-gc.patch
@@ -14,10 +14,10 @@ index c64d981..6099a92 100644
dnl build section {
diff --git a/gc.c b/gc.c
-index e38930d..c728587 100644
+index f307ecb..0be1ec9 100644
--- a/gc.c
+++ b/gc.c
-@@ -97,6 +97,15 @@ ruby_gc_params_t initial_params = {
+@@ -98,6 +98,15 @@ ruby_gc_params_t initial_params = {
#endif
};
@@ -33,7 +33,7 @@ index e38930d..c728587 100644
#define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory]
#if SIZEOF_LONG == SIZEOF_VOIDP
-@@ -301,7 +310,7 @@ typedef struct RVALUE {
+@@ -302,7 +311,7 @@ typedef struct RVALUE {
struct RComplex complex;
} as;
#ifdef GC_DEBUG
@@ -42,7 +42,7 @@ index e38930d..c728587 100644
int line;
#endif
} RVALUE;
-@@ -371,11 +380,25 @@ typedef struct rb_objspace {
+@@ -372,11 +381,25 @@ typedef struct rb_objspace {
size_t free_min;
size_t final_num;
size_t do_heap_free;
@@ -68,7 +68,7 @@ index e38930d..c728587 100644
} flags;
struct {
st_table *table;
-@@ -392,6 +415,14 @@ typedef struct rb_objspace {
+@@ -393,6 +416,14 @@ typedef struct rb_objspace {
struct gc_list *global_list;
size_t count;
int gc_stress;
@@ -83,7 +83,7 @@ index e38930d..c728587 100644
} rb_objspace_t;
#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
-@@ -414,6 +445,16 @@ int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
+@@ -415,6 +446,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
@@ -100,7 +100,7 @@ index e38930d..c728587 100644
#define finalizer_table objspace->final.table
#define deferred_final_list objspace->final.deferred
#define global_List objspace->global_list
-@@ -421,6 +462,14 @@ int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
+@@ -422,6 +463,14 @@ int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
#define initial_malloc_limit initial_params.initial_malloc_limit
#define initial_heap_min_slots initial_params.initial_heap_min_slots
#define initial_free_min initial_params.initial_free_min
@@ -115,7 +115,7 @@ index e38930d..c728587 100644
static void rb_objspace_call_finalizer(rb_objspace_t *objspace);
-@@ -443,24 +492,59 @@ static void init_mark_stack(mark_stack_t *stack);
+@@ -444,24 +493,59 @@ static void init_mark_stack(mark_stack_t *stack);
void
rb_gc_set_params(void)
{
@@ -182,7 +182,7 @@ index e38930d..c728587 100644
if (RTEST(ruby_verbose))
fprintf(stderr, "heap_min_slots=%d (%d)\n",
heap_min_slots_i, initial_heap_min_slots);
-@@ -470,15 +554,42 @@ rb_gc_set_params(void)
+@@ -471,15 +555,42 @@ rb_gc_set_params(void)
}
}
@@ -228,7 +228,7 @@ index e38930d..c728587 100644
}
#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
-@@ -775,6 +886,11 @@ vm_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
+@@ -776,6 +887,11 @@ vm_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
mem = (size_t *)mem + 1;
#endif
@@ -240,7 +240,7 @@ index e38930d..c728587 100644
return mem;
}
-@@ -835,6 +951,13 @@ vm_xrealloc(rb_objspace_t *objspace, void *ptr, size_t size)
+@@ -836,6 +952,13 @@ vm_xrealloc(rb_objspace_t *objspace, void *ptr, size_t size)
mem = (size_t *)mem + 1;
#endif
@@ -254,7 +254,7 @@ index e38930d..c728587 100644
return mem;
}
-@@ -916,7 +1039,6 @@ ruby_xfree(void *x)
+@@ -917,7 +1040,6 @@ ruby_xfree(void *x)
vm_xfree(&rb_objspace, x);
}
@@ -262,7 +262,7 @@ index e38930d..c728587 100644
/*
* call-seq:
* GC.enable -> true or false
-@@ -962,6 +1084,455 @@ rb_gc_disable(void)
+@@ -963,6 +1085,455 @@ rb_gc_disable(void)
return old ? Qtrue : Qfalse;
}
@@ -718,7 +718,7 @@ index e38930d..c728587 100644
VALUE rb_mGC;
void
-@@ -1033,6 +1604,12 @@ allocate_sorted_heaps(rb_objspace_t *objspace, size_t next_heaps_length)
+@@ -1034,6 +1605,12 @@ allocate_sorted_heaps(rb_objspace_t *objspace, size_t next_heaps_length)
static void
assign_heap_slot(rb_objspace_t *objspace)
{
@@ -731,7 +731,7 @@ index e38930d..c728587 100644
RVALUE *p, *pend, *membase;
struct heaps_slot *slot;
size_t hi, lo, mid;
-@@ -1094,6 +1671,7 @@ assign_heap_slot(rb_objspace_t *objspace)
+@@ -1095,6 +1672,7 @@ assign_heap_slot(rb_objspace_t *objspace)
if (lomem == 0 || lomem > p) lomem = p;
if (himem < pend) himem = pend;
heaps_used++;
@@ -739,7 +739,7 @@ index e38930d..c728587 100644
while (p < pend) {
p->as.free.flags = 0;
-@@ -1150,7 +1728,7 @@ initial_expand_heap(rb_objspace_t *objspace)
+@@ -1151,7 +1729,7 @@ initial_expand_heap(rb_objspace_t *objspace)
static void
set_heaps_increment(rb_objspace_t *objspace)
{
@@ -748,7 +748,7 @@ index e38930d..c728587 100644
if (next_heaps_length == heaps_used) {
next_heaps_length++;
-@@ -1183,6 +1761,22 @@ rb_during_gc(void)
+@@ -1184,6 +1762,22 @@ rb_during_gc(void)
#define RANY(o) ((RVALUE*)(o))
@@ -771,7 +771,7 @@ index e38930d..c728587 100644
VALUE
rb_newobj(void)
{
-@@ -1214,9 +1808,11 @@ rb_newobj(void)
+@@ -1215,9 +1809,11 @@ rb_newobj(void)
MEMZERO((void*)obj, RVALUE, 1);
#ifdef GC_DEBUG
@@ -784,7 +784,7 @@ index e38930d..c728587 100644
GC_PROF_INC_LIVE_NUM;
return obj;
-@@ -1768,6 +2364,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
+@@ -1769,6 +2365,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
{
register RVALUE *obj = RANY(ptr);
@@ -797,7 +797,7 @@ index e38930d..c728587 100644
goto marking; /* skip */
again:
-@@ -1778,6 +2380,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
+@@ -1779,6 +2381,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
obj->as.basic.flags |= FL_MARK;
objspace->heap.live_num++;
@@ -810,7 +810,7 @@ index e38930d..c728587 100644
marking:
if (FL_TEST(obj, FL_EXIVAR)) {
rb_mark_generic_ivar(ptr);
-@@ -2120,6 +2728,25 @@ free_unused_heaps(rb_objspace_t *objspace)
+@@ -2121,6 +2729,25 @@ free_unused_heaps(rb_objspace_t *objspace)
}
}
@@ -836,7 +836,7 @@ index e38930d..c728587 100644
static void
slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
{
-@@ -2127,14 +2754,23 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+@@ -2128,14 +2755,23 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
RVALUE *p, *pend;
RVALUE *free = freelist, *final = deferred_final_list;
int deferred;
@@ -860,7 +860,7 @@ index e38930d..c728587 100644
p->as.free.flags = T_ZOMBIE;
RDATA(p)->dfree = 0;
}
-@@ -2144,6 +2780,10 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+@@ -2145,6 +2781,10 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
final_num++;
}
else {
@@ -871,7 +871,7 @@ index e38930d..c728587 100644
add_freelist(objspace, p);
free_num++;
}
-@@ -2151,13 +2791,22 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+@@ -2152,13 +2792,22 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
else if (BUILTIN_TYPE(p) == T_ZOMBIE) {
/* objects to be finalized */
/* do nothing remain marked */
@@ -895,7 +895,7 @@ index e38930d..c728587 100644
objspace->heap.free_num > objspace->heap.do_heap_free) {
RVALUE *pp;
-@@ -2168,6 +2817,8 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+@@ -2169,6 +2818,8 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
sweep_slot->limit = final_num;
freelist = free; /* cancel this page from freelist */
unlink_heap_slot(objspace, sweep_slot);
@@ -904,7 +904,7 @@ index e38930d..c728587 100644
}
else {
objspace->heap.free_num += free_num;
-@@ -2180,6 +2831,10 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+@@ -2181,6 +2832,10 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
RUBY_VM_SET_FINALIZER_INTERRUPT(th);
}
}
@@ -915,7 +915,7 @@ index e38930d..c728587 100644
}
static int
-@@ -2200,6 +2855,21 @@ ready_to_gc(rb_objspace_t *objspace)
+@@ -2201,6 +2856,21 @@ ready_to_gc(rb_objspace_t *objspace)
static void
before_gc_sweep(rb_objspace_t *objspace)
{
@@ -937,7 +937,7 @@ index e38930d..c728587 100644
freelist = 0;
objspace->heap.do_heap_free = (size_t)((heaps_used * HEAP_OBJ_LIMIT) * 0.65);
objspace->heap.free_min = (size_t)((heaps_used * HEAP_OBJ_LIMIT) * 0.2);
-@@ -2219,8 +2889,13 @@ before_gc_sweep(rb_objspace_t *objspace)
+@@ -2220,8 +2890,13 @@ before_gc_sweep(rb_objspace_t *objspace)
static void
after_gc_sweep(rb_objspace_t *objspace)
{
@@ -951,7 +951,7 @@ index e38930d..c728587 100644
if (objspace->heap.free_num < objspace->heap.free_min) {
set_heaps_increment(objspace);
heaps_increment(objspace);
-@@ -2233,6 +2908,29 @@ after_gc_sweep(rb_objspace_t *objspace)
+@@ -2234,6 +2909,29 @@ after_gc_sweep(rb_objspace_t *objspace)
malloc_increase = 0;
free_unused_heaps(objspace);
@@ -981,7 +981,7 @@ index e38930d..c728587 100644
}
static int
-@@ -2266,9 +2964,11 @@ rest_sweep(rb_objspace_t *objspace)
+@@ -2267,9 +2965,11 @@ rest_sweep(rb_objspace_t *objspace)
static void gc_marks(rb_objspace_t *objspace);
@@ -993,7 +993,7 @@ index e38930d..c728587 100644
int res;
INIT_GC_PROF_PARAMS;
-@@ -2290,7 +2990,6 @@ gc_lazy_sweep(rb_objspace_t *objspace)
+@@ -2291,7 +2991,6 @@ gc_lazy_sweep(rb_objspace_t *objspace)
GC_PROF_TIMER_STOP(Qfalse);
return res;
}
@@ -1001,7 +1001,7 @@ index e38930d..c728587 100644
}
else {
if (heaps_increment(objspace)) {
-@@ -2298,6 +2997,18 @@ gc_lazy_sweep(rb_objspace_t *objspace)
+@@ -2299,6 +2998,18 @@ gc_lazy_sweep(rb_objspace_t *objspace)
return TRUE;
}
}
@@ -1020,7 +1020,7 @@ index e38930d..c728587 100644
gc_marks(objspace);
-@@ -2306,6 +3017,10 @@ gc_lazy_sweep(rb_objspace_t *objspace)
+@@ -2307,6 +3018,10 @@ gc_lazy_sweep(rb_objspace_t *objspace)
set_heaps_increment(objspace);
}
@@ -1031,7 +1031,7 @@ index e38930d..c728587 100644
GC_PROF_SWEEP_TIMER_START;
if(!(res = lazy_sweep(objspace))) {
after_gc_sweep(objspace);
-@@ -2317,6 +3032,7 @@ gc_lazy_sweep(rb_objspace_t *objspace)
+@@ -2318,6 +3033,7 @@ gc_lazy_sweep(rb_objspace_t *objspace)
GC_PROF_SWEEP_TIMER_STOP;
GC_PROF_TIMER_STOP(Qtrue);
@@ -1039,7 +1039,7 @@ index e38930d..c728587 100644
return res;
}
-@@ -2543,9 +3259,15 @@ gc_marks(rb_objspace_t *objspace)
+@@ -2544,9 +3260,15 @@ gc_marks(rb_objspace_t *objspace)
rb_thread_t *th = GET_THREAD();
GC_PROF_MARK_TIMER_START;
@@ -1056,7 +1056,7 @@ index e38930d..c728587 100644
SET_STACK_END;
-@@ -2577,11 +3299,15 @@ gc_marks(rb_objspace_t *objspace)
+@@ -2578,11 +3300,15 @@ gc_marks(rb_objspace_t *objspace)
gc_mark_stacked_objects(objspace);
GC_PROF_MARK_TIMER_STOP;
@@ -1072,7 +1072,7 @@ index e38930d..c728587 100644
INIT_GC_PROF_PARAMS;
if (GC_NOTIFY) printf("start garbage_collect()\n");
-@@ -2597,15 +3323,31 @@ garbage_collect(rb_objspace_t *objspace)
+@@ -2598,15 +3324,31 @@ garbage_collect(rb_objspace_t *objspace)
rest_sweep(objspace);
@@ -1104,7 +1104,7 @@ index e38930d..c728587 100644
return TRUE;
}
-@@ -3094,6 +3836,39 @@ rb_gc_call_finalizer_at_exit(void)
+@@ -3095,6 +3837,39 @@ rb_gc_call_finalizer_at_exit(void)
rb_objspace_call_finalizer(&rb_objspace);
}
@@ -1144,7 +1144,7 @@ index e38930d..c728587 100644
static void
rb_objspace_call_finalizer(rb_objspace_t *objspace)
{
-@@ -3408,6 +4183,49 @@ count_objects(int argc, VALUE *argv, VALUE os)
+@@ -3403,6 +4178,49 @@ count_objects(int argc, VALUE *argv, VALUE os)
return hash;
}
@@ -1194,7 +1194,7 @@ index e38930d..c728587 100644
/*
* call-seq:
* GC.count -> Integer
-@@ -3700,6 +4518,28 @@ Init_GC(void)
+@@ -3695,6 +4513,28 @@ Init_GC(void)
rb_define_singleton_method(rb_mGC, "stat", gc_stat, -1);
rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
@@ -1223,7 +1223,7 @@ index e38930d..c728587 100644
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);
-@@ -3713,6 +4553,9 @@ Init_GC(void)
+@@ -3708,6 +4548,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);
View
4 patches/ruby/1.9.3/head/railsexpress/04-fork-support-for-gc-logging.patch
@@ -1,8 +1,8 @@
diff --git a/gc.c b/gc.c
-index c728587..6b6c9f0 100644
+index 0be1ec9..c198770 100644
--- a/gc.c
+++ b/gc.c
-@@ -1344,6 +1344,34 @@ rb_gc_log_file(int argc, VALUE *argv, VALUE self)
+@@ -1345,6 +1345,34 @@ rb_gc_log_file(int argc, VALUE *argv, VALUE self)
}
/*
View
8 patches/ruby/1.9.3/head/railsexpress/05-track-live-dataset-size.patch
@@ -1,8 +1,8 @@
diff --git a/gc.c b/gc.c
-index 6b6c9f0..c995568 100644
+index c198770..68dd48a 100644
--- a/gc.c
+++ b/gc.c
-@@ -280,7 +280,6 @@ getrusage_time(void)
+@@ -281,7 +281,6 @@ getrusage_time(void)
#define GC_PROF_DEC_LIVE_NUM
#endif
@@ -10,7 +10,7 @@ index 6b6c9f0..c995568 100644
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
#pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
#endif
-@@ -1561,6 +1560,24 @@ rb_gc_time()
+@@ -1562,6 +1561,24 @@ rb_gc_time()
#endif
}
@@ -35,7 +35,7 @@ index 6b6c9f0..c995568 100644
VALUE rb_mGC;
void
-@@ -4553,6 +4570,7 @@ Init_GC(void)
+@@ -4548,6 +4565,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);
View
22 patches/ruby/1.9.3/head/railsexpress/07-export-a-few-more-symbols-for-ruby-prof.patch
@@ -1,8 +1,8 @@
diff --git a/gc.c b/gc.c
-index c995568..52b2c56 100644
+index 68dd48a..e8e73c0 100644
--- a/gc.c
+++ b/gc.c
-@@ -1051,6 +1051,7 @@ ruby_xfree(void *x)
+@@ -1052,6 +1052,7 @@ ruby_xfree(void *x)
*
*/
@@ -10,7 +10,7 @@ index c995568..52b2c56 100644
VALUE
rb_gc_enable(void)
{
-@@ -1073,6 +1074,7 @@ rb_gc_enable(void)
+@@ -1074,6 +1075,7 @@ rb_gc_enable(void)
*
*/
@@ -18,7 +18,7 @@ index c995568..52b2c56 100644
VALUE
rb_gc_disable(void)
{
-@@ -1095,6 +1097,7 @@ rb_gc_disable(void)
+@@ -1096,6 +1098,7 @@ rb_gc_disable(void)
*
*/
@@ -26,7 +26,7 @@ index c995568..52b2c56 100644
VALUE
rb_gc_enable_stats()
{
-@@ -1116,6 +1119,7 @@ rb_gc_enable_stats()
+@@ -1117,6 +1120,7 @@ rb_gc_enable_stats()
*
*/
@@ -34,7 +34,7 @@ index c995568..52b2c56 100644
VALUE
rb_gc_disable_stats()
{
-@@ -1135,6 +1139,7 @@ rb_gc_disable_stats()
+@@ -1136,6 +1140,7 @@ rb_gc_disable_stats()
*
*/
@@ -42,7 +42,7 @@ index c995568..52b2c56 100644
VALUE
rb_gc_stats_enabled()
{
-@@ -1154,6 +1159,7 @@ rb_gc_stats_enabled()
+@@ -1155,6 +1160,7 @@ rb_gc_stats_enabled()
*
*/
@@ -50,7 +50,7 @@ index c995568..52b2c56 100644
VALUE
rb_gc_clear_stats()
{
-@@ -1221,6 +1227,7 @@ rb_gc_num_allocations()
+@@ -1222,6 +1228,7 @@ rb_gc_num_allocations()
*
*/
@@ -58,7 +58,7 @@ index c995568..52b2c56 100644
VALUE
rb_gc_enable_trace()
{
-@@ -1242,6 +1249,7 @@ rb_gc_enable_trace()
+@@ -1243,6 +1250,7 @@ rb_gc_enable_trace()
*
*/
@@ -66,7 +66,7 @@ index c995568..52b2c56 100644
VALUE
rb_gc_disable_trace()
{
-@@ -1261,6 +1269,7 @@ rb_gc_disable_trace()
+@@ -1262,6 +1270,7 @@ rb_gc_disable_trace()
*
*/
@@ -74,7 +74,7 @@ index c995568..52b2c56 100644
VALUE
rb_gc_trace_enabled()
{
-@@ -1289,6 +1298,7 @@ const char* GC_LOGFILE_IVAR = "@gc_logfile_name";
+@@ -1290,6 +1299,7 @@ const char* GC_LOGFILE_IVAR = "@gc_logfile_name";
*
*/
View
10 patches/ruby/1.9.3/head/railsexpress/08-thread-variables.patch
@@ -104,7 +104,7 @@ index e8cb3b1..9ff491d 100644
m = Mutex.new
r = 0
diff --git a/thread.c b/thread.c
-index 5393e4b..6a915d6 100644
+index c7da34b..ce1fa3f 100644
--- a/thread.c
+++ b/thread.c
@@ -2112,7 +2112,9 @@ rb_thread_local_aset(VALUE thread, ID id, VALUE val)
@@ -286,7 +286,7 @@ index 5393e4b..6a915d6 100644
/*
* call-seq:
* thgrp.list -> array
-@@ -4686,6 +4835,10 @@ Init_Thread(void)
+@@ -4680,6 +4829,10 @@ Init_Thread(void)
rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
rb_define_method(rb_cThread, "status", rb_thread_status, 0);
@@ -298,10 +298,10 @@ index 5393e4b..6a915d6 100644
rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
diff --git a/vm.c b/vm.c
-index 4dd242f..f00b193 100644
+index 4c44aa4..1517e73 100644
--- a/vm.c
+++ b/vm.c
-@@ -1900,6 +1900,7 @@ ruby_thread_init(VALUE self)
+@@ -1909,6 +1909,7 @@ ruby_thread_init(VALUE self)
GetThreadPtr(self, th);
th_init(th, self);
@@ -309,7 +309,7 @@ index 4dd242f..f00b193 100644
th->vm = vm;
th->top_wrapper = 0;
-@@ -2168,6 +2169,7 @@ Init_VM(void)
+@@ -2177,6 +2178,7 @@ Init_VM(void)
/* create main thread */
th_self = th->self = TypedData_Wrap_Struct(rb_cThread, &thread_data_type, th);
View
8 patches/ruby/1.9.3/head/railsexpress/09-faster-loading.patch
@@ -712,10 +712,10 @@ index 58a9ee2..ec75096 100644
+ end
end
diff --git a/vm.c b/vm.c
-index f00b193..4a97fba 100644
+index 1517e73..04ef956 100644
--- a/vm.c
+++ b/vm.c
-@@ -1582,7 +1582,12 @@ rb_vm_mark(void *ptr)
+@@ -1591,7 +1591,12 @@ rb_vm_mark(void *ptr)
RUBY_MARK_UNLESS_NULL(vm->thgroup_default);
RUBY_MARK_UNLESS_NULL(vm->mark_object_ary);
RUBY_MARK_UNLESS_NULL(vm->load_path);
@@ -729,10 +729,10 @@ index f00b193..4a97fba 100644
RUBY_MARK_UNLESS_NULL(vm->coverages);
rb_gc_mark_locations(vm->special_exceptions, vm->special_exceptions + ruby_special_error_count);
diff --git a/vm_core.h b/vm_core.h
-index 1a10162..c6829a3 100644
+index dfc0e3c..70c5f5c 100644
--- a/vm_core.h
+++ b/vm_core.h
-@@ -298,7 +298,12 @@ typedef struct rb_vm_struct {
+@@ -299,7 +299,12 @@ typedef struct rb_vm_struct {
/* load */
VALUE top_self;
VALUE load_path;
View
26 patches/ruby/1.9.3/head/railsexpress/10-falcon-st-opt.patch
@@ -49,10 +49,10 @@ index e186cd4..53d9e1b 100644
void
diff --git a/gc.c b/gc.c
-index 52b2c56..7f78316 100644
+index e8e73c0..dfb5473 100644
--- a/gc.c
+++ b/gc.c
-@@ -20,11 +20,13 @@
+@@ -20,6 +20,7 @@
#include "vm_core.h"
#include "internal.h"
#include "gc.h"
@@ -60,13 +60,7 @@ index 52b2c56..7f78316 100644
#include "constant.h"
#include "ruby_atomic.h"
#include <stdio.h>
- #include <setjmp.h>
- #include <sys/types.h>
-+#include <assert.h>
-
- #ifdef HAVE_SYS_TIME_H
- #include <sys/time.h>
-@@ -36,7 +38,12 @@
+@@ -37,7 +38,12 @@
#if defined _WIN32 || defined __CYGWIN__
#include <windows.h>
@@ -79,7 +73,7 @@ index 52b2c56..7f78316 100644
#ifdef HAVE_VALGRIND_MEMCHECK_H
# include <valgrind/memcheck.h>
-@@ -355,6 +362,24 @@ typedef struct mark_stack {
+@@ -356,6 +362,24 @@ typedef struct mark_stack {
#define CALC_EXACT_MALLOC_SIZE 0
@@ -104,7 +98,7 @@ index 52b2c56..7f78316 100644
typedef struct rb_objspace {
struct {
size_t limit;
-@@ -364,6 +389,9 @@ typedef struct rb_objspace {
+@@ -365,6 +389,9 @@ typedef struct rb_objspace {
size_t allocations;
#endif
} malloc_params;
@@ -114,7 +108,7 @@ index 52b2c56..7f78316 100644
struct {
size_t increment;
struct heaps_slot *ptr;
-@@ -429,7 +457,11 @@ typedef struct rb_objspace {
+@@ -430,7 +457,11 @@ typedef struct rb_objspace {
#define ruby_initial_gc_stress initial_params.gc_stress
int *ruby_initial_gc_stress_ptr = &ruby_initial_gc_stress;
#else
@@ -126,7 +120,7 @@ index 52b2c56..7f78316 100644
int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
#endif
#define malloc_limit objspace->malloc_params.limit
-@@ -480,6 +512,10 @@ rb_objspace_alloc(void)
+@@ -481,6 +512,10 @@ rb_objspace_alloc(void)
memset(objspace, 0, sizeof(*objspace));
malloc_limit = initial_malloc_limit;
ruby_gc_stress = ruby_initial_gc_stress;
@@ -137,7 +131,7 @@ index 52b2c56..7f78316 100644
return objspace;
}
-@@ -623,6 +659,13 @@ rb_objspace_free(rb_objspace_t *objspace)
+@@ -624,6 +659,13 @@ rb_objspace_free(rb_objspace_t *objspace)
heaps = 0;
}
free_stack_chunks(&objspace->mark_stack);
@@ -151,7 +145,7 @@ index 52b2c56..7f78316 100644
free(objspace);
}
#endif
-@@ -1147,6 +1190,27 @@ rb_gc_stats_enabled()
+@@ -1148,6 +1190,27 @@ rb_gc_stats_enabled()
return gc_statistics ? Qtrue : Qfalse;
}
@@ -179,7 +173,7 @@ index 52b2c56..7f78316 100644
/*
* call-seq:
-@@ -1656,6 +1720,55 @@ allocate_sorted_heaps(rb_objspace_t *objspace, size_t next_heaps_length)
+@@ -1657,6 +1720,55 @@ allocate_sorted_heaps(rb_objspace_t *objspace, size_t next_heaps_length)
heaps_length = next_heaps_length;
}
View
8 patches/ruby/1.9.3/head/railsexpress/11-falcon-sparse-array.patch
@@ -493,7 +493,7 @@ index 66e33a3..4b31f92 100644
case T_STRING:
size += rb_str_memsize(obj);
diff --git a/gc.c b/gc.c
-index 7f78316..95875da 100644
+index dfb5473..48774cc 100644
--- a/gc.c
+++ b/gc.c
@@ -688,7 +688,7 @@ rb_objspace_free(rb_objspace_t *objspace)
@@ -874,7 +874,7 @@ index 9229896..2fecd57 100644
#endif /* METHOD_H */
diff --git a/object.c b/object.c
-index e8cb328..d99323f 100644
+index 59611fd..4522d08 100644
--- a/object.c
+++ b/object.c
@@ -236,17 +236,8 @@ init_copy(VALUE dest, VALUE obj)
@@ -2371,10 +2371,10 @@ index 3da500e..4a61453 100644
}
if (rb_cvar_defined(mod, id)) {
diff --git a/vm.c b/vm.c
-index 4a97fba..ecfa90d 100644
+index 04ef956..6edabf6 100644
--- a/vm.c
+++ b/vm.c
-@@ -1046,7 +1046,7 @@ static void
+@@ -1055,7 +1055,7 @@ static void
add_opt_method(VALUE klass, ID mid, VALUE bop)
{
rb_method_entry_t *me;
View
13 patches/ruby/1.9.3/p374/railsexpress/01-fix-make-clean.patch
@@ -0,0 +1,13 @@
+diff --git a/lib/mkmf.rb b/lib/mkmf.rb
+index 7c706e8..c381a38 100644
+--- a/lib/mkmf.rb
++++ b/lib/mkmf.rb
+@@ -2191,7 +2191,7 @@ def init_mkmf(config = CONFIG, rbconfig = RbConfig::CONFIG)
+ $LOCAL_LIBS = ""
+
+ $cleanfiles = config_string('CLEANFILES') {|s| Shellwords.shellwords(s)} || []
+- $cleanfiles << "mkmf.log"
++ $cleanfiles << "mkmf.log .*.time"
+ $distcleanfiles = config_string('DISTCLEANFILES') {|s| Shellwords.shellwords(s)} || []
+ $distcleandirs = config_string('DISTCLEANDIRS') {|s| Shellwords.shellwords(s)} || []
+
View
1,235 patches/ruby/1.9.3/p374/railsexpress/02-railsbench-gc.patch
@@ -0,0 +1,1235 @@
+diff --git a/configure.in b/configure.in
+index c64d981..6099a92 100644
+--- a/configure.in
++++ b/configure.in
+@@ -2416,6 +2416,10 @@ if test "$EXEEXT" = .exe; then
+ AC_SUBST(EXECUTABLE_EXTS)
+ fi
+
++dnl enable gc debugging
++AC_ARG_ENABLE(gcdebug,
++ AS_HELP_STRING([--enable-gcdebug], [build garbage collector with debugging enabled]),
++ [AC_DEFINE(GC_DEBUG,1)])
+ dnl }
+ dnl build section {
+
+diff --git a/gc.c b/gc.c
+index f307ecb..0be1ec9 100644
+--- a/gc.c
++++ b/gc.c
+@@ -98,6 +98,15 @@ ruby_gc_params_t initial_params = {
+ #endif
+ };
+
++#ifndef HAVE_LONG_LONG
++#define LONG_LONG long
++#endif
++
++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;
++
+ #define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory]
+
+ #if SIZEOF_LONG == SIZEOF_VOIDP
+@@ -302,7 +311,7 @@ typedef struct RVALUE {
+ struct RComplex complex;
+ } as;
+ #ifdef GC_DEBUG
+- const char *file;
++ VALUE file;
+ int line;
+ #endif
+ } RVALUE;
+@@ -372,11 +381,25 @@ typedef struct rb_objspace {
+ size_t free_min;
+ size_t final_num;
+ size_t do_heap_free;
++ unsigned long max_blocks_to_free;
++ unsigned long freed_blocks;
+ } heap;
+ struct {
++ unsigned long processed;
++ unsigned long freed_objects;
++ unsigned long freelist_size;
++ unsigned long zombies;
++ unsigned long free_counts[T_MASK+1];
++ unsigned long live_counts[T_MASK+1];
++ unsigned long gc_time_accumulator_before_gc;
++ unsigned long live_after_last_mark_phase;
++ } stats;
++ struct {
+ int dont_gc;
+ int dont_lazy_sweep;
+ int during_gc;
++ int gc_statistics;
++ int verbose_gc_stats;
+ } flags;
+ struct {
+ st_table *table;
+@@ -393,6 +416,14 @@ typedef struct rb_objspace {
+ struct gc_list *global_list;
+ size_t 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
+@@ -415,6 +446,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 global_List objspace->global_list
+@@ -422,6 +463,14 @@ int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
+ #define initial_malloc_limit initial_params.initial_malloc_limit
+ #define initial_heap_min_slots initial_params.initial_heap_min_slots
+ #define initial_free_min initial_params.initial_free_min
++#define free_counts objspace->stats.free_counts
++#define live_counts objspace->stats.live_counts
++#define processed objspace->stats.processed
++#define zombies objspace->stats.zombies
++#define freelist_size objspace->stats.freelist_size
++#define freed_objects objspace->stats.freed_objects
++#define gc_time_accumulator_before_gc objspace->stats.gc_time_accumulator_before_gc
++#define live_after_last_mark_phase objspace->stats.live_after_last_mark_phase
+
+ static void rb_objspace_call_finalizer(rb_objspace_t *objspace);
+
+@@ -444,24 +493,59 @@ static void init_mark_stack(mark_stack_t *stack);
+ void
+ rb_gc_set_params(void)
+ {
+- char *malloc_limit_ptr, *heap_min_slots_ptr, *free_min_ptr;
++ char *envp;
++
++ rb_objspace_t *objspace = &rb_objspace;
++
++ gc_data_file = stderr;
+
+ if (rb_safe_level() > 0) return;
+
+- malloc_limit_ptr = getenv("RUBY_GC_MALLOC_LIMIT");
+- if (malloc_limit_ptr != NULL) {
+- int malloc_limit_i = atoi(malloc_limit_ptr);
++ envp = getenv("RUBY_GC_STATS");
++ if (envp != NULL) {
++ int i = atoi(envp);
++ if (i > 0) {
++ verbose_gc_stats = 1;
++ fprintf(stderr, "RUBY_GC_STATS=%d\n", verbose_gc_stats);
++ }
++ /* 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_GC_MALLOC_LIMIT");
++ if (envp != NULL) {
++ int malloc_limit_i = atoi(envp);
++ if (verbose_gc_stats) {
++ fprintf(gc_data_file, "RUBY_GC_MALLOC_LIMIT=%s\n", envp);
++ }
+ if (RTEST(ruby_verbose))
+ fprintf(stderr, "malloc_limit=%d (%d)\n",
+ malloc_limit_i, initial_malloc_limit);
+ if (malloc_limit_i > 0) {
+ initial_malloc_limit = malloc_limit_i;
++ // malloc_limit = initial_malloc_limit;
+ }
+ }
+
+- heap_min_slots_ptr = getenv("RUBY_HEAP_MIN_SLOTS");
+- if (heap_min_slots_ptr != NULL) {
+- int heap_min_slots_i = atoi(heap_min_slots_ptr);
++ envp = getenv("RUBY_HEAP_MIN_SLOTS");
++ if (envp != NULL) {
++ int heap_min_slots_i = atoi(envp);
++ if (verbose_gc_stats) {
++ fprintf(gc_data_file, "RUBY_HEAP_MIN_SLOTS=%s\n", envp);
++ }
+ if (RTEST(ruby_verbose))
+ fprintf(stderr, "heap_min_slots=%d (%d)\n",
+ heap_min_slots_i, initial_heap_min_slots);
+@@ -471,15 +555,42 @@ rb_gc_set_params(void)
+ }
+ }
+
+- free_min_ptr = getenv("RUBY_FREE_MIN");
+- if (free_min_ptr != NULL) {
+- int free_min_i = atoi(free_min_ptr);
++ if (!(envp = getenv("RUBY_FREE_MIN")))
++ envp = getenv("RUBY_HEAP_FREE_MIN");
++ if (envp != NULL) {
++ int free_min_i = atoi(envp);
++ if (verbose_gc_stats) {
++ fprintf(gc_data_file, "RUBY_HEAP_FREE_MIN=%s\n", envp);
++ }
+ if (RTEST(ruby_verbose))
+ fprintf(stderr, "free_min=%d (%d)\n", free_min_i, initial_free_min);
+ if (free_min_i > 0) {
+ initial_free_min = 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;
++ }
++ }
++
++ fflush(gc_data_file);
+ }
+
+ #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
+@@ -776,6 +887,11 @@ vm_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
+ mem = (size_t *)mem + 1;
+ #endif
+
++ if (gc_statistics) {
++ gc_allocated_size += size;
++ gc_num_allocations += 1;
++ }
++
+ return mem;
+ }
+
+@@ -836,6 +952,13 @@ vm_xrealloc(rb_objspace_t *objspace, void *ptr, size_t size)
+ mem = (size_t *)mem + 1;
+ #endif
+
++ /* TODO: we can't count correctly unless we store old size on heap
++ if (gc_statistics) {
++ gc_allocated_size += size;
++ gc_num_allocations += 1;
++ }
++ */
++
+ return mem;
+ }
+
+@@ -917,7 +1040,6 @@ ruby_xfree(void *x)
+ vm_xfree(&rb_objspace, x);
+ }
+
+-
+ /*
+ * call-seq:
+ * GC.enable -> true or false
+@@ -963,6 +1085,455 @@ 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_time_accumulator_before_gc = 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 = objspace->heap.sorted[i].slot->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 = objspace->heap.sorted[i].start; pend = objspace->heap.sorted[i].end;
++ 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
+@@ -1034,6 +1605,12 @@ allocate_sorted_heaps(rb_objspace_t *objspace, size_t next_heaps_length)
+ static void
+ assign_heap_slot(rb_objspace_t *objspace)
+ {
++ /*
++ if (gc_statistics & verbose_gc_stats) {
++ fprintf(gc_data_file, "assigning heap slot\n");
++ }
++ */
++
+ RVALUE *p, *pend, *membase;
+ struct heaps_slot *slot;
+ size_t hi, lo, mid;
+@@ -1095,6 +1672,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;
+@@ -1151,7 +1729,7 @@ initial_expand_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++;
+@@ -1184,6 +1762,22 @@ rb_during_gc(void)
+
+ #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
++
+ VALUE
+ rb_newobj(void)
+ {
+@@ -1215,9 +1809,11 @@ rb_newobj(void)
+
+ 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++;
+ GC_PROF_INC_LIVE_NUM;
+
+ return obj;
+@@ -1769,6 +2365,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
+ {
+ 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:
+@@ -1779,6 +2381,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
+ obj->as.basic.flags |= FL_MARK;
+ objspace->heap.live_num++;
+
++#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);
+@@ -2121,6 +2729,25 @@ free_unused_heaps(rb_objspace_t *objspace)
+ }
+ }
+
++static inline unsigned long
++elapsed_musecs(struct timeval since)
++{
++ struct timeval now;
++ struct timeval temp;
++
++ gettimeofday(&now, NULL);
++
++ if ((now.tv_usec-since.tv_usec)<0) {
++ temp.tv_sec = now.tv_sec-since.tv_sec-1;
++ temp.tv_usec = 1000000+now.tv_usec-since.tv_usec;
++ } else {
++ temp.tv_sec = now.tv_sec-since.tv_sec;
++ temp.tv_usec = now.tv_usec-since.tv_usec;
++ }
++
++ return temp.tv_sec*1000000 + temp.tv_usec;
++}
++
+ static void
+ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+ {
+@@ -2128,14 +2755,23 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+ RVALUE *p, *pend;
+ RVALUE *free = freelist, *final = deferred_final_list;
+ int deferred;
++ int do_gc_stats = gc_statistics & verbose_gc_stats;
++
++ struct timeval tv1;
++ if (gc_statistics) gettimeofday(&tv1, NULL);
+
+ p = sweep_slot->slot; pend = p + sweep_slot->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)))) {
+ if (!deferred) {
++ if (do_gc_stats) zombies++;
+ p->as.free.flags = T_ZOMBIE;
+ RDATA(p)->dfree = 0;
+ }
+@@ -2145,6 +2781,10 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+ 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++;
+ }
+@@ -2152,13 +2792,22 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+ 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;
++ if (do_gc_stats) {
++ live_counts[p->as.basic.flags & T_MASK]++;
++ }
+ }
+ p++;
++ processed++;
+ }
+- if (final_num + free_num == sweep_slot->limit &&
++
++ freed_objects += free_num;
++
++ if (objspace->heap.freed_blocks < objspace->heap.max_blocks_to_free &&
++ final_num + free_num == sweep_slot->limit &&
+ objspace->heap.free_num > objspace->heap.do_heap_free) {
+ RVALUE *pp;
+
+@@ -2169,6 +2818,8 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+ sweep_slot->limit = final_num;
+ freelist = free; /* cancel this page from freelist */
+ unlink_heap_slot(objspace, sweep_slot);
++ objspace->heap.freed_blocks += 1;
++ heap_size -= final_num + free_num;
+ }
+ else {
+ objspace->heap.free_num += free_num;
+@@ -2181,6 +2832,10 @@ slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
+ RUBY_VM_SET_FINALIZER_INTERRUPT(th);
+ }
+ }
++
++ if (gc_statistics) {
++ gc_time_accumulator += elapsed_musecs(tv1);
++ }
+ }
+
+ static int
+@@ -2201,6 +2856,21 @@ ready_to_gc(rb_objspace_t *objspace)
+ static void
+ before_gc_sweep(rb_objspace_t *objspace)
+ {
++ if (gc_statistics & verbose_gc_stats) {
++ /*
++ fprintf(gc_data_file, "Sweep started\n");
++ */
++ freed_objects = 0;
++ processed = 0;
++ zombies = 0;
++ freelist_size = 0;
++ MEMZERO((void*)free_counts, unsigned long, T_MASK+1);
++ MEMZERO((void*)live_counts, unsigned long, T_MASK+1);
++ }
++
++ objspace->heap.max_blocks_to_free = heaps_used - (heap_min_slots / HEAP_OBJ_LIMIT);
++ objspace->heap.freed_blocks = 0;
++
+ freelist = 0;
+ objspace->heap.do_heap_free = (size_t)((heaps_used * HEAP_OBJ_LIMIT) * 0.65);
+ objspace->heap.free_min = (size_t)((heaps_used * HEAP_OBJ_LIMIT) * 0.2);
+@@ -2220,8 +2890,13 @@ before_gc_sweep(rb_objspace_t *objspace)
+ static void
+ after_gc_sweep(rb_objspace_t *objspace)
+ {
++ int i;
++ struct timeval tv1;
++
+ GC_PROF_SET_MALLOC_INFO;
+
++ if (gc_statistics) gettimeofday(&tv1, NULL);
++
+ if (objspace->heap.free_num < objspace->heap.free_min) {
+ set_heaps_increment(objspace);
+ heaps_increment(objspace);
+@@ -2234,6 +2909,29 @@ after_gc_sweep(rb_objspace_t *objspace)
+ malloc_increase = 0;
+
+ free_unused_heaps(objspace);
++
++ if (gc_statistics) {
++ gc_time_accumulator += elapsed_musecs(tv1);
++
++ if (verbose_gc_stats) {
++ /* log gc stats if requested */
++ fprintf(gc_data_file, "GC time: %lu musec\n", (unsigned long)(gc_time_accumulator-gc_time_accumulator_before_gc));
++ fprintf(gc_data_file, "objects processed: %7lu\n", (unsigned long)processed);
++ fprintf(gc_data_file, "live objects : %7lu\n", (unsigned long)live_after_last_mark_phase);
++ fprintf(gc_data_file, "freelist objects : %7lu\n", (unsigned long)freelist_size);
++ fprintf(gc_data_file, "freed objects : %7lu\n", (unsigned long)freed_objects);
++ 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);
++ }
++ }
+ }
+
+ static int
+@@ -2267,9 +2965,11 @@ rest_sweep(rb_objspace_t *objspace)
+
+ static void gc_marks(rb_objspace_t *objspace);
+
++/* only called from rb_new_obj */
+ static int
+ gc_lazy_sweep(rb_objspace_t *objspace)
+ {
++ struct timeval gctv1;
+ int res;
+ INIT_GC_PROF_PARAMS;
+
+@@ -2291,7 +2991,6 @@ gc_lazy_sweep(rb_objspace_t *objspace)
+ GC_PROF_TIMER_STOP(Qfalse);
+ return res;
+ }
+- after_gc_sweep(objspace);
+ }
+ else {
+ if (heaps_increment(objspace)) {
+@@ -2299,6 +2998,18 @@ gc_lazy_sweep(rb_objspace_t *objspace)
+ return TRUE;
+ }
+ }
++ after_gc_sweep(objspace);
++
++ if (gc_statistics) {
++ gc_time_accumulator_before_gc = gc_time_accumulator;
++ gc_collections++;
++ gettimeofday(&gctv1, NULL);
++ /*
++ if (verbose_gc_stats) {
++ fprintf(gc_data_file, "Garbage collection started (gc_lazy_sweep)\n");
++ }
++ */
++ }
+
+ gc_marks(objspace);
+
+@@ -2307,6 +3018,10 @@ gc_lazy_sweep(rb_objspace_t *objspace)
+ set_heaps_increment(objspace);
+ }
+
++ if (gc_statistics) {
++ gc_time_accumulator += elapsed_musecs(gctv1);
++ }
++
+ GC_PROF_SWEEP_TIMER_START;
+ if(!(res = lazy_sweep(objspace))) {
+ after_gc_sweep(objspace);
+@@ -2318,6 +3033,7 @@ gc_lazy_sweep(rb_objspace_t *objspace)
+ GC_PROF_SWEEP_TIMER_STOP;
+
+ GC_PROF_TIMER_STOP(Qtrue);
++
+ return res;
+ }
+
+@@ -2544,9 +3260,15 @@ gc_marks(rb_objspace_t *objspace)
+ rb_thread_t *th = GET_THREAD();
+ GC_PROF_MARK_TIMER_START;
+
++ /*
++ if (gc_statistics & verbose_gc_stats) {
++ fprintf(gc_data_file, "Marking objects\n");
++ }
++ */
++
+ objspace->heap.live_num = 0;
+ objspace->count++;
+-
++ live_objects = 0;
+
+ SET_STACK_END;
+
+@@ -2578,11 +3300,15 @@ gc_marks(rb_objspace_t *objspace)
+ gc_mark_stacked_objects(objspace);
+
+ GC_PROF_MARK_TIMER_STOP;
++
++ live_after_last_mark_phase = objspace->heap.live_num;
+ }
+
+ static int
+ garbage_collect(rb_objspace_t *objspace)
+ {
++ struct timeval gctv1;
++
+ INIT_GC_PROF_PARAMS;
+
+ if (GC_NOTIFY) printf("start garbage_collect()\n");
+@@ -2598,15 +3324,31 @@ garbage_collect(rb_objspace_t *objspace)
+
+ rest_sweep(objspace);
+
++ if (gc_statistics) {
++ gc_time_accumulator_before_gc = gc_time_accumulator;
++ gc_collections++;
++ gettimeofday(&gctv1, NULL);
++ /*
++ if (verbose_gc_stats) {
++ fprintf(gc_data_file, "Garbage collection started (garbage_collect)\n");
++ }
++ */
++ }
++
+ during_gc++;
+ gc_marks(objspace);
+
++ if (gc_statistics) {
++ gc_time_accumulator += elapsed_musecs(gctv1);
++ }
++
+ GC_PROF_SWEEP_TIMER_START;
+ gc_sweep(objspace);
+ GC_PROF_SWEEP_TIMER_STOP;
+
+ GC_PROF_TIMER_STOP(Qtrue);
+ if (GC_NOTIFY) printf("end garbage_collect()\n");
++
+ return TRUE;
+ }
+
+@@ -3095,6 +3837,39 @@ rb_gc_call_finalizer_at_exit(void)
+ rb_objspace_call_finalizer(&rb_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
+ rb_objspace_call_finalizer(rb_objspace_t *objspace)
+ {
+@@ -3403,6 +4178,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
+@@ -3695,6 +4513,28 @@ Init_GC(void)
+ rb_define_singleton_method(rb_mGC, "stat", gc_stat, -1);
+ 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);
+@@ -3708,6 +4548,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);
+
View
15 patches/ruby/1.9.3/p374/railsexpress/03-display-more-detailed-stack-trace.patch
@@ -0,0 +1,15 @@
+diff --git a/eval_error.c b/eval_error.c
+index fd06adf..69c3b48 100644
+--- a/eval_error.c
++++ b/eval_error.c
+@@ -164,8 +164,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.3/p374/railsexpress/04-fork-support-for-gc-logging.patch
@@ -0,0 +1,68 @@
+diff --git a/gc.c b/gc.c
+index 0be1ec9..c198770 100644
+--- a/gc.c
++++ b/gc.c
+@@ -1345,6 +1345,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 9745afd..7c3d970 100644
+--- a/include/ruby/intern.h
++++ b/include/ruby/intern.h
+@@ -426,6 +426,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)
+ void rb_gc_set_params(void);
+ /* hash.c */
+diff --git a/process.c b/process.c
+index 99cfc69..8bee602 100644
+--- a/process.c
++++ b/process.c
+@@ -2804,9 +2804,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
45 patches/ruby/1.9.3/p374/railsexpress/05-track-live-dataset-size.patch
@@ -0,0 +1,45 @@
+diff --git a/gc.c b/gc.c
+index c198770..68dd48a 100644
+--- a/gc.c
++++ b/gc.c
+@@ -281,7 +281,6 @@ getrusage_time(void)
+ #define GC_PROF_DEC_LIVE_NUM
+ #endif
+
+-
+ #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
+ #pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
+ #endif
+@@ -1562,6 +1561,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(live_after_last_mark_phase);
++}
++
++
++
+ VALUE rb_mGC;
+
+ void
+@@ -4548,6 +4565,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
13 patches/ruby/1.9.3/p374/railsexpress/06-webrick_204_304_keep_alive_fix.patch
@@ -0,0 +1,13 @@
+diff --git a/lib/webrick/httpresponse.rb b/lib/webrick/httpresponse.rb
+index 0d36c07..ce72b3f 100644
+--- a/lib/webrick/httpresponse.rb
++++ b/lib/webrick/httpresponse.rb
+@@ -202,7 +202,7 @@ module WEBrick
+ if @header['connection'] == "close"
+ @keep_alive = false
+ elsif keep_alive?
+- if chunked? || @header['content-length']
++ if chunked? || @header['content-length'] || @status == 304 || @status == 204
+ @header['connection'] = "Keep-Alive"
+ else
+ msg = "Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true"
View
84 patches/ruby/1.9.3/p374/railsexpress/07-export-a-few-more-symbols-for-ruby-prof.patch
@@ -0,0 +1,84 @@
+diff --git a/gc.c b/gc.c
+index 68dd48a..e8e73c0 100644
+--- a/gc.c
++++ b/gc.c
+@@ -1052,6 +1052,7 @@ ruby_xfree(void *x)
+ *
+ */
+
++RUBY_FUNC_EXPORTED
+ VALUE
+ rb_gc_enable(void)
+ {
+@@ -1074,6 +1075,7 @@ rb_gc_enable(void)
+ *
+ */
+
++RUBY_FUNC_EXPORTED
+ VALUE
+ rb_gc_disable(void)
+ {
+@@ -1096,6 +1098,7 @@ rb_gc_disable(void)
+ *
+ */
+
++RUBY_FUNC_EXPORTED
+ VALUE
+ rb_gc_enable_stats()
+ {
+@@ -1117,6 +1120,7 @@ rb_gc_enable_stats()
+ *
+ */
+
++RUBY_FUNC_EXPORTED
+ VALUE
+ rb_gc_disable_stats()
+ {
+@@ -1136,6 +1140,7 @@ rb_gc_disable_stats()
+ *
+ */
+
++RUBY_FUNC_EXPORTED
+ VALUE
+ rb_gc_stats_enabled()
+ {
+@@ -1155,6 +1160,7 @@ rb_gc_stats_enabled()
+ *
+ */
+
++RUBY_FUNC_EXPORTED
+ VALUE
+ rb_gc_clear_stats()
+ {
+@@ -1222,6 +1228,7 @@ rb_gc_num_allocations()
+ *
+ */
+
++RUBY_FUNC_EXPORTED
+ VALUE
+ rb_gc_enable_trace()
+ {
+@@ -1243,6 +1250,7 @@ rb_gc_enable_trace()
+ *
+ */
+
++RUBY_FUNC_EXPORTED
+ VALUE
+ rb_gc_disable_trace()
+ {
+@@ -1262,6 +1270,7 @@ rb_gc_disable_trace()
+ *
+ */
+
++RUBY_FUNC_EXPORTED
+ VALUE
+ rb_gc_trace_enabled()
+ {
+@@ -1290,6 +1299,7 @@ const char* GC_LOGFILE_IVAR = "@gc_logfile_name";
+ *
+ */
+
++RUBY_FUNC_EXPORTED
+ VALUE
+ rb_gc_log_file(int argc, VALUE *argv, VALUE self)
+ {
View
319 patches/ruby/1.9.3/p374/railsexpress/08-thread-variables.patch
@@ -0,0 +1,319 @@
+diff --git a/NEWS b/NEWS
+index 30fec33..9f6c172 100644
+--- a/NEWS
++++ b/NEWS
+@@ -103,6 +103,16 @@ with all sufficient information, see the ChangeLog file.
+ * String#prepend
+ * String#byteslice
+
++ * Thread
++ * added method:
++ * added Thread#thread_variable_get for getting thread local variables
++ (these are different than Fiber local variables).
++ * added Thread#thread_variable_set for setting thread local variables.
++ * added Thread#thread_variables for getting a list of the thread local
++ variable keys.
++ * added Thread#thread_variable? for testing to see if a particular thread
++ variable has been set.
++
+ * Time
+ * extended method:
+ * Time#strftime supports %:z and %::z.
+diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
+index e8cb3b1..9ff491d 100644
+--- a/test/ruby/test_thread.rb
++++ b/test/ruby/test_thread.rb
+@@ -27,6 +27,79 @@ class TestThread < Test::Unit::TestCase
+ end
+ end
+
++ def test_main_thread_variable_in_enumerator
++ assert_equal Thread.main, Thread.current
++
++ Thread.current.thread_variable_set :foo, "bar"
++
++ thread, value = Fiber.new {
++ Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
++ }.resume
++
++ assert_equal Thread.current, thread
++ assert_equal Thread.current.thread_variable_get(:foo), value
++ end
++
++ def test_thread_variable_in_enumerator
++ Thread.new {
++ Thread.current.thread_variable_set :foo, "bar"
++
++ thread, value = Fiber.new {
++ Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
++ }.resume
++
++ assert_equal Thread.current, thread
++ assert_equal Thread.current.thread_variable_get(:foo), value
++ }.join
++ end
++
++ def test_thread_variables
++ assert_equal [], Thread.new { Thread.current.thread_variables }.join.value
++
++ t = Thread.new {
++ Thread.current.thread_variable_set(:foo, "bar")
++ Thread.current.thread_variables
++ }
++ assert_equal [:foo], t.join.value
++ end
++
++ def test_thread_variable?
++ refute Thread.new { Thread.current.thread_variable?("foo") }.join.value
++ t = Thread.new {
++ Thread.current.thread_variable_set("foo", "bar")
++ }.join
++
++ assert t.thread_variable?("foo")
++ assert t.thread_variable?(:foo)
++ refute t.thread_variable?(:bar)
++ end
++
++ def test_thread_variable_strings_and_symbols_are_the_same_key
++ t = Thread.new {}.join
++ t.thread_variable_set("foo", "bar")
++ assert_equal "bar", t.thread_variable_get(:foo)
++ end
++
++ def test_thread_variable_frozen
++ t = Thread.new { }.join
++ t.freeze
++ assert_raises(RuntimeError) do
++ t.thread_variable_set(:foo, "bar")
++ end
++ end
++
++ def test_thread_variable_security
++ t = Thread.new { sleep }
++
++ assert_raises(SecurityError) do
++ Thread.new { $SAFE = 4; t.thread_variable_get(:foo) }.join
++ end
++
++ assert_raises(SecurityError) do
++ Thread.new { $SAFE = 4; t.thread_variable_set(:foo, :baz) }.join
++ end
++ end
++
+ def test_mutex_synchronize
+ m = Mutex.new
+ r = 0
+diff --git a/thread.c b/thread.c
+index c7da34b..ce1fa3f 100644
+--- a/thread.c
++++ b/thread.c
+@@ -2112,7 +2112,9 @@ rb_thread_local_aset(VALUE thread, ID id, VALUE val)
+ * thr[sym] = obj -> obj
+ *
+ * Attribute Assignment---Sets or creates the value of a thread-local variable,
+- * using either a symbol or a string. See also <code>Thread#[]</code>.
++ * using either a symbol or a string. See also <code>Thread#[]</code>. For
++ * thread-local variables, please see <code>Thread#thread_variable_set</code>
++ * and <code>Thread#thread_variable_get</code>.
+ */
+
+ static VALUE
+@@ -2123,6 +2125,80 @@ rb_thread_aset(VALUE self, VALUE id, VALUE val)
+
+ /*
+ * call-seq:
++ * thr.thread_variable_get(key) -> obj or nil
++ *
++ * Returns the value of a thread local variable that has been set. Note that
++ * these are different than fiber local values. For fiber local values,
++ * please see Thread#[] and Thread#[]=.
++ *
++ * Thread local values are carried along with threads, and do not respect
++ * fibers. For example:
++ *
++ * Thread.new {
++ * Thread.current.thread_variable_set("foo", "bar") # set a thread local
++ * Thread.current["foo"] = "bar" # set a fiber local
++ *
++ * Fiber.new {
++ * Fiber.yield [
++ * Thread.current.thread_variable_get("foo"), # get the thread local
++ * Thread.current["foo"], # get the fiber local
++ * ]
++ * }.resume
++ * }.join.value # => ['bar', nil]
++ *
++ * The value "bar" is returned for the thread local, where nil is returned
++ * for the fiber local. The fiber is executed in the same thread, so the
++ * thread local values are available.
++ *
++ * See also Thread#[]
++ */
++
++static VALUE
++rb_thread_variable_get(VALUE thread, VALUE id)
++{
++ VALUE locals;
++ rb_thread_t *th;
++
++ GetThreadPtr(thread, th);
++
++ if (rb_safe_level() >= 4 && th != GET_THREAD()) {
++ rb_raise(rb_eSecurityError, "Insecure: can't access thread locals");
++ }
++
++ locals = rb_iv_get(thread, "locals");
++ return rb_hash_aref(locals, ID2SYM(rb_to_id(id)));
++}
++
++/*
++ * call-seq:
++ * thr.thread_variable_set(key, value)
++ *
++ * Sets a thread local with +key+ to +value+. Note that these are local to
++ * threads, and not to fibers. Please see Thread#thread_variable_get and
++ * Thread#[] for more information.
++ */
++
++static VALUE
++rb_thread_variable_set(VALUE thread, VALUE id, VALUE val)
++{
++ VALUE locals;
++ rb_thread_t *th;
++
++ GetThreadPtr(thread, th);
++
++ if (rb_safe_level() >= 4 && th != GET_THREAD()) {
++ rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
++ }
++ if (OBJ_FROZEN(thread)) {
++ rb_error_frozen("thread locals");
++ }
++
++ locals = rb_iv_get(thread, "locals");
++ return rb_hash_aset(locals, ID2SYM(rb_to_id(id)), val);
++}
++
++/*
++ * call-seq:
+ * thr.key?(sym) -> true or false
+ *
+ * Returns <code>true</code> if the given string (or symbol) exists as a
+@@ -2993,6 +3069,9 @@ rb_gc_save_machine_context(rb_thread_t *th)
+
+ /*
+ *
++ * For thread-local variables, please see <code>Thread#thread_local_get</code>
++ * and <code>Thread#thread_local_set</code>.