| require 'common/Logger' | |
| require 'dbgp/DbgpThread' | |
| require 'dbgp/ThreadEventHandler' | |
| require 'dbgp/Utils' | |
| require 'debugger/DebugEventHandler' | |
| module XoredDebugger | |
| class ThreadManager | |
| include ThreadEventHandler | |
| include DebugEventHandler | |
| include Logger | |
| include XoredDebuggerUtils | |
| attr_reader :debugger | |
| attr_reader :capture_manager | |
| attr_reader :source_manager | |
| def initialize(debugger) | |
| begin | |
| @debugger = debugger | |
| @debugger.handler = self | |
| @capture_manager = CaptureManager.new(self) | |
| @source_manager = SourceManager.instance | |
| Thread.set_event_handler(self) | |
| # fake started event for main thread | |
| started() | |
| rescue Exception | |
| Thread.set_event_handler(nil) | |
| end | |
| end | |
| def terminate(excpt = nil) | |
| log('Terminating thread manager') | |
| begin | |
| Thread.list.each do |thread| | |
| dbgp_thread = thread[ :dbgp_thread_wrapper ] | |
| dbgp_thread.exited(nil) unless dbgp_thread.nil? || Thread.main == thread | |
| end | |
| print_exception(excpt) unless excpt.nil? || excpt.is_a?(SystemExit) | |
| @capture_manager.terminate | |
| exited(excpt) | |
| ensure | |
| Thread.set_event_handler(nil) | |
| end | |
| end | |
| def get_dbgp_thread(thread) | |
| thread[ :dbgp_thread_wrapper ] | |
| end | |
| def started() | |
| if (@debugger.is_debug_thread?(Thread.current)) | |
| return | |
| end | |
| log('Application thread started') | |
| thread = Thread.current | |
| thread[ :dbgp_thread_wrapper ] = DbgpThread.new(thread, self) | |
| # suspending current context, till wrapper initialized | |
| @debugger.current_context.suspend | |
| end | |
| def exited(excpt) | |
| if (@debugger.is_debug_thread?(Thread.current)) | |
| return | |
| end | |
| log('Application thread exited') | |
| logException(excpt, 'at exited()') unless excpt.nil? | |
| dbgp_thread = Thread.current[ :dbgp_thread_wrapper ] | |
| Thread.current[ :dbgp_thread_wrapper ] = nil | |
| dbgp_thread.exited(excpt) unless dbgp_thread.nil? | |
| end | |
| def at_breakpoint(context) | |
| log('at_breakpoint') | |
| dbgp_thread = Thread.current[ :dbgp_thread_wrapper ] | |
| dbgp_thread.at_breakpoint(context) unless dbgp_thread.nil? | |
| end | |
| def at_catchpoint(context, excpt) | |
| log('at_catchpoint: ' + excpt.class.name) | |
| dbgp_thread = Thread.current[ :dbgp_thread_wrapper ] | |
| dbgp_thread.at_catchpoint(context, excpt) unless dbgp_thread.nil? | |
| end | |
| def at_line(context, file, line) | |
| log('at_line: ' + file + ':' + line.to_s) | |
| if (in_debugger_code?(context)) | |
| log('stepover debugger code') | |
| context.suspend unless context.status == AbstractContext::BREAK | |
| context.step_over() | |
| else | |
| dbgp_thread = Thread.current[ :dbgp_thread_wrapper ] | |
| dbgp_thread.at_line(context, file, line) unless dbgp_thread.nil? | |
| end | |
| end | |
| def in_debugger_code?(context) | |
| # Don't debug debugger :) | |
| depth = get_stack_depth(context) | |
| if (depth == 0) | |
| return true | |
| end | |
| depth.times { |index| | |
| sf = context.stack_frame(index) | |
| file = sf.file | |
| method = sf.method.to_s | |
| if (method.index('XoredDebugger::') == 0 || !file.index(@debugger.get_debugger_id).nil?) | |
| return true | |
| end | |
| } | |
| false | |
| end | |
| # TODO: Why this is not printed by VM | |
| def print_exception(ex) | |
| message = ex.backtrace.delete_if { |s| s.index(@debugger.get_debugger_id) != nil } | |
| message[0] += ': ' + ex.message + ' (' + ex.class.name + ')' | |
| message.each_index { | |
| |i| message[i] = (i==0 ? message[i] : "\t from " + message[i]) | |
| } | |
| $stderr.write( message.join("\n")+"\n" ) | |
| end | |
| end | |
| end |