rbtrace 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ Makefile
2
+ *.bundle
3
+ *.o
4
+ *.so
@@ -5,4 +5,7 @@ if RUBY_PLATFORM =~ /linux/
5
5
  $defs.push("-DBUF_SIZE=256")
6
6
  end
7
7
 
8
+ # warnings save lives
9
+ $CFLAGS << " -Wall "
10
+
8
11
  create_makefile('rbtrace')
@@ -17,8 +17,11 @@
17
17
  #include <ruby.h>
18
18
 
19
19
  #ifndef RUBY_VM
20
- #include <node.h>
20
+ #include <env.h>
21
21
  #include <intern.h>
22
+ #include <node.h>
23
+ #define rb_sourcefile() (ruby_current_node ? ruby_current_node->nd_file : 0)
24
+ #define rb_sourceline() (ruby_current_node ? nd_line(ruby_current_node) : 0)
22
25
  #endif
23
26
 
24
27
  #ifndef RSTRING_PTR
@@ -36,7 +39,13 @@ timeofday_usec()
36
39
  return (uint64_t)tv.tv_sec*1e6 + (uint64_t)tv.tv_usec;
37
40
  }
38
41
 
39
- #define MAX_EXPRS 10
42
+ #define MAX_CALLS 32768 // up to this many stack frames examined in slow watch mode
43
+ #define MAX_TRACERS 100 // max method tracers
44
+ #define MAX_EXPRS 10 // max expressions per tracer
45
+ #ifndef BUF_SIZE // msgq buffer size
46
+ #define BUF_SIZE 120
47
+ #endif
48
+
40
49
  struct rbtracer_t {
41
50
  int id;
42
51
 
@@ -50,28 +59,54 @@ struct rbtracer_t {
50
59
  };
51
60
  typedef struct rbtracer_t rbtracer_t;
52
61
 
53
- #define MAX_TRACERS 100
54
- static rbtracer_t tracers[MAX_TRACERS];
55
- static unsigned int num_tracers = 0;
56
-
57
- key_t mqi_key, mqo_key;
58
- int mqi_id = -1, mqo_id = -1;
59
-
60
- #ifndef BUF_SIZE
61
- #define BUF_SIZE 120
62
- #endif
63
62
 
64
63
  struct event_msg {
65
64
  long mtype;
66
65
  char buf[BUF_SIZE];
67
66
  };
68
67
 
68
+
69
+ static struct {
70
+ bool installed;
71
+
72
+ bool firehose;
73
+
74
+ bool slow;
75
+ uint64_t call_times[MAX_CALLS];
76
+ int num_calls;
77
+ uint32_t threshold;
78
+
79
+ unsigned int num;
80
+ rbtracer_t list[MAX_TRACERS];
81
+
82
+ key_t mqo_key;
83
+ key_t mqi_key;
84
+ int mqo_id;
85
+ int mqi_id;
86
+ }
87
+ rbtracer = {
88
+ .installed = false,
89
+
90
+ .firehose = false,
91
+
92
+ .slow = false,
93
+ .num_calls = 0,
94
+ .threshold = 250,
95
+
96
+ .num = 0,
97
+
98
+ .mqo_key = 0,
99
+ .mqi_key = 0,
100
+ .mqo_id = -1,
101
+ .mqi_id = -1
102
+ };
103
+
69
104
  #define SEND_EVENT(format, ...) do {\
70
105
  uint64_t usec = timeofday_usec();\
71
106
  if (false) {\
72
107
  fprintf(stderr, "%" PRIu64 "," format, usec, __VA_ARGS__);\
73
108
  fprintf(stderr, "\n");\
74
- } else {\
109
+ } else if (rbtracer.mqo_id != -1) {\
75
110
  struct event_msg msg;\
76
111
  int ret = -1, n = 0;\
77
112
  \
@@ -79,31 +114,16 @@ struct event_msg {
79
114
  snprintf(msg.buf, sizeof(msg.buf), "%" PRIu64 "," format, usec, __VA_ARGS__);\
80
115
  \
81
116
  for (n=0; n<10 && ret==-1; n++)\
82
- ret = msgsnd(mqo_id, &msg, sizeof(msg)-sizeof(long), IPC_NOWAIT);\
117
+ ret = msgsnd(rbtracer.mqo_id, &msg, sizeof(msg)-sizeof(long), IPC_NOWAIT);\
83
118
  if (ret == -1) {\
84
119
  fprintf(stderr, "msgsnd(): %s\n", strerror(errno));\
85
120
  struct msqid_ds stat;\
86
- msgctl(mqo_id, IPC_STAT, &stat);\
121
+ msgctl(rbtracer.mqo_id, IPC_STAT, &stat);\
87
122
  fprintf(stderr, "cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);\
88
123
  }\
89
124
  }\
90
125
  } while (0)
91
126
 
92
- static int in_event_hook = 0;
93
- static bool event_hook_installed = false;
94
-
95
- #define MAX_CALLS 32768
96
- static struct {
97
- bool enabled;
98
- uint64_t call_times[ MAX_CALLS ];
99
- int num_calls;
100
- uint32_t threshold;
101
- } slow_tracer = {
102
- .enabled = false,
103
- .num_calls = 0,
104
- .threshold = 250
105
- };
106
-
107
127
  static void
108
128
  #ifdef RUBY_VM
109
129
  event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
@@ -111,6 +131,8 @@ event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
111
131
  event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
112
132
  #endif
113
133
  {
134
+ static int in_event_hook = 0;
135
+
114
136
  // do not re-enter this function
115
137
  // after this, must `goto out` instead of `return`
116
138
  if (in_event_hook) return;
@@ -129,35 +151,35 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
129
151
  }
130
152
 
131
153
  // are we watching for any slow methods?
132
- if (slow_tracer.enabled) {
154
+ if (rbtracer.slow) {
133
155
  uint64_t usec = timeofday_usec(), diff = 0;
134
156
 
135
157
  switch (event) {
136
158
  case RUBY_EVENT_C_CALL:
137
159
  case RUBY_EVENT_CALL:
138
- if (slow_tracer.num_calls < MAX_CALLS)
139
- slow_tracer.call_times[ slow_tracer.num_calls ] = usec;
160
+ if (rbtracer.num_calls < MAX_CALLS)
161
+ rbtracer.call_times[ rbtracer.num_calls ] = usec;
140
162
 
141
- slow_tracer.num_calls++;
163
+ rbtracer.num_calls++;
142
164
  break;
143
165
 
144
166
  case RUBY_EVENT_C_RETURN:
145
167
  case RUBY_EVENT_RETURN:
146
- if (slow_tracer.num_calls > 0) {
147
- slow_tracer.num_calls--;
168
+ if (rbtracer.num_calls > 0) {
169
+ rbtracer.num_calls--;
148
170
 
149
- if (slow_tracer.num_calls < MAX_CALLS)
150
- diff = usec - slow_tracer.call_times[ slow_tracer.num_calls ];
171
+ if (rbtracer.num_calls < MAX_CALLS)
172
+ diff = usec - rbtracer.call_times[ rbtracer.num_calls ];
151
173
  }
152
174
  break;
153
175
  }
154
176
 
155
- if (diff > slow_tracer.threshold * 1e3) {
177
+ if (diff > rbtracer.threshold * 1e3) {
156
178
  SEND_EVENT(
157
179
  "%s,-1,%" PRIu64 ",%d,%s,%d,%s",
158
180
  event == RUBY_EVENT_RETURN ? "slow" : "cslow",
159
181
  diff,
160
- slow_tracer.num_calls,
182
+ rbtracer.num_calls,
161
183
  rb_id2name(mid),
162
184
  singleton,
163
185
  klass ? rb_class2name(singleton ? self : klass) : ""
@@ -167,28 +189,30 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
167
189
  goto out;
168
190
  }
169
191
 
170
- // are there specific methods we're waiting for?
171
- if (num_tracers == 0) goto out;
172
-
173
192
  int i, n;
174
193
  rbtracer_t *tracer = NULL;
175
194
 
176
- for (i=0, n=0; i<MAX_TRACERS && n<num_tracers; i++) {
177
- if (tracers[i].query) {
178
- n++;
195
+ if (!rbtracer.firehose) {
196
+ // are there specific methods we're waiting for?
197
+ if (rbtracer.num == 0) goto out;
198
+
199
+ for (i=0, n=0; i<MAX_TRACERS && n<rbtracer.num; i++) {
200
+ if (rbtracer.list[i].query) {
201
+ n++;
179
202
 
180
- if (!tracers[i].mid || tracers[i].mid == mid) {
181
- if (!tracers[i].klass || tracers[i].klass == klass) {
182
- if (!tracers[i].self || tracers[i].self == self) {
183
- tracer = &tracers[i];
203
+ if (!rbtracer.list[i].mid || rbtracer.list[i].mid == mid) {
204
+ if (!rbtracer.list[i].klass || rbtracer.list[i].klass == klass) {
205
+ if (!rbtracer.list[i].self || rbtracer.list[i].self == self) {
206
+ tracer = &rbtracer.list[i];
207
+ }
184
208
  }
185
209
  }
186
210
  }
187
211
  }
188
- }
189
212
 
190
- // no matching method tracer found, so bail!
191
- if (!tracer) goto out;
213
+ // no matching method tracer found, so bail!
214
+ if (!tracer) goto out;
215
+ }
192
216
 
193
217
  switch (event) {
194
218
  case RUBY_EVENT_C_CALL:
@@ -196,31 +220,43 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
196
220
  SEND_EVENT(
197
221
  "%s,%d,%s,%d,%s",
198
222
  event == RUBY_EVENT_CALL ? "call" : "ccall",
199
- tracer->id,
223
+ tracer ? tracer->id : 255, // hax
200
224
  rb_id2name(mid),
201
225
  singleton,
202
226
  klass ? rb_class2name(singleton ? self : klass) : ""
203
227
  );
204
228
 
205
- if (tracer->num_exprs) {
229
+ if (tracer && tracer->num_exprs) {
206
230
  for (i=0; i<tracer->num_exprs; i++) {
207
231
  char *expr = tracer->exprs[i];
208
232
  size_t len = strlen(expr);
209
- VALUE str = Qnil, val = Qnil;
233
+
234
+ VALUE val = Qnil;
235
+ char buffer[len+50];
236
+ char *result = NULL;
210
237
 
211
238
  if (len == 4 && strcmp("self", expr) == 0) {
212
239
  val = rb_inspect(self);
213
240
 
241
+ } else if (len == 10 && strcmp("__source__", expr) == 0) {
242
+ snprintf(buffer, len+50, "\"%s:%d\"", rb_sourcefile(), rb_sourceline());
243
+ result = buffer;
244
+
245
+ } else if (len > 1 && expr[0] == '@') {
246
+ val = rb_inspect(rb_ivar_get(self, rb_intern(expr)));
247
+
214
248
  } else if (event == RUBY_EVENT_CALL) {
215
- char code[len+50];
216
- snprintf(code, len+50, "(begin; %s; rescue Exception => e; e; end).inspect", expr);
249
+ snprintf(buffer, len+50, "(begin; %s; rescue Exception => e; e; end).inspect", expr);
217
250
 
218
- str = rb_str_new2(code);
251
+ VALUE str = rb_str_new2(buffer);
219
252
  val = rb_obj_instance_eval(1, &str, self);
220
253
  }
221
254
 
222
255
  if (RTEST(val) && TYPE(val) == T_STRING) {
223
- char *result = RSTRING_PTR(val);
256
+ result = RSTRING_PTR(val);
257
+ }
258
+
259
+ if (result) {
224
260
  SEND_EVENT(
225
261
  "%s,%d,%d,%s",
226
262
  "exprval",
@@ -238,7 +274,7 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
238
274
  SEND_EVENT(
239
275
  "%s,%d",
240
276
  event == RUBY_EVENT_RETURN ? "return" : "creturn",
241
- tracer->id
277
+ tracer ? tracer->id : 255 // hax
242
278
  );
243
279
  break;
244
280
  }
@@ -250,7 +286,7 @@ out:
250
286
  static void
251
287
  event_hook_install()
252
288
  {
253
- if (!event_hook_installed) {
289
+ if (!rbtracer.installed) {
254
290
  rb_add_event_hook(
255
291
  event_hook,
256
292
  RUBY_EVENT_CALL | RUBY_EVENT_C_CALL |
@@ -259,16 +295,16 @@ event_hook_install()
259
295
  , 0
260
296
  #endif
261
297
  );
262
- event_hook_installed = true;
298
+ rbtracer.installed = true;
263
299
  }
264
300
  }
265
301
 
266
302
  static void
267
303
  event_hook_remove()
268
304
  {
269
- if (event_hook_installed) {
305
+ if (rbtracer.installed) {
270
306
  rb_remove_event_hook(event_hook);
271
- event_hook_installed = false;
307
+ rbtracer.installed = false;
272
308
  }
273
309
  }
274
310
 
@@ -281,16 +317,16 @@ rbtracer_remove(char *query, int id)
281
317
 
282
318
  if (query) {
283
319
  for (i=0; i<MAX_TRACERS; i++) {
284
- if (tracers[i].query) {
285
- if (0 == strcmp(query, tracers[i].query)) {
286
- tracer = &tracers[i];
320
+ if (rbtracer.list[i].query) {
321
+ if (0 == strcmp(query, rbtracer.list[i].query)) {
322
+ tracer = &rbtracer.list[i];
287
323
  break;
288
324
  }
289
325
  }
290
326
  }
291
327
  } else {
292
328
  if (id >= MAX_TRACERS) goto out;
293
- tracer = &tracers[id];
329
+ tracer = &rbtracer.list[id];
294
330
  }
295
331
 
296
332
  if (tracer->query) {
@@ -308,8 +344,8 @@ rbtracer_remove(char *query, int id)
308
344
  tracer->num_exprs = 0;
309
345
  }
310
346
 
311
- num_tracers--;
312
- if (num_tracers == 0)
347
+ rbtracer.num--;
348
+ if (rbtracer.num == 0)
313
349
  event_hook_remove();
314
350
  }
315
351
 
@@ -325,9 +361,12 @@ out:
325
361
  static void
326
362
  rbtracer_remove_all()
327
363
  {
364
+ rbtracer.firehose = false;
365
+ rbtracer.slow = false;
366
+
328
367
  int i;
329
368
  for (i=0; i<MAX_TRACERS; i++) {
330
- if (tracers[i].query) {
369
+ if (rbtracer.list[i].query) {
331
370
  rbtracer_remove(NULL, i);
332
371
  }
333
372
  }
@@ -340,11 +379,11 @@ rbtracer_add(char *query)
340
379
  int tracer_id = -1;
341
380
  rbtracer_t *tracer = NULL;
342
381
 
343
- if (num_tracers >= MAX_TRACERS) goto out;
382
+ if (rbtracer.num >= MAX_TRACERS) goto out;
344
383
 
345
384
  for (i=0; i<MAX_TRACERS; i++) {
346
- if (!tracers[i].query) {
347
- tracer = &tracers[i];
385
+ if (!rbtracer.list[i].query) {
386
+ tracer = &rbtracer.list[i];
348
387
  tracer_id = i;
349
388
  break;
350
389
  }
@@ -389,10 +428,10 @@ rbtracer_add(char *query)
389
428
  tracer->query = strdup(query);
390
429
  tracer->num_exprs = 0;
391
430
 
392
- if (num_tracers == 0)
431
+ if (rbtracer.num == 0)
393
432
  event_hook_install();
394
433
 
395
- num_tracers++;
434
+ rbtracer.num++;
396
435
 
397
436
  out:
398
437
  SEND_EVENT(
@@ -411,7 +450,7 @@ rbtracer_add_expr(int id, char *expr)
411
450
  rbtracer_t *tracer = NULL;
412
451
 
413
452
  if (id >= MAX_TRACERS) goto out;
414
- tracer = &tracers[id];
453
+ tracer = &rbtracer.list[id];
415
454
 
416
455
  if (tracer->query) {
417
456
  tracer_id = tracer->id;
@@ -434,10 +473,11 @@ out:
434
473
  static void
435
474
  rbtracer_watch(uint32_t threshold)
436
475
  {
437
- if (!slow_tracer.enabled) {
438
- slow_tracer.num_calls = 0;
439
- slow_tracer.threshold = threshold;
440
- slow_tracer.enabled = true;
476
+ if (!rbtracer.slow) {
477
+ rbtracer.num_calls = 0;
478
+ rbtracer.threshold = threshold;
479
+ rbtracer.firehose = false;
480
+ rbtracer.slow = true;
441
481
 
442
482
  event_hook_install();
443
483
  }
@@ -446,61 +486,83 @@ rbtracer_watch(uint32_t threshold)
446
486
  static void
447
487
  rbtracer_unwatch()
448
488
  {
449
- if (slow_tracer.enabled) {
489
+ if (rbtracer.slow) {
450
490
  event_hook_remove();
451
491
 
452
- slow_tracer.enabled = false;
492
+ rbtracer.firehose = false;
493
+ rbtracer.slow = false;
453
494
  }
454
495
  }
455
496
 
456
- static VALUE
457
- rbtrace(VALUE self, VALUE query)
497
+ static void
498
+ msgq_teardown()
458
499
  {
459
- Check_Type(query, T_STRING);
460
-
461
- char *str = RSTRING_PTR(query);
462
- int tracer_id = -1;
463
-
464
- tracer_id = rbtracer_add(str);
465
- return tracer_id == -1 ? Qfalse : Qtrue;
500
+ if (rbtracer.mqo_id != -1) {
501
+ msgctl(rbtracer.mqo_id, IPC_RMID, NULL);
502
+ rbtracer.mqo_id = -1;
503
+ rbtracer.mqo_key = 0;
504
+ }
505
+ if (rbtracer.mqi_id != -1) {
506
+ msgctl(rbtracer.mqi_id, IPC_RMID, NULL);
507
+ rbtracer.mqi_id = -1;
508
+ rbtracer.mqi_key = 0;
509
+ }
466
510
  }
467
511
 
468
- static VALUE
469
- untrace(VALUE self, VALUE query)
512
+ static void
513
+ ruby_teardown(VALUE data)
470
514
  {
471
- Check_Type(query, T_STRING);
472
-
473
- char *str = RSTRING_PTR(query);
474
- int tracer_id = -1;
475
-
476
- tracer_id = rbtracer_remove(str, -1);
477
- return tracer_id == -1 ? Qfalse : Qtrue;
515
+ msgq_teardown();
478
516
  }
479
517
 
480
518
  static void
481
- cleanup()
519
+ msgq_setup()
482
520
  {
483
- if (mqo_id != -1) {
484
- msgctl(mqo_id, IPC_RMID, NULL);
485
- mqo_id = -1;
486
- }
487
- if (mqi_id != -1) {
488
- msgctl(mqi_id, IPC_RMID, NULL);
489
- mqi_id = -1;
521
+ pid_t pid = getpid();
522
+
523
+ if (rbtracer.mqo_key != (key_t)pid ||
524
+ rbtracer.mqi_key != (key_t)-pid) {
525
+ msgq_teardown();
526
+ } else {
527
+ return;
490
528
  }
491
- }
492
529
 
493
- static void
494
- cleanup_ruby(VALUE data)
495
- {
496
- cleanup();
530
+ rbtracer.mqo_key = (key_t) pid;
531
+ rbtracer.mqo_id = msgget(rbtracer.mqo_key, 0666 | IPC_CREAT);
532
+
533
+ if (rbtracer.mqo_id == -1)
534
+ fprintf(stderr, "msgget() failed to create msgq\n");
535
+
536
+
537
+ rbtracer.mqi_key = (key_t) -pid;
538
+ rbtracer.mqi_id = msgget(rbtracer.mqi_key, 0666 | IPC_CREAT);
539
+
540
+ if (rbtracer.mqi_id == -1)
541
+ fprintf(stderr, "msgget() failed to create msgq\n");
542
+
543
+ /*
544
+ struct msqid_ds stat;
545
+ int ret;
546
+
547
+ msgctl(rbtracer.mqo_id, IPC_STAT, &stat);
548
+ printf("cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);
549
+
550
+ stat.msg_qbytes += 10;
551
+ ret = msgctl(rbtracer.mqo_id, IPC_SET, &stat);
552
+ printf("cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);
553
+ printf("ret: %d, errno: %d\n", ret, errno);
554
+
555
+ msgctl(rbtracer.mqo_id, IPC_STAT, &stat);
556
+ printf("cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);
557
+ */
497
558
  }
498
559
 
499
560
  static void
500
561
  sigurg(int signal)
501
562
  {
502
563
  static int last_tracer_id = -1; // hax
503
- if (mqi_id == -1) return;
564
+ msgq_setup();
565
+ if (rbtracer.mqi_id == -1) return;
504
566
 
505
567
  struct event_msg msg;
506
568
  char *query = NULL;
@@ -511,7 +573,7 @@ sigurg(int signal)
511
573
  int ret = -1;
512
574
 
513
575
  for (n=0; n<10 && ret==-1; n++)
514
- ret = msgrcv(mqi_id, &msg, sizeof(msg)-sizeof(long), 0, IPC_NOWAIT);
576
+ ret = msgrcv(rbtracer.mqi_id, &msg, sizeof(msg)-sizeof(long), 0, IPC_NOWAIT);
515
577
 
516
578
  if (ret == -1) {
517
579
  break;
@@ -528,8 +590,9 @@ sigurg(int signal)
528
590
  query = msg.buf + 4;
529
591
  rbtracer_remove(query, -1);
530
592
 
531
- } else if (0 == strncmp("delall", msg.buf, 6)) {
532
- rbtracer_remove_all();
593
+ } else if (0 == strncmp("firehose", msg.buf, 8)) {
594
+ rbtracer.firehose = true;
595
+ event_hook_install();
533
596
 
534
597
  } else if (0 == strncmp("addexpr,", msg.buf, 8)) {
535
598
  query = msg.buf + 8;
@@ -547,6 +610,9 @@ sigurg(int signal)
547
610
  } else if (0 == strncmp("unwatch", msg.buf, 7)) {
548
611
  rbtracer_unwatch();
549
612
 
613
+ } else if (0 == strncmp("detach", msg.buf, 6)) {
614
+ rbtracer_remove_all();
615
+
550
616
  }
551
617
  }
552
618
  }
@@ -555,39 +621,13 @@ sigurg(int signal)
555
621
  void
556
622
  Init_rbtrace()
557
623
  {
558
- atexit(cleanup);
559
- rb_set_end_proc(cleanup_ruby, 0);
624
+ // zero out tracer
625
+ memset(&rbtracer.list, 0, sizeof(rbtracer.list));
560
626
 
627
+ // catch signal telling us to read from the msgq
561
628
  signal(SIGURG, sigurg);
562
629
 
563
- memset(&tracers, 0, sizeof(tracers));
564
-
565
- mqo_key = (key_t) getpid();
566
- mqo_id = msgget(mqo_key, 0666 | IPC_CREAT);
567
- if (mqo_id == -1)
568
- rb_sys_fail("msgget");
569
-
570
- mqi_key = (key_t) -getpid();
571
- mqi_id = msgget(mqi_key, 0666 | IPC_CREAT);
572
- if (mqi_id == -1)
573
- rb_sys_fail("msgget");
574
-
575
- /*
576
- struct msqid_ds stat;
577
- int ret;
578
-
579
- msgctl(mqo_id, IPC_STAT, &stat);
580
- printf("cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);
581
-
582
- stat.msg_qbytes += 10;
583
- ret = msgctl(mqo_id, IPC_SET, &stat);
584
- printf("cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);
585
- printf("ret: %d, errno: %d\n", ret, errno);
586
-
587
- msgctl(mqo_id, IPC_STAT, &stat);
588
- printf("cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);
589
- */
590
-
591
- rb_define_method(rb_cObject, "rbtrace", rbtrace, 1);
592
- rb_define_method(rb_cObject, "untrace", untrace, 1);
630
+ // cleanup the msgq on exit
631
+ atexit(msgq_teardown);
632
+ rb_set_end_proc(ruby_teardown, 0);
593
633
  }