XRay 1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,10 @@
1
+ $: << File.dirname(__FILE__) + '/../ext/xray'
2
+ $: << File.dirname(__FILE__) + '/../lib'
3
+ require 'xray/xray'
4
+ require 'xray/dtrace/tracer'
5
+
6
+ require 'test/unit'
7
+ require 'rubygems'
8
+ require 'mocha'
9
+ require 'dust'
10
+
@@ -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