rbtrace 0.3.4 → 0.3.5
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/README.md +6 -5
- data/bin/rbtrace +118 -43
- data/ext/rbtrace.c +159 -89
- data/rbtrace.gemspec +1 -1
- data/server.rb +13 -0
- data/test.sh +2 -0
- data/tracers/unicorn.tracer +1 -0
- metadata +5 -4
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -186,15 +186,16 @@ for popular ruby libraries and functions.
|
|
186
186
|
|
187
187
|
## todo
|
188
188
|
|
189
|
-
*
|
189
|
+
* write some real tests (that assert things)
|
190
|
+
* more expressive selectors (.*Controller#)
|
191
|
+
* include date in show time (-t)
|
192
|
+
* add --eval
|
193
|
+
* add --forkgdb
|
194
|
+
* add --strace mode/expression
|
190
195
|
* add triggers to start tracing slow methods only inside another method
|
191
196
|
* add watch expressions to fire tracers only when an expression is true
|
192
|
-
* syntax check expressions before adding them
|
193
197
|
* add special expressions for method args (_arg0_, _arguments_)
|
194
198
|
* optimize local variable lookup to avoid instance_eval
|
195
199
|
* run process via bin/rbtrace (to trace rubygems and bootup time)
|
196
|
-
* let bin/rbtrace attach to multiple pids
|
197
|
-
* how to select on multiple msgrcv() targets?
|
198
|
-
* prefix pid to output in multiple pid mode
|
199
200
|
* investigate mach_msg on osx since msgget(2) has hard kernel limits
|
200
201
|
|
data/bin/rbtrace
CHANGED
@@ -9,8 +9,13 @@ class String
|
|
9
9
|
end unless ''.respond_to?(:bytesize)
|
10
10
|
|
11
11
|
module FFI::LastError
|
12
|
+
Errnos = Errno::constants.map(&Errno.method(:const_get)).inject({}) do |hash, c|
|
13
|
+
hash[ c.const_get(:Errno) ] = c
|
14
|
+
hash
|
15
|
+
end
|
16
|
+
|
12
17
|
def self.exception
|
13
|
-
|
18
|
+
Errnos[error]
|
14
19
|
end
|
15
20
|
def self.raise(msg=nil)
|
16
21
|
Kernel.raise exception, msg
|
@@ -189,6 +194,13 @@ class RBTracer
|
|
189
194
|
send_cmd(:firehose)
|
190
195
|
end
|
191
196
|
|
197
|
+
# Turn on dev mode.
|
198
|
+
#
|
199
|
+
# Returns nothing.
|
200
|
+
def devmode
|
201
|
+
send_cmd(:devmode)
|
202
|
+
end
|
203
|
+
|
192
204
|
# Turn on GC tracing.
|
193
205
|
#
|
194
206
|
# Returns nothing.
|
@@ -207,15 +219,20 @@ class RBTracer
|
|
207
219
|
next if func.empty?
|
208
220
|
|
209
221
|
if func =~ /^(.+)\((.+)\)$/
|
210
|
-
|
222
|
+
name, args = $1, $2
|
211
223
|
args = args.split(',').map{ |a| a.strip }
|
212
224
|
end
|
213
225
|
|
214
|
-
send_cmd(:add, func)
|
226
|
+
send_cmd(:add, name || func)
|
215
227
|
|
216
|
-
args
|
217
|
-
|
218
|
-
|
228
|
+
if args and args.any?
|
229
|
+
args.each do |arg|
|
230
|
+
if (err = valid_syntax?(arg)) != true
|
231
|
+
raise ArgumentError, "#{err.class} for expression #{arg.inspect} in method #{func.inspect}"
|
232
|
+
end
|
233
|
+
send_cmd(:addexpr, arg)
|
234
|
+
end
|
235
|
+
end
|
219
236
|
end
|
220
237
|
end
|
221
238
|
|
@@ -252,6 +269,10 @@ class RBTracer
|
|
252
269
|
rescue Errno::EINVAL, Errno::EIDRM
|
253
270
|
puts
|
254
271
|
STDERR.puts "*** process #{pid} is gone"
|
272
|
+
STDERR.puts "*** #{$!.inspect}"
|
273
|
+
STDERR.puts $!.backtrace.join("\n ")
|
274
|
+
rescue Interrupt, SignalException
|
275
|
+
retry
|
255
276
|
end
|
256
277
|
|
257
278
|
# Process events from the traced process.
|
@@ -328,6 +349,14 @@ class RBTracer
|
|
328
349
|
retry
|
329
350
|
end
|
330
351
|
|
352
|
+
def valid_syntax?(code)
|
353
|
+
begin
|
354
|
+
eval("#{code}\nBEGIN {return true}", nil, 'rbtrace_expression', 0)
|
355
|
+
rescue Exception => e
|
356
|
+
e
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
331
360
|
def print(*args)
|
332
361
|
@out.print(*args)
|
333
362
|
end
|
@@ -499,8 +528,9 @@ class RBTracer
|
|
499
528
|
print "<%f>" % (diff/1_000_000.0)
|
500
529
|
end
|
501
530
|
puts
|
502
|
-
puts if nesting == 0
|
531
|
+
puts if nesting == 0 and @max_nesting > 1
|
503
532
|
|
533
|
+
@max_nesting = nesting if nesting > @max_nesting
|
504
534
|
@last_nesting = nesting
|
505
535
|
|
506
536
|
when 'gc'
|
@@ -532,7 +562,7 @@ class RBTracer
|
|
532
562
|
parser = Trollop::Parser.new do
|
533
563
|
version <<-EOS
|
534
564
|
rbtrace: like strace, but for ruby code
|
535
|
-
version 0.3.
|
565
|
+
version 0.3.5
|
536
566
|
(c) 2011 Aman Gupta (tmm1)
|
537
567
|
http://github.com/tmm1/rbtrace
|
538
568
|
EOS
|
@@ -585,7 +615,7 @@ All Options:\n
|
|
585
615
|
EOS
|
586
616
|
opt :pid,
|
587
617
|
"pid of the ruby process to trace",
|
588
|
-
:type => :
|
618
|
+
:type => :ints,
|
589
619
|
:short => '-p'
|
590
620
|
|
591
621
|
opt :firehose,
|
@@ -632,6 +662,9 @@ EOS
|
|
632
662
|
"config file",
|
633
663
|
:type => :strings,
|
634
664
|
:short => '-c'
|
665
|
+
|
666
|
+
opt :devmode,
|
667
|
+
"assume the ruby process is reloading classes and methods"
|
635
668
|
end
|
636
669
|
|
637
670
|
opts = Trollop.with_standard_exception_handling(parser) do
|
@@ -639,36 +672,23 @@ EOS
|
|
639
672
|
parser.parse(ARGV)
|
640
673
|
end
|
641
674
|
|
642
|
-
|
643
|
-
|
644
|
-
|
675
|
+
unless %w[ slow firehose methods config gc ].find{ |n| opts[:"#{n}_given"] }
|
676
|
+
$stderr.puts "Error: --slow, --gc, --firehose, --methods or --config required."
|
677
|
+
$stderr.puts "Try --help for help."
|
678
|
+
exit(-1)
|
645
679
|
end
|
646
680
|
|
647
|
-
|
648
|
-
firehose = nil
|
649
|
-
methods = nil
|
650
|
-
gc = nil
|
681
|
+
methods = []
|
651
682
|
|
652
|
-
if opts[:
|
653
|
-
|
683
|
+
if opts[:methods_given]
|
684
|
+
methods += opts[:methods]
|
654
685
|
end
|
655
686
|
|
656
|
-
if opts[:
|
657
|
-
slow = opts[:slow]
|
658
|
-
|
659
|
-
elsif opts[:firehose]
|
660
|
-
firehose = true
|
661
|
-
|
662
|
-
elsif opts[:methods]
|
663
|
-
methods = opts[:methods]
|
664
|
-
|
665
|
-
elsif opts[:config_given]
|
666
|
-
methods = []
|
667
|
-
|
687
|
+
if opts[:config_given]
|
668
688
|
Array(opts[:config]).each do |config|
|
669
689
|
file = [
|
670
690
|
config,
|
671
|
-
File.expand_path("../../tracers/#{config}.tracer",__FILE__)
|
691
|
+
File.expand_path("../../tracers/#{config}.tracer", __FILE__)
|
672
692
|
].find{ |f| File.exists?(f) }
|
673
693
|
|
674
694
|
unless file
|
@@ -683,30 +703,85 @@ EOS
|
|
683
703
|
methods << line
|
684
704
|
end
|
685
705
|
end
|
706
|
+
end
|
686
707
|
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
708
|
+
tracee = nil
|
709
|
+
|
710
|
+
if opts[:pid].size <= 1
|
711
|
+
tracee = opts[:pid].first
|
712
|
+
|
713
|
+
else
|
714
|
+
tracers = []
|
715
|
+
|
716
|
+
opts[:pid].each do |pid|
|
717
|
+
if child = fork
|
718
|
+
tracers << child
|
719
|
+
else
|
720
|
+
Process.setpgrp
|
721
|
+
STDIN.reopen '/dev/null'
|
722
|
+
$0 = "rbtrace -p #{pid} (parent: #{Process.ppid})"
|
723
|
+
|
724
|
+
opts[:output] += ".#{pid}" if opts[:output]
|
725
|
+
tracee = pid
|
726
|
+
|
727
|
+
# fall through and start tracing
|
728
|
+
break
|
729
|
+
end
|
730
|
+
end
|
731
|
+
|
732
|
+
if tracee.nil?
|
733
|
+
# this is the parent
|
734
|
+
while true
|
735
|
+
begin
|
736
|
+
break if tracers.empty?
|
737
|
+
if pid = Process.wait
|
738
|
+
tracers.delete(pid)
|
739
|
+
end
|
740
|
+
rescue Interrupt, SignalException
|
741
|
+
STDERR.puts "*** waiting on child tracers: #{tracers.inspect}"
|
742
|
+
tracers.each do |pid|
|
743
|
+
begin
|
744
|
+
Process.kill 'INT', pid
|
745
|
+
rescue Errno::ESRCH
|
746
|
+
end
|
747
|
+
end
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
751
|
+
exit!
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
755
|
+
if out = opts[:output]
|
756
|
+
output = File.open(out, opts[:append] ? 'a+' : 'w')
|
757
|
+
output.sync = true
|
691
758
|
end
|
692
759
|
|
693
760
|
begin
|
694
761
|
begin
|
695
|
-
tracer = RBTracer.new(
|
762
|
+
tracer = RBTracer.new(tracee)
|
696
763
|
rescue ArgumentError => e
|
697
764
|
parser.die :pid, "(#{e.message})"
|
698
765
|
end
|
699
766
|
|
700
|
-
if
|
767
|
+
if opts[:devmode_given]
|
768
|
+
tracer.devmode
|
769
|
+
end
|
770
|
+
|
771
|
+
if opts[:gc_given]
|
701
772
|
tracer.gc
|
702
773
|
end
|
703
774
|
|
704
|
-
if
|
705
|
-
tracer.watch(slow)
|
706
|
-
elsif firehose
|
775
|
+
if opts[:firehose_given]
|
707
776
|
tracer.firehose
|
708
|
-
|
709
|
-
|
777
|
+
else
|
778
|
+
if methods.any?
|
779
|
+
tracer.add(methods)
|
780
|
+
end
|
781
|
+
|
782
|
+
if opts[:slow_given]
|
783
|
+
tracer.watch(opts[:slow])
|
784
|
+
end
|
710
785
|
end
|
711
786
|
|
712
787
|
if output
|
@@ -719,7 +794,7 @@ EOS
|
|
719
794
|
|
720
795
|
begin
|
721
796
|
tracer.recv_loop
|
722
|
-
rescue Interrupt
|
797
|
+
rescue Interrupt, SignalException
|
723
798
|
end
|
724
799
|
ensure
|
725
800
|
if tracer
|
data/ext/rbtrace.c
CHANGED
@@ -57,8 +57,12 @@ timeofday_usec()
|
|
57
57
|
|
58
58
|
typedef struct {
|
59
59
|
int id;
|
60
|
-
|
61
60
|
char *query;
|
61
|
+
|
62
|
+
char *klass_name;
|
63
|
+
size_t klass_len;
|
64
|
+
bool is_singleton;
|
65
|
+
|
62
66
|
VALUE self;
|
63
67
|
VALUE klass;
|
64
68
|
ID mid;
|
@@ -79,6 +83,7 @@ static struct {
|
|
79
83
|
pid_t attached_pid;
|
80
84
|
|
81
85
|
bool installed;
|
86
|
+
bool devmode;
|
82
87
|
|
83
88
|
bool gc;
|
84
89
|
bool firehose;
|
@@ -106,6 +111,7 @@ rbtracer = {
|
|
106
111
|
.attached_pid = 0,
|
107
112
|
|
108
113
|
.installed = false,
|
114
|
+
.devmode = false,
|
109
115
|
|
110
116
|
.gc = false,
|
111
117
|
.firehose = false,
|
@@ -126,6 +132,10 @@ rbtracer = {
|
|
126
132
|
.msgpacker = NULL
|
127
133
|
};
|
128
134
|
|
135
|
+
static void
|
136
|
+
msgq_teardown(),
|
137
|
+
rbtracer_detach();
|
138
|
+
|
129
139
|
static inline void
|
130
140
|
rbtrace__send_event(int nargs, const char *name, ...)
|
131
141
|
{
|
@@ -160,38 +170,38 @@ rbtrace__send_event(int nargs, const char *name, ...)
|
|
160
170
|
for (n=0; n<nargs; n++) {
|
161
171
|
type = va_arg(ap, int);
|
162
172
|
switch (type) {
|
163
|
-
case '
|
164
|
-
uint64 = va_arg(ap, uint64_t);
|
165
|
-
msgpack_pack_uint64(pk, uint64);
|
166
|
-
break;
|
167
|
-
|
168
|
-
case 'n':
|
169
|
-
msgpack_pack_uint64(pk, timeofday_usec());
|
170
|
-
break;
|
171
|
-
|
172
|
-
case 'b':
|
173
|
+
case 'b': // boolean
|
173
174
|
if (va_arg(ap, int))
|
174
175
|
msgpack_pack_true(pk);
|
175
176
|
else
|
176
177
|
msgpack_pack_false(pk);
|
177
178
|
break;
|
178
179
|
|
179
|
-
case '
|
180
|
+
case 'd': // signed int
|
181
|
+
sint = va_arg(ap, int);
|
182
|
+
msgpack_pack_int(pk, sint);
|
183
|
+
break;
|
184
|
+
|
185
|
+
case 'u': // unsigned int
|
180
186
|
uint = va_arg(ap, uint32_t);
|
181
187
|
msgpack_pack_uint32(pk, uint);
|
182
188
|
break;
|
183
189
|
|
184
|
-
case 'l':
|
190
|
+
case 'l': // unsigned long (VALUE/ID)
|
185
191
|
ulong = va_arg(ap, unsigned long);
|
186
192
|
msgpack_pack_unsigned_long(pk, ulong);
|
187
193
|
break;
|
188
194
|
|
189
|
-
case '
|
190
|
-
|
191
|
-
|
195
|
+
case 't': // unsigned long long (timestamps)
|
196
|
+
uint64 = va_arg(ap, uint64_t);
|
197
|
+
msgpack_pack_uint64(pk, uint64);
|
192
198
|
break;
|
193
199
|
|
194
|
-
case '
|
200
|
+
case 'n': // current timestamp
|
201
|
+
msgpack_pack_uint64(pk, timeofday_usec());
|
202
|
+
break;
|
203
|
+
|
204
|
+
case 's': // string
|
195
205
|
str = va_arg(ap, char *);
|
196
206
|
if (!str)
|
197
207
|
str = (char *)"";
|
@@ -222,7 +232,12 @@ rbtrace__send_event(int nargs, const char *name, ...)
|
|
222
232
|
for (n=0; n<10 && ret==-1; n++)
|
223
233
|
ret = msgsnd(rbtracer.mqo_id, &msg, sizeof(msg)-sizeof(long), IPC_NOWAIT);
|
224
234
|
|
225
|
-
if (ret == -1 &&
|
235
|
+
if (ret == -1 && errno == EINVAL) {
|
236
|
+
fprintf(stderr, "msgsnd(%d): %s [detaching]\n", rbtracer.mqo_id, strerror(errno));
|
237
|
+
|
238
|
+
msgq_teardown();
|
239
|
+
rbtracer_detach();
|
240
|
+
} else if (ret == -1) {
|
226
241
|
fprintf(stderr, "msgsnd(%d): %s\n", rbtracer.mqo_id, strerror(errno));
|
227
242
|
|
228
243
|
struct msqid_ds stat;
|
@@ -259,35 +274,6 @@ rbtrace__send_names(ID mid, VALUE klass)
|
|
259
274
|
}
|
260
275
|
}
|
261
276
|
|
262
|
-
static void
|
263
|
-
rbtracer__resolve_query(char *query, VALUE *klass, VALUE *self, ID *mid)
|
264
|
-
{
|
265
|
-
char *idx = NULL, *method = NULL;
|
266
|
-
|
267
|
-
assert(klass && self && mid);
|
268
|
-
*klass = *self = *mid = 0;
|
269
|
-
|
270
|
-
if (NULL != (idx = rindex(query, '.'))) {
|
271
|
-
*idx = 0;
|
272
|
-
*self = rb_eval_string_protect(query, 0);
|
273
|
-
*idx = '.';
|
274
|
-
|
275
|
-
method = idx+1;
|
276
|
-
} else if (NULL != (idx = rindex(query, '#'))) {
|
277
|
-
*idx = 0;
|
278
|
-
*klass = rb_eval_string_protect(query, 0);
|
279
|
-
*idx = '#';
|
280
|
-
|
281
|
-
method = idx+1;
|
282
|
-
} else {
|
283
|
-
method = query;
|
284
|
-
}
|
285
|
-
|
286
|
-
if (method && *method) {
|
287
|
-
*mid = rb_intern(method);
|
288
|
-
}
|
289
|
-
}
|
290
|
-
|
291
277
|
static int in_event_hook = 0;
|
292
278
|
|
293
279
|
static void
|
@@ -326,7 +312,55 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
|
326
312
|
singleton = FL_TEST(klass, FL_SINGLETON);
|
327
313
|
}
|
328
314
|
|
329
|
-
|
315
|
+
rbtracer_t *tracer = NULL;
|
316
|
+
|
317
|
+
if (rbtracer.firehose) {
|
318
|
+
// trace everything
|
319
|
+
|
320
|
+
} else if (rbtracer.num > 0) {
|
321
|
+
// tracing only specific methods
|
322
|
+
int i, n;
|
323
|
+
for (i=0, n=0; i<MAX_TRACERS && n<rbtracer.num; i++) {
|
324
|
+
rbtracer_t *curr = &rbtracer.list[i];
|
325
|
+
|
326
|
+
if (curr->query) {
|
327
|
+
n++;
|
328
|
+
|
329
|
+
if (rbtracer.devmode) {
|
330
|
+
if ((!curr->mid || curr->mid == mid) &&
|
331
|
+
(!curr->klass_name || (
|
332
|
+
(singleton == curr->is_singleton) &&
|
333
|
+
(0 == strncmp(rb_class2name(singleton ? self : klass), curr->klass_name, curr->klass_len)))))
|
334
|
+
{
|
335
|
+
tracer = curr;
|
336
|
+
break;
|
337
|
+
}
|
338
|
+
} else {
|
339
|
+
if ((!curr->mid || curr->mid == mid) &&
|
340
|
+
(!curr->klass || curr->klass == klass) &&
|
341
|
+
(!curr->self || curr->self == self))
|
342
|
+
{
|
343
|
+
tracer = curr;
|
344
|
+
break;
|
345
|
+
}
|
346
|
+
}
|
347
|
+
}
|
348
|
+
}
|
349
|
+
|
350
|
+
// no tracer for current method call
|
351
|
+
if (!tracer) goto out;
|
352
|
+
|
353
|
+
} else if (rbtracer.slow) {
|
354
|
+
// trace anything that's slow
|
355
|
+
// fall through to slow logic below, after the previous conditional
|
356
|
+
// selects specific methods we might be interested in
|
357
|
+
|
358
|
+
} else {
|
359
|
+
// what are we doing here?
|
360
|
+
goto out;
|
361
|
+
}
|
362
|
+
|
363
|
+
// are we watching for slow method calls?
|
330
364
|
if (rbtracer.slow) {
|
331
365
|
uint64_t usec = timeofday_usec(), diff = 0;
|
332
366
|
|
@@ -366,34 +400,6 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
|
366
400
|
goto out;
|
367
401
|
}
|
368
402
|
|
369
|
-
int i, n;
|
370
|
-
rbtracer_t *tracer = NULL;
|
371
|
-
|
372
|
-
if (!rbtracer.firehose) {
|
373
|
-
// are there specific methods we're waiting for?
|
374
|
-
if (rbtracer.num == 0) goto out;
|
375
|
-
|
376
|
-
for (i=0, n=0; i<MAX_TRACERS && n<rbtracer.num; i++) {
|
377
|
-
rbtracer_t *curr = &rbtracer.list[i];
|
378
|
-
|
379
|
-
if (curr->query) {
|
380
|
-
n++;
|
381
|
-
|
382
|
-
if (!curr->mid || curr->mid == mid) {
|
383
|
-
if (!curr->klass || curr->klass == klass) {
|
384
|
-
if (!curr->self || curr->self == self) {
|
385
|
-
tracer = curr;
|
386
|
-
break;
|
387
|
-
}
|
388
|
-
}
|
389
|
-
}
|
390
|
-
}
|
391
|
-
}
|
392
|
-
|
393
|
-
// no matching method tracer found, so bail!
|
394
|
-
if (!tracer) goto out;
|
395
|
-
}
|
396
|
-
|
397
403
|
switch (event) {
|
398
404
|
case RUBY_EVENT_CALL:
|
399
405
|
case RUBY_EVENT_C_CALL:
|
@@ -408,6 +414,7 @@ event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
|
408
414
|
);
|
409
415
|
|
410
416
|
if (tracer && tracer->num_exprs) {
|
417
|
+
int i;
|
411
418
|
for (i=0; i<tracer->num_exprs; i++) {
|
412
419
|
char *expr = tracer->exprs[i];
|
413
420
|
size_t len = strlen(expr);
|
@@ -537,11 +544,14 @@ out:
|
|
537
544
|
}
|
538
545
|
|
539
546
|
static void
|
540
|
-
|
547
|
+
rbtracer_detach()
|
541
548
|
{
|
549
|
+
rbtracer.attached_pid = 0;
|
550
|
+
|
542
551
|
rbtracer.firehose = false;
|
543
552
|
rbtracer.slow = false;
|
544
553
|
rbtracer.gc = false;
|
554
|
+
rbtracer.devmode = false;
|
545
555
|
|
546
556
|
int i;
|
547
557
|
for (i=0; i<MAX_TRACERS; i++) {
|
@@ -577,22 +587,78 @@ rbtracer_add(char *query)
|
|
577
587
|
}
|
578
588
|
if (!tracer) goto out;
|
579
589
|
|
580
|
-
|
590
|
+
size_t
|
591
|
+
klass_begin = 0,
|
592
|
+
klass_end = 0;
|
593
|
+
|
594
|
+
bool is_singleton = false;
|
595
|
+
|
596
|
+
VALUE
|
597
|
+
klass = 0,
|
598
|
+
self = 0;
|
599
|
+
|
581
600
|
ID mid = 0;
|
582
601
|
|
583
|
-
|
602
|
+
{ // resolve query into its parts
|
603
|
+
char
|
604
|
+
*idx = NULL,
|
605
|
+
*method = NULL;
|
584
606
|
|
585
|
-
|
586
|
-
|
607
|
+
if (NULL != (idx = rindex(query, '.'))) {
|
608
|
+
klass_begin = 0;
|
609
|
+
klass_end = idx - query;
|
610
|
+
is_singleton = true;
|
611
|
+
|
612
|
+
*idx = 0;
|
613
|
+
if (!rbtracer.devmode)
|
614
|
+
self = rb_eval_string_protect(query, 0);
|
615
|
+
*idx = '.';
|
616
|
+
|
617
|
+
method = idx+1;
|
618
|
+
|
619
|
+
} else if (NULL != (idx = rindex(query, '#'))) {
|
620
|
+
klass_begin = 0;
|
621
|
+
klass_end = idx - query;
|
622
|
+
is_singleton = false;
|
623
|
+
|
624
|
+
*idx = 0;
|
625
|
+
if (!rbtracer.devmode)
|
626
|
+
klass = rb_eval_string_protect(query, 0);
|
627
|
+
*idx = '#';
|
628
|
+
|
629
|
+
method = idx+1;
|
630
|
+
|
631
|
+
} else {
|
632
|
+
method = query;
|
633
|
+
}
|
634
|
+
|
635
|
+
if (method && *method) {
|
636
|
+
mid = rb_intern(method);
|
637
|
+
}
|
638
|
+
}
|
639
|
+
|
640
|
+
if (rbtracer.devmode) {
|
641
|
+
if (!mid && (klass_begin == klass_end))
|
642
|
+
goto out;
|
643
|
+
} else {
|
644
|
+
if (!mid && !klass && !self)
|
645
|
+
goto out;
|
646
|
+
}
|
587
647
|
|
588
648
|
memset(tracer, 0, sizeof(*tracer));
|
589
649
|
|
590
650
|
tracer->id = tracer_id;
|
651
|
+
tracer->query = strdup(query);
|
652
|
+
|
653
|
+
if (klass_end != klass_begin) {
|
654
|
+
tracer->klass_name = tracer->query + klass_begin;
|
655
|
+
tracer->klass_len = klass_end - klass_begin;
|
656
|
+
}
|
657
|
+
tracer->is_singleton = is_singleton;
|
658
|
+
|
591
659
|
tracer->self = self;
|
592
660
|
tracer->klass = klass;
|
593
661
|
tracer->mid = mid;
|
594
|
-
tracer->query = strdup(query);
|
595
|
-
tracer->num_exprs = 0;
|
596
662
|
|
597
663
|
if (rbtracer.num == 0)
|
598
664
|
event_hook_install();
|
@@ -769,13 +835,14 @@ sigurg(int signal)
|
|
769
835
|
);
|
770
836
|
|
771
837
|
} else if (0 == strncmp("detach", str.ptr, str.size)) {
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
838
|
+
if (rbtracer.attached_pid) {
|
839
|
+
rbtrace__send_event(1,
|
840
|
+
"detached",
|
841
|
+
'u', (uint32_t) rbtracer.attached_pid
|
842
|
+
);
|
843
|
+
}
|
776
844
|
|
777
|
-
|
778
|
-
rbtracer_remove_all();
|
845
|
+
rbtracer_detach();
|
779
846
|
|
780
847
|
} else if (0 == strncmp("watch", str.ptr, str.size)) {
|
781
848
|
if (ary.size != 2 ||
|
@@ -814,6 +881,9 @@ sigurg(int signal)
|
|
814
881
|
} else if (0 == strncmp("gc", str.ptr, str.size)) {
|
815
882
|
rbtracer.gc = true;
|
816
883
|
|
884
|
+
} else if (0 == strncmp("devmode", str.ptr, str.size)) {
|
885
|
+
rbtracer.devmode = true;
|
886
|
+
|
817
887
|
}
|
818
888
|
}
|
819
889
|
}
|
data/rbtrace.gemspec
CHANGED
data/server.rb
CHANGED
@@ -7,6 +7,15 @@ class String
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
+
(reload_test = proc{
|
11
|
+
Object.send(:remove_const, :Test) if defined? Test
|
12
|
+
Test = Class.new do
|
13
|
+
def self.run
|
14
|
+
:abc
|
15
|
+
end
|
16
|
+
end
|
17
|
+
}).call
|
18
|
+
|
10
19
|
while true
|
11
20
|
proc {
|
12
21
|
Dir.chdir("/tmp") do
|
@@ -14,7 +23,11 @@ while true
|
|
14
23
|
Process.pid
|
15
24
|
'hello'.multiply_vowels(3){ :ohai }
|
16
25
|
sleep rand*0.5
|
26
|
+
|
17
27
|
GC.start
|
28
|
+
|
29
|
+
reload_test.call
|
30
|
+
Test.run
|
18
31
|
end
|
19
32
|
}.call
|
20
33
|
end
|
data/test.sh
CHANGED
@@ -30,6 +30,7 @@ trace() {
|
|
30
30
|
echo
|
31
31
|
}
|
32
32
|
|
33
|
+
trace -m Test.run --devmode
|
33
34
|
trace -m sleep
|
34
35
|
trace -m sleep Dir.chdir Dir.pwd Process.pid "String#gsub" "String#*"
|
35
36
|
trace -m "Kernel#"
|
@@ -37,6 +38,7 @@ trace -m "String#gsub(self,@test)" "String#*(self,__source__)" "String#multiply_
|
|
37
38
|
trace --gc --slow=200
|
38
39
|
trace --gc -m Dir.
|
39
40
|
trace --slow=250
|
41
|
+
trace --slow=250 -m sleep
|
40
42
|
trace --firehose
|
41
43
|
|
42
44
|
cleanup
|
@@ -0,0 +1 @@
|
|
1
|
+
Unicorn::App::OldRails#call(env['PATH_INFO'])
|
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: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
9
|
+
- 5
|
10
|
+
version: 0.3.5
|
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-21 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -91,6 +91,7 @@ files:
|
|
91
91
|
- tracers/eventmachine.tracer
|
92
92
|
- tracers/io.tracer
|
93
93
|
- tracers/redis.tracer
|
94
|
+
- tracers/unicorn.tracer
|
94
95
|
has_rdoc: true
|
95
96
|
homepage: http://github.com/tmm1/rbtrace
|
96
97
|
licenses: []
|