ruby-dtrace 0.0.4 → 0.0.5
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 +7 -0
- data/Manifest.txt +4 -0
- data/ext/dtrace_api.c +21 -0
- data/ext/dtrace_api.h +29 -1
- data/ext/dtrace_dropdata.c +131 -0
- data/ext/dtrace_errdata.c +110 -0
- data/ext/dtrace_hdl.c +212 -67
- data/ext/dtrace_probedata.c +6 -6
- data/ext/dtrace_program.c +10 -5
- data/lib/dtrace.rb +1 -1
- data/lib/dtraceconsumer.rb +54 -4
- data/lib/dtracedata.rb +14 -7
- data/test/test_dtrace_drops_errors.rb +183 -0
- data/test/test_dtrace_typefilter.rb +108 -0
- metadata +8 -2
data/ext/dtrace_probedata.c
CHANGED
@@ -202,17 +202,17 @@ VALUE dtraceprobedata_each_record(VALUE self)
|
|
202
202
|
dtrace_probedata_t *data;
|
203
203
|
dtrace_eprobedesc_t *eprobe;
|
204
204
|
dtrace_recdesc_t *rec;
|
205
|
-
|
205
|
+
dtrace_handle_t *handle;
|
206
206
|
dtrace_actkind_t act;
|
207
207
|
int i;
|
208
208
|
caddr_t addr;
|
209
209
|
VALUE dtracerecord;
|
210
|
-
VALUE
|
210
|
+
VALUE dtrace;
|
211
211
|
VALUE v;
|
212
212
|
|
213
213
|
Data_Get_Struct(self, dtrace_probedata_t, data);
|
214
|
-
|
215
|
-
Data_Get_Struct(
|
214
|
+
dtrace = rb_iv_get(self, "@handle");
|
215
|
+
Data_Get_Struct(dtrace, dtrace_handle_t, handle);
|
216
216
|
|
217
217
|
eprobe = data->dtpda_edesc;
|
218
218
|
|
@@ -228,8 +228,8 @@ VALUE dtraceprobedata_each_record(VALUE self)
|
|
228
228
|
case DTRACEACT_USTACK:
|
229
229
|
case DTRACEACT_JSTACK:
|
230
230
|
/* Stack records come from bufdata */
|
231
|
-
/* v = _handle_stack_record(handle, addr, rec); */
|
232
|
-
/* v = _handle_ustack_record(handle, addr, rec); */
|
231
|
+
/* v = _handle_stack_record(handle->hdl, addr, rec); */
|
232
|
+
/* v = _handle_ustack_record(handle->hdl, addr, rec); */
|
233
233
|
break;
|
234
234
|
case DTRACEACT_PRINTA:
|
235
235
|
/* don't want the probedata record for a printa() action */
|
data/ext/dtrace_program.c
CHANGED
@@ -24,17 +24,22 @@ VALUE dtraceprogram_exec(VALUE self)
|
|
24
24
|
{
|
25
25
|
dtrace_prog_t *prog;
|
26
26
|
dtrace_proginfo_t *proginfo;
|
27
|
-
|
27
|
+
dtrace_handle_t *handle;
|
28
28
|
VALUE dtrace;
|
29
29
|
VALUE dtraceprograminfo;
|
30
30
|
int ret;
|
31
31
|
|
32
32
|
Data_Get_Struct(self, dtrace_prog_t, prog);
|
33
|
-
dtrace = rb_iv_get(self, "@
|
34
|
-
Data_Get_Struct(dtrace,
|
33
|
+
dtrace = rb_iv_get(self, "@handle");
|
34
|
+
Data_Get_Struct(dtrace, dtrace_handle_t, handle);
|
35
35
|
|
36
36
|
proginfo = ALLOC(dtrace_proginfo_t);
|
37
|
-
|
37
|
+
if (!proginfo) {
|
38
|
+
rb_raise(eDtraceException, "alloc failed");
|
39
|
+
return Qnil;
|
40
|
+
}
|
41
|
+
|
42
|
+
ret = dtrace_program_exec(handle->hdl, prog, proginfo);
|
38
43
|
|
39
44
|
if (ret == 0) {
|
40
45
|
dtraceprograminfo = Data_Wrap_Struct(cDtraceProgramInfo, 0, NULL, proginfo);
|
@@ -42,7 +47,7 @@ VALUE dtraceprogram_exec(VALUE self)
|
|
42
47
|
}
|
43
48
|
|
44
49
|
if (ret < 0)
|
45
|
-
rb_raise(eDtraceException, dtrace_errmsg(handle, dtrace_errno(handle)));
|
50
|
+
rb_raise(eDtraceException, dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
|
46
51
|
|
47
52
|
return Qnil;
|
48
53
|
}
|
data/lib/dtrace.rb
CHANGED
data/lib/dtraceconsumer.rb
CHANGED
@@ -41,8 +41,11 @@ class DtraceConsumer
|
|
41
41
|
|
42
42
|
def initialize(t)
|
43
43
|
@t = t
|
44
|
-
@curr = DtraceData.new
|
45
44
|
@done = false
|
45
|
+
@types = []
|
46
|
+
|
47
|
+
@drophandler = nil
|
48
|
+
@errhandler = nil
|
46
49
|
end
|
47
50
|
|
48
51
|
private
|
@@ -69,7 +72,7 @@ class DtraceConsumer
|
|
69
72
|
else
|
70
73
|
@curr.finish
|
71
74
|
block.call(@curr)
|
72
|
-
@curr = DtraceData.new
|
75
|
+
@curr = DtraceData.new(@types)
|
73
76
|
end
|
74
77
|
end
|
75
78
|
end
|
@@ -85,9 +88,36 @@ class DtraceConsumer
|
|
85
88
|
@curr.add_bufdata(buf)
|
86
89
|
end
|
87
90
|
end
|
91
|
+
|
92
|
+
def filter_types(types)
|
93
|
+
@types = types
|
94
|
+
@curr = DtraceData.new(types)
|
95
|
+
end
|
88
96
|
|
89
97
|
public
|
90
98
|
|
99
|
+
# Provide a proc which will be executed when a drop record is
|
100
|
+
# received.
|
101
|
+
def drophandler(&block)
|
102
|
+
@drophandler = block
|
103
|
+
@t.drop_consumer(proc do |drop|
|
104
|
+
if @drophandler
|
105
|
+
@drophandler.call(drop)
|
106
|
+
end
|
107
|
+
end)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Provide a proc which will be executed when an error record is
|
111
|
+
# received.
|
112
|
+
def errhandler(&block)
|
113
|
+
@errhandler = block
|
114
|
+
@t.err_consumer(proc do |err|
|
115
|
+
if @errhandler
|
116
|
+
@errhandler.call(err)
|
117
|
+
end
|
118
|
+
end)
|
119
|
+
end
|
120
|
+
|
91
121
|
# Signals that the client wishes to stop consuming trace data.
|
92
122
|
def finish
|
93
123
|
@t.stop
|
@@ -96,7 +126,17 @@ class DtraceConsumer
|
|
96
126
|
|
97
127
|
# Waits for data from the D program, and yields the records returned
|
98
128
|
# to the block given. Returns when the D program exits.
|
99
|
-
|
129
|
+
#
|
130
|
+
# Pass a list of classes to restrict the types of data returned,
|
131
|
+
# from:
|
132
|
+
#
|
133
|
+
# * DtraceRecord
|
134
|
+
# * DtracePrintfRecord
|
135
|
+
# * DtraceAggregateSet
|
136
|
+
# * DtraceStackRecord
|
137
|
+
#
|
138
|
+
def consume(*types, &block)
|
139
|
+
filter_types(types)
|
100
140
|
@t.buf_consumer(buf_consumer)
|
101
141
|
begin
|
102
142
|
while(true) do
|
@@ -113,7 +153,17 @@ class DtraceConsumer
|
|
113
153
|
end
|
114
154
|
|
115
155
|
# Yields the data waiting from the current program, then returns.
|
116
|
-
|
156
|
+
#
|
157
|
+
# Pass a list of classes to restrict the types of data returned,
|
158
|
+
# from:
|
159
|
+
#
|
160
|
+
# * DtraceRecord
|
161
|
+
# * DtracePrintfRecord
|
162
|
+
# * DtraceAggregateSet
|
163
|
+
# * DtraceStackRecord
|
164
|
+
#
|
165
|
+
def consume_once(*types, &block)
|
166
|
+
filter_types(types)
|
117
167
|
@t.buf_consumer(buf_consumer)
|
118
168
|
@t.stop
|
119
169
|
@t.work(probe_consumer, rec_consumer(block))
|
data/lib/dtracedata.rb
CHANGED
@@ -9,22 +9,29 @@ class DtraceData
|
|
9
9
|
attr_reader :probe
|
10
10
|
attr_reader :cpu, :indent, :prefix, :flow
|
11
11
|
|
12
|
-
def initialize
|
12
|
+
def initialize(types)
|
13
|
+
@types = types
|
13
14
|
@data = []
|
14
15
|
@curraggset = nil
|
15
16
|
@curragg = nil
|
16
17
|
end
|
17
18
|
|
19
|
+
def add_data(d)
|
20
|
+
if @types.length == 0 || @types.include?(d.class)
|
21
|
+
@data << d
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
18
25
|
def finish
|
19
26
|
if @curraggset
|
20
|
-
@
|
27
|
+
add_data(@curraggset)
|
21
28
|
@curraggset = nil
|
22
29
|
end
|
23
30
|
end
|
24
31
|
|
25
32
|
def add_recdata(rec)
|
26
33
|
if @curraggset
|
27
|
-
@
|
34
|
+
add_data(@curraggset)
|
28
35
|
@curraggset = nil
|
29
36
|
end
|
30
37
|
if rec.action == "printa"
|
@@ -34,7 +41,7 @@ class DtraceData
|
|
34
41
|
|
35
42
|
def add_probedata(probedata)
|
36
43
|
probedata.each_record do |p|
|
37
|
-
|
44
|
+
add_data(p)
|
38
45
|
end
|
39
46
|
|
40
47
|
# Record the probe that fired, and CPU/indent/prefix/flow
|
@@ -51,11 +58,11 @@ class DtraceData
|
|
51
58
|
if r
|
52
59
|
case r.class.to_s
|
53
60
|
when DtraceStackRecord.to_s
|
54
|
-
|
61
|
+
add_data(r)
|
55
62
|
when DtraceRecord.to_s
|
56
|
-
|
63
|
+
add_data(r)
|
57
64
|
when DtracePrintfRecord.to_s
|
58
|
-
|
65
|
+
add_data(r)
|
59
66
|
when DtraceAggData.to_s
|
60
67
|
if @curragg == nil
|
61
68
|
@curragg = DtraceAggregate.new
|
@@ -0,0 +1,183 @@
|
|
1
|
+
#
|
2
|
+
# Ruby-Dtrace
|
3
|
+
# (c) 2007 Chris Andrews <chris@nodnol.org>
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'dtrace'
|
7
|
+
require 'test/unit'
|
8
|
+
|
9
|
+
# Tests for the DropData record.
|
10
|
+
|
11
|
+
class TestDtraceDropsErrors < Test::Unit::TestCase
|
12
|
+
|
13
|
+
def test_drops
|
14
|
+
t = Dtrace.new
|
15
|
+
t.setopt("bufsize", "512")
|
16
|
+
t.setopt("strsize", "1024")
|
17
|
+
|
18
|
+
# drp.DTRACEDROP_PRINCIPAL.d
|
19
|
+
progtext = <<EOD
|
20
|
+
BEGIN
|
21
|
+
{
|
22
|
+
trace("Harding");
|
23
|
+
trace("Hoover");
|
24
|
+
trace("Nixon");
|
25
|
+
trace("Bush");
|
26
|
+
}
|
27
|
+
|
28
|
+
BEGIN
|
29
|
+
{
|
30
|
+
exit(0);
|
31
|
+
}
|
32
|
+
EOD
|
33
|
+
|
34
|
+
prog = t.compile progtext
|
35
|
+
prog.execute
|
36
|
+
|
37
|
+
c = DtraceConsumer.new(t)
|
38
|
+
assert c
|
39
|
+
|
40
|
+
i = 0
|
41
|
+
c.drophandler do |d|
|
42
|
+
assert_match(/1 drop on CPU [0-9]+/, d.msg)
|
43
|
+
assert_equal "drop to principal buffer", d.kind
|
44
|
+
assert_not_nil d.cpu
|
45
|
+
assert_equal 1, d.drops
|
46
|
+
assert_not_nil d.total
|
47
|
+
i = 1
|
48
|
+
end
|
49
|
+
|
50
|
+
t.go
|
51
|
+
c.consume do |d|
|
52
|
+
end
|
53
|
+
|
54
|
+
assert_equal 1, i
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_error_handler_too_late
|
58
|
+
t = Dtrace.new
|
59
|
+
t.setopt("bufsize", "512")
|
60
|
+
t.setopt("strsize", "1024")
|
61
|
+
|
62
|
+
progtext = <<EOD
|
63
|
+
BEGIN
|
64
|
+
{
|
65
|
+
*(char *)NULL;
|
66
|
+
}
|
67
|
+
EOD
|
68
|
+
|
69
|
+
prog = t.compile progtext
|
70
|
+
prog.execute
|
71
|
+
t.go
|
72
|
+
|
73
|
+
c = DtraceConsumer.new(t)
|
74
|
+
assert c
|
75
|
+
|
76
|
+
# since we've already said "go", we now can't apply an error
|
77
|
+
# handler (DTrace will let us, but won't call it).
|
78
|
+
assert_raise(DtraceException) do
|
79
|
+
c.errhandler do |d|
|
80
|
+
# nothing
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_errors
|
87
|
+
t = Dtrace.new
|
88
|
+
t.setopt("bufsize", "512")
|
89
|
+
t.setopt("strsize", "1024")
|
90
|
+
|
91
|
+
progtext = <<EOD
|
92
|
+
BEGIN
|
93
|
+
{
|
94
|
+
*(char *)NULL;
|
95
|
+
}
|
96
|
+
EOD
|
97
|
+
|
98
|
+
prog = t.compile progtext
|
99
|
+
prog.execute
|
100
|
+
|
101
|
+
c = DtraceConsumer.new(t)
|
102
|
+
assert c
|
103
|
+
|
104
|
+
i = 0
|
105
|
+
c.errhandler do |d|
|
106
|
+
assert_match(/error on enabled probe ID [0-9]+ \(ID [0-9]+: dtrace:::BEGIN\): invalid address \(0x0\) in action #1 at DIF offset 16/, d.msg)
|
107
|
+
assert_not_nil d.cpu
|
108
|
+
assert d.action
|
109
|
+
assert d.offset
|
110
|
+
assert d.fault
|
111
|
+
assert_not_nil d.addr
|
112
|
+
i = 1
|
113
|
+
end
|
114
|
+
|
115
|
+
t.go
|
116
|
+
c.consume_once do |d|
|
117
|
+
end
|
118
|
+
|
119
|
+
assert_equal 1, i
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_error_and_drop_handler
|
123
|
+
|
124
|
+
t = Dtrace.new
|
125
|
+
t.setopt("bufsize", "512")
|
126
|
+
t.setopt("strsize", "1024")
|
127
|
+
|
128
|
+
progtext = <<EOD
|
129
|
+
BEGIN
|
130
|
+
{
|
131
|
+
trace("Harding");
|
132
|
+
trace("Hoover");
|
133
|
+
trace("Nixon");
|
134
|
+
trace("Bush");
|
135
|
+
}
|
136
|
+
|
137
|
+
BEGIN
|
138
|
+
{
|
139
|
+
*(char *)NULL;
|
140
|
+
}
|
141
|
+
|
142
|
+
ERROR
|
143
|
+
{
|
144
|
+
exit(0);
|
145
|
+
}
|
146
|
+
EOD
|
147
|
+
|
148
|
+
prog = t.compile progtext
|
149
|
+
prog.execute
|
150
|
+
|
151
|
+
c = DtraceConsumer.new(t)
|
152
|
+
assert c
|
153
|
+
|
154
|
+
errors = 0
|
155
|
+
c.errhandler do |d|
|
156
|
+
assert_match(/error on enabled probe ID [0-9]+ \(ID [0-9]+: dtrace:::BEGIN\): invalid address \(0x0\) in action #1 at DIF offset 16/, d.msg)
|
157
|
+
assert_not_nil d.cpu
|
158
|
+
assert d.action
|
159
|
+
assert d.offset
|
160
|
+
assert d.fault
|
161
|
+
assert_not_nil d.addr
|
162
|
+
errors = 1
|
163
|
+
end
|
164
|
+
|
165
|
+
drops = 0
|
166
|
+
c.drophandler do |d|
|
167
|
+
assert_match(/1 drop on CPU [0-9]+/, d.msg)
|
168
|
+
assert_equal "drop to principal buffer", d.kind
|
169
|
+
assert_not_nil d.cpu
|
170
|
+
assert_equal 1, d.drops
|
171
|
+
assert_not_nil d.total
|
172
|
+
drops = 1
|
173
|
+
end
|
174
|
+
|
175
|
+
t.go
|
176
|
+
c.consume do |d|
|
177
|
+
end
|
178
|
+
|
179
|
+
assert_equal 1, errors
|
180
|
+
assert_equal 1, drops
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
#
|
2
|
+
# Ruby-Dtrace
|
3
|
+
# (c) 2007 Chris Andrews <chris@nodnol.org>
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'dtrace'
|
7
|
+
require 'test/unit'
|
8
|
+
|
9
|
+
# Tests for the feature allowing you to filter DtraceData types
|
10
|
+
|
11
|
+
class TestDtraceTypefilter < Test::Unit::TestCase
|
12
|
+
def test_filter
|
13
|
+
t = Dtrace.new
|
14
|
+
t.setopt("bufsize", "4m")
|
15
|
+
t.setopt("aggsize", "4m")
|
16
|
+
|
17
|
+
progtext =<<EOD
|
18
|
+
profile-1000
|
19
|
+
{
|
20
|
+
@a[execname] = count();
|
21
|
+
@b[execname] = count();
|
22
|
+
}
|
23
|
+
|
24
|
+
profile-10
|
25
|
+
{
|
26
|
+
trace("foo");
|
27
|
+
printa(@a);
|
28
|
+
printf("bar");
|
29
|
+
printa(@b);
|
30
|
+
}
|
31
|
+
EOD
|
32
|
+
|
33
|
+
prog = t.compile progtext
|
34
|
+
prog.execute
|
35
|
+
t.go
|
36
|
+
|
37
|
+
sleep 1
|
38
|
+
|
39
|
+
c = DtraceConsumer.new(t)
|
40
|
+
assert c
|
41
|
+
|
42
|
+
data = []
|
43
|
+
c.consume_once(DtraceAggregateSet) do |d|
|
44
|
+
data << d
|
45
|
+
end
|
46
|
+
|
47
|
+
assert data.length > 0
|
48
|
+
data.each do |d|
|
49
|
+
assert d
|
50
|
+
assert_equal DtraceData, d.class
|
51
|
+
d.data.each do |agg|
|
52
|
+
assert_equal DtraceAggregateSet, agg.class
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_filter_two_classes
|
59
|
+
t = Dtrace.new
|
60
|
+
t.setopt("bufsize", "4m")
|
61
|
+
t.setopt("aggsize", "4m")
|
62
|
+
|
63
|
+
progtext =<<EOD
|
64
|
+
profile-1000
|
65
|
+
{
|
66
|
+
@a[execname] = count();
|
67
|
+
@b[execname] = count();
|
68
|
+
}
|
69
|
+
|
70
|
+
profile-10
|
71
|
+
{
|
72
|
+
trace("foo");
|
73
|
+
printa(@a);
|
74
|
+
printf("bar");
|
75
|
+
printa(@b);
|
76
|
+
}
|
77
|
+
EOD
|
78
|
+
|
79
|
+
prog = t.compile progtext
|
80
|
+
prog.execute
|
81
|
+
t.go
|
82
|
+
|
83
|
+
sleep 1
|
84
|
+
|
85
|
+
c = DtraceConsumer.new(t)
|
86
|
+
assert c
|
87
|
+
|
88
|
+
data = []
|
89
|
+
c.consume_once(DtraceAggregateSet, DtracePrintfRecord) do |d|
|
90
|
+
data << d
|
91
|
+
end
|
92
|
+
|
93
|
+
assert data.length > 0
|
94
|
+
data.each do |d|
|
95
|
+
assert d
|
96
|
+
assert_equal DtraceData, d.class
|
97
|
+
d.data.each do |r|
|
98
|
+
if r.respond_to?(:add_aggregate)
|
99
|
+
assert_equal DtraceAggregateSet, r.class
|
100
|
+
else
|
101
|
+
assert_equal DtracePrintfRecord, r.class
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|