rbtrace 0.2.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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