Example #1
0
    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()
Example #2
0
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)