Skip to content
Browse files

added support for 1.8.7-p357

  • Loading branch information...
1 parent 9339048 commit a33ff2595e9081c8b2a2d39c23584bceb039af73 @skaes skaes committed
View
7 README.rdoc
@@ -41,9 +41,16 @@ but there's no guarantee. The following versions are currently supported:
1.8.7-p334 # not recommended, as p352 fixes a serious GC corruption problem
1.8.7-p352 # current default for rvm MRI-ruby 1.8.7
+ 1.8.7-p357 # recommended: fixes a DOS vulnerability in 1.8.7
1.9.2-p180 #
1.9.2-p290 # current default for rvm MRI-ruby 1.9.2
+In order to make 357 the defualt patch level for 1.8.7, add the line
+
+ ruby_1.8.7_patch_level=357
+
+to $rvm_path/user/db.
+
To enable heap dump support, pass the --enable-gcdebug option to the rvm install command.
rvm install 1.8.7 --force --patch railsexpress -C --enable-gcdebug -n gcdebug
View
101 patches/ruby/1.8.7/p357/railsexpress/01-ignore-generated-files.patch
@@ -0,0 +1,101 @@
+diff --git a/.gitignore b/.gitignore
+new file mode 100644
+index 0000000..00c347a
+--- /dev/null
++++ b/.gitignore
+@@ -0,0 +1,95 @@
++.ext
++.installed.list
++.rbconfig.time
++Makefile
++autom4te.cache/
++config.h
++config.status
++configure
++ext/Win32API/Makefile
++ext/bigdecimal/Makefile
++ext/curses/Makefile
++ext/dbm/Makefile
++ext/digest/Makefile
++ext/digest/bubblebabble/Makefile
++ext/digest/md5/Makefile
++ext/digest/rmd160/Makefile
++ext/digest/sha1/Makefile
++ext/digest/sha2/Makefile
++ext/dl/Makefile
++ext/dl/call.func
++ext/dl/callback.func
++ext/dl/cbtable.func
++ext/dl/dlconfig.h
++ext/dl/dlconfig.rb
++ext/enumerator/Makefile
++ext/etc/Makefile
++ext/fcntl/Makefile
++ext/gdbm/Makefile
++ext/iconv/Makefile
++ext/io/wait/Makefile
++ext/nkf/Makefile
++ext/openssl/Makefile
++ext/openssl/extconf.h
++ext/pty/Makefile
++ext/racc/cparse/Makefile
++ext/readline/Makefile
++ext/sdbm/Makefile
++ext/socket/Makefile
++ext/stringio/Makefile
++ext/strscan/Makefile
++ext/syck/Makefile
++ext/syslog/Makefile
++ext/thread/Makefile
++ext/tk/Makefile
++ext/tk/tkutil/Makefile
++ext/win32ole/Makefile
++ext/win32ole/.document
++ext/zlib/Makefile
++largefile.h
++miniruby
++parse.c
++rbconfig.rb
++ruby
++enc.mk
++ext/bigdecimal/extconf.h
++ext/continuation/
++ext/coverage/
++ext/curses/extconf.h
++ext/dbm/extconf.h
++ext/digest/bubblebabble/extconf.h
++ext/digest/extconf.h
++ext/digest/md5/extconf.h
++ext/digest/rmd160/extconf.h
++ext/digest/sha1/extconf.h
++ext/digest/sha2/extconf.h
++ext/dl/callback.h
++ext/dl/extconf.h
++ext/etc/extconf.h
++ext/fcntl/extconf.h
++ext/fiber/
++ext/iconv/extconf.h
++ext/io/wait/extconf.h
++ext/json/
++ext/nkf/extconf.h
++ext/pty/extconf.h
++ext/racc/cparse/extconf.h
++ext/readline/extconf.h
++ext/ripper/
++ext/sdbm/extconf.h
++ext/socket/constants.h
++ext/socket/extconf.h
++ext/stringio/extconf.h
++ext/strscan/extconf.h
++ext/syck/extconf.h
++ext/syslog/extconf.h
++ext/tk/extconf.h
++ext/tk/tkutil/extconf.h
++ext/zlib/extconf.h
++miniprelude.c
++prelude.c
++revision.h
++*.dylib
++*.log
++*.dSYM
++patches-ruby*
View
27 patches/ruby/1.8.7/p357/railsexpress/02-sigvtalrm-fix.patch
@@ -0,0 +1,27 @@
+diff --git a/eval.c b/eval.c
+index 7886e17..6ff2560 100644
+--- a/eval.c
++++ b/eval.c
+@@ -12461,6 +12461,11 @@ rb_thread_start_0(fn, arg, th)
+ curr_thread->next = th;
+ th->priority = curr_thread->priority;
+ th->thgroup = curr_thread->thgroup;
++#if defined(HAVE_SETITIMER) || defined(_THREAD_SAFE)
++ if (!thread_init) {
++ rb_thread_start_timer();
++ }
++#endif
+ }
+ START_TIMER();
+
+@@ -13189,7 +13194,9 @@ rb_thread_atfork()
+ main_thread = curr_thread;
+ curr_thread->next = curr_thread;
+ curr_thread->prev = curr_thread;
+- STOP_TIMER();
++#if defined(HAVE_SETITIMER) || defined(_THREAD_SAFE)
++ rb_thread_stop_timer();
++#endif
+ }
+
+
View
1,876 patches/ruby/1.8.7/p357/railsexpress/03-railsbench-gc-patch.patch
1,876 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
15 patches/ruby/1.8.7/p357/railsexpress/04-display-full-stack-trace.patch
@@ -0,0 +1,15 @@
+diff --git a/eval.c b/eval.c
+index 6ff2560..fb3307c 100644
+--- a/eval.c
++++ b/eval.c
+@@ -1325,8 +1325,8 @@ error_print()
+ int truncate = 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
+
+ ep = RARRAY(errat);
+ for (i=1; i<ep->len; i++) {
View
13 patches/ruby/1.8.7/p357/railsexpress/05-better-source-file-tracing.patch
@@ -0,0 +1,13 @@
+diff --git a/eval.c b/eval.c
+index fb3307c..356226e 100644
+--- a/eval.c
++++ b/eval.c
+@@ -1161,7 +1161,7 @@ static VALUE trace_func = 0;
+ static int tracing = 0;
+ static void call_trace_func _((rb_event_t,NODE*,VALUE,ID,VALUE));
+
+-#if 0
++#if 1
+ #define SET_CURRENT_SOURCE() (ruby_sourcefile = ruby_current_node->nd_file, \
+ ruby_sourceline = nd_line(ruby_current_node))
+ #else
View
159 patches/ruby/1.8.7/p357/railsexpress/06-heap-dump-support.patch
@@ -0,0 +1,159 @@
+diff --git a/configure.in b/configure.in
+index 62b34a8..4be088c 100644
+--- a/configure.in
++++ b/configure.in
+@@ -1595,6 +1595,14 @@ fi
+ LDFLAGS="-L. $LDFLAGS"
+ AC_SUBST(ARCHFILE)
+
++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 build rdoc index if requested
+ RDOCTARGET=""
+ AC_ARG_ENABLE(install-doc,
+diff --git a/gc.c b/gc.c
+index ab71d22..9ad716f 100644
+--- a/gc.c
++++ b/gc.c
+@@ -411,7 +411,6 @@ rb_gc_unregister_address(addr)
+ }
+ }
+
+-#undef GC_DEBUG
+
+ void
+ rb_global_variable(var)
+@@ -602,6 +601,85 @@ rb_gc_dump()
+ return Qnil;
+ }
+
++
++static char* obj_type(int 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)
++{
++ VALUE filename, str, include_classnames = Qnil;
++ char *fname = NULL;
++ char *klass = NULL;
++ FILE* f = NULL;
++ int i,n = 0;
++
++ rb_scan_args(argc, argv, "11", &filename, &include_classnames);
++
++ str = StringValue(filename);
++ fname = RSTRING(str)->ptr;
++ 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) {
++ fprintf(f, "%s:%s:%d", obj_type(p->as.basic.flags & T_MASK), p->file, 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 (TYPE(p)) {
++ case T_NONE:
++ fprintf(f, "__none__");
++ break;
++ case T_BLKTAG:
++ fprintf(f, "__blktag__");
++ break;
++ case T_UNDEF:
++ fprintf(f, "__undef__");
++ break;
++ case T_VARMAP:
++ fprintf(f, "__varmap__");
++ break;
++ case T_SCOPE:
++ fprintf(f, "__scope__");
++ break;
++ case T_NODE:
++ fprintf(f, "__node__");
++ break;
++ default:
++ if (!p->as.basic.klass) {
++ fprintf(f, "__unknown__");
++ } else {
++ fprintf(f, rb_obj_classname((VALUE)p));
++ }
++ }
++ }
++ fprintf(f, "\n");
++ }
++ }
++ }
++ fclose(f);
++ return Qnil;
++}
++#endif
++
+ /*
+ * call-seq:
+ * GC.log String => String
+@@ -1066,6 +1144,11 @@ gc_mark(ptr, lev)
+ if (obj->as.basic.flags & FL_MARK) return; /* already marked */
+ obj->as.basic.flags |= FL_MARK;
+
++#ifdef GC_DEBUG
++ /* mark our new reference point for sourcefile objects */
++ mark_source_filename(RANY(obj)->file);
++#endif
++
+ if (lev > GC_LEVEL_MAX || (lev == 0 && ruby_stack_check())) {
+ if (!mark_stack_overflow) {
+ if (mark_stack_ptr - mark_stack < MARK_STACK_MAX) {
+@@ -1104,6 +1187,11 @@ gc_mark_children(ptr, lev)
+ if (obj->as.basic.flags & FL_MARK) return; /* already marked */
+ obj->as.basic.flags |= FL_MARK;
+
++#ifdef GC_DEBUG
++ /* mark our new reference point for sourcefile objects */
++ mark_source_filename(RANY(obj)->file);
++#endif
++
+ marking:
+ if (FL_TEST(obj, FL_EXIVAR)) {
+ rb_mark_generic_ivar(ptr);
+@@ -1550,6 +1638,7 @@ gc_sweep()
+ live_counts[i], free_counts[i], obj_type(i));
+ }
+ }
++ fflush(gc_data_file);
+ }
+
+ /* clear finalization list */
+@@ -2526,6 +2615,9 @@ Init_GC()
+ 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_define_singleton_method(rb_mGC, "log", rb_gc_log, 1);
+
+ rb_mObSpace = rb_define_module("ObjectSpace");
View
249 patches/ruby/1.8.7/p357/railsexpress/07-fork-support-for-gc-logging.patch
@@ -0,0 +1,249 @@
+diff --git a/gc.c b/gc.c
+index 9ad716f..a3cbe91 100644
+--- a/gc.c
++++ b/gc.c
+@@ -223,6 +223,8 @@ static int dont_gc;
+ static int gc_statistics = 0;
+ static GC_TIME_TYPE gc_time = 0;
+ static int gc_collections = 0;
++static int verbose_gc_stats = Qfalse;
++static FILE* gc_data_file = NULL;
+ static int during_gc;
+ static int need_call_final = 0;
+ static st_table *finalizer_table = 0;
+@@ -368,9 +370,148 @@ rb_gc_time()
+ #endif
+ }
+
+-
+ VALUE rb_mGC;
+
++/*
++ * 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()
++{
++ int old = verbose_gc_stats;
++ verbose_gc_stats = Qtrue;
++ return old;
++}
++
++/*
++ * 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()
++{
++ int old = verbose_gc_stats;
++ verbose_gc_stats = Qfalse;
++ return old;
++}
++
++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)
++{
++ VALUE filename = Qnil;
++ VALUE mode_str = Qnil;
++ FILE* f = NULL;
++ 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(StringValue(mode_str))->ptr;
++ }
++
++ /* try to open file in given mode */
++ if (f = fopen(RSTRING(filename)->ptr, 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;
++}
++
++
++/*
++ * 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()
++{
++ /* flush gc log file */
++ 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_gc_disable_stats();
++ rb_gc_clear_stats();
++ rb_gc_disable_trace();
++ gc_data_file = stderr;
++ rb_iv_set(rb_mGC, GC_LOGFILE_IVAR, Qnil);
++}
++
+ static struct gc_list {
+ VALUE *varptr;
+ struct gc_list *next;
+@@ -477,10 +618,6 @@ static double heap_slots_growth_factor = 1.8;
+
+ static long initial_malloc_limit = GC_MALLOC_LIMIT;
+
+-static int verbose_gc_stats = Qfalse;
+-
+-static FILE* gc_data_file = NULL;
+-
+ static RVALUE *himem, *lomem;
+
+ static void set_gc_parameters()
+@@ -496,6 +633,8 @@ static void set_gc_parameters()
+ if (gc_stats_i > 0) {
+ verbose_gc_stats = Qtrue;
+ }
++ /* child processes should not inherit RUBY_GC_STATS */
++ unsetenv("RUBY_GC_STATS");
+ }
+
+ gc_heap_file_ptr = getenv("RUBY_GC_DATA_FILE");
+@@ -508,6 +647,8 @@ static void set_gc_parameters()
+ fprintf(stderr,
+ "can't open gc log file %s for writing, using default\n", gc_heap_file_ptr);
+ }
++ /* child processes should not inherit RUBY_GC_DATA_FILE to avoid clobbering */
++ unsetenv("RUBY_GC_DATA_FILE");
+ }
+
+ min_slots_ptr = getenv("RUBY_HEAP_MIN_SLOTS");
+@@ -2619,6 +2760,9 @@ Init_GC()
+ rb_define_singleton_method(rb_mGC, "dump_file_and_line_info", rb_gc_dump_file_and_line_info, -1);
+ #endif
+ 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_mObSpace = rb_define_module("ObjectSpace");
+ rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
+diff --git a/intern.h b/intern.h
+index 950ae9d..99696f1 100644
+--- a/intern.h
++++ b/intern.h
+@@ -270,6 +270,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));
+ /* hash.c */
+ void st_foreach_safe _((struct st_table *, int (*)(ANYARGS), unsigned long));
+ void rb_hash_foreach _((VALUE, int (*)(ANYARGS), VALUE));
+diff --git a/process.c b/process.c
+index 8f6285d..ea28cb8 100644
+--- a/process.c
++++ b/process.c
+@@ -1330,6 +1330,8 @@ rb_f_fork(obj)
+ fflush(stderr);
+ #endif
+
++ rb_gc_before_fork();
++
+ before_exec();
+ pid = fork();
+ after_exec();
+@@ -1339,6 +1341,7 @@ rb_f_fork(obj)
+ #ifdef linux
+ after_exec();
+ #endif
++ rb_gc_after_fork();
+ rb_thread_atfork();
+ if (rb_block_given_p()) {
+ int status;
+@@ -1574,10 +1577,12 @@ rb_f_system(argc, argv)
+
+ chfunc = signal(SIGCHLD, SIG_DFL);
+ retry:
++ rb_gc_before_fork();
+ before_exec();
+ pid = fork();
+ if (pid == 0) {
+ /* child process */
++ rb_gc_after_fork();
+ rb_thread_atfork();
+ rb_protect(proc_exec_args, (VALUE)&earg, NULL);
+ _exit(127);
View
120 patches/ruby/1.8.7/p357/railsexpress/08-track-malloc-size.patch
@@ -0,0 +1,120 @@
+diff --git a/gc.c b/gc.c
+index a3cbe91..30a1219 100644
+--- a/gc.c
++++ b/gc.c
+@@ -79,6 +79,17 @@ void *alloca ();
+
+ static unsigned long malloc_increase = 0;
+ static unsigned long malloc_limit = GC_MALLOC_LIMIT;
++
++#ifdef HAVE_LONG_LONG
++static unsigned LONG_LONG gc_allocated_size = 0;
++static unsigned LONG_LONG gc_num_allocations = 0;
++#else
++static unsigned long gc_allocated_size = 0;
++static unsigned long gc_num_allocations = 0;
++#endif
++static int gc_statistics = 0;
++
++
+ static void run_final();
+ static VALUE nomem_error;
+ static void garbage_collect();
+@@ -163,6 +174,11 @@ ruby_xmalloc(size)
+ }
+ malloc_increase += size;
+
++ if (gc_statistics) {
++ gc_allocated_size += size;
++ gc_num_allocations += 1;
++ }
++
+ return mem;
+ }
+
+@@ -220,7 +236,6 @@ ruby_xfree(x)
+
+ extern int ruby_in_compile;
+ static int dont_gc;
+-static int gc_statistics = 0;
+ static GC_TIME_TYPE gc_time = 0;
+ static int gc_collections = 0;
+ static int verbose_gc_stats = Qfalse;
+@@ -329,11 +344,55 @@ rb_gc_clear_stats()
+ {
+ gc_collections = 0;
+ gc_time = 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()
++{
++#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()
++{
++#if HAVE_LONG_LONG
++ return ULL2NUM(gc_num_allocations);
++#else
++ return ULONG2NUM(gc_num_allocations);
++#endif
++}
++
++/*
++
++/*
++ * call-seq:
+ * GC.collections => Integer
+ *
+ * Returns the number of garbage collections performed while GC statistics collection
+@@ -2753,6 +2812,8 @@ Init_GC()
+ 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, "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, "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);
+diff --git a/intern.h b/intern.h
+index 99696f1..1117614 100644
+--- a/intern.h
++++ b/intern.h
+@@ -272,6 +272,8 @@ VALUE rb_gc_disable _((void));
+ VALUE rb_gc_start _((void));
+ void rb_gc_before_fork _((void));
+ void rb_gc_after_fork _((void));
++VALUE rb_gc_allocated_size _((void));
++VALUE rb_gc_num_allocations _((void));
+ /* hash.c */
+ void st_foreach_safe _((struct st_table *, int (*)(ANYARGS), unsigned long));
+ void rb_hash_foreach _((VALUE, int (*)(ANYARGS), VALUE));
View
111 patches/ruby/1.8.7/p357/railsexpress/09-track-object-allocation.patch
@@ -0,0 +1,111 @@
+diff --git a/gc.c b/gc.c
+index 30a1219..5b42b90 100644
+--- a/gc.c
++++ b/gc.c
+@@ -96,6 +96,26 @@ static void garbage_collect();
+
+ int ruby_gc_stress = 0;
+
++static unsigned long live_objects = 0;
++unsigned long rb_os_live_objects()
++{
++ return live_objects;
++}
++
++#if defined(HAVE_LONG_LONG)
++static unsigned LONG_LONG allocated_objects = 0;
++unsigned LONG_LONG rb_os_allocated_objects()
++{
++ return allocated_objects;
++}
++#else
++static unsigned long allocated_objects = 0;
++unsigned long rb_os_allocated_objects()
++{
++ return allocated_objects;
++}
++#endif
++
+ NORETURN(void rb_exc_jump _((VALUE)));
+
+ void
+@@ -987,6 +1007,8 @@ rb_newobj()
+ RANY(obj)->file = ruby_sourcefile;
+ RANY(obj)->line = ruby_sourceline;
+ #endif
++ live_objects++;
++ allocated_objects++;
+ return obj;
+ }
+
+@@ -1825,6 +1847,7 @@ gc_sweep()
+ add_heap();
+ }
+ during_gc = 0;
++ live_objects = live;
+
+ if (do_gc_stats) {
+ fprintf(gc_data_file, "objects processed: %.7d\n", live+freed);
+@@ -2790,6 +2813,35 @@ rb_obj_id(VALUE obj)
+ return (VALUE)((long)obj|FIXNUM_FLAG);
+ }
+
++/* 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)
++{
++ return ULONG2NUM(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)
++{
++#if defined(HAVE_LONG_LONG)
++ return ULL2NUM(allocated_objects);
++#else
++ return ULONG2NUM(allocated_objects);
++#endif
++}
++
+ /*
+ * The <code>GC</code> module provides an interface to Ruby's mark and
+ * sweep garbage collection mechanism. Some of the underlying methods
+@@ -2833,6 +2885,9 @@ Init_GC()
+ rb_define_module_function(rb_mObSpace, "finalizers", finals, 0);
+ rb_define_module_function(rb_mObSpace, "call_finalizer", call_final, 1);
+
++ 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/intern.h b/intern.h
+index 1117614..a87661d 100644
+--- a/intern.h
++++ b/intern.h
+@@ -274,6 +274,12 @@ void rb_gc_before_fork _((void));
+ void rb_gc_after_fork _((void));
+ VALUE rb_gc_allocated_size _((void));
+ VALUE rb_gc_num_allocations _((void));
++unsigned long rb_os_live_objects _((void));
++#ifdef HAVE_LONG_LONG
++unsigned LONG_LONG rb_os_allocated_objects _((void));
++#else
++unsigned long rb_os_allocated_objects _((void));
++#endif
+ /* hash.c */
+ void st_foreach_safe _((struct st_table *, int (*)(ANYARGS), unsigned long));
+ void rb_hash_foreach _((VALUE, int (*)(ANYARGS), VALUE));
View
70 patches/ruby/1.8.7/p357/railsexpress/10-expose-heap-slots.patch
@@ -0,0 +1,70 @@
+diff --git a/gc.c b/gc.c
+index 5b42b90..21b3f6b 100644
+--- a/gc.c
++++ b/gc.c
+@@ -690,6 +690,7 @@ static int heaps_used = 0;
+
+ static int heap_min_slots = 10000;
+ static int heap_slots = 10000;
++static int heap_size = 0;
+
+ static int heap_free_min = 4096;
+ static int heap_slots_increment = 10000;
+@@ -800,6 +801,21 @@ static void set_gc_parameters()
+
+ /*
+ * 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()
++{
++ return INT2NUM(heap_size);
++}
++
++/*
++ * call-seq:
+ * GC.dump => nil
+ *
+ * dumps information about the current GC data structures to the GC log file
+@@ -967,6 +983,7 @@ add_heap()
+ heaps[heaps_used].limit = heap_slots;
+ break;
+ }
++ heap_size += heap_slots;
+ pend = p + heap_slots;
+ if (lomem == 0 || lomem > p) lomem = p;
+ if (himem < pend) himem = pend;
+@@ -1828,6 +1845,7 @@ gc_sweep()
+ if (n == heaps[i].limit && freed > free_min) {
+ RVALUE *pp;
+
++ heap_size -= n;
+ heaps[i].limit = 0;
+ for (pp = final_list; pp != final; pp = pp->as.free.next) {
+ pp->as.free.flags |= FL_SINGLETON; /* freeing page mark */
+@@ -2866,6 +2884,7 @@ Init_GC()
+ 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_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);
+diff --git a/intern.h b/intern.h
+index a87661d..e8f3209 100644
+--- a/intern.h
++++ b/intern.h
+@@ -274,6 +274,7 @@ void rb_gc_before_fork _((void));
+ void rb_gc_after_fork _((void));
+ VALUE rb_gc_allocated_size _((void));
+ VALUE rb_gc_num_allocations _((void));
++VALUE rb_gc_heap_slots _((void));
+ unsigned long rb_os_live_objects _((void));
+ #ifdef HAVE_LONG_LONG
+ unsigned LONG_LONG rb_os_allocated_objects _((void));
View
54 patches/ruby/1.8.7/p357/railsexpress/11-fix-heap-size-growth-logic.patch
@@ -0,0 +1,54 @@
+diff --git a/gc.c b/gc.c
+index 21b3f6b..7db1ef6 100644
+--- a/gc.c
++++ b/gc.c
+@@ -694,6 +694,7 @@ static int heap_size = 0;
+
+ static int heap_free_min = 4096;
+ static int heap_slots_increment = 10000;
++static int initial_heap_slots_increment = 10000;
+ static double heap_slots_growth_factor = 1.8;
+
+ static long initial_malloc_limit = GC_MALLOC_LIMIT;
+@@ -771,14 +772,13 @@ static void set_gc_parameters()
+ if (verbose_gc_stats) {
+ fprintf(gc_data_file, "RUBY_HEAP_SLOTS_INCREMENT=%s\n", heap_slots_incr_ptr);
+ }
+- if (heap_slots_incr_i > 0) {
+- heap_slots_increment = heap_slots_incr_i;
+- }
++ heap_slots_increment = heap_slots_incr_i;
++ initial_heap_slots_increment = heap_slots_increment;
+ }
+
+ heap_slots_growth_factor_ptr = getenv("RUBY_HEAP_SLOTS_GROWTH_FACTOR");
+ if (heap_slots_growth_factor_ptr != NULL) {
+- double heap_slots_growth_factor_d = atoi(heap_slots_growth_factor_ptr);
++ double heap_slots_growth_factor_d = atof(heap_slots_growth_factor_ptr);
+ if (verbose_gc_stats) {
+ fprintf(gc_data_file, "RUBY_HEAP_SLOTS_GROWTH_FACTOR=%s\n", heap_slots_growth_factor_ptr);
+ }
+@@ -988,8 +988,13 @@ add_heap()
+ if (lomem == 0 || lomem > p) lomem = p;
+ if (himem < pend) himem = pend;
+ heaps_used++;
+- heap_slots += heap_slots_increment;
+- heap_slots_increment *= heap_slots_growth_factor;
++ if (heaps_used == 1)
++ heap_slots = initial_heap_slots_increment;
++ else {
++ heap_slots_increment *= heap_slots_growth_factor;
++ heap_slots += heap_slots_increment;
++ }
++
+ if (heap_slots <= 0) heap_slots = heap_min_slots;
+
+ while (p < pend) {
+@@ -1879,6 +1884,7 @@ gc_sweep()
+ live_counts[i], free_counts[i], obj_type(i));
+ }
+ }
++ rb_gc_dump();
+ fflush(gc_data_file);
+ }
+
View
12 patches/ruby/1.8.7/p357/railsexpress/12-heap-slot-size.patch
@@ -0,0 +1,12 @@
+diff --git a/gc.c b/gc.c
+index 7db1ef6..57740d2 100644
+--- a/gc.c
++++ b/gc.c
+@@ -2891,6 +2891,7 @@ Init_GC()
+ 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, "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);
View
66 patches/ruby/1.8.7/p357/railsexpress/13-add-trace-stats-enabled-methods.patch
@@ -0,0 +1,66 @@
+diff --git a/gc.c b/gc.c
+index 57740d2..2c34932 100644
+--- a/gc.c
++++ b/gc.c
+@@ -350,6 +350,22 @@ rb_gc_disable_stats()
+
+ /*
+ * 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()
++{
++ return gc_statistics ? Qtrue : Qfalse;
++}
++
++/*
++ * call-seq:
+ * GC.clear_stats => nil
+ *
+ * Clears garbage collection statistics, returning nil. This resets the number
+@@ -491,6 +507,22 @@ rb_gc_disable_trace()
+ return old;
+ }
+
++/*
++ * 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()
++{
++ return verbose_gc_stats ? Qtrue : Qfalse;
++}
++
+ char* GC_LOGFILE_IVAR = "@gc_logfile_name";
+
+ /*
+@@ -2887,6 +2919,7 @@ Init_GC()
+
+ 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);
+@@ -2902,6 +2935,7 @@ Init_GC()
+ 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_mObSpace = rb_define_module("ObjectSpace");
+ rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
View
52 patches/ruby/1.8.7/p357/railsexpress/14-track-live-dataset-size.patch
@@ -0,0 +1,52 @@
+diff --git a/gc.c b/gc.c
+index 2c34932..0ce7e68 100644
+--- a/gc.c
++++ b/gc.c
+@@ -89,6 +89,7 @@ static unsigned long gc_num_allocations = 0;
+ #endif
+ static int gc_statistics = 0;
+
++static unsigned long heap_slots_live_after_last_gc = 0;
+
+ static void run_final();
+ static VALUE nomem_error;
+@@ -465,6 +466,23 @@ 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()
++{
++ return ULONG2NUM(heap_slots_live_after_last_gc);
++}
++
++
++
+ VALUE rb_mGC;
+
+ /*
+@@ -1903,6 +1921,7 @@ gc_sweep()
+ }
+ during_gc = 0;
+ live_objects = live;
++ heap_slots_live_after_last_gc = live;
+
+ if (do_gc_stats) {
+ fprintf(gc_data_file, "objects processed: %.7d\n", live+freed);
+@@ -2924,6 +2943,7 @@ Init_GC()
+ 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, "collections", rb_gc_collections, 0);
+ rb_define_singleton_method(rb_mGC, "time", rb_gc_time, 0);
View
51 patches/ruby/1.8.7/p357/railsexpress/15-add-object-size-information-to-heap-dump.patch
@@ -0,0 +1,51 @@
+diff --git a/gc.c b/gc.c
+index 0ce7e68..53450bf 100644
+--- a/gc.c
++++ b/gc.c
+@@ -953,9 +953,21 @@ rb_gc_dump_file_and_line_info(int argc, VALUE *argv)
+ if (!p->as.basic.klass) {
+ fprintf(f, "__unknown__");
+ } else {
+- fprintf(f, rb_obj_classname((VALUE)p));
++ fprintf(f, "%s", rb_obj_classname((VALUE)p));
+ }
+ }
++ /* print object size for some known object types */
++ switch (TYPE(p)) {
++ case T_STRING:
++ fprintf(f, ":%lu", RSTRING(p)->len);
++ break;
++ case T_ARRAY:
++ fprintf(f, ":%lu", RARRAY(p)->len);
++ break;
++ case T_HASH:
++ fprintf(f, ":%d", RHASH(p)->tbl->num_entries);
++ break;
++ }
+ }
+ fprintf(f, "\n");
+ }
+@@ -1924,10 +1936,10 @@ gc_sweep()
+ heap_slots_live_after_last_gc = live;
+
+ if (do_gc_stats) {
+- fprintf(gc_data_file, "objects processed: %.7d\n", live+freed);
+- fprintf(gc_data_file, "live objects : %.7d\n", live);
+- fprintf(gc_data_file, "freelist objects : %.7d\n", freed - really_freed);
+- fprintf(gc_data_file, "freed objects : %.7d\n", really_freed);
++ fprintf(gc_data_file, "objects processed: %.7lu\n", live+freed);
++ fprintf(gc_data_file, "live objects : %.7lu\n", live);
++ fprintf(gc_data_file, "freelist objects : %.7lu\n", freed - really_freed);
++ fprintf(gc_data_file, "freed objects : %.7lu\n", really_freed);
+ for(i=0; i<256; i++) {
+ if (free_counts[i]>0 || live_counts[i]>0) {
+ fprintf(gc_data_file,
+@@ -2258,7 +2270,7 @@ garbage_collect()
+ gc_time += musecs_used;
+
+ if (verbose_gc_stats) {
+- fprintf(gc_data_file, "GC time: %d msec\n", musecs_used / 1000);
++ fprintf(gc_data_file, "GC time: %ld msec\n", (long)(musecs_used / 1000));
+ fflush(gc_data_file);
+ }
+ }
View
230 patches/ruby/1.8.7/p357/railsexpress/16-caller-for-all-threads.patch
@@ -0,0 +1,230 @@
+diff --git a/eval.c b/eval.c
+index 356226e..a0fdc55 100644
+--- a/eval.c
++++ b/eval.c
+@@ -8199,6 +8199,17 @@ rb_f_method_name()
+ }
+ }
+
++/* Hash (Thread => Backtrace) used to collect backtrace for each threads. */
++static VALUE backtrace_for_each_thread;
++
++static int backtrace_level_for_each_thread;
++
++static VALUE
++switch_thread_context_to_collect_backtrace(rb_thread_t next);
++
++static VALUE
++rb_f_caller_for_all_threads();
++
+ void
+ Init_eval()
+ {
+@@ -8244,6 +8255,7 @@ Init_eval()
+ rb_define_global_function("fail", rb_f_raise, -1);
+
+ rb_define_global_function("caller", rb_f_caller, -1);
++ rb_define_global_function("caller_for_all_threads", rb_f_caller_for_all_threads, -1);
+
+ rb_define_global_function("exit", rb_f_exit, -1);
+ rb_define_global_function("abort", rb_f_abort, -1);
+@@ -10599,6 +10611,7 @@ static int th_sig, th_safe;
+ #define RESTORE_RAISE 5
+ #define RESTORE_SIGNAL 6
+ #define RESTORE_EXIT 7
++#define RESTORE_BACKTRACE 8
+
+ extern VALUE *rb_gc_stack_start;
+ #ifdef __ia64
+@@ -10705,6 +10718,15 @@ rb_thread_switch(n)
+ }
+ rb_exc_raise(th_raise_exception);
+ break;
++ case RESTORE_BACKTRACE:
++ rb_hash_aset(backtrace_for_each_thread, curr_thread->thread,
++ backtrace(backtrace_level_for_each_thread));
++ if (curr_thread != main_thread) {
++ switch_thread_context_to_collect_backtrace(curr_thread->next);
++ } else {
++ /* Circled back to main thread, cycle is complete. */
++ }
++ break;
+ case RESTORE_NORMAL:
+ default:
+ break;
+@@ -13875,3 +13897,74 @@ rb_throw(tag, val)
+ argv[1] = val;
+ rb_f_throw(2, argv);
+ }
++
++static VALUE
++switch_thread_context_to_collect_backtrace(rb_thread_t next)
++{
++ if (THREAD_SAVE_CONTEXT(curr_thread)) {
++ return Qnil;
++ }
++ curr_thread = next;
++ rb_thread_restore_context(next, RESTORE_BACKTRACE);
++ return Qnil;
++}
++
++
++/*
++ * call-seq:
++ * caller_for_all_threads(start=1) => array
++ *
++ * Returns the current execution stack for all threads
++ * ---a hash whose keys are thread instances and values
++ * the thread caller backtrace.
++ *
++ * Backtraces are array of hashes indicating location on the
++ * stack. Hash keys include ``<em>:line</em>'' or ``<em>:file</em>''
++ * and ``<em>:method'</em>''.
++ *
++ * The optional _start_ parameter
++ * determines the number of initial stack entries to omit from the
++ * result.
++ *
++ * def a(skip)
++ * caller_for_all_threads(skip)
++ * end
++ * def b(skip)
++ * a(skip)
++ * end
++ * def c(skip)
++ * b(skip)
++ * end
++ * c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10"]
++ * c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11"]
++ * c(2) #=> ["prog:8:in `c'", "prog:12"]
++ * c(3) #=> ["prog:13"]
++ */
++static VALUE
++rb_f_caller_for_all_threads(argc, argv)
++ int argc;
++ VALUE *argv;
++{
++ volatile int critical;
++ VALUE level;
++ VALUE result;
++
++ rb_scan_args(argc, argv, "01", &level);
++ backtrace_level_for_each_thread = NIL_P(level) ? 0 : NUM2INT(level);
++ if (backtrace_level_for_each_thread < 0) {
++ rb_raise(rb_eArgError, "negative level (%d)", backtrace_level_for_each_thread);
++ }
++
++ critical = rb_thread_critical;
++ rb_thread_critical = Qtrue;
++
++ backtrace_for_each_thread = rb_hash_new();
++ switch_thread_context_to_collect_backtrace(main_thread->next);
++
++ result = backtrace_for_each_thread;
++ backtrace_for_each_thread = Qnil;
++ backtrace_for_each_thread = 0;
++
++ rb_thread_critical = critical;
++ return result;
++}
+diff --git a/test/callerforallthreads/test_caller_for_each_thread.rb b/test/callerforallthreads/test_caller_for_each_thread.rb
+new file mode 100644
+index 0000000..6aebaed
+--- /dev/null
++++ b/test/callerforallthreads/test_caller_for_each_thread.rb
+@@ -0,0 +1,95 @@
++# -*- ruby-indent-level: 4 -*-
++require 'thread'
++require 'test/unit'
++
++class AClassWithNestedmethods
++
++ def an_ultra_nested_method(skip)
++ caller_for_all_threads skip
++ end
++
++ def a_nested_method(skip)
++ an_ultra_nested_method skip
++ end
++
++ def a_method(skip=0)
++ a_nested_method skip
++ end
++
++end
++
++class CallerForEachThreadTest < Test::Unit::TestCase
++
++ def testCollectMeaningfulBacktraceForASingleThread
++ backtraces = AClassWithNestedmethods.new.a_method
++ backtrace = backtraces[Thread.current]
++ assert_not_nil backtrace
++ assert_equal __FILE__ + ":8:in `an_ultra_nested_method'", backtrace[0]
++ assert_equal __FILE__ + ":12:in `a_nested_method'", backtrace[1]
++ assert_equal __FILE__ + ":16:in `a_method'", backtrace[2]
++ assert_equal __FILE__ + ":24:in `testCollectMeaningfulBacktraceForASingleThread'",
++ backtrace[3]
++ end
++
++ def testCanSkipFirstStackEntries
++ backtraces = AClassWithNestedmethods.new.a_method 2
++ backtrace = backtraces[Thread.current]
++ assert_not_nil backtrace
++ assert_equal __FILE__ + ":16:in `a_method'", backtrace[0]
++ assert_equal __FILE__ + ":35:in `testCanSkipFirstStackEntries'",
++ backtrace[1]
++ end
++
++ def testCollectMeaningfulBacktraceForMultipleThreads
++ first_thread = Thread.new do
++ loop do
++ Thread.pass
++ sleep 1
++ end
++ end
++
++ second_thread = Thread.new do
++ loop do
++ Thread.pass
++ sleep 1
++ end
++ end
++
++ backtraces = AClassWithNestedmethods.new.a_method
++
++ backtrace = backtraces[Thread.current]
++ assert_not_nil backtrace
++ assert_match __FILE__ + ":8:in `an_ultra_nested_method'", backtrace[0]
++ assert_match __FILE__ + ":12:in `a_nested_method'", backtrace[1]
++ assert_equal __FILE__ + ":16:in `a_method'", backtrace[2]
++ assert_equal __FILE__ + ":58:in `testCollectMeaningfulBacktraceForMultipleThreads'",
++ backtrace[3]
++
++ backtrace = backtraces[first_thread]
++ assert_not_nil backtrace
++ assert_equal __FILE__ + ":47:in `testCollectMeaningfulBacktraceForMultipleThreads'",
++ backtrace[0]
++ assert_equal __FILE__ + ":45:in `loop'",
++ backtrace[1]
++ assert_equal __FILE__ + ":45:in `testCollectMeaningfulBacktraceForMultipleThreads'",
++ backtrace[2]
++ assert_equal __FILE__ + ":44:in `initialize'",backtrace[3]
++ assert_equal __FILE__ + ":44:in `new'", backtrace[4]
++ assert_equal __FILE__ + ":44:in `testCollectMeaningfulBacktraceForMultipleThreads'",
++ backtrace[5]
++
++ backtrace = backtraces[second_thread]
++ assert_not_nil backtrace
++ assert_equal __FILE__ + ":53:in `testCollectMeaningfulBacktraceForMultipleThreads'",
++ backtrace[0]
++ assert_equal __FILE__ + ":52:in `loop'", backtrace[1]
++ assert_equal __FILE__ + ":52:in `testCollectMeaningfulBacktraceForMultipleThreads'",
++ backtrace[2]
++ assert_equal __FILE__ + ":51:in `initialize'",backtrace[3]
++ assert_equal __FILE__ + ":51:in `new'", backtrace[4]
++ assert_equal __FILE__ + ":51:in `testCollectMeaningfulBacktraceForMultipleThreads'",
++ backtrace[5]
++ end
++
++end
++
View
16 patchsets/ruby/1.8.7/p357/railsexpress
@@ -0,0 +1,16 @@
+railsexpress/01-ignore-generated-files.patch
+railsexpress/02-sigvtalrm-fix.patch
+railsexpress/03-railsbench-gc-patch.patch
+railsexpress/04-display-full-stack-trace.patch
+railsexpress/05-better-source-file-tracing.patch
+railsexpress/06-heap-dump-support.patch
+railsexpress/07-fork-support-for-gc-logging.patch
+railsexpress/08-track-malloc-size.patch
+railsexpress/09-track-object-allocation.patch
+railsexpress/10-expose-heap-slots.patch
+railsexpress/11-fix-heap-size-growth-logic.patch
+railsexpress/12-heap-slot-size.patch
+railsexpress/13-add-trace-stats-enabled-methods.patch
+railsexpress/14-track-live-dataset-size.patch
+railsexpress/15-add-object-size-information-to-heap-dump.patch
+railsexpress/16-caller-for-all-threads.patch

0 comments on commit a33ff25

Please sign in to comment.
Something went wrong with that request. Please try again.