ruby-dtrace 0.0.6 → 0.2.8

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.
Files changed (81) hide show
  1. data/History.txt +21 -0
  2. data/Manifest.txt +86 -19
  3. data/README.txt +48 -6
  4. data/Rakefile +61 -20
  5. data/examples/scsi.rb +1 -1
  6. data/ext/dof/Makefile +154 -0
  7. data/ext/dof/constants.c +57 -0
  8. data/ext/dof/dof.h +56 -0
  9. data/ext/dof/dof_api.c +58 -0
  10. data/ext/dof/dof_helper.c +82 -0
  11. data/ext/dof/extconf.rb +4 -0
  12. data/ext/dof/file.c +90 -0
  13. data/ext/dof/generator.c +9 -0
  14. data/ext/dof/header.c +79 -0
  15. data/ext/dof/mkmf.log +10 -0
  16. data/ext/dof/parser.c +415 -0
  17. data/ext/dof/parser.h +10 -0
  18. data/ext/dof/section.c +312 -0
  19. data/ext/dtrace_aggdata.c +2 -2
  20. data/ext/dtrace_api.c +46 -34
  21. data/ext/dtrace_api.h +31 -7
  22. data/ext/dtrace_bufdata.c +3 -3
  23. data/ext/dtrace_hdl.c +66 -3
  24. data/ext/dtrace_probedata.c +4 -4
  25. data/ext/{dtrace_probe.c → dtrace_probedesc.c} +7 -7
  26. data/ext/extconf.rb +25 -0
  27. data/ext/i386-darwin/dtrace_probe.c +278 -0
  28. data/ext/i386-solaris/dtrace_probe.c +225 -0
  29. data/ext/stubs.txt +78 -0
  30. data/lib/dtrace.rb +34 -13
  31. data/lib/dtrace/aggregate.rb +40 -0
  32. data/lib/dtrace/aggregateset.rb +19 -0
  33. data/lib/dtrace/consumer.rb +174 -0
  34. data/lib/dtrace/data.rb +82 -0
  35. data/lib/dtrace/dof.rb +8 -0
  36. data/lib/dtrace/dof/file.rb +64 -0
  37. data/lib/dtrace/dof/section.rb +75 -0
  38. data/lib/dtrace/dof/section/strtab.rb +28 -0
  39. data/lib/{dtraceprintfrecord.rb → dtrace/printfrecord.rb} +4 -2
  40. data/lib/dtrace/probe.rb +3 -6
  41. data/lib/dtrace/probedata.rb +23 -0
  42. data/lib/dtrace/probedesc.rb +15 -0
  43. data/lib/dtrace/provider.rb +190 -169
  44. data/lib/dtrace/provider/klass.rb +33 -0
  45. data/lib/dtrace/provider/probedef.rb +24 -0
  46. data/lib/{dtracerecord.rb → dtrace/record.rb} +4 -2
  47. data/lib/{dtracestackrecord.rb → dtrace/stackrecord.rb} +10 -8
  48. data/lib/dtrace/version.rb +9 -0
  49. data/lib/dtraceconsumer.rb +3 -167
  50. data/plugin/dtrace/lib/dtracer.rb +4 -4
  51. data/test/apple-dof +0 -0
  52. data/test/disabled_probe_effect.txt +19 -0
  53. data/test/dof +0 -0
  54. data/test/dof2 +0 -0
  55. data/test/test_disabled_probe_effect.rb +56 -0
  56. data/test/test_dof_generator.rb +142 -0
  57. data/test/test_dof_helper.rb +106 -0
  58. data/test/test_dof_parser.rb +27 -0
  59. data/test/test_dof_providers.rb +278 -0
  60. data/test/test_dof_strtabs.rb +98 -0
  61. data/test/test_dtrace.rb +67 -1
  62. data/test/test_dtrace_aggregates.rb +5 -5
  63. data/test/test_dtrace_drops_errors.rb +5 -5
  64. data/test/test_dtrace_probe.rb +385 -0
  65. data/test/test_dtrace_probes.rb +414 -0
  66. data/test/test_dtrace_processes.rb +2 -2
  67. data/test/test_dtrace_profile.rb +12 -12
  68. data/test/test_dtrace_provider.rb +138 -0
  69. data/test/test_dtrace_repeat.rb +1 -1
  70. data/test/test_dtrace_rubyprobe.rb +3 -1
  71. data/test/test_dtrace_typefilter.rb +9 -9
  72. data/test/test_legacy_consumer.rb +56 -0
  73. metadata +112 -71
  74. data/lib/dtrace/provider/osx.rb +0 -25
  75. data/lib/dtrace/provider/solaris.rb +0 -29
  76. data/lib/dtraceaggregate.rb +0 -37
  77. data/lib/dtraceaggregateset.rb +0 -17
  78. data/lib/dtracedata.rb +0 -80
  79. data/lib/dtraceprobe.rb +0 -13
  80. data/lib/dtraceprobedata.rb +0 -21
  81. data/test/test_dynusdt.rb +0 -135
