ruby-dtrace-consumer 0.4.0

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.
Files changed (75) hide show
  1. data/LICENCE +20 -0
  2. data/README.md +51 -0
  3. data/ext/Makefile +187 -0
  4. data/ext/dtrace_aggdata.c +132 -0
  5. data/ext/dtrace_aggdata.c~ +141 -0
  6. data/ext/dtrace_aggdata.o +0 -0
  7. data/ext/dtrace_api.bundle +0 -0
  8. data/ext/dtrace_api.c +102 -0
  9. data/ext/dtrace_api.c~ +113 -0
  10. data/ext/dtrace_api.h +138 -0
  11. data/ext/dtrace_api.h~ +155 -0
  12. data/ext/dtrace_api.o +0 -0
  13. data/ext/dtrace_bufdata.c +130 -0
  14. data/ext/dtrace_bufdata.c~ +139 -0
  15. data/ext/dtrace_bufdata.o +0 -0
  16. data/ext/dtrace_dropdata.c +121 -0
  17. data/ext/dtrace_dropdata.c~ +131 -0
  18. data/ext/dtrace_dropdata.o +0 -0
  19. data/ext/dtrace_errdata.c +100 -0
  20. data/ext/dtrace_errdata.c~ +110 -0
  21. data/ext/dtrace_errdata.o +0 -0
  22. data/ext/dtrace_hdl.c +677 -0
  23. data/ext/dtrace_hdl.c~ +689 -0
  24. data/ext/dtrace_hdl.o +0 -0
  25. data/ext/dtrace_probedata.c +273 -0
  26. data/ext/dtrace_probedata.c~ +283 -0
  27. data/ext/dtrace_probedata.o +0 -0
  28. data/ext/dtrace_probedesc.c +93 -0
  29. data/ext/dtrace_probedesc.c~ +78 -0
  30. data/ext/dtrace_probedesc.o +0 -0
  31. data/ext/dtrace_process.c +44 -0
  32. data/ext/dtrace_process.c~ +56 -0
  33. data/ext/dtrace_process.o +0 -0
  34. data/ext/dtrace_program.c +52 -0
  35. data/ext/dtrace_program.c~ +62 -0
  36. data/ext/dtrace_program.o +0 -0
  37. data/ext/dtrace_programinfo.c +70 -0
  38. data/ext/dtrace_programinfo.c~ +60 -0
  39. data/ext/dtrace_programinfo.o +0 -0
  40. data/ext/dtrace_recdesc.c +37 -0
  41. data/ext/dtrace_recdesc.c~ +46 -0
  42. data/ext/dtrace_recdesc.o +0 -0
  43. data/ext/dtrace_util.c +92 -0
  44. data/ext/dtrace_util.o +0 -0
  45. data/ext/extconf.rb +7 -0
  46. data/lib/dtrace.rb +95 -0
  47. data/lib/dtrace/aggregate.rb +40 -0
  48. data/lib/dtrace/aggregateset.rb +19 -0
  49. data/lib/dtrace/consumer.rb +174 -0
  50. data/lib/dtrace/data.rb +85 -0
  51. data/lib/dtrace/dof.rb +8 -0
  52. data/lib/dtrace/printfrecord.rb +10 -0
  53. data/lib/dtrace/probedata.rb +23 -0
  54. data/lib/dtrace/probedesc.rb +15 -0
  55. data/lib/dtrace/record.rb +11 -0
  56. data/lib/dtrace/stackrecord.rb +31 -0
  57. data/lib/dtrace/tracer.rb +35 -0
  58. data/lib/dtrace/version.rb +8 -0
  59. data/lib/dtrace/version.rb~ +8 -0
  60. data/lib/dtraceconsumer.rb +9 -0
  61. data/test/test_aggregates.rb +45 -0
  62. data/test/test_drops_errors.rb +166 -0
  63. data/test/test_dtrace.rb +155 -0
  64. data/test/test_gc.rb +11 -0
  65. data/test/test_helper.rb +20 -0
  66. data/test/test_helper.rb~ +16 -0
  67. data/test/test_legacy_consumer.rb +47 -0
  68. data/test/test_probedata.rb +30 -0
  69. data/test/test_processes.rb +66 -0
  70. data/test/test_profile.rb +198 -0
  71. data/test/test_repeat.rb +50 -0
  72. data/test/test_rubyprobe.rb +52 -0
  73. data/test/test_rubyprobe.rb~ +52 -0
  74. data/test/test_typefilter.rb +94 -0
  75. metadata +121 -0
