LINES__ = {} unless defined? 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 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 DEBUG_LAST_CMD = [] def interrupt @stop_next = 1 end 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 end 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] else DEBUG_LAST_CMD[0] = input end case input when /^b(?:reak)?\s+((?:[^:\n]+:)?.+)$/ 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 /^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 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 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 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 /^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 when /^disp(?:lay)?$/, /^info disp(?:lay)?$/ display_expressions(binding) 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 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 /^c(?:ont)?$/ return when /^s(?:tep)?(?:\s+(\d+))?$/ if $1 lev = $1.to_i else lev = 1 end @stop_next = lev return when /^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 /^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)] else STDOUT.printf " #%d %s\n", n, at[-(@frames.size - n)] 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" 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 end when /^q(?:uit)?$/ input = readline("really quit? (y/n) ", false) exit if input == "y" when /^p\s+/ p debug_eval($', binding) else v = debug_eval(input, binding) p v unless (v == nil) 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 def frame_info(pos = 0) info = caller(0)[-(@frames.size - pos)] info.sub(/:in `.*'$/, "") =~ /^(.*):(\d+)$/ #` [info, $1, $2.to_i] 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 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 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 end n += 1 end return false end def excn_handle(file, line, id, binding) fs = @frames.size tb = caller(0)[-fs..-1] STDOUT.printf "%s\n", $! for i in tb STDOUT.printf "\tfrom %s\n", i 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... 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 end if check_break_points(file, line, binding, id) debug_command(file, line, id, binding) end when 'call' @frames.unshift binding if check_break_points(file, id.id2name, binding, id) debug_command(file, line, id, binding) end when 'class' @frames.unshift binding when 'return', 'end' @frames.shift if @frames.size == @finish_pos @stop_next = 1 end when 'raise' excn_handle(file, line, id, binding) 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 } end