ruby-dtrace 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +9 -0
- data/Manifest.txt +11 -1
- data/README.txt +10 -11
- data/examples/scsi.rb +442 -0
- data/ext/dtrace_aggdata.c +4 -1
- data/ext/dtrace_api.c +18 -7
- data/ext/dtrace_api.h +48 -9
- data/ext/dtrace_bufdata.c +52 -4
- data/ext/dtrace_hdl.c +137 -86
- data/ext/dtrace_probedata.c +188 -50
- data/ext/dtrace_process.c +37 -0
- data/ext/dtrace_recdesc.c +31 -0
- data/lib/dtrace.rb +53 -1
- data/lib/dtraceaggregate.rb +25 -1
- data/lib/dtraceaggregateset.rb +17 -0
- data/lib/dtraceconsumer.rb +91 -67
- data/lib/dtracedata.rb +73 -0
- data/lib/dtraceprintfrecord.rb +8 -0
- data/lib/dtraceprobedata.rb +5 -0
- data/lib/dtracerecord.rb +2 -1
- data/lib/dtracestackrecord.rb +29 -0
- data/plugin/dtrace/lib/dtracer.rb +42 -2
- data/plugin/dtrace/views/dtrace/_report.rhtml +48 -3
- data/test/test_dtrace.rb +2 -0
- data/test/test_dtrace_aggregates.rb +56 -0
- data/test/test_dtrace_processes.rb +83 -0
- data/test/test_dtrace_profile.rb +232 -0
- data/test/test_dtrace_repeat.rb +51 -0
- data/test/test_dtrace_rubyprobe.rb +52 -0
- metadata +19 -5
- data/test/test_dtrace_workapi.rb +0 -142
data/ext/dtrace_probedata.c
CHANGED
@@ -16,92 +16,225 @@ VALUE dtraceprobedata_init(VALUE self)
|
|
16
16
|
return self;
|
17
17
|
}
|
18
18
|
|
19
|
-
VALUE
|
19
|
+
static VALUE _handle_ustack_record(dtrace_hdl_t *handle, caddr_t addr, const dtrace_recdesc_t *rec)
|
20
20
|
{
|
21
|
+
VALUE stack;
|
22
|
+
stack = rb_ary_new();
|
23
|
+
|
24
|
+
/* TODO, apple and solaris ustack */
|
25
|
+
return stack;
|
26
|
+
}
|
27
|
+
|
28
|
+
static VALUE _handle_stack_record(dtrace_hdl_t *handle, caddr_t addr, const dtrace_recdesc_t *rec)
|
29
|
+
{
|
30
|
+
VALUE stack = Qnil;
|
31
|
+
int size, i;
|
32
|
+
int depth;
|
33
|
+
uint64_t pc;
|
34
|
+
dtrace_syminfo_t dts;
|
35
|
+
char c[PATH_MAX * 2];
|
36
|
+
|
37
|
+
#ifdef __APPLE__
|
38
|
+
__GElf_Sym sym;
|
39
|
+
#else
|
40
|
+
GElf_Sym sym;
|
41
|
+
#endif
|
42
|
+
|
43
|
+
size = rec->dtrd_size / rec->dtrd_arg;
|
44
|
+
depth = rec->dtrd_arg;
|
45
|
+
|
46
|
+
stack = rb_ary_new();
|
47
|
+
|
48
|
+
for (i = 0; i < depth; i++) {
|
49
|
+
|
50
|
+
switch (size) {
|
51
|
+
case sizeof (uint32_t):
|
52
|
+
pc = *((uint32_t *)addr);
|
53
|
+
break;
|
54
|
+
|
55
|
+
case sizeof (uint64_t):
|
56
|
+
pc = *((uint64_t *)addr);
|
57
|
+
break;
|
58
|
+
|
59
|
+
default:
|
60
|
+
rb_raise(eDtraceException, "bad stack pc");
|
61
|
+
return Qnil;
|
62
|
+
}
|
63
|
+
|
64
|
+
if (pc == (uint64_t)NULL)
|
65
|
+
break;
|
66
|
+
|
67
|
+
addr += size;
|
68
|
+
|
69
|
+
if (dtrace_lookup_by_addr(handle, pc, &sym, &dts) == 0) {
|
70
|
+
if (pc > sym.st_value) {
|
71
|
+
(void) snprintf(c, sizeof (c), "%s`%s+0x%llx",
|
72
|
+
dts.dts_object, dts.dts_name,
|
73
|
+
pc - sym.st_value);
|
74
|
+
}
|
75
|
+
else {
|
76
|
+
(void) snprintf(c, sizeof (c), "%s`%s",
|
77
|
+
dts.dts_object, dts.dts_name);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
else {
|
81
|
+
if (dtrace_lookup_by_addr(handle, pc, NULL, &dts) == 0) {
|
82
|
+
(void) snprintf(c, sizeof (c), "%s`0x%llx",
|
83
|
+
dts.dts_object, pc);
|
84
|
+
}
|
85
|
+
else {
|
86
|
+
(void) snprintf(c, sizeof (c), "0x%llx", pc);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
rb_ary_push(stack, rb_str_new2(c));
|
91
|
+
}
|
92
|
+
|
93
|
+
return stack;
|
94
|
+
}
|
95
|
+
|
96
|
+
/*
|
97
|
+
* Returns the enabled probe id which generated this data
|
98
|
+
*/
|
99
|
+
VALUE dtraceprobedata_epid(VALUE self)
|
100
|
+
{
|
101
|
+
dtrace_probedata_t *data;
|
102
|
+
|
103
|
+
Data_Get_Struct(self, dtrace_probedata_t, data);
|
104
|
+
return INT2FIX(data->dtpda_edesc->dtepd_epid);
|
105
|
+
}
|
106
|
+
|
107
|
+
/*
|
108
|
+
* Returns the DtraceProbe for the probe which generated this data
|
109
|
+
*/
|
110
|
+
VALUE dtraceprobedata_probe(VALUE self)
|
111
|
+
{
|
112
|
+
VALUE dtraceprobe;
|
21
113
|
dtrace_probedata_t *data;
|
22
|
-
dtrace_probedesc_t *
|
23
|
-
VALUE probe;
|
114
|
+
dtrace_probedesc_t *pd;
|
24
115
|
|
25
116
|
Data_Get_Struct(self, dtrace_probedata_t, data);
|
117
|
+
pd = data->dtpda_pdesc;
|
26
118
|
|
27
|
-
|
28
|
-
|
119
|
+
if (pd) {
|
120
|
+
dtraceprobe = Data_Wrap_Struct(cDtraceProbe, 0, NULL, (dtrace_probedesc_t *)pd);
|
121
|
+
return dtraceprobe;
|
122
|
+
}
|
29
123
|
|
30
|
-
return
|
124
|
+
return Qnil;
|
31
125
|
}
|
32
126
|
|
33
|
-
|
127
|
+
/* Returns the CPU which generated this data */
|
128
|
+
VALUE dtraceprobedata_cpu(VALUE self)
|
34
129
|
{
|
35
|
-
|
36
|
-
|
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;
|
130
|
+
dtrace_probedata_t *data;
|
131
|
+
processorid_t cpu;
|
41
132
|
|
42
|
-
|
133
|
+
Data_Get_Struct(self, dtrace_probedata_t, data);
|
134
|
+
|
135
|
+
if (data) {
|
136
|
+
cpu = data->dtpda_cpu;
|
137
|
+
return INT2FIX(cpu);
|
43
138
|
}
|
44
|
-
|
139
|
+
else {
|
140
|
+
return Qnil;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
/* Returns the indent level given to this data by DTrace */
|
145
|
+
VALUE dtraceprobedata_indent(VALUE self)
|
146
|
+
{
|
147
|
+
dtrace_probedata_t *data;
|
148
|
+
int indent;
|
45
149
|
|
46
|
-
|
47
|
-
for (i = 0; i < size; i++)
|
48
|
-
rb_ary_push(raw, INT2FIX(addr[i]));
|
150
|
+
Data_Get_Struct(self, dtrace_probedata_t, data);
|
49
151
|
|
50
|
-
|
51
|
-
|
52
|
-
|
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);
|
152
|
+
if (data) {
|
153
|
+
indent = data->dtpda_indent;
|
154
|
+
return INT2FIX(indent / 2);
|
62
155
|
}
|
156
|
+
else {
|
157
|
+
return Qnil;
|
158
|
+
}
|
159
|
+
}
|
63
160
|
|
64
|
-
|
161
|
+
/* Returns the prefix given to this data by DTrace */
|
162
|
+
VALUE dtraceprobedata_prefix(VALUE self)
|
163
|
+
{
|
164
|
+
dtrace_probedata_t *data;
|
165
|
+
const char *prefix;
|
166
|
+
|
167
|
+
Data_Get_Struct(self, dtrace_probedata_t, data);
|
168
|
+
prefix = data->dtpda_prefix;
|
169
|
+
|
170
|
+
if (prefix)
|
171
|
+
return rb_str_new2(prefix);
|
172
|
+
else
|
173
|
+
return Qnil;
|
65
174
|
}
|
66
175
|
|
67
|
-
|
68
|
-
|
176
|
+
/* Returns the flow kind given to this data by DTrace */
|
177
|
+
VALUE dtraceprobedata_flow(VALUE self)
|
69
178
|
{
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
179
|
+
dtrace_probedata_t *data;
|
180
|
+
|
181
|
+
Data_Get_Struct(self, dtrace_probedata_t, data);
|
182
|
+
|
183
|
+
switch (data->dtpda_flow) {
|
184
|
+
case DTRACEFLOW_ENTRY:
|
185
|
+
return rb_str_new2("->");
|
186
|
+
break;
|
187
|
+
case DTRACEFLOW_RETURN:
|
188
|
+
return rb_str_new2("<-");
|
76
189
|
break;
|
77
190
|
default:
|
78
|
-
|
191
|
+
return Qnil;
|
79
192
|
}
|
80
|
-
return (stack_action);
|
81
193
|
}
|
82
194
|
|
195
|
+
/*
|
196
|
+
* Yields each record in this DtraceProbedata in turn. Records are
|
197
|
+
* yielded as either DtraceRecords or DtraceStackRecords as
|
198
|
+
* appropriate for the type of action.
|
199
|
+
*/
|
83
200
|
VALUE dtraceprobedata_each_record(VALUE self)
|
84
201
|
{
|
85
202
|
dtrace_probedata_t *data;
|
86
203
|
dtrace_eprobedesc_t *eprobe;
|
87
204
|
dtrace_recdesc_t *rec;
|
205
|
+
dtrace_hdl_t *handle;
|
206
|
+
dtrace_actkind_t act;
|
88
207
|
int i;
|
208
|
+
caddr_t addr;
|
89
209
|
VALUE dtracerecord;
|
210
|
+
VALUE dtracehandle;
|
90
211
|
VALUE v;
|
91
|
-
caddr_t addr;
|
92
212
|
|
93
213
|
Data_Get_Struct(self, dtrace_probedata_t, data);
|
214
|
+
dtracehandle = rb_iv_get(self, "@handle");
|
215
|
+
Data_Get_Struct(dtracehandle, dtrace_hdl_t, handle);
|
216
|
+
|
94
217
|
eprobe = data->dtpda_edesc;
|
95
218
|
|
96
219
|
for (i = 0; i < eprobe->dtepd_nrecs; i++) {
|
220
|
+
v = 0;
|
97
221
|
rec = &eprobe->dtepd_rec[i];
|
98
222
|
if (rec->dtrd_size > 0) {
|
223
|
+
act = rec->dtrd_action;
|
99
224
|
addr = data->dtpda_data + rec->dtrd_offset;
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
225
|
+
|
226
|
+
switch (act) {
|
227
|
+
case DTRACEACT_STACK:
|
228
|
+
case DTRACEACT_USTACK:
|
229
|
+
case DTRACEACT_JSTACK:
|
230
|
+
/* Stack records come from bufdata */
|
231
|
+
/* v = _handle_stack_record(handle, addr, rec); */
|
232
|
+
/* v = _handle_ustack_record(handle, addr, rec); */
|
233
|
+
break;
|
234
|
+
case DTRACEACT_PRINTA:
|
235
|
+
/* don't want the probedata record for a printa() action */
|
236
|
+
break;
|
237
|
+
default:
|
105
238
|
switch (rec->dtrd_size) {
|
106
239
|
case 1:
|
107
240
|
v = INT2FIX((int)(*((uint8_t *)addr)));
|
@@ -121,9 +254,14 @@ VALUE dtraceprobedata_each_record(VALUE self)
|
|
121
254
|
}
|
122
255
|
}
|
123
256
|
|
124
|
-
|
125
|
-
|
126
|
-
|
257
|
+
if (v) {
|
258
|
+
dtracerecord = rb_class_new_instance(0, NULL, rb_path2class("DtraceRecord"));
|
259
|
+
rb_iv_set(dtracerecord, "@value", v);
|
260
|
+
rb_iv_set(dtracerecord, "@from", rb_str_new2("probedata"));
|
261
|
+
rb_iv_set(dtracerecord, "@index", INT2FIX(i));
|
262
|
+
rb_iv_set(dtracerecord, "@action", INT2FIX(act));
|
263
|
+
rb_yield(dtracerecord);
|
264
|
+
}
|
127
265
|
}
|
128
266
|
}
|
129
267
|
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
/* Ruby-Dtrace
|
2
|
+
* (c) 2007 Chris Andrews <chris@nodnol.org>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "dtrace_api.h"
|
6
|
+
|
7
|
+
/* :nodoc: */
|
8
|
+
VALUE dtrace_process_init(VALUE self)
|
9
|
+
{
|
10
|
+
dtrace_process_t *process;
|
11
|
+
|
12
|
+
Data_Get_Struct(self, dtrace_process_t, process);
|
13
|
+
if (process)
|
14
|
+
return self;
|
15
|
+
else
|
16
|
+
return Qnil;
|
17
|
+
}
|
18
|
+
|
19
|
+
/* :nodoc: */
|
20
|
+
void dtrace_process_release(dtrace_process_t *process)
|
21
|
+
{
|
22
|
+
dtrace_proc_release(process->handle, process->proc);
|
23
|
+
free(process);
|
24
|
+
}
|
25
|
+
|
26
|
+
/*
|
27
|
+
* Start or restart the process. Call this having configured tracing
|
28
|
+
* for the process, using $target in the D program.
|
29
|
+
*/
|
30
|
+
VALUE dtrace_process_continue(VALUE self)
|
31
|
+
{
|
32
|
+
dtrace_process_t *process;
|
33
|
+
|
34
|
+
Data_Get_Struct(self, dtrace_process_t, process);
|
35
|
+
dtrace_proc_continue(process->handle, process->proc);
|
36
|
+
}
|
37
|
+
|
data/ext/dtrace_recdesc.c
CHANGED
@@ -13,3 +13,34 @@ VALUE dtracerecdesc_init(VALUE self)
|
|
13
13
|
return self;
|
14
14
|
}
|
15
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
|
+
}
|
data/lib/dtrace.rb
CHANGED
@@ -7,10 +7,62 @@ require 'dtrace_api'
|
|
7
7
|
require 'dtracerecord'
|
8
8
|
require 'dtraceconsumer'
|
9
9
|
require 'dtraceaggregate'
|
10
|
+
require 'dtraceaggregateset'
|
10
11
|
require 'dtraceprobedata'
|
11
12
|
require 'dtraceprobe'
|
13
|
+
require 'dtracestackrecord'
|
14
|
+
require 'dtraceprintfrecord'
|
15
|
+
require 'dtracedata'
|
16
|
+
|
17
|
+
# A DTrace handle. Provides methods for inspecting available probes,
|
18
|
+
# compiling and running programs, and for setting up callbacks to
|
19
|
+
# receive trace data.
|
20
|
+
#
|
21
|
+
# The general structure of a Dtrace-based program is:
|
22
|
+
#
|
23
|
+
# * Create a handle with Dtrace.new
|
24
|
+
# * Set options
|
25
|
+
# * Compile the program, possibly inspecting the return DtraceProgramInfo
|
26
|
+
# * Execute the program
|
27
|
+
# * Start tracing
|
28
|
+
# * Consume data, either directly by setting up callbacks, or using a DtraceConsumer.
|
29
|
+
# * Stop tracing
|
30
|
+
#
|
31
|
+
# === Listing probes
|
32
|
+
#
|
33
|
+
# d.each_probe do |p|
|
34
|
+
# puts "#{p.provider}:#{p.mod}:#{p.func}:#{p.name}"
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# === Setting options
|
38
|
+
#
|
39
|
+
# d.setopt("bufsize", "8m")
|
40
|
+
# d.setopt("aggsize", "4m")
|
41
|
+
# d.setopt("stackframes", "5")
|
42
|
+
# d.setopt("strsize", "131072")
|
43
|
+
#
|
44
|
+
# === Compiling a program
|
45
|
+
#
|
46
|
+
# d.compile "syscall:::entry { trace(execname); stack(); }"
|
47
|
+
# d.execute
|
48
|
+
#
|
49
|
+
# === Setting up callbacks
|
50
|
+
#
|
51
|
+
# d.buf_consumer(prob {|buf| yield buf })
|
52
|
+
# d.work(proc {|probe| yield probe }, proc {|rec| yield rec })
|
53
|
+
#
|
54
|
+
# === Creating a process
|
55
|
+
#
|
56
|
+
# p = t.createprocess([ '/usr/bin/true' ])
|
57
|
+
# t.go
|
58
|
+
# p.continue
|
59
|
+
#
|
60
|
+
# c = DtraceConsumer.new(t)
|
61
|
+
# c.consume do |d|
|
62
|
+
# ..
|
63
|
+
# end
|
12
64
|
|
13
65
|
class Dtrace
|
14
|
-
VERSION = '0.0.
|
66
|
+
VERSION = '0.0.3'
|
15
67
|
end
|
16
68
|
|
data/lib/dtraceaggregate.rb
CHANGED
@@ -3,11 +3,35 @@
|
|
3
3
|
# (c) 2007 Chris Andrews <chris@nodnol.org>
|
4
4
|
#
|
5
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
|
+
# DtraceAggData objects until a completed DtraceAggregate is returned.
|
11
|
+
# (until a complete record is available, +add_record+ returns nil).
|
12
|
+
#
|
13
|
+
# See dtraceconsumer.rb for an example of this.
|
6
14
|
class DtraceAggregate
|
7
|
-
|
15
|
+
attr_reader :value, :tuple
|
8
16
|
|
17
|
+
# Create an empty DtraceAggregate: use +add_record+ to add data.
|
9
18
|
def initialize
|
10
19
|
@tuple = Array.new
|
11
20
|
end
|
21
|
+
|
22
|
+
# Add a DtraceAggData record to this aggregate. Returns nil until it
|
23
|
+
# receives a record of aggtype "last", when it returns the complete
|
24
|
+
# DtraceAggregate.
|
25
|
+
def add_record(r)
|
26
|
+
case r.aggtype
|
27
|
+
when "tuple"
|
28
|
+
@tuple << r.value
|
29
|
+
when "value"
|
30
|
+
@value = r.value
|
31
|
+
when "last"
|
32
|
+
return self
|
33
|
+
end
|
34
|
+
nil
|
35
|
+
end
|
12
36
|
|
13
37
|
end
|