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 +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
|