ruby-dtrace-consumer 0.4.0

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