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.
@@ -16,92 +16,225 @@ VALUE dtraceprobedata_init(VALUE self)
16
16
  return self;
17
17
  }
18
18
 
19
- VALUE dtraceprobedata_probedesc(VALUE self)
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 *pdp;
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
- pdp = data->dtpda_pdesc;
28
- probe = Data_Wrap_Struct(cDtraceProbe, 0, NULL, (dtrace_probedesc_t *)pdp);
119
+ if (pd) {
120
+ dtraceprobe = Data_Wrap_Struct(cDtraceProbe, 0, NULL, (dtrace_probedesc_t *)pd);
121
+ return dtraceprobe;
122
+ }
29
123
 
30
- return probe;
124
+ return Qnil;
31
125
  }
32
126
 
33
- static VALUE _handle_stack_record(const caddr_t addr, const dtrace_recdesc_t *rec)
127
+ /* Returns the CPU which generated this data */
128
+ VALUE dtraceprobedata_cpu(VALUE self)
34
129
  {
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;
130
+ dtrace_probedata_t *data;
131
+ processorid_t cpu;
41
132
 
42
- for (i = rec->dtrd_size - 1; (i >= 0) && !addr[i]; --i) {
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
- size = (i + 1);
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
- raw = rb_ary_new();
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
- 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);
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
- return raw;
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
- static int
68
- _is_stack_action(dtrace_actkind_t act)
176
+ /* Returns the flow kind given to this data by DTrace */
177
+ VALUE dtraceprobedata_flow(VALUE self)
69
178
  {
70
- int stack_action;
71
- switch (act) {
72
- case DTRACEACT_STACK:
73
- case DTRACEACT_USTACK:
74
- case DTRACEACT_JSTACK:
75
- stack_action = 1;
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
- stack_action = 0;
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
- if (_is_stack_action(rec->dtrd_action)) {
102
- v = _handle_stack_record(addr, rec);
103
- }
104
- else {
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
- dtracerecord = rb_class_new_instance(0, NULL, rb_path2class("DtraceRecord"));
125
- rb_iv_set(dtracerecord, "@value", v);
126
- rb_yield(dtracerecord);
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.2'
66
+ VERSION = '0.0.3'
15
67
  end
16
68
 
@@ -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
- attr_accessor :value, :tuple
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