diff options
author | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-16 18:49:26 +0900 |
---|---|---|
committer | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-30 00:39:06 +0900 |
commit | fcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch) | |
tree | 64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/debug.rb |
Fresh start
Diffstat (limited to 'jni/ruby/lib/debug.rb')
-rw-r--r-- | jni/ruby/lib/debug.rb | 1087 |
1 files changed, 1087 insertions, 0 deletions
diff --git a/jni/ruby/lib/debug.rb b/jni/ruby/lib/debug.rb new file mode 100644 index 0000000..fcbf849 --- /dev/null +++ b/jni/ruby/lib/debug.rb @@ -0,0 +1,1087 @@ +# 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> + +require 'continuation' + +if $SAFE > 0 + STDERR.print "-r debug.rb is not available in safe mode\n" + exit 1 +end + +require 'tracer' +require 'pp' + +class Tracer # :nodoc: + def Tracer.trace_func(*vars) + Single.trace_func(*vars) + end +end + +SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ # :nodoc: + +## +# This library provides debugging functionality to Ruby. +# +# To add a debugger to your code, start by requiring +debug+ in your +# program: +# +# def say(word) +# require 'debug' +# puts word +# end +# +# This will cause Ruby to interrupt execution and show a prompt when the +say+ +# method is run. +# +# Once you're inside the prompt, you can start debugging your program. +# +# (rdb:1) p word +# "hello" +# +# == Getting help +# +# You can get help at any time by pressing +h+. +# +# (rdb:1) h +# 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] (<exception>|off) 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] 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 +# +# == Usage +# +# The following is a list of common functionalities that the debugger +# provides. +# +# === Navigating through your code +# +# In general, a debugger is used to find bugs in your program, which +# often means pausing execution and inspecting variables at some point +# in time. +# +# Let's look at an example: +# +# def my_method(foo) +# require 'debug' +# foo = get_foo if foo.nil? +# raise if foo.nil? +# end +# +# When you run this program, the debugger will kick in just before the +# +foo+ assignment. +# +# (rdb:1) p foo +# nil +# +# In this example, it'd be interesting to move to the next line and +# inspect the value of +foo+ again. You can do that by pressing +n+: +# +# (rdb:1) n # goes to next line +# (rdb:1) p foo +# nil +# +# You now know that the original value of +foo+ was nil, and that it +# still was nil after calling +get_foo+. +# +# Other useful commands for navigating through your code are: +# +# +c+:: +# Runs the program until it either exists or encounters another breakpoint. +# You usually press +c+ when you are finished debugging your program and +# want to resume its execution. +# +s+:: +# Steps into method definition. In the previous example, +s+ would take you +# inside the method definition of +get_foo+. +# +r+:: +# Restart the program. +# +q+:: +# Quit the program. +# +# === Inspecting variables +# +# You can use the debugger to easily inspect both local and global variables. +# We've seen how to inspect local variables before: +# +# (rdb:1) p my_arg +# 42 +# +# You can also pretty print the result of variables or expressions: +# +# (rdb:1) pp %w{a very long long array containing many words} +# ["a", +# "very", +# "long", +# ... +# ] +# +# You can list all local variables with +v l+: +# +# (rdb:1) v l +# foo => "hello" +# +# Similarly, you can show all global variables with +v g+: +# +# (rdb:1) v g +# all global variables +# +# Finally, you can omit +p+ if you simply want to evaluate a variable or +# expression +# +# (rdb:1) 5**2 +# 25 +# +# === Going beyond basics +# +# Ruby Debug provides more advanced functionalities like switching +# between threads, setting breakpoints and watch expressions, and more. +# The full list of commands is available at any time by pressing +h+. +# +# == Staying out of trouble +# +# Make sure you remove every instance of +require 'debug'+ before +# shipping your code. Failing to do so may result in your program +# hanging unpredictably. +# +# Debug is not available in safe mode. + +class DEBUGGER__ + MUTEX = Mutex.new # :nodoc: + + class Context # :nodoc: + 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 MUTEX.synchronize { + if @suspend_next + DEBUGGER__.waiting.push Thread.current + @suspend_next = false + true + end + } + end + 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 + 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 + + def var_list(ary, binding) + ary.sort! + for v in ary + stdout.printf " %s => %s\n", v, eval(v.to_s, binding).inspect + end + end + + def debug_variable_info(input, binding) + case input + when /^\s*g(?:lobal)?\s*$/ + var_list(global_variables, binding) + + when /^\s*l(?:ocal)?\s*$/ + 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) + + 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 + unless defined?($debugger_restart) and $debugger_restart + callcc{|c| $debugger_restart = c} + end + 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*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 + 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:%s\n", break_points.size, exp + + when /^\s*b(?:reak)?$/ + if break_points.find{|b| b[1] == 0} + n = 1 + stdout.print "Breakpoints:\n" + break_points.each do |b| + 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+/ + PP.pp(debug_eval($', binding), stdout) + + when /^\s*p\s+/ + stdout.printf "%s\n", debug_eval($', binding).inspect + + when /^\s*r(?:estart)?$/ + $debugger_restart.call + + 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] (<exception>|off) 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] 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 + pp expression evaluate expression and pretty_print its value + p expression evaluate expression and print its value + r[estart] restart program + 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) + _, file, line, id = @frames[pos] + sprintf "#%d %s:%s%s\n", pos + 1, file, line, + (id ? ":in `#{id.id2name}'" : "") + end + + def script_lines(file, line) + unless (lines = SCRIPT_LINES__[file]) and lines != true + Tracer::Single.get_line(file, line) if File.exist?(file) + lines = SCRIPT_LINES__[file] + lines = nil if lines == true + end + lines + end + + def display_list(b, e, file, line) + if lines = script_lines(file, line) + stdout.printf "[%d, %d] in %s\n", b, e, file + 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, line) + if lines and line = lines[line-1] + 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 + @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 + if @stop_next == 0 or check_break_points(file, nil, 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, 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 '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__ + # Returns the IO used as stdout. Defaults to STDOUT + def stdout + @stdout + end + + # Sets the IO used as stdout. Defaults to STDOUT + def stdout=(s) + @stdout = s + end + + # Returns the display expression list + # + # See DEBUGGER__ for more usage + def display + @display + end + + # Returns the list of break points where execution will be stopped. + # + # See DEBUGGER__ for more usage + def break_points + @break_points + end + + # Returns the list of waiting threads. + # + # When stepping through the traces of a function, thread gets suspended, to + # be resumed later. + def waiting + @waiting + end + + def set_trace( arg ) + MUTEX.synchronize do + make_thread_list + for th, in @thread_list + context(th).set_trace arg + end + end + arg + end + + def set_last_thread(th) + @last_thread = th + end + + def suspend + MUTEX.synchronize do + make_thread_list + for th, in @thread_list + next if th == Thread.current + context(th).set_suspend + end + end + # Schedule other threads to suspend as soon as possible. + Thread.pass + end + + def resume + MUTEX.synchronize do + make_thread_list + @thread_list.each do |th,| + next if th == Thread.current + context(th).clear_suspend + end + waiting.each do |th| + th.run + end + waiting.clear + end + # 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.key(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 + + stdout.printf "Debug.rb\n" + stdout.printf "Emacs support available.\n\n" + RubyVM::InstructionSequence.compile_option = { + trace_instruction: true + } + set_trace_func proc { |event, file, line, id, binding, klass, *rest| + DEBUGGER__.context.trace_func event, file, line, id, binding, klass + } +end |