@@ -0,0 +1,8 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2008 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ require 'dof/dof_api'
7
+ require 'dtrace/dof/section'
8
+ require 'dtrace/dof/file'
@@ -0,0 +1,64 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2008 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ class Dtrace::Dof::File
7
+ include Dtrace::Dof::Constants
8
+ attr_accessor :sections
9
+
10
+ def initialize
11
+ @sections = []
12
+ end
13
+
14
+ def generate
15
+ hdr = Dtrace::Dof::Header.new
16
+ hdr.secnum = @sections.length
17
+ filesz = hdr.hdrlen
18
+ loadsz = filesz
19
+ dof_version = 1
20
+
21
+ @sections.each do |s|
22
+ # Presence of is_enabled probes forces DOF version 2.
23
+ if s.section_type == DOF_SECT_PRENOFFS
24
+ dof_version = 2
25
+ end
26
+
27
+ length = s.generate
28
+ s.offset = filesz
29
+
30
+ pad = 0
31
+ if s.align > 1
32
+ i = s.offset.to_f % s.align
33
+ if i > 0
34
+ pad = (s.align - i).to_i
35
+ s.offset = pad + s.offset
36
+ s.pad = "\000" * pad
37
+ end
38
+ end
39
+
40
+ s.size = length
41
+
42
+ loadsz += (s.size + pad) if (s.flags & 1) == 1 # DOF_SECF_LOAD
43
+ filesz += (s.size + pad)
44
+
45
+ end
46
+
47
+ hdr.loadsz = loadsz
48
+ hdr.filesz = filesz
49
+ hdr.dof_version = dof_version
50
+
51
+ self << hdr.generate
52
+
53
+ @sections.each do |s|
54
+ self << s.generate_header
55
+ end
56
+
57
+ @sections.each do |s|
58
+ self << s.pad if s.pad
59
+ self << s.dof
60
+ end
61
+
62
+ end
63
+ end
64
+
@@ -0,0 +1,75 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2008 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ require 'dtrace/dof/section/strtab'
7
+
8
+ class Dtrace::Dof::Section
9
+ include Dtrace::Dof::Constants
10
+ attr_writer :entsize
11
+ attr_accessor :flags, :data, :offset, :align, :pad, :size
12
+ attr_reader :section_type, :dof
13
+
14
+ def initialize(type, index)
15
+ @section_type = type
16
+ @index = index
17
+ @flags = 1 # DOF_SECF_LOAD
18
+ @data = {}
19
+ end
20
+
21
+ def generate
22
+ case @section_type
23
+ when DOF_SECT_COMMENTS
24
+ @align = 1
25
+ @dof = dof_generate_comments
26
+ when DOF_SECT_STRTAB
27
+ @align = 1
28
+ @dof = dof_generate_strtab
29
+ when DOF_SECT_PROBES
30
+ @align = 8
31
+ @dof = dof_generate_probes
32
+ when DOF_SECT_PRARGS
33
+ @align = 1
34
+ @dof = dof_generate_prargs
35
+ when DOF_SECT_PROFFS
36
+ @align = 4
37
+ @dof = dof_generate_proffs
38
+ when DOF_SECT_PRENOFFS
39
+ @align = 4
40
+ @dof = dof_generate_prenoffs
41
+ when DOF_SECT_PROVIDER
42
+ @align = 4
43
+ @dof = dof_generate_provider
44
+ when DOF_SECT_RELTAB
45
+ @align = 8
46
+ @dof = dof_generate_reltab
47
+ when DOF_SECT_URELHDR
48
+ @align = 4
49
+ @dof = dof_generate_relhdr
50
+ when DOF_SECT_UTSNAME
51
+ @align = 1
52
+ @dof = dof_generate_utsname
53
+ else
54
+ @dof = ''
55
+ end
56
+
57
+ @entsize = compute_entsize
58
+
59
+ return @dof.length
60
+ end
61
+
62
+ def compute_entsize
63
+ begin
64
+ if @data.class == Array
65
+ entsize = @dof.length / @data.length
66
+ else
67
+ entsize = 0
68
+ end
69
+ rescue ZeroDivisionError
70
+ entsize = 0
71
+ end
72
+ entsize
73
+ end
74
+
75
+ end
@@ -0,0 +1,28 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2008 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ class Dtrace::Dof::Section::Strtab < Dtrace::Dof::Section
7
+ def initialize(index)
8
+ super(DOF_SECT_STRTAB, index)
9
+ self.data = []
10
+ @idx = 1
11
+ end
12
+
13
+ def add(string)
14
+ idx = @idx
15
+ string = string.to_s
16
+ @idx += (string.length + 1)
17
+ self.data << string
18
+ return idx
19
+ end
20
+
21
+ def length
22
+ return @idx
23
+ end
24
+
25
+ def compute_entsize
26
+ 0
27
+ end
28
+ end
@@ -3,6 +3,8 @@
3
3
  # (c) 2007 Chris Andrews <chris@nodnol.org>
