Permalink
Browse files

New caller_for_all_threads patch for M.R.I 1.8.7

  • Loading branch information...
ph7 committed Jul 23, 2008
1 parent 815c718 commit eed75a348fa9c6e41347e0496068629eb863b07e
Showing with 229 additions and 0 deletions.
  1. +229 −0 patches_for_mri/caller_for_all_threads_patch_for_MRI_1.8.7.diff
@@ -0,0 +1,229 @@
+Index: test/callerforallthreads/test_caller_for_each_thread.rb
+===================================================================
+--- test/callerforallthreads/test_caller_for_each_thread.rb (revision 0)
++++ test/callerforallthreads/test_caller_for_each_thread.rb (revision 0)
+@@ -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
++
+Index: eval.c
+===================================================================
+--- eval.c (revision 16289)
++++ eval.c (working copy)
+@@ -7969,6 +7969,17 @@
+ ruby_safe_level = safe;
+ }
+
++/* 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()
+ {
+@@ -8014,6 +8025,7 @@
+ 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);
+@@ -10206,6 +10218,7 @@
+ #define RESTORE_RAISE 5
+ #define RESTORE_SIGNAL 6
+ #define RESTORE_EXIT 7
++#define RESTORE_BACKTRACE 8
+
+ extern VALUE *rb_gc_stack_start;
+ #ifdef __ia64
+@@ -10313,6 +10326,15 @@
+ }
+ 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;
+@@ -13240,3 +13262,74 @@
+ 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;
++}

0 comments on commit eed75a3

Please sign in to comment.