rbtrace 0.3.8 → 0.3.9
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 +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"
|