XRay 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +34 -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 +111 -0
- data/lib/xray/enable_thread_aware_dispatcher.rb +1 -0
- data/lib/xray/rails_stack_signal_handler.rb +15 -0
- data/lib/xray/signal_handler.rb +26 -0
- data/lib/xray/thread_aware_dispatcher.rb +27 -0
- data/lib/xray/xray.rb +1 -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 +10 -0
- data/test/unit/thread_aware_dispatcher_test.rb +51 -0
- data/test/unit/xray/dtrace/tracer_test.rb +54 -0
- metadata +72 -0
data/README
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
XRay provides a lightweight yet powerful toolbox for troubleshooting Ruby
|
2
|
+
applications when things stop making sense. Includes GDB and DTrace tooling.
|
3
|
+
|
4
|
+
*** GDB ***
|
5
|
+
|
6
|
+
|
7
|
+
Copy +gdb_macros+ file provided in the gem as your ~/.gdbinit file.
|
8
|
+
|
9
|
+
*** Fire DTrace Application Probes ***
|
10
|
+
|
11
|
+
See XRay::DTrace::Tracer
|
12
|
+
|
13
|
+
*** Out-of-the-box Rails DTrace Instrumentation ***
|
14
|
+
|
15
|
+
You are one require away from triggering automatically DTrace events for
|
16
|
+
Rails requests, database access and template rendering. As simple as
|
17
|
+
|
18
|
+
# environment.rb
|
19
|
+
Rails::Initializer.run do |config|
|
20
|
+
|
21
|
+
...
|
22
|
+
|
23
|
+
config.after_initialize do
|
24
|
+
require "xray/dtrace/rails/enable_tracing"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
See
|
29
|
+
* lib/xray/dtrace/railsenable_tracing.rb
|
30
|
+
* lib/xray/dtrace/action_controller_tracing_extension.rb
|
31
|
+
* lib/xray/dtrace/active_record_tracing_extension.rb
|
32
|
+
|
33
|
+
|
34
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#
|
2
|
+
# Decorate ActionController::Base with request tracing
|
3
|
+
#
|
4
|
+
ActionController::Base.class_eval do
|
5
|
+
include XRay::DTrace::Tracer
|
6
|
+
|
7
|
+
def perform_action_with_tracing
|
8
|
+
firing('request', "#{self.class.to_s}##{action_name.to_s}") do
|
9
|
+
perform_action_without_tracing
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def render_with_tracing(options = nil, deprecated_status = nil, &block)
|
14
|
+
firing('render', options.to_s) do
|
15
|
+
render_without_tracing options, deprecated_status
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
alias_method_chain :perform_action, :tracing
|
20
|
+
alias_method_chain :render, :tracing
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#
|
2
|
+
# Decorate ActiveRecord connection with DB tracing
|
3
|
+
#
|
4
|
+
ActiveRecord::Base.connection.class.class_eval do
|
5
|
+
include XRay::DTrace::Tracer
|
6
|
+
|
7
|
+
def execute_with_tracing(sql, name=nil)
|
8
|
+
firing('db', sql) do
|
9
|
+
execute_without_tracing sql, name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method_chain :execute, :tracing
|
14
|
+
|
15
|
+
end
|
@@ -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 'xray/dtrace/tracer'
|
17
|
+
if Object.const_defined? :ActionController
|
18
|
+
puts "Enabling controller tracing"
|
19
|
+
require "xray/dtrace/rails/action_controller_tracing_extension"
|
20
|
+
end
|
21
|
+
|
22
|
+
if Object.const_defined? :ActiveRecord
|
23
|
+
puts "Enabling DB tracing"
|
24
|
+
require "xray/dtrace/rails/active_record_connection_tracing_extension"
|
25
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module XRay
|
2
|
+
module DTrace
|
3
|
+
|
4
|
+
# Ruby module to fire application-level Dtrace events (using ruby-probe).
|
5
|
+
#
|
6
|
+
# This module provide a convenient and unified API abstracting
|
7
|
+
# different tracing implementations in Leopard Ruby VM (by Apple)
|
8
|
+
# and in the one provided by Joyent (https://dev.joyent.com/projects/ruby-dtrace).
|
9
|
+
# This module also provides a NOOP implementation for Ruby VMs with
|
10
|
+
# no DTrace support: So you can use the exact same code while developing
|
11
|
+
# on Linux and deploying on Solaris for instance.
|
12
|
+
module Tracer
|
13
|
+
|
14
|
+
if Object.const_defined?(:DTracer) ### Leopard tracer ###
|
15
|
+
|
16
|
+
# Fire an application-level probe using ruby-probe.
|
17
|
+
#
|
18
|
+
# The first argument passed will be passed to the D script as arg0 for
|
19
|
+
# the ruby-probe probe. This is conventionally a probe name.
|
20
|
+
#
|
21
|
+
# The second argument is optional and can be used to pass additional
|
22
|
+
# data into the D script as arg1.
|
23
|
+
#
|
24
|
+
# Example:
|
25
|
+
#
|
26
|
+
# XRay::DTrace::Tracer.fire('service-start', "order processing")
|
27
|
+
#
|
28
|
+
# XRay::DTrace::Tracer.fire('service-stop')
|
29
|
+
#
|
30
|
+
def fire(name, data = nil)
|
31
|
+
DTracer.fire(name, data)
|
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. This is conventionally a probe base name. The
|
39
|
+
# probes which fire before and after the block runs will have
|
40
|
+
# "-start" and "-end" appended, respectively.
|
41
|
+
#
|
42
|
+
# The second argument is optional and can be used to pass additional
|
43
|
+
# data into the D script as arg1.
|
44
|
+
#
|
45
|
+
# Example:
|
46
|
+
#
|
47
|
+
# XRay::DTrace::Tracer.fire('service-start', "order processing")
|
48
|
+
#
|
49
|
+
# XRay::DTrace::Tracer.firing('db-query', "select * from dual;") do
|
50
|
+
# ActiveRecord::Base.execute("select * from dual;")
|
51
|
+
#
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# Will:
|
55
|
+
# 1. Fire a probe with arg0 set to "db-query-start", and arg1 set
|
56
|
+
# to the sql query.
|
57
|
+
#
|
58
|
+
# 2. Run the block and execute the SQL.
|
59
|
+
#
|
60
|
+
# 3. Fire a probe with arg0 set to "db-query-start".
|
61
|
+
def firing(name, data = nil)
|
62
|
+
fire(name + "-start", data)
|
63
|
+
result = yield
|
64
|
+
fire(name + "-end", data)
|
65
|
+
result
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns true if ruby-probe probes are enables
|
69
|
+
# (application-level probes for Ruby).
|
70
|
+
def enabled?
|
71
|
+
DTracer.enabled?
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
elsif Object.const_defined?(:Tracer) && Tracer.methods.include?(:fire) ### Joyent Tracer ##
|
76
|
+
|
77
|
+
raise "got here"
|
78
|
+
|
79
|
+
def fire(name, data = nil) #:nodoc: all
|
80
|
+
|
81
|
+
::Tracer.fire(name, data)
|
82
|
+
end
|
83
|
+
|
84
|
+
def firing(name, data = nil) #:nodoc: all
|
85
|
+
::Tracer.fire(name, data)
|
86
|
+
end
|
87
|
+
|
88
|
+
def enabled? #:nodoc: all
|
89
|
+
STDERR.puts "WARNING: XRay::DTrace.Tracer.enabled? does not work with Joyent Tracer"
|
90
|
+
false
|
91
|
+
end
|
92
|
+
|
93
|
+
else ### No ruby-probe support ###
|
94
|
+
|
95
|
+
def fire(name, data = nil) #:nodoc: all
|
96
|
+
end
|
97
|
+
|
98
|
+
def firing(name, data = nil) #:nodoc: all
|
99
|
+
yield
|
100
|
+
end
|
101
|
+
|
102
|
+
def enabled? #:nodoc: all
|
103
|
+
false
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dispatcher.send :include, XRay::ThreadAwareDispatcher
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#
|
2
|
+
# Install a signal handler dumping Rails stack by raising an exception
|
3
|
+
#
|
4
|
+
# Trigger it with: kill -QUIT <pid>
|
5
|
+
#
|
6
|
+
|
7
|
+
trap "QUIT" do
|
8
|
+
STDERR.puts "=============== XRay - Raising exception in Rails thread ==============="
|
9
|
+
if Dispatcher.thread_in_dispatch
|
10
|
+
Dispatcher.thread_in_dispatch.raise "XRay - Forced exception to Rails stack trace"
|
11
|
+
else
|
12
|
+
STDERR.puts "No Rails thread in dispatch"
|
13
|
+
end
|
14
|
+
STDERR.puts "=============== XRay - Done ==============="
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#
|
2
|
+
# Install a signal handler dumping current thread stack
|
3
|
+
#
|
4
|
+
# Trigger it with: kill -QUIT <pid>
|
5
|
+
#
|
6
|
+
module XRay
|
7
|
+
|
8
|
+
def self.dump_threads
|
9
|
+
STDERR.puts "=============== XRay Inspector ==============="
|
10
|
+
STDERR.puts "Current Thread\n "
|
11
|
+
STDERR.puts caller.join("\n \_ ")
|
12
|
+
STDERR.puts Thread.current.xray_backtrace.join("\n \_ ")
|
13
|
+
# STDERR.puts "----------------------------------------------"
|
14
|
+
# Thread.list.each_with_index do |t,i|
|
15
|
+
# STDERR.puts "Dumping Thread #{i}\n "
|
16
|
+
# t.xray_backtrace.join("\n \_ ")
|
17
|
+
# end
|
18
|
+
STDERR.puts "=============================================="
|
19
|
+
STDERR.flush
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
trap "QUIT" do
|
25
|
+
XRay.dump_threads
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module XRay
|
2
|
+
|
3
|
+
module ThreadAwareDispatcher
|
4
|
+
|
5
|
+
# Intercept dispatch with our own method when the module is included.
|
6
|
+
def self.included(target)
|
7
|
+
class << target
|
8
|
+
attr_reader :thread_in_dispatch
|
9
|
+
|
10
|
+
alias actual_dispatch dispatch
|
11
|
+
|
12
|
+
# Capture as an instance variable the current
|
13
|
+
# thread -- which is processing current Rails request --
|
14
|
+
# and do the actual dispatch.
|
15
|
+
def dispatch(*args)
|
16
|
+
@thread_in_dispatch = Thread.current
|
17
|
+
actual_dispatch *args
|
18
|
+
ensure
|
19
|
+
@thread_in_dispatch = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/xray/xray.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'xray/thread_aware_dispatcher'
|
data/test/all_tests.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir["#{File.dirname __FILE__}/**/*_test.rb"].each { |test_case| require test_case }
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path(__FILE__ + '/../../../test_helper')
|
2
|
+
|
3
|
+
functional_tests do
|
4
|
+
|
5
|
+
test "fire a probe with no data and no block" do
|
6
|
+
aClass = Class.new do
|
7
|
+
include XRay::DTrace::Tracer
|
8
|
+
end
|
9
|
+
|
10
|
+
aClass.new.fire "a name"
|
11
|
+
end
|
12
|
+
|
13
|
+
test "fire a probe with data and no block" do
|
14
|
+
aClass = Class.new do
|
15
|
+
include XRay::DTrace::Tracer
|
16
|
+
end
|
17
|
+
|
18
|
+
aClass.new.fire "a name", "some data"
|
19
|
+
end
|
20
|
+
|
21
|
+
test "Can check whether ruby-probe is enabled" do
|
22
|
+
aClass = Class.new do
|
23
|
+
include XRay::DTrace::Tracer
|
24
|
+
end
|
25
|
+
|
26
|
+
assert [true, false].include?(aClass.new.enabled?)
|
27
|
+
end
|
28
|
+
|
29
|
+
test "fire a probe with block and no data" do
|
30
|
+
anObject = Class.new do
|
31
|
+
include XRay::DTrace::Tracer
|
32
|
+
end.new
|
33
|
+
|
34
|
+
result = anObject.firing("a-name") do
|
35
|
+
:expected_result
|
36
|
+
end
|
37
|
+
|
38
|
+
assert_equal :expected_result, result
|
39
|
+
end
|
40
|
+
|
41
|
+
test "fire a probe with block and data" do
|
42
|
+
anObject = Class.new do
|
43
|
+
include XRay::DTrace::Tracer
|
44
|
+
end.new
|
45
|
+
|
46
|
+
result = anObject.firing("a-name", "some data") do
|
47
|
+
:expected_result
|
48
|
+
end
|
49
|
+
|
50
|
+
assert_equal :expected_result, result
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + '/../../lib'
|
2
|
+
require 'xray/dtrace/tracer'
|
3
|
+
|
4
|
+
class Service
|
5
|
+
include XRay::DTrace::Tracer
|
6
|
+
|
7
|
+
def process
|
8
|
+
puts "Processing new request"
|
9
|
+
firing "my-service", "a sql query" do
|
10
|
+
sleep 2
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
loop do
|
17
|
+
Service.new.process
|
18
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path(__FILE__ + '/../../test_helper')
|
2
|
+
|
3
|
+
unit_tests do
|
4
|
+
|
5
|
+
test "thread aware dispatcher returns original dispath result" do
|
6
|
+
dispatcher = Class.new
|
7
|
+
dispatcher.expects(:dispatch).with(:the_args).returns(:the_result)
|
8
|
+
dispatcher.send :include, XRay::ThreadAwareDispatcher
|
9
|
+
dispatcher.dispatch(:the_args)
|
10
|
+
end
|
11
|
+
|
12
|
+
test "thread aware dispatcher adds accessor for thread in dispatch" do
|
13
|
+
dispatcher = Class.new
|
14
|
+
dispatcher.stubs(:dispatch)
|
15
|
+
dispatcher.send :include, XRay::ThreadAwareDispatcher
|
16
|
+
dispatcher.thread_in_dispatch
|
17
|
+
end
|
18
|
+
|
19
|
+
test "thread in dispatch is nil when not in dispatch" do
|
20
|
+
dispatcher = Class.new
|
21
|
+
dispatcher.stubs(:dispatch)
|
22
|
+
dispatcher.send :include, XRay::ThreadAwareDispatcher
|
23
|
+
assert_nil dispatcher.thread_in_dispatch
|
24
|
+
end
|
25
|
+
|
26
|
+
test "thread in dispatch is nil when dispatch raises" do
|
27
|
+
begin
|
28
|
+
dispatcher = Class.new
|
29
|
+
dispatcher.expects(:dispatch).raises(StandardError.new("Fake Problem"))
|
30
|
+
dispatcher.send :include, XRay::ThreadAwareDispatcher
|
31
|
+
dispatcher.dispatch
|
32
|
+
flunk "Should relay exception"
|
33
|
+
rescue StandardError
|
34
|
+
assert_nil dispatcher.thread_in_dispatch
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
test "thread in dispatch is set to current thread when in dispatch" do
|
39
|
+
dispatcher = Class.new do
|
40
|
+
extend Test::Unit::Assertions
|
41
|
+
|
42
|
+
def self.dispatch(*args)
|
43
|
+
assert_equal Thread.current, thread_in_dispatch
|
44
|
+
end
|
45
|
+
|
46
|
+
include XRay::ThreadAwareDispatcher
|
47
|
+
end
|
48
|
+
dispatcher.dispatch
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path(__FILE__ + '/../../../../test_helper')
|
2
|
+
|
3
|
+
unit_tests do
|
4
|
+
|
5
|
+
test "fire a probe with no data and no block" do
|
6
|
+
aClass = Class.new do
|
7
|
+
include XRay::DTrace::Tracer
|
8
|
+
end
|
9
|
+
|
10
|
+
aClass.new.fire "a name"
|
11
|
+
end
|
12
|
+
|
13
|
+
test "fire a probe with data and no block" do
|
14
|
+
aClass = Class.new do
|
15
|
+
include XRay::DTrace::Tracer
|
16
|
+
end
|
17
|
+
|
18
|
+
aClass.new.fire "a name", "some data"
|
19
|
+
end
|
20
|
+
|
21
|
+
test "Can check whether ruby-probe is enabled" do
|
22
|
+
aClass = Class.new do
|
23
|
+
include XRay::DTrace::Tracer
|
24
|
+
end
|
25
|
+
|
26
|
+
assert [true, false].include?(aClass.new.enabled?)
|
27
|
+
end
|
28
|
+
|
29
|
+
test "fire a probe with block and no data" do
|
30
|
+
anObject = Class.new do
|
31
|
+
include XRay::DTrace::Tracer
|
32
|
+
end.new
|
33
|
+
|
34
|
+
result = anObject.firing("a-name") do
|
35
|
+
:expected_result
|
36
|
+
end
|
37
|
+
|
38
|
+
assert_equal :expected_result, result
|
39
|
+
end
|
40
|
+
|
41
|
+
test "fire a probe with block and data" do
|
42
|
+
anObject = Class.new do
|
43
|
+
include XRay::DTrace::Tracer
|
44
|
+
end.new
|
45
|
+
|
46
|
+
result = anObject.firing("a-name", "some data") do
|
47
|
+
:expected_result
|
48
|
+
end
|
49
|
+
|
50
|
+
assert_equal :expected_result, result
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: XRay
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.0"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Philippe Hanrigou
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-03-28 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: xray-developer@rubyforge.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- lib/xray/dtrace/rails/action_controller_tracing_extension.rb
|
26
|
+
- lib/xray/dtrace/rails/active_record_connection_tracing_extension.rb
|
27
|
+
- lib/xray/dtrace/rails/enable_tracing.rb
|
28
|
+
- lib/xray/dtrace/tracer.rb
|
29
|
+
- lib/xray/enable_thread_aware_dispatcher.rb
|
30
|
+
- lib/xray/rails_stack_signal_handler.rb
|
31
|
+
- lib/xray/signal_handler.rb
|
32
|
+
- lib/xray/thread_aware_dispatcher.rb
|
33
|
+
- lib/xray/xray.rb
|
34
|
+
- test/all_tests.rb
|
35
|
+
- test/functional/dtrace/tracer_test.rb
|
36
|
+
- test/functional/tracer_script.rb
|
37
|
+
- test/test_helper.rb
|
38
|
+
- test/unit/thread_aware_dispatcher_test.rb
|
39
|
+
- test/unit/xray/dtrace/tracer_test.rb
|
40
|
+
- README
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://xray.rubyforge.com
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --title
|
46
|
+
- XRay
|
47
|
+
- --main
|
48
|
+
- README
|
49
|
+
- --line-numbers
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project: xray
|
67
|
+
rubygems_version: 1.0.1
|
68
|
+
signing_key:
|
69
|
+
specification_version: 2
|
70
|
+
summary: Dump backtrace for all threads.
|
71
|
+
test_files:
|
72
|
+
- test/all_tests.rb
|