rails-dtrace 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .DS_Store
data/LICENSE CHANGED
@@ -1,22 +1,13 @@
1
1
  Copyright (c) 2012 Eric Saxby
2
2
 
3
- MIT License
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
4
6
 
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
7
+ http://www.apache.org/licenses/LICENSE-2.0
12
8
 
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md CHANGED
@@ -5,11 +5,15 @@ instruments in Rails, turning them into DTrace probes.
5
5
 
6
6
  ## Requirements
7
7
 
8
+ Rails > 3.0
9
+
8
10
  An OS that supports DTrace. For example:
9
11
  * MacOS X
10
12
  * Illumos
11
13
  * SmartOS
12
14
  * Solaris
15
+ * BSD
16
+ * Oracle Linux ?
13
17
 
14
18
  ## Installation
15
19
 
@@ -27,6 +31,17 @@ And then execute:
27
31
  $ bundle
28
32
  ```
29
33
 
34
+ If you are using edge Rails (master/4.0), the notification syntax has
35
+ changed in an awesome way. In this case, you'll want the `rails_4`
36
+ branch.
37
+
38
+ ```ruby
39
+ gem 'ruby-usdt', :git => 'git://github.com/chrisa/ruby-usdt.git',
40
+ :submodules => true, :branch => 'disable_provider'
41
+ gem 'rails-dtrace', :git => 'git://github.com/sax/rails-dtrace.git',
42
+ :branch => 'rails_4'
43
+ ```
44
+
30
45
  ## Warning
31
46
 
32
47
  This gem in an experiment in progress. The code does not have automated
@@ -42,26 +57,77 @@ tests around this gem) these warnings will disappear.
42
57
  ## Usage
43
58
 
44
59
  Once you add the gem to your Rails application, it will automatically
45
- subscribe to all notifications, turning them into DTrace probes. The
46
- arguments to the probes will be:
60
+ subscribe to all notifications, turning them into DTrace probes.
61
+
62
+ Note that notifications are lazy-loaded, so even though rails-dtrace subscribes to all available instruments, they will only be converted to probes as they fire in Rails code. For this reason, in order to get a full picture of what is happening in a Rails process, enough load should be generated to ensure that all important probes are registered before tracing.
63
+
64
+ ### Rails 3
47
65
 
48
- * `arg0` - Start time of notification - Integer
49
- * `arg1` - End time of notification - Integer
50
- * `arg2` - Unique identifier of notification - String
51
- * `arg3` - Stringified hash of notification payload - String
66
+ Arguments to probes are:
67
+
68
+ * `arg0` - Unique identifier of notification - String
69
+ * `arg1` - Stringified hash of notification payload - String
70
+ * `arg2` - Nanoseconds between start and finish of event - Integer
71
+
72
+ The notification name is turned into the probe function, and the probe name is defined as 'event'. For instance, `sql.active_record` becomes `ruby*:rails:sql.active_record:event`. This is to make events somewhat compatible with the edge Rails method of firing events, described below.
73
+
74
+ Some operating systems (MacOS X, I'm looking at you) don't give you nanosecond time resolution. In this case you get microseconds multipled by 1000.
52
75
 
53
76
  The following dtrace command can be used, as an example:
54
77
 
55
78
  ```bash
