chrisa-ruby-dtrace 0.2.0
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/History.txt +34 -0
- data/Manifest.txt +58 -0
- data/README.txt +88 -0
- data/Rakefile +73 -0
- data/examples/scsi.rb +442 -0
- data/ext/dof/constants.c +49 -0
- data/ext/dof/dof.h +55 -0
- data/ext/dof/dof_api.c +57 -0
- data/ext/dof/dof_helper.c +82 -0
- data/ext/dof/extconf.rb +4 -0
- data/ext/dof/file.c +56 -0
- data/ext/dof/generator.c +9 -0
- data/ext/dof/header.c +80 -0
- data/ext/dof/parser.c +415 -0
- data/ext/dof/parser.h +10 -0
- data/ext/dof/section.c +302 -0
- data/ext/dtrace_aggdata.c +142 -0
- data/ext/dtrace_api.c +119 -0
- data/ext/dtrace_api.h +150 -0
- data/ext/dtrace_bufdata.c +139 -0
- data/ext/dtrace_dropdata.c +131 -0
- data/ext/dtrace_errdata.c +110 -0
- data/ext/dtrace_hdl.c +577 -0
- data/ext/dtrace_probedata.c +267 -0
- data/ext/dtrace_probedesc.c +78 -0
- data/ext/dtrace_process.c +37 -0
- data/ext/dtrace_program.c +62 -0
- data/ext/dtrace_programinfo.c +60 -0
- data/ext/dtrace_recdesc.c +46 -0
- data/ext/dtrace_util.c +92 -0
- data/ext/extconf.rb +28 -0
- data/ext/stubs.txt +78 -0
- data/lib/dtrace/aggregate.rb +40 -0
- data/lib/dtrace/aggregateset.rb +19 -0
- data/lib/dtrace/consumer.rb +174 -0
- data/lib/dtrace/data.rb +82 -0
- data/lib/dtrace/dof/file.rb +63 -0
- data/lib/dtrace/dof/section/strtab.rb +21 -0
- data/lib/dtrace/dof/section.rb +69 -0
- data/lib/dtrace/dof.rb +8 -0
- data/lib/dtrace/printfrecord.rb +10 -0
- data/lib/dtrace/probe.rb +46 -0
- data/lib/dtrace/probedata.rb +23 -0
- data/lib/dtrace/probedesc.rb +15 -0
- data/lib/dtrace/provider/probedef.rb +24 -0
- data/lib/dtrace/provider.rb +231 -0
- data/lib/dtrace/record.rb +11 -0
- data/lib/dtrace/stackrecord.rb +31 -0
- data/lib/dtrace/tracer.rb +35 -0
- data/lib/dtrace.rb +74 -0
- data/lib/dtraceconsumer.rb +9 -0
- data/plugin/dtrace/README +81 -0
- data/plugin/dtrace/Rakefile +22 -0
- data/plugin/dtrace/bin/dtracer.rb +29 -0
- data/plugin/dtrace/init.rb +7 -0
- data/plugin/dtrace/lib/dtrace_helper.rb +2 -0
- data/plugin/dtrace/lib/dtrace_report.rb +67 -0
- data/plugin/dtrace/lib/dtracer.rb +52 -0
- data/plugin/dtrace/lib/dtracer_client.rb +26 -0
- data/plugin/dtrace/public/stylesheets/dtrace.css +48 -0
- data/plugin/dtrace/scripts/default.d +11 -0
- data/plugin/dtrace/scripts/rails_mysql.d +29 -0
- data/plugin/dtrace/tasks/dtrace.rake +52 -0
- data/plugin/dtrace/test/dtrace_test.rb +8 -0
- data/plugin/dtrace/views/dtrace/_report.rhtml +26 -0
- data/test/apple-dof +0 -0
- data/test/disabled_probe_effect.txt +19 -0
- data/test/dof +0 -0
- data/test/dof2 +0 -0
- data/test/test_disabled_probe_effect.rb +60 -0
- data/test/test_dof_generator.rb +142 -0
- data/test/test_dof_helper.rb +106 -0
- data/test/test_dof_parser.rb +25 -0
- data/test/test_dof_providers.rb +282 -0
- data/test/test_dof_strtabs.rb +92 -0
- data/test/test_dtrace.rb +111 -0
- data/test/test_dtrace_aggregates.rb +56 -0
- data/test/test_dtrace_drops_errors.rb +183 -0
- data/test/test_dtrace_probe.rb +383 -0
- data/test/test_dtrace_probes.rb +400 -0
- data/test/test_dtrace_processes.rb +83 -0
- data/test/test_dtrace_profile.rb +232 -0
- data/test/test_dtrace_provider.rb +153 -0
- data/test/test_dtrace_repeat.rb +51 -0
- data/test/test_dtrace_rubyprobe.rb +52 -0
- data/test/test_dtrace_typefilter.rb +108 -0
- data/test/test_legacy_consumer.rb +56 -0
- metadata +165 -0
@@ -0,0 +1,231 @@
|
|
1
|
+
#
|
2
|
+
# Ruby-Dtrace
|
3
|
+
# (c) 2008 Chris Andrews <chris@nodnol.org>
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'dtrace'
|
7
|
+
require 'dtrace/dof'
|
8
|
+
require 'dtrace/probe'
|
9
|
+
require 'dtrace/provider/probedef'
|
10
|
+
|
11
|
+
class Dtrace
|
12
|
+
|
13
|
+
# A DTrace provider. Allows creation of USDT probes on a running
|
14
|
+
# Ruby program. You can use this with a Ruby interpreter compiled
|
15
|
+
# with the core DTrace probes, but you don't have to.
|
16
|
+
#
|
17
|
+
# Firing probes is explained in Dtrace::Probe.
|
18
|
+
#
|
19
|
+
class Provider
|
20
|
+
include Dtrace::Dof::Constants
|
21
|
+
|
22
|
+
Typemap = { :string => 'char *', :integer => 'int' }
|
23
|
+
|
24
|
+
# Creates a DTrace provider.
|
25
|
+
#
|
26
|
+
# Example:
|
27
|
+
#
|
28
|
+
# Dtrace::Provider.create :action_controller do |p|
|
29
|
+
# p.probe :process_start, :string
|
30
|
+
# p.probe :process_finish, :string, :integer
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# The symbol passed to create becomes the name of the provider,
|
34
|
+
# and the class exposed under Dtrace::Probe in Ruby (camelized, so
|
35
|
+
# the above statement creates Dtrace::Probe::ActionController).
|
36
|
+
#
|
37
|
+
# create yields a Provider for the current platform, on which you
|
38
|
+
# can call probe, to create the individual probes.
|
39
|
+
#
|
40
|
+
# You can override the module name in the created probes, by
|
41
|
+
# passing in an hash:
|
42
|
+
#
|
43
|
+
# Dtrace::Provider.create :foo, { :module => 'somemodule' } do |p|
|
44
|
+
# p.probe...
|
45
|
+
# end
|
46
|
+
def self.create(name, options={})
|
47
|
+
options[:module] ||= 'ruby'
|
48
|
+
provider = Dtrace::Provider.new(name, options[:module])
|
49
|
+
yield provider
|
50
|
+
provider.enable
|
51
|
+
end
|
52
|
+
|
53
|
+
# Creates a DTrace USDT probe. Arguments are the probe name, and
|
54
|
+
# then the argument types it will accept. The following argument
|
55
|
+
# types are supported:
|
56
|
+
#
|
57
|
+
# :string (char *)
|
58
|
+
# :integer (int)
|
59
|
+
#
|
60
|
+
# Providing an options hash as the second argument allows you to
|
61
|
+
# override the function name, otherwise it will be taken from the
|
62
|
+
# caller of this function:
|
63
|
+
#
|
64
|
+
# p.probe :foo, { :function => 'somefunction' }, :int, ...
|
65
|
+
#
|
66
|
+
def probe(name, *types)
|
67
|
+
options = {}
|
68
|
+
if types[0].respond_to? :keys
|
69
|
+
options = types.shift
|
70
|
+
end
|
71
|
+
options[:function] ||= Kernel.caller[0].match(/`(.*)'/)[1]
|
72
|
+
|
73
|
+
pd = Dtrace::Provider::ProbeDef.new(name, options[:function])
|
74
|
+
types.each do |t|
|
75
|
+
if Typemap[t].nil?
|
76
|
+
raise Dtrace::Exception.new("type '#{t}' invalid")
|
77
|
+
else
|
78
|
+
pd.args << Typemap[t]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
@probe_defs << pd
|
83
|
+
end
|
84
|
+
|
85
|
+
def initialize(provider_name, module_name)
|
86
|
+
@name = provider_name.to_s
|
87
|
+
@module = module_name.to_s
|
88
|
+
@class = camelize(provider_name)
|
89
|
+
@probe_defs = []
|
90
|
+
end
|
91
|
+
|
92
|
+
def enable
|
93
|
+
f = Dtrace::Dof::File.new
|
94
|
+
|
95
|
+
strtab = Dtrace::Dof::Section::Strtab.new(0)
|
96
|
+
f.sections << strtab
|
97
|
+
|
98
|
+
s = Dtrace::Dof::Section.new(DOF_SECT_PROBES, 1)
|
99
|
+
probes = Array.new
|
100
|
+
stubs = Hash.new
|
101
|
+
argidx = 0
|
102
|
+
offidx = 0
|
103
|
+
@probe_defs.each do |pd|
|
104
|
+
argc = pd.argc
|
105
|
+
|
106
|
+
argv = 0
|
107
|
+
pd.args.each do |type|
|
108
|
+
i = strtab.add(type)
|
109
|
+
argv = i if argv == 0
|
110
|
+
end
|
111
|
+
|
112
|
+
probe = Dtrace::Probe.new(argc)
|
113
|
+
probes <<
|
114
|
+
{
|
115
|
+
:name => strtab.add(pd.name),
|
116
|
+
:func => strtab.add(pd.function),
|
117
|
+
:noffs => 1,
|
118
|
+
:enoffidx => offidx,
|
119
|
+
:argidx => argidx,
|
120
|
+
:nenoffs => 1,
|
121
|
+
:offidx => offidx,
|
122
|
+
:addr => probe.addr,
|
123
|
+
:nargc => argc,
|
124
|
+
:xargc => argc,
|
125
|
+
:nargv => argv,
|
126
|
+
:xargv => argv,
|
127
|
+
}
|
128
|
+
|
129
|
+
stubs[pd.name] = probe
|
130
|
+
argidx += argc
|
131
|
+
offidx += 1
|
132
|
+
end
|
133
|
+
s.data = probes
|
134
|
+
f.sections << s
|
135
|
+
|
136
|
+
s = Dtrace::Dof::Section.new(DOF_SECT_PRARGS, 2)
|
137
|
+
s.data = Array.new
|
138
|
+
@probe_defs.each do |pd|
|
139
|
+
pd.args.each_with_index do |arg, i|
|
140
|
+
s.data << i
|
141
|
+
end
|
142
|
+
end
|
143
|
+
if s.data.empty?
|
144
|
+
s.data = [ 0 ]
|
145
|
+
end
|
146
|
+
f.sections << s
|
147
|
+
|
148
|
+
s = Dtrace::Dof::Section.new(DOF_SECT_PROFFS, 3)
|
149
|
+
s.data = Array.new
|
150
|
+
@probe_defs.each do |pd|
|
151
|
+
s.data << stubs[pd.name].probe_offset(f.addr, pd.argc)
|
152
|
+
end
|
153
|
+
if s.data.empty?
|
154
|
+
s.data = [ 0 ]
|
155
|
+
end
|
156
|
+
f.sections << s
|
157
|
+
|
158
|
+
s = Dtrace::Dof::Section.new(DOF_SECT_PRENOFFS, 4)
|
159
|
+
s.data = Array.new
|
160
|
+
@probe_defs.each do |pd|
|
161
|
+
s.data << stubs[pd.name].is_enabled_offset(f.addr)
|
162
|
+
end
|
163
|
+
if s.data.empty?
|
164
|
+
s.data = [ 0 ]
|
165
|
+
end
|
166
|
+
f.sections << s
|
167
|
+
|
168
|
+
s = Dtrace::Dof::Section.new(DOF_SECT_PROVIDER, 5)
|
169
|
+
s.data = {
|
170
|
+
:strtab => 0,
|
171
|
+
:probes => 1,
|
172
|
+
:prargs => 2,
|
173
|
+
:proffs => 3,
|
174
|
+
:prenoffs => 4,
|
175
|
+
:name => strtab.add(@name),
|
176
|
+
:provattr => {
|
177
|
+
:name => DTRACE_STABILITY_EVOLVING,
|
178
|
+
:data => DTRACE_STABILITY_EVOLVING,
|
179
|
+
:class => DTRACE_STABILITY_EVOLVING
|
180
|
+
},
|
181
|
+
:modattr => {
|
182
|
+
:name => DTRACE_STABILITY_PRIVATE,
|
183
|
+
:data => DTRACE_STABILITY_PRIVATE,
|
184
|
+
:class => DTRACE_STABILITY_EVOLVING
|
185
|
+
},
|
186
|
+
:funcattr => {
|
187
|
+
:name => DTRACE_STABILITY_PRIVATE,
|
188
|
+
:data => DTRACE_STABILITY_PRIVATE,
|
189
|
+
:class => DTRACE_STABILITY_EVOLVING
|
190
|
+
},
|
191
|
+
:nameattr => {
|
192
|
+
:name => DTRACE_STABILITY_EVOLVING,
|
193
|
+
:data => DTRACE_STABILITY_EVOLVING,
|
194
|
+
:class => DTRACE_STABILITY_EVOLVING
|
195
|
+
},
|
196
|
+
:argsattr => {
|
197
|
+
:name => DTRACE_STABILITY_EVOLVING,
|
198
|
+
:data => DTRACE_STABILITY_EVOLVING,
|
199
|
+
:class => DTRACE_STABILITY_EVOLVING
|
200
|
+
},
|
201
|
+
}
|
202
|
+
f.sections << s
|
203
|
+
|
204
|
+
f.generate
|
205
|
+
Dtrace::Dof.loaddof(f, @module)
|
206
|
+
|
207
|
+
c = Class.new
|
208
|
+
c.class_eval do
|
209
|
+
@@probes = stubs
|
210
|
+
def self.method_missing(name)
|
211
|
+
name = name.to_s
|
212
|
+
unless @@probes[name].nil?
|
213
|
+
if @@probes[name].is_enabled?
|
214
|
+
yield @@probes[name]
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
eval "Dtrace::Probe::#{@class} = c"
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
def camelize(lower_case_and_underscored_word)
|
226
|
+
# Pinched from ActiveSupport's Inflector
|
227
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# Ruby-Dtrace
|
3
|
+
# (c) 2007 Chris Andrews <chris@nodnol.org>
|
4
|
+
#
|
5
|
+
|
6
|
+
# A record representing the result of a stack() or ustack()
|
7
|
+
# action. Its value is a list of symbolic stack frames:
|
8
|
+
#
|
9
|
+
# #<DtraceStackRecord:0x14e24 @value=
|
10
|
+
# ["libSystem.B.dylib`__sysctl+0xa",
|
11
|
+
# "libdtrace.dylib`dt_aggregate_go+0x9a",
|
12
|
+
# "dtrace_api.bundle`dtrace_hdl_go+0x30",
|
13
|
+
# "libruby.1.dylib`rb_eval_string_wrap+0x40fd",
|
14
|
+
# "libruby.1.dylib`rb_eval_string_wrap+0x4cdb",
|
15
|
+
# ...
|
16
|
+
# "libruby.1.dylib`rb_apply+0x392",
|
17
|
+
# "libruby.1.dylib`rb_eval_string_wrap+0xe82"]>
|
18
|
+
#
|
19
|
+
class Dtrace
|
20
|
+
class StackRecord
|
21
|
+
attr_reader :value
|
22
|
+
|
23
|
+
# Given a stack as a string returned from DTrace, set the value of
|
24
|
+
# this record to a list of stack frames.
|
25
|
+
def parse(raw)
|
26
|
+
frames = raw.split(/\n/)
|
27
|
+
@value = frames.map {|f| f.lstrip }.select {|f| f.length > 0 }
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#
|
2
|
+
# Ruby-Dtrace
|
3
|
+
# (c) 2007 Chris Andrews <chris@nodnol.org>
|
4
|
+
#
|
5
|
+
|
6
|
+
# Leopard's ruby-probe is DTracer, Solaris's is Tracer.
|
7
|
+
|
8
|
+
class Dtrace
|
9
|
+
class Tracer
|
10
|
+
|
11
|
+
class NullTracer
|
12
|
+
def self.fire(arg0, arg1)
|
13
|
+
puts "NullTracer: #{arg0} #{arg1}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
@@tracer = nil
|
18
|
+
def self.fire(*args)
|
19
|
+
if @@tracer == nil
|
20
|
+
begin
|
21
|
+
# Avoid getting ourselves here:
|
22
|
+
@@tracer = Module.const_get('Tracer')
|
23
|
+
rescue NameError
|
24
|
+
begin
|
25
|
+
@@tracer = DTracer
|
26
|
+
rescue NameError
|
27
|
+
@@tracer = Dtrace::Tracer::NullTracer
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@@tracer.fire(*args)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/lib/dtrace.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#
|
2
|
+
# Ruby-Dtrace
|
3
|
+
# (c) 2007 Chris Andrews <chris@nodnol.org>
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'dtrace_api'
|
7
|
+
require 'dtrace/record'
|
8
|
+
require 'dtrace/consumer'
|
9
|
+
require 'dtraceconsumer'
|
10
|
+
require 'dtrace/aggregate'
|
11
|
+
require 'dtrace/aggregateset'
|
12
|
+
require 'dtrace/probedata'
|
13
|
+
require 'dtrace/probedesc'
|
14
|
+
require 'dtrace/stackrecord'
|
15
|
+
require 'dtrace/printfrecord'
|
16
|
+
require 'dtrace/data'
|
17
|
+
require 'dtrace/version'
|
18
|
+
|
19
|
+
# A DTrace handle. Provides methods for inspecting available probes,
|
20
|
+
# compiling and running programs, and for setting up callbacks to
|
21
|
+
# receive trace data.
|
22
|
+
#
|
23
|
+
# The general structure of a Dtrace-based program is:
|
24
|
+
#
|
25
|
+
# * Create a handle with Dtrace.new
|
26
|
+
# * Set options
|
27
|
+
# * Compile the program, possibly inspecting the return Dtrace::ProgramInfo
|
28
|
+
# * Execute the program
|
29
|
+
# * Start tracing
|
30
|
+
# * Consume data, either directly by setting up callbacks, or using a Dtrace::Consumer.
|
31
|
+
# * Stop tracing
|
32
|
+
#
|
33
|
+
# === Listing probes
|
34
|
+
#
|
35
|
+
# d.each_probe do |p|
|
36
|
+
# puts "#{p.provider}:#{p.mod}:#{p.func}:#{p.name}"
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# === Setting options
|
40
|
+
#
|
41
|
+
# d.setopt("bufsize", "8m")
|
42
|
+
# d.setopt("aggsize", "4m")
|
43
|
+
# d.setopt("stackframes", "5")
|
44
|
+
# d.setopt("strsize", "131072")
|
45
|
+
#
|
46
|
+
# === Compiling a program
|
47
|
+
#
|
48
|
+
# d.compile "syscall:::entry { trace(execname); stack(); }"
|
49
|
+
# d.execute
|
50
|
+
#
|
51
|
+
# === Setting up callbacks
|
52
|
+
#
|
53
|
+
# d.buf_consumer(prob {|buf| yield buf })
|
54
|
+
# d.work(proc {|probe| yield probe }, proc {|rec| yield rec })
|
55
|
+
#
|
56
|
+
# === Creating a process
|
57
|
+
#
|
58
|
+
# p = t.createprocess([ '/usr/bin/true' ])
|
59
|
+
# t.go
|
60
|
+
# p.continue
|
61
|
+
#
|
62
|
+
# c = Dtrace::Consumer.new(t)
|
63
|
+
# c.consume do |d|
|
64
|
+
# ..
|
65
|
+
# end
|
66
|
+
|
67
|
+
class Dtrace
|
68
|
+
STATUS_NONE = 0
|
69
|
+
STATUS_OKAY = 1
|
70
|
+
STATUS_EXITED = 2
|
71
|
+
STATUS_FILLED = 3
|
72
|
+
STATUS_STOPPED = 4
|
73
|
+
end
|
74
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
Dtrace
|
2
|
+
======
|
3
|
+
|
4
|
+
A simple plugin to run a DTrace script while your Rails app runs.
|
5
|
+
|
6
|
+
Installation:
|
7
|
+
|
8
|
+
script/plugin install http://ruby-dtrace.rubyforge.org/svn/trunk/plugin/dtrace
|
9
|
+
|
10
|
+
Or copy the plugin/dtrace directory from the installed ruby-dtrace gem
|
11
|
+
to your app's vendor/plugins directory.
|
12
|
+
|
13
|
+
Choose a mode of operation:
|
14
|
+
|
15
|
+
The process running DTrace needs additional privileges: on Solaris,
|
16
|
+
you can grant the dtrace_* privileges, and on both Leopard and Solaris
|
17
|
+
you can run the process as root.
|
18
|
+
|
19
|
+
This plugin can run in two modes: you can run the whole app with
|
20
|
+
DTrace privileges, or you can run a helper process with the
|
21
|
+
privileges, and the app will use DRb to communicate with it.
|
22
|
+
|
23
|
+
If you choose to run the app as root, you need to set the :tracer
|
24
|
+
option to the dtrace macro to :self, and if you choose to run the
|
25
|
+
helper as root, you need to set the :tracer option to :helper.
|
26
|
+
|
27
|
+
Configuring the plugin:
|
28
|
+
|
29
|
+
Add the following line to either the specific controller you want
|
30
|
+
traced, or to the ApplicationController, in which case it will be
|
31
|
+
applied to every controller in your app:
|
32
|
+
|
33
|
+
dtrace :on, :tracer => :self
|
34
|
+
|
35
|
+
or
|
36
|
+
|
37
|
+
dtrace :on, :tracer => :helper
|
38
|
+
|
39
|
+
You can also set the name of the script to be run, from the
|
40
|
+
vendor/plugins/dtrace/scripts/ directory with the :script option:
|
41
|
+
|
42
|
+
dtrace :on, :tracer => :self, :script => 'rails_mysql.d'
|
43
|
+
|
44
|
+
There are two scripts in the distribution:
|
45
|
+
* default.d
|
46
|
+
- a very simple script to log system calls, which doesn't require
|
47
|
+
a DTrace-enabled ruby.
|
48
|
+
|
49
|
+
* rails_mysql.d
|
50
|
+
- a simplified version of the script shown here:
|
51
|
+
http://blogs.sun.com/bmc/entry/dtrace_on_rails, which relies on
|
52
|
+
DTrace-enabled ruby, and the use of MySQL with the
|
53
|
+
libmysqlclient-based adapter, not the pure-ruby adapter.
|
54
|
+
|
55
|
+
Troubleshooting:
|
56
|
+
|
57
|
+
If you don't get a DTrace report, check the log for messages like:
|
58
|
+
|
59
|
+
DTrace start setup: unable to open dtrace (not root?)
|
60
|
+
|
61
|
+
(indicating a lack of privileges to start DTrace: check your setting
|
62
|
+
of the :tracer option, and how you are starting the application and/or
|
63
|
+
helper).
|
64
|
+
|
65
|
+
DTrace start compile: probe description pid14484::mysql_real_query:entry does not match any probes
|
66
|
+
|
67
|
+
(indicating the function mysql_real_query isn't found: either you're
|
68
|
+
not using MySQL, or you are using the pure ruby client).
|
69
|
+
|
70
|
+
DTrace start compile: probe description ruby15253:::function-entry does not match any probes
|
71
|
+
|
72
|
+
(indicating you are not using a DTrace-enabled ruby binary)
|
73
|
+
|
74
|
+
A DRb::DRbConnError reported by Rails, like this:
|
75
|
+
|
76
|
+
druby://localhost:2999 - #<Errno::ECONNREFUSED: Connection refused - connect(2)>
|
77
|
+
|
78
|
+
indicates you've selected the :helper option, but the helper isn't
|
79
|
+
running. Check that your app can connect to localhost:2999 for the DRb
|
80
|
+
service the helper provides.
|
81
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the dtrace plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the dtrace plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'Dtrace'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'drb'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
# allow running standalone or in a rails app
|
8
|
+
script_dir = Pathname.new(File.dirname(__FILE__)).realpath
|
9
|
+
rails_script = script_dir + '../vendor/plugins/dtrace/bin'
|
10
|
+
plugin_script = script_dir + '../../../../vendor/plugins/dtrace/bin'
|
11
|
+
if rails_script.directory?
|
12
|
+
rails_root = script_dir + '../'
|
13
|
+
elsif plugin_script.directory?
|
14
|
+
rails_root = script_dir + '../../../..'
|
15
|
+
end
|
16
|
+
$LOAD_PATH << File.join(rails_root + 'vendor/plugins/dtrace/lib')
|
17
|
+
|
18
|
+
require 'dtracer'
|
19
|
+
|
20
|
+
here = "druby://localhost:2999"
|
21
|
+
tracer = Dtracer.new
|
22
|
+
DRb.start_service here, tracer
|
23
|
+
puts "DTrace helper started"
|
24
|
+
begin
|
25
|
+
DRb.thread.join
|
26
|
+
rescue Interrupt
|
27
|
+
exit 0
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'dtracer'
|
2
|
+
require 'dtracer_client'
|
3
|
+
|
4
|
+
module DtraceReport
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend DtraceMacro
|
8
|
+
@@tracer = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
module DtraceMacro
|
12
|
+
def dtrace(enable=:on, options={})
|
13
|
+
if enable == :on
|
14
|
+
|
15
|
+
# Set tracer type, in-process or helper-process
|
16
|
+
if options[:tracer] == :self
|
17
|
+
tracer = Dtracer.new
|
18
|
+
elsif options[:tracer] == :helper
|
19
|
+
tracer = DtracerClient.new
|
20
|
+
else
|
21
|
+
raise "tracer option is self or helper"
|
22
|
+
end
|
23
|
+
|
24
|
+
tracer.logger = logger
|
25
|
+
|
26
|
+
# Set script, or default
|
27
|
+
if options[:script]
|
28
|
+
tracer.script = options[:script]
|
29
|
+
else
|
30
|
+
tracer.script = 'default.d'
|
31
|
+
end
|
32
|
+
|
33
|
+
DtraceReport.tracer = tracer
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :dtrace_report
|
39
|
+
attr_reader :dtrace_script
|
40
|
+
|
41
|
+
protected
|
42
|
+
def self.tracer=(tracer)
|
43
|
+
@@tracer = tracer
|
44
|
+
end
|
45
|
+
|
46
|
+
def enable_dtrace
|
47
|
+
if @@tracer
|
48
|
+
@@tracer.start_dtrace($$)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def append_dtrace_report
|
53
|
+
if @@tracer
|
54
|
+
@dtrace_script = @@tracer.script
|
55
|
+
@dtrace_report = @@tracer.end_dtrace
|
56
|
+
# yuck!
|
57
|
+
old_template_root = @template.base_path
|
58
|
+
begin
|
59
|
+
@template.view_paths = File.expand_path(File.dirname(__FILE__) + '/../views')
|
60
|
+
response.body.gsub!(/<\/body/, @template.render(:partial => 'dtrace/report') + '</body')
|
61
|
+
ensure
|
62
|
+
@template.view_paths = old_template_root
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'dtrace'
|
2
|
+
|
3
|
+
class Dtracer
|
4
|
+
attr_writer :logger, :dprogram
|
5
|
+
attr_reader :script
|
6
|
+
|
7
|
+
def script=(script)
|
8
|
+
@script = script
|
9
|
+
scriptdir = File.expand_path(File.dirname(__FILE__) + "/../scripts")
|
10
|
+
@dprogram = IO.read("#{scriptdir}/#{script}")
|
11
|
+
end
|
12
|
+
|
13
|
+
def start_dtrace(pid)
|
14
|
+
begin
|
15
|
+
@d = Dtrace.new
|
16
|
+
@d.setopt("aggsize", "4m")
|
17
|
+
@d.setopt("bufsize", "4m")
|
18
|
+
rescue DtraceException => e
|
19
|
+
@logger.warn("DTrace start setup: #{e.message}")
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
prog = @d.compile(@dprogram, pid.to_s)
|
25
|
+
prog.execute
|
26
|
+
@d.go
|
27
|
+
rescue DtraceException => e
|
28
|
+
@logger.warn("DTrace start compile: #{e.message}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def end_dtrace
|
33
|
+
# Check presence of handle and correct status.
|
34
|
+
return [] unless @d && @d.status == Dtrace::STATUS_OKAY
|
35
|
+
|
36
|
+
dtrace_data = nil
|
37
|
+
begin
|
38
|
+
c = DtraceConsumer.new(@d)
|
39
|
+
c.consume_once do |d|
|
40
|
+
dtrace_data = d
|
41
|
+
end
|
42
|
+
rescue DtraceException => e
|
43
|
+
@logger.warn("DTrace end: #{e.message}")
|
44
|
+
end
|
45
|
+
|
46
|
+
if dtrace_data
|
47
|
+
return dtrace_data.data
|
48
|
+
else
|
49
|
+
return []
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'drb'
|
2
|
+
|
3
|
+
class DtracerClient
|
4
|
+
attr_writer :logger
|
5
|
+
attr_reader :script
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
DRb.start_service
|
9
|
+
@tracer = DRbObject.new(nil, 'druby://localhost:2999')
|
10
|
+
end
|
11
|
+
|
12
|
+
def script=(script)
|
13
|
+
@script = script
|
14
|
+
scriptdir = File.expand_path(File.dirname(__FILE__) + "/../scripts")
|
15
|
+
@tracer.dprogram = IO.read("#{scriptdir}/#{script}")
|
16
|
+
end
|
17
|
+
|
18
|
+
def start_dtrace(pid)
|
19
|
+
@tracer.start_dtrace(pid)
|
20
|
+
end
|
21
|
+
|
22
|
+
def end_dtrace
|
23
|
+
@tracer.end_dtrace
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|