4
4
  #
5
5
  # A DTrace record for the formatted part of a printf() action.
6
- class DtracePrintfRecord
7
- attr_accessor :value
6
+ class Dtrace
7
+ class PrintfRecord
8
+ attr_accessor :value
9
+ end
8
10
  end
@@ -1,12 +1,10 @@
1
1
  #
2
2
  # Ruby-Dtrace
3
- # (c) 2007 Chris Andrews <chris@nodnol.org>
4
- #
3
+ # (c) 2008 Chris Andrews <chris@nodnol.org>
5
4
 
6
5
  class Dtrace
7
- # DTrace::Probe - Using dynamically created USDT probes in Ruby
8
- # programs:
9
- #
6
+ # Using dynamically created USDT probes in Ruby programs.
7
+ #
10
8
  # Having created the following probes with Dtrace::Provider:
11
9
  #
12
10
  # 74777 action_controller12297 action_controller.so process_finish process-finish
@@ -46,4 +44,3 @@ class Dtrace
46
44
  class Probe
47
45
  end
48
46
  end
49
-
@@ -0,0 +1,23 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2007 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ class Dtrace
7
+ class ProbeData
8
+
9
+ def records
10
+ records = Array.new
11
+ self.each_record do |rec|
12
+ records << rec
13
+ end
14
+ records
15
+ end
16
+
17
+ def to_s
18
+ rs = self.records
19
+ rs.map {|r| r.value }.join ', '
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2007 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ class Dtrace
7
+ class ProbeDesc
8
+
9
+ def to_s
10
+ "#{provider}:#{mod}:#{func}:#{name}"
11
+ end
12
+
13
+ end
14
+ end
15
+
@@ -1,61 +1,28 @@
1
1
  #
2
2
  # Ruby-Dtrace
3
- # (c) 2007 Chris Andrews <chris@nodnol.org>
3
+ # (c) 2008 Chris Andrews <chris@nodnol.org>
4
4
  #