56
- sudo dtrace -n 'ruby*:rails:: { printf("%d %d %s %s", arg0, arg1,
57
- copyinstr(arg2), copyinstr(arg3)) }'
79
+ sudo dtrace -n 'ruby*:rails:sql.active_record: {
80
+ printf(
81
+ "%s \n\t %s \n\t %d",
82
+ copyinstr(arg0), copyinstr(arg1),
83
+ arg2
84
+ )
85
+ }'
86
+ ```
87
+
88
+ Example output:
89
+
90
+ ```
91
+ 1 15407 sql.active_record:event 4595e4d30b17bdb96fe9
92
+ {:sql=>"SELECT \"things\".* FROM \"things\" ", :name=>"Thing Load", :connection_id=>70184895988280, :binds=>[]}
93
+ 238000
94
+ ```
95
+
96
+
97
+ ### Rails 4 / Edge Rails
98
+
99
+ Note that you need to use the `rails_4` branch in order for this to work. In newer Rails, notifications become more dtrace-like. Rather than a notification sending a message to `#call` (which gets start time and end time), they can send two messages, `#start` and `#finish`. This way, you can do your own math on the notifications.
100
+
101
+ In *rails-dtrace*, the notification name becomes the probe function, and we map `start -> entry` and `finish -> exit`.
102
+
103
+ If a notification subscriber responds to #call, ActiveSupport::Notifications will send methods in the Rails 3 way. For this reason the rails-dtrace code is incompatible at this time. It could conceivably be done with some metaprogramming to trick the Rails framework, but I'd prefer to keep the rails-dtrace code small and readable.
104
+
105
+ Arguments to probes are:
106
+
107
+ * `arg0` - Unique identifier of notification - String
108
+ * `arg1` - Stringified hash of notification payload - String
109
+
110
+ The following dtrace command can be used, as an example:
111
+
112
+ ```bash
113
+ sudo dtrace -n 'ruby*:rails:sql.active_record: {
114
+ printf(
115
+ "%s \n\t %s",
116
+ copyinstr(arg0),
117
+ copyinstr(arg1)
118
+ )
119
+ }'
120
+ ```
121
+
122
+ Example output:
123
+
124
+ ```
125
+ 3 52609 sql.active_record:entry a392841cfd75a132432e
126
+ {:sql=>"SELECT \"things\".* FROM \"things\" ", :name=>"Thing Load", :connection_id=>70362294355260, :binds=>[]}
127
+ 3 52610 sql.active_record:exit a392841cfd75a132432e
128
+ {:sql=>"SELECT \"things\".* FROM \"things\" ", :name=>"Thing Load", :connection_id=>70362294355260, :binds=>[]}
58
129
  ```
59
130
 
60
- Notifications are lazy-loaded, so even though rails-dtrace subscribes to
61
- all available instruments, they will only be converted to probes as
62
- they fire in Rails code. For this reason, in order to get a full picture
63
- of what is happening in a Rails process, enough load should be generated
64
- to ensure that all important probes are registered before tracing.
65
131
 
66
132
  ## Contributing
67
133
 
@@ -74,17 +140,13 @@ to ensure that all important probes are registered before tracing.
74
140
  ## Suggestions for Contributions
75
141
 
76
142
  * How the F do you test this thing?
77
- * Can Rails instruments be detected at initialization time?
78
- * Notifications do start/end in one instrument. DTrace probes tend to
79
- fire multiple probes, on entry and exit. This lets you write scripts
80
- to do neat analytics on event timing. Can Notifications be hacked to
81
- do this?
82
- * The Responder turns the Notification payload (a hash) into a string.
143
+ * Can Rails instruments be detected at initialization time to register all probes at once?
144
+ * The Subscriber turns the Notification payload (a hash) into a string.
83
145
  This can surely be better.
84
146
 
85
147
  ## License
86
148
 
87
- Copyright 2012 Eric Saxby
149
+ Copyright (c) 2012 Eric Saxby
88
150
 
89
151
  Licensed under the Apache License, Version 2.0 (the "License");
90
152
  you may not use this file except in compliance with the License.
@@ -2,8 +2,7 @@ module DTrace
2
2
  class Railtie < Rails::Railtie
3
3
 
4
4
  initializer 'railtie.configure_rails.initialization' do
5
- DTrace::Responder.logger = Rails.logger
6
- ActiveSupport::Notifications.subscribe(/.*/, DTrace::Responder)
5
+ ActiveSupport::Notifications.subscribe(/.*/, DTrace::Subscriber)
7
6
  end
8
7
  end
9
8
  end
@@ -0,0 +1,71 @@
1
+ require 'usdt'
2
+
3
+ module DTrace
4
+ class Subscriber
5
+ cattr_reader :probes, :provider
6
+
7
+ @@provider = USDT::Provider.create :ruby, :rails
8
+ @@probes = {}
9
+ @@enabled = false
10
+
11
+ class << self
12
+ def logger
13
+ @logger ||= Rails.logger if defined?(Rails)
14
+ end
15
+
16
+ attr_writer :logger
17
+
18
+ # Rails 3.x define instruments as blocks that wrap code. When the code
19
+ # finishes executing, subscribers are called with the start and end time.
20
+
21
+ def call(notification, start_time, end_time, id, payload)
22
+ fire_probe(notification, id, payload, 'event', start_time, end_time)
23
+ end
24
+
25
+ # Rails 4.x defines instruments in a different way, where #start is called
26
+ # when the block begins and #finish is called when the block ends.
27
+
28
+ def start(notification, id, payload)
29
+ fire_probe(notification, id, payload, 'entry')
30
+ end
31
+
32
+ def finish(notification, id, payload)
33
+ fire_probe(notification, id, payload, 'exit')
34
+ end
35
+
36
+ protected
37
+
38
+ def find_or_create_probe(probe_func, probe_name)
39
+ probe_id = "#{probe_func}::#{probe_name}"
40
+
41
+ unless probes.keys.include?(probe_id)
42
+ probe = provider.probe(probe_func, probe_name, :string, :string, :integer)
43
+ probes[probe_id] = probe
44
+
45
+ logger.debug "Adding DTrace probe: #{probe_id}"
46
+
47
+ provider.disable if @@enabled
48
+ provider.enable
49
+ @@enabled = true
50
+ end
51
+
52
+ probes[probe_id]
53
+ end
54
+
55
+ private
56
+
57
+ def fire_probe(notification, id, payload, type, start_time = nil, end_time = nil)
58
+ probe = find_or_create_probe(notification, type)
59
+
60
+ if probe.enabled?
61
+ probe.fire id, payload.inspect, nsec_time_diff(start_time, end_time)
62
+ end
63
+ end
64
+
65
+ def nsec_time_diff(start_time, end_time)
66
+ return 0 unless start_time and end_time
67
+ ((end_time - start_time) * 1000000000).to_i
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,3 +1,3 @@
1
1
  module Dtrace
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/rails-dtrace.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  require "rails-dtrace/version"
2
- require "rails-dtrace/responder"
2
+ require "rails-dtrace/subscriber"
3
3
 
