ruby-dtrace 0.0.6 → 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
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