def cleanup(cls, w=None): global _is_debugging _is_debugging = False set_disabled_bps([]) ui_updater().stop() driver = driver_instance() if driver: driver.stop() set_driver_instance(None) lldb_view_send('\nDebugging session ended.\n')
def __init__(self, window, log_callback=None, process_stopped_callback=None, on_exit_callback=None): super(LldbDriver, self).__init__(name='sublime.lldb.driver') self.__window = window lldb.SBDebugger.Initialize() self.__broadcaster = lldb.SBBroadcaster('Driver') self.__process_stopped_callback = process_stopped_callback self.__on_exit_callback = on_exit_callback # if log_callback: # self._debugger = lldb.SBDebugger.Create(False, log_callback) # else: self._debugger = lldb.SBDebugger.Create(False) self.__listener = self._debugger.GetListener() set_driver_instance(self) r, w = os.pipe() self.__io_channel_r_fh = os.fdopen(r, 'r', 0) self.__io_channel_w_fh = os.fdopen(w, 'w', 0) self.__io_channel = IOChannel(self, self.__io_channel_r_fh, lldb_view_send) # self._debugger.SetCloseInputOnEOF(False) self.__input_reader = lldb.SBInputReader()
def run(self): thread_created('<' + self.name + '>') sb_interpreter = self._debugger.GetCommandInterpreter() #listener = self._debugger.GetListener() listener = self.__listener listener.StartListeningForEventClass(self._debugger, lldb.SBTarget.GetBroadcasterClassName(), lldb.SBTarget.eBroadcastBitBreakpointChanged) # This isn't in Driver.cpp. Check why it listens to those events (because it uses SBDebugger's listener?) # listener.StartListeningForEventClass(self._debugger, # lldb.SBProcess.GetBroadcasterClassName(), # lldb.SBProcess.eBroadcastBitStateChanged | \ # lldb.SBProcess.eBroadcastBitInterrupt | \ # lldb.SBProcess.eBroadcastBitSTDOUT | \ # lldb.SBProcess.eBroadcastBitSTDERR) # Warn whoever started us that we can start working self.broadcaster.BroadcastEventByType(LldbDriver.eBroadcastBitThreadDidStart) # Create pipes for communicating with the debugger in_pipe_fd, out_pipe_fd = os.pipe() self.__from_debugger_fh_r = os.fdopen(in_pipe_fd, 'r', 0) self.__from_debugger_fh_w = os.fdopen(out_pipe_fd, 'w', 0) in_pipe_fd, out_pipe_fd = os.pipe() self.__to_debugger_fh_r = os.fdopen(in_pipe_fd, 'r', 0) self.__to_debugger_fh_w = os.fdopen(out_pipe_fd, 'w', 0) self.__file_monitor = FileMonitor(self.__master_thread_bytes_received, self.__from_debugger_fh_r) self.debugger.SetOutputFileHandle(self.__from_debugger_fh_w, False) self.debugger.SetErrorFileHandle(self.__from_debugger_fh_w, False) self.debugger.SetInputFileHandle(self.__to_debugger_fh_r, False) # m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor); error = lldb.SBError(self.__input_reader.Initialize(self.debugger, self.__input_reader_callback, lldb.eInputReaderGranularityLine, None, # end token (NULL == never done) None, # Prompt (NULL == taken care of elsewhere) False)) # echo input (we'll take care of this elsewhere) if error.Fail(): # Fail now... We can't have any input reader sublime.error_message('error: ' + error.GetCString()) return self.debugger.PushInputReader(self.__input_reader) if listener.IsValid(): iochannel_thread_exited = False listener.StartListeningForEvents(self.io_channel.broadcaster, IOChannel.eBroadcastBitHasUserInput | \ IOChannel.eBroadcastBitUserInterrupt | \ IOChannel.eBroadcastBitThreadShouldExit | \ IOChannel.eBroadcastBitThreadDidStart | \ IOChannel.eBroadcastBitThreadDidExit) self.io_channel.start() if self.io_channel.is_alive(): listener.StartListeningForEvents(sb_interpreter.GetBroadcaster(), lldb.SBCommandInterpreter.eBroadcastBitQuitCommandReceived | \ lldb.SBCommandInterpreter.eBroadcastBitAsynchronousOutputData | \ lldb.SBCommandInterpreter.eBroadcastBitAsynchronousErrorData) result = lldb.SBCommandReturnObject() sb_interpreter.SourceInitFileInHomeDirectory(result) # TODO Remove this debugging feature and have a way to report # errors in init files. if True: result.PutError(self.debugger.GetErrorFileHandle()) result.PutOutput(self.debugger.GetOutputFileHandle()) sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result) # TODO Remove this debugging feature and have a way to report # errors in init files. if True: result.PutError(self.debugger.GetErrorFileHandle()) result.PutOutput(self.debugger.GetOutputFileHandle()) event = lldb.SBEvent() listener.WaitForEventForBroadcasterWithType(BIG_TIMEOUT, self.io_channel.broadcaster, IOChannel.eBroadcastBitThreadDidStart, event) self.ready_for_command() while not self.is_done: listener.WaitForEvent(BIG_TIMEOUT, event) if event: if event.GetBroadcaster(): ev_type = event.GetType() if (event.BroadcasterMatchesRef(self.io_channel.broadcaster)): if ev_type & IOChannel.eBroadcastBitHasUserInput: command_string = lldb.SBEvent.GetCStringFromEvent(event) if command_string is None: command_string = '' result = lldb.SBCommandReturnObject() self.debugger.GetCommandInterpreter().HandleCommand(command_string, result, True) if result.GetOutputSize() > 0: self.io_channel.out_write(result.GetOutput(), IOChannel.NO_ASYNC) if result.GetErrorSize() > 0: self.io_channel.err_write(result.GetError(), IOChannel.NO_ASYNC) debug(debugDriver, 'waiting_for_command = False') self.__waiting_for_command = False if self.__input_reader.IsActive(): self.ready_for_command() elif ev_type & IOChannel.eBroadcastBitThreadShouldExit \ or ev_type & IOChannel.eBroadcastBitThreadDidExit: self.is_done = True if ev_type & IOChannel.eBroadcastBitThreadDidExit: iochannel_thread_exited = True else: # TODO: __handle_io_event is not implemented if self.__handle_io_event(event): self.is_done = True elif lldb.SBProcess.EventIsProcessEvent(event): self.__handle_process_event(event) elif lldb.SBBreakpoint.EventIsBreakpointEvent(event): self.__handle_breakpoint_event(event) elif event.BroadcasterMatchesRef(sb_interpreter.GetBroadcaster()): # This first one should be replaced with a CommandOverrideCallback function if ev_type & lldb.SBCommandInterpreter.eBroadcastBitQuitCommandReceived: self.is_done = True elif ev_type & lldb.SBCommandInterpreter.eBroadcastBitAsynchronousErrorData: data = lldb.SBEvent.GetCStringFromEvent(event) self.io_channel.err_write(data, IOChannel.ASYNC) lldb_view_send(stderr_msg(data)) elif ev_type & lldb.SBCommandInterpreter.eBroadcastBitAsynchronousOutputData: data = lldb.SBEvent.GetCStringFromEvent(event) self.io_channel.out_write(data, IOChannel.ASYNC) lldb_view_send(stdout_msg(data)) if not iochannel_thread_exited: event.Clear() listener.GetNextEventForBroadcasterWithType(self.io_channel.broadcaster, IOChannel.eBroadcastBitThreadDidExit, event) if not event: self.io_channel.stop() self.__file_monitor.setDone() # Ensure the listener (and everything else, really) is destroyed BEFORE the SBDebugger # Otherwise lldb will try to lock a destroyed mutex. # TODO: Track that bug! listener = None lldb.SBDebugger.Destroy(self.debugger) debug(debugDriver, 'leaving') set_driver_instance(None) if self.__on_exit_callback: self.__on_exit_callback()