4
4
  require 'rails-dtrace/railtie' if defined?(Rails)
data/rails-dtrace.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
6
6
  gem.email = ["sax@livinginthepast.org"]
7
7
  gem.description = %q{Turn ActiveSupport::Notification instruments into DTrace probes. This allows you to trace Rails apps.}
8
8
  gem.summary = %q{Add DTrace probes to Rails}
9
- gem.homepage = ""
9
+ gem.homepage = "https://github.com/sax/dtrace-rails"
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
12
12
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| ::File.basename(f) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-dtrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-09 00:00:00.000000000 Z
12
+ date: 2012-08-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ruby-usdt
@@ -59,11 +59,11 @@ files:
59
59
  - Rakefile
60
60
  - lib/rails-dtrace.rb
61
61
  - lib/rails-dtrace/railtie.rb
62
- - lib/rails-dtrace/responder.rb
62
+ - lib/rails-dtrace/subscriber.rb
63
63
  - lib/rails-dtrace/version.rb
64
64
  - rails-dtrace.gemspec
65
65
  - spec/spec_helper.rb
66
- homepage: ''
66
+ homepage: https://github.com/sax/dtrace-rails
67
67
  licenses: []
68
68
  post_install_message:
69
69
  rdoc_options: []
@@ -75,23 +75,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
- segments:
79
- - 0
80
- hash: 1821254754778372356
81
78
  required_rubygems_version: !ruby/object:Gem::Requirement
82
79
  none: false
83
80
  requirements:
84
81
  - - ! '>='
85
82
  - !ruby/object:Gem::Version
86
83
  version: '0'
87
- segments:
88
- - 0
89
- hash: 1821254754778372356
90
84
  requirements: []
91
85
  rubyforge_project:
92
- rubygems_version: 1.8.23
86
+ rubygems_version: 1.8.24
93
87
  signing_key:
94
88
  specification_version: 3
95
89
  summary: Add DTrace probes to Rails
96
90
  test_files:
97
91
  - spec/spec_helper.rb
92
+ has_rdoc:
@@ -1,41 +0,0 @@
1
- require 'usdt'
2
-
3
- =begin
4
-
5
- Problems:
6
- * How do you dynamically enable/disable probes?
7
- * Split name on '.' sucks
8
- * Notifications include entry AND exit. Can we fire our own entry/exit probes?
9
-
10
- =end
11
-
12
- module DTrace
13
- class Responder
14
- cattr_reader :probes, :provider
15
- cattr_writer :logger
16
-
17
- @@provider = USDT::Provider.create :ruby, :rails
18
- @@probes = {}
19
- @@enabled = false
20
-
21
- def self.logger
22
- @@logger || Logger.new
23
- end
24
-
25
- def self.call(name, started, finished, unique_id, payload)
26
- unless probes.keys.include?(name)
27
- func_name, probe_name = name.split('.')
28
- p = @@provider.probe(func_name, probe_name, :integer, :integer, :string, :string)
29
- probes[name] = p
30
- logger.debug "Adding dtrace probe: #{name}"
31
- @@provider.disable if @@enabled
32
- @@provider.enable
33
- @@enabled = true
34
- end
35
-
36
- if probes[name].enabled?
37
- probes[name].fire(started.to_i, finished.to_i, unique_id, payload.inspect)
38
- end
39
- end
40
- end
41
- end