ruby-dtrace-consumer 0.4.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.
Files changed (75) hide show
  1. data/LICENCE +20 -0
  2. data/README.md +51 -0
  3. data/ext/Makefile +187 -0
  4. data/ext/dtrace_aggdata.c +132 -0
  5. data/ext/dtrace_aggdata.c~ +141 -0
  6. data/ext/dtrace_aggdata.o +0 -0
  7. data/ext/dtrace_api.bundle +0 -0
  8. data/ext/dtrace_api.c +102 -0
  9. data/ext/dtrace_api.c~ +113 -0
  10. data/ext/dtrace_api.h +138 -0
  11. data/ext/dtrace_api.h~ +155 -0
  12. data/ext/dtrace_api.o +0 -0
  13. data/ext/dtrace_bufdata.c +130 -0
  14. data/ext/dtrace_bufdata.c~ +139 -0
  15. data/ext/dtrace_bufdata.o +0 -0
  16. data/ext/dtrace_dropdata.c +121 -0
  17. data/ext/dtrace_dropdata.c~ +131 -0
  18. data/ext/dtrace_dropdata.o +0 -0
  19. data/ext/dtrace_errdata.c +100 -0
  20. data/ext/dtrace_errdata.c~ +110 -0
  21. data/ext/dtrace_errdata.o +0 -0
  22. data/ext/dtrace_hdl.c +677 -0
  23. data/ext/dtrace_hdl.c~ +689 -0
  24. data/ext/dtrace_hdl.o +0 -0
  25. data/ext/dtrace_probedata.c +273 -0
  26. data/ext/dtrace_probedata.c~ +283 -0
  27. data/ext/dtrace_probedata.o +0 -0
  28. data/ext/dtrace_probedesc.c +93 -0
  29. data/ext/dtrace_probedesc.c~ +78 -0
  30. data/ext/dtrace_probedesc.o +0 -0
  31. data/ext/dtrace_process.c +44 -0
  32. data/ext/dtrace_process.c~ +56 -0
  33. data/ext/dtrace_process.o +0 -0
  34. data/ext/dtrace_program.c +52 -0
  35. data/ext/dtrace_program.c~ +62 -0
  36. data/ext/dtrace_program.o +0 -0
  37. data/ext/dtrace_programinfo.c +70 -0
  38. data/ext/dtrace_programinfo.c~ +60 -0
  39. data/ext/dtrace_programinfo.o +0 -0
  40. data/ext/dtrace_recdesc.c +37 -0
  41. data/ext/dtrace_recdesc.c~ +46 -0
  42. data/ext/dtrace_recdesc.o +0 -0
  43. data/ext/dtrace_util.c +92 -0
  44. data/ext/dtrace_util.o +0 -0
  45. data/ext/extconf.rb +7 -0
  46. data/lib/dtrace.rb +95 -0
  47. data/lib/dtrace/aggregate.rb +40 -0
  48. data/lib/dtrace/aggregateset.rb +19 -0
  49. data/lib/dtrace/consumer.rb +174 -0
  50. data/lib/dtrace/data.rb +85 -0
  51. data/lib/dtrace/dof.rb +8 -0
  52. data/lib/dtrace/printfrecord.rb +10 -0
  53. data/lib/dtrace/probedata.rb +23 -0
  54. data/lib/dtrace/probedesc.rb +15 -0
  55. data/lib/dtrace/record.rb +11 -0
  56. data/lib/dtrace/stackrecord.rb +31 -0
  57. data/lib/dtrace/tracer.rb +35 -0
  58. data/lib/dtrace/version.rb +8 -0
  59. data/lib/dtrace/version.rb~ +8 -0
  60. data/lib/dtraceconsumer.rb +9 -0
  61. data/test/test_aggregates.rb +45 -0
  62. data/test/test_drops_errors.rb +166 -0
  63. data/test/test_dtrace.rb +155 -0
  64. data/test/test_gc.rb +11 -0
  65. data/test/test_helper.rb +20 -0
  66. data/test/test_helper.rb~ +16 -0
  67. data/test/test_legacy_consumer.rb +47 -0
  68. data/test/test_probedata.rb +30 -0
  69. data/test/test_processes.rb +66 -0
  70. data/test/test_profile.rb +198 -0
  71. data/test/test_repeat.rb +50 -0
  72. data/test/test_rubyprobe.rb +52 -0
  73. data/test/test_rubyprobe.rb~ +52 -0
  74. data/test/test_typefilter.rb +94 -0
  75. metadata +121 -0
