From: "NAKAMURA, Hiroshi" Date: 2000-12-25T16:17:40+09:00 Subject: [ruby-dev:11846] [Patch] debug.rb: Re: Debugging thread なひです。 > From: Yukihiro Matsumoto > Sent: Friday, December 08, 2000 10:22 AM > |> |1-a. いずれかのthreadがbpなどで停止すると、全threadが止まる。 > |> |1-b. いずれかのthreadがbpなどで停止すると、そのthreadのみが止まる。 > |> |2. thread stopすると、指定されたthreadが止まる。 > |> |3. thread resumeすると、指定されたthreadが再開する。 > |> | > |> |とかですかね?ちょっと排他制御が難しくなりそう。。。 > |> > |> 2,3はその通りです。1は1-bが望ましいと思ってます。 > | > |A) 1-aが欲しいシチュエーションはどうしましょうか。 > |「何か起こった時」の状態を検査したい場合、 > |動きつづけられると困る場合もあると思います。 > > ああ、そうか。たとえば1-aで、次の再開(nとかcとか)で全部が動 > きだすとかが可能であればその方が良いかもしれませんね。 そのように変更しました。^Cやbpでは、全スレッドが止まります。 n/s/cでは全スレッドを再開します。 デバッグコマンドとして、thread resume ##、thread stop ##で、 指定したスレッドの再開/停止をします。 デバッグ中のカレントスレッドは再開しません。 従来、thread stop ##は、thread ##と同じく、「カレントスレッドを 再開し、指定したスレッドを止める」すなわちスレッドの切り替えでしたが、 上記のように意味が変わっていますのでご注意ください。 trace on/offは、[ruby-dev:11785]のtracer.rbへのpatchを当てないと動きません。 1.6.2に間に合わないだろうことは承知してます。 patchを出すのが遅すぎました。動作確認に時間がかかってしまいました。 って書いてるところで[ruby-list:26846]が届きました。^^; --- /usr/local/lib/ruby/1.6/debug.rb Sun Oct 22 14:11:28 2000 +++ ./debug.rb Mon Dec 25 16:08:20 2000 @@ -92,4 +92,5 @@ class DEBUGGER__ @trace = false @catch = "StandardError" + @suspend_next = false end @@ -98,14 +99,45 @@ class DEBUGGER__ end + def suspend + @suspend_next = true + end + + def check_suspend + while (Thread.critical = true; @suspend_next) + waiting.push Thread.current + @suspend_next = false + Thread.stop + end + Thread.critical = false + end + + def trace? + @trace + end + + def set_trace(arg) + @trace = arg + end + def stdout DEBUGGER__.stdout end + def break_points DEBUGGER__.break_points end + def display DEBUGGER__.display end + def waiting + DEBUGGER__.waiting + end + + def set_trace_all(arg) + DEBUGGER__.set_trace(arg) + end + def debug_eval(str, binding) begin @@ -219,5 +251,6 @@ class DEBUGGER__ @frames[0] = [binding, file, line, id] display_expressions(binding) - while input = readline("(rdb:%d) "%thnum(), true) + prompt = true + while prompt and input = readline("(rdb:%d) "%thnum(), true) catch(:debug_error) do if input == "" @@ -229,16 +262,22 @@ class DEBUGGER__ case input - when /^\s*tr(?:ace)?(?:\s+(on|off))?$/ - if defined?( $1 ) + 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' - @trace = true + set_trace true else - @trace = false + set_trace false end end - if @trace - stdout.print "Trace on\n" + if trace? + stdout.print "Trace on.\n" else - stdout.print "Trace off\n" + stdout.print "Trace off.\n" end @@ -337,6 +376,5 @@ class DEBUGGER__ when /^\s*c(?:ont)?$/ - MUTEX.unlock - return + prompt = false when /^\s*s(?:tep)?(?:\s+(\d+))?$/ @@ -347,5 +385,5 @@ class DEBUGGER__ end @stop_next = lev - return + prompt = false when /^\s*n(?:ext)?(?:\s+(\d+))?$/ @@ -357,5 +395,5 @@ class DEBUGGER__ @stop_next = lev @no_step = @frames.size - frame_pos - return + prompt = false when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/ @@ -418,6 +456,5 @@ class DEBUGGER__ @finish_pos = @frames.size - frame_pos frame_pos = 0 - MUTEX.unlock - return + prompt = false end @@ -441,6 +478,8 @@ class DEBUGGER__ when /^\s*q(?:uit)?$/ - input = readline("really quit? (y/n) ", false) - exit if input == "y" + 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+/ @@ -452,6 +491,5 @@ class DEBUGGER__ when /^\s*th(?:read)?\s+/ if DEBUGGER__.debug_thread_info($', binding) == :cont - MUTEX.unlock - return + prompt = false end @@ -468,4 +506,6 @@ class DEBUGGER__ end end + MUTEX.unlock + DEBUGGER__.resume_all_thread end @@ -493,5 +533,6 @@ Commands down[ nn] move to lower frame fin[ish] return to outer frame - tr[ace][ (on|off)] set trace mode + 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 @@ -502,9 +543,8 @@ Commands m[ethod] show instance methods of class or module th[read] l[ist] list all threads - th[read] c[ur[rent]] show current threads - th[read] stop thread nnn - th[read] stop alias for th[read] - th[read] c[ur[rent]] alias for th[read] - th[read] resume run thread nnn + th[read] c[ur[rent]] show current thread + th[read] [sw[itch]] switch thread context to nnn + th[read] stop stop thread nnn + th[read] resume resume thread nnn p expression evaluate expression and print its value h[elp] print this help @@ -588,5 +628,5 @@ EOHELP def check_break_points(file, pos, binding, id) - MUTEX.lock # Stop all threads before 'line' and 'call'. + return false if break_points.empty? file = File.basename(file) n = 1 @@ -605,5 +645,4 @@ EOHELP n += 1 end - MUTEX.unlock return false end @@ -617,5 +656,4 @@ EOHELP if @catch and ($!.type.ancestors.find { |e| e.to_s == @catch }) - MUTEX.lock fs = @frames.size tb = caller(0)[-fs..-1] @@ -625,4 +663,5 @@ EOHELP end end + DEBUGGER__.suspend_all_thread debug_command(file, line, id, binding) end @@ -630,5 +669,6 @@ EOHELP def trace_func(event, file, line, id, binding, klass) - Tracer.trace_func(event, file, line, id, binding) if @trace + Tracer.trace_func(event, file, line, id, binding, klass) if trace? + DEBUGGER__.context(Thread.current).check_suspend @file = file @line = line @@ -648,4 +688,5 @@ EOHELP else @no_step = nil + DEBUGGER__.suspend_all_thread debug_command(file, line, id, binding) @last = [file, line] @@ -657,4 +698,5 @@ EOHELP if check_break_points(file, id.id2name, binding, id) or check_break_points(klass.to_s, id.id2name, binding, id) + DEBUGGER__.suspend_all_thread debug_command(file, line, id, binding) end @@ -683,6 +725,5 @@ EOHELP end - trap("INT") { DEBUGGER__.interrupt } -# $DEBUG = true + trap("INT") { DEBUGGER__.interrupt } @last_thread = Thread::main @max_thread = 1 @@ -690,4 +731,5 @@ EOHELP @break_points = [] @display = [] + @waiting = [] @stdout = STDOUT @@ -696,4 +738,5 @@ EOHELP @stdout end + def stdout=(s) @stdout = s @@ -708,8 +751,44 @@ EOHELP end + def waiting + @waiting + end + + def set_trace( arg ) + Thread.critical = true + make_thread_list + for th in @thread_list + context(th[0]).set_trace arg + end + Thread.critical = false + end + def set_last_thread(th) @last_thread = th end + def suspend_all_thread + Thread.critical = true + make_thread_list + for th in @thread_list + next if th[0] == Thread.current + context(th[0]).suspend + end + Thread.critical = false + # Schedule other threads to suspend as soon as possible. + Thread.pass + end + + def resume_all_thread + Thread.critical = true + waiting.each do |th| + th.run + end + waiting.clear + Thread.critical = false + # Schedule other threads to restart as soon as possible. + Thread.pass + end + def context(thread=Thread.current) c = thread[:__debugger_data__] @@ -727,5 +806,5 @@ EOHELP th = @thread_list.index(num) unless th - @stdout.print "no thread no.", num, "\n" + @stdout.print "No thread ##{num}\n" throw :debug_error end @@ -774,22 +853,43 @@ EOHELP thread_list_all - when /^c(?:ur(?:rent)?)?\s+(\d+)/, /^stop\s+(\d+)/, /^(\d+)/ + 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) - thread_list(@thread_list[th]) - context(th).stop_next - th.run - return :cont + 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 /^c(?:ur(?:rent)?)?$/ + when /^stop\s+(\d+)/ make_thread_list - thread_list(@thread_list[Thread.current]) + 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) - thread_list(@thread_list[th]) - th.run - return :cont + 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 @@ -798,6 +898,6 @@ EOHELP stdout.printf "Debug.rb\n" stdout.printf "Emacs support available.\n\n" - set_trace_func proc{|event, file, line, id, binding,klass,*rest| - DEBUGGER__.context.trace_func event, file, line, id, binding,klass + set_trace_func proc { |event, file, line, id, binding, klass, *rest| + DEBUGGER__.context.trace_func event, file, line, id, binding, klass } end