diff --git a/lib/debug.rb b/lib/debug.rb index b69e7f9913..481658ccbf 100644 --- a/lib/debug.rb +++ b/lib/debug.rb @@ -18,202 +18,218 @@ end SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ class DEBUGGER__ - class Mutex - def initialize - @locker = nil - @waiting = [] - @locked = false; - end +class Mutex + def initialize + @locker = nil + @waiting = [] + @locked = false; + end - def locked? - @locked - end + def locked? + @locked + end - def lock - return if @locker == Thread.current - while (Thread.critical = true; @locked) - @waiting.push Thread.current - Thread.stop + 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 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 = [] + + 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 + exit unless line + line.chomp! + line + end + USE_READLINE = false + end + + def initialize + if Thread.current == Thread.main + @stop_next = 1 + else + @stop_next = 0 + end + @last_file = nil + @file = nil + @line = nil + @no_step = nil + @frames = [] + @finish_pos = 0 + @trace = false + @catch = "StandardError" + @suspend_next = false + end + + def stop_next(n=1) + @stop_next = n + end + + def set_suspend + @suspend_next = true + end + + def clear_suspend + @suspend_next = false + end + + def suspend_all + DEBUGGER__.suspend + end + + def resume_all + DEBUGGER__.resume + end + + def check_suspend + while (Thread.critical = true; @suspend_next) + DEBUGGER__.waiting.push Thread.current + @suspend_next = false + Thread.stop + end + Thread.critical = false + end + + def trace? + @trace + end + + def set_trace(arg) + @trace = arg + end + + def stdout + DEBUGGER__.stdout + end + + def break_points + DEBUGGER__.break_points + end + + def display + DEBUGGER__.display + end + + def context(th) + DEBUGGER__.context(th) + end + + def set_trace_all(arg) + DEBUGGER__.set_trace(arg) + end + + def set_last_thread(th) + DEBUGGER__.set_last_thread(th) + end + + def debug_eval(str, binding) + begin + val = eval(str, binding) + val + rescue StandardError, ScriptError + 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 - @locked = true - @locker = Thread.current - Thread.critical = false - self - end - - 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 + throw :debug_error end end - MUTEX = Mutex.new - - class Context - DEBUG_LAST_CMD = [] + def debug_silent_eval(str, binding) 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 - exit unless line - line.chomp! - line - end - USE_READLINE = false + val = eval(str, binding) + val + rescue StandardError, ScriptError + nil end + end - def initialize - if Thread.current == Thread.main - @stop_next = 1 + def var_list(ary, binding) + ary.sort! + for v in ary + stdout.printf " %s => %s\n", v, eval(v, binding).inspect + end + end + + def debug_variable_info(input, binding) + case input + when /^\s*g(?:lobal)?$/ + 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, obj.instance_eval{binding()}) + + when /^\s*c(?:onst(?:ant)?)?\s+/ + obj = debug_eval($', binding) + unless obj.kind_of? Module + stdout.print "Should be Class/Module: ", $', "\n" else - @stop_next = 0 + var_list(obj.constants, obj.module_eval{binding()}) end - @last_file = nil - @file = nil - @line = nil - @no_step = nil - @frames = [] - @finish_pos = 0 - @trace = false - @catch = "StandardError" - @suspend_next = false end + end - def stop_next(n=1) - @stop_next = n - end + def debug_method_info(input, binding) + case input + when /^i(:?nstance)?\s+/ + obj = debug_eval($', binding) - def set_suspend - @suspend_next = true - end - - def clear_suspend - @suspend_next = false - end - - def suspend_all - DEBUGGER__.suspend - end - - def resume_all - DEBUGGER__.resume - end - - def check_suspend - while (Thread.critical = true; @suspend_next) - DEBUGGER__.waiting.push Thread.current - @suspend_next = false - Thread.stop - end - Thread.critical = false - end - - def trace? - @trace - end - - def set_trace(arg) - @trace = arg - end - - def stdout - DEBUGGER__.stdout - end - - def break_points - DEBUGGER__.break_points - end - - def display - DEBUGGER__.display - end - - def context(th) - DEBUGGER__.context(th) - end - - def set_trace_all(arg) - DEBUGGER__.set_trace(arg) - end - - def set_last_thread(th) - DEBUGGER__.set_last_thread(th) - end - - def debug_eval(str, binding) - begin - val = eval(str, binding) - val - rescue StandardError, ScriptError - 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 + len = 0 + for v in obj.methods.sort + len += v.size + 1 + if len > 70 + len = v.size + 1 + stdout.print "\n" end - throw :debug_error + stdout.print v, " " end - end - - def debug_silent_eval(str, binding) - begin - val = eval(str, binding) - val - rescue StandardError, ScriptError - nil - end - end - - def var_list(ary, binding) - ary.sort! - for v in ary - stdout.printf " %s => %s\n", v, eval(v, binding).inspect - end - end - - def debug_variable_info(input, binding) - case input - when /^\s*g(?:lobal)?$/ - 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, obj.instance_eval{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 - end - end - - def debug_method_info(input, binding) - case input - when /^i(:?nstance)?\s+/ - obj = debug_eval($', binding) + stdout.print "\n" + else + obj = debug_eval(input, binding) + unless obj.kind_of? Module + stdout.print "Should be Class/Module: ", input, "\n" + else len = 0 - for v in obj.methods.sort + for v in obj.instance_methods.sort len += v.size + 1 if len > 70 len = v.size + 1 @@ -222,316 +238,300 @@ class DEBUGGER__ stdout.print v, " " end stdout.print "\n" - - else - obj = debug_eval(input, binding) - unless obj.kind_of? Module - stdout.print "Should be Class/Module: ", input, "\n" - else - 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 + end - def thnum + def thnum + num = DEBUGGER__.instance_eval{@thread_list[Thread.current]} + unless num + DEBUGGER__.make_thread_list 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 + num + end - def debug_command(file, line, id, binding) - MUTEX.lock - 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) - prompt = true - while prompt and input = readline("(rdb:%d) "%thnum(), true) - catch(:debug_error) do - if input == "" - input = DEBUG_LAST_CMD[0] - stdout.print input, "\n" + def debug_command(file, line, id, binding) + MUTEX.lock + 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) + prompt = true + while prompt and 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*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/ + if defined?( $2 ) + if $1 == 'on' + set_trace_all true + else + set_trace_all false + end + elsif defined?( $1 ) + if $1 == 'on' + set_trace true + else + set_trace false + end + end + if trace? + stdout.print "Trace on.\n" + else + stdout.print "Trace off.\n" + end + + when /^\s*b(?:reak)?\s+((?:.*?+:)?.+)$/ + pos = $1 + if pos.index(":") + pos = pos.split(":") + file = pos[0...-1].join(":") + pos = pos[-1] + end + file = File.basename(file) + if pos =~ /^\d+$/ + pname = pos + pos = pos.to_i else - DEBUG_LAST_CMD[0] = input + 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 - case input - when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/ - if defined?( $2 ) - if $1 == 'on' - set_trace_all true - else - set_trace_all false - end - elsif defined?( $1 ) - if $1 == 'on' - set_trace true - else - set_trace false - end - end - if trace? - stdout.print "Trace on.\n" - else - stdout.print "Trace off.\n" - end - - when /^\s*b(?:reak)?\s+((?:.*?+:)?.+)$/ - pos = $1 - if pos.index(":") - pos = pos.split(":") - file = pos[0...-1].join(":") - pos = pos[-1] - 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 [true, exp] - stdout.printf "%d: ", display.size - display_expression(exp, binding) - - 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)?$/ - prompt = false - - when /^\s*s(?:tep)?(?:\s+(\d+))?$/ - if $1 - lev = $1.to_i - else - lev = 1 - end - @stop_next = lev - prompt = 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 - prompt = false - - 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 - 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.print format_frame(frame_pos) - - 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.print format_frame(frame_pos) - - when /^\s*fin(?:ish)?$/ - if frame_pos == @frames.size - stdout.print "\"finish\" not meaningful in the outermost frame.\n" - else - @finish_pos = @frames.size - frame_pos - frame_pos = 0 - prompt = false - end - - when /^\s*cat(?:ch)?(?:\s+(.+))?$/ - if $1 - excn = $1 - if excn == 'off' - @catch = nil - stdout.print "Clear catchpoint.\n" - else - @catch = excn - stdout.printf "Set catchpoint %s.\n", @catch - end - else - if @catch - stdout.printf "Catchpoint %s.\n", @catch - else - stdout.print "No catchpoint.\n" - end - end - - when /^\s*q(?:uit)?$/ - input = readline("Really quit? (y/n) ", false) + when /^\s*del(?:ete)?(?:\s+(\d+))?$/ + pos = $1 + unless pos + input = readline("Clear all breakpoints? (y/n) ", false) if input == "y" - exit! # exit -> exit!: No graceful way to stop threads... + for b in break_points + b[0] = false + end end - - 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 - prompt = false - end - - when /^\s*pp\s+/ - PP.pp(debug_eval($', binding), 79, stdout) - - when /^\s*p\s+/ - stdout.printf "%s\n", debug_eval($', binding).inspect - - when /^\s*h(?:elp)?$/ - debug_print_help() - else - v = debug_eval(input, binding) - stdout.printf "%s\n", v.inspect unless (v == nil) + 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 [true, exp] + stdout.printf "%d: ", display.size + display_expression(exp, binding) + + 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)?$/ + prompt = false + + when /^\s*s(?:tep)?(?:\s+(\d+))?$/ + if $1 + lev = $1.to_i + else + lev = 1 + end + @stop_next = lev + prompt = 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 + prompt = false + + 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 + 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.print format_frame(frame_pos) + + 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.print format_frame(frame_pos) + + when /^\s*fin(?:ish)?$/ + if frame_pos == @frames.size + stdout.print "\"finish\" not meaningful in the outermost frame.\n" + else + @finish_pos = @frames.size - frame_pos + frame_pos = 0 + prompt = false + end + + when /^\s*cat(?:ch)?(?:\s+(.+))?$/ + if $1 + excn = $1 + if excn == 'off' + @catch = nil + stdout.print "Clear catchpoint.\n" + else + @catch = excn + stdout.printf "Set catchpoint %s.\n", @catch + end + else + if @catch + stdout.printf "Catchpoint %s.\n", @catch + else + stdout.print "No catchpoint.\n" + end + end + + when /^\s*q(?:uit)?$/ + input = readline("Really quit? (y/n) ", false) + if input == "y" + exit! # exit -> exit!: No graceful way to stop threads... + end + + 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 + prompt = false + end + + when /^\s*pp\s+/ + PP.pp(debug_eval($', binding), 79, stdout) + + when /^\s*p\s+/ + stdout.printf "%s\n", debug_eval($', binding).inspect + + when /^\s*h(?:elp)?$/ + debug_print_help() + + else + v = debug_eval(input, binding) + stdout.printf "%s\n", v.inspect unless (v == nil) end end - MUTEX.unlock - resume_all end + MUTEX.unlock + resume_all + end - def debug_print_help - stdout.print < @@ -571,358 +571,358 @@ Commands h[elp] print this help evaluate EOHELP - end + end - def display_expressions(binding) - n = 1 - for d in display - if d[0] - stdout.printf "%d: ", n - display_expression(d[1], binding) - end - n += 1 + def display_expressions(binding) + n = 1 + for d in display + if d[0] + stdout.printf "%d: ", n + display_expression(d[1], binding) end + n += 1 end + end - def display_expression(exp, binding) - stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s + def display_expression(exp, binding) + stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s + end + + def frame_set_pos(file, line) + if @frames[0] + @frames[0][1] = file + @frames[0][2] = line end + end - def frame_set_pos(file, line) - if @frames[0] - @frames[0][1] = file - @frames[0][2] = line + def display_frames(pos) + 0.upto(@frames.size - 1) do |n| + if n == pos + stdout.print "--> " + else + stdout.print " " end + stdout.print format_frame(n) end + end - def display_frames(pos) - 0.upto(@frames.size - 1) do |n| - if n == pos - stdout.print "--> " - else - stdout.print " " - end - stdout.print format_frame(n) - end - end + def format_frame(pos) + bind, file, line, id = @frames[pos] + sprintf "#%d %s:%s%s\n", pos + 1, file, line, + (id ? ":in `#{id.id2name}'" : "") + end - def format_frame(pos) - bind, file, line, id = @frames[pos] - sprintf "#%d %s:%s%s\n", pos + 1, file, line, - (id ? ":in `#{id.id2name}'" : "") - 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 + 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 - else - stdout.printf "No sourcefile available for %s\n", file 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" + 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.nil? - "toplevel" - else - id.id2name - end + def debug_funcname(id) + if id.nil? + "toplevel" + else + id.id2name end + end - def check_break_points(file, pos, binding, id) - return false if break_points.empty? - 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 + def check_break_points(file, pos, binding, id) + return false if break_points.empty? + 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 + if debug_silent_eval(b[2], binding) + stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos return true - elsif b[1] == 1 - if debug_silent_eval(b[2], binding) - stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos - return true - end end end - n += 1 end - return false + n += 1 + end + return false + end + + def excn_handle(file, line, id, binding) + stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.type + if $!.type <= SystemExit + set_trace_func nil + exit end - def excn_handle(file, line, id, binding) - stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.type - if $!.type <= SystemExit - set_trace_func nil - exit - end - - if @catch and ($!.type.ancestors.find { |e| e.to_s == @catch }) - fs = @frames.size - tb = caller(0)[-fs..-1] - if tb - for i in tb - stdout.printf "\tfrom %s\n", i - end + if @catch and ($!.type.ancestors.find { |e| e.to_s == @catch }) + fs = @frames.size + tb = caller(0)[-fs..-1] + if tb + for i in tb + stdout.printf "\tfrom %s\n", i end + end + suspend_all + debug_command(file, line, id, binding) + end + end + + def trace_func(event, file, line, id, binding, klass) + Tracer.trace_func(event, file, line, id, binding, klass) if trace? + context(Thread.current).check_suspend + @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 + # nothing to do. skipped. + end + if @stop_next == 0 or check_break_points(file, line, binding, id) + @no_step = nil suspend_all debug_command(file, line, id, binding) end - end - - def trace_func(event, file, line, id, binding, klass) - Tracer.trace_func(event, file, line, id, binding, klass) if trace? - context(Thread.current).check_suspend - @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 - # nothing to do. skipped. - end - if @stop_next == 0 or check_break_points(file, line, binding, id) - @no_step = nil - suspend_all - debug_command(file, line, id, binding) - end - - 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) - suspend_all - debug_command(file, line, id, binding) - end - - when 'c-call' - frame_set_pos(file, line) - - when 'class' - @frames.unshift [binding, file, line, id] - - when 'return', 'end' - if @frames.size == @finish_pos - @stop_next = 1 - @finish_pos = 0 - end - @frames.shift - - when 'end' - @frames.shift - - when 'raise' - excn_handle(file, line, id, binding) + 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) + suspend_all + debug_command(file, line, id, binding) end - @last_file = file + + when 'c-call' + frame_set_pos(file, line) + + when 'class' + @frames.unshift [binding, file, line, id] + + when 'return', 'end' + if @frames.size == @finish_pos + @stop_next = 1 + @finish_pos = 0 + end + @frames.shift + + when 'end' + @frames.shift + + when 'raise' + excn_handle(file, line, id, binding) + + end + @last_file = file + end +end + +trap("INT") { DEBUGGER__.interrupt } +@last_thread = Thread::main +@max_thread = 1 +@thread_list = {Thread::main => 1} +@break_points = [] +@display = [] +@waiting = [] +@stdout = STDOUT + +class << DEBUGGER__ + def stdout + @stdout + end + + def stdout=(s) + @stdout = s + end + + def display + @display + end + + def break_points + @break_points + end + + def waiting + @waiting + end + + def set_trace( arg ) + Thread.critical = true + make_thread_list + for th in @thread_list + context(th[0]).set_trace arg + end + Thread.critical = false + arg + end + + def set_last_thread(th) + @last_thread = th + end + + def suspend + Thread.critical = true + make_thread_list + for th in @thread_list + next if th[0] == Thread.current + context(th[0]).set_suspend + end + Thread.critical = false + # Schedule other threads to suspend as soon as possible. + Thread.pass + end + + def resume + Thread.critical = true + make_thread_list + for th in @thread_list + next if th[0] == Thread.current + context(th[0]).clear_suspend + end + waiting.each do |th| + th.run + end + waiting.clear + Thread.critical = false + # Schedule other threads to restart as soon as possible. + Thread.pass + end + + def context(thread=Thread.current) + c = thread[:__debugger_data__] + unless c + thread[:__debugger_data__] = c = Context.new + end + c + end + + def interrupt + context(@last_thread).stop_next + end + + def get_thread(num) + th = @thread_list.index(num) + unless th + @stdout.print "No thread ##{num}\n" + throw :debug_error + end + th + end + + def thread_list(num) + th = get_thread(num) + if th == Thread.current + @stdout.print "+" + else + @stdout.print " " + end + @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 + + def thread_list_all + for th in @thread_list.values.sort + thread_list(th) end end - trap("INT") { DEBUGGER__.interrupt } - @last_thread = Thread::main - @max_thread = 1 - @thread_list = {Thread::main => 1} - @break_points = [] - @display = [] - @waiting = [] - @stdout = STDOUT - - class <