ruby-dtrace 0.0.2 → 0.0.3
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 +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
|