lib/debug.rb


DEFINITIONS

This source file includes following functions.


   1  # Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
   2  # Copyright (C) 2000  Information-technology Promotion Agency, Japan
   3  
   4  if $SAFE > 0
   5    STDERR.print "-r debug.rb is not available in safe mode\n"
   6    exit 1
   7  end
   8  
   9  require 'tracer'
  10  require 'pp'
  11  
  12  class Tracer
  13    def Tracer.trace_func(*vars)
  14      Single.trace_func(*vars)
  15    end
  16  end
  17  
  18  SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
  19  
  20  class DEBUGGER__
  21  class Mutex
  22    def initialize
  23      @locker = nil
  24      @waiting = []
  25      @locked = false;
  26    end
  27  
  28    def locked?
  29      @locked
  30    end
  31  
  32    def lock
  33      return if @locker == Thread.current
  34      while (Thread.critical = true; @locked)
  35        @waiting.push Thread.current
  36        Thread.stop
  37      end
  38      @locked = true
  39      @locker = Thread.current
  40      Thread.critical = false
  41      self
  42    end
  43  
  44    def unlock
  45      return unless @locked
  46      unless @locker == Thread.current
  47        raise RuntimeError, "unlocked by other"
  48      end
  49      Thread.critical = true
  50      t = @waiting.shift
  51      @locked = false
  52      @locker = nil
  53      Thread.critical = false
  54      t.run if t
  55      self
  56    end
  57  end
  58  MUTEX = Mutex.new
  59  
  60  class Context
  61    DEBUG_LAST_CMD = []
  62  
  63    begin
  64      require 'readline'
  65      def readline(prompt, hist)
  66        Readline::readline(prompt, hist)
  67      end
  68    rescue LoadError
  69      def readline(prompt, hist)
  70        STDOUT.print prompt
  71        STDOUT.flush
  72        line = STDIN.gets
  73        exit unless line
  74        line.chomp!
  75        line
  76      end
  77      USE_READLINE = false
  78    end
  79  
  80    def initialize
  81      if Thread.current == Thread.main
  82        @stop_next = 1
  83      else
  84        @stop_next = 0
  85      end
  86      @last_file = nil
  87      @file = nil
  88      @line = nil
  89      @no_step = nil
  90      @frames = []
  91      @finish_pos = 0
  92      @trace = false
  93      @catch = "StandardError"
  94      @suspend_next = false
  95    end
  96  
  97    def stop_next(n=1)
  98      @stop_next = n
  99    end
 100  
 101    def set_suspend
 102      @suspend_next = true
 103    end
 104  
 105    def clear_suspend
 106      @suspend_next = false
 107    end
 108  
 109    def suspend_all
 110      DEBUGGER__.suspend
 111    end
 112  
 113    def resume_all
 114      DEBUGGER__.resume
 115    end
 116  
 117    def check_suspend
 118      while (Thread.critical = true; @suspend_next)
 119        DEBUGGER__.waiting.push Thread.current
 120        @suspend_next = false
 121        Thread.stop
 122      end
 123      Thread.critical = false
 124    end
 125  
 126    def trace?
 127      @trace
 128    end
 129  
 130    def set_trace(arg)
 131      @trace = arg
 132    end
 133  
 134    def stdout
 135      DEBUGGER__.stdout
 136    end
 137  
 138    def break_points
 139      DEBUGGER__.break_points
 140    end
 141  
 142    def display
 143      DEBUGGER__.display
 144    end
 145  
 146    def context(th)
 147      DEBUGGER__.context(th)
 148    end
 149  
 150    def set_trace_all(arg)
 151      DEBUGGER__.set_trace(arg)
 152    end
 153  
 154    def set_last_thread(th)
 155      DEBUGGER__.set_last_thread(th)
 156    end
 157  
 158    def debug_eval(str, binding)
 159      begin
 160        val = eval(str, binding)
 161        val
 162      rescue StandardError, ScriptError
 163        at = eval("caller(0)", binding)
 164        stdout.printf "%s:%s\n", at.shift, $!.to_s.sub(/\(eval\):1:(in `.*?':)?/, '') #`
 165        for i in at
 166          stdout.printf "\tfrom %s\n", i
 167        end
 168        throw :debug_error
 169      end
 170    end
 171  
 172    def debug_silent_eval(str, binding)
 173      begin
 174        val = eval(str, binding)
 175        val
 176      rescue StandardError, ScriptError
 177        nil
 178      end
 179    end
 180  
 181    def var_list(ary, binding)
 182      ary.sort!
 183      for v in ary
 184        stdout.printf "  %s => %s\n", v, eval(v, binding).inspect
 185      end
 186    end
 187  
 188    def debug_variable_info(input, binding)
 189      case input
 190      when /^\s*g(?:lobal)?$/
 191        var_list(global_variables, binding)
 192  
 193      when /^\s*l(?:ocal)?$/
 194        var_list(eval("local_variables", binding), binding)
 195  
 196      when /^\s*i(?:nstance)?\s+/
 197        obj = debug_eval($', binding)
 198        var_list(obj.instance_variables, obj.instance_eval{binding()})
 199  
 200      when /^\s*c(?:onst(?:ant)?)?\s+/
 201        obj = debug_eval($', binding)
 202        unless obj.kind_of? Module
 203          stdout.print "Should be Class/Module: ", $', "\n"
 204        else
 205          var_list(obj.constants, obj.module_eval{binding()})
 206        end
 207      end
 208    end
 209  
 210    def debug_method_info(input, binding)
 211      case input
 212      when /^i(:?nstance)?\s+/
 213        obj = debug_eval($', binding)
 214  
 215        len = 0
 216        for v in obj.methods.sort
 217          len += v.size + 1
 218          if len > 70
 219            len = v.size + 1
 220            stdout.print "\n"
 221          end
 222          stdout.print v, " "
 223        end
 224        stdout.print "\n"
 225  
 226      else
 227        obj = debug_eval(input, binding)
 228        unless obj.kind_of? Module
 229          stdout.print "Should be Class/Module: ", input, "\n"
 230        else
 231          len = 0
 232          for v in obj.instance_methods.sort
 233            len += v.size + 1
 234            if len > 70
 235              len = v.size + 1
 236              stdout.print "\n"
 237            end
 238            stdout.print v, " "
 239          end
 240          stdout.print "\n"
 241        end
 242      end
 243    end
 244  
 245    def thnum
 246      num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
 247      unless num
 248        DEBUGGER__.make_thread_list
 249        num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
 250      end
 251      num
 252    end
 253  
 254    def debug_command(file, line, id, binding)
 255      MUTEX.lock
 256      set_last_thread(Thread.current)
 257      frame_pos = 0
 258      binding_file = file
 259      binding_line = line
 260      previous_line = nil
 261      if (ENV['EMACS'] == 't')
 262        stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
 263      else
 264        stdout.printf "%s:%d:%s", binding_file, binding_line,
 265          line_at(binding_file, binding_line)
 266      end
 267      @frames[0] = [binding, file, line, id]
 268      display_expressions(binding)
 269      prompt = true
 270      while prompt and input = readline("(rdb:%d) "%thnum(), true)
 271        catch(:debug_error) do
 272          if input == ""
 273            input = DEBUG_LAST_CMD[0]
 274            stdout.print input, "\n"
 275          else
 276            DEBUG_LAST_CMD[0] = input
 277          end
 278  
 279          case input
 280          when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
 281            if defined?( $2 )
 282              if $1 == 'on'
 283                set_trace_all true
 284              else
 285                set_trace_all false
 286              end
 287            elsif defined?( $1 )
 288              if $1 == 'on'
 289                set_trace true
 290              else
 291                set_trace false
 292              end
 293            end
 294            if trace?
 295              stdout.print "Trace on.\n"
 296            else
 297              stdout.print "Trace off.\n"
 298            end
 299  
 300          when /^\s*b(?:reak)?\s+(?:(.+):)?(.+)$/
 301            pos = $2
 302            file = File.basename($1) if $1
 303            if pos =~ /^\d+$/
 304              pname = pos
 305              pos = pos.to_i
 306            else
 307              pname = pos = pos.intern.id2name
 308            end
 309            break_points.push [true, 0, file, pos]
 310            stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, file, pname
 311  
 312          when /^\s*wat(?:ch)?\s+(.+)$/
 313            exp = $1
 314            break_points.push [true, 1, exp]
 315            stdout.printf "Set watchpoint %d\n", break_points.size, exp
 316  
 317          when /^\s*b(?:reak)?$/
 318            if break_points.find{|b| b[1] == 0}
 319              n = 1
 320              stdout.print "Breakpoints:\n"
 321              for b in break_points
 322                if b[0] and b[1] == 0
 323                  stdout.printf "  %d %s:%s\n", n, b[2], b[3] 
 324                end
 325                n += 1
 326              end
 327            end
 328            if break_points.find{|b| b[1] == 1}
 329              n = 1
 330              stdout.print "\n"
 331              stdout.print "Watchpoints:\n"
 332              for b in break_points
 333                if b[0] and b[1] == 1
 334                  stdout.printf "  %d %s\n", n, b[2]
 335                end
 336                n += 1
 337              end
 338            end
 339            if break_points.size == 0
 340              stdout.print "No breakpoints\n"
 341            else
 342              stdout.print "\n"
 343            end
 344  
 345          when /^\s*del(?:ete)?(?:\s+(\d+))?$/
 346            pos = $1
 347            unless pos
 348              input = readline("Clear all breakpoints? (y/n) ", false)
 349              if input == "y"
 350                for b in break_points
 351                  b[0] = false
 352                end
 353              end
 354            else
 355              pos = pos.to_i
 356              if break_points[pos-1]
 357                break_points[pos-1][0] = false
 358              else
 359                stdout.printf "Breakpoint %d is not defined\n", pos
 360              end
 361            end
 362  
 363          when /^\s*disp(?:lay)?\s+(.+)$/
 364            exp = $1
 365            display.push [true, exp]
 366            stdout.printf "%d: ", display.size
 367            display_expression(exp, binding)
 368  
 369          when /^\s*disp(?:lay)?$/
 370            display_expressions(binding)
 371  
 372          when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
 373            pos = $1
 374            unless pos
 375              input = readline("Clear all expressions? (y/n) ", false)
 376              if input == "y"
 377                for d in display
 378                  d[0] = false
 379                end
 380              end
 381            else
 382              pos = pos.to_i
 383              if display[pos-1]
 384                display[pos-1][0] = false
 385              else
 386                stdout.printf "Display expression %d is not defined\n", pos
 387              end
 388            end
 389  
 390          when /^\s*c(?:ont)?$/
 391            prompt = false
 392  
 393          when /^\s*s(?:tep)?(?:\s+(\d+))?$/
 394            if $1
 395              lev = $1.to_i
 396            else
 397              lev = 1
 398            end
 399            @stop_next = lev
 400            prompt = false
 401  
 402          when /^\s*n(?:ext)?(?:\s+(\d+))?$/
 403            if $1
 404              lev = $1.to_i
 405            else
 406              lev = 1
 407            end
 408            @stop_next = lev
 409            @no_step = @frames.size - frame_pos
 410            prompt = false
 411  
 412          when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
 413            display_frames(frame_pos)
 414  
 415          when /^\s*l(?:ist)?(?:\s+(.+))?$/
 416            if not $1
 417              b = previous_line ? previous_line + 10 : binding_line - 5
 418              e = b + 9
 419            elsif $1 == '-'
 420              b = previous_line ? previous_line - 10 : binding_line - 5
 421              e = b + 9
 422            else
 423              b, e = $1.split(/[-,]/)
 424              if e
 425                b = b.to_i
 426                e = e.to_i
 427              else
 428                b = b.to_i - 5
 429                e = b + 9
 430              end
 431            end
 432            previous_line = b
 433            display_list(b, e, binding_file, binding_line)
 434  
 435          when /^\s*up(?:\s+(\d+))?$/
 436            previous_line = nil
 437            if $1
 438              lev = $1.to_i
 439            else
 440              lev = 1
 441            end
 442            frame_pos += lev
 443            if frame_pos >= @frames.size
 444              frame_pos = @frames.size - 1
 445              stdout.print "At toplevel\n"
 446            end
 447            binding, binding_file, binding_line = @frames[frame_pos]
 448            stdout.print format_frame(frame_pos)
 449  
 450          when /^\s*down(?:\s+(\d+))?$/
 451            previous_line = nil
 452            if $1
 453              lev = $1.to_i
 454            else
 455              lev = 1
 456            end
 457            frame_pos -= lev
 458            if frame_pos < 0
 459              frame_pos = 0
 460              stdout.print "At stack bottom\n"
 461            end
 462            binding, binding_file, binding_line = @frames[frame_pos]
 463            stdout.print format_frame(frame_pos)
 464  
 465          when /^\s*fin(?:ish)?$/
 466            if frame_pos == @frames.size
 467              stdout.print "\"finish\" not meaningful in the outermost frame.\n"
 468            else
 469              @finish_pos = @frames.size - frame_pos
 470              frame_pos = 0
 471              prompt = false
 472            end
 473  
 474          when /^\s*cat(?:ch)?(?:\s+(.+))?$/
 475            if $1
 476              excn = $1
 477              if excn == 'off'
 478                @catch = nil
 479                stdout.print "Clear catchpoint.\n"
 480              else
 481                @catch = excn
 482                stdout.printf "Set catchpoint %s.\n", @catch
 483              end
 484            else
 485              if @catch
 486                stdout.printf "Catchpoint %s.\n", @catch
 487              else
 488                stdout.print "No catchpoint.\n"
 489              end
 490            end
 491  
 492          when /^\s*q(?:uit)?$/
 493            input = readline("Really quit? (y/n) ", false)
 494            if input == "y"
 495              exit!       # exit -> exit!: No graceful way to stop threads...
 496            end
 497  
 498          when /^\s*v(?:ar)?\s+/
 499            debug_variable_info($', binding)
 500  
 501          when /^\s*m(?:ethod)?\s+/
 502            debug_method_info($', binding)
 503  
 504          when /^\s*th(?:read)?\s+/
 505            if DEBUGGER__.debug_thread_info($', binding) == :cont
 506              prompt = false
 507            end
 508  
 509          when /^\s*pp\s+/
 510            PP.pp(debug_eval($', binding), stdout)
 511  
 512          when /^\s*p\s+/
 513            stdout.printf "%s\n", debug_eval($', binding).inspect
 514  
 515          when /^\s*h(?:elp)?$/
 516            debug_print_help()
 517  
 518          else
 519            v = debug_eval(input, binding)
 520            stdout.printf "%s\n", v.inspect unless (v == nil)
 521          end
 522        end
 523      end
 524      MUTEX.unlock
 525      resume_all
 526    end
 527  
 528    def debug_print_help
 529      stdout.print <<EOHELP
 530  Debugger help v.-0.002b
 531  Commands
 532    b[reak] [file|method:]<line|method>
 533                               set breakpoint to some position
 534    wat[ch] <expression>       set watchpoint to some expression
 535    cat[ch] <an Exception>     set catchpoint to an exception
 536    b[reak]                    list breakpoints
 537    cat[ch]                    show catchpoint
 538    del[ete][ nnn]             delete some or all breakpoints
 539    disp[lay] <expression>     add expression into display expression list
 540    undisp[lay][ nnn]          delete one particular or all display expressions
 541    c[ont]                     run until program ends or hit breakpoint
 542    s[tep][ nnn]               step (into methods) one line or till line nnn
 543    n[ext][ nnn]               go over one line or till line nnn
 544    w[here]                    display frames
 545    f[rame]                    alias for where
 546    l[ist][ (-|nn-mm)]         list program, - lists backwards
 547                               nn-mm lists given lines
 548    up[ nn]                    move to higher frame
 549    down[ nn]                  move to lower frame
 550    fin[ish]                   return to outer frame
 551    tr[ace] (on|off)           set trace mode of current thread
 552    tr[ace] (on|off) all       set trace mode of all threads
 553    q[uit]                     exit from debugger
 554    v[ar] g[lobal]             show global variables
 555    v[ar] l[ocal]              show local variables
 556    v[ar] i[nstance] <object>  show instance variables of object
 557    v[ar] c[onst] <object>     show constants of object
 558    m[ethod] i[nstance] <obj>  show methods of object
 559    m[ethod] <class|module>    show instance methods of class or module
 560    th[read] l[ist]            list all threads
 561    th[read] c[ur[rent]]       show current thread
 562    th[read] [sw[itch]] <nnn>  switch thread context to nnn
 563    th[read] stop <nnn>        stop thread nnn
 564    th[read] resume <nnn>      resume thread nnn
 565    p expression               evaluate expression and print its value
 566    h[elp]                     print this help
 567    <everything else>          evaluate
 568  EOHELP
 569    end
 570  
 571    def display_expressions(binding)
 572      n = 1
 573      for d in display
 574        if d[0]
 575          stdout.printf "%d: ", n
 576          display_expression(d[1], binding)
 577        end
 578        n += 1
 579      end
 580    end
 581  
 582    def display_expression(exp, binding)
 583      stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
 584    end
 585  
 586    def frame_set_pos(file, line)
 587      if @frames[0]
 588        @frames[0][1] = file
 589        @frames[0][2] = line
 590      end
 591    end
 592  
 593    def display_frames(pos)
 594      0.upto(@frames.size - 1) do |n|
 595        if n == pos
 596          stdout.print "--> "
 597        else
 598          stdout.print "    "
 599        end
 600        stdout.print format_frame(n)
 601      end
 602    end
 603  
 604    def format_frame(pos)
 605      bind, file, line, id = @frames[pos]
 606      sprintf "#%d %s:%s%s\n", pos + 1, file, line,
 607        (id ? ":in `#{id.id2name}'" : "")
 608    end
 609  
 610    def display_list(b, e, file, line)
 611      stdout.printf "[%d, %d] in %s\n", b, e, file
 612      if lines = SCRIPT_LINES__[file] and lines != true
 613        n = 0
 614        b.upto(e) do |n|
 615          if n > 0 && lines[n-1]
 616            if n == line
 617              stdout.printf "=> %d  %s\n", n, lines[n-1].chomp
 618            else
 619              stdout.printf "   %d  %s\n", n, lines[n-1].chomp
 620            end
 621          end
 622        end
 623      else
 624        stdout.printf "No sourcefile available for %s\n", file
 625      end
 626    end
 627  
 628    def line_at(file, line)
 629      lines = SCRIPT_LINES__[file]
 630      if lines
 631        return "\n" if lines == true
 632        line = lines[line-1]
 633        return "\n" unless line
 634        return line
 635      end
 636      return "\n"
 637    end
 638  
 639    def debug_funcname(id)
 640      if id.nil?
 641        "toplevel"
 642      else
 643        id.id2name
 644      end
 645    end
 646  
 647    def check_break_points(file, pos, binding, id)
 648      return false if break_points.empty?
 649      file = File.basename(file)
 650      n = 1
 651      for b in break_points
 652        if b[0]
 653          if b[1] == 0 and b[2] == file and b[3] == pos
 654            stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
 655            return true
 656          elsif b[1] == 1
 657            if debug_silent_eval(b[2], binding)
 658              stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
 659              return true
 660            end
 661          end
 662        end
 663        n += 1
 664      end
 665      return false
 666    end
 667  
 668    def excn_handle(file, line, id, binding)
 669      stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.type
 670      if $!.type <= SystemExit
 671        set_trace_func nil
 672        exit
 673      end
 674  
 675      if @catch and ($!.type.ancestors.find { |e| e.to_s == @catch })
 676        fs = @frames.size
 677        tb = caller(0)[-fs..-1]
 678        if tb
 679          for i in tb
 680            stdout.printf "\tfrom %s\n", i
 681          end
 682        end
 683        suspend_all
 684        debug_command(file, line, id, binding)
 685      end
 686    end
 687  
 688    def trace_func(event, file, line, id, binding, klass)
 689      Tracer.trace_func(event, file, line, id, binding, klass) if trace?
 690      context(Thread.current).check_suspend
 691      @file = file
 692      @line = line
 693      case event
 694      when 'line'
 695        frame_set_pos(file, line)
 696        if !@no_step or @frames.size == @no_step
 697          @stop_next -= 1
 698        elsif @frames.size < @no_step
 699          @stop_next = 0          # break here before leaving...
 700        else
 701          # nothing to do. skipped.
 702        end
 703        if @stop_next == 0 or check_break_points(file, line, binding, id)
 704          @no_step = nil
 705          suspend_all
 706          debug_command(file, line, id, binding)
 707        end
 708  
 709      when 'call'
 710        @frames.unshift [binding, file, line, id]
 711        if check_break_points(file, id.id2name, binding, id) or
 712            check_break_points(klass.to_s, id.id2name, binding, id)
 713          suspend_all
 714          debug_command(file, line, id, binding)
 715        end
 716  
 717      when 'c-call'
 718        frame_set_pos(file, line)
 719  
 720      when 'class'
 721        @frames.unshift [binding, file, line, id]
 722  
 723      when 'return', 'end'
 724        if @frames.size == @finish_pos
 725          @stop_next = 1
 726          @finish_pos = 0
 727        end
 728        @frames.shift
 729  
 730      when 'end'
 731        @frames.shift
 732  
 733      when 'raise' 
 734        excn_handle(file, line, id, binding)
 735  
 736      end
 737      @last_file = file
 738    end
 739  end
 740  
 741  trap("INT") { DEBUGGER__.interrupt }
 742  @last_thread = Thread::main
 743  @max_thread = 1
 744  @thread_list = {Thread::main => 1}
 745  @break_points = []
 746  @display = []
 747  @waiting = []
 748  @stdout = STDOUT
 749  
 750  class << DEBUGGER__
 751    def stdout
 752      @stdout
 753    end
 754  
 755    def stdout=(s)
 756      @stdout = s
 757    end
 758  
 759    def display
 760      @display
 761    end
 762  
 763    def break_points
 764      @break_points
 765    end
 766  
 767    def waiting
 768      @waiting
 769    end
 770  
 771    def set_trace( arg )
 772      Thread.critical = true
 773      make_thread_list
 774      for th in @thread_list
 775        context(th[0]).set_trace arg
 776      end
 777      Thread.critical = false
 778      arg
 779    end
 780  
 781    def set_last_thread(th)
 782      @last_thread = th
 783    end
 784  
 785    def suspend
 786      Thread.critical = true
 787      make_thread_list
 788      for th in @thread_list
 789        next if th[0] == Thread.current
 790        context(th[0]).set_suspend
 791      end
 792      Thread.critical = false
 793      # Schedule other threads to suspend as soon as possible.
 794      Thread.pass
 795    end
 796  
 797    def resume
 798      Thread.critical = true
 799      make_thread_list
 800      for th in @thread_list
 801        next if th[0] == Thread.current
 802        context(th[0]).clear_suspend
 803      end
 804      waiting.each do |th|
 805        th.run
 806      end
 807      waiting.clear
 808      Thread.critical = false
 809      # Schedule other threads to restart as soon as possible.
 810      Thread.pass
 811    end
 812  
 813    def context(thread=Thread.current)
 814      c = thread[:__debugger_data__]
 815      unless c
 816        thread[:__debugger_data__] = c = Context.new
 817      end
 818      c
 819    end
 820  
 821    def interrupt
 822      context(@last_thread).stop_next
 823    end
 824  
 825    def get_thread(num)
 826      th = @thread_list.index(num)
 827      unless th
 828        @stdout.print "No thread ##{num}\n"
 829        throw :debug_error
 830      end
 831      th
 832    end
 833  
 834    def thread_list(num)
 835      th = get_thread(num)
 836      if th == Thread.current
 837        @stdout.print "+"
 838      else
 839        @stdout.print " "
 840      end
 841      @stdout.printf "%d ", num
 842      @stdout.print th.inspect, "\t"
 843      file = context(th).instance_eval{@file}
 844      if file
 845        @stdout.print file,":",context(th).instance_eval{@line}
 846      end
 847      @stdout.print "\n"
 848    end
 849  
 850    def thread_list_all
 851      for th in @thread_list.values.sort
 852        thread_list(th)
 853      end
 854    end
 855  
 856    def make_thread_list
 857      hash = {}
 858      for th in Thread::list
 859        if @thread_list.key? th
 860          hash[th] = @thread_list[th]
 861        else
 862          @max_thread += 1
 863          hash[th] = @max_thread
 864        end
 865      end
 866      @thread_list = hash
 867    end
 868  
 869    def debug_thread_info(input, binding)
 870      case input
 871      when /^l(?:ist)?/
 872        make_thread_list
 873        thread_list_all
 874  
 875      when /^c(?:ur(?:rent)?)?$/
 876        make_thread_list
 877        thread_list(@thread_list[Thread.current])
 878  
 879      when /^(?:sw(?:itch)?\s+)?(\d+)/
 880        make_thread_list
 881        th = get_thread($1.to_i)
 882        if th == Thread.current
 883          @stdout.print "It's the current thread.\n"
 884        else
 885          thread_list(@thread_list[th])
 886          context(th).stop_next
 887          th.run
 888          return :cont
 889        end
 890  
 891      when /^stop\s+(\d+)/
 892        make_thread_list
 893        th = get_thread($1.to_i)
 894        if th == Thread.current
 895          @stdout.print "It's the current thread.\n"
 896        elsif th.stop?
 897          @stdout.print "Already stopped.\n"
 898        else
 899          thread_list(@thread_list[th])
 900          context(th).suspend 
 901        end
 902  
 903      when /^resume\s+(\d+)/
 904        make_thread_list
 905        th = get_thread($1.to_i)
 906        if th == Thread.current
 907          @stdout.print "It's the current thread.\n"
 908        elsif !th.stop?
 909          @stdout.print "Already running."
 910        else
 911          thread_list(@thread_list[th])
 912          th.run
 913        end
 914      end
 915    end
 916  end
 917  
 918  stdout.printf "Debug.rb\n"
 919  stdout.printf "Emacs support available.\n\n"
 920  set_trace_func proc { |event, file, line, id, binding, klass, *rest|
 921    DEBUGGER__.context.trace_func event, file, line, id, binding, klass
 922  }
 923  end