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 +3 -1
- data/README.md +30 -3
- data/bin/rbtrace +67 -50
- data/ext/.gitignore +5 -0
- data/ext/extconf.rb +33 -0
- data/ext/rbtrace.c +221 -129
- data/ext/src/msgpack-0.5.4.tar.gz +0 -0
- data/rbtrace.gemspec +3 -2
- metadata +22 -5
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rbtrace (0.2.
|
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
|
-
##
|
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 :
|
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.
|
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(
|
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(
|
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(
|
205
|
+
send_cmd(:add, func)
|
201
206
|
|
202
207
|
args.each do |arg|
|
203
|
-
send_cmd(
|
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(
|
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(
|
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(
|
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
|
-
|
325
|
-
|
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
|
-
|
331
|
-
if
|
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
|
-
|
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
|
-
|
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
|
-
|
372
|
+
mid, name = *cmd
|
373
|
+
@methods[mid] = name
|
352
374
|
|
353
375
|
when 'klass'
|
354
|
-
|
376
|
+
kid, name = *cmd
|
377
|
+
@klasses[kid] = name
|
355
378
|
|
356
379
|
when 'add'
|
357
|
-
|
358
|
-
|
359
|
-
|
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(
|
384
|
+
@tracers.delete(tracer_id)
|
385
|
+
@tracers[tracer_id][:query] = query
|
370
386
|
end
|
371
387
|
|
372
388
|
when 'newexpr'
|
373
|
-
expr_id, expr = *
|
374
|
-
|
389
|
+
tracer_id, expr_id, expr = *cmd
|
390
|
+
tracer = @tracers[tracer_id]
|
375
391
|
|
376
|
-
if expr_id > -1
|
392
|
+
if expr_id > -1
|
377
393
|
tracer[:exprs][expr_id] = expr
|
378
394
|
end
|
379
395
|
|
380
396
|
when 'exprval'
|
381
|
-
expr_id =
|
382
|
-
|
383
|
-
|
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
|
-
|
398
|
-
|
399
|
-
|
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[
|
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,
|
453
|
-
nesting = nesting.to_i
|
454
|
-
diff = diff.to_i
|
472
|
+
diff, nesting, mid, is_singleton, klass = *cmd
|
455
473
|
|
456
|
-
|
457
|
-
klass = @klasses[klass.to_i(16)]
|
474
|
+
klass = @klasses[klass]
|
458
475
|
name = klass ? "#{klass}#{ is_singleton ? '.' : '#' }" : ''
|
459
|
-
name += @methods[
|
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: #{
|
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.
|
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
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
|
54
|
+
#define BUF_SIZE 64
|
53
55
|
#endif
|
54
56
|
|
55
|
-
struct
|
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
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
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
|
164
|
-
|
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
|
-
|
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
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
654
|
-
|
655
|
-
|
709
|
+
msgpack_object cmd;
|
710
|
+
msgpack_object_array ary;
|
711
|
+
msgpack_object_raw str;
|
656
712
|
|
657
|
-
|
658
|
-
|
659
|
-
event_hook_install();
|
713
|
+
msgpack_unpacked unpacked;
|
714
|
+
msgpack_unpacked_init(&unpacked);
|
660
715
|
|
661
|
-
|
662
|
-
|
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
|
-
|
666
|
-
|
719
|
+
if (!success || cmd.type != MSGPACK_OBJECT_ARRAY)
|
720
|
+
continue;
|
667
721
|
|
668
|
-
|
669
|
-
if (query && *query)
|
670
|
-
msec = atoi(query);
|
722
|
+
ary = cmd.via.array;
|
671
723
|
|
672
|
-
|
724
|
+
if (ary.size < 1 ||
|
725
|
+
ary.ptr[0].type != MSGPACK_OBJECT_RAW)
|
726
|
+
continue;
|
673
727
|
|
674
|
-
|
675
|
-
rbtracer_unwatch();
|
728
|
+
str = ary.ptr[0].via.raw;
|
676
729
|
|
677
|
-
|
678
|
-
|
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
|
-
|
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
|
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",
|
693
|
-
SEND_EVENT(
|
694
|
-
"detached
|
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.
|
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',
|
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:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
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-
|
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
|