@@ -0,0 +1,60 @@
1
+ /* Ruby-DTrace
2
+ * (c) 2007 Chris Andrews <chris@nodnol.org>
3
+ */
4
+
5
+ #include "dtrace_api.h"
6
+
7
+ /* :nodoc: */
8
+ VALUE dtraceprograminfo_init(VALUE self)
9
+ {
10
+ dtrace_proginfo_t *proginfo;
11
+
12
+ Data_Get_Struct(self, dtrace_proginfo_t, proginfo);
13
+ return self;
14
+ }
15
+
16
+ /*
17
+ * Returns the number of aggregates associated with this program.
18
+ */
19
+ VALUE dtraceprograminfo_aggregates_count(VALUE self)
20
+ {
21
+ dtrace_proginfo_t *proginfo;
22
+
23
+ Data_Get_Struct(self, dtrace_proginfo_t, proginfo);
24
+ return INT2NUM(proginfo->dpi_aggregates);
25
+ }
26
+
27
+ /*
28
+ * Returns the number of record generating probes associated with this
29
+ * program.
30
+ */
31
+ VALUE dtraceprograminfo_recgens_count(VALUE self)
32
+ {
33
+ dtrace_proginfo_t *proginfo;
34
+
35
+ Data_Get_Struct(self, dtrace_proginfo_t, proginfo);
36
+ return INT2NUM(proginfo->dpi_recgens);
37
+ }
38
+
39
+ /*
40
+ * Returns the number of probes matched by this program.
41
+ */
42
+ VALUE dtraceprograminfo_matches_count(VALUE self)
43
+ {
44
+ dtrace_proginfo_t *proginfo;
45
+
46
+ Data_Get_Struct(self, dtrace_proginfo_t, proginfo);
47
+ return INT2NUM(proginfo->dpi_matches);
48
+ }
49
+
50
+ /*
51
+ * Returns the number of speculations specified by this program.
52
+ */
53
+ VALUE dtraceprograminfo_speculations_count(VALUE self)
54
+ {
55
+ dtrace_proginfo_t *proginfo;
56
+
57
+ Data_Get_Struct(self, dtrace_proginfo_t, proginfo);
58
+ return INT2NUM(proginfo->dpi_speculations);
59
+ }
60
+
Binary file
@@ -0,0 +1,37 @@
1
+ /* Ruby-DTrace
2
+ * (c) 2007 Chris Andrews <chris@nodnol.org>
3
+ */
4
+
5
+ #include "dtrace_api.h"
6
+
7
+ /*
8
+ * Returns the type of action which generated this recdesc.
9
+ * (exit, printf, printa or "other" for all other actions).
10
+ */
11
+ VALUE dtracerecdesc_action(VALUE self)
12
+ {
13
+ dtrace_recdesc_t *recdesc;
14
+ VALUE v;
15
+ Data_Get_Struct(self, dtrace_recdesc_t, recdesc);
16
+
17
+ if (recdesc){
18
+ switch (recdesc->dtrd_action) {
19
+ case DTRACEACT_EXIT:
20
+ v = rb_str_new2("exit");
21
+ break;
22
+ case DTRACEACT_PRINTF:
23
+ v = rb_str_new2("printf");
24
+ break;
25
+ case DTRACEACT_PRINTA:
26
+ v = rb_str_new2("printa");
27
+ break;
28
+ default:
29
+ v = rb_str_new2("other");
30
+ break;
31
+ }
32
+ return v;
33
+ }
34
+ else {
35
+ return Qnil;
36
+ }
37
+ }
@@ -0,0 +1,46 @@
1
+ /* Ruby-DTrace
2
+ * (c) 2007 Chris Andrews <chris@nodnol.org>
3
+ */
4
+
5
+ #include "dtrace_api.h"
6
+
7
+ /* :nodoc: */
8
+ VALUE dtracerecdesc_init(VALUE self)
9
+ {
10
+ dtrace_recdesc_t *recdesc;
11
+
12
+ Data_Get_Struct(self, dtrace_recdesc_t, recdesc);
13
+ return self;
14
+ }
15
+
16
+ /*
17
+ * Returns the type of action which generated this recdesc.
18
+ * (exit, printf, printa or "other" for all other actions).
19
+ */
20
+ VALUE dtracerecdesc_action(VALUE self)
21
+ {
22
+ dtrace_recdesc_t *recdesc;
23
+ VALUE v;
24
+ Data_Get_Struct(self, dtrace_recdesc_t, recdesc);
25
+
26
+ if (recdesc){
27
+ switch (recdesc->dtrd_action) {
28
+ case DTRACEACT_EXIT:
29
+ v = rb_str_new2("exit");
30
+ break;
31
+ case DTRACEACT_PRINTF:
32
+ v = rb_str_new2("printf");
33
+ break;
34
+ case DTRACEACT_PRINTA:
35
+ v = rb_str_new2("printa");
36
+ break;
37
+ default:
38
+ v = rb_str_new2("other");
39
+ break;
40
+ }
41
+ return v;
42
+ }
43
+ else {
44
+ return Qnil;
45
+ }
46
+ }
Binary file
@@ -0,0 +1,92 @@
1
+ /* Ruby-DTrace
2
+ * (c) 2007 Chris Andrews <chris@nodnol.org>
3
+ */
4
+
5
+ #include "dtrace_api.h"
6
+ #include <ctype.h>
7
+
8
+ RUBY_EXTERN VALUE eDTraceException;
9
+
10
+ /*
11
+ * Most of this function lifted from libdtrace/common/dt_consume.c
12
+ * dt_print_bytes().
13
+ */
14
+ VALUE handle_bytedata(caddr_t addr, uint32_t nbytes)
15
+ {
16
+ /*
17
+ * If the byte stream is a series of printable characters, followed by
18
+ * a terminating byte, we print it out as a string. Otherwise, we
19
+ * assume that it's something else and just print the bytes.
20
+ */
21
+ int i, j;
22
+ char *c = addr;
23
+
24
+ VALUE robj;
25
+
26
+ if (nbytes == 0) {
27
+ return rb_str_new2("");
28
+ }
29
+
30
+ for (i = 0; i < nbytes; i++) {
31
+ /*
32
+ * We define a "printable character" to be one for which
33
+ * isprint(3C) returns non-zero, isspace(3C) returns non-zero,
34
+ * or a character which is either backspace or the bell.
35
+ * Backspace and the bell are regrettably special because
36
+ * they fail the first two tests -- and yet they are entirely
37
+ * printable. These are the only two control characters that
38
+ * have meaning for the terminal and for which isprint(3C) and
39
+ * isspace(3C) return 0.
40
+ */
41
+ if (isprint(c[i]) || isspace(c[i]) ||
42
+ c[i] == '\b' || c[i] == '\a')
43
+ continue;
44
+
45
+ if (c[i] == '\0' && i > 0) {
46
+ /*
47
+ * This looks like it might be a string. Before we
48
+ * assume that it is indeed a string, check the
49
+ * remainder of the byte range; if it contains
50
+ * additional non-nul characters, we'll assume that
51
+ * it's a binary stream that just happens to look like
52
+ * a string.
53
+ */
54
+ for (j = i + 1; j < nbytes; j++) {
55
+ if (c[j] != '\0')
56
+ break;
57
+ }
58
+
59
+ if (j != nbytes)
60
+ break;
61
+
62
+ /* It's a string */
63
+ return (rb_str_new2((char *)addr));
64
+ }
65
+
66
+ break;
67
+ }
68
+
69
+ if (i == nbytes) {
70
+ /*
71
+ * The byte range is all printable characters, but there is
72
+ * no trailing nul byte. We'll assume that it's a string.
73
+ */
74
+ char *s = malloc(nbytes + 1);
75
+ if (!s) {
76
+ rb_raise(eDTraceException, "out of memory: failed to allocate string value");
77
+ return (Qnil);
78
+ }
79
+ (void) strncpy(s, c, nbytes);
80
+ s[nbytes] = '\0';
81
+ robj = rb_str_new2(s);
82
+ free(s);
83
+ return (robj);
84
+ }
85
+
86
+ /* return byte array */
87
+ robj = rb_ary_new();
88
+ for (i = 0; i < nbytes; i++)
89
+ rb_ary_push(robj, INT2FIX(addr[i]));
90
+
91
+ return (robj);
92
+ }
Binary file
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+ require 'rbconfig'
3
+
4
+ # Need to specify full path to dtrace.h or we'll pick up ruby's
5
+ # dtrace.h on Solaris or other builds with the runtime probes included.
6
+ have_library("dtrace", "dtrace_open", "/usr/include/dtrace.h")
7
+ create_makefile("dtrace_api")
@@ -0,0 +1,95 @@
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
+
74
+ # Yields each probe on the system, optionally matching against a
75
+ # probe specification:
76
+ #
77
+ # e.g.
78
+ # syscall::: -> all probes in the syscall provider
79
+ # pid123:::return -> all return probes in pid 123.
80
+ #
81
+ def each_probe(match=nil, &block)
82
+ if match
83
+ parts = match.split(':', 4)
84
+ begin
85
+ each_probe_match(*parts, &block)
86
+ rescue ArgumentError => e
87
+ raise DTrace::Exception.new("each_probe: probe specification expected (e.g. 'provider:::')")
88
+ end
89
+ else
90
+ each_probe_all(&block)
91
+ end
92
+ end
93
+
94
+ end
95
+
@@ -0,0 +1,40 @@
1
+ #
2
+ # Ruby-DTrace
3
+ # (c) 2007 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ # Represents an aggregation record built from a series of
7
+ # DTraceAggData records.
8
+ #
9
+ # Intended to to built up by calling +add_record+ repeatedly with
10
+ # DTrace::AggData objects until a completed DTrace::Aggregate is
11
+ # returned. (until a complete record is available, +add_record+
12
+ # returns nil).
13
+ #
14
+ # See consumer.rb for an example of this.
15
+ class DTrace
16
+ class Aggregate
17
+ attr_reader :value, :tuple
18
+
19
+ # Create an empty DTrace::Aggregate: use +add_record+ to add data.
20
+ def initialize
21
+ @tuple = Array.new
22
+ end
23
+
24
+ # Add a DTrace::AggData record to this aggregate. Returns nil until it
25
+ # receives a record of aggtype "last", when it returns the complete
26
+ # DTrace::Aggregate.
27
+ def add_record(r)
28
+ case r.aggtype
29
+ when "tuple"
30
+ @tuple << r.value
31
+ when "value"
32
+ @value = r.value
33
+ when "last"
34
+ return self
35
+ end
36
+ nil
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,19 @@
1
+ #
2
+ # Ruby-DTrace
3
+ # (c) 2007 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ class DTrace
7
+ class AggregateSet
8
+ attr_reader :data
9
+
10
+ def initialize
11
+ @data = Array.new
12
+ end
13
+
14
+ def add_aggregate(agg)
15
+ @data << agg
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,174 @@
1
+ #
2
+ # Ruby-DTrace
3
+ # (c) 2007 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ # A DTrace::Consumer provides access to the data produced by the running
7
+ # D program. Having compiled and executed a D program, you typically
8
+ # create a DTrace::Consumer, and wait for data.
9
+ #
10
+ # You can either wait indefinitely for data, or consume all the data
11
+ # waiting and then stop: if your D program consists of only of
12
+ # aggregations, returned by printa() actions in the END block, this
13
+ # will be the best approach. If you have a mix of trace() and printf()
14
+ # actions elsewhere, you'll probably want to wait until interrupted,
15
+ # the D program itself exits, or your program decides it has collected
16
+ # enough data.
17
+ #
18
+ # The two approaches are implemented by the +consume+ and
19
+ # +consume_once+ methods.
20
+ #
21
+ # The +consume+ and +consume_once+ methods accept a block to which is
22
+ # yielded complete DTrace::Data objects, one for each probe which fires.
23
+ #
24
+ # You must have already started tracing when you call +consume+ or
25
+ # +consume_once+, so the general structure will look like:
26
+ #
27
+ # t = DTrace.new
28
+ # progtext = "..."
29
+ # prog = t.compile progtext
30
+ # prog.execute
31
+ # t.go
32
+ # c = DTrace::Consumer.new(t)
33
+ # c.consume_once do |d|
34
+ # # handle DTrace::Data objects
35
+ # # ...
36
+ # # get bored:
37
+ # c.finish
38
+ # end
39
+
40
+ class DTrace
41
+ class Consumer
42
+
43
+ def initialize(t)
44
+ @t = t
45
+ @done = false
46
+ @types = []
47
+
48
+ @drophandler = nil
49
+ @errhandler = nil
50
+ end
51
+
52
+ private
53
+
54
+ # The consumer callbacks:
55
+ #
56
+ # DTraceRecDesc -> rec_consumer
57
+ # DTraceProbeData -> probe_consumer
58
+ # DTraceBufData -> buf_consumer
59
+ #
60
+ # We expect a sequence of calls to these procs, and we accumulate
61
+ # data in the @curr DTrace::Data based on this:
62
+ #
63
+ # DTrace::ProbeData (initial callback for a probe firing)
64
+ # DTrace::RecDesc
65
+ # ...
66
+ # DTrace::RecDesc = nil (end of data)
67
+ #
68
+
69
+ def rec_consumer(block)
70
+ proc do |rec|
71
+ if rec
72
+ @curr.add_recdata(rec)
73
+ else
74
+ @curr.finish
75
+ block.call(@curr)
76
+ @curr = DTrace::Data.new(@types)
77
+ end
78
+ end
79
+ end
80
+
81
+ def probe_consumer
82
+ proc do |probe|
83
+ @curr.add_probedata(probe)
84
+ end
85
+ end
86
+
87
+ def buf_consumer
88
+ proc do |buf|
89
+ @curr.add_bufdata(buf)
90
+ end
91
+ end
92
+
93
+ def filter_types(types)
94
+ @types = types
95
+ @curr = DTrace::Data.new(types)
96
+ end
97
+
98
+ public
99
+
100
+ # Provide a proc which will be executed when a drop record is
101
+ # received.
102
+ def drophandler(&block)
103
+ @drophandler = block
104
+ @t.drop_consumer(proc do |drop|
105
+ if @drophandler
106
+ @drophandler.call(drop)
107
+ end
108
+ end)
109
+ end
110
+
111
+ # Provide a proc which will be executed when an error record is
112
+ # received.
113
+ def errhandler(&block)
114
+ @errhandler = block
115
+ @t.err_consumer(proc do |err|
116
+ if @errhandler
117
+ @errhandler.call(err)
118
+ end
119
+ end)
120
+ end
121
+
122
+ # Signals that the client wishes to stop consuming trace data.
123
+ def finish
124
+ @t.stop
125
+ @done = true
126
+ end
127
+
128
+ # Waits for data from the D program, and yields the records returned
129
+ # to the block given. Returns when the D program exits.
130
+ #
131
+ # Pass a list of classes to restrict the types of data returned,
132
+ # from:
133
+ #
134
+ # * DTraceRecord
135
+ # * DTracePrintfRecord
136
+ # * DTraceAggregateSet
137
+ # * DTraceStackRecord
138
+ #
139
+ def consume(*types, &block)
140
+ filter_types(types)
141
+ @t.buf_consumer(buf_consumer)
142
+ begin
143
+ while(true) do
144
+ @t.sleep
145
+ work = @t.work(probe_consumer, rec_consumer(block))
146
+ if (@done || work > 0)
147
+ break
148
+ end
149
+ end
150
+ ensure
151
+ @t.stop
152
+ @t.work(probe_consumer)
153
+ end
154
+ end
155
+
156
+ # Yields the data waiting from the current program, then returns.
157
+ #
158
+ # Pass a list of classes to restrict the types of data returned,
159
+ # from:
160
+ #
161
+ # * DTraceRecord
162
+ # * DTracePrintfRecord
163
+ # * DTraceAggregateSet
164
+ # * DTraceStackRecord
165
+ #
166
+ def consume_once(*types, &block)
167
+ filter_types(types)
168
+ @t.buf_consumer(buf_consumer)
169
+ @t.stop
170
+ @t.work(probe_consumer, rec_consumer(block))
171
+ end
172
+
173
+ end
174
+ end