xray 1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +65 -0
- data/bin/install_dtrace_on_ubuntu +30 -0
- data/bin/xray_profile_ruby_function_calls.d +54 -0
- data/bin/xray_top_10_busiest_code_path_for_process.d +12 -0
- data/bin/xray_top_10_busiest_code_path_on_system.d +35 -0
- data/bin/xray_trace_all_custom_ruby_probes.d +25 -0
- data/bin/xray_trace_all_ruby_probes.d +20 -0
- data/bin/xray_trace_memcached.d +20 -0
- data/bin/xray_trace_mysql.d +30 -0
- data/bin/xray_trace_rails_response_times.d +114 -0
- data/lib/xray.rb +2 -0
- data/lib/xray/dtrace/rails/action_controller_tracing_extension.rb +22 -0
- data/lib/xray/dtrace/rails/active_record_connection_tracing_extension.rb +15 -0
- data/lib/xray/dtrace/rails/enable_tracing.rb +25 -0
- data/lib/xray/dtrace/tracer.rb +86 -0
- data/lib/xray/dtrace/tracer/joyent.rb +25 -0
- data/lib/xray/dtrace/tracer/leopard.rb +28 -0
- data/lib/xray/dtrace/usdt/provider_extensions.rb +45 -0
- data/lib/xray/thread_dump_signal_handler.rb +31 -0
- data/patches_for_mri/caller_for_all_threads_patch_for_MRI_1.8.6.diff +229 -0
- data/patches_for_mri/caller_for_all_threads_patch_for_MRI_1.8.7.diff +229 -0
- data/patches_for_mri/patch-for-dtrace-instrumentation-of-matz-1.8.6-p114.diff +522 -0
- data/patches_for_mri/patch-for-joyent-mri-1.8.6-on-mac-os-x-leopard.diff +20645 -0
- data/rails/init.rb +2 -0
- data/test/all_tests.rb +1 -0
- data/test/functional/dtrace/tracer_test.rb +54 -0
- data/test/functional/tracer_script.rb +18 -0
- data/test/test_helper.rb +9 -0
- data/test/unit/xray/dtrace/tracer_test.rb +54 -0
- metadata +84 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
# Require this file to enable out the box DTrace tracing
|
2
|
+
# for your Rails application
|
3
|
+
#
|
4
|
+
# You typically change your environment.rb to require it after config
|
5
|
+
# initialization:
|
6
|
+
#
|
7
|
+
# Rails::Initializer.run do |config|
|
8
|
+
#
|
9
|
+
# ...
|
10
|
+
#
|
11
|
+
# config.after_initialize do
|
12
|
+
# require "xray/dtrace/rails/enable_tracing"
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
|
16
|
+
require File.expand_path(File.dirname(__FILE__) + '/../tracer')
|
17
|
+
if Object.const_defined? :ActionController
|
18
|
+
puts "XRay: Enabling Rails Controller Tracing"
|
19
|
+
require File.expand_path(File.dirname(__FILE__) + "/../rails/action_controller_tracing_extension")
|
20
|
+
end
|
21
|
+
|
22
|
+
if Object.const_defined? :ActiveRecord
|
23
|
+
puts "XRay: Enabling Rails DB Tracing"
|
24
|
+
require File.expand_path(File.dirname(__FILE__) + "/../rails/active_record_connection_tracing_extension")
|
25
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/tracer/leopard.rb'
|
2
|
+
require File.dirname(__FILE__) + '/tracer/joyent.rb'
|
3
|
+
|
4
|
+
module XRay
|
5
|
+
module DTrace
|
6
|
+
|
7
|
+
# Ruby module to fire application-level Dtrace events (using ruby-probe).
|
8
|
+
#
|
9
|
+
# This module provide a convenient and unified API abstracting
|
10
|
+
# different tracing implementations in Leopard Ruby VM (by Apple)
|
11
|
+
# and in the one provided by Joyent (https://dev.joyent.com/projects/ruby-dtrace).
|
12
|
+
# This module also provides a NOOP implementation for Ruby VMs with
|
13
|
+
# no DTrace support: So you can use the exact same code while developing
|
14
|
+
# on Linux and deploying on Solaris for instance.
|
15
|
+
module Tracer
|
16
|
+
|
17
|
+
# Fire an application-level probe using ruby-probe.
|
18
|
+
#
|
19
|
+
# The first argument passed will be passed to the D script as arg0 for
|
20
|
+
# the ruby-probe probe. This is conventionally a probe name.
|
21
|
+
#
|
22
|
+
# The second argument is optional and can be used to pass additional
|
23
|
+
# data into the D script as arg1.
|
24
|
+
#
|
25
|
+
# Example:
|
26
|
+
#
|
27
|
+
# XRay::DTrace::Tracer.fire('service-start', "order processing")
|
28
|
+
#
|
29
|
+
# XRay::DTrace::Tracer.fire('service-stop')
|
30
|
+
#
|
31
|
+
def fire(name, data = nil)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Use ruby-probe to fire 2 application-level probes before and after
|
35
|
+
# evaling a block .
|
36
|
+
#
|
37
|
+
# The first argument passed will be passed to the D script as arg0 for
|
38
|
+
# the ruby-probe probe. The first argument is conventionally a
|
39
|
+
# probe base name. The probes which fire before and after the block
|
40
|
+
# runs will have "-start" and "-end" appended to the probe base name,
|
41
|
+
# respectively.
|
42
|
+
#
|
43
|
+
# The second argument is optional and can be used to pass additional
|
44
|
+
# data into the D script as arg1.
|
45
|
+
#
|
46
|
+
# Example:
|
47
|
+
#
|
48
|
+
# XRay::DTrace::Tracer.firing('db-query', "select * from dual;") do
|
49
|
+
# ActiveRecord::Base.execute("select * from dual;")
|
50
|
+
#
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# Will:
|
54
|
+
# 1. Fire a probe with arg0 set to "db-query-start", and arg1 set
|
55
|
+
# to the sql query.
|
56
|
+
#
|
57
|
+
# 2. Run the block and execute the SQL.
|
58
|
+
#
|
59
|
+
# 3. Fire a probe with arg0 set to "db-query-start".
|
60
|
+
def firing(name, data = nil)
|
61
|
+
yield
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns true if ruby-probe probes are enabled.
|
65
|
+
# (application-level probes for Ruby).
|
66
|
+
def enabled?
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
if Object.const_defined?(:DTracer)
|
73
|
+
actual_tracer = Tracer::Leopard
|
74
|
+
send :remove_const, :Tracer
|
75
|
+
Tracer = actual_tracer
|
76
|
+
elsif Object.const_defined?(:Tracer) && Tracer.methods.include?(:fire)
|
77
|
+
actual_tracer = Tracer::Joyent
|
78
|
+
send :remove_const, :Tracer
|
79
|
+
Tracer = actual_tracer
|
80
|
+
else
|
81
|
+
# No DTrace support, keep the NOOP implementation
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module XRay
|
2
|
+
module DTrace
|
3
|
+
module Tracer
|
4
|
+
|
5
|
+
# Wrapper around OS X DTracer exposing a custom API and namespace.
|
6
|
+
module Joyent
|
7
|
+
|
8
|
+
def fire(name, data = nil) #:nodoc: all
|
9
|
+
::Tracer.fire(name, data)
|
10
|
+
end
|
11
|
+
|
12
|
+
def firing(name, data = nil) #:nodoc: all
|
13
|
+
::Tracer.fire(name, data)
|
14
|
+
end
|
15
|
+
|
16
|
+
def enabled? #:nodoc: all
|
17
|
+
STDERR.puts "WARNING: XRay::DTrace.Tracer.enabled? does not work with Joyent Tracer"
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module XRay
|
2
|
+
module DTrace
|
3
|
+
module Tracer
|
4
|
+
|
5
|
+
# Wrapper around OS X DTracer exposing a custom API and namespace.
|
6
|
+
module Leopard
|
7
|
+
|
8
|
+
def fire(name, data = nil)
|
9
|
+
DTracer.fire(name, data)
|
10
|
+
end
|
11
|
+
|
12
|
+
def firing(name, data = nil)
|
13
|
+
fire(name + "-start", data)
|
14
|
+
result = yield
|
15
|
+
fire(name + "-end", data)
|
16
|
+
result
|
17
|
+
end
|
18
|
+
|
19
|
+
def enabled?
|
20
|
+
DTracer.enabled?
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# Extend ruby-dtrace API with a more convenient one to fire custom USDT
|
3
|
+
# probes
|
4
|
+
#
|
5
|
+
|
6
|
+
require "rubygems"
|
7
|
+
require 'dtrace/provider'
|
8
|
+
|
9
|
+
class Dtrace
|
10
|
+
|
11
|
+
class Provider
|
12
|
+
|
13
|
+
alias :original_load :load
|
14
|
+
def load
|
15
|
+
provider_module = original_load
|
16
|
+
|
17
|
+
xray_extension_module = Module.new do
|
18
|
+
@@provider_module = provider_module
|
19
|
+
|
20
|
+
def firing(function_prefix, *args)
|
21
|
+
@@provider_module.send :"#{function_prefix}_start" do |probe|
|
22
|
+
probe.fire(*args)
|
23
|
+
end
|
24
|
+
result = yield
|
25
|
+
@@provider_module.send :"#{function_prefix}_end" do |probe|
|
26
|
+
probe.fire(*args)
|
27
|
+
end
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
def fire(function_name, *args)
|
32
|
+
@@provider_module.send function_name do |p|
|
33
|
+
p.fire(*args)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
provider_module.const_set :XRay, xray_extension_module
|
40
|
+
provider_module
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# Install a signal handler to dump backtraces for all threads
|
3
|
+
#
|
4
|
+
# Trigger it with: kill -QUIT <pid>
|
5
|
+
#
|
6
|
+
trap "QUIT" do
|
7
|
+
if Kernel.respond_to? :caller_for_all_threads
|
8
|
+
STDERR.puts "\n=============== XRay - Thread Dump ==============="
|
9
|
+
caller_for_all_threads.each_pair do |thread, stack|
|
10
|
+
thread_description = thread.inspect
|
11
|
+
thread_description << " [main]" if thread == Thread.main
|
12
|
+
thread_description << " [current]" if thread == Thread.current
|
13
|
+
thread_description << " alive=#{thread.alive?}"
|
14
|
+
thread_description << " priority=#{thread.priority}"
|
15
|
+
thread_separator = "-" * 78
|
16
|
+
|
17
|
+
full_description = "\n#{thread_separator}\n"
|
18
|
+
full_description << thread_description
|
19
|
+
full_description << "\n#{thread_separator}\n"
|
20
|
+
full_description << " #{stack.join("\n \\_ ")}\n"
|
21
|
+
|
22
|
+
# Single puts to avoid interleaved output
|
23
|
+
STDERR.puts full_description
|
24
|
+
end
|
25
|
+
else
|
26
|
+
STDERR.puts "=============== XRay - Current Thread Backtrace ==============="
|
27
|
+
STDERR.puts "Current thread : #{Thread.inspect}"
|
28
|
+
STDERR.puts caller.join("\n \\_ ")
|
29
|
+
end
|
30
|
+
STDERR.puts "\n=============== XRay - Done ===============\n"
|
31
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
Index: test/callerforallthreads/test_caller_for_each_thread.rb
|
2
|
+
===================================================================
|
3
|
+
--- test/callerforallthreads/test_caller_for_each_thread.rb (revision 0)
|
4
|
+
+++ test/callerforallthreads/test_caller_for_each_thread.rb (revision 0)
|
5
|
+
@@ -0,0 +1,95 @@
|
6
|
+
+# -*- ruby-indent-level: 4 -*-
|
7
|
+
+require 'thread'
|
8
|
+
+require 'test/unit'
|
9
|
+
+
|
10
|
+
+class AClassWithNestedmethods
|
11
|
+
+
|
12
|
+
+ def an_ultra_nested_method(skip)
|
13
|
+
+ caller_for_all_threads skip
|
14
|
+
+ end
|
15
|
+
+
|
16
|
+
+ def a_nested_method(skip)
|
17
|
+
+ an_ultra_nested_method skip
|
18
|
+
+ end
|
19
|
+
+
|
20
|
+
+ def a_method(skip=0)
|
21
|
+
+ a_nested_method skip
|
22
|
+
+ end
|
23
|
+
+
|
24
|
+
+end
|
25
|
+
+
|
26
|
+
+class CallerForEachThreadTest < Test::Unit::TestCase
|
27
|
+
+
|
28
|
+
+ def testCollectMeaningfulBacktraceForASingleThread
|
29
|
+
+ backtraces = AClassWithNestedmethods.new.a_method
|
30
|
+
+ backtrace = backtraces[Thread.current]
|
31
|
+
+ assert_not_nil backtrace
|
32
|
+
+ assert_equal __FILE__ + ":8:in `an_ultra_nested_method'", backtrace[0]
|
33
|
+
+ assert_equal __FILE__ + ":12:in `a_nested_method'", backtrace[1]
|
34
|
+
+ assert_equal __FILE__ + ":16:in `a_method'", backtrace[2]
|
35
|
+
+ assert_equal __FILE__ + ":24:in `testCollectMeaningfulBacktraceForASingleThread'",
|
36
|
+
+ backtrace[3]
|
37
|
+
+ end
|
38
|
+
+
|
39
|
+
+ def testCanSkipFirstStackEntries
|
40
|
+
+ backtraces = AClassWithNestedmethods.new.a_method 2
|
41
|
+
+ backtrace = backtraces[Thread.current]
|
42
|
+
+ assert_not_nil backtrace
|
43
|
+
+ assert_equal __FILE__ + ":16:in `a_method'", backtrace[0]
|
44
|
+
+ assert_equal __FILE__ + ":35:in `testCanSkipFirstStackEntries'",
|
45
|
+
+ backtrace[1]
|
46
|
+
+ end
|
47
|
+
+
|
48
|
+
+ def testCollectMeaningfulBacktraceForMultipleThreads
|
49
|
+
+ first_thread = Thread.new do
|
50
|
+
+ loop do
|
51
|
+
+ Thread.pass
|
52
|
+
+ sleep 1
|
53
|
+
+ end
|
54
|
+
+ end
|
55
|
+
+
|
56
|
+
+ second_thread = Thread.new do
|
57
|
+
+ loop do
|
58
|
+
+ Thread.pass
|
59
|
+
+ sleep 1
|
60
|
+
+ end
|
61
|
+
+ end
|
62
|
+
+
|
63
|
+
+ backtraces = AClassWithNestedmethods.new.a_method
|
64
|
+
+
|
65
|
+
+ backtrace = backtraces[Thread.current]
|
66
|
+
+ assert_not_nil backtrace
|
67
|
+
+ assert_match __FILE__ + ":8:in `an_ultra_nested_method'", backtrace[0]
|
68
|
+
+ assert_match __FILE__ + ":12:in `a_nested_method'", backtrace[1]
|
69
|
+
+ assert_equal __FILE__ + ":16:in `a_method'", backtrace[2]
|
70
|
+
+ assert_equal __FILE__ + ":58:in `testCollectMeaningfulBacktraceForMultipleThreads'",
|
71
|
+
+ backtrace[3]
|
72
|
+
+
|
73
|
+
+ backtrace = backtraces[first_thread]
|
74
|
+
+ assert_not_nil backtrace
|
75
|
+
+ assert_equal __FILE__ + ":47:in `testCollectMeaningfulBacktraceForMultipleThreads'",
|
76
|
+
+ backtrace[0]
|
77
|
+
+ assert_equal __FILE__ + ":45:in `loop'",
|
78
|
+
+ backtrace[1]
|
79
|
+
+ assert_equal __FILE__ + ":45:in `testCollectMeaningfulBacktraceForMultipleThreads'",
|
80
|
+
+ backtrace[2]
|
81
|
+
+ assert_equal __FILE__ + ":44:in `initialize'",backtrace[3]
|
82
|
+
+ assert_equal __FILE__ + ":44:in `new'", backtrace[4]
|
83
|
+
+ assert_equal __FILE__ + ":44:in `testCollectMeaningfulBacktraceForMultipleThreads'",
|
84
|
+
+ backtrace[5]
|
85
|
+
+
|
86
|
+
+ backtrace = backtraces[second_thread]
|
87
|
+
+ assert_not_nil backtrace
|
88
|
+
+ assert_equal __FILE__ + ":53:in `testCollectMeaningfulBacktraceForMultipleThreads'",
|
89
|
+
+ backtrace[0]
|
90
|
+
+ assert_equal __FILE__ + ":52:in `loop'", backtrace[1]
|
91
|
+
+ assert_equal __FILE__ + ":52:in `testCollectMeaningfulBacktraceForMultipleThreads'",
|
92
|
+
+ backtrace[2]
|
93
|
+
+ assert_equal __FILE__ + ":51:in `initialize'",backtrace[3]
|
94
|
+
+ assert_equal __FILE__ + ":51:in `new'", backtrace[4]
|
95
|
+
+ assert_equal __FILE__ + ":51:in `testCollectMeaningfulBacktraceForMultipleThreads'",
|
96
|
+
+ backtrace[5]
|
97
|
+
+ end
|
98
|
+
+
|
99
|
+
+end
|
100
|
+
+
|
101
|
+
Index: eval.c
|
102
|
+
===================================================================
|
103
|
+
--- eval.c (revision 16289)
|
104
|
+
+++ eval.c (working copy)
|
105
|
+
@@ -7969,6 +7969,17 @@
|
106
|
+
ruby_safe_level = safe;
|
107
|
+
}
|
108
|
+
|
109
|
+
+/* Hash (Thread => Backtrace) used to collect backtrace for each threads. */
|
110
|
+
+static VALUE backtrace_for_each_thread;
|
111
|
+
+
|
112
|
+
+static int backtrace_level_for_each_thread;
|
113
|
+
+
|
114
|
+
+static VALUE
|
115
|
+
+switch_thread_context_to_collect_backtrace(rb_thread_t next);
|
116
|
+
+
|
117
|
+
+static VALUE
|
118
|
+
+rb_f_caller_for_all_threads();
|
119
|
+
+
|
120
|
+
void
|
121
|
+
Init_eval()
|
122
|
+
{
|
123
|
+
@@ -8014,6 +8025,7 @@
|
124
|
+
rb_define_global_function("fail", rb_f_raise, -1);
|
125
|
+
|
126
|
+
rb_define_global_function("caller", rb_f_caller, -1);
|
127
|
+
+ rb_define_global_function("caller_for_all_threads", rb_f_caller_for_all_threads, -1);
|
128
|
+
|
129
|
+
rb_define_global_function("exit", rb_f_exit, -1);
|
130
|
+
rb_define_global_function("abort", rb_f_abort, -1);
|
131
|
+
@@ -10206,6 +10218,7 @@
|
132
|
+
#define RESTORE_RAISE 5
|
133
|
+
#define RESTORE_SIGNAL 6
|
134
|
+
#define RESTORE_EXIT 7
|
135
|
+
+#define RESTORE_BACKTRACE 8
|
136
|
+
|
137
|
+
extern VALUE *rb_gc_stack_start;
|
138
|
+
#ifdef __ia64
|
139
|
+
@@ -10313,6 +10326,15 @@
|
140
|
+
}
|
141
|
+
rb_exc_raise(th_raise_exception);
|
142
|
+
break;
|
143
|
+
+ case RESTORE_BACKTRACE:
|
144
|
+
+ rb_hash_aset(backtrace_for_each_thread, curr_thread->thread,
|
145
|
+
+ backtrace(backtrace_level_for_each_thread));
|
146
|
+
+ if (curr_thread != main_thread) {
|
147
|
+
+ switch_thread_context_to_collect_backtrace(curr_thread->next);
|
148
|
+
+ } else {
|
149
|
+
+ /* Circled back to main thread, cycle is complete. */
|
150
|
+
+ }
|
151
|
+
+ break;
|
152
|
+
case RESTORE_NORMAL:
|
153
|
+
default:
|
154
|
+
break;
|
155
|
+
@@ -13240,3 +13262,74 @@
|
156
|
+
argv[1] = val;
|
157
|
+
rb_f_throw(2, argv);
|
158
|
+
}
|
159
|
+
+
|
160
|
+
+static VALUE
|
161
|
+
+switch_thread_context_to_collect_backtrace(rb_thread_t next)
|
162
|
+
+{
|
163
|
+
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
|
164
|
+
+ return Qnil;
|
165
|
+
+ }
|
166
|
+
+ curr_thread = next;
|
167
|
+
+ rb_thread_restore_context(next, RESTORE_BACKTRACE);
|
168
|
+
+ return Qnil;
|
169
|
+
+}
|
170
|
+
+
|
171
|
+
+
|
172
|
+
+/*
|
173
|
+
+ * call-seq:
|
174
|
+
+ * caller_for_all_threads(start=1) => array
|
175
|
+
+ *
|
176
|
+
+ * Returns the current execution stack for all threads
|
177
|
+
+ * ---a hash whose keys are thread instances and values
|
178
|
+
+ * the thread caller backtrace.
|
179
|
+
+ *
|
180
|
+
+ * Backtraces are array of hashes indicating location on the
|
181
|
+
+ * stack. Hash keys include ``<em>:line</em>'' or ``<em>:file</em>''
|
182
|
+
+ * and ``<em>:method'</em>''.
|
183
|
+
+ *
|
184
|
+
+ * The optional _start_ parameter
|
185
|
+
+ * determines the number of initial stack entries to omit from the
|
186
|
+
+ * result.
|
187
|
+
+ *
|
188
|
+
+ * def a(skip)
|
189
|
+
+ * caller_for_all_threads(skip)
|
190
|
+
+ * end
|
191
|
+
+ * def b(skip)
|
192
|
+
+ * a(skip)
|
193
|
+
+ * end
|
194
|
+
+ * def c(skip)
|
195
|
+
+ * b(skip)
|
196
|
+
+ * end
|
197
|
+
+ * c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10"]
|
198
|
+
+ * c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11"]
|
199
|
+
+ * c(2) #=> ["prog:8:in `c'", "prog:12"]
|
200
|
+
+ * c(3) #=> ["prog:13"]
|
201
|
+
+ */
|
202
|
+
+static VALUE
|
203
|
+
+rb_f_caller_for_all_threads(argc, argv)
|
204
|
+
+ int argc;
|
205
|
+
+ VALUE *argv;
|
206
|
+
+{
|
207
|
+
+ volatile int critical;
|
208
|
+
+ VALUE level;
|
209
|
+
+ VALUE result;
|
210
|
+
+
|
211
|
+
+ rb_scan_args(argc, argv, "01", &level);
|
212
|
+
+ backtrace_level_for_each_thread = NIL_P(level) ? 0 : NUM2INT(level);
|
213
|
+
+ if (backtrace_level_for_each_thread < 0) {
|
214
|
+
+ rb_raise(rb_eArgError, "negative level (%d)", backtrace_level_for_each_thread);
|
215
|
+
+ }
|
216
|
+
+
|
217
|
+
+ critical = rb_thread_critical;
|
218
|
+
+ rb_thread_critical = Qtrue;
|
219
|
+
+
|
220
|
+
+ backtrace_for_each_thread = rb_hash_new();
|
221
|
+
+ switch_thread_context_to_collect_backtrace(main_thread->next);
|
222
|
+
+
|
223
|
+
+ result = backtrace_for_each_thread;
|
224
|
+
+ backtrace_for_each_thread = Qnil;
|
225
|
+
+ backtrace_for_each_thread = 0;
|
226
|
+
+
|
227
|
+
+ rb_thread_critical = critical;
|
228
|
+
+ return result;
|
229
|
+
+}
|