rbtrace 0.2.8 → 0.3.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.
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbtrace (0.2.3)
4
+ rbtrace (0.2.8)
5
5
  ffi (>= 1.0.5)
6
+ msgpack (>= 0.4.4)
6
7
  trollop (>= 1.16.2)
7
8
 
8
9
  GEM
@@ -10,6 +11,7 @@ GEM
10
11
  specs:
11
12
  ffi (1.0.5)
12
13
  rake (>= 0.8.7)
14
+ msgpack (0.4.4)
13
15
  rake (0.8.7)
14
16
  trollop (1.16.2)
15
17
 
data/README.md CHANGED
@@ -5,12 +5,40 @@ time.
5
5
 
6
6
  rbtrace works on ruby 1.8 and 1.9, running on linux or mac osx.
7
7
 
8
+ rbtrace is designed to have minimal overhead, and should be safe to run
9
+ in production.
10
+
8
11
  ## usage
9
12
 
10
13
  % gem install rbtrace
11
14
  % rbtrace --help
12
15
 
13
- ## examples
16
+ ## tracer types
17
+
18
+ rbtrace has several different tracing modes:
19
+
20
+ ### firehose: show everything
21
+
22
+ % rbtrace -p <PID> --firehose
23
+
24
+ ### slow: show any method calls that take longer than `<N>` milliseconds
25
+
26
+ % rbtrace -p <PID> --slow=<N>
27
+
28
+ ### methods: trace calls to specific methods
29
+
30
+ % rbtrace -p <PID> --methods "Kernel#sleep" "Proc#call"
31
+
32
+ ## predefined tracers
33
+
34
+ rbtrace also includes a set of [predefined tracers](https://github.com/tmm1/rbtrace/tree/master/tracers)
35
+ for popular ruby libraries and functions.
36
+
37
+ ### trace calls to activerecord adapters and any i/o functions
38
+
39
+ % rbtrace -p <PID> -c activerecord io
40
+
41
+ ## detailed example
14
42
 
15
43
  ### require rbtrace into a process
16
44
 
@@ -119,7 +147,7 @@ rbtrace works on ruby 1.8 and 1.9, running on linux or mac osx.
119
147
 
120
148
  ### get values of variables and other expressions
121
149
 
122
- % rbtrace -p 87854 -m "String#gsub(self, @test)" "String#*(self)" "String#multiply_vowels(self, self.length, num)"
150
+ % rbtrace -p 87854 -m "String#gsub(self, @test)" "String#*(self, __source__)" "String#multiply_vowels(self, self.length, num)"
123
151
  *** attached to process 87854
124
152
 
125
153
  String#multiply_vowels(self="hello", self.length=5, num=3)
@@ -154,7 +182,6 @@ rbtrace works on ruby 1.8 and 1.9, running on linux or mac osx.
154
182
 
155
183
  ## todo
156
184
 
157
- * switch ipc to [msgpack](https://github.com/dhotson/msgpack/tree/master/c) instead of csv
158
185
  * add triggers to start tracing slow methods only inside another method
159
186
  * syntax check expressions before adding them
160
187
  * add special expressions for method args (_arg0_, _arguments_)
data/bin/rbtrace CHANGED
@@ -1,8 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'rubygems'
3
3
  require 'ffi'
4
+ require 'msgpack'
4
5
  require 'trollop'
5
6
 
7
+ class String
8
+ alias :bytesize :size
9
+ end unless ''.respond_to?(:bytesize)
10
+
6
11
  module FFI::LastError
7
12
  def self.exception
8
13
  Errno::constants.map(&Errno.method(:const_get)).find{ |c| c.const_get(:Errno) == error }
@@ -17,7 +22,7 @@ module MsgQ
17
22
  ffi_lib 'c'
18
23
 
19
24
  class EventMsg < FFI::Struct
20
- BUF_SIZE = RUBY_PLATFORM =~ /linux/ ? 256 : 120
25
+ BUF_SIZE = RUBY_PLATFORM =~ /linux/ ? 256 : 64
21
26
  IPC_NOWAIT = 004000
22
27
 
23
28
  layout :mtype, :long,
@@ -43,7 +48,7 @@ module MsgQ
43
48
  FFI::LastError.raise
44
49
  end
45
50
 
46
- msg[:buf].to_ptr.read_string
51
+ msg[:buf].to_ptr.read_string_length(BUF_SIZE)
47
52
  end
48
53
  end
49
54
 
@@ -172,14 +177,14 @@ class RBTracer
172
177
  #
173
178
  # Returns nothing.
174
179
  def watch(msec)
175
- send_cmd("watch,#{msec}")
180
+ send_cmd(:watch, msec)
176
181
  end
177
182
 
178
183
  # Turn on the firehose (show all method calls).
179
184
  #
180
185
  # Returns nothing.
181
186
  def firehose
182
- send_cmd('firehose')
187
+ send_cmd(:firehose)
183
188
  end
184
189
 
185
190
  # Add tracers for the given list of methods.
@@ -197,10 +202,10 @@ class RBTracer
197
202
  args = args.split(',').map{ |a| a.strip }
198
203
  end
199
204
 
200
- send_cmd("add,#{func}")
205
+ send_cmd(:add, func)
201
206
 
202
207
  args.each do |arg|
203
- send_cmd("addexpr,#{arg}")
208
+ send_cmd(:addexpr, arg)
204
209
  end if args and args.any?
205
210
  end
206
211
  end
@@ -209,7 +214,7 @@ class RBTracer
209
214
  #
210
215
  # Returns nothing.
211
216
  def attach
212
- send_cmd("attach,#{Process.pid}")
217
+ send_cmd(:attach, Process.pid)
213
218
  if wait_for{ @attached == true }
214
219
  STDERR.puts "*** attached to process #{pid}"
215
220
  else
@@ -222,7 +227,7 @@ class RBTracer
222
227
  # Returns nothing.
223
228
  def detach
224
229
  begin
225
- send_cmd('detach')
230
+ send_cmd(:detach)
226
231
  rescue Errno::ESRCH
227
232
  end
228
233
 
@@ -294,8 +299,10 @@ class RBTracer
294
299
  false
295
300
  end
296
301
 
297
- def send_cmd(msg)
302
+ def send_cmd(*cmd)
298
303
  begin
304
+ msg = cmd.to_msgpack
305
+ raise ArgumentError, 'command is too long' if msg.bytesize > MsgQ::EventMsg::BUF_SIZE
299
306
  MsgQ::EventMsg.send_cmd(@qo, msg)
300
307
  rescue Errno::EINTR
301
308
  retry
@@ -320,67 +327,76 @@ class RBTracer
320
327
  @out.puts(*args)
321
328
  end
322
329
 
330
+ def parse_cmd(line)
331
+ unpacker = MessagePack::Unpacker.new
332
+ unpacker.feed(line)
333
+
334
+ obj = nil
335
+ unpacker.each{|o| obj = o; break }
336
+ obj
337
+ end
338
+
323
339
  def process_line(line)
324
- time, event, _id, *args = line.strip.split(',')
325
- time = time.to_i
326
- id = _id.to_i
340
+ return unless cmd = parse_cmd(line)
341
+ event = cmd.shift
327
342
 
328
343
  case event
329
344
  when 'attached'
330
- @attached = true
331
- if id != Process.pid
345
+ tracer_pid = *cmd
346
+ if tracer_pid != Process.pid
332
347
  STDERR.puts "*** process #{pid} is already being traced"
333
348
  exit!(-1)
334
349
  end
350
+
351
+ @attached = true
335
352
  return
336
353
 
337
354
  when 'detached'
338
- @attached = false
355
+ tracer_pid = *cmd
356
+ if tracer_pid != Process.pid
357
+ STDERR.puts "*** process #{pid} detached #{tracer_pid}, but we are #{Process.pid}"
358
+ else
359
+ @attached = false
360
+ end
361
+
339
362
  return
340
363
  end
341
364
 
342
- if !@attached
365
+ unless @attached
343
366
  STDERR.puts "*** got #{event} before attaching"
344
367
  return
345
368
  end
346
369
 
347
- tracer = @tracers[id] if id > -1
348
-
349
370
  case event
350
371
  when 'mid'
351
- @methods[id.to_i] = args.first
372
+ mid, name = *cmd
373
+ @methods[mid] = name
352
374
 
353
375
  when 'klass'
354
- @klasses[_id.to_i(16)] = args.first
376
+ kid, name = *cmd
377
+ @klasses[kid] = name
355
378
 
356
379
  when 'add'
357
- if id == -1
358
- puts line
359
- else
360
- query = args.first
361
- @tracers.delete(id)
362
- @tracers[id][:query] = query
363
- end
364
-
365
- when 'remove'
366
- if id == -1
367
- puts line
380
+ tracer_id, query = *cmd
381
+ if tracer_id == -1
382
+ STDERR.puts "*** unable to add tracer for #{query}"
368
383
  else
369
- @tracers.delete(id)
384
+ @tracers.delete(tracer_id)
385
+ @tracers[tracer_id][:query] = query
370
386
  end
371
387
 
372
388
  when 'newexpr'
373
- expr_id, expr = *args
374
- expr_id = expr_id.to_i
389
+ tracer_id, expr_id, expr = *cmd
390
+ tracer = @tracers[tracer_id]
375
391
 
376
- if expr_id > -1 and tracer
392
+ if expr_id > -1
377
393
  tracer[:exprs][expr_id] = expr
378
394
  end
379
395
 
380
396
  when 'exprval'
381
- expr_id = args.shift
382
- val = args.join(',')
383
- expr_id = expr_id.to_i
397
+ tracer_id, expr_id, val = *cmd
398
+
399
+ tracer = @tracers[tracer_id]
384
400
  expr = tracer[:exprs][expr_id]
385
401
 
386
402
  if tracer[:arglist]
@@ -394,11 +410,12 @@ class RBTracer
394
410
  tracer[:arglist] = true
395
411
 
396
412
  when 'call','ccall'
397
- method, is_singleton, klass = *args
398
- is_singleton = (is_singleton == '1')
399
- klass = @klasses[klass.to_i(16)]
413
+ time, tracer_id, mid, is_singleton, klass = *cmd
414
+
415
+ tracer = @tracers[tracer_id]
416
+ klass = @klasses[klass]
400
417
  name = klass ? "#{klass}#{ is_singleton ? '.' : '#' }" : ''
401
- name += @methods[method.to_i] || '(unknown)'
418
+ name += @methods[mid] || '(unknown)'
402
419
 
403
420
  tracer[:times] << time
404
421
  tracer[:names] << name
@@ -422,6 +439,9 @@ class RBTracer
422
439
  tracer[:last] = name
423
440
 
424
441
  when 'return','creturn'
442
+ time, tracer_id = *cmd
443
+ tracer = @tracers[tracer_id]
444
+
425
445
  @nesting -= 1 if @nesting > 0
426
446
 
427
447
  if start = tracer[:times].pop
@@ -449,14 +469,11 @@ class RBTracer
449
469
  tracer[:arglist] = false
450
470
 
451
471
  when 'slow', 'cslow'
452
- diff, nesting, method, is_singleton, klass = *args
453
- nesting = nesting.to_i
454
- diff = diff.to_i
472
+ diff, nesting, mid, is_singleton, klass = *cmd
455
473
 
456
- is_singleton = (is_singleton == '1')
457
- klass = @klasses[klass.to_i(16)]
474
+ klass = @klasses[klass]
458
475
  name = klass ? "#{klass}#{ is_singleton ? '.' : '#' }" : ''
459
- name += @methods[method.to_i] || '(unknown)'
476
+ name += @methods[mid] || '(unknown)'
460
477
 
461
478
  print @prefix*nesting if nesting > 0
462
479
  print name
@@ -472,7 +489,7 @@ class RBTracer
472
489
 
473
490
  end
474
491
  rescue => e
475
- STDERR.puts "error on: #{line}"
492
+ STDERR.puts "error on #{event}: #{cmd.inspect}"
476
493
  raise e
477
494
  end
478
495
 
@@ -483,7 +500,7 @@ class RBTracer
483
500
  parser = Trollop::Parser.new do
484
501
  version <<-EOS
485
502
  rbtrace: like strace, but for ruby code
486
- version 0.2.8
503
+ version 0.3.0
487
504
  (c) 2011 Aman Gupta (tmm1)
488
505
  http://github.com/tmm1/rbtrace
489
506
  EOS
data/ext/.gitignore CHANGED
@@ -2,3 +2,8 @@ Makefile
2
2
  *.bundle
3
3
  *.o
4
4
  *.so
5
+ *.a
6
+ mkmf.log
7
+ conftest.dSYM/
8
+ dst/
9
+ src/msgpack-0.5.4/
data/ext/extconf.rb CHANGED
@@ -1,4 +1,37 @@
1
+ CWD = File.expand_path('../', __FILE__)
2
+
3
+ def sys(cmd)
4
+ puts " -- #{cmd}"
5
+ unless ret = xsystem(cmd)
6
+ raise "#{cmd} failed, please report to https://github.com/tmm1/rbtrace/issues"
7
+ end
8
+ ret
9
+ end
10
+
1
11
  require 'mkmf'
12
+ require 'fileutils'
13
+
14
+ Logging.message "Building msgpack\n"
15
+
16
+ msgpack = File.basename('msgpack-0.5.4.tar.gz')
17
+ dir = File.basename(msgpack, '.tar.gz')
18
+
19
+ Dir.chdir('src') do
20
+ FileUtils.rm_rf(dir) if File.exists?(dir)
21
+
22
+ sys("tar zxvf #{msgpack}")
23
+ Dir.chdir(dir) do
24
+ sys("./configure --disable-shared --with-pic --prefix=#{CWD}/dst/")
25
+ sys("make install")
26
+ end
27
+ end
28
+
29
+ FileUtils.cp "#{CWD}/dst/lib/libmsgpackc.a", "#{CWD}/libmsgpackc_ext.a"
30
+ $INCFLAGS[0,0] = "-I#{CWD}/dst/include "
31
+
32
+ unless have_library('msgpackc_ext') and have_header('msgpack.h')
33
+ raise 'msgpack build failed'
34
+ end
2
35
 
3
36
  # increase message size on linux
4
37
  if RUBY_PLATFORM =~ /linux/
data/ext/rbtrace.c CHANGED
@@ -1,6 +1,7 @@
1
1
  #include <inttypes.h>
2
2
  #include <errno.h>
3
3
  #include <signal.h>
4
+ #include <stdarg.h>
4
5
  #include <stdbool.h>
5
6
  #include <stdio.h>
6
7
  #include <stdint.h>
@@ -14,6 +15,7 @@
14
15
  #include <time.h>
15
16
  #include <unistd.h>
16
17
 
18
+ #include <msgpack.h>
17
19
  #include <ruby.h>
18
20
 
19
21
  #ifndef RUBY_VM
@@ -49,10 +51,10 @@ timeofday_usec()
49
51
  #define MAX_TRACERS 100 // max method tracers
50
52
  #define MAX_EXPRS 10 // max expressions per tracer
51
53
  #ifndef BUF_SIZE // msgq buffer size
52
- #define BUF_SIZE 120
54
+ #define BUF_SIZE 64
53
55
  #endif
54
56
 
55
- struct rbtracer_t {
57
+ typedef struct {
56
58
  int id;
57
59
 
58
60
  char *query;
@@ -62,13 +64,12 @@ struct rbtracer_t {
62
64
 
63
65
  int num_exprs;
64
66
  char *exprs[MAX_EXPRS];
65
- };
66
- typedef struct rbtracer_t rbtracer_t;
67
+ } rbtracer_t;
67
68
 
68
- struct event_msg {
69
+ typedef struct {
69
70
  long mtype;
70
71
  char buf[BUF_SIZE];
71
- };
72
+ } event_msg_t;
72
73
 
73
74
  static struct {
74
75
  st_table *mid_tbl;
@@ -92,6 +93,9 @@ static struct {
92
93
  key_t mqi_key;
93
94
  int mqo_id;
94
95
  int mqi_id;
96
+
97
+ msgpack_sbuffer *sbuf;
98
+ msgpack_packer *msgpacker;
95
99
  }
96
100
  rbtracer = {
97
101
  .mid_tbl = NULL,
@@ -108,36 +112,114 @@ rbtracer = {
108
112
  .threshold = 250,
109
113
 
110
114
  .num = 0,
115
+ .list = {},
111
116
 
112
117
  .mqo_key = 0,
113
118
  .mqi_key = 0,
114
119
  .mqo_id = -1,
115
- .mqi_id = -1
120
+ .mqi_id = -1,
121
+
122
+ .sbuf = NULL,
123
+ .msgpacker = NULL
116
124
  };
117
125
 
118
- #define SEND_EVENT(format, ...) do {\
119
- uint64_t usec = timeofday_usec();\
120
- if (false) {\
121
- fprintf(stderr, "%" PRIu64 "," format, usec, __VA_ARGS__);\
122
- fprintf(stderr, "\n");\
123
- } else if (rbtracer.mqo_id != -1 && rbtracer.attached_pid) {\
124
- struct event_msg msg;\
125
- int ret = -1, n = 0;\
126
- \
127
- msg.mtype = 1;\
128
- snprintf(msg.buf, sizeof(msg.buf), "%" PRIu64 "," format, usec, __VA_ARGS__);\
129
- \
130
- for (n=0; n<10 && ret==-1; n++)\
131
- ret = msgsnd(rbtracer.mqo_id, &msg, sizeof(msg)-sizeof(long), IPC_NOWAIT);\
132
- \
133
- if (ret == -1 && rbtracer.mqo_id != -1 && errno != EINVAL) {\
134
- fprintf(stderr, "msgsnd(%d): %s\n", rbtracer.mqo_id, strerror(errno));\
135
- struct msqid_ds stat;\
136
- msgctl(rbtracer.mqo_id, IPC_STAT, &stat);\
137
- fprintf(stderr, "cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);\
138
- }\
139
- }\
140
- } while (0)
126
+ static inline void
127
+ SEND_EVENT(int nargs, char *name, ...)
128
+ {
129
+ if (!rbtracer.attached_pid ||
130
+ !rbtracer.sbuf ||
131
+ !rbtracer.msgpacker ||
132
+ rbtracer.mqo_id == -1)
133
+ return;
134
+
135
+ int n;
136
+
137
+ msgpack_sbuffer_clear(rbtracer.sbuf);
138
+ msgpack_packer *pk = rbtracer.msgpacker;
139
+
140
+ msgpack_pack_array(pk, nargs+1);
141
+
142
+ msgpack_pack_raw(pk, strlen(name));
143
+ msgpack_pack_raw_body(pk, name, strlen(name));
144
+
145
+ if (nargs > 0) {
146
+ int type;
147
+ uint32_t uint;
148
+ int sint;
149
+ unsigned long ulong;
150
+ char *str;
151
+
152
+ va_list ap;
153
+ va_start(ap, name);
154
+
155
+ for (n=0; n<nargs; n++) {
156
+ type = va_arg(ap, int);
157
+ switch (type) {
158
+ case 't':
159
+ msgpack_pack_uint64(pk, timeofday_usec());
160
+ break;
161
+
162
+ case 'b':
163
+ if (va_arg(ap, int))
164
+ msgpack_pack_true(pk);
165
+ else
166
+ msgpack_pack_false(pk);
167
+ break;
168
+
169
+ case 'u':
170
+ uint = va_arg(ap, uint32_t);
171
+ msgpack_pack_uint32(pk, uint);
172
+ break;
173
+
174
+ case 'l':
175
+ ulong = va_arg(ap, unsigned long);
176
+ msgpack_pack_unsigned_long(pk, ulong);
177
+ break;
178
+
179
+ case 'd':
180
+ sint = va_arg(ap, int);
181
+ msgpack_pack_int(pk, sint);
182
+ break;
183
+
184
+ case 's':
185
+ str = va_arg(ap, char *);
186
+ if (!str)
187
+ str = "";
188
+
189
+ msgpack_pack_raw(pk, strlen(str));
190
+ msgpack_pack_raw_body(pk, str, strlen(str));
191
+ break;
192
+
193
+ default:
194
+ fprintf(stderr, "unknown type (%c) passed to SEND_EVENT\n", (char)type);
195
+ }
196
+ }
197
+
198
+ va_end(ap);
199
+ }
200
+
201
+ event_msg_t msg;
202
+ msg.mtype = 1;
203
+
204
+ if (rbtracer.sbuf->size > sizeof(msg.buf)) {
205
+ fprintf(stderr, "SEND_EVENT(): message is too large (%zd > %lu)\n", rbtracer.sbuf->size, sizeof(msg.buf));
206
+ return;
207
+ }
208
+
209
+ memcpy(msg.buf, rbtracer.sbuf->data, rbtracer.sbuf->size);
210
+
211
+ int ret = -1;
212
+ for (n=0; n<10 && ret==-1; n++)
213
+ ret = msgsnd(rbtracer.mqo_id, &msg, sizeof(msg)-sizeof(long), IPC_NOWAIT);
214
+
215
+ if (ret == -1 && rbtracer.mqo_id != -1 && errno != EINVAL) {
216
+ fprintf(stderr, "msgsnd(%d): %s\n", rbtracer.mqo_id, strerror(errno));
217
+
218
+ struct msqid_ds stat;
219
+ msgctl(rbtracer.mqo_id, IPC_STAT, &stat);
220
+ fprintf(stderr, "cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);
221
+ }
222
+ }
141
223
 
142
224
  static inline void
143
225
  SEND_NAMES(ID mid, VALUE klass)
@@ -147,10 +229,10 @@ SEND_NAMES(ID mid, VALUE klass)
147
229
 
148
230
  if (!st_is_member(rbtracer.mid_tbl, mid)) {
149
231
  st_insert(rbtracer.mid_tbl, (st_data_t)mid, (st_data_t)1);
150
- SEND_EVENT(
151
- "mid,%lu,%s",
152
- mid,
153
- rb_id2name(mid)
232
+ SEND_EVENT(2,
233
+ "mid",
234
+ 'l', mid,
235
+ 's', rb_id2name(mid)
154
236
  );
155
237
  }
156
238
 
@@ -159,10 +241,10 @@ SEND_NAMES(ID mid, VALUE klass)
159
241
 
160
242
  if (!st_is_member(rbtracer.klass_tbl, klass)) {
161
243
  st_insert(rbtracer.klass_tbl, (st_data_t)klass, (st_data_t)1);
162
- SEND_EVENT(
163
- "klass,%p,%s",
164
- (void*)klass,
165
- rb_class2name(klass)
244
+ SEND_EVENT(2,
245
+ "klass",
246
+ 'l', klass,
247
+ 's', rb_class2name(klass)
166
248
  );
167
249
  }
168
250
  }
@@ -231,14 +313,13 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
231
313
 
232
314
  if (diff > rbtracer.threshold * 1e3) {
233
315
  SEND_NAMES(mid, singleton ? self : klass);
234
- SEND_EVENT(
235
- "%s,-1,%" PRIu64 ",%d,%lu,%d,%p",
316
+ SEND_EVENT(5,
236
317
  event == RUBY_EVENT_RETURN ? "slow" : "cslow",
237
- diff,
238
- rbtracer.num_calls,
239
- mid,
240
- singleton,
241
- (void*)(singleton ? self : klass)
318
+ 'u', diff,
319
+ 'u', rbtracer.num_calls,
320
+ 'l', mid,
321
+ 'b', singleton,
322
+ 'l', singleton ? self : klass
242
323
  );
243
324
  }
244
325
 
@@ -275,13 +356,13 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
275
356
  case RUBY_EVENT_CALL:
276
357
  case RUBY_EVENT_C_CALL:
277
358
  SEND_NAMES(mid, singleton ? self : klass);
278
- SEND_EVENT(
279
- "%s,%d,%lu,%d,%p",
359
+ SEND_EVENT(5,
280
360
  event == RUBY_EVENT_CALL ? "call" : "ccall",
281
- tracer ? tracer->id : 255, // hax
282
- mid,
283
- singleton,
284
- (void*)(singleton ? self : klass)
361
+ 't',
362
+ 'd', tracer ? tracer->id : 255, // hax
363
+ 'l', mid,
364
+ 'b', singleton,
365
+ 'l', singleton ? self : klass
285
366
  );
286
367
 
287
368
  if (tracer && tracer->num_exprs) {
@@ -312,13 +393,12 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
312
393
  result = RSTRING_PTR(val);
313
394
  }
314
395
 
315
- if (result) {
316
- SEND_EVENT(
317
- "%s,%d,%d,%s",
396
+ if (result && *result) {
397
+ SEND_EVENT(3,
318
398
  "exprval",
319
- tracer->id,
320
- i,
321
- result
399
+ 'd', tracer->id,
400
+ 'd', i,
401
+ 's', result
322
402
  );
323
403
  }
324
404
  }
@@ -327,10 +407,10 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
327
407
 
328
408
  case RUBY_EVENT_RETURN:
329
409
  case RUBY_EVENT_C_RETURN:
330
- SEND_EVENT(
331
- "%s,%d",
410
+ SEND_EVENT(2,
332
411
  event == RUBY_EVENT_RETURN ? "return" : "creturn",
333
- tracer ? tracer->id : 255 // hax
412
+ 't',
413
+ 'd', tracer ? tracer->id : 255 // hax
334
414
  );
335
415
  break;
336
416
  }
@@ -406,10 +486,10 @@ rbtracer_remove(char *query, int id)
406
486
  }
407
487
 
408
488
  out:
409
- SEND_EVENT(
410
- "remove,%d,%s",
411
- tracer_id,
412
- query
489
+ SEND_EVENT(2,
490
+ "remove",
491
+ 'd', tracer_id,
492
+ 's', query
413
493
  );
414
494
  return tracer_id;
415
495
  }
@@ -498,10 +578,10 @@ rbtracer_add(char *query)
498
578
  rbtracer.num++;
499
579
 
500
580
  out:
501
- SEND_EVENT(
502
- "add,%d,%s",
503
- tracer_id,
504
- query
581
+ SEND_EVENT(2,
582
+ "add",
583
+ 'd', tracer_id,
584
+ 's', query
505
585
  );
506
586
  return tracer_id;
507
587
  }
@@ -526,11 +606,11 @@ rbtracer_add_expr(int id, char *expr)
526
606
  }
527
607
 
528
608
  out:
529
- SEND_EVENT(
530
- "newexpr,%d,%d,%s",
531
- tracer_id,
532
- expr_id,
533
- expr
609
+ SEND_EVENT(3,
610
+ "newexpr",
611
+ 'd', tracer_id,
612
+ 'd', expr_id,
613
+ 's', expr
534
614
  );
535
615
  }
536
616
 
@@ -579,7 +659,7 @@ ruby_teardown(VALUE data)
579
659
  msgq_teardown();
580
660
  }
581
661
 
582
- static void
662
+ static inline void
583
663
  msgq_setup()
584
664
  {
585
665
  pid_t pid = getpid();
@@ -603,22 +683,6 @@ msgq_setup()
603
683
 
604
684
  if (rbtracer.mqi_id == -1)
605
685
  fprintf(stderr, "msgget() failed to create msgq\n");
606
-
607
- /*
608
- struct msqid_ds stat;
609
- int ret;
610
-
611
- msgctl(rbtracer.mqo_id, IPC_STAT, &stat);
612
- printf("cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);
613
-
614
- stat.msg_qbytes += 10;
615
- ret = msgctl(rbtracer.mqo_id, IPC_SET, &stat);
616
- printf("cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);
617
- printf("ret: %d, errno: %d\n", ret, errno);
618
-
619
- msgctl(rbtracer.mqo_id, IPC_STAT, &stat);
620
- printf("cbytes: %lu, qbytes: %lu, qnum: %lu\n", stat.msg_cbytes, stat.msg_qbytes, stat.msg_qnum);
621
- */
622
686
  }
623
687
 
624
688
  static void
@@ -628,9 +692,7 @@ sigurg(int signal)
628
692
  msgq_setup();
629
693
  if (rbtracer.mqi_id == -1) return;
630
694
 
631
- struct event_msg msg;
632
- char *query = NULL;
633
- size_t len = 0;
695
+ event_msg_t msg;
634
696
  int n = 0;
635
697
 
636
698
  while (true) {
@@ -642,61 +704,87 @@ sigurg(int signal)
642
704
  if (ret == -1) {
643
705
  break;
644
706
  } else {
645
- len = strlen(msg.buf);
646
- if (msg.buf[len-1] == '\n')
647
- msg.buf[len-1] = 0;
648
-
649
- if (0 == strncmp("add,", msg.buf, 4)) {
650
- query = msg.buf + 4;
651
- last_tracer_id = rbtracer_add(query);
707
+ char query[sizeof(msg.buf)];
652
708
 
653
- } else if (0 == strncmp("del,", msg.buf, 4)) {
654
- query = msg.buf + 4;
655
- rbtracer_remove(query, -1);
709
+ msgpack_object cmd;
710
+ msgpack_object_array ary;
711
+ msgpack_object_raw str;
656
712
 
657
- } else if (0 == strncmp("firehose", msg.buf, 8)) {
658
- rbtracer.firehose = true;
659
- event_hook_install();
713
+ msgpack_unpacked unpacked;
714
+ msgpack_unpacked_init(&unpacked);
660
715
 
661
- } else if (0 == strncmp("addexpr,", msg.buf, 8)) {
662
- query = msg.buf + 8;
663
- rbtracer_add_expr(last_tracer_id, query);
716
+ bool success = msgpack_unpack_next(&unpacked, msg.buf, sizeof(msg.buf), NULL);
717
+ cmd = unpacked.data;
664
718
 
665
- } else if (0 == strncmp("watch,", msg.buf, 6)) {
666
- int msec = 250;
719
+ if (!success || cmd.type != MSGPACK_OBJECT_ARRAY)
720
+ continue;
667
721
 
668
- query = msg.buf + 6;
669
- if (query && *query)
670
- msec = atoi(query);
722
+ ary = cmd.via.array;
671
723
 
672
- rbtracer_watch(msec);
724
+ if (ary.size < 1 ||
725
+ ary.ptr[0].type != MSGPACK_OBJECT_RAW)
726
+ continue;
673
727
 
674
- } else if (0 == strncmp("unwatch", msg.buf, 7)) {
675
- rbtracer_unwatch();
728
+ str = ary.ptr[0].via.raw;
676
729
 
677
- } else if (0 == strncmp("attach,", msg.buf, 7)) {
678
- pid_t pid = 0;
730
+ if (0 == strncmp("attach", str.ptr, str.size)) {
731
+ if (ary.size != 2 ||
732
+ ary.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER)
733
+ continue;
679
734
 
680
- query = msg.buf + 7;
681
- if (query && *query)
682
- pid = (pid_t)atoi(query);
735
+ pid_t pid = (pid_t) ary.ptr[1].via.u64;
683
736
 
684
737
  if (pid && rbtracer.attached_pid == 0)
685
738
  rbtracer.attached_pid = pid;
686
739
 
687
- SEND_EVENT(
688
- "attached,%u",
689
- rbtracer.attached_pid
740
+ SEND_EVENT(1,
741
+ "attached",
742
+ 'u', (uint32_t) rbtracer.attached_pid
690
743
  );
691
744
 
692
- } else if (0 == strncmp("detach", msg.buf, 6)) {
693
- SEND_EVENT(
694
- "detached,%u",
695
- rbtracer.attached_pid
745
+ } else if (0 == strncmp("detach", str.ptr, str.size)) {
746
+ SEND_EVENT(1,
747
+ "detached",
748
+ 'u', (uint32_t) rbtracer.attached_pid
696
749
  );
750
+
697
751
  rbtracer.attached_pid = 0;
698
752
  rbtracer_remove_all();
699
753
 
754
+ } else if (0 == strncmp("watch", str.ptr, str.size)) {
755
+ if (ary.size != 2 ||
756
+ ary.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER)
757
+ continue;
758
+
759
+ unsigned int msec = ary.ptr[1].via.u64;
760
+ rbtracer_watch(msec);
761
+
762
+ } else if (0 == strncmp("firehose", str.ptr, str.size)) {
763
+ rbtracer.firehose = true;
764
+ event_hook_install();
765
+
766
+ } else if (0 == strncmp("add", str.ptr, str.size)) {
767
+ if (ary.size != 2 ||
768
+ ary.ptr[1].type != MSGPACK_OBJECT_RAW)
769
+ continue;
770
+
771
+ str = ary.ptr[1].via.raw;
772
+
773
+ strncpy(query, str.ptr, str.size);
774
+ query[str.size] = 0;
775
+ last_tracer_id = rbtracer_add(query);
776
+
777
+ } else if (0 == strncmp("addexpr", str.ptr, str.size)) {
778
+ if (ary.size != 2 ||
779
+ ary.ptr[1].type != MSGPACK_OBJECT_RAW)
780
+ continue;
781
+
782
+ str = ary.ptr[1].via.raw;
783
+
784
+ strncpy(query, str.ptr, str.size);
785
+ query[str.size] = 0;
786
+ rbtracer_add_expr(last_tracer_id, query);
787
+
700
788
  }
701
789
  }
702
790
  }
@@ -705,6 +793,10 @@ sigurg(int signal)
705
793
  void
706
794
  Init_rbtrace()
707
795
  {
796
+ // setup msgpack
797
+ rbtracer.sbuf = msgpack_sbuffer_new();
798
+ rbtracer.msgpacker = msgpack_packer_new(rbtracer.sbuf, msgpack_sbuffer_write);
799
+
708
800
  // zero out tracer
709
801
  memset(&rbtracer.list, 0, sizeof(rbtracer.list));
710
802
 
Binary file
data/rbtrace.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'rbtrace'
3
- s.version = '0.2.8'
3
+ s.version = '0.3.0'
4
4
  s.homepage = 'http://github.com/tmm1/rbtrace'
5
5
 
6
6
  s.authors = 'Aman Gupta'
@@ -12,8 +12,9 @@ Gem::Specification.new do |s|
12
12
  s.bindir = 'bin'
13
13
  s.executables << 'rbtrace'
14
14
 
15
- s.add_dependency 'ffi', '>= 1.0.5'
15
+ s.add_dependency 'ffi', '>= 1.0.5'
16
16
  s.add_dependency 'trollop', '>= 1.16.2'
17
+ s.add_dependency 'msgpack', '>= 0.4.4'
17
18
 
18
19
  s.summary = 'rbtrace: like strace but for ruby code'
19
20
  s.description = 'rbtrace shows you method calls happening inside another ruby process in real time.'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbtrace
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 8
10
- version: 0.2.8
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Aman Gupta
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-17 00:00:00 -08:00
18
+ date: 2011-02-18 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -50,6 +50,22 @@ dependencies:
50
50
  version: 1.16.2
51
51
  type: :runtime
52
52
  version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: msgpack
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 7
62
+ segments:
63
+ - 0
64
+ - 4
65
+ - 4
66
+ version: 0.4.4
67
+ type: :runtime
68
+ version_requirements: *id003
53
69
  description: rbtrace shows you method calls happening inside another ruby process in real time.
54
70
  email: aman@tmm1.net
55
71
  executables:
@@ -67,6 +83,7 @@ files:
67
83
  - ext/.gitignore
68
84
  - ext/extconf.rb
69
85
  - ext/rbtrace.c
86
+ - ext/src/msgpack-0.5.4.tar.gz
70
87
  - rbtrace.gemspec
71
88
  - server.rb
72
89
  - test.sh