ruby-dtrace 0.0.1 → 0.0.2
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 +10 -1
- data/Rakefile +3 -1
- data/ext/dtrace_aggdata.c +139 -0
- data/ext/dtrace_api.c +17 -9
- data/ext/dtrace_api.h +15 -7
- data/ext/dtrace_bufdata.c +91 -0
- data/ext/dtrace_hdl.c +101 -0
- data/ext/dtrace_probedata.c +129 -0
- data/ext/dtrace_recdesc.c +0 -23
- data/ext/dtrace_util.c +91 -0
- data/ext/extconf.rb +1 -1
- data/lib/dtrace.rb +6 -1
- data/lib/dtraceaggregate.rb +13 -0
- data/lib/dtraceconsumer.rb +99 -0
- data/lib/dtraceprobe.rb +13 -0
- data/lib/dtraceprobedata.rb +16 -0
- data/lib/dtracerecord.rb +8 -0
- data/plugin/dtrace/lib/dtracer.rb +6 -5
- data/test/test_dtrace.rb +0 -306
- data/test/test_dtrace_workapi.rb +142 -0
- metadata +13 -3
- data/ext/dtrace_aggregate.c +0 -150
@@ -0,0 +1,129 @@
|
|
1
|
+
/* Ruby-Dtrace
|
2
|
+
* (c) 2007 Chris Andrews <chris@nodnol.org>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "dtrace_api.h"
|
6
|
+
|
7
|
+
RUBY_EXTERN VALUE eDtraceException;
|
8
|
+
RUBY_EXTERN VALUE cDtraceProbe;
|
9
|
+
|
10
|
+
/* :nodoc: */
|
11
|
+
VALUE dtraceprobedata_init(VALUE self)
|
12
|
+
{
|
13
|
+
dtrace_probedata_t *data;
|
14
|
+
|
15
|
+
Data_Get_Struct(self, dtrace_probedata_t, data);
|
16
|
+
return self;
|
17
|
+
}
|
18
|
+
|
19
|
+
VALUE dtraceprobedata_probedesc(VALUE self)
|
20
|
+
{
|
21
|
+
dtrace_probedata_t *data;
|
22
|
+
dtrace_probedesc_t *pdp;
|
23
|
+
VALUE probe;
|
24
|
+
|
25
|
+
Data_Get_Struct(self, dtrace_probedata_t, data);
|
26
|
+
|
27
|
+
pdp = data->dtpda_pdesc;
|
28
|
+
probe = Data_Wrap_Struct(cDtraceProbe, 0, NULL, (dtrace_probedesc_t *)pdp);
|
29
|
+
|
30
|
+
return probe;
|
31
|
+
}
|
32
|
+
|
33
|
+
static VALUE _handle_stack_record(const caddr_t addr, const dtrace_recdesc_t *rec)
|
34
|
+
{
|
35
|
+
dtrace_actkind_t act;
|
36
|
+
uint64_t *pc;
|
37
|
+
pid_t pid = -1;
|
38
|
+
int size; /* size of raw bytes not including trailing zeros */
|
39
|
+
int i; /* index of last non-zero byte */
|
40
|
+
VALUE raw;
|
41
|
+
|
42
|
+
for (i = rec->dtrd_size - 1; (i >= 0) && !addr[i]; --i) {
|
43
|
+
}
|
44
|
+
size = (i + 1);
|
45
|
+
|
46
|
+
raw = rb_ary_new();
|
47
|
+
for (i = 0; i < size; i++)
|
48
|
+
rb_ary_push(raw, INT2FIX(addr[i]));
|
49
|
+
|
50
|
+
act = rec->dtrd_action;
|
51
|
+
switch (act) {
|
52
|
+
case DTRACEACT_STACK:
|
53
|
+
break;
|
54
|
+
case DTRACEACT_USTACK:
|
55
|
+
case DTRACEACT_JSTACK:
|
56
|
+
/* Get pid of user process */
|
57
|
+
pc = (uint64_t *)(uintptr_t)addr;
|
58
|
+
pid = (pid_t)*pc;
|
59
|
+
break;
|
60
|
+
default:
|
61
|
+
rb_raise(eDtraceException, "Expected stack action, got %d\n", act);
|
62
|
+
}
|
63
|
+
|
64
|
+
return raw;
|
65
|
+
}
|
66
|
+
|
67
|
+
static int
|
68
|
+
_is_stack_action(dtrace_actkind_t act)
|
69
|
+
{
|
70
|
+
int stack_action;
|
71
|
+
switch (act) {
|
72
|
+
case DTRACEACT_STACK:
|
73
|
+
case DTRACEACT_USTACK:
|
74
|
+
case DTRACEACT_JSTACK:
|
75
|
+
stack_action = 1;
|
76
|
+
break;
|
77
|
+
default:
|
78
|
+
stack_action = 0;
|
79
|
+
}
|
80
|
+
return (stack_action);
|
81
|
+
}
|
82
|
+
|
83
|
+
VALUE dtraceprobedata_each_record(VALUE self)
|
84
|
+
{
|
85
|
+
dtrace_probedata_t *data;
|
86
|
+
dtrace_eprobedesc_t *eprobe;
|
87
|
+
dtrace_recdesc_t *rec;
|
88
|
+
int i;
|
89
|
+
VALUE dtracerecord;
|
90
|
+
VALUE v;
|
91
|
+
caddr_t addr;
|
92
|
+
|
93
|
+
Data_Get_Struct(self, dtrace_probedata_t, data);
|
94
|
+
eprobe = data->dtpda_edesc;
|
95
|
+
|
96
|
+
for (i = 0; i < eprobe->dtepd_nrecs; i++) {
|
97
|
+
rec = &eprobe->dtepd_rec[i];
|
98
|
+
if (rec->dtrd_size > 0) {
|
99
|
+
addr = data->dtpda_data + rec->dtrd_offset;
|
100
|
+
|
101
|
+
if (_is_stack_action(rec->dtrd_action)) {
|
102
|
+
v = _handle_stack_record(addr, rec);
|
103
|
+
}
|
104
|
+
else {
|
105
|
+
switch (rec->dtrd_size) {
|
106
|
+
case 1:
|
107
|
+
v = INT2FIX((int)(*((uint8_t *)addr)));
|
108
|
+
break;
|
109
|
+
case 2:
|
110
|
+
v = INT2FIX((int)(*((uint16_t *)addr)));
|
111
|
+
break;
|
112
|
+
case 4:
|
113
|
+
v = INT2FIX(*((int32_t *)addr));
|
114
|
+
break;
|
115
|
+
case 8:
|
116
|
+
v = INT2FIX(*((int64_t *)addr));
|
117
|
+
break;
|
118
|
+
default:
|
119
|
+
v = handle_bytedata(addr, rec->dtrd_size);
|
120
|
+
break;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
dtracerecord = rb_class_new_instance(0, NULL, rb_path2class("DtraceRecord"));
|
125
|
+
rb_iv_set(dtracerecord, "@value", v);
|
126
|
+
rb_yield(dtracerecord);
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
data/ext/dtrace_recdesc.c
CHANGED
@@ -13,26 +13,3 @@ VALUE dtracerecdesc_init(VALUE self)
|
|
13
13
|
return self;
|
14
14
|
}
|
15
15
|
|
16
|
-
/*
|
17
|
-
* Return the data for this record.
|
18
|
-
*/
|
19
|
-
VALUE dtracerecdesc_data(VALUE self)
|
20
|
-
{
|
21
|
-
VALUE dtraceaggdata;
|
22
|
-
dtrace_recdesc_t *recdesc;
|
23
|
-
dtrace_aggdata_t *aggdata;
|
24
|
-
|
25
|
-
Data_Get_Struct(self, dtrace_recdesc_t, recdesc);
|
26
|
-
|
27
|
-
dtraceaggdata = rb_iv_get(self, "@aggdata");
|
28
|
-
Data_Get_Struct(dtraceaggdata, dtrace_aggdata_t, aggdata);
|
29
|
-
|
30
|
-
if (recdesc->dtrd_size == 256) {
|
31
|
-
char *c = aggdata->dtada_data + recdesc->dtrd_offset;
|
32
|
-
return rb_str_new2(c);
|
33
|
-
}
|
34
|
-
else {
|
35
|
-
uint64_t n = *((uint64_t *)(aggdata->dtada_data + recdesc->dtrd_offset));
|
36
|
-
return INT2FIX(n);
|
37
|
-
}
|
38
|
-
}
|
data/ext/dtrace_util.c
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
/* Ruby-Dtrace
|
2
|
+
* (c) 2007 Chris Andrews <chris@nodnol.org>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "dtrace_api.h"
|
6
|
+
|
7
|
+
RUBY_EXTERN VALUE eDtraceException;
|
8
|
+
|
9
|
+
/*
|
10
|
+
* Most of this function lifted from libdtrace/common/dt_consume.c
|
11
|
+
* dt_print_bytes().
|
12
|
+
*/
|
13
|
+
VALUE handle_bytedata(caddr_t addr, uint32_t nbytes)
|
14
|
+
{
|
15
|
+
/*
|
16
|
+
* If the byte stream is a series of printable characters, followed by
|
17
|
+
* a terminating byte, we print it out as a string. Otherwise, we
|
18
|
+
* assume that it's something else and just print the bytes.
|
19
|
+
*/
|
20
|
+
int i, j;
|
21
|
+
char *c = addr;
|
22
|
+
|
23
|
+
VALUE robj;
|
24
|
+
|
25
|
+
if (nbytes == 0) {
|
26
|
+
return rb_str_new2("");
|
27
|
+
}
|
28
|
+
|
29
|
+
for (i = 0; i < nbytes; i++) {
|
30
|
+
/*
|
31
|
+
* We define a "printable character" to be one for which
|
32
|
+
* isprint(3C) returns non-zero, isspace(3C) returns non-zero,
|
33
|
+
* or a character which is either backspace or the bell.
|
34
|
+
* Backspace and the bell are regrettably special because
|
35
|
+
* they fail the first two tests -- and yet they are entirely
|
36
|
+
* printable. These are the only two control characters that
|
37
|
+
* have meaning for the terminal and for which isprint(3C) and
|
38
|
+
* isspace(3C) return 0.
|
39
|
+
*/
|
40
|
+
if (isprint(c[i]) || isspace(c[i]) ||
|
41
|
+
c[i] == '\b' || c[i] == '\a')
|
42
|
+
continue;
|
43
|
+
|
44
|
+
if (c[i] == '\0' && i > 0) {
|
45
|
+
/*
|
46
|
+
* This looks like it might be a string. Before we
|
47
|
+
* assume that it is indeed a string, check the
|
48
|
+
* remainder of the byte range; if it contains
|
49
|
+
* additional non-nul characters, we'll assume that
|
50
|
+
* it's a binary stream that just happens to look like
|
51
|
+
* a string.
|
52
|
+
*/
|
53
|
+
for (j = i + 1; j < nbytes; j++) {
|
54
|
+
if (c[j] != '\0')
|
55
|
+
break;
|
56
|
+
}
|
57
|
+
|
58
|
+
if (j != nbytes)
|
59
|
+
break;
|
60
|
+
|
61
|
+
/* It's a string */
|
62
|
+
return (rb_str_new2((char *)addr));
|
63
|
+
}
|
64
|
+
|
65
|
+
break;
|
66
|
+
}
|
67
|
+
|
68
|
+
if (i == nbytes) {
|
69
|
+
/*
|
70
|
+
* The byte range is all printable characters, but there is
|
71
|
+
* no trailing nul byte. We'll assume that it's a string.
|
72
|
+
*/
|
73
|
+
char *s = malloc(nbytes + 1);
|
74
|
+
if (!s) {
|
75
|
+
rb_raise(eDtraceException, "out of memory: failed to allocate string value");
|
76
|
+
return (Qnil);
|
77
|
+
}
|
78
|
+
(void) strncpy(s, c, nbytes);
|
79
|
+
s[nbytes] = '\0';
|
80
|
+
robj = rb_str_new2(s);
|
81
|
+
free(s);
|
82
|
+
return (robj);
|
83
|
+
}
|
84
|
+
|
85
|
+
/* return byte array */
|
86
|
+
robj = rb_ary_new();
|
87
|
+
for (i = 0; i < nbytes; i++)
|
88
|
+
rb_ary_push(robj, INT2FIX(addr[i]));
|
89
|
+
|
90
|
+
return (robj);
|
91
|
+
}
|
data/ext/extconf.rb
CHANGED
data/lib/dtrace.rb
CHANGED
@@ -0,0 +1,99 @@
|
|
1
|
+
#
|
2
|
+
# Ruby-Dtrace
|
3
|
+
# (c) 2007 Chris Andrews <chris@nodnol.org>
|
4
|
+
#
|
5
|
+
|
6
|
+
class DtraceConsumer
|
7
|
+
|
8
|
+
def initialize(t)
|
9
|
+
@t = t
|
10
|
+
@curragg = DtraceAggregate.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def consume
|
14
|
+
|
15
|
+
probe_consumer = proc do |probe|
|
16
|
+
yield probe
|
17
|
+
end
|
18
|
+
|
19
|
+
rec_consumer = proc do |rec|
|
20
|
+
#yield rec
|
21
|
+
end
|
22
|
+
|
23
|
+
buf_consumer = proc do |buf|
|
24
|
+
r = buf.record
|
25
|
+
# buf records can be empty (trace();)
|
26
|
+
if r
|
27
|
+
case r.class.to_s
|
28
|
+
when DtraceRecord.to_s
|
29
|
+
yield r
|
30
|
+
when DtraceAggData.to_s
|
31
|
+
case r.aggtype
|
32
|
+
when "tuple"
|
33
|
+
@curragg.tuple << r.value
|
34
|
+
when "value"
|
35
|
+
@curragg.value = r.value
|
36
|
+
when "last"
|
37
|
+
yield @curragg
|
38
|
+
@curragg = DtraceAggregate.new
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
@t.go
|
46
|
+
@t.buf_consumer(buf_consumer)
|
47
|
+
begin
|
48
|
+
while(true) do
|
49
|
+
@t.sleep
|
50
|
+
@t.work(probe_consumer, rec_consumer)
|
51
|
+
end
|
52
|
+
ensure
|
53
|
+
@t.stop
|
54
|
+
@t.work(probe_consumer, rec_consumer)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
def consume_once
|
60
|
+
|
61
|
+
probe_consumer = proc do |probe|
|
62
|
+
probe.each_record do |r|
|
63
|
+
yield r
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
rec_consumer = proc do |rec|
|
68
|
+
#yield rec
|
69
|
+
end
|
70
|
+
|
71
|
+
buf_consumer = proc do |buf|
|
72
|
+
r = buf.record
|
73
|
+
# buf records can be empty (trace();)
|
74
|
+
if r
|
75
|
+
case r.class.to_s
|
76
|
+
when DtraceRecord.to_s
|
77
|
+
yield r
|
78
|
+
when DtraceAggData.to_s
|
79
|
+
case r.aggtype
|
80
|
+
when "tuple"
|
81
|
+
@curragg.tuple << r.value
|
82
|
+
when "value"
|
83
|
+
@curragg.value = r.value
|
84
|
+
when "last"
|
85
|
+
yield @curragg
|
86
|
+
@curragg = DtraceAggregate.new
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
@t.buf_consumer(buf_consumer)
|
94
|
+
@t.stop
|
95
|
+
@t.work(probe_consumer, rec_consumer)
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
data/lib/dtraceprobe.rb
ADDED
data/lib/dtracerecord.rb
ADDED
@@ -3,7 +3,7 @@ require 'dtrace'
|
|
3
3
|
class Dtracer
|
4
4
|
|
5
5
|
def start_dtrace(pid)
|
6
|
-
progtext = 'ruby$1:::function-entry{ @[strjoin(strjoin(copyinstr(arg0),"."),copyinstr(arg1))] = count(); }'
|
6
|
+
progtext = 'ruby$1:::function-entry{ @a[strjoin(strjoin(copyinstr(arg0),"."),copyinstr(arg1))] = count(); } END { printa(@a); }'
|
7
7
|
|
8
8
|
begin
|
9
9
|
@d = Dtrace.new
|
@@ -28,11 +28,12 @@ class Dtracer
|
|
28
28
|
return {} unless @d
|
29
29
|
|
30
30
|
begin
|
31
|
-
@d.stop
|
32
|
-
@d.aggregate_snap
|
33
31
|
dtrace_report = Hash.new
|
34
|
-
@d
|
35
|
-
|
32
|
+
c = DtraceConsumer.new(@d)
|
33
|
+
c.consume_once do |e|
|
34
|
+
if e.respond_to? :tuple
|
35
|
+
dtrace_report[e.tuple.first] = e.value
|
36
|
+
end
|
36
37
|
end
|
37
38
|
rescue DtraceException => e
|
38
39
|
puts "end: #{e.message}"
|