ruby-dtrace 0.0.1 → 0.0.2
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 +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}"
|