1# Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
2# Copyright (C) 2000  Information-technology Promotion Agency, Japan
3# Copyright (C) 2000-2003  NAKAMURA, Hiroshi  <nahi@ruby-lang.org>
4
5require 'continuation'
6
7if $SAFE > 0
8  STDERR.print "-r debug.rb is not available in safe mode\n"
9  exit 1
10end
11
12require 'tracer'
13require 'pp'
14
15class Tracer # :nodoc:
16  def Tracer.trace_func(*vars)
17    Single.trace_func(*vars)
18  end
19end
20
21SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ # :nodoc:
22
23##
24# This library provides debugging functionality to Ruby.
25#
26# To add a debugger to your code, start by requiring +debug+ in your
27# program:
28#
29#   def say(word)
30#     require 'debug'
31#     puts word
32#   end
33#
34# This will cause Ruby to interrupt execution and show a prompt when the +say+
35# method is run.
36#
37# Once you're inside the prompt, you can start debugging your program.
38#
39#   (rdb:1) p word
40#   "hello"
41#
42# == Getting help
43#
44# You can get help at any time by pressing +h+.
45#
46#   (rdb:1) h
47#   Debugger help v.-0.002b
48#   Commands
49#     b[reak] [file:|class:]<line|method>
50#     b[reak] [class.]<line|method>
51#                                set breakpoint to some position
52#     wat[ch] <expression>       set watchpoint to some expression
53#     cat[ch] (<exception>|off)  set catchpoint to an exception
54#     b[reak]                    list breakpoints
55#     cat[ch]                    show catchpoint
56#     del[ete][ nnn]             delete some or all breakpoints
57#     disp[lay] <expression>     add expression into display expression list
58#     undisp[lay][ nnn]          delete one particular or all display expressions
59#     c[ont]                     run until program ends or hit breakpoint
60#     s[tep][ nnn]               step (into methods) one line or till line nnn
61#     n[ext][ nnn]               go over one line or till line nnn
62#     w[here]                    display frames
63#     f[rame]                    alias for where
64#     l[ist][ (-|nn-mm)]         list program, - lists backwards
65#                                nn-mm lists given lines
66#     up[ nn]                    move to higher frame
67#     down[ nn]                  move to lower frame
68#     fin[ish]                   return to outer frame
69#     tr[ace] (on|off)           set trace mode of current thread
70#     tr[ace] (on|off) all       set trace mode of all threads
71#     q[uit]                     exit from debugger
72#     v[ar] g[lobal]             show global variables
73#     v[ar] l[ocal]              show local variables
74#     v[ar] i[nstance] <object>  show instance variables of object
75#     v[ar] c[onst] <object>     show constants of object
76#     m[ethod] i[nstance] <obj>  show methods of object
77#     m[ethod] <class|module>    show instance methods of class or module
78#     th[read] l[ist]            list all threads
79#     th[read] c[ur[rent]]       show current thread
80#     th[read] [sw[itch]] <nnn>  switch thread context to nnn
81#     th[read] stop <nnn>        stop thread nnn
82#     th[read] resume <nnn>      resume thread nnn
83#     p expression               evaluate expression and print its value
84#     h[elp]                     print this help
85#     <everything else>          evaluate
86#
87# == Usage
88#
89# The following is a list of common functionalities that the debugger
90# provides.
91#
92# === Navigating through your code
93#
94# In general, a debugger is used to find bugs in your program, which
95# often means pausing execution and inspecting variables at some point
96# in time.
97#
98# Let's look at an example:
99#
100#   def my_method(foo)
101#     require 'debug'
102#     foo = get_foo if foo.nil?
103#     raise if foo.nil?
104#   end
105#
106# When you run this program, the debugger will kick in just before the
107# +foo+ assignment.
108#
109#   (rdb:1) p foo
110#   nil
111#
112# In this example, it'd be interesting to move to the next line and
113# inspect the value of +foo+ again. You can do that by pressing +n+:
114#
115#   (rdb:1) n # goes to next line
116#   (rdb:1) p foo
117#   nil
118#
119# You now know that the original value of +foo+ was nil, and that it
120# still was nil after calling +get_foo+.
121#
122# Other useful commands for navigating through your code are:
123#
124# +c+::
125#   Runs the program until it either exists or encounters another breakpoint.
126#   You usually press +c+ when you are finished debugging your program and
127#   want to resume its execution.
128# +s+::
129#   Steps into method definition. In the previous example, +s+ would take you
130#   inside the method definition of +get_foo+.
131# +r+::
132#   Restart the program.
133# +q+::
134#   Quit the program.
135#
136# === Inspecting variables
137#
138# You can use the debugger to easily inspect both local and global variables.
139# We've seen how to inspect local variables before:
140#
141#   (rdb:1) p my_arg
142#   42
143#
144# You can also pretty print the result of variables or expressions:
145#
146#   (rdb:1) pp %w{a very long long array containing many words}
147#   ["a",
148#    "very",
149#    "long",
150#    ...
151#   ]
152#
153# You can list all local variables with +v l+:
154#
155#   (rdb:1) v l
156#     foo => "hello"
157#
158# Similarly, you can show all global variables with +v g+:
159#
160#   (rdb:1) v g
161#     all global variables
162#
163# Finally, you can omit +p+ if you simply want to evaluate a variable or
164# expression
165#
166#   (rdb:1) 5**2
167#   25
168#
169# === Going beyond basics
170#
171# Ruby Debug provides more advanced functionalities like switching
172# between threads, setting breakpoints and watch expressions, and more.
173# The full list of commands is available at any time by pressing +h+.
174#
175# == Staying out of trouble
176#
177# Make sure you remove every instance of +require 'debug'+ before
178# shipping your code. Failing to do so may result in your program
179# hanging unpredictably.
180#
181# Debug is not available in safe mode.
182
183class DEBUGGER__
184  MUTEX = Mutex.new # :nodoc:
185
186  class Context # :nodoc:
187    DEBUG_LAST_CMD = []
188
189    begin
190      require 'readline'
191      def readline(prompt, hist)
192        Readline::readline(prompt, hist)
193      end
194    rescue LoadError
195      def readline(prompt, hist)
196        STDOUT.print prompt
197        STDOUT.flush
198        line = STDIN.gets
199        exit unless line
200        line.chomp!
201        line
202      end
203      USE_READLINE = false
204    end
205
206    def initialize
207      if Thread.current == Thread.main
208        @stop_next = 1
209      else
210        @stop_next = 0
211      end
212      @last_file = nil
213      @file = nil
214      @line = nil
215      @no_step = nil
216      @frames = []
217      @finish_pos = 0
218      @trace = false
219      @catch = "StandardError"
220      @suspend_next = false
221    end
222
223    def stop_next(n=1)
224      @stop_next = n
225    end
226
227    def set_suspend
228      @suspend_next = true
229    end
230
231    def clear_suspend
232      @suspend_next = false
233    end
234
235    def suspend_all
236      DEBUGGER__.suspend
237    end
238
239    def resume_all
240      DEBUGGER__.resume
241    end
242
243    def check_suspend
244      while MUTEX.synchronize {
245          if @suspend_next
246            DEBUGGER__.waiting.push Thread.current
247            @suspend_next = false
248            true
249          end
250        }
251      end
252    end
253
254    def trace?
255      @trace
256    end
257
258    def set_trace(arg)
259      @trace = arg
260    end
261
262    def stdout
263      DEBUGGER__.stdout
264    end
265
266    def break_points
267      DEBUGGER__.break_points
268    end
269
270    def display
271      DEBUGGER__.display
272    end
273
274    def context(th)
275      DEBUGGER__.context(th)
276    end
277
278    def set_trace_all(arg)
279      DEBUGGER__.set_trace(arg)
280    end
281
282    def set_last_thread(th)
283      DEBUGGER__.set_last_thread(th)
284    end
285
286    def debug_eval(str, binding)
287      begin
288        eval(str, binding)
289      rescue StandardError, ScriptError => e
290        at = eval("caller(1)", binding)
291        stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
292        for i in at
293          stdout.printf "\tfrom %s\n", i
294        end
295        throw :debug_error
296      end
297    end
298
299    def debug_silent_eval(str, binding)
300      begin
301        eval(str, binding)
302      rescue StandardError, ScriptError
303        nil
304      end
305    end
306
307    def var_list(ary, binding)
308      ary.sort!
309      for v in ary
310        stdout.printf "  %s => %s\n", v, eval(v.to_s, binding).inspect
311      end
312    end
313
314    def debug_variable_info(input, binding)
315      case input
316      when /^\s*g(?:lobal)?\s*$/
317        var_list(global_variables, binding)
318
319      when /^\s*l(?:ocal)?\s*$/
320        var_list(eval("local_variables", binding), binding)
321
322      when /^\s*i(?:nstance)?\s+/
323        obj = debug_eval($', binding)
324        var_list(obj.instance_variables, obj.instance_eval{binding()})
325
326      when /^\s*c(?:onst(?:ant)?)?\s+/
327        obj = debug_eval($', binding)
328        unless obj.kind_of? Module
329          stdout.print "Should be Class/Module: ", $', "\n"
330        else
331          var_list(obj.constants, obj.module_eval{binding()})
332        end
333      end
334    end
335
336    def debug_method_info(input, binding)
337      case input
338      when /^i(:?nstance)?\s+/
339        obj = debug_eval($', binding)
340
341        len = 0
342        for v in obj.methods.sort
343          len += v.size + 1
344          if len > 70
345            len = v.size + 1
346            stdout.print "\n"
347          end
348          stdout.print v, " "
349        end
350        stdout.print "\n"
351
352      else
353        obj = debug_eval(input, binding)
354        unless obj.kind_of? Module
355          stdout.print "Should be Class/Module: ", input, "\n"
356        else
357          len = 0
358          for v in obj.instance_methods(false).sort
359            len += v.size + 1
360            if len > 70
361              len = v.size + 1
362              stdout.print "\n"
363            end
364            stdout.print v, " "
365          end
366          stdout.print "\n"
367        end
368      end
369    end
370
371    def thnum
372      num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
373      unless num
374        DEBUGGER__.make_thread_list
375        num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
376      end
377      num
378    end
379
380    def debug_command(file, line, id, binding)
381      MUTEX.lock
382      unless defined?($debugger_restart) and $debugger_restart
383        callcc{|c| $debugger_restart = c}
384      end
385      set_last_thread(Thread.current)
386      frame_pos = 0
387      binding_file = file
388      binding_line = line
389      previous_line = nil
390      if ENV['EMACS']
391        stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
392      else
393        stdout.printf "%s:%d:%s", binding_file, binding_line,
394          line_at(binding_file, binding_line)
395      end
396      @frames[0] = [binding, file, line, id]
397      display_expressions(binding)
398      prompt = true
399      while prompt and input = readline("(rdb:%d) "%thnum(), true)
400        catch(:debug_error) do
401          if input == ""
402            next unless DEBUG_LAST_CMD[0]
403            input = DEBUG_LAST_CMD[0]
404            stdout.print input, "\n"
405          else
406            DEBUG_LAST_CMD[0] = input
407          end
408
409          case input
410          when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
411            if defined?( $2 )
412              if $1 == 'on'
413                set_trace_all true
414              else
415                set_trace_all false
416              end
417            elsif defined?( $1 )
418              if $1 == 'on'
419                set_trace true
420              else
421                set_trace false
422              end
423            end
424            if trace?
425              stdout.print "Trace on.\n"
426            else
427              stdout.print "Trace off.\n"
428            end
429
430          when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
431            pos = $2
432            if $1
433              klass = debug_silent_eval($1, binding)
434              file = $1
435            end
436            if pos =~ /^\d+$/
437              pname = pos
438              pos = pos.to_i
439            else
440              pname = pos = pos.intern.id2name
441            end
442            break_points.push [true, 0, klass || file, pos]
443            stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname
444
445          when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/
446            pos = $2.intern.id2name
447            klass = debug_eval($1, binding)
448            break_points.push [true, 0, klass, pos]
449            stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos
450
451          when /^\s*wat(?:ch)?\s+(.+)$/
452            exp = $1
453            break_points.push [true, 1, exp]
454            stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp
455
456          when /^\s*b(?:reak)?$/
457            if break_points.find{|b| b[1] == 0}
458              n = 1
459              stdout.print "Breakpoints:\n"
460              break_points.each do |b|
461                if b[0] and b[1] == 0
462                  stdout.printf "  %d %s:%s\n", n, b[2], b[3]
463                end
464                n += 1
465              end
466            end
467            if break_points.find{|b| b[1] == 1}
468              n = 1
469              stdout.print "\n"
470              stdout.print "Watchpoints:\n"
471              for b in break_points
472                if b[0] and b[1] == 1
473                  stdout.printf "  %d %s\n", n, b[2]
474                end
475                n += 1
476              end
477            end
478            if break_points.size == 0
479              stdout.print "No breakpoints\n"
480            else
481              stdout.print "\n"
482            end
483
484          when /^\s*del(?:ete)?(?:\s+(\d+))?$/
485            pos = $1
486            unless pos
487              input = readline("Clear all breakpoints? (y/n) ", false)
488              if input == "y"
489                for b in break_points
490                  b[0] = false
491                end
492              end
493            else
494              pos = pos.to_i
495              if break_points[pos-1]
496                break_points[pos-1][0] = false
497              else
498                stdout.printf "Breakpoint %d is not defined\n", pos
499              end
500            end
501
502          when /^\s*disp(?:lay)?\s+(.+)$/
503            exp = $1
504            display.push [true, exp]
505            stdout.printf "%d: ", display.size
506            display_expression(exp, binding)
507
508          when /^\s*disp(?:lay)?$/
509            display_expressions(binding)
510
511          when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
512            pos = $1
513            unless pos
514              input = readline("Clear all expressions? (y/n) ", false)
515              if input == "y"
516                for d in display
517                  d[0] = false
518                end
519              end
520            else
521              pos = pos.to_i
522              if display[pos-1]
523                display[pos-1][0] = false
524              else
525                stdout.printf "Display expression %d is not defined\n", pos
526              end
527            end
528
529          when /^\s*c(?:ont)?$/
530            prompt = false
531
532          when /^\s*s(?:tep)?(?:\s+(\d+))?$/
533            if $1
534              lev = $1.to_i
535            else
536              lev = 1
537            end
538            @stop_next = lev
539            prompt = false
540
541          when /^\s*n(?:ext)?(?:\s+(\d+))?$/
542            if $1
543              lev = $1.to_i
544            else
545              lev = 1
546            end
547            @stop_next = lev
548            @no_step = @frames.size - frame_pos
549            prompt = false
550
551          when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
552            display_frames(frame_pos)
553
554          when /^\s*l(?:ist)?(?:\s+(.+))?$/
555            if not $1
556              b = previous_line ? previous_line + 10 : binding_line - 5
557              e = b + 9
558            elsif $1 == '-'
559              b = previous_line ? previous_line - 10 : binding_line - 5
560              e = b + 9
561            else
562              b, e = $1.split(/[-,]/)
563              if e
564                b = b.to_i
565                e = e.to_i
566              else
567                b = b.to_i - 5
568                e = b + 9
569              end
570            end
571            previous_line = b
572            display_list(b, e, binding_file, binding_line)
573
574          when /^\s*up(?:\s+(\d+))?$/
575            previous_line = nil
576            if $1
577              lev = $1.to_i
578            else
579              lev = 1
580            end
581            frame_pos += lev
582            if frame_pos >= @frames.size
583              frame_pos = @frames.size - 1
584              stdout.print "At toplevel\n"
585            end
586            binding, binding_file, binding_line = @frames[frame_pos]
587            stdout.print format_frame(frame_pos)
588
589          when /^\s*down(?:\s+(\d+))?$/
590            previous_line = nil
591            if $1
592              lev = $1.to_i
593            else
594              lev = 1
595            end
596            frame_pos -= lev
597            if frame_pos < 0
598              frame_pos = 0
599              stdout.print "At stack bottom\n"
600            end
601            binding, binding_file, binding_line = @frames[frame_pos]
602            stdout.print format_frame(frame_pos)
603
604          when /^\s*fin(?:ish)?$/
605            if frame_pos == @frames.size
606              stdout.print "\"finish\" not meaningful in the outermost frame.\n"
607            else
608              @finish_pos = @frames.size - frame_pos
609              frame_pos = 0
610              prompt = false
611            end
612
613          when /^\s*cat(?:ch)?(?:\s+(.+))?$/
614            if $1
615              excn = $1
616              if excn == 'off'
617                @catch = nil
618                stdout.print "Clear catchpoint.\n"
619              else
620                @catch = excn
621                stdout.printf "Set catchpoint %s.\n", @catch
622              end
623            else
624              if @catch
625                stdout.printf "Catchpoint %s.\n", @catch
626              else
627                stdout.print "No catchpoint.\n"
628              end
629            end
630
631          when /^\s*q(?:uit)?$/
632            input = readline("Really quit? (y/n) ", false)
633            if input == "y"
634              exit!  # exit -> exit!: No graceful way to stop threads...
635            end
636
637          when /^\s*v(?:ar)?\s+/
638            debug_variable_info($', binding)
639
640          when /^\s*m(?:ethod)?\s+/
641            debug_method_info($', binding)
642
643          when /^\s*th(?:read)?\s+/
644            if DEBUGGER__.debug_thread_info($', binding) == :cont
645              prompt = false
646            end
647
648          when /^\s*pp\s+/
649            PP.pp(debug_eval($', binding), stdout)
650
651          when /^\s*p\s+/
652            stdout.printf "%s\n", debug_eval($', binding).inspect
653
654          when /^\s*r(?:estart)?$/
655            $debugger_restart.call
656
657          when /^\s*h(?:elp)?$/
658            debug_print_help()
659
660          else
661            v = debug_eval(input, binding)
662            stdout.printf "%s\n", v.inspect
663          end
664        end
665      end
666      MUTEX.unlock
667      resume_all
668    end
669
670    def debug_print_help
671      stdout.print <<EOHELP
672Debugger help v.-0.002b
673Commands
674  b[reak] [file:|class:]<line|method>
675  b[reak] [class.]<line|method>
676                             set breakpoint to some position
677  wat[ch] <expression>       set watchpoint to some expression
678  cat[ch] (<exception>|off)  set catchpoint to an exception
679  b[reak]                    list breakpoints
680  cat[ch]                    show catchpoint
681  del[ete][ nnn]             delete some or all breakpoints
682  disp[lay] <expression>     add expression into display expression list
683  undisp[lay][ nnn]          delete one particular or all display expressions
684  c[ont]                     run until program ends or hit breakpoint
685  s[tep][ nnn]               step (into methods) one line or till line nnn
686  n[ext][ nnn]               go over one line or till line nnn
687  w[here]                    display frames
688  f[rame]                    alias for where
689  l[ist][ (-|nn-mm)]         list program, - lists backwards
690                             nn-mm lists given lines
691  up[ nn]                    move to higher frame
692  down[ nn]                  move to lower frame
693  fin[ish]                   return to outer frame
694  tr[ace] (on|off)           set trace mode of current thread
695  tr[ace] (on|off) all       set trace mode of all threads
696  q[uit]                     exit from debugger
697  v[ar] g[lobal]             show global variables
698  v[ar] l[ocal]              show local variables
699  v[ar] i[nstance] <object>  show instance variables of object
700  v[ar] c[onst] <object>     show constants of object
701  m[ethod] i[nstance] <obj>  show methods of object
702  m[ethod] <class|module>    show instance methods of class or module
703  th[read] l[ist]            list all threads
704  th[read] c[ur[rent]]       show current thread
705  th[read] [sw[itch]] <nnn>  switch thread context to nnn
706  th[read] stop <nnn>        stop thread nnn
707  th[read] resume <nnn>      resume thread nnn
708  pp expression              evaluate expression and pretty_print its value
709  p expression               evaluate expression and print its value
710  r[estart]                  restart program
711  h[elp]                     print this help
712  <everything else>          evaluate
713EOHELP
714    end
715
716    def display_expressions(binding)
717      n = 1
718      for d in display
719        if d[0]
720          stdout.printf "%d: ", n
721          display_expression(d[1], binding)
722        end
723        n += 1
724      end
725    end
726
727    def display_expression(exp, binding)
728      stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
729    end
730
731    def frame_set_pos(file, line)
732      if @frames[0]
733        @frames[0][1] = file
734        @frames[0][2] = line
735      end
736    end
737
738    def display_frames(pos)
739      0.upto(@frames.size - 1) do |n|
740        if n == pos
741          stdout.print "--> "
742        else
743          stdout.print "    "
744        end
745        stdout.print format_frame(n)
746      end
747    end
748
749    def format_frame(pos)
750      _, file, line, id = @frames[pos]
751      sprintf "#%d %s:%s%s\n", pos + 1, file, line,
752        (id ? ":in `#{id.id2name}'" : "")
753    end
754
755    def display_list(b, e, file, line)
756      stdout.printf "[%d, %d] in %s\n", b, e, file
757      if lines = SCRIPT_LINES__[file] and lines != true
758        b.upto(e) do |n|
759          if n > 0 && lines[n-1]
760            if n == line
761              stdout.printf "=> %d  %s\n", n, lines[n-1].chomp
762            else
763              stdout.printf "   %d  %s\n", n, lines[n-1].chomp
764            end
765          end
766        end
767      else
768        stdout.printf "No sourcefile available for %s\n", file
769      end
770    end
771
772    def line_at(file, line)
773      lines = SCRIPT_LINES__[file]
774      if lines
775        return "\n" if lines == true
776        line = lines[line-1]
777        return "\n" unless line
778        return line
779      end
780      return "\n"
781    end
782
783    def debug_funcname(id)
784      if id.nil?
785        "toplevel"
786      else
787        id.id2name
788      end
789    end
790
791    def check_break_points(file, klass, pos, binding, id)
792      return false if break_points.empty?
793      n = 1
794      for b in break_points
795        if b[0]           # valid
796          if b[1] == 0    # breakpoint
797            if (b[2] == file and b[3] == pos) or
798                (klass and b[2] == klass and b[3] == pos)
799              stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
800              return true
801            end
802          elsif b[1] == 1 # watchpoint
803            if debug_silent_eval(b[2], binding)
804              stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
805              return true
806            end
807          end
808        end
809        n += 1
810      end
811      return false
812    end
813
814    def excn_handle(file, line, id, binding)
815      if $!.class <= SystemExit
816        set_trace_func nil
817        exit
818      end
819
820      if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch })
821        stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class
822        fs = @frames.size
823        tb = caller(0)[-fs..-1]
824        if tb
825          for i in tb
826            stdout.printf "\tfrom %s\n", i
827          end
828        end
829        suspend_all
830        debug_command(file, line, id, binding)
831      end
832    end
833
834    def trace_func(event, file, line, id, binding, klass)
835      Tracer.trace_func(event, file, line, id, binding, klass) if trace?
836      context(Thread.current).check_suspend
837      @file = file
838      @line = line
839      case event
840      when 'line'
841        frame_set_pos(file, line)
842        if !@no_step or @frames.size == @no_step
843          @stop_next -= 1
844          @stop_next = -1 if @stop_next < 0
845        elsif @frames.size < @no_step
846          @stop_next = 0          # break here before leaving...
847        else
848          # nothing to do. skipped.
849        end
850        if @stop_next == 0 or check_break_points(file, nil, line, binding, id)
851          @no_step = nil
852          suspend_all
853          debug_command(file, line, id, binding)
854        end
855
856      when 'call'
857        @frames.unshift [binding, file, line, id]
858        if check_break_points(file, klass, id.id2name, binding, id)
859          suspend_all
860          debug_command(file, line, id, binding)
861        end
862
863      when 'c-call'
864        frame_set_pos(file, line)
865
866      when 'class'
867        @frames.unshift [binding, file, line, id]
868
869      when 'return', 'end'
870        if @frames.size == @finish_pos
871          @stop_next = 1
872          @finish_pos = 0
873        end
874        @frames.shift
875
876      when 'raise'
877        excn_handle(file, line, id, binding)
878
879      end
880      @last_file = file
881    end
882  end
883
884  trap("INT") { DEBUGGER__.interrupt }
885  @last_thread = Thread::main
886  @max_thread = 1
887  @thread_list = {Thread::main => 1}
888  @break_points = []
889  @display = []
890  @waiting = []
891  @stdout = STDOUT
892
893  class << DEBUGGER__
894    # Returns the IO used as stdout. Defaults to STDOUT
895    def stdout
896      @stdout
897    end
898
899    # Sets the IO used as stdout. Defaults to STDOUT
900    def stdout=(s)
901      @stdout = s
902    end
903
904    # Returns the display expression list
905    #
906    # See DEBUGGER__ for more usage
907    def display
908      @display
909    end
910
911    # Returns the list of break points where execution will be stopped.
912    #
913    # See DEBUGGER__ for more useage
914    def break_points
915      @break_points
916    end
917
918    # Returns the list of waiting threads.
919    #
920    # When stepping through the traces of a function, thread gets suspended, to
921    # be resumed later.
922    def waiting
923      @waiting
924    end
925
926    def set_trace( arg )
927      MUTEX.synchronize do
928        make_thread_list
929        for th, in @thread_list
930          context(th).set_trace arg
931        end
932      end
933      arg
934    end
935
936    def set_last_thread(th)
937      @last_thread = th
938    end
939
940    def suspend
941      MUTEX.synchronize do
942        make_thread_list
943        for th, in @thread_list
944          next if th == Thread.current
945          context(th).set_suspend
946        end
947      end
948      # Schedule other threads to suspend as soon as possible.
949      Thread.pass
950    end
951
952    def resume
953      MUTEX.synchronize do
954        make_thread_list
955        @thread_list.each do |th,|
956          next if th == Thread.current
957          context(th).clear_suspend
958        end
959        waiting.each do |th|
960          th.run
961        end
962        waiting.clear
963      end
964      # Schedule other threads to restart as soon as possible.
965      Thread.pass
966    end
967
968    def context(thread=Thread.current)
969      c = thread[:__debugger_data__]
970      unless c
971        thread[:__debugger_data__] = c = Context.new
972      end
973      c
974    end
975
976    def interrupt
977      context(@last_thread).stop_next
978    end
979
980    def get_thread(num)
981      th = @thread_list.key(num)
982      unless th
983        @stdout.print "No thread ##{num}\n"
984        throw :debug_error
985      end
986      th
987    end
988
989    def thread_list(num)
990      th = get_thread(num)
991      if th == Thread.current
992        @stdout.print "+"
993      else
994        @stdout.print " "
995      end
996      @stdout.printf "%d ", num
997      @stdout.print th.inspect, "\t"
998      file = context(th).instance_eval{@file}
999      if file
1000        @stdout.print file,":",context(th).instance_eval{@line}
1001      end
1002      @stdout.print "\n"
1003    end
1004
1005    def thread_list_all
1006      for th in @thread_list.values.sort
1007        thread_list(th)
1008      end
1009    end
1010
1011    def make_thread_list
1012      hash = {}
1013      for th in Thread::list
1014        if @thread_list.key? th
1015          hash[th] = @thread_list[th]
1016        else
1017          @max_thread += 1
1018          hash[th] = @max_thread
1019        end
1020      end
1021      @thread_list = hash
1022    end
1023
1024    def debug_thread_info(input, binding)
1025      case input
1026      when /^l(?:ist)?/
1027        make_thread_list
1028        thread_list_all
1029
1030      when /^c(?:ur(?:rent)?)?$/
1031        make_thread_list
1032        thread_list(@thread_list[Thread.current])
1033
1034      when /^(?:sw(?:itch)?\s+)?(\d+)/
1035        make_thread_list
1036        th = get_thread($1.to_i)
1037        if th == Thread.current
1038          @stdout.print "It's the current thread.\n"
1039        else
1040          thread_list(@thread_list[th])
1041          context(th).stop_next
1042          th.run
1043          return :cont
1044        end
1045
1046      when /^stop\s+(\d+)/
1047        make_thread_list
1048        th = get_thread($1.to_i)
1049        if th == Thread.current
1050          @stdout.print "It's the current thread.\n"
1051        elsif th.stop?
1052          @stdout.print "Already stopped.\n"
1053        else
1054          thread_list(@thread_list[th])
1055          context(th).suspend
1056        end
1057
1058      when /^resume\s+(\d+)/
1059        make_thread_list
1060        th = get_thread($1.to_i)
1061        if th == Thread.current
1062          @stdout.print "It's the current thread.\n"
1063        elsif !th.stop?
1064          @stdout.print "Already running."
1065        else
1066          thread_list(@thread_list[th])
1067          th.run
1068        end
1069      end
1070    end
1071  end
1072
1073  stdout.printf "Debug.rb\n"
1074  stdout.printf "Emacs support available.\n\n"
1075  RubyVM::InstructionSequence.compile_option = {
1076    trace_instruction: true
1077  }
1078  set_trace_func proc { |event, file, line, id, binding, klass, *rest|
1079    DEBUGGER__.context.trace_func event, file, line, id, binding, klass
1080  }
1081end
1082