def run(self): with self: debug("Binding socket on {}:{}...", self.host, self.port) try: self.sock.bind((self.host, self.port)) except socket.error as ex: debug("Bind failed. {}", ex) raise DebuggerError("Bind failed on {}:{}".format(self.host, self.port)) debug("Socket bound.") debug("Starting to listen on socket...") self.sock.listen(1) listen_thread = Thread(target=self._listen, name="Listener") listen_thread.start() debug_engine = config.get('debug_engine', 'pydbgp.py') debug_cmd = None init_type = config.get('init_type') if init_type == 'current': debug_cmd = os.path.abspath(vim.current.buffer.name) debug("Forking DebugEngine...") self.engine = DebugEngine(debug_engine, debug_cmd) self.engine.start() # TODO: check debug engine status and exit gracefully debug("Waiting for mutual connection...") listen_thread.join() debug("Mutual connection acquired, starting client...") self.client.start() init_packet = self.client_q.get() # TODO: make appropriate checks on init_packet self.running = True while self.running: self.handle_response()
class Debugger(Thread): def __init__(self, host='', port=9000): super(Debugger, self).__init__(name="Debugger") self.host = host self.port = port self.listening = False self._callbacks = {} debug("Initialising Debugger...") self.status_q = Queue() debug("Initialising socket...") self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.client = None self.client_q = Queue() self.engine = None self.running = False self.lock = Lock() self._txn_ids = self._txn_id_genetaor() self.status_q = Queue() self.stream_q = Queue() def close(self): debug("Stopping Debugger...") if self.client: debug("Stopping client...") if self.client.sock: self.client.sock.shutdown(socket.SHUT_RDWR) self.client.join() if self.engine: debug("Stopping DebugEngine...") self.engine.kill() if self.running: debug("Closing server socket...") self.running = False self.sock.close() if threading.current_thread().name != self.name: self.join() def _listen(self): debug("Waiting for connections...") conn, addr = self.sock.accept() debug("Got connections from {}", addr) self.client = DebuggerClient(conn, self.client_q) def run(self): with self: debug("Binding socket on {}:{}...", self.host, self.port) try: self.sock.bind((self.host, self.port)) except socket.error as ex: debug("Bind failed. {}", ex) raise DebuggerError("Bind failed on {}:{}".format(self.host, self.port)) debug("Socket bound.") debug("Starting to listen on socket...") self.sock.listen(1) listen_thread = Thread(target=self._listen, name="Listener") listen_thread.start() debug_engine = config.get('debug_engine', 'pydbgp.py') debug_cmd = None init_type = config.get('init_type') if init_type == 'current': debug_cmd = os.path.abspath(vim.current.buffer.name) debug("Forking DebugEngine...") self.engine = DebugEngine(debug_engine, debug_cmd) self.engine.start() # TODO: check debug engine status and exit gracefully debug("Waiting for mutual connection...") listen_thread.join() debug("Mutual connection acquired, starting client...") self.client.start() init_packet = self.client_q.get() # TODO: make appropriate checks on init_packet self.running = True while self.running: self.handle_response() def handle_response(self): try: response = self.client_q.get(timeout=1) except Empty: return dom = minidom.parseString(response) responses = dom.getElementsByTagName("response") streams = dom.getElementsByTagName("stream") if responses: element = responses[0] txn_id = int(element.attributes['transaction_id'].value) debug("Handling response for txn_id {}", txn_id) callback = self._callbacks.get(txn_id) if callback: debug("Firing callback for txn_id {}", txn_id) callback(element) elif streams: stream = streams[0] stream_type = stream.attributes['type'].value encoded = stream.firstChild.nodeValue plain = base64.decodestring(encoded) debug("Received data on {}: '{}'", stream_type, plain) self.stream_q.put(plain) def configure_streams(self): debug("Configuring streams...") element = self.send_command_and_wait("stdout", "", "-c", "1") debug("Stdout redirect configured ->\n{}", element) element = self.send_command_and_wait("stderr", "", "-c", "1") debug("Stderr redirect configured ->\n{}", element) def stop(self): self.send_command_and_wait("stop", "") def status(self): element = self.send_command_and_wait("status", "") return element.attributes['status'].value def send_command(self, callback, name, data, *args): data_encoded = (" -- " + base64.encodestring(data)) if data else '' all_args = ' '.join(args) txn_id = self._next_txn_id() txn_arg = "-i {} ".format(txn_id) all_args = txn_arg + ' ' + all_args if all_args else txn_arg command = "{name} {args}{data}\0".format(name=name, args=all_args, data=data_encoded) debug("sending command ->\n{}", command) if callback: self._callbacks[txn_id] = callback self.client.send(command) def waiting_callback(self, container, element): container.append(element) def send_command_and_wait(self, name, data, *args): debug("Sending command and waiting...") container = [] callback = partial(self.waiting_callback, container) self.send_command(callback, name, data, *args) debug("Waiting for response...") attempts = 60 while not container: time.sleep(0.05) attempts -= 1 if not attempts: raise DebuggerError("Wait for response timed out") debug("Got response.") return container[0] def status_update_callback(self, element): with self: self.status_q.put(element.attributes['status'].value) def update_status(self): self.status_q.put(self.status()) def cont(self): with self: self.send_command(self.status_update_callback, "run", "") def step_over(self): with self: self.send_command(self.status_update_callback, "step_over", "") def step_into(self): with self: self.send_command(self.status_update_callback, "step_into", "") def step_out(self): with self: self.send_command(self.status_update_callback, "step_out", "") def get_stack(self): filename_prefix = "file:" elem = self.send_command_and_wait("stack_get", "") # TODO: check children call_stack = Stack() stack = [] for child_node in elem.childNodes: level = child_node.attributes['level'].value filename = child_node.attributes['filename'].value if filename.startswith(filename_prefix): filename = filename[len(filename_prefix):] line = int(child_node.attributes['lineno'].value) stack_frame = StackFrame(level, filename, line) stack.append(stack_frame) for sf in reversed(stack): call_stack.push(sf) return call_stack def add_breakpoint(self, breakpoint): response = self.send_command_and_wait("breakpoint_set", breakpoint.condition, breakpoint.to_args()) breakpoint._id = response.attributes['id'].value def remove_breakpoint(self, breakpoint): self.send_command_and_wait("breakpoint_remove", "", "-d {bid}".format(bid=breakpoint._id)) def __enter__(self): self.lock.acquire() return self def __exit__(self, exc_type, exc_value, exc_tb): self.lock.release() if exc_type: debug("Exception captured, exiting: ->\n{}", exc_value) self.close() def _txn_id_genetaor(self): start = 0 while True: new = yield start start = new if new else start + 1 def _next_txn_id(self): return next(self._txn_ids)