@@ -0,0 +1,689 @@
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
+ RUBY_EXTERN VALUE cDTrace;
10
+ RUBY_EXTERN VALUE cDTraceProbeDesc;
11
+ RUBY_EXTERN VALUE cDTraceProgram;
12
+ RUBY_EXTERN VALUE cDTraceRecDesc;
13
+ RUBY_EXTERN VALUE cDTraceProbeData;
14
+ RUBY_EXTERN VALUE cDTraceBufData;
15
+ RUBY_EXTERN VALUE cDTraceProcess;
16
+ RUBY_EXTERN VALUE cDTraceDropData;
17
+ RUBY_EXTERN VALUE cDTraceErrData;
18
+
19
+ static void dtrace_hdl_free(void *arg)
20
+ {
21
+ dtrace_handle_t *handle = (dtrace_handle_t *)arg;
22
+ VALUE proc;
23
+
24
+ if (handle->hdl != NULL) {
25
+ if (handle->procs != Qnil) {
26
+ while ((proc = rb_ary_pop(handle->procs)) != Qnil) {
27
+ dtrace_process_release(proc);
28
+ }
29
+ }
30
+ dtrace_close(handle->hdl);
31
+ }
32
+ free(handle);
33
+ }
34
+
35
+ static void dtrace_hdl_mark(void *arg)
36
+ {
37
+ dtrace_handle_t *handle = (dtrace_handle_t *)arg;
38
+
39
+ if (handle) {
40
+ rb_gc_mark(handle->probe);
41
+ rb_gc_mark(handle->rec);
42
+ rb_gc_mark(handle->buf);
43
+ rb_gc_mark(handle->err);
44
+ rb_gc_mark(handle->drop);
45
+ rb_gc_mark(handle->procs);
46
+ }
47
+ }
48
+
49
+ VALUE dtrace_hdl_alloc(VALUE klass)
50
+ {
51
+ dtrace_hdl_t *hdl;
52
+ dtrace_handle_t *handle;
53
+ int err;
54
+ VALUE obj;
55
+
56
+ hdl = dtrace_open(DTRACE_VERSION, 0, &err);
57
+
58
+ if (hdl) {
59
+ /*
60
+ * Leopard's DTrace requires symbol resolution to be
61
+ * switched on explicitly
62
+ */
63
+ #ifdef __APPLE__
64
+ (void) dtrace_setopt(hdl, "stacksymbols", "enabled");
65
+ #endif
66
+
67
+ /* always request flowindent information */
68
+ (void) dtrace_setopt(hdl, "flowindent", 0);
69
+
70
+ handle = ALLOC(dtrace_handle_t);
71
+ if (!handle) {
72
+ rb_raise(eDTraceException, "alloc failed");
73
+ return Qnil;
74
+ }
75
+
76
+ handle->hdl = hdl;
77
+ handle->probe = Qnil;
78
+ handle->rec = Qnil;
79
+ handle->buf = Qnil;
80
+ handle->err = Qnil;
81
+ handle->drop = Qnil;
82
+ handle->procs = Qnil;
83
+
84
+ obj = Data_Wrap_Struct(klass, dtrace_hdl_mark, dtrace_hdl_free, handle);
85
+ return obj;
86
+ }
87
+ else {
88
+ rb_raise(eDTraceException, "unable to open dtrace: %s (not root?)", strerror(err));
89
+ return Qnil;
90
+ }
91
+ }
92
+
93
+ /* :nodoc: */
94
+ VALUE dtrace_init(VALUE self)
95
+ {
96
+ dtrace_handle_t *handle;
97
+
98
+ Data_Get_Struct(self, dtrace_handle_t, handle);
99
+ if (handle)
100
+ return self;
101
+ else
102
+ return Qnil;
103
+ }
104
+
105
+ VALUE dtrace_hdl_close(VALUE self)
106
+ {
107
+ dtrace_handle_t *handle;
108
+
109
+ Data_Get_Struct(self, dtrace_handle_t, handle);
110
+ dtrace_close(handle->hdl);
111
+ handle->hdl = NULL;
112
+
113
+ return Qnil;
114
+ }
115
+
116
+ static
117
+ int _dtrace_next_probe(dtrace_hdl_t *hdl, const dtrace_probedesc_t *pdp, void *arg)
118
+ {
119
+ VALUE probe;
120
+
121
+ probe = Data_Wrap_Struct(cDTraceProbeDesc, 0, NULL, (dtrace_probedesc_t *)pdp);
122
+
123
+ rb_yield(probe);
124
+ return 0;
125
+ }
126
+
127
+ /*
128
+ * Yields each probe found on the system.
129
+ * (equivalent to dtrace -l)
130
+ *
131
+ * Each probe is represented by a DTraceProbe object
132
+ */
133
+ VALUE dtrace_each_probe_all(VALUE self)
134
+ {
135
+ dtrace_handle_t *handle;
136
+
137
+ Data_Get_Struct(self, dtrace_handle_t, handle);
138
+ (void) dtrace_probe_iter(handle->hdl, NULL, _dtrace_next_probe, NULL);
139
+
140
+ return self;
141
+ }
142
+
143
+ /*
144
+ * Yields each probe found on the system, matching against a
145
+ * partial name.
146
+ * (equivalent to dtrace -l -n 'probe:::spec')
147
+ *
148
+ * Each probe is represented by a DTraceProbe object
149
+ */
150
+ VALUE dtrace_each_probe_match(VALUE self, VALUE provider, VALUE mod, VALUE func, VALUE name)
151
+ {
152
+ dtrace_handle_t *handle;
153
+
154
+ dtrace_probedesc_t desc;
155
+ desc.dtpd_id = 0;
156
+ strcpy(desc.dtpd_provider, RSTRING_PTR(provider));
157
+ strcpy(desc.dtpd_mod, RSTRING_PTR(mod));
158
+ strcpy(desc.dtpd_func, RSTRING_PTR(func));
159
+ strcpy(desc.dtpd_name, RSTRING_PTR(name));
160
+
161
+ Data_Get_Struct(self, dtrace_handle_t, handle);
162
+ (void) dtrace_probe_iter(handle->hdl, &desc, _dtrace_next_probe, NULL);
163
+
164
+ return self;
165
+ }
166
+
167
+ static int
168
+ _dtrace_next_stmt(dtrace_hdl_t *hdl, dtrace_prog_t *program,
169
+ dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last)
170
+ {
171
+ dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc;
172
+
173
+ if (edp == *last)
174
+ return 0;
175
+
176
+ if (dtrace_probe_iter(hdl, &edp->dted_probe, _dtrace_next_probe, NULL) != 0) {
177
+ rb_raise(eDTraceException, "failed to match %s:%s:%s:%s: %s\n",
178
+ edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod,
179
+ edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name,
180
+ dtrace_errmsg(hdl, dtrace_errno(hdl)));
181
+
182
+ }
183
+
184
+ *last = edp;
185
+ return 0;
186
+ }
187
+
188
+ /*
189
+ * Yields each probe enabled by the given D program.
190
+ * (equivalent to dtrace -n -s program.d)
191
+ */
192
+ VALUE dtrace_each_probe_prog(VALUE self, VALUE program)
193
+ {
194
+ dtrace_handle_t *handle;
195
+ dtrace_prog_t *prog;
196
+ dtrace_ecbdesc_t *last = NULL;
197
+
198
+ Data_Get_Struct(self, dtrace_handle_t, handle);
199
+ Data_Get_Struct(program, dtrace_prog_t, prog);
200
+
201
+ (void) dtrace_stmt_iter(handle->hdl, prog, (dtrace_stmt_f *)_dtrace_next_stmt, &last);
202
+ return Qnil;
203
+ }
204
+
205
+ /*
206
+ * Compile a D program.
207
+ *
208
+ * Arguments:
209
+ * * The program text to compile
210
+ * * (Optionally) any arguments required by the program
211
+ *
212
+ * Raises a DTraceException if the program cannot be compiled.
213
+ */
214
+ VALUE dtrace_strcompile(int argc, VALUE *argv, VALUE self)
215
+ {
216
+ dtrace_handle_t *handle;
217
+ dtrace_prog_t *program;
218
+ VALUE dtrace_program;
219
+
220
+ VALUE dtrace_text;
221
+ int dtrace_argc;
222
+ VALUE dtrace_argv_array;
223
+
224
+ char **dtrace_argv;
225
+ int i;
226
+
227
+ rb_scan_args(argc, argv, "1*", &dtrace_text, &dtrace_argv_array);
228
+
229
+ dtrace_argc = rb_ary_len(dtrace_argv_array);
230
+ dtrace_argv = ALLOC_N(char *, dtrace_argc + 1);
231
+ if (!dtrace_argv) {
232
+ rb_raise(eDTraceException, "alloc failed");
233
+ return Qnil;
234
+ }
235
+
236
+ for (i = 0; i < dtrace_argc; i++) {
237
+ dtrace_argv[i + 1] = strdup(RSTRING_PTR(rb_ary_entry(dtrace_argv_array, i)));
238
+ }
239
+
240
+ dtrace_argv[0] = "ruby";
241
+ dtrace_argc++;
242
+
243
+ Data_Get_Struct(self, dtrace_handle_t, handle);
244
+ program = dtrace_program_strcompile(
245
+ handle->hdl, RSTRING_PTR(dtrace_text),
246
+ DTRACE_PROBESPEC_NAME, DTRACE_C_PSPEC,
247
+ dtrace_argc, dtrace_argv
248
+ );
249
+
250
+ if (!program) {
251
+ rb_raise(eDTraceException, "%s", dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
252
+ return Qnil;
253
+ }
254
+ else {
255
+ dtrace_program = Data_Wrap_Struct(cDTraceProgram, 0, NULL, program);
256
+ rb_iv_set(dtrace_program, "@handle", self);
257
+ return dtrace_program;
258
+ }
259
+ }
260
+
261
+ /*
262
+ * Start tracing. Must be called once a program has been successfully
263
+ * compiled and executed.
264
+ *
265
+ * Raises a DTraceException on any error.
266
+ */
267
+ VALUE dtrace_hdl_go(VALUE self)
268
+ {
269
+ dtrace_handle_t *handle;
270
+
271
+ Data_Get_Struct(self, dtrace_handle_t, handle);
272
+ if (dtrace_go(handle->hdl) < 0)
273
+ rb_raise(eDTraceException, "%s", dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
274
+
275
+ return Qnil;
276
+ }
277
+
278
+ /*
279
+ * Returns the status of the DTrace handle.
280
+ *
281
+ * Status values are defined as:
282
+ *
283
+ * * 0 - none
284
+ * * 1 - ok
285
+ * * 4 - stopped
286
+ */
287
+ VALUE dtrace_hdl_status(VALUE self)
288
+ {
289
+ dtrace_handle_t *handle;
290
+ int status;
291
+
292
+ Data_Get_Struct(self, dtrace_handle_t, handle);
293
+ if ((status = dtrace_status(handle->hdl)) < 0)
294
+ rb_raise(eDTraceException, "%s", dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
295
+
296
+ return INT2FIX(status);
297
+ }
298
+
299
+ /*
300
+ * Set an option on the DTrace handle.
301
+ *
302
+ * Options which may be set:
303
+ *
304
+ * * aggsize
305
+ * * bufsize
306
+ */
307
+ VALUE dtrace_hdl_setopt(VALUE self, VALUE key, VALUE value)
308
+ {
309
+ dtrace_handle_t *handle;
310
+ int ret;
311
+
312
+ Data_Get_Struct(self, dtrace_handle_t, handle);
313
+
314
+ if (NIL_P(value)) {
315
+ ret = dtrace_setopt(handle->hdl, RSTRING_PTR(key), 0);
316
+ }
317
+ else {
318
+ ret = dtrace_setopt(handle->hdl, RSTRING_PTR(key), RSTRING_PTR(value));
319
+ }
320
+
321
+ if (ret < 0)
322
+ rb_raise(eDTraceException, "%s", dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
323
+
324
+ return Qnil;
325
+ }
326
+
327
+ /* Stop tracing.
328
+ *
329
+ * Must be called after go has been called to start tracing.
330
+ */
331
+ VALUE dtrace_hdl_stop(VALUE self)
332
+ {
333
+ dtrace_handle_t *handle;
334
+
335
+ Data_Get_Struct(self, dtrace_handle_t, handle);
336
+ if (dtrace_stop(handle->hdl) < 0)
337
+ rb_raise(eDTraceException, "%s",
338
+ dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
339
+
340
+ return Qnil;
341
+ }
342
+
343
+ /*
344
+ * Return the most recent DTrace error.
345
+ */
346
+ VALUE dtrace_hdl_error(VALUE self)
347
+ {
348
+ dtrace_handle_t *handle;
349
+ const char *error_string;
350
+
351
+ Data_Get_Struct(self, dtrace_handle_t, handle);
352
+ error_string = dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl));
353
+ return rb_str_new2(error_string);
354
+ }
355
+
356
+ /*
357
+ * Sleep until we need to wake up to honour D options controlling
358
+ * consumption rates.
359
+ */
360
+ VALUE dtrace_hdl_sleep(VALUE self)
361
+ {
362
+ dtrace_handle_t *handle;
363
+
364
+ Data_Get_Struct(self, dtrace_handle_t, handle);
365
+ dtrace_sleep(handle->hdl);
366
+ return Qnil;
367
+ }
368
+
369
+ static int _probe_consumer(const dtrace_probedata_t *data, void *arg)
370
+ {
371
+ VALUE proc;
372
+ dtrace_work_handlers_t handlers;
373
+ VALUE probedata;
374
+
375
+ handlers = *(dtrace_work_handlers_t *) arg;
376
+ proc = handlers.probe;
377
+
378
+ if (!NIL_P(proc)) {
379
+ probedata = Data_Wrap_Struct(cDTraceProbeData, 0, NULL,
380
+ (dtrace_probedata_t *)data);
381
+
382
+ rb_iv_set(probedata, "@handle", handlers.handle);
383
+ rb_funcall(proc, rb_intern("call"), 1, probedata);
384
+ }
385
+
386
+ return (DTRACE_CONSUME_THIS);
387
+ }
388
+
389
+ static int _rec_consumer(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg)
390
+ {
391
+ VALUE proc;
392
+ dtrace_work_handlers_t handlers;
393
+ VALUE recdesc;
394
+ VALUE probedata;
395
+
396
+ dtrace_actkind_t act;
397
+
398
+ handlers = *(dtrace_work_handlers_t *) arg;
399
+ proc = handlers.rec;
400
+ if (!NIL_P(proc)) {
401
+ if (rec) {
402
+ recdesc = Data_Wrap_Struct(cDTraceRecDesc, 0, NULL, (dtrace_recdesc_t *)rec);
403
+ rb_iv_set(recdesc, "@handle", handlers.handle);
404
+ rb_funcall(proc, rb_intern("call"), 1, recdesc);
405
+ }
406
+ else {
407
+ rb_funcall(proc, rb_intern("call"), 1, Qnil);
408
+ return (DTRACE_CONSUME_NEXT);
409
+ }
410
+ }
411
+
412
+ if (rec) {
413
+ act = rec->dtrd_action;
414
+ if (act == DTRACEACT_EXIT)
415
+ return (DTRACE_CONSUME_NEXT);
416
+ }
417
+
418
+ return (DTRACE_CONSUME_THIS);
419
+ }
420
+
421
+ static int _buf_consumer(const dtrace_bufdata_t *bufdata, void *arg)
422
+ {
423
+ VALUE proc;
424
+ VALUE dtracebufdata;
425
+
426
+ proc = (VALUE)arg;
427
+
428
+ if (!NIL_P(proc)) {
429
+ dtracebufdata = Data_Wrap_Struct(cDTraceBufData, 0, NULL, (dtrace_bufdata_t *)bufdata);
430
+ rb_funcall(proc, rb_intern("call"), 1, dtracebufdata);
431
+ }
432
+
433
+ return (DTRACE_HANDLE_OK);
434
+ }
435
+
436
+ /*
437
+ * Process any data waiting from the D program.
438
+ *
439
+ * Takes a Proc to which DTraceProbeData objects will be yielded, and
440
+ * an optional second Proc to which DTraceRecDesc objects will be
441
+ * yielded.
442
+ *
443
+ */
444
+ VALUE dtrace_hdl_work(int argc, VALUE *argv, VALUE self)
445
+ {
446
+ dtrace_handle_t *handle;
447
+ dtrace_workstatus_t status;
448
+ dtrace_work_handlers_t handlers;
449
+ VALUE probe_consumer;
450
+ VALUE rec_consumer;
451
+
452
+ Data_Get_Struct(self, dtrace_handle_t, handle);
453
+
454
+ /* handle args - probe_consumer_proc is mandatory, rec_consumer_proc
455
+ is optional */
456
+ rb_scan_args(argc, argv, "11", &probe_consumer, &rec_consumer);
457
+
458
+ /* to mark during GC */
459
+ handle->probe = probe_consumer;
460
+ if (!NIL_P(rec_consumer))
461
+ handle->rec = rec_consumer;
462
+
463
+ /* fill out the handlers struct */
464
+ handlers.probe = probe_consumer;
465
+ handlers.rec = rec_consumer;
466
+ handlers.handle = self;
467
+
468
+ FILE *devnull = fopen("/dev/null", "w");
469
+ status = dtrace_work(handle->hdl, devnull, _probe_consumer, _rec_consumer, &handlers);
470
+ fclose(devnull);
471
+
472
+ if (status < 0)
473
+ rb_raise(eDTraceException, "%s", dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
474
+
475
+ return INT2FIX(status);
476
+ }
477
+
478
+ /*
479
+ * Set up the buffered output handler for this handle.
480
+ */
481
+ VALUE dtrace_hdl_buf_consumer(VALUE self, VALUE buf_consumer)
482
+ {
483
+ dtrace_handle_t *handle;
484
+ Data_Get_Struct(self, dtrace_handle_t, handle);
485
+
486
+ /* to mark during GC */
487
+ handle->buf = buf_consumer;
488
+
489
+ /* attach the buffered output handler */
490
+ if (dtrace_handle_buffered(handle->hdl, &_buf_consumer, (void *)buf_consumer) == -1) {
491
+ rb_raise(eDTraceException, "failed to establish buffered handler: %s",
492
+ (dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl))));
493
+ }
494
+
495
+ return Qnil;
496
+ }
497
+
498
+ static int _drop_consumer(const dtrace_dropdata_t *dropdata, void *arg)
499
+ {
500
+ VALUE proc;
501
+ VALUE dtracedropdata;
502
+
503
+ proc = (VALUE)arg;
504
+
505
+ if (!NIL_P(proc)) {
506
+ dtracedropdata = Data_Wrap_Struct(cDTraceDropData, 0, NULL, (dtrace_dropdata_t *)dropdata);
507
+ rb_funcall(proc, rb_intern("call"), 1, dtracedropdata);
508
+ }
509
+
510
+ return (DTRACE_HANDLE_OK);
511
+ }
512
+
513
+ /*
514
+ * Set up the drop-record handler for this handle. Takes a block,
515
+ * which will be called with any drop records returned by DTrace,
516
+ * represented by DTraceDropData objects.
517
+ */
518
+ VALUE dtrace_hdl_drop_consumer(VALUE self, VALUE drop_consumer)
519
+ {
520
+ dtrace_handle_t *handle;
521
+ Data_Get_Struct(self, dtrace_handle_t, handle);
522
+
523
+ /* to mark during GC */
524
+ handle->drop = drop_consumer;
525
+
526
+ /* attach the drop-record handler */
527
+ if (dtrace_handle_drop(handle->hdl, &_drop_consumer, (void *)drop_consumer) == -1) {
528
+ rb_raise(eDTraceException, "failed to establish drop-record handler");
529
+ }
530
+
531
+ return Qnil;
532
+ }
533
+
534
+ static int _err_consumer(const dtrace_errdata_t *errdata, void *arg)
535
+ {
536
+ VALUE proc;
537
+ VALUE dtraceerrdata;
538
+
539
+ proc = (VALUE)arg;
540
+
541
+ /* guard against bad invocations where arg is not what we provided... */
542
+ if (TYPE(proc) == T_DATA) {
543
+ dtraceerrdata = Data_Wrap_Struct(cDTraceErrData, 0, NULL,
544
+ (dtrace_errdata_t *)errdata);
545
+ rb_funcall(proc, rb_intern("call"), 1, dtraceerrdata);
546
+ }
547
+ else {
548
+ /* arg looked bad, throw an exception */
549
+ rb_raise(eDTraceException,
550
+ "bad argument to _err_consumer: %p -> type 0x%x\n", arg, TYPE(proc));
551
+ }
552
+
553
+ return (DTRACE_HANDLE_OK);
554
+ }
555
+
556
+ /*
557
+ * Set up the err-record handler for this handle. Takes a block, which
558
+ * will be called with any error records returned by DTrace,
559
+ * represented by DTraceErrData records.
560
+ */
561
+ VALUE dtrace_hdl_err_consumer(VALUE self, VALUE err_consumer)
562
+ {
563
+ dtrace_handle_t *handle;
564
+ void *arg;
565
+ Data_Get_Struct(self, dtrace_handle_t, handle);
566
+
567
+ if (dtrace_status(handle->hdl) != 0) {
568
+ rb_raise(eDTraceException, "too late to add error handler");
569
+ return Qnil;
570
+ }
571
+
572
+ /* to mark during GC */
573
+ handle->err = err_consumer;
574
+
575
+ /* attach the err-record handler */
576
+ if (dtrace_handle_err(handle->hdl, &_err_consumer, (void *)err_consumer) == -1) {
577
+ rb_raise(eDTraceException, "failed to establish err-record handler: %s",
578
+ dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
579
+ }
580
+
581
+ return Qnil;
582
+ }
583
+
584
+ static void _push_proc(dtrace_handle_t *handle, VALUE proc)
585
+ {
586
+ if (handle->procs == Qnil)
587
+ handle->procs = rb_ary_new();
588
+
589
+ rb_ary_push(handle->procs, proc);
590
+ }
591
+
592
+ /*
593
+ * Start a process which will be traced. The pid of the started
594
+ * process will be available in D as $target.
595
+ *
596
+ * Pass an array, where the first element is the full path to the
597
+ * program to start, and subsequent elements are its arguments.
598
+ *
599
+ * Returns a DTraceProcess object which is used to start the process
600
+ * once tracing is set up.
601
+ */
602
+ VALUE dtrace_hdl_createprocess(VALUE self, VALUE rbargv)
603
+ {
604
+ dtrace_handle_t *handle;
605
+ struct ps_prochandle *P;
606
+ char **argv;
607
+ long len;
608
+ int i;
609
+ dtrace_process_t *process;
610
+ VALUE rb_process;
611
+
612
+ Data_Get_Struct(self, dtrace_handle_t, handle);
613
+
614
+ Check_Type(rbargv, T_ARRAY);
615
+ len = rb_ary_len(rbargv);
616
+
617
+ argv = ALLOC_N(char *, len + 1);
618
+ if (!argv) {
619
+ rb_raise(eDTraceException, "alloc failed");
620
+ return Qnil;
621
+ }
622
+
623
+ for (i = 0; i < len; i++)
624
+ argv[i] = strdup(RSTRING_PTR(rb_ary_entry(rbargv, i)));
625
+ argv[len] = NULL;
626
+
627
+ P = dtrace_proc_create(handle->hdl, argv[0], argv);
628
+
629
+ for (i = 0; i < len; i++)
630
+ free(argv[i]);
631
+ free(argv);
632
+
633
+ if (P == NULL)
634
+ rb_raise(eDTraceException, "%s",
635
+ dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
636
+
637
+ process = ALLOC(dtrace_process_t);
638
+ if (!process) {
639
+ rb_raise(eDTraceException, "alloc failed");
640
+ return Qnil;
641
+ }
642
+
643
+ process->handle = handle;
644
+ process->proc = P;
645
+
646
+ rb_process = Data_Wrap_Struct(cDTraceProcess, 0, dtrace_process_free,
647
+ (dtrace_process_t *)process);
648
+
649
+ _push_proc(handle, rb_process);
650
+ return rb_process;
651
+ }
652
+
653
+ /*
654
+ * Grab a currently-running process by pid.
655
+ *
656
+ * Returns a Rb_Process object which is used to start the process
657
+ * once tracing is set up.
658
+ */
659
+ VALUE dtrace_hdl_grabprocess(VALUE self, VALUE pid)
660
+ {
661
+ dtrace_handle_t *handle;
662
+ struct ps_prochandle *P;
663
+ dtrace_process_t *process;
664
+ VALUE rb_process;
665
+
666
+ Data_Get_Struct(self, dtrace_handle_t, handle);
667
+
668
+ P = dtrace_proc_grab(handle->hdl, FIX2INT(pid), 0);
669
+
670
+ if (P == NULL) {
671
+ rb_raise(eDTraceException, "%s",
672
+ dtrace_errmsg(handle->hdl, dtrace_errno(handle->hdl)));
673
+ }
674
+
675
+ process = ALLOC(dtrace_process_t);
676
+ if (!process) {
677
+ rb_raise(eDTraceException, "alloc failed");
678
+ return Qnil;
679
+ }
680
+
681
+ process->handle = handle;
682
+ process->proc = P;
683
+
684
+ rb_process = Data_Wrap_Struct(cDTraceProcess, 0, dtrace_process_free,
685
+ (dtrace_process_t *)process);
686
+
687
+ _push_proc(handle, rb_process);
688
+ return rb_process;
689
+ }