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