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,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
|
+
+}
|
@@ -0,0 +1,522 @@
|
|
1
|
+
Index: Makefile.in
|
2
|
+
===================================================================
|
3
|
+
--- Makefile.in (revision 16089)
|
4
|
+
+++ Makefile.in (working copy)
|
5
|
+
@@ -86,6 +86,8 @@
|
6
|
+
OBJEXT = @OBJEXT@
|
7
|
+
MANTYPE = @MANTYPE@
|
8
|
+
|
9
|
+
+DTRACE_HEADER = @DTRACE_HEADER@
|
10
|
+
+
|
11
|
+
INSTALLED_LIST= .installed.list
|
12
|
+
#### End of variables
|
13
|
+
|
14
|
+
@@ -171,8 +173,11 @@
|
15
|
+
$(AS) $(ASFLAGS) -o $@ $<
|
16
|
+
|
17
|
+
clean-local::
|
18
|
+
- @$(RM) ext/extinit.c ext/extinit.$(OBJEXT)
|
19
|
+
+ @$(RM) ext/extinit.c ext/extinit.$(OBJEXT) $(DTRACE_HEADER)
|
20
|
+
|
21
|
+
+dtrace.h: dtrace.d
|
22
|
+
+ /usr/sbin/dtrace -h -s dtrace.d
|
23
|
+
+
|
24
|
+
distclean-local::
|
25
|
+
@$(RM) ext/config.cache $(RBCONFIG)
|
26
|
+
|
27
|
+
Index: configure.in
|
28
|
+
===================================================================
|
29
|
+
--- configure.in (revision 16089)
|
30
|
+
+++ configure.in (working copy)
|
31
|
+
@@ -488,6 +488,11 @@
|
32
|
+
sys/mkdev.h sys/utime.h netinet/in_systm.h float.h ieeefp.h pthread.h \
|
33
|
+
ucontext.h intrinsics.h)
|
34
|
+
|
35
|
+
+AC_CHECK_HEADER(sys/sdt.h)
|
36
|
+
+if test "$ac_cv_header_sys_sdt_h" == "yes"; then
|
37
|
+
+ AC_DEFINE(HAVE_SDT_H)
|
38
|
+
+fi
|
39
|
+
+
|
40
|
+
dnl Check additional types.
|
41
|
+
AC_CHECK_SIZEOF(rlim_t, 0, [
|
42
|
+
#ifdef HAVE_SYS_TYPES_H
|
43
|
+
@@ -547,6 +552,18 @@
|
44
|
+
AC_DEFINE(USE_SETREUID)
|
45
|
+
AC_DEFINE(USE_SETREGID)
|
46
|
+
fi
|
47
|
+
+
|
48
|
+
+AC_ARG_ENABLE(dtrace,
|
49
|
+
+ [ --enable-dtrace enable DTrace support.],
|
50
|
+
+ [enable_dtrace=$enableval])
|
51
|
+
+if test "$enable_dtrace" == "yes" -a "$ac_cv_header_sys_sdt_h" == "yes"; then
|
52
|
+
+ AC_DEFINE(ENABLE_DTRACE)
|
53
|
+
+ DTRACE_HEADER="dtrace.h"
|
54
|
+
+else
|
55
|
+
+ DTRACE_HEADER=""
|
56
|
+
+fi
|
57
|
+
+AC_SUBST(DTRACE_HEADER)
|
58
|
+
+
|
59
|
+
AC_STRUCT_TIMEZONE
|
60
|
+
AC_CACHE_CHECK(for struct tm.tm_gmtoff, rb_cv_member_struct_tm_tm_gmtoff,
|
61
|
+
[AC_TRY_COMPILE([#include <time.h>],
|
62
|
+
Index: inits.c
|
63
|
+
===================================================================
|
64
|
+
--- inits.c (revision 16089)
|
65
|
+
+++ inits.c (working copy)
|
66
|
+
@@ -45,6 +45,7 @@
|
67
|
+
void Init_Time _((void));
|
68
|
+
void Init_var_tables _((void));
|
69
|
+
void Init_version _((void));
|
70
|
+
+void Init_Tracer _((void));
|
71
|
+
|
72
|
+
void
|
73
|
+
rb_call_inits()
|
74
|
+
@@ -81,4 +82,5 @@
|
75
|
+
Init_GC();
|
76
|
+
Init_marshal();
|
77
|
+
Init_version();
|
78
|
+
+ Init_Tracer();
|
79
|
+
}
|
80
|
+
Index: object.c
|
81
|
+
===================================================================
|
82
|
+
--- object.c (revision 16089)
|
83
|
+
+++ object.c (working copy)
|
84
|
+
@@ -19,7 +19,35 @@
|
85
|
+
#include <errno.h>
|
86
|
+
#include <ctype.h>
|
87
|
+
#include <math.h>
|
88
|
+
+#ifdef ENABLE_DTRACE
|
89
|
+
+#include "dtrace.h"
|
90
|
+
+#include "node.h"
|
91
|
+
+#endif
|
92
|
+
|
93
|
+
+#ifdef ENABLE_DTRACE
|
94
|
+
+
|
95
|
+
+#define FIRE_OBJECT_CREATE_START_PROBE(ruby_current_node, klass) \
|
96
|
+
+ if (RUBY_OBJECT_CREATE_START_ENABLED()) { \
|
97
|
+
+ char *file = ruby_current_node == NULL ? "" : ruby_current_node->nd_file; \
|
98
|
+
+ int line = ruby_current_node == NULL ? 0 : nd_line(ruby_current_node); \
|
99
|
+
+ RUBY_OBJECT_CREATE_START(rb_class2name(klass), file, line); \
|
100
|
+
+ }
|
101
|
+
+
|
102
|
+
+#define FIRE_OBJECT_CREATE_DONE_PROBE(ruby_current_node, klass) \
|
103
|
+
+ if (RUBY_OBJECT_CREATE_DONE_ENABLED()) { \
|
104
|
+
+ char *file = ruby_current_node == NULL ? "" : ruby_current_node->nd_file; \
|
105
|
+
+ int line = ruby_current_node == NULL ? 0 : nd_line(ruby_current_node); \
|
106
|
+
+ RUBY_OBJECT_CREATE_DONE(rb_class2name(klass), file, line); \
|
107
|
+
+ }
|
108
|
+
+
|
109
|
+
+#else
|
110
|
+
+
|
111
|
+
+#define FIRE_OBJECT_CREATE_START_PROBE(ruby_current_node, klass) /* NOOP */
|
112
|
+
+#define FIRE_OBJECT_CREATE_DONE_PROBE(ruby_current_node, klass) /* NOOP */
|
113
|
+
+
|
114
|
+
+#endif
|
115
|
+
+
|
116
|
+
+
|
117
|
+
VALUE rb_mKernel;
|
118
|
+
VALUE rb_cObject;
|
119
|
+
VALUE rb_cModule;
|
120
|
+
@@ -1553,7 +1581,9 @@
|
121
|
+
if (FL_TEST(klass, FL_SINGLETON)) {
|
122
|
+
rb_raise(rb_eTypeError, "can't create instance of virtual class");
|
123
|
+
}
|
124
|
+
+ FIRE_OBJECT_CREATE_START_PROBE(ruby_current_node, klass);
|
125
|
+
obj = rb_funcall(klass, ID_ALLOCATOR, 0, 0);
|
126
|
+
+ FIRE_OBJECT_CREATE_DONE_PROBE(ruby_current_node, klass);
|
127
|
+
if (rb_obj_class(obj) != rb_class_real(klass)) {
|
128
|
+
rb_raise(rb_eTypeError, "wrong instance allocation");
|
129
|
+
}
|
130
|
+
Index: tracer.c
|
131
|
+
===================================================================
|
132
|
+
--- tracer.c (revision 0)
|
133
|
+
+++ tracer.c (revision 0)
|
134
|
+
@@ -0,0 +1,73 @@
|
135
|
+
+#include "ruby.h"
|
136
|
+
+
|
137
|
+
+#ifdef ENABLE_DTRACE
|
138
|
+
+#include "dtrace.h"
|
139
|
+
+#endif
|
140
|
+
+
|
141
|
+
+VALUE rb_mDtrace;
|
142
|
+
+
|
143
|
+
+static VALUE
|
144
|
+
+ruby_dtrace_fire(argc, argv, klass)
|
145
|
+
+ int argc;
|
146
|
+
+ VALUE *argv;
|
147
|
+
+ VALUE klass;
|
148
|
+
+{
|
149
|
+
+ int args;
|
150
|
+
+ VALUE name, data, ret;
|
151
|
+
+ char *probe_data;
|
152
|
+
+ char *probe_name;
|
153
|
+
+ char *start_probe;
|
154
|
+
+ char *end_probe;
|
155
|
+
+
|
156
|
+
+#ifdef ENABLE_DTRACE
|
157
|
+
+
|
158
|
+
+ args = rb_scan_args(argc, argv, "11", &name, &data);
|
159
|
+
+ probe_data = args == 2 ? StringValuePtr(data) : "";
|
160
|
+
+ probe_name = StringValuePtr(name);
|
161
|
+
+
|
162
|
+
+ if (rb_block_given_p()) {
|
163
|
+
+ start_probe = malloc(strlen(probe_name) + 7);
|
164
|
+
+ end_probe = malloc(strlen(probe_name) + 5);
|
165
|
+
+
|
166
|
+
+ sprintf(start_probe, "%s-start", probe_name);
|
167
|
+
+ sprintf(end_probe, "%s-end", probe_name);
|
168
|
+
+
|
169
|
+
+ /* Build -start and -end strings for probe names */
|
170
|
+
+ if (RUBY_RUBY_PROBE_ENABLED())
|
171
|
+
+ RUBY_RUBY_PROBE(start_probe, probe_data);
|
172
|
+
+#endif
|
173
|
+
+
|
174
|
+
+ ret = rb_yield(Qnil);
|
175
|
+
+
|
176
|
+
+#if ENABLE_DTRACE
|
177
|
+
+ if (RUBY_RUBY_PROBE_ENABLED())
|
178
|
+
+ RUBY_RUBY_PROBE(end_probe, probe_data);
|
179
|
+
+
|
180
|
+
+ free(start_probe);
|
181
|
+
+ free(end_probe);
|
182
|
+
+ } else {
|
183
|
+
+ if (RUBY_RUBY_PROBE_ENABLED())
|
184
|
+
+ RUBY_RUBY_PROBE(probe_name, probe_data);
|
185
|
+
+ ret = Qnil;
|
186
|
+
+ }
|
187
|
+
+#endif
|
188
|
+
+ return ret;
|
189
|
+
+}
|
190
|
+
+
|
191
|
+
+static VALUE
|
192
|
+
+ruby_dtrace_enabled(klass)
|
193
|
+
+ VALUE klass;
|
194
|
+
+{
|
195
|
+
+#ifdef ENABLE_DTRACE
|
196
|
+
+ return RUBY_RUBY_PROBE_ENABLED() ? Qtrue : Qfalse;
|
197
|
+
+#else
|
198
|
+
+ return Qfalse;
|
199
|
+
+#endif
|
200
|
+
+}
|
201
|
+
+
|
202
|
+
+void Init_Tracer()
|
203
|
+
+{
|
204
|
+
+ rb_mDtrace = rb_define_module("Tracer");
|
205
|
+
+ rb_define_module_function(rb_mDtrace, "fire", ruby_dtrace_fire, -1);
|
206
|
+
+ rb_define_module_function(rb_mDtrace, "enabled?", ruby_dtrace_enabled, 0);
|
207
|
+
+}
|
208
|
+
Index: dtrace.d
|
209
|
+
===================================================================
|
210
|
+
--- dtrace.d (revision 0)
|
211
|
+
+++ dtrace.d (revision 0)
|
212
|
+
@@ -0,0 +1,26 @@
|
213
|
+
+/* -*- Mode: C -*- */
|
214
|
+
+
|
215
|
+
+provider ruby {
|
216
|
+
+ probe function__entry(char*, char*, char*, int);
|
217
|
+
+ probe function__return(char*, char*, char*, int);
|
218
|
+
+ probe raise(char*, char*, int);
|
219
|
+
+ probe rescue(char*, int);
|
220
|
+
+ probe line(char*, int);
|
221
|
+
+
|
222
|
+
+ /* gc probes */
|
223
|
+
+ probe gc__begin();
|
224
|
+
+ probe gc__end();
|
225
|
+
+
|
226
|
+
+ /* Some initial memory type probes */
|
227
|
+
+ probe object__create__start(char*, char*, int);
|
228
|
+
+ probe object__create__done(char*, char*, int);
|
229
|
+
+ probe object__free(char*);
|
230
|
+
+
|
231
|
+
+ probe ruby__probe(char*, char*);
|
232
|
+
+};
|
233
|
+
+
|
234
|
+
+#pragma D attributes Evolving/Evolving/Common provider ruby provider
|
235
|
+
+#pragma D attributes Private/Private/Common provider ruby module
|
236
|
+
+#pragma D attributes Private/Private/Common provider ruby function
|
237
|
+
+#pragma D attributes Evolving/Evolving/Common provider ruby name
|
238
|
+
+#pragma D attributes Evolving/Evolving/Common provider ruby args
|
239
|
+
+
|
240
|
+
Index: common.mk
|
241
|
+
===================================================================
|
242
|
+
--- common.mk (revision 16089)
|
243
|
+
+++ common.mk (working copy)
|
244
|
+
@@ -49,6 +49,7 @@
|
245
|
+
string.$(OBJEXT) \
|
246
|
+
struct.$(OBJEXT) \
|
247
|
+
time.$(OBJEXT) \
|
248
|
+
+ tracer.$(OBJEXT) \
|
249
|
+
util.$(OBJEXT) \
|
250
|
+
variable.$(OBJEXT) \
|
251
|
+
version.$(OBJEXT) \
|
252
|
+
@@ -262,7 +263,7 @@
|
253
|
+
|
254
|
+
clean: clean-ext clean-local
|
255
|
+
clean-local::
|
256
|
+
- @$(RM) $(OBJS) $(MAINOBJ) $(WINMAINOBJ) $(LIBRUBY_A) $(LIBRUBY_SO) $(LIBRUBY) $(LIBRUBY_ALIASES)
|
257
|
+
+ @$(RM) $(OBJS) $(MAINOBJ) $(WINMAINOBJ) $(LIBRUBY_A) $(LIBRUBY_SO) $(LIBRUBY) $(LIBRUBY_ALIASES) $(DTRACE_HEADER)
|
258
|
+
@$(RM) $(PROGRAM) $(WPROGRAM) miniruby$(EXEEXT) dmyext.$(OBJEXT) $(ARCHFILE) .*.time
|
259
|
+
clean-ext:
|
260
|
+
@-$(MINIRUBY) $(srcdir)/ext/extmk.rb $(EXTMK_ARGS) clean
|
261
|
+
@@ -363,7 +364,7 @@
|
262
|
+
error.$(OBJEXT): {$(VPATH)}error.c {$(VPATH)}ruby.h config.h \
|
263
|
+
{$(VPATH)}defines.h {$(VPATH)}intern.h {$(VPATH)}missing.h \
|
264
|
+
{$(VPATH)}env.h {$(VPATH)}st.h
|
265
|
+
-eval.$(OBJEXT): {$(VPATH)}eval.c {$(VPATH)}ruby.h config.h \
|
266
|
+
+eval.$(OBJEXT): $(DTRACE_HEADER) {$(VPATH)}eval.c {$(VPATH)}ruby.h config.h \
|
267
|
+
{$(VPATH)}defines.h {$(VPATH)}intern.h {$(VPATH)}missing.h \
|
268
|
+
{$(VPATH)}node.h {$(VPATH)}env.h {$(VPATH)}util.h \
|
269
|
+
{$(VPATH)}rubysig.h {$(VPATH)}st.h {$(VPATH)}dln.h
|
270
|
+
@@ -371,7 +372,7 @@
|
271
|
+
{$(VPATH)}defines.h {$(VPATH)}intern.h {$(VPATH)}missing.h \
|
272
|
+
{$(VPATH)}rubyio.h {$(VPATH)}rubysig.h {$(VPATH)}util.h \
|
273
|
+
{$(VPATH)}dln.h
|
274
|
+
-gc.$(OBJEXT): {$(VPATH)}gc.c {$(VPATH)}ruby.h config.h \
|
275
|
+
+gc.$(OBJEXT): $(DTRACE_HEADER) {$(VPATH)}gc.c {$(VPATH)}ruby.h config.h \
|
276
|
+
{$(VPATH)}defines.h {$(VPATH)}intern.h {$(VPATH)}missing.h \
|
277
|
+
{$(VPATH)}rubysig.h {$(VPATH)}st.h {$(VPATH)}node.h \
|
278
|
+
{$(VPATH)}env.h {$(VPATH)}re.h {$(VPATH)}regex.h
|
279
|
+
@@ -394,7 +395,7 @@
|
280
|
+
numeric.$(OBJEXT): {$(VPATH)}numeric.c {$(VPATH)}ruby.h config.h \
|
281
|
+
{$(VPATH)}env.h {$(VPATH)}defines.h {$(VPATH)}intern.h \
|
282
|
+
{$(VPATH)}missing.h
|
283
|
+
-object.$(OBJEXT): {$(VPATH)}object.c {$(VPATH)}ruby.h config.h \
|
284
|
+
+object.$(OBJEXT): $(DTRACE_HEADER) {$(VPATH)}object.c {$(VPATH)}ruby.h config.h \
|
285
|
+
{$(VPATH)}defines.h {$(VPATH)}intern.h {$(VPATH)}missing.h \
|
286
|
+
{$(VPATH)}st.h {$(VPATH)}util.h
|
287
|
+
pack.$(OBJEXT): {$(VPATH)}pack.c {$(VPATH)}ruby.h config.h \
|
288
|
+
Index: eval.c
|
289
|
+
===================================================================
|
290
|
+
--- eval.c (revision 16089)
|
291
|
+
+++ eval.c (working copy)
|
292
|
+
@@ -217,6 +217,9 @@
|
293
|
+
#endif
|
294
|
+
|
295
|
+
#include <sys/stat.h>
|
296
|
+
+#ifdef ENABLE_DTRACE
|
297
|
+
+#include "dtrace.h"
|
298
|
+
+#endif
|
299
|
+
|
300
|
+
VALUE rb_cProc;
|
301
|
+
VALUE rb_cBinding;
|
302
|
+
@@ -1138,6 +1141,53 @@
|
303
|
+
} \
|
304
|
+
} while (0)
|
305
|
+
|
306
|
+
+#ifdef ENABLE_DTRACE
|
307
|
+
+
|
308
|
+
+#define FIRE_LINE_PROBE(ruby_current_node) \
|
309
|
+
+ if (RUBY_LINE_ENABLED()) { \
|
310
|
+
+ if (ruby_current_node && ruby_current_node->nd_file) { \
|
311
|
+
+ RUBY_LINE(ruby_current_node->nd_file, nd_line(ruby_current_node)); \
|
312
|
+
+ } \
|
313
|
+
+ }
|
314
|
+
+
|
315
|
+
+#define FIRE_RESCUE_PROBE(ruby_current_node) \
|
316
|
+
+ if (RUBY_RESCUE_ENABLED()) { \
|
317
|
+
+ if (ruby_current_node && ruby_current_node->nd_file) { \
|
318
|
+
+ RUBY_RESCUE(ruby_current_node->nd_file, nd_line(ruby_current_node)); \
|
319
|
+
+ } \
|
320
|
+
+ }
|
321
|
+
+
|
322
|
+
+#define FIRE_RAISE_PROBE(ruby_errinfo, ruby_sourcefile, ruby_sourceline) \
|
323
|
+
+ if (RUBY_RAISE_ENABLED()) { \
|
324
|
+
+ RUBY_RAISE(rb_obj_classname(ruby_errinfo), ruby_sourcefile, ruby_sourceline); \
|
325
|
+
+ }
|
326
|
+
+
|
327
|
+
+#define FIRE_FUNCTION_ENTRY(ruby_current_node, klass, id) \
|
328
|
+
+ if (RUBY_FUNCTION_ENTRY_ENABLED()) { \
|
329
|
+
+ char *classname = rb_class2name(klass), *methodname = rb_id2name(id); \
|
330
|
+
+ if (ruby_current_node && ruby_current_node->nd_file && classname && methodname) { \
|
331
|
+
+ RUBY_FUNCTION_ENTRY(classname, methodname, ruby_current_node->nd_file, nd_line(ruby_current_node)); \
|
332
|
+
+ } \
|
333
|
+
+ }
|
334
|
+
+
|
335
|
+
+#define FIRE_FUNCTION_RETURN(ruby_current_node, klass, id) \
|
336
|
+
+ if (RUBY_FUNCTION_RETURN_ENABLED()) { \
|
337
|
+
+ char *classname = rb_class2name(klass), *methodname = rb_id2name(id); \
|
338
|
+
+ if (ruby_current_node && ruby_current_node->nd_file && classname && methodname) { \
|
339
|
+
+ RUBY_FUNCTION_RETURN(classname, methodname, ruby_current_node->nd_file, nd_line(ruby_current_node)); \
|
340
|
+
+ } \
|
341
|
+
+ }
|
342
|
+
+
|
343
|
+
+#else
|
344
|
+
+
|
345
|
+
+#define FIRE_LINE_PROBE(ruby_current_node) /* NOOP */
|
346
|
+
+#define FIRE_RESCUE_PROBE(ruby_current_node) /* NOOP */
|
347
|
+
+#define FIRE_RAISE_PROBE(ruby_errinfo, ruby_sourcefile, ruby_sourceline) /* NOOP */
|
348
|
+
+#define FIRE_FUNCTION_ENTRY(ruby_current_node, klass, id) /* NOOP */
|
349
|
+
+#define FIRE_FUNCTION_RETURN(ruby_current_node, klass, id) /* NOOP */
|
350
|
+
+
|
351
|
+
+#endif
|
352
|
+
+
|
353
|
+
static VALUE trace_func = 0;
|
354
|
+
static int tracing = 0;
|
355
|
+
static void call_trace_func _((rb_event_t,NODE*,VALUE,ID,VALUE));
|
356
|
+
@@ -3028,6 +3078,7 @@
|
357
|
+
RETURN(Qfalse);
|
358
|
+
|
359
|
+
case NODE_IF:
|
360
|
+
+ FIRE_LINE_PROBE(ruby_current_node);
|
361
|
+
EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
|
362
|
+
ruby_frame->last_func,
|
363
|
+
ruby_frame->last_class);
|
364
|
+
@@ -3046,6 +3097,7 @@
|
365
|
+
if (nd_type(node) != NODE_WHEN) goto again;
|
366
|
+
tag = node->nd_head;
|
367
|
+
while (tag) {
|
368
|
+
+ FIRE_LINE_PROBE(ruby_current_node);
|
369
|
+
EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self,
|
370
|
+
ruby_frame->last_func,
|
371
|
+
ruby_frame->last_class);
|
372
|
+
@@ -3087,6 +3139,7 @@
|
373
|
+
}
|
374
|
+
tag = node->nd_head;
|
375
|
+
while (tag) {
|
376
|
+
+ FIRE_LINE_PROBE(ruby_current_node);
|
377
|
+
EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self,
|
378
|
+
ruby_frame->last_func,
|
379
|
+
ruby_frame->last_class);
|
380
|
+
@@ -3307,6 +3360,7 @@
|
381
|
+
rescuing = -1;
|
382
|
+
while (resq) {
|
383
|
+
ruby_current_node = resq;
|
384
|
+
+ FIRE_RESCUE_PROBE(ruby_current_node);
|
385
|
+
if (handle_rescue(self, resq)) {
|
386
|
+
state = 0;
|
387
|
+
rescuing = 1;
|
388
|
+
@@ -4124,6 +4178,7 @@
|
389
|
+
break;
|
390
|
+
|
391
|
+
case NODE_NEWLINE:
|
392
|
+
+ FIRE_LINE_PROBE(ruby_current_node);
|
393
|
+
EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
|
394
|
+
ruby_frame->last_func,
|
395
|
+
ruby_frame->last_class);
|
396
|
+
@@ -4598,6 +4653,7 @@
|
397
|
+
|
398
|
+
rb_trap_restore_mask();
|
399
|
+
if (tag != TAG_FATAL) {
|
400
|
+
+ FIRE_RAISE_PROBE(ruby_errinfo, ruby_sourcefile, ruby_sourceline);
|
401
|
+
EXEC_EVENT_HOOK(RUBY_EVENT_RAISE, ruby_current_node,
|
402
|
+
ruby_frame->self,
|
403
|
+
ruby_frame->last_func,
|
404
|
+
@@ -5828,6 +5884,7 @@
|
405
|
+
rb_bug("bad argc (%d) specified for `%s(%s)'",
|
406
|
+
len, rb_class2name(klass), rb_id2name(id));
|
407
|
+
}
|
408
|
+
+ FIRE_FUNCTION_ENTRY(ruby_current_node, klass, id);
|
409
|
+
if (event_hooks) {
|
410
|
+
int state;
|
411
|
+
|
412
|
+
@@ -5846,6 +5903,7 @@
|
413
|
+
else {
|
414
|
+
result = call_cfunc(body->nd_cfnc, recv, len, argc, argv);
|
415
|
+
}
|
416
|
+
+ FIRE_FUNCTION_RETURN(ruby_current_node, klass, id);
|
417
|
+
}
|
418
|
+
break;
|
419
|
+
|
420
|
+
@@ -5873,12 +5931,14 @@
|
421
|
+
|
422
|
+
case NODE_BMETHOD:
|
423
|
+
ruby_frame->flags |= FRAME_DMETH;
|
424
|
+
+ FIRE_FUNCTION_ENTRY(ruby_current_node, klass, id);
|
425
|
+
if (event_hooks) {
|
426
|
+
struct BLOCK *data;
|
427
|
+
Data_Get_Struct(body->nd_cval, struct BLOCK, data);
|
428
|
+
EXEC_EVENT_HOOK(RUBY_EVENT_CALL, data->body, recv, id, klass);
|
429
|
+
}
|
430
|
+
result = proc_invoke(body->nd_cval, rb_ary_new4(argc, argv), recv, klass);
|
431
|
+
+ FIRE_FUNCTION_RETURN(ruby_current_node, klass, id);
|
432
|
+
if (event_hooks) {
|
433
|
+
EXEC_EVENT_HOOK(RUBY_EVENT_RETURN, body, recv, id, klass);
|
434
|
+
}
|
435
|
+
@@ -5992,6 +6052,7 @@
|
436
|
+
}
|
437
|
+
ruby_frame->argc = i;
|
438
|
+
}
|
439
|
+
+ FIRE_FUNCTION_ENTRY(ruby_current_node, klass, id);
|
440
|
+
if (event_hooks) {
|
441
|
+
EXEC_EVENT_HOOK(RUBY_EVENT_CALL, b2, recv, id, klass);
|
442
|
+
}
|
443
|
+
@@ -6002,6 +6063,7 @@
|
444
|
+
state = 0;
|
445
|
+
}
|
446
|
+
POP_TAG();
|
447
|
+
+ FIRE_FUNCTION_RETURN(ruby_current_node, klass, id);
|
448
|
+
if (event_hooks) {
|
449
|
+
EXEC_EVENT_HOOK(RUBY_EVENT_RETURN, body, recv, id, klass);
|
450
|
+
}
|
451
|
+
Index: gc.c
|
452
|
+
===================================================================
|
453
|
+
--- gc.c (revision 16089)
|
454
|
+
+++ gc.c (working copy)
|
455
|
+
@@ -30,6 +30,11 @@
|
456
|
+
#include <sys/resource.h>
|
457
|
+
#endif
|
458
|
+
|
459
|
+
+#ifdef ENABLE_DTRACE
|
460
|
+
+#include <sys/sdt.h>
|
461
|
+
+#include "dtrace.h"
|
462
|
+
+#endif
|
463
|
+
+
|
464
|
+
#if defined _WIN32 || defined __CYGWIN__
|
465
|
+
#include <windows.h>
|
466
|
+
#endif
|
467
|
+
@@ -68,6 +73,31 @@
|
468
|
+
#endif
|
469
|
+
#endif
|
470
|
+
|
471
|
+
+#ifdef ENABLE_DTRACE
|
472
|
+
+
|
473
|
+
+#define FIRE_OBJECT_FREE_PROBE(obj) \
|
474
|
+
+ if (RUBY_OBJECT_FREE_ENABLED()) { \
|
475
|
+
+ RUBY_OBJECT_FREE(rb_class2name(CLASS_OF(obj))); \
|
476
|
+
+ } \
|
477
|
+
+
|
478
|
+
+#define FIRE_GC_BEGIN_PROBE() \
|
479
|
+
+ if (RUBY_GC_BEGIN_ENABLED()) { \
|
480
|
+
+ RUBY_GC_BEGIN(); \
|
481
|
+
+ }
|
482
|
+
+
|
483
|
+
+#define FIRE_GC_END_PROBE() \
|
484
|
+
+ if (RUBY_GC_END_ENABLED()) { \
|
485
|
+
+ RUBY_GC_END(); \
|
486
|
+
+ }
|
487
|
+
+
|
488
|
+
+#else
|
489
|
+
+
|
490
|
+
+#define FIRE_OBJECT_FREE_PROBE(obj) /* NOOP */
|
491
|
+
+#define FIRE_GC_BEGIN_PROBE() /* NOOP */
|
492
|
+
+#define FIRE_GC_END_PROBE() /* NOOP */
|
493
|
+
+
|
494
|
+
+#endif
|
495
|
+
+
|
496
|
+
static unsigned long malloc_increase = 0;
|
497
|
+
static unsigned long malloc_limit = GC_MALLOC_LIMIT;
|
498
|
+
static void run_final();
|
499
|
+
@@ -1159,6 +1189,7 @@
|
500
|
+
break;
|
501
|
+
}
|
502
|
+
|
503
|
+
+ FIRE_OBJECT_FREE_PROBE(obj);
|
504
|
+
if (FL_TEST(obj, FL_EXIVAR)) {
|
505
|
+
rb_free_generic_ivar((VALUE)obj);
|
506
|
+
}
|
507
|
+
@@ -1322,6 +1353,7 @@
|
508
|
+
{
|
509
|
+
struct gc_list *list;
|
510
|
+
struct FRAME * volatile frame; /* gcc 2.7.2.3 -O2 bug?? */
|
511
|
+
+ FIRE_GC_BEGIN_PROBE();
|
512
|
+
jmp_buf save_regs_gc_mark;
|
513
|
+
SET_STACK_END;
|
514
|
+
|
515
|
+
@@ -1414,6 +1446,7 @@
|
516
|
+
} while (!MARK_STACK_EMPTY);
|
517
|
+
|
518
|
+
gc_sweep();
|
519
|
+
+ FIRE_GC_END_PROBE();
|
520
|
+
}
|
521
|
+
|
522
|
+
void
|