1
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
2
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
3
# Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
6
STDERR.print "-r debug.rb is not available in safe mode\n"
14
def Tracer.trace_func(*vars)
15
Single.trace_func(*vars)
19
SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
34
return if Thread.critical
35
return if @locker == Thread.current
36
while (Thread.critical = true; @locked)
37
@waiting.push Thread.current
41
@locker = Thread.current
42
Thread.critical = false
47
return if Thread.critical
49
unless @locker == Thread.current
50
raise RuntimeError, "unlocked by other"
52
Thread.critical = true
56
Thread.critical = false
68
def readline(prompt, hist)
69
Readline::readline(prompt, hist)
72
def readline(prompt, hist)
84
if Thread.current == Thread.main
96
@catch = "StandardError"
109
@suspend_next = false
121
return if Thread.critical
122
while (Thread.critical = true; @suspend_next)
123
DEBUGGER__.waiting.push Thread.current
124
@suspend_next = false
127
Thread.critical = false
143
DEBUGGER__.break_points
151
DEBUGGER__.context(th)
154
def set_trace_all(arg)
155
DEBUGGER__.set_trace(arg)
158
def set_last_thread(th)
159
DEBUGGER__.set_last_thread(th)
162
def debug_eval(str, binding)
164
val = eval(str, binding)
165
rescue StandardError, ScriptError => e
166
at = eval("caller(1)", binding)
167
stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
169
stdout.printf "\tfrom %s\n", i
175
def debug_silent_eval(str, binding)
178
rescue StandardError, ScriptError
183
def var_list(ary, binding)
186
stdout.printf " %s => %s\n", v, eval(v, binding).inspect
190
def debug_variable_info(input, binding)
192
when /^\s*g(?:lobal)?\s*$/
193
var_list(global_variables, binding)
195
when /^\s*l(?:ocal)?\s*$/
196
var_list(eval("local_variables", binding), binding)
198
when /^\s*i(?:nstance)?\s+/
199
obj = debug_eval($', binding)
200
var_list(obj.instance_variables, obj.instance_eval{binding()})
202
when /^\s*c(?:onst(?:ant)?)?\s+/
203
obj = debug_eval($', binding)
204
unless obj.kind_of? Module
205
stdout.print "Should be Class/Module: ", $', "\n"
207
var_list(obj.constants, obj.module_eval{binding()})
212
def debug_method_info(input, binding)
214
when /^i(:?nstance)?\s+/
215
obj = debug_eval($', binding)
218
for v in obj.methods.sort
229
obj = debug_eval(input, binding)
230
unless obj.kind_of? Module
231
stdout.print "Should be Class/Module: ", input, "\n"
234
for v in obj.instance_methods(false).sort
248
num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
250
DEBUGGER__.make_thread_list
251
num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
256
def debug_command(file, line, id, binding)
258
unless defined?($debugger_restart) and $debugger_restart
259
callcc{|c| $debugger_restart = c}
261
set_last_thread(Thread.current)
267
stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
269
stdout.printf "%s:%d:%s", binding_file, binding_line,
270
line_at(binding_file, binding_line)
272
@frames[0] = [binding, file, line, id]
273
display_expressions(binding)
275
while prompt and input = readline("(rdb:%d) "%thnum(), true)
276
catch(:debug_error) do
278
next unless DEBUG_LAST_CMD[0]
279
input = DEBUG_LAST_CMD[0]
280
stdout.print input, "\n"
282
DEBUG_LAST_CMD[0] = input
286
when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
301
stdout.print "Trace on.\n"
303
stdout.print "Trace off.\n"
306
when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
309
klass = debug_silent_eval($1, binding)
316
pname = pos = pos.intern.id2name
318
break_points.push [true, 0, klass || file, pos]
319
stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname
321
when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/
322
pos = $2.intern.id2name
323
klass = debug_eval($1, binding)
324
break_points.push [true, 0, klass, pos]
325
stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos
327
when /^\s*wat(?:ch)?\s+(.+)$/
329
break_points.push [true, 1, exp]
330
stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp
332
when /^\s*b(?:reak)?$/
333
if break_points.find{|b| b[1] == 0}
335
stdout.print "Breakpoints:\n"
336
for b in break_points
337
if b[0] and b[1] == 0
338
stdout.printf " %d %s:%s\n", n, b[2], b[3]
343
if break_points.find{|b| b[1] == 1}
346
stdout.print "Watchpoints:\n"
347
for b in break_points
348
if b[0] and b[1] == 1
349
stdout.printf " %d %s\n", n, b[2]
354
if break_points.size == 0
355
stdout.print "No breakpoints\n"
360
when /^\s*del(?:ete)?(?:\s+(\d+))?$/
363
input = readline("Clear all breakpoints? (y/n) ", false)
365
for b in break_points
371
if break_points[pos-1]
372
break_points[pos-1][0] = false
374
stdout.printf "Breakpoint %d is not defined\n", pos
378
when /^\s*disp(?:lay)?\s+(.+)$/
380
display.push [true, exp]
381
stdout.printf "%d: ", display.size
382
display_expression(exp, binding)
384
when /^\s*disp(?:lay)?$/
385
display_expressions(binding)
387
when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
390
input = readline("Clear all expressions? (y/n) ", false)
399
display[pos-1][0] = false
401
stdout.printf "Display expression %d is not defined\n", pos
405
when /^\s*c(?:ont)?$/
408
when /^\s*s(?:tep)?(?:\s+(\d+))?$/
417
when /^\s*n(?:ext)?(?:\s+(\d+))?$/
424
@no_step = @frames.size - frame_pos
427
when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
428
display_frames(frame_pos)
430
when /^\s*l(?:ist)?(?:\s+(.+))?$/
432
b = previous_line ? previous_line + 10 : binding_line - 5
435
b = previous_line ? previous_line - 10 : binding_line - 5
438
b, e = $1.split(/[-,]/)
448
display_list(b, e, binding_file, binding_line)
450
when /^\s*up(?:\s+(\d+))?$/
458
if frame_pos >= @frames.size
459
frame_pos = @frames.size - 1
460
stdout.print "At toplevel\n"
462
binding, binding_file, binding_line = @frames[frame_pos]
463
stdout.print format_frame(frame_pos)
465
when /^\s*down(?:\s+(\d+))?$/
475
stdout.print "At stack bottom\n"
477
binding, binding_file, binding_line = @frames[frame_pos]
478
stdout.print format_frame(frame_pos)
480
when /^\s*fin(?:ish)?$/
481
if frame_pos == @frames.size
482
stdout.print "\"finish\" not meaningful in the outermost frame.\n"
484
@finish_pos = @frames.size - frame_pos
489
when /^\s*cat(?:ch)?(?:\s+(.+))?$/
494
stdout.print "Clear catchpoint.\n"
497
stdout.printf "Set catchpoint %s.\n", @catch
501
stdout.printf "Catchpoint %s.\n", @catch
503
stdout.print "No catchpoint.\n"
507
when /^\s*q(?:uit)?$/
508
input = readline("Really quit? (y/n) ", false)
510
exit! # exit -> exit!: No graceful way to stop threads...
513
when /^\s*v(?:ar)?\s+/
514
debug_variable_info($', binding)
516
when /^\s*m(?:ethod)?\s+/
517
debug_method_info($', binding)
519
when /^\s*th(?:read)?\s+/
520
if DEBUGGER__.debug_thread_info($', binding) == :cont
525
PP.pp(debug_eval($', binding), stdout)
528
stdout.printf "%s\n", debug_eval($', binding).inspect
530
when /^\s*r(?:estart)?$/
531
$debugger_restart.call
533
when /^\s*h(?:elp)?$/
537
v = debug_eval(input, binding)
538
stdout.printf "%s\n", v.inspect
547
stdout.print <<EOHELP
548
Debugger help v.-0.002b
550
b[reak] [file:|class:]<line|method>
551
b[reak] [class.]<line|method>
552
set breakpoint to some position
553
wat[ch] <expression> set watchpoint to some expression
554
cat[ch] (<exception>|off) set catchpoint to an exception
555
b[reak] list breakpoints
556
cat[ch] show catchpoint
557
del[ete][ nnn] delete some or all breakpoints
558
disp[lay] <expression> add expression into display expression list
559
undisp[lay][ nnn] delete one particular or all display expressions
560
c[ont] run until program ends or hit breakpoint
561
s[tep][ nnn] step (into methods) one line or till line nnn
562
n[ext][ nnn] go over one line or till line nnn
563
w[here] display frames
564
f[rame] alias for where
565
l[ist][ (-|nn-mm)] list program, - lists backwards
566
nn-mm lists given lines
567
up[ nn] move to higher frame
568
down[ nn] move to lower frame
569
fin[ish] return to outer frame
570
tr[ace] (on|off) set trace mode of current thread
571
tr[ace] (on|off) all set trace mode of all threads
572
q[uit] exit from debugger
573
v[ar] g[lobal] show global variables
574
v[ar] l[ocal] show local variables
575
v[ar] i[nstance] <object> show instance variables of object
576
v[ar] c[onst] <object> show constants of object
577
m[ethod] i[nstance] <obj> show methods of object
578
m[ethod] <class|module> show instance methods of class or module
579
th[read] l[ist] list all threads
580
th[read] c[ur[rent]] show current thread
581
th[read] [sw[itch]] <nnn> switch thread context to nnn
582
th[read] stop <nnn> stop thread nnn
583
th[read] resume <nnn> resume thread nnn
584
p expression evaluate expression and print its value
585
h[elp] print this help
586
<everything else> evaluate
590
def display_expressions(binding)
594
stdout.printf "%d: ", n
595
display_expression(d[1], binding)
601
def display_expression(exp, binding)
602
stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
605
def frame_set_pos(file, line)
612
def display_frames(pos)
613
0.upto(@frames.size - 1) do |n|
619
stdout.print format_frame(n)
623
def format_frame(pos)
624
bind, file, line, id = @frames[pos]
625
sprintf "#%d %s:%s%s\n", pos + 1, file, line,
626
(id ? ":in `#{id.id2name}'" : "")
629
def display_list(b, e, file, line)
630
stdout.printf "[%d, %d] in %s\n", b, e, file
631
if lines = SCRIPT_LINES__[file] and lines != true
634
if n > 0 && lines[n-1]
636
stdout.printf "=> %d %s\n", n, lines[n-1].chomp
638
stdout.printf " %d %s\n", n, lines[n-1].chomp
643
stdout.printf "No sourcefile available for %s\n", file
647
def line_at(file, line)
648
lines = SCRIPT_LINES__[file]
650
return "\n" if lines == true
652
return "\n" unless line
658
def debug_funcname(id)
666
def check_break_points(file, klass, pos, binding, id)
667
return false if break_points.empty?
669
for b in break_points
671
if b[1] == 0 # breakpoint
672
if (b[2] == file and b[3] == pos) or
673
(klass and b[2] == klass and b[3] == pos)
674
stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
677
elsif b[1] == 1 # watchpoint
678
if debug_silent_eval(b[2], binding)
679
stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
689
def excn_handle(file, line, id, binding)
690
if $!.class <= SystemExit
695
if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch })
696
stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class
698
tb = caller(0)[-fs..-1]
701
stdout.printf "\tfrom %s\n", i
705
debug_command(file, line, id, binding)
709
def trace_func(event, file, line, id, binding, klass)
710
Tracer.trace_func(event, file, line, id, binding, klass) if trace?
711
context(Thread.current).check_suspend
716
frame_set_pos(file, line)
717
if !@no_step or @frames.size == @no_step
719
@stop_next = -1 if @stop_next < 0
720
elsif @frames.size < @no_step
721
@stop_next = 0 # break here before leaving...
723
# nothing to do. skipped.
725
if @stop_next == 0 or check_break_points(file, nil, line, binding, id)
728
debug_command(file, line, id, binding)
732
@frames.unshift [binding, file, line, id]
733
if check_break_points(file, klass, id.id2name, binding, id)
735
debug_command(file, line, id, binding)
739
frame_set_pos(file, line)
742
@frames.unshift [binding, file, line, id]
745
if @frames.size == @finish_pos
755
excn_handle(file, line, id, binding)
762
trap("INT") { DEBUGGER__.interrupt }
763
@last_thread = Thread::main
765
@thread_list = {Thread::main => 1}
793
saved_crit = Thread.critical
794
Thread.critical = true
796
for th, in @thread_list
797
context(th).set_trace arg
799
Thread.critical = saved_crit
803
def set_last_thread(th)
808
saved_crit = Thread.critical
809
Thread.critical = true
811
for th, in @thread_list
812
next if th == Thread.current
813
context(th).set_suspend
815
Thread.critical = saved_crit
816
# Schedule other threads to suspend as soon as possible.
817
Thread.pass unless Thread.critical
821
saved_crit = Thread.critical
822
Thread.critical = true
824
for th, in @thread_list
825
next if th == Thread.current
826
context(th).clear_suspend
832
Thread.critical = saved_crit
833
# Schedule other threads to restart as soon as possible.
837
def context(thread=Thread.current)
838
c = thread[:__debugger_data__]
840
thread[:__debugger_data__] = c = Context.new
846
context(@last_thread).stop_next
850
th = @thread_list.index(num)
852
@stdout.print "No thread ##{num}\n"
860
if th == Thread.current
865
@stdout.printf "%d ", num
866
@stdout.print th.inspect, "\t"
867
file = context(th).instance_eval{@file}
869
@stdout.print file,":",context(th).instance_eval{@line}
875
for th in @thread_list.values.sort
882
for th in Thread::list
883
if @thread_list.key? th
884
hash[th] = @thread_list[th]
887
hash[th] = @max_thread
893
def debug_thread_info(input, binding)
899
when /^c(?:ur(?:rent)?)?$/
901
thread_list(@thread_list[Thread.current])
903
when /^(?:sw(?:itch)?\s+)?(\d+)/
905
th = get_thread($1.to_i)
906
if th == Thread.current
907
@stdout.print "It's the current thread.\n"
909
thread_list(@thread_list[th])
910
context(th).stop_next
917
th = get_thread($1.to_i)
918
if th == Thread.current
919
@stdout.print "It's the current thread.\n"
921
@stdout.print "Already stopped.\n"
923
thread_list(@thread_list[th])
927
when /^resume\s+(\d+)/
929
th = get_thread($1.to_i)
930
if th == Thread.current
931
@stdout.print "It's the current thread.\n"
933
@stdout.print "Already running."
935
thread_list(@thread_list[th])
942
stdout.printf "Debug.rb\n"
943
stdout.printf "Emacs support available.\n\n"
944
set_trace_func proc { |event, file, line, id, binding, klass, *rest|
945
DEBUGGER__.context.trace_func event, file, line, id, binding, klass