rails-dtrace 0.0.1 → 0.0.2
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/.gitignore +1 -0
- data/LICENSE +9 -18
- data/README.md +82 -20
- data/lib/rails-dtrace/railtie.rb +1 -2
- data/lib/rails-dtrace/subscriber.rb +71 -0
- data/lib/rails-dtrace/version.rb +1 -1
- data/lib/rails-dtrace.rb +1 -1
- data/rails-dtrace.gemspec +1 -1
- metadata +6 -11
- data/lib/rails-dtrace/responder.rb +0 -41
data/.gitignore
CHANGED
data/LICENSE
CHANGED
@@ -1,22 +1,13 @@
|
|
1
1
|
Copyright (c) 2012 Eric Saxby
|
2
2
|
|
3
|
-
|
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
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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.
|
46
|
-
|
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
|
-
|
49
|
-
|
50
|
-
* `
|
51
|
-
* `
|
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
|
57
|
-
|
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
|
-
*
|
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.
|
data/lib/rails-dtrace/railtie.rb
CHANGED
@@ -2,8 +2,7 @@ module DTrace
|
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
|
4
4
|
initializer 'railtie.configure_rails.initialization' do
|
5
|
-
|
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
|
data/lib/rails-dtrace/version.rb
CHANGED
data/lib/rails-dtrace.rb
CHANGED
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.
|
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-
|
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/
|
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.
|
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
|