5
5
 
6
+ require 'dtrace'
7
+ require 'dtrace/dof'
6
8
  require 'dtrace/probe'
7
- require 'dtrace/provider/solaris'
8
- require 'dtrace/provider/osx'
9
- require 'pathname'
10
- require 'tempfile'
11
-
12
- DTRACE = '/usr/sbin/dtrace'
9
+ require 'dtrace/provider/probedef'
10
+ require 'dtrace/provider/klass'
13
11
 
14
12
  class Dtrace
15
13
 
16
14
  # A DTrace provider. Allows creation of USDT probes on a running
17
- # Ruby program, by dynamically creating an extension module
18
- # implementing the probes, and compiling and loading it. You can use
19
- # this with a Ruby interpreter compiled with the core DTrace probes,
20
- # but you don't have to.
21
- #
22
- # This requires the DTrace and Ruby toolchains to be available:
23
- # dtrace(1M), and the compiler and linker used to build Ruby. The
24
- # process is similar to RubyInline, but the actual RubyInline
25
- # library is not required (the build process for DTrace USDT probes
26
- # is sufficiently differnent to a standard Ruby extension that it's
27
- # not worth using it).
28
- #
29
- # Both Solaris and OSX 10.5 are supported. Other DTrace-supporting
30
- # platforms can be added by creating a new class under
31
- # Dtrace::Provider and implementing or overriding the required steps
32
- # in the build process.
15
+ # Ruby program. You can use this with a Ruby interpreter compiled
16
+ # with the core DTrace probes, but you don't have to.
33
17
  #
34
18
  # Firing probes is explained in Dtrace::Probe.
35
19
  #
36
- # There are some limitations:
37
- #
38
- # You cannot choose all the components of the probe name: you can
39
- # choose the provider and probe name, but the module and function
40
- # components will be derived by DTrace, and won't be meaningful
41
- # (they'll refer to the shim extension that gets created, not to
42
- # anything in your Ruby program). It seems unlikely it's possible to
43
- # change this.
44
- #
45
- # You cannot currently set D attributes: they're hardcoded to a
46
- # default set. This will change.
47
- #
48
- # The extension will currently be rebuilt every time the provider is
49
- # created, as there's not yet any support for packaging the provider
50
- # in some way. This will change, to something along the lines of
51
- # what RubyInline does to allow a pre-built extension to be used.
52
- #
53
20
  class Provider
21
+ include Dtrace::Dof::Constants
54
22
 
55
- class BuildError < StandardError; end
23
+ Typemap = { :string => 'char *', :integer => 'int' }
56
24
 
57
- # Creates a DTrace provider. Causes a shim extension to be built
58
- # and loaded, implementing the probes.
25
+ # Creates a DTrace provider.
59
26
  #
60
27
  # Example:
61
28
  #
@@ -71,12 +38,15 @@ class Dtrace
71
38
  # create yields a Provider for the current platform, on which you
72
39
  # can call probe, to create the individual probes.
73
40
  #
74
- def self.create(name)
75
- if RUBY_PLATFORM =~ /darwin/
76
- provider = Dtrace::Provider::OSX.new(name)
77
- else
78
- provider = Dtrace::Provider::Solaris.new(name)
79
- end
41
+ # You can override the module name in the created probes, by
42
+ # passing in an hash:
43
+ #
44
+ # Dtrace::Provider.create :foo, { :module => 'somemodule' } do |p|
45
+ # p.probe...
46
+ # end
47
+ def self.create(name, options={})
48
+ options[:module] ||= 'ruby'
49
+ provider = Dtrace::Provider.new(name, options[:module])
80
50
  yield provider
81
51
  provider.enable
82
52
  end
@@ -88,61 +58,192 @@ class Dtrace
88
58
  # :string (char *)
89
59
  # :integer (int)
90
60
  #
91
- # The probe will be named based on the provider name and the
92
- # probe's name:
93
- #
94
- # provider_name:provider_name.so:probe_name:probe-name
61
+ # Providing an options hash as the second argument allows you to
62
+ # override the function name, otherwise it will be taken from the
63
+ # caller of this function:
95
64
  #
