diff options
| author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 | 
|---|---|---|
| committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 | 
| commit | 114a878c64ce6f8223cfd22d76a20eb16d177e5e (patch) | |
| tree | acaf47eb0fa12142d3896416a69e74cbf5a72242 /languages/ruby/debugger/debuggee.rb | |
| download | tdevelop-114a878c64ce6f8223cfd22d76a20eb16d177e5e.tar.gz tdevelop-114a878c64ce6f8223cfd22d76a20eb16d177e5e.zip | |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdevelop@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'languages/ruby/debugger/debuggee.rb')
| -rw-r--r-- | languages/ruby/debugger/debuggee.rb | 1214 | 
1 files changed, 1214 insertions, 0 deletions
| diff --git a/languages/ruby/debugger/debuggee.rb b/languages/ruby/debugger/debuggee.rb new file mode 100644 index 00000000..38e2dea7 --- /dev/null +++ b/languages/ruby/debugger/debuggee.rb @@ -0,0 +1,1214 @@ +# Copyright (C) 2000  Network Applied Communication Laboratory, Inc. +# Copyright (C) 2000  Information-technology Promotion Agency, Japan +# Copyright (C) 2000-2003  NAKAMURA, Hiroshi  <nahi@ruby-lang.org> + +# Changes for the FreeRIDE IDE by Laurent JULLIARD. FreeRIDE uses +# Distributed ruby (DRuby) to communicate with the debugger back +# end. However, this can't interoperate with C++ in KDevelop and so +# a Unix domain socket connection is used instead. +	 +#                          Adapted for KDevelop debugging +#                          ------------------------------ +#    begin                : Mon Nov 1 2004 +#    copyright            : (C) 2004 by Richard Dale +#    email                : Richard_Dale@tipitina.demon.co.uk + +if $SAFE > 0 +  STDERR.print "-r debug.rb is not available in safe mode\n" +  exit 1 +end + +require 'tracer' +require 'pp' +require 'rbconfig' + +class Tracer +  def Tracer.trace_func(*vars) +    Single.trace_func(*vars) +  end +end + +# FreeRIDE/KDevelop must always intercept exits hence the exit! redefinition +# at_exit calls the quit method to cleanly disconnect from the  +# FreeRIDE/KDevelop debugger client +module Kernel +  alias_method :exit!, :exit +end + +BEGIN { +  at_exit do +    set_trace_func nil +    DEBUGGER__.quit +  end +} + +SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ + +class DEBUGGER__ +class Mutex +  def initialize +    @locker = nil +    @waiting = [] +    @locked = false; +  end + +  def locked? +    @locked +  end + +  def lock +    return if Thread.critical +    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 if Thread.critical +    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 readline(prompt_cmd, hist) +     DEBUGGER__.client.readline(prompt_cmd) +  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 +    @trace_ruby = 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 +    return if Thread.critical +    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 trace_ruby? +    @trace_ruby +  end + +  def set_trace_ruby(arg) +    @trace_ruby = 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) +    rescue StandardError, ScriptError => e +      at = eval("caller(1)", binding) +      stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '') +      for i in at +	stdout.printf "\tfrom %s\n", i +      end +      throw :debug_error +    end +  end + +  def debug_silent_eval(str, binding) +    begin +      eval(str, binding) +    rescue StandardError, ScriptError +      nil +    end +  end +   +    # Temporarily change the pretty_print methods to not expand arrays +	# and hashes, just give the length +	def customize_debug_pp +		Array.module_eval %q{ +			def pretty_print(pp) +				pp.pp "Array (%d element(s))" % length +			end +		} +		 +		Hash.module_eval %q{ +			def pretty_print(pp) +				pp.pp "Hash (%d element(s))" % length +			end +		} +	end + +    # Restore the original pretty_print methods for arrays and hashes +	def restore_debug_pp +		Array.module_eval %q{ +			def pretty_print(q) +					q.group(1, '[', ']') { +					self.each {|v| +						q.comma_breakable unless q.first? +						q.pp v +					} +				} +			end +		} +		Hash.module_eval %q{ +			def pretty_print(q) +				q.pp_hash self +			end +		} +	end +	 +  # Prevent the 'var *' commands from expanding Arrays and Hashes +  # This could be done by redefining inspect, but that would affect +  # everywhere not just here and in the pp command. +  def debug_inspect(obj) +  	if obj.kind_of? Array +		"Array (%d element(s))" % obj.length +	elsif obj.kind_of? Hash +		"Hash (%d element(s))" % obj.length +	elsif obj.kind_of? String +		str = obj.inspect +		if str.length > 255 +			"String (length %d)" % obj.length +		else +			str +		end +	else +		obj.inspect + 	end +  end +   +  def var_list(ary, binding) +    ary.sort! +    for v in ary +       stdout.printf "  %s => %s\n", v, debug_inspect(eval(v, binding)) +    end +  end + +  def const_list(ary, obj) +    ary.sort! +    for c in ary +	  str = debug_inspect(obj.module_eval(c)) +      if c.to_s != str && +	    str !~ /^Qt::|^KDE::/ && c.to_s !~ /@@classes$|@@cpp_names$|@@idclass$|@@debug_level$/ && +	    c.to_s !~ /^DCOPMeta$|^Meta$|SCRIPT_LINES__|TRUE|FALSE|NIL|MatchingData/ && +        c.to_s !~ /^PLATFORM$|^RELEASE_DATE$|^VERSION$|SilentClient|SilentObject/ && +        c.to_s !~ /^Client$|^Context$|^DEBUG_LAST_CMD$|^MUTEX$|^Mutex$|^SimpleDelegater$|^Delegater$/ && +        c.to_s !~ /IPsocket|IPserver|UDPsocket|UDPserver|TCPserver|TCPsocket|UNIXserver|UNIXsocket/ +		if c.to_s == "ENV" +      	  stdout.printf "  %s => Hash (%d element(s))\n", c, obj.module_eval(c).length +		else +      	  stdout.printf "  %s => %s\n", c, str +		end +	  end +    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) << "self", binding) + +    when /^\s*i(?:nstance)?\s+/ +      obj = debug_eval($', binding) +      var_list(obj.instance_variables, obj.instance_eval{binding()}) +     +	when /^\s*cl(?:ass)?\s+/ +      obj = debug_eval($', binding) +      unless obj.kind_of? Module +		stdout.print "Should be Class/Module: ", $', "\n" +      else +		const_list(obj.class_variables, obj) +      end + +    when /^\s*c(?:onst(?:ant)?)?\s+/ +      obj = debug_eval($', binding) +      unless obj.kind_of? Module +	stdout.print "Should be Class/Module: ", $', "\n" +      else +	const_list(obj.constants, obj) +      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(input, binding) +      unless obj.kind_of? Module +	stdout.print "Should be Class/Module: ", input, "\n" +      else +	len = 0 +	for v in obj.instance_methods(false).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 + +  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 + +  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'] +      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 == "" +          next unless DEBUG_LAST_CMD[0] +	  input = DEBUG_LAST_CMD[0] +	  stdout.print input, "\n" +	else +	  DEBUG_LAST_CMD[0] = input +	end + +	case input +	when /^\s*trace_ruby(?:\s+(on|off))?$/ +          if defined?( $1 ) +            if $1 == 'on' +              set_trace_ruby true +            else +              set_trace_ruby false +            end +          end + +	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 = $2 +          if $1 +            klass = debug_silent_eval($1, binding) +#            file = $1 +			file = File.expand_path($1) +          end +	  if pos =~ /^\d+$/ +	    pname = pos +	    pos = pos.to_i +	  else +	    pname = pos = pos.intern.id2name +	  end +	  break_points.push [true, 0, klass || file, pos] +	  stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname + +	when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/ +	  pos = $2.intern.id2name +	  klass = debug_eval($1, binding) +	  break_points.push [true, 0, klass, pos] +	  stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos + +	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) +#	  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+/ +	  obj_name = $' +	  obj = debug_eval($', binding) +	  customize_debug_pp +	  if obj.kind_of? Array +	  	obj.each_index { |i| stdout.printf "[%d]=%s\n", i.to_s, debug_inspect(obj[i]) } +	  elsif obj.kind_of? Hash or obj_name =~ /^ENV$/ +	    # Special case ENV to print like a hash +	  	obj.each { |key, value| stdout.printf "[%s]=%s\n", key.inspect, debug_inspect(value) } +	  elsif obj.kind_of?(String) && obj.inspect.length > 255 +	    # Assume long strings contain packed data and show them as a +		# sequence of 12 byte slices in hex +	  	i = 0 +		while i < obj.length +			j = (i + 12 < obj.length ? i + 12 : obj.length) - 1 +            stdout.printf "[%d..%d]=0x", i, j +			for k in i..j +                stdout.printf "%2.2x", obj[k] +			end +            stdout.printf " %s\n", obj[i..j].dump +			 +			i += 12 +		end +	  else +	    PP.pp(obj, stdout) +	  end +	  restore_debug_pp + +	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 +	end +      end +    end +    MUTEX.unlock +    resume_all +  end + +  def debug_print_help +    stdout.print <<EOHELP +Debugger help v.-0.002b +Commands +  b[reak] [file|class:]<line|method> +  b[reak] [class.]<line|method> +                             set breakpoint to some position +  wat[ch] <expression>       set watchpoint to some expression +  cat[ch] <an Exception>     set catchpoint to an exception +  b[reak]                    list breakpoints +  cat[ch]                    show catchpoint +  del[ete][ nnn]             delete some or all breakpoints +  disp[lay] <expression>     add expression into display expression list +  undisp[lay][ nnn]          delete one particular or all display expressions +  c[ont]                     run until program ends or hit breakpoint +  s[tep][ nnn]               step (into methods) one line or till line nnn +  n[ext][ nnn]               go over one line or till line nnn +  w[here]                    display frames +  f[rame]                    alias for where +  l[ist][ (-|nn-mm)]         list program, - lists backwards +                             nn-mm lists given lines +  up[ nn]                    move to higher frame +  down[ nn]                  move to lower frame +  fin[ish]                   return to outer frame +  tr[ace] (on|off)           set trace mode of current thread +  tr[ace] (on|off) all       set trace mode of all threads +  q[uit]                     exit from debugger +  v[ar] g[lobal]             show global variables +  v[ar] l[ocal]              show local variables +  v[ar] i[nstance] <object>  show instance variables of object +  v[ar] cl[ass] <object>     show class variables of object +  v[ar] c[onst] <object>     show constants of object +  m[ethod] i[nstance] <obj>  show methods of object +  m[ethod] <class|module>    show instance methods of class or module +  th[read] l[ist]            list all threads +  th[read] c[ur[rent]]       show current thread +  th[read] [sw[itch]] <nnn>  switch thread context to nnn +  th[read] stop <nnn>        stop thread nnn +  th[read] resume <nnn>      resume thread nnn +  p expression               evaluate expression and print its value +  h[elp]                     print this help +  <everything else>          evaluate +EOHELP +  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 +    end +  end + +  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 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 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.nil? +      "toplevel" +    else +      id.id2name +    end +  end + +  def check_break_points(file, klass, pos, binding, id) +    return false if break_points.empty? +    n = 1 +    for b in break_points +      if b[0]		# valid +	if b[1] == 0	# breakpoint +	  if (b[2] == file and b[3] == pos) or +	      (klass and b[2] == klass and b[3] == pos) +	    stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos +	    return true +	  end +	elsif b[1] == 1	# watchpoint +	  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 +  end + +  def excn_handle(file, line, id, binding) +    if $!.class <= SystemExit +      set_trace_func nil +      exit +    end + +    if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch }) +      stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class +      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 +   +	if not trace_ruby? and  +		(	file =~ /#{Config::CONFIG['sitelibdir']}/ or         +			file =~ /#{Config::CONFIG['rubylibdir']}/ or           +			file =~ %r{/debuggee.rb} ) +    	case event +    	when 'line' +      		frame_set_pos(file, line) +			 +		when 'call' +       		@frames.unshift [binding, file, line, id] +		 +    	when 'c-call' +      		frame_set_pos(file, line) +     +		when 'class' +      		@frames.unshift [binding, file, line, id] +			 +    	when 'return', 'end' +      		@frames.shift + +    	when 'end' +      		@frames.shift + +    	when 'raise'  +      		excn_handle(file, line, id, binding) +			 +		end +		return +	end +     +	@file = file +    @line = line +    case event +    when 'line' +      frame_set_pos(file, line) +      if !@no_step or @frames.size == @no_step +	@stop_next -= 1 +	@stop_next = -1 if @stop_next < 0 +      elsif @frames.size < @no_step +	@stop_next = 0		# break here before leaving... +      else +	# nothing to do. skipped. +      end +	#LJ reverse the test here because we always want the breakpoint reached +	# message to be display. if stop_next is null *AND* there is also a break point +	# the message will never display. +	if check_break_points(file, nil, line, binding, id) or @stop_next == 0  +	  # LJ this test doesn't make sense and cause troubles when  +	  # on a line with a recursive call and a breakpoint on it (e.g factorial) +	  # or when in a while loop with one line only inside the loop +	  # +	  # RJD: reinstated the test with a check on whether '@frames.size' +	  # has changed to catch the recursive factorial case LJ describes +	  # above. The while loop problem still exists though +	  if [file, line, @frames.size] == @last +	    @stop_next = 1 +	  else +		@no_step = nil +		suspend_all +		debug_command(file, line, id, binding) +	    @last = [file, line, @frames.size] +	  end +    end + +   when 'call' +       @frames.unshift [binding, file, line, id] +      if check_break_points(file, klass, 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) + +    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 SilentObject +    def method_missing( msg_id, *a, &b ); end +  end +  SilentClient = SilentObject.new() +  @client = SilentClient +  @attached = false + +class << DEBUGGER__ +  def stdout +    @stdout +  end + +  def stdout=(s) +    @stdout = s +  end + +  def display +    @display +  end + +  def break_points +    @break_points +  end +   +  def client +    @client +  end +   +  def set_client( client ) +  	@client = client +    DEBUGGER__.stdout = Tracer.stdout = @client +  end +   +  def quit +    #LJ flush STDOUT and ERR +#    @stdout.print "Quitting debugger" +    STDERR.flush; STDOUT.flush +    $stderr.flush; $stdout.flush +#    STDERR.close; STDOUT.close +    detach +    #DebugSvr.stop_service +  end +   +  def detach +    @attached = false +    @client.detach +    set_client( SilentClient ) +  end + +  def waiting +    @waiting +  end + +  def set_trace( arg ) +    saved_crit = Thread.critical +    Thread.critical = true +    make_thread_list +    for th, in @thread_list +      context(th).set_trace arg +    end +    Thread.critical = saved_crit +    arg +  end + +  def set_last_thread(th) +    @last_thread = th +  end + +  def suspend +    saved_crit = Thread.critical +    Thread.critical = true +    make_thread_list +    for th, in @thread_list +      next if th == Thread.current +      context(th).set_suspend +    end +    Thread.critical = saved_crit +    # Schedule other threads to suspend as soon as possible. +    Thread.pass unless Thread.critical +  end + +  def resume +    saved_crit = Thread.critical +    Thread.critical = true +    make_thread_list +    for th, in @thread_list +      next if th == Thread.current +      context(th).clear_suspend +    end +    waiting.each do |th| +      th.run +    end +    waiting.clear +    Thread.critical = saved_crit +    # 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 + +  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 +    @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)?)?$/ +      make_thread_list +      thread_list(@thread_list[Thread.current]) + +    when /^(?:sw(?:itch)?\s+)?(\d+)/ +      make_thread_list +      th = get_thread($1.to_i) +      if th == Thread.current +	@stdout.print "It's the current thread.\n" +      else +	thread_list(@thread_list[th]) +	context(th).stop_next +	th.run +	return :cont +      end + +    when /^stop\s+(\d+)/ +      make_thread_list +      th = get_thread($1.to_i) +      if th == Thread.current +	@stdout.print "It's the current thread.\n" +      elsif th.stop? +	@stdout.print "Already stopped.\n" +      else +	thread_list(@thread_list[th]) +	context(th).suspend  +      end + +    when /^resume\s+(\d+)/ +      make_thread_list +      th = get_thread($1.to_i) +      if th == Thread.current +	@stdout.print "It's the current thread.\n" +      elsif !th.stop? +	@stdout.print "Already running." +      else +	thread_list(@thread_list[th]) +	th.run +      end +    end +  end +end + +require 'socket' +   +  ## +  #  DEBUGGEE   ->  socket ->  KDevelop +  # The Client class holds all the methods invoked from the debuggee that send and +  # receive data from KDevelop via the Unix domain socket.  +  # +  class Client +	def initialize(path) +		@debugger = UNIXSocket.open(path) +		@debugger.sync=true +	end + +	def detach +#		@debugger.close +	end +	 +    def printf( *args ) +		str = sprintf(*args) +		@debugger.send(str, 0) +    end + +	def print( *args ) +		str = args.to_s +		@debugger.send(str, 0) +	end + +	def <<( arg ) +		@debugger.send(arg, 0) +	end +	 +	# Return next command +	def readline(prompt_cmd) +		@debugger.send(prompt_cmd, 0) +		msg = @debugger.recvfrom(2048) +		return msg[0] +	end +  end + +#stdout.printf "Debug.rb\n" +#stdout.printf "Emacs support available.\n\n" +   +STDERR.sync=true +STDOUT.sync=true + +path = $stdin.gets.chomp + +DEBUGGER__.set_client( Client.new(path) ) + +set_trace_func proc { |event, file, line, id, binding, klass, *rest| +	 +    # LJ make sure the file path is always absolute. It is needed by +    # the Debugger plugin in KDevelop and can only be determined here +    # in the context of the debugged process +	file = File.expand_path(file) +	 +	DEBUGGER__.context.trace_func event, file, line, id, binding, klass +} + +end | 
