rbtrace 0.3.8 → 0.3.9
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/bin/rbtrace +57 -14
- data/ext/extconf.rb +1 -1
- data/ext/rbtrace.c +13 -6
- data/rbtrace.gemspec +1 -1
- data/test.sh +2 -1
- data/tracers/testunit.tracer +1 -0
- metadata +12 -5
data/Gemfile.lock
CHANGED
data/bin/rbtrace
CHANGED
@@ -123,6 +123,9 @@ class RBTracer
|
|
123
123
|
# Public: The IO where tracing output is written (default: STDOUT).
|
124
124
|
attr_accessor :out
|
125
125
|
|
126
|
+
# Public: The timeout before giving up on attaching/detaching to a process.
|
127
|
+
attr_accessor :timeout
|
128
|
+
|
126
129
|
# The String prefix used on nested method calls (default: ' ').
|
127
130
|
attr_accessor :prefix
|
128
131
|
|
@@ -176,7 +179,10 @@ class RBTracer
|
|
176
179
|
@max_nesting = @last_nesting = @nesting = 0
|
177
180
|
@last_tracer = nil
|
178
181
|
|
182
|
+
@timeout = 5
|
183
|
+
|
179
184
|
@out = STDOUT
|
185
|
+
@out.sync = true
|
180
186
|
@prefix = ' '
|
181
187
|
@printed_newline = true
|
182
188
|
|
@@ -216,7 +222,7 @@ class RBTracer
|
|
216
222
|
# Returns a Fixnum pid.
|
217
223
|
def fork
|
218
224
|
send_cmd(:fork)
|
219
|
-
if
|
225
|
+
if wait('for fork', 30){ !!@forked_pid }
|
220
226
|
@forked_pid
|
221
227
|
else
|
222
228
|
STDERR.puts '*** timed out waiting for fork'
|
@@ -233,7 +239,7 @@ class RBTracer
|
|
233
239
|
|
234
240
|
send_cmd(:eval, code)
|
235
241
|
|
236
|
-
if
|
242
|
+
if wait('for eval response', 15){ !!@eval_result }
|
237
243
|
@eval_result
|
238
244
|
else
|
239
245
|
STDERR.puts '*** timed out waiting for eval response'
|
@@ -247,28 +253,42 @@ class RBTracer
|
|
247
253
|
send_cmd(:gc)
|
248
254
|
end
|
249
255
|
|
256
|
+
# Restrict slow tracing to a specific list of methods.
|
257
|
+
#
|
258
|
+
# methods - The String or Array of method selectors.
|
259
|
+
#
|
260
|
+
# Returns nothing.
|
261
|
+
def add_slow(methods)
|
262
|
+
add(methods, true)
|
263
|
+
end
|
264
|
+
|
250
265
|
# Add tracers for the given list of methods.
|
251
266
|
#
|
252
267
|
# methods - The String or Array of method selectors to trace.
|
253
268
|
#
|
254
269
|
# Returns nothing.
|
255
|
-
def add(methods)
|
270
|
+
def add(methods, slow=false)
|
256
271
|
Array(methods).each do |func|
|
257
272
|
func = func.strip
|
258
273
|
next if func.empty?
|
259
274
|
|
260
|
-
if func =~ /^(
|
275
|
+
if func =~ /^(.+?)\((.+)\)$/
|
261
276
|
name, args = $1, $2
|
262
277
|
args = args.split(',').map{ |a| a.strip }
|
263
278
|
end
|
264
279
|
|
265
|
-
send_cmd(:add, name || func)
|
280
|
+
send_cmd(:add, name || func, slow)
|
266
281
|
|
267
282
|
if args and args.any?
|
268
283
|
args.each do |arg|
|
269
284
|
if (err = valid_syntax?(arg)) != true
|
270
285
|
raise ArgumentError, "#{err.class} for expression #{arg.inspect} in method #{func.inspect}"
|
271
286
|
end
|
287
|
+
if arg =~ /^@/ and arg !~ /^@[_a-z][_a-z0-9]+$/i
|
288
|
+
# arg[0]=='@' means ivar, but if this is an expr
|
289
|
+
# we can hack a space in front so it gets eval'd instead
|
290
|
+
arg = " #{arg}"
|
291
|
+
end
|
272
292
|
send_cmd(:addexpr, arg)
|
273
293
|
end
|
274
294
|
end
|
@@ -280,7 +300,7 @@ class RBTracer
|
|
280
300
|
# Returns nothing.
|
281
301
|
def attach
|
282
302
|
send_cmd(:attach, Process.pid)
|
283
|
-
if
|
303
|
+
if wait('to attach'){ @attached == true }
|
284
304
|
STDERR.puts "*** attached to process #{pid}"
|
285
305
|
else
|
286
306
|
raise ArgumentError, 'process already being traced?'
|
@@ -298,7 +318,7 @@ class RBTracer
|
|
298
318
|
|
299
319
|
newline
|
300
320
|
|
301
|
-
if
|
321
|
+
if wait('to detach cleanly'){ @attached == false }
|
302
322
|
newline
|
303
323
|
STDERR.puts "*** detached from process #{pid}"
|
304
324
|
else
|
@@ -358,13 +378,18 @@ class RBTracer
|
|
358
378
|
# block - The Block that is checked every 50ms until it returns true.
|
359
379
|
#
|
360
380
|
# Returns true when the condition was met, or false on a timeout.
|
361
|
-
def
|
381
|
+
def wait(reason, time=@timeout)
|
362
382
|
wait = 0.05 # polling interval
|
363
383
|
|
364
384
|
(time/wait).to_i.times do
|
365
385
|
begin
|
366
386
|
recv_lines
|
367
387
|
sleep(wait)
|
388
|
+
begin
|
389
|
+
signal
|
390
|
+
rescue Errno::ESRCH
|
391
|
+
break
|
392
|
+
end
|
368
393
|
time -= wait
|
369
394
|
|
370
395
|
return true if yield
|
@@ -491,7 +516,7 @@ class RBTracer
|
|
491
516
|
tracer = @tracers[tracer_id]
|
492
517
|
|
493
518
|
if expr_id > -1
|
494
|
-
tracer[:exprs][expr_id] = expr
|
519
|
+
tracer[:exprs][expr_id] = expr.strip
|
495
520
|
end
|
496
521
|
|
497
522
|
when 'exprval'
|
@@ -554,7 +579,7 @@ class RBTracer
|
|
554
579
|
print ')' if @last_tracer and @last_tracer[:arglist]
|
555
580
|
|
556
581
|
unless tracer == @last_tracer and @last_tracer[:last] == name
|
557
|
-
|
582
|
+
newline
|
558
583
|
print ' '*16 if @show_time
|
559
584
|
print @prefix*@nesting if @nesting > 0
|
560
585
|
print name
|
@@ -578,6 +603,9 @@ class RBTracer
|
|
578
603
|
name = klass ? "#{klass}#{ is_singleton ? '.' : '#' }" : ''
|
579
604
|
name += @methods[mid] || '(unknown)'
|
580
605
|
|
606
|
+
newline
|
607
|
+
nesting = @nesting if @nesting > 0
|
608
|
+
|
581
609
|
if @show_time
|
582
610
|
t = Time.at(time/1_000_000)
|
583
611
|
print t.strftime("%H:%M:%S.")
|
@@ -599,7 +627,7 @@ class RBTracer
|
|
599
627
|
when 'gc'
|
600
628
|
time, = *cmd
|
601
629
|
|
602
|
-
|
630
|
+
newline
|
603
631
|
if @show_time
|
604
632
|
t = Time.at(time/1_000_000)
|
605
633
|
print t.strftime("%H:%M:%S.")
|
@@ -625,7 +653,7 @@ class RBTracer
|
|
625
653
|
parser = Trollop::Parser.new do
|
626
654
|
version <<-EOS
|
627
655
|
rbtrace: like strace, but for ruby code
|
628
|
-
version 0.3.
|
656
|
+
version 0.3.9
|
629
657
|
(c) 2011 Aman Gupta (tmm1)
|
630
658
|
http://github.com/tmm1/rbtrace
|
631
659
|
EOS
|
@@ -690,6 +718,10 @@ EOS
|
|
690
718
|
:default => 250,
|
691
719
|
:short => '-s'
|
692
720
|
|
721
|
+
opt :slow_methods,
|
722
|
+
"method(s) to restrict --slow to",
|
723
|
+
:type => :strings
|
724
|
+
|
693
725
|
opt :methods,
|
694
726
|
"method(s) to trace (valid formats: sleep String#gsub Process.pid Kernel# Dir.)",
|
695
727
|
:type => :strings,
|
@@ -736,6 +768,10 @@ EOS
|
|
736
768
|
"evaluate a ruby expression in the process",
|
737
769
|
:type => String,
|
738
770
|
:short => '-e'
|
771
|
+
|
772
|
+
opt :timeout,
|
773
|
+
"seconds to wait before giving up on attach/detach",
|
774
|
+
:default => 5
|
739
775
|
end
|
740
776
|
|
741
777
|
opts = Trollop.with_standard_exception_handling(parser) do
|
@@ -753,11 +789,14 @@ EOS
|
|
753
789
|
parser.die :fork, '(can only be invoked with one pid)'
|
754
790
|
end
|
755
791
|
|
756
|
-
methods = []
|
792
|
+
methods, smethods = [], []
|
757
793
|
|
758
794
|
if opts[:methods_given]
|
759
795
|
methods += opts[:methods]
|
760
796
|
end
|
797
|
+
if opts[:slow_methods_given]
|
798
|
+
smethods += opts[:slow_methods]
|
799
|
+
end
|
761
800
|
|
762
801
|
if opts[:config_given]
|
763
802
|
Array(opts[:config]).each do |config|
|
@@ -851,6 +890,7 @@ EOS
|
|
851
890
|
|
852
891
|
else
|
853
892
|
tracer.out = output if output
|
893
|
+
tracer.timeout = opts[:timeout] if opts[:timeout] > 0
|
854
894
|
tracer.prefix = ' ' * opts[:prefix]
|
855
895
|
tracer.show_time = opts[:start_time]
|
856
896
|
tracer.show_duration = !opts[:no_duration]
|
@@ -862,7 +902,10 @@ EOS
|
|
862
902
|
tracer.firehose
|
863
903
|
else
|
864
904
|
tracer.add(methods) if methods.any?
|
865
|
-
|
905
|
+
if opts[:slow_given]
|
906
|
+
tracer.watch(opts[:slow])
|
907
|
+
tracer.add_slow(smethods) if smethods.any?
|
908
|
+
end
|
866
909
|
end
|
867
910
|
begin
|
868
911
|
tracer.recv_loop
|
data/ext/extconf.rb
CHANGED
@@ -22,7 +22,7 @@ unless File.exists?("#{CWD}/dst/lib/libmsgpackc.a")
|
|
22
22
|
|
23
23
|
sys("tar zxvf #{msgpack}")
|
24
24
|
Dir.chdir(dir) do
|
25
|
-
sys("./configure --disable-shared --with-pic --prefix=#{CWD}/dst/")
|
25
|
+
sys("./configure --disable-shared --disable-cxx --with-pic --prefix=#{CWD}/dst/")
|
26
26
|
sys("make install")
|
27
27
|
end
|
28
28
|
end
|
data/ext/rbtrace.c
CHANGED
@@ -54,6 +54,7 @@ timeofday_usec()
|
|
54
54
|
typedef struct {
|
55
55
|
int id;
|
56
56
|
char *query;
|
57
|
+
bool is_slow;
|
57
58
|
|
58
59
|
char *klass_name;
|
59
60
|
size_t klass_len;
|
@@ -331,6 +332,9 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
|
331
332
|
if (curr->query) {
|
332
333
|
n++;
|
333
334
|
|
335
|
+
// there should never be slow method tracers outside slow mode
|
336
|
+
if (!rbtracer.slow && curr->is_slow) continue;
|
337
|
+
|
334
338
|
if (rbtracer.devmode) {
|
335
339
|
if ((!curr->mid || curr->mid == mid) &&
|
336
340
|
(!curr->klass_name || (
|
@@ -366,7 +370,7 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
|
366
370
|
}
|
367
371
|
|
368
372
|
// are we watching for slow method calls?
|
369
|
-
if (rbtracer.slow) {
|
373
|
+
if (rbtracer.slow && (!tracer || tracer->is_slow)) {
|
370
374
|
uint64_t usec = timeofday_usec(), diff = 0;
|
371
375
|
|
372
376
|
switch (event) {
|
@@ -481,7 +485,7 @@ event_hook_install()
|
|
481
485
|
event_hook,
|
482
486
|
RUBY_EVENT_CALL | RUBY_EVENT_C_CALL |
|
483
487
|
RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN
|
484
|
-
#ifdef
|
488
|
+
#ifdef RUBY_VM
|
485
489
|
, 0
|
486
490
|
#endif
|
487
491
|
);
|
@@ -575,7 +579,7 @@ rbtracer_detach()
|
|
575
579
|
}
|
576
580
|
|
577
581
|
static int
|
578
|
-
rbtracer_add(char *query)
|
582
|
+
rbtracer_add(char *query, bool is_slow)
|
579
583
|
{
|
580
584
|
int i;
|
581
585
|
int tracer_id = -1;
|
@@ -654,6 +658,7 @@ rbtracer_add(char *query)
|
|
654
658
|
|
655
659
|
tracer->id = tracer_id;
|
656
660
|
tracer->query = strdup(query);
|
661
|
+
tracer->is_slow = is_slow;
|
657
662
|
|
658
663
|
if (klass_end != klass_begin) {
|
659
664
|
tracer->klass_name = tracer->query + klass_begin;
|
@@ -834,15 +839,17 @@ rbtrace__process_event(msgpack_object cmd)
|
|
834
839
|
event_hook_install();
|
835
840
|
|
836
841
|
} else if (0 == strncmp("add", str.ptr, str.size)) {
|
837
|
-
if (ary.size !=
|
838
|
-
ary.ptr[1].type != MSGPACK_OBJECT_RAW
|
842
|
+
if (ary.size != 3 ||
|
843
|
+
ary.ptr[1].type != MSGPACK_OBJECT_RAW ||
|
844
|
+
ary.ptr[2].type != MSGPACK_OBJECT_BOOLEAN)
|
839
845
|
return;
|
840
846
|
|
841
847
|
str = ary.ptr[1].via.raw;
|
848
|
+
bool is_slow = ary.ptr[2].via.boolean;
|
842
849
|
|
843
850
|
strncpy(query, str.ptr, str.size);
|
844
851
|
query[str.size] = 0;
|
845
|
-
last_tracer_id = rbtracer_add(query);
|
852
|
+
last_tracer_id = rbtracer_add(query, is_slow);
|
846
853
|
|
847
854
|
} else if (0 == strncmp("addexpr", str.ptr, str.size)) {
|
848
855
|
if (ary.size != 2 ||
|
data/rbtrace.gemspec
CHANGED
data/test.sh
CHANGED
@@ -38,7 +38,8 @@ trace -m "String#gsub(self,@test)" "String#*(self,__source__)" "String#multiply_
|
|
38
38
|
trace --gc --slow=200
|
39
39
|
trace --gc -m Dir.
|
40
40
|
trace --slow=250
|
41
|
-
trace --slow=250 -
|
41
|
+
trace --slow=250 --slow-methods sleep
|
42
|
+
trace --gc -m Dir. --slow=250 --slow-methods sleep
|
42
43
|
trace --firehose
|
43
44
|
|
44
45
|
cleanup
|
@@ -0,0 +1 @@
|
|
1
|
+
Test::Unit::TestSuite#run(name, size)
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbtrace
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 1
|
5
|
+
prerelease:
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 3
|
8
|
-
-
|
9
|
-
version: 0.3.
|
9
|
+
- 9
|
10
|
+
version: 0.3.9
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Aman Gupta
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2011-02-
|
18
|
+
date: 2011-02-25 00:00:00 -08:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
@@ -25,6 +26,7 @@ dependencies:
|
|
25
26
|
requirements:
|
26
27
|
- - ">="
|
27
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 29
|
28
30
|
segments:
|
29
31
|
- 1
|
30
32
|
- 0
|
@@ -40,6 +42,7 @@ dependencies:
|
|
40
42
|
requirements:
|
41
43
|
- - ">="
|
42
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 83
|
43
46
|
segments:
|
44
47
|
- 1
|
45
48
|
- 16
|
@@ -55,6 +58,7 @@ dependencies:
|
|
55
58
|
requirements:
|
56
59
|
- - ">="
|
57
60
|
- !ruby/object:Gem::Version
|
61
|
+
hash: 9
|
58
62
|
segments:
|
59
63
|
- 0
|
60
64
|
- 4
|
@@ -88,6 +92,7 @@ files:
|
|
88
92
|
- tracers/io.tracer
|
89
93
|
- tracers/mongo.tracer
|
90
94
|
- tracers/redis.tracer
|
95
|
+
- tracers/testunit.tracer
|
91
96
|
- tracers/unicorn.tracer
|
92
97
|
has_rdoc: true
|
93
98
|
homepage: http://github.com/tmm1/rbtrace
|
@@ -103,6 +108,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
108
|
requirements:
|
104
109
|
- - ">="
|
105
110
|
- !ruby/object:Gem::Version
|
111
|
+
hash: 3
|
106
112
|
segments:
|
107
113
|
- 0
|
108
114
|
version: "0"
|
@@ -111,13 +117,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
117
|
requirements:
|
112
118
|
- - ">="
|
113
119
|
- !ruby/object:Gem::Version
|
120
|
+
hash: 3
|
114
121
|
segments:
|
115
122
|
- 0
|
116
123
|
version: "0"
|
117
124
|
requirements: []
|
118
125
|
|
119
126
|
rubyforge_project:
|
120
|
-
rubygems_version: 1.
|
127
|
+
rubygems_version: 1.4.2
|
121
128
|
signing_key:
|
122
129
|
specification_version: 3
|
123
130
|
summary: "rbtrace: like strace but for ruby code"
|