96
- # See the limitations explained elsewhere for an explanation of
97
- # this redundancy in probe names.
65
+ # p.probe :foo, { :function => 'somefunction' }, :int, ...
98
66
  #
99
67
  def probe(name, *types)
100
- typemap = { :string => 'char *', :integer => 'int' }
101
- @probes[name] = types.map {|t| typemap[t]}
68
+ options = {}
69
+ if types[0].respond_to? :keys
70
+ options = types.shift
71
+ end
72
+ caller = Kernel.caller[0].match(/`(.*)'/)
73
+ if caller
74
+ options[:function] ||= caller[1]
75
+ else
76
+ options[:function] ||= name
77
+ end
78
+
79
+ pd = Dtrace::Provider::ProbeDef.new(name, options[:function])
80
+ types.each do |t|
81
+ if Typemap[t].nil?
82
+ raise Dtrace::Exception.new("type '#{t}' invalid")
83
+ else
84
+ pd.args << Typemap[t]
85
+ end
86
+ end
87
+
88
+ @probe_defs << pd
102
89
  end
103
90
 
104
- def initialize(name)
105
- @name = name.to_s
106
- @class = camelize(name)
107
- @probes = {}
91
+ def initialize(provider_name, module_name)
92
+ @name = provider_name.to_s
93
+ @module = module_name.to_s
94
+ @class = camelize(provider_name)
95
+ @probe_defs = []
96
+ end
97
+
98
+ # attempt to turn the probe_count into the eventual size of DOF
99
+ # we'll need, based on the current state of the strtab.
100
+ def dof_size
101
+ probes = @probe_defs.length
102
+ args = (@probe_defs.inject(0) {|sum, pd| sum + pd.args.length }) + 1
103
+
104
+ size = 0
105
+ [
106
+ DOF_DOFHDR_SIZE,
107
+ DOF_SECHDR_SIZE * 6, # we have 6 sections, see provider.rb
108
+ @strtab.length, # we're told the size of the string table
109
+ (DOF_PROBE_SIZE * probes), # probes
110
+ (DOF_PRARGS_SIZE * args), # prargs
111
+ (DOF_PROFFS_SIZE * probes), # proffs
112
+ (DOF_PRENOFFS_SIZE * probes), # prenoffs
113
+ DOF_PROVIDER_SIZE # provider
114
+ ].each do |sec|
115
+ size += sec
116
+ i = size.to_f % 8 # assume longest alignment, 8, will overestimate but not by much
117
+ if i > 0
118
+ size += (8 - i).to_i
119
+ end
120
+ end
121
+ size
108
122
  end
109
123
 
110
124
  def enable
111
- Tempfile.open("dtrace_probe_#{@name}") do |f|
112
- p = Pathname.new(f.path)
113
- @tempdir = "#{p.dirname}/#{@name}"
114
- begin
115
- Dir.mkdir @tempdir
116
- rescue Errno::EEXIST
117
- nil
125
+ @strtab = Dtrace::Dof::Section::Strtab.new(0)
126
+ provider_name_idx = @strtab.add(@name)
127
+
128
+ f = Dtrace::Dof::File.new
129
+ f.sections << @strtab
130
+
131
+ s = Dtrace::Dof::Section.new(DOF_SECT_PROBES, 1)
132
+ probes = Array.new
133
+ stubs = Hash.new
134
+ argidx = 0
135
+ offidx = 0
136
+ @probe_defs.each do |pd|
137
+ argc = pd.argc
138
+
139
+ argv = 0
140
+ pd.args.each do |type|
141
+ i = @strtab.add(type)
142
+ argv = i if argv == 0
118
143
  end
144
+
145
+ probe = Dtrace::Probe.new(argc)
146
+ probes <<
147
+ {
148
+ :name => @strtab.add(pd.name),
149
+ :func => @strtab.add(pd.function),
150
+ :noffs => 1,
151
+ :enoffidx => offidx,
152
+ :argidx => argidx,
153
+ :nenoffs => 1,
154
+ :offidx => offidx,
155
+ :addr => probe.addr,
156
+ :nargc => argc,
157
+ :xargc => argc,
158
+ :nargv => argv,
159
+ :xargv => argv,
160
+ }
161
+
162
+ stubs[pd.name] = probe
163
+ argidx += argc
164
+ offidx += 1
165
+ end
166
+ s.data = probes
167
+ f.sections << s
119
168
 
120
- # Probe setup is split up for easy overriding
121
- definition
122
- header
123
- source
124
- ruby_object
125
- dtrace_object
126
- link
127
- load
169
+ s = Dtrace::Dof::Section.new(DOF_SECT_PRARGS, 2)
170
+ s.data = Array.new
171
+ @probe_defs.each do |pd|
172
+ pd.args.each_with_index do |arg, i|
173
+ s.data << i
174
+ end
128
175
  end
129
- end
176
+ if s.data.empty?
177
+ s.data = [ 0 ]
178
+ end
179
+ f.sections << s
180
+
181
+ # After last addition to strtab, but before first offset!
182
+ f.allocate(self.dof_size)
130
183
 
131
- protected
184
+ s = Dtrace::Dof::Section.new(DOF_SECT_PROFFS, 3)
185
+ s.data = Array.new
186
+ @probe_defs.each do |pd|
187
+ s.data << stubs[pd.name].probe_offset(f.addr, pd.argc)
188
+ end
189
+ if s.data.empty?
190
+ s.data = [ 0 ]
191
+ end
192
+ f.sections << s
132
193
 
133
- def run(cmd)
134
- result = `#{cmd}`
135
- if $? != 0
136
- raise BuildError.new("Error running:\n#{cmd}\n\n#{result}")
194
+ s = Dtrace::Dof::Section.new(DOF_SECT_PRENOFFS, 4)
195
+ s.data = Array.new
196
+ @probe_defs.each do |pd|
197
+ s.data << stubs[pd.name].is_enabled_offset(f.addr)
137
198
  end
138
- end
199
+ if s.data.empty?
200
+ s.data = [ 0 ]
201
+ end
202
+ f.sections << s
203
+
204
+ s = Dtrace::Dof::Section.new(DOF_SECT_PROVIDER, 5)
205
+ s.data = {
206
+ :strtab => 0,
207
+ :probes => 1,
208
+ :prargs => 2,
209
+ :proffs => 3,
210
+ :prenoffs => 4,
211
+ :name => provider_name_idx,
212
+ :provattr => {
213
+ :name => DTRACE_STABILITY_EVOLVING,
214
+ :data => DTRACE_STABILITY_EVOLVING,
215
+ :class => DTRACE_STABILITY_EVOLVING
216
+ },
217
+ :modattr => {
218
+ :name => DTRACE_STABILITY_PRIVATE,
219
+ :data => DTRACE_STABILITY_PRIVATE,
220
+ :class => DTRACE_STABILITY_EVOLVING
221
+ },
222
+ :funcattr => {
223
+ :name => DTRACE_STABILITY_PRIVATE,
224
+ :data => DTRACE_STABILITY_PRIVATE,
225
+ :class => DTRACE_STABILITY_EVOLVING
226
+ },
227
+ :nameattr => {
228
+ :name => DTRACE_STABILITY_EVOLVING,
229
+ :data => DTRACE_STABILITY_EVOLVING,
230
+ :class => DTRACE_STABILITY_EVOLVING
231
+ },
232
+ :argsattr => {
233
+ :name => DTRACE_STABILITY_EVOLVING,
234
+ :data => DTRACE_STABILITY_EVOLVING,
235
+ :class => DTRACE_STABILITY_EVOLVING
236
+ },
237
+ }
238
+ f.sections << s
239
+
240
+ f.generate
241
+ Dtrace::Dof.loaddof(f, @module)
242
+
243
+ provider = Dtrace::Provider::Klass.new(f, stubs)
244
+ Dtrace::Probe.const_set(@class, provider)
139
245
 
140
- def hdrdir
141
- %w(srcdir archdir).map { |name|
142
- dir = Config::CONFIG[name]
143
- }.find { |dir|
144
- dir and File.exist? File.join(dir, "/ruby.h")
145
- } or abort "ERROR: Can't find header dir for ruby. Exiting..."
246
+ provider
146
247
  end
147
248
 
148
249
  private
@@ -151,86 +252,6 @@ class Dtrace
151
252
  # Pinched from ActiveSupport's Inflector
152
253
  lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
153
254
  end
154
-
155
- # compose provider definition .d file
156
- def definition
157
- stability = <<EOS
158
- #pragma D attributes Evolving/Evolving/Common provider #{@name} provider
159
- #pragma D attributes Private/Private/Common provider #{@name} module
160
- #pragma D attributes Private/Private/Common provider #{@name} function
161
- #pragma D attributes Evolving/Evolving/Common provider #{@name} name
162
- #pragma D attributes Evolving/Evolving/Common provider #{@name} args
163
- EOS
164
- File.open("#{@tempdir}/probes.d", 'w') do |io|
165
- io << "provider #{@name} {\n"
166
- @probes.each_pair do |name, types|
167
- probename = name.to_s.gsub(/_/, '__')
168
- typesdesc = types.join(', ')
169
- probedesc = " probe #{probename}(#{typesdesc});\n"
170
- io << probedesc
171
- end
172
- io << "\n};\n\n#{stability}"
173
- end
174
- end
175
-
176
- def header
177
- run "#{DTRACE} -h -s #{@tempdir}/probes.d -o #{@tempdir}/probes.h"
178
- end
179
-
180
- # Generate the C source for the provider class
181
- def source
182
- rb2c = { 'char *' => 'STR2CSTR', 'int' => 'FIX2INT' }
183
-
184
- File.open("#{@tempdir}/probes.c", 'w') do |io|
185
- io.puts '#include "ruby.h"'
186
- io.puts "#include \"#{@tempdir}/probes.h\""
187
-
188
- @probes.each_pair do |name, types|
189
- defn_args = []
190
- call_args = []
191
- types.each_with_index { |type, i| defn_args << "#{type} arg#{i}" }
192
- types.each_with_index { |type, i| call_args << "#{rb2c[type]}(rb_ary_entry(args, #{i}))" }
193
-
194
- io.puts <<EOC
195
- static VALUE #{name}(VALUE self) {
196
- if (#{@name.upcase}_#{name.to_s.upcase}_ENABLED()) {
197
- VALUE args = rb_yield(self);
198
- #{@name.upcase}_#{name.to_s.upcase}(#{call_args.join(', ')});
199
- }
200
- return Qnil;
201
- }
202
- EOC
203
- end
204
- io.puts <<EOC
205
- static VALUE fire(VALUE self, VALUE args) {
206
- return args;
207
- }
208
-
209
- void Init_#{@name}() {
210
- VALUE c = rb_cObject;
211
- rb_define_method(c, "fire", (VALUE(*)(ANYARGS))fire, -2);
212
- EOC
213
-
214
- @probes.each_pair do |name, types|
215
- io.puts " rb_define_singleton_method(c, \"#{name}\", (VALUE(*)(ANYARGS))#{name}, 0);"
216
- end
217
-
218
- io.puts '}'
219
- end
220
- end
221
-
222
- def load
223
- # Load the generated extension with a full path (saves adjusting
224
- # $:) Done in the context of an anonymous class, since the
225
- # module does not itself define a class. TODO: find a way of
226
- # doing this without string eval...
227
- lib = "#{@tempdir}/#{@name}"
228
- c = Class.new
229
- c.module_eval do
230
- require lib
231
- end
232
- eval "Dtrace::Probe::#{@class} = c"
233
- end
234
255
 
235
256
  end
236
257
  end