summaryrefslogtreecommitdiff
path: root/lib/debug.rb
diff options
context:
space:
mode:
authormatz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2000-01-05 04:41:21 +0000
committermatz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2000-01-05 04:41:21 +0000
commitde7161526014b781468cea5d84411e23be945f79 (patch)
treece7e90b3c16ce6246be9850c9b1ea1328992c0e7 /lib/debug.rb
parenta1d1b1516750c1047ceb7010f8f5ca34b358c7e3 (diff)
20000105
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@598 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/debug.rb')
-rw-r--r--lib/debug.rb948
1 files changed, 625 insertions, 323 deletions
diff --git a/lib/debug.rb b/lib/debug.rb
index 244db02..d9c4864 100644
--- a/lib/debug.rb
+++ b/lib/debug.rb
@@ -1,399 +1,701 @@
-LINES__ = {} unless defined? LINES__
+if $SAFE > 0
+ STDERR.print "-r debug.rb is not available in safe mode\n"
+ exit 1
+end
+SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
class DEBUGGER__
- begin
- require 'readline'
- def readline(prompt, hist)
- Readline::readline(prompt, hist)
- end
- rescue LoadError
- def readline(prompt, hist)
- STDOUT.print prompt
- STDOUT.flush
- line = STDIN.gets
- line.chomp!
- line
- end
- USE_READLINE = false
- end
+ class Mutex
+ def initialize
+ @locker = nil
+ @waiting = []
+ @locked = false;
+ end
- trap("INT") { DEBUGGER__::CONTEXT.interrupt }
- $DEBUG = true
- def initialize
- @break_points = []
- @display = []
- @stop_next = 1
- @frames = [nil]
- @last_file = nil
- @last = [nil, nil]
- @no_step = nil
- @finish_pos = 0
- end
+ def locked?
+ @locked
+ end
- DEBUG_LAST_CMD = []
+ def lock
+ return if @locker == Thread.current
+ while (Thread.critical = true; @locked)
+ @waiting.push Thread.current
+ Thread.stop
+ end
+ @locked = true
+ @locker = Thread.current
+ Thread.critical = false
+ self
+ end
- def interrupt
- @stop_next = 1
+ def unlock
+ return unless @locked
+ unless @locker == Thread.current
+ raise RuntimeError, "unlocked by other"
+ end
+ Thread.critical = true
+ t = @waiting.shift
+ @locked = false
+ @locker = nil
+ Thread.critical = false
+ t.run if t
+ self
+ end
end
+ MUTEX = Mutex.new
+
+ class Context
+ DEBUG_LAST_CMD = []
- def debug_eval(str, binding)
begin
- val = eval(str, binding)
- val
- rescue
- at = caller(0)
- STDOUT.printf "%s:%s\n", at.shift, $!
- for i in at
- break if i =~ /`debug_(eval|command)'$/ #`
- STDOUT.printf "\tfrom %s\n", i
+ require 'readline'
+ def readline(prompt, hist)
+ Readline::readline(prompt, hist)
end
+ rescue LoadError
+ def readline(prompt, hist)
+ STDOUT.print prompt
+ STDOUT.flush
+ line = STDIN.gets
+ exit unless line
+ line.chomp!
+ line
+ end
+ USE_READLINE = false
end
- end
- def debug_command(file, line, id, binding)
- frame_pos = 0
- binding_file = file
- binding_line = line
- previus_line = nil
- if (ENV['EMACS'] == 't')
- STDOUT.printf "\032\032%s:%d:\n", binding_file, binding_line
- else
- STDOUT.printf "%s:%d:%s", binding_file, binding_line,
- line_at(binding_file, binding_line)
- end
- @frames[0] = binding
- display_expressions(binding)
- while input = readline("(rdb:-) ", true)
- if input == ""
- input = DEBUG_LAST_CMD[0]
+ def initialize
+ if Thread.current == Thread.main
+ @stop_next = 1
else
- DEBUG_LAST_CMD[0] = input
+ @stop_next = 0
end
+ @last_file = nil
+ @last = [nil, nil]
+ @file = nil
+ @line = nil
+ @no_step = nil
+ @frames = []
+ @finish_pos = 0
+ end
- case input
- when /^b(?:reak)?\s+((?:[^:\n]+:)?.+)$/
- pos = $1
- if pos.index(":")
- file, pos = pos.split(":")
+ def stop_next(n=1)
+ @stop_next = n
+ end
+
+ def stdout
+ DEBUGGER__.stdout
+ end
+ def break_points
+ DEBUGGER__.break_points
+ end
+ def display
+ DEBUGGER__.display
+ end
+
+ def debug_eval(str, binding)
+ begin
+ val = eval(str, binding)
+ val
+ rescue
+ at = eval("caller(0)", binding)
+ stdout.printf "%s:%s\n", at.shift, $!.to_s.sub(/\(eval\):1:(in `.*?':)?/, '') #`
+ for i in at
+ stdout.printf "\tfrom %s\n", i
end
- file = File.basename(file)
- if pos =~ /^\d+$/
- pname = pos
- pos = pos.to_i
- else
- pname = pos = pos.intern.id2name
+ throw :debug_error
+ end
+ end
+
+ def var_list(ary, binding)
+ ary.sort!
+ if ary.size > 24
+ f = open("|less", "w")
+ for v in ary
+ f.printf " %s => %s\n", v, eval(v, binding).inspect
end
- @break_points.push [true, 0, file, pos]
- STDOUT.printf "Set breakpoint %d at %s:%s\n", @break_points.size, file,
- pname
-
- when /^wat(?:ch)?\s+(.+)$/
- exp = $1
- @break_points.push [true, 1, exp]
- STDOUT.printf "Set watchpoint %d\n", @break_points.size, exp
-
- when /^b(?:reak)?$/, /^info b(?:reak)?$/
- n = 1
- STDOUT.print "breakpoints:\n"
- for b in @break_points
- if b[0] and (b[1] == 0)
- STDOUT.printf " %d %s:%s\n", n, b[2], b[3]
- end
- n += 1
+ f.close
+ else
+ for v in ary
+ stdout.printf " %s => %s\n", v, eval(v, binding).inspect
end
- n = 1
- STDOUT.print "\n"
- STDOUT.print "watchpoints:\n"
- for b in @break_points
- if b[0] and (b[1] == 1)
- STDOUT.printf " %d %s\n", n, b[2]
- end
- n += 1
+ end
+ end
+
+ def debug_variable_info(input, binding)
+ case input
+ when /^\s*g(?:lobal)?$/
+ f = open("|less", "w")
+ var_list(global_variables, binding)
+
+ when /^\s*l(?:ocal)?$/
+ var_list(eval("local_variables", binding), binding)
+
+ when /^\s*i(?:nstance)?\s+/
+ obj = debug_eval($', binding)
+ var_list(obj.instance_variables, binding)
+
+ when /^\s*c(?:onst(?:ant)?)?\s+/
+ obj = debug_eval($', binding)
+ unless obj.kind_of? Module
+ stdout.print "should be Class/Module: ", $', "\n"
+ else
+ var_list(obj.constants, obj.module_eval{binding()})
end
- STDOUT.print "\n"
-
- when /^del(?:ete)?(?:\s+(\d+))?$/
- pos = $1
- unless pos
- input = readline("clear all breakpoints? (y/n) ", false)
- if input == "y"
- for b in @break_points
- b[0] = false
- end
+ end
+ end
+
+ def debug_method_info(input, binding)
+ case input
+ when /^i(:?nstance)?\s+/
+ obj = debug_eval($', binding)
+
+ len = 0
+ for v in obj.methods.sort
+ len += v.size + 1
+ if len > 70
+ len = v.size + 1
+ stdout.print "\n"
end
+ stdout.print v, " "
+ end
+ stdout.print "\n"
+
+ else
+ obj = debug_eval($', binding)
+ unless obj.kind_of? Module
+ stdout.print "should be Class/Module: ", $', "\n"
else
- pos = pos.to_i
- if @break_points[pos-1]
- @break_points[pos-1][0] = false
- else
- STDOUT.printf "Breakpoint %d is not defined\n", pos
+ len = 0
+ for v in obj.instance_methods.sort
+ len += v.size + 1
+ if len > 70
+ len = v.size + 1
+ stdout.print "\n"
+ end
+ stdout.print v, " "
end
+ stdout.print "\n"
end
+ end
+ end
- when /^disp(?:lay)?\s+(.+)$/
- exp = $1
- @display.push.push [true, exp]
- STDOUT.printf " %d: %s = %s\n", @display.size, exp,
- debug_eval(exp, binding).to_s
+ def thnum
+ num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
+ unless num
+ DEBUGGER__.make_thread_list
+ num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
+ end
+ num
+ end
- when /^disp(?:lay)?$/, /^info disp(?:lay)?$/
+ def debug_command(file, line, id, binding)
+ MUTEX.lock
+ DEBUGGER__.set_last_thread(Thread.current)
+ frame_pos = 0
+ binding_file = file
+ binding_line = line
+ previous_line = nil
+ if (ENV['EMACS'] == 't')
+ stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
+ else
+ stdout.printf "%s:%d:%s", binding_file, binding_line,
+ line_at(binding_file, binding_line)
+ end
+ @frames[0] = [binding, file, line, id]
display_expressions(binding)
+ while input = readline("(rdb:%d) "%thnum(), true)
+ catch (:debug_error) do
+ if input == ""
+ input = DEBUG_LAST_CMD[0]
+ stdout.print input, "\n"
+ else
+ DEBUG_LAST_CMD[0] = input
+ end
+
+ case input
+ when /^\s*b(?:reak)?\s+((?:.*?+:)?.+)$/
+ pos = $1
+ if pos.index(":")
+ file, pos = pos.split(":")
+ end
+ file = File.basename(file)
+ if pos =~ /^\d+$/
+ pname = pos
+ pos = pos.to_i
+ else
+ pname = pos = pos.intern.id2name
+ end
+ break_points.push [true, 0, file, pos]
+ stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, file,
+ pname
+
+ when /^\s*wat(?:ch)?\s+(.+)$/
+ exp = $1
+ break_points.push [true, 1, exp]
+ stdout.printf "Set watchpoint %d\n", break_points.size, exp
+
+ when /^\s*b(?:reak)?$/
+ if break_points.find{|b| b[1] == 0}
+ n = 1
+ stdout.print "breakpoints:\n"
+ for b in break_points
+ if b[0] and b[1] == 0
+ stdout.printf " %d %s:%s\n", n, b[2], b[3]
+ end
+ n += 1
+ end
+ end
+ if break_points.find{|b| b[1] == 1}
+ n = 1
+ stdout.print "\n"
+ stdout.print "watchpoints:\n"
+ for b in break_points
+ if b[0] and b[1] == 1
+ stdout.printf " %d %s\n", n, b[2]
+ end
+ n += 1
+ end
+ end
+ if break_points.size == 0
+ stdout.print "no breakpoints\n"
+ else
+ stdout.print "\n"
+ end
+
+ when /^\s*del(?:ete)?(?:\s+(\d+))?$/
+ pos = $1
+ unless pos
+ input = readline("clear all breakpoints? (y/n) ", false)
+ if input == "y"
+ for b in break_points
+ b[0] = false
+ end
+ end
+ else
+ pos = pos.to_i
+ if break_points[pos-1]
+ break_points[pos-1][0] = false
+ else
+ stdout.printf "Breakpoint %d is not defined\n", pos
+ end
+ end
+
+ when /^\s*disp(?:lay)?\s+(.+)$/
+ exp = $1
+ display.push.push [true, exp]
+ stdout.printf " %d: %s = %s\n", display.size, exp,
+ eval(exp, binding) rescue "--"
+
+ when /^\s*disp(?:lay)?$/
+ display_expressions(binding)
+
+ when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
+ pos = $1
+ unless pos
+ input = readline("clear all expressions? (y/n) ", false)
+ if input == "y"
+ for d in display
+ d[0] = false
+ end
+ end
+ else
+ pos = pos.to_i
+ if display[pos-1]
+ display[pos-1][0] = false
+ else
+ stdout.printf "display expression %d is not defined\n", pos
+ end
+ end
+
+ when /^\s*c(?:ont)?$/
+ MUTEX.unlock
+ return
+
+ when /^\s*s(?:tep)?(?:\s+(\d+))?$/
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ @stop_next = lev
+ return
- when /^undisp(?:lay)?(?:\s+(\d+))?$/
- pos = $1
- unless pos
- input = readline("clear all expressions? (y/n) ", false)
- if input == "y"
- for d in @display
- d[0] = false
+ when /^\s*n(?:ext)?(?:\s+(\d+))?$/
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ @stop_next = lev
+ @no_step = @frames.size - frame_pos
+ return
+
+ when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
+ display_frames(frame_pos)
+
+ when /^\s*l(?:ist)?(?:\s+(.+))?$/
+ if not $1
+ b = previous_line ? previous_line + 10 : binding_line - 5
+ e = b + 9
+ elsif $1 == '-'
+ b = previous_line ? previous_line - 10 : binding_line - 5
+ e = b + 9
+ else
+ b, e = $1.split(/[-,]/)
+ if e
+ b = b.to_i
+ e = e.to_i
+ else
+ b = b.to_i - 5
+ e = b + 9
+ end
end
- end
- else
- pos = pos.to_i
- if @display[pos-1]
- @display[pos-1][0] = false
+ previous_line = b
+ display_list(b, e, binding_file, binding_line)
+
+ when /^\s*up(?:\s+(\d+))?$/
+ previous_line = nil
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ frame_pos += lev
+ if frame_pos >= @frames.size
+ frame_pos = @frames.size - 1
+ stdout.print "at toplevel\n"
+ end
+ binding, binding_file, binding_line = @frames[frame_pos]
+ stdout.printf "#%d %s:%s\n", frame_pos, binding_file, binding_line
+
+ when /^\s*down(?:\s+(\d+))?$/
+ previous_line = nil
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ frame_pos -= lev
+ if frame_pos < 0
+ frame_pos = 0
+ stdout.print "at stack bottom\n"
+ end
+ binding, binding_file, binding_line = @frames[frame_pos]
+ stdout.printf "#%d %s:%s\n", frame_pos, binding_file, binding_line
+
+ when /^\s*fi(?:nish)?$/
+ if frame_pos == 0
+ stdout.print "\"finish\" not meaningful in the outermost frame.\n"
+ else
+ @finish_pos = @frames.size - frame_pos
+ frame_pos = 0
+ return
+ end
+
+ when /^\s*q(?:uit)?$/
+ input = readline("really quit? (y/n) ", false)
+ exit if input == "y"
+
+ when /^\s*v(?:ar)?\s+/
+ debug_variable_info($', binding)
+
+ when /^\s*m(?:ethod)?\s+/
+ debug_method_info($', binding)
+
+ when /^\s*th(?:read)?\s+/
+ if DEBUGGER__.debug_thread_info($', binding) == :cont
+ MUTEX.unlock
+ return
+ end
+
+ when /^\s*p\s+/
+ p debug_eval($', binding)
+
else
- STDOUT.printf "display expression %d is not defined\n", pos
+ v = debug_eval(input, binding)
+ p v unless (v == nil)
end
end
+ end
+ end
+
+ def display_expressions(binding)
+ n = 1
+ for d in display
+ if d[0]
+ stdout.printf "%d: %s = %s\n", n, d[1], debug_eval(d[1], binding).to_s
+ end
+ n += 1
+ end
+ end
- when /^c(?:ont)?$/
- return
+ def frame_set_pos(file, line)
+ if @frames[0]
+ @frames[0][1] = file
+ @frames[0][2] = line
+ end
+ end
- when /^s(?:tep)?(?:\s+(\d+))?$/
- if $1
- lev = $1.to_i
+ def display_frames(pos)
+ pos += 1
+ n = 0
+ at = @frames
+ for bind, file, line, id in at
+ n += 1
+ break unless bind
+ if pos == n
+ stdout.printf "--> #%d %s:%s%s\n", n, file, line, id != 0 ? ":in `#{id.id2name}'":""
else
- lev = 1
+ stdout.printf " #%d %s:%s%s\n", n, file, line, id != 0 ? ":in `#{id.id2name}'":""
+ end
+ end
+ end
+
+ def display_list(b, e, file, line)
+ stdout.printf "[%d, %d] in %s\n", b, e, file
+ if lines = SCRIPT_LINES__[file] and lines != true
+ n = 0
+ b.upto(e) do |n|
+ if n > 0 && lines[n-1]
+ if n == line
+ stdout.printf "=> %d %s\n", n, lines[n-1].chomp
+ else
+ stdout.printf " %d %s\n", n, lines[n-1].chomp
+ end
+ end
+ end
+ else
+ stdout.printf "no sourcefile available for %s\n", file
+ end
+ end
+
+ def line_at(file, line)
+ lines = SCRIPT_LINES__[file]
+ if lines
+ return "\n" if lines == true
+ line = lines[line-1]
+ return "\n" unless line
+ return line
+ end
+ return "\n"
+ end
+
+ def debug_funcname(id)
+ if id == 0
+ "toplevel"
+ else
+ id.id2name
+ end
+ end
+
+ def check_break_points(file, pos, binding, id)
+ file = File.basename(file)
+ n = 1
+ for b in break_points
+ if b[0]
+ if b[1] == 0 and b[2] == file and b[3] == pos
+ MUTEX.lock
+ stdout.printf "breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
+ return true
+ elsif b[1] == 1 and debug_eval(b[2], binding)
+ MUTEX.lock
+ stdout.printf "watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
+ return true
+ end
+ end
+ n += 1
+ end
+ return false
+ end
+
+ def excn_handle(file, line, id, binding)
+ if $!.type <= SystemExit
+ set_trace_func nil
+ exit
+ end
+ MUTEX.lock
+ fs = @frames.size
+ tb = caller(0)[-fs..-1]
+
+ stdout.printf "%s\n", $!
+ if tb
+ for i in tb
+ stdout.printf "\tfrom %s\n", i
end
- @stop_next = lev
- return
+ end
+ debug_command(file, line, id, binding)
+ end
- when /^n(?:ext)?(?:\s+(\d+))?$/
- if $1
- lev = $1.to_i
+ def trace_func(event, file, line, id, binding, klass)
+ @file = file
+ @line = line
+ case event
+ when 'line'
+ frame_set_pos(file, line)
+ if !@no_step or @frames.size == @no_step
+ @stop_next -= 1
+ elsif @frames.size < @no_step
+ @stop_next = 0 # break here before leaving...
else
- lev = 1
+ # nothing to do. skipped.
end
- @stop_next = lev
- @no_step = @frames.size - frame_pos
- return
-
- when /^w(?:here)?$/, /^f(?:rame)?$/
- at = caller(0)
- 0.upto(@frames.size - 1) do |n|
- if frame_pos == n
- STDOUT.printf "--> #%d %s\n", n, at[-(@frames.size - n)]
+ if @stop_next == 0 or check_break_points(file, line, binding, id)
+ if [file, line] == @last
+ @stop_next = 1
else
- STDOUT.printf " #%d %s\n", n, at[-(@frames.size - n)]
+ @no_step = nil
+ debug_command(file, line, id, binding)
+ @last = [file, line]
end
end
- when /^l(?:ist)?(?:\s+(.+))?$/
- if not $1
- b = previus_line ? previus_line + 10 : binding_line - 5
- e = b + 9
- elsif $1 == '-'
- b = previus_line ? previus_line - 10 : binding_line - 5
- e = b + 9
- else
- b, e = $1.split(/[-,]/)
- if e
- b = b.to_i
- e = e.to_i
- else
- b = b.to_i - 5
- e = b + 9
- end
- end
- previus_line = b
- STDOUT.printf "[%d, %d] in %s\n", b, e, binding_file
- line_at(binding_file, binding_line)
- if lines = LINES__[binding_file] and lines != true
- n = 0
- b.upto(e) do |n|
- if n > 0 && lines[n-1]
- if n == binding_line
- STDOUT.printf "=> %d %s\n", n, lines[n-1].chomp
- else
- STDOUT.printf " %d %s\n", n, lines[n-1].chomp
- end
- end
- end
- else
- STDOUT.printf "no sourcefile available for %s\n", binding_file
- end
-
- when /^up(?:\s+(\d+))?$/
- previus_line = nil
- if $1
- lev = $1.to_i
- else
- lev = 1
- end
- frame_pos += lev
- if frame_pos >= @frames.size
- frame_pos = @frames.size - 1
- STDOUT.print "at toplevel\n"
- end
- binding = @frames[frame_pos]
- info, binding_file, binding_line = frame_info(frame_pos)
- STDOUT.printf "#%d %s\n", frame_pos, info
-
- when /^down(?:\s+(\d+))?$/
- previus_line = nil
- if $1
- lev = $1.to_i
- else
- lev = 1
- end
- frame_pos -= lev
- if frame_pos < 0
- frame_pos = 0
- STDOUT.print "at stack bottom\n"
+ when 'call'
+ @frames.unshift [binding, file, line, id]
+ if check_break_points(file, id.id2name, binding, id) or
+ check_break_points(klass.to_s, id.id2name, binding, id)
+ debug_command(file, line, id, binding)
end
- binding = @frames[frame_pos]
- info, binding_file, binding_line = frame_info(frame_pos)
- STDOUT.printf "#%d %s\n", frame_pos, info
- when /^fi(?:nish)?$/
- if frame_pos == 0
- STDOUT.print "\"finish\" not meaningful in the outermost frame.\n"
- else
- @finish_pos = @frames.size - frame_pos
- frame_pos = 0
- return
+ when 'c-call'
+ frame_set_pos(file, line)
+
+ when 'class'
+ @frames.unshift [binding, file, line, id]
+
+ when 'return', 'end'
+ @frames.shift
+ if @frames.size == @finish_pos
+ @stop_next = 1
end
- when /^q(?:uit)?$/
- input = readline("really quit? (y/n) ", false)
- exit if input == "y"
+ when 'end'
+ @frames.shift
- when /^p\s+/
- p debug_eval($', binding)
+ when 'raise'
+ excn_handle(file, line, id, binding)
- else
- v = debug_eval(input, binding)
- p v unless (v == nil)
end
+ @last_file = file
end
end
-
- def display_expressions(binding)
- n = 1
- for d in @display
- if d[0]
- STDOUT.printf "%d: %s = %s\n", n, d[1], debug_eval(d[1], binding).to_s
- end
- n += 1
+
+ trap("INT") { DEBUGGER__.interrupt }
+# $DEBUG = true
+ @last_thread = Thread::main
+ @max_thread = 1
+ @thread_list = {Thread::main => 1}
+ @break_points = []
+ @display = []
+ @stdout = STDOUT
+
+ class <<DEBUGGER__
+ def stdout
+ @stdout
+ end
+ def stdout=(s)
+ @stdout = s
end
- end
- def frame_info(pos = 0)
- info = caller(0)[-(@frames.size - pos)]
- info.sub(/:in `.*'$/, "") =~ /^(.*):(\d+)$/ #`
- [info, $1, $2.to_i]
- end
+ def display
+ @display
+ end
- def line_at(file, line)
- lines = LINES__[file]
- if lines
- return "\n" if lines == true
- line = lines[line-1]
- return "\n" unless line
- return line
+ def break_points
+ @break_points
end
- return "\n"
- end
- def debug_funcname(id)
- if id == 0
- "toplevel"
- else
- id.id2name
+ def set_last_thread(th)
+ @last_thread = th
end
- end
- def check_break_points(file, pos, binding, id)
- file = File.basename(file)
- n = 1
- for b in @break_points
- if b[0]
- if b[1] == 0 and b[2] == file and b[3] == pos
- STDOUT.printf "breakpoint %d, %s at %s:%s\n", n, debug_funcname(id),
- file, pos
- return true
- elsif b[1] == 1 and debug_eval(b[2], binding)
- STDOUT.printf "watchpoint %d, %s at %s:%s\n", n, debug_funcname(id),
- file, pos
- return true
- end
+ def context(thread=Thread.current)
+ c = thread[:__debugger_data__]
+ unless c
+ thread[:__debugger_data__] = c = Context.new
end
- n += 1
+ c
end
- return false
- end
- def excn_handle(file, line, id, binding)
- fs = @frames.size
- tb = caller(0)[-fs..-1]
+ def interrupt
+ context(@last_thread).stop_next
+ end
- STDOUT.printf "%s\n", $!
- for i in tb
- STDOUT.printf "\tfrom %s\n", i
+ def get_thread(num)
+ th = @thread_list.index(num)
+ unless th
+ @stdout.print "no thread no.", num, "\n"
+ throw :debug_error
+ end
+ th
end
- debug_command(file, line, id, binding)
- end
- def trace_func(event, file, line, id, binding)
- case event
- when 'line'
- if !@no_step or @frames.size == @no_step
- @stop_next -= 1
- elsif @frames.size < @no_step
- @stop_next = 0 # break here before leaving...
+ def thread_list(num)
+ th = get_thread(num)
+ if th == Thread.current
+ @stdout.print "+"
else
- # nothing to do. skipped.
- end
- if @stop_next == 0
- if [file, line] == @last
- @stop_next = 1
- else
- @no_step = nil
- debug_command(file, line, id, binding)
- @last = [file, line]
- end
+ @stdout.print " "
end
- if check_break_points(file, line, binding, id)
- debug_command(file, line, id, binding)
+ @stdout.printf "%d ", num
+ @stdout.print th.inspect, "\t"
+ file = context(th).instance_eval{@file}
+ if file
+ @stdout.print file,":",context(th).instance_eval{@line}
end
+ @stdout.print "\n"
+ end
- when 'call'
- @frames.unshift binding
- if check_break_points(file, id.id2name, binding, id)
- debug_command(file, line, id, binding)
+ def thread_list_all
+ for th in @thread_list.values.sort
+ thread_list(th)
end
+ end
- when 'class'
- @frames.unshift binding
-
- when 'return', 'end'
- @frames.shift
- if @frames.size == @finish_pos
- @stop_next = 1
+ def make_thread_list
+ hash = {}
+ for th in Thread::list
+ if @thread_list.key? th
+ hash[th] = @thread_list[th]
+ else
+ @max_thread += 1
+ hash[th] = @max_thread
+ end
end
-
- when 'raise'
- excn_handle(file, line, id, binding)
+ @thread_list = hash
+ end
+ def debug_thread_info(input, binding)
+ case input
+ when /^l(?:ist)?/
+ make_thread_list
+ thread_list_all
+
+ when /^c(?:ur(?:rent)?)?\s+(\d+)/, /^stop\s+(\d+)/, /^(\d+)/
+ make_thread_list
+ th = get_thread($1.to_i)
+ thread_list(@thread_list[th])
+ context(th).stop_next
+ th.run
+ return :cont
+
+ when /^c(?:ur(?:rent)?)?$/
+ make_thread_list
+ thread_list(@thread_list[Thread.current])
+
+ when /^resume\s+(\d+)/
+ make_thread_list
+ th = get_thread($1.to_i)
+ thread_list(@thread_list[th])
+ th.run
+ return :cont
+ end
end
- @last_file = file
end
- CONTEXT = new
-
-
- set_trace_func proc{|event, file, line, id, binding,*rest|
- DEBUGGER__::CONTEXT.trace_func event, file, line, id, binding
+ @stdout.printf "Debug.rb\n"
+ @stdout.printf "Emacs support available.\n\n"
+ set_trace_func proc{|event, file, line, id, binding,klass,*rest|
+ DEBUGGER__.context.trace_func event, file, line, id, binding,klass
}
end