emdbg.debug.remote.mi
1# Copyright (c) 2023, Auterion AG 2# SPDX-License-Identifier: BSD-3-Clause 3 4from __future__ import annotations 5from contextlib import contextmanager 6import time 7import os 8import threading 9import signal 10import logging 11from .gdb import Interface 12LOGGER = logging.getLogger(__name__) 13from ...logger import VERBOSITY 14 15 16class Gdb(Interface): 17 """ 18 Provides access to the GDB command prompt using the [GDB/MI protocol][gdbmi]. 19 20 .. note:: The Machine Interface protocol is not easily human-readable. 21 The GDB output will be formatted using the GDB/MI protocol, which needs 22 (simple) post-processing to convert into a normal log again. 23 This is especially important when logging GDB output to a file using the 24 `set logging enabled on` GDB command. 25 26 [gdbmi]: https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI.html 27 """ 28 def __init__(self, backend: "emdbg.debug.ProbeBackend", mi: "pygdbmi.gdbcontroller.GdbController"): 29 """ 30 :param backend: a debug backend implementation. 31 :param mi: GdbController with the command string. 32 """ 33 super().__init__(backend) 34 self.mi = mi 35 self.type = "mi" 36 37 self._run_thread = True 38 self._command_is_done = False 39 self._stopped = False 40 self._payloads = [] 41 self._continue_timeout = -1 42 self._response_thread = threading.Thread(target=self._handle_responses) 43 self._response_thread.start() 44 45 def _handle_responses(self): 46 while self._run_thread: 47 responses = self.mi.io_manager.get_gdb_response(timeout_sec=0, raise_error_on_timeout=False) 48 for response in responses: 49 # print(response) 50 if response["type"] == "result" and response["message"] in ["done", "running"]: 51 self._command_is_done = True 52 elif response["type"] == "console": 53 if payload := response["payload"].encode("latin-1", "ignore").decode("unicode_escape"): 54 payload = payload.replace("\\e", "\033") 55 self._payloads.append(payload) 56 if "#" not in payload or VERBOSITY >= 3: 57 LOGGER.debug(payload) 58 elif response["type"] == "notify" and response["message"] == "stopped": 59 self._stopped = True 60 self.interrupted = True 61 time.sleep(0.01) 62 63 def read(self, clear: bool = True) -> list[str]: 64 p = self._payloads 65 if clear: self._payloads = [] 66 return "".join(p) 67 68 def continue_wait(self, timeout: float = 1) -> bool: 69 self._stopped = False 70 self.continue_nowait() 71 while(not self._stopped and timeout > 0): 72 time.sleep(0.1) 73 timeout -= 0.1 74 return self._stopped 75 76 def _write(self, cmd: str): 77 LOGGER.debug(f"(gdb) {cmd}") 78 self.mi.write(cmd, timeout_sec=0, raise_error_on_timeout=False, read_response=False) 79 80 def execute(self, cmd: str, timeout: float = 1, to_string: bool = False) -> str | None: 81 self._command_is_done = False 82 self._payloads = [] 83 self._write(cmd) 84 while(not self._command_is_done and timeout > 0): 85 time.sleep(0.1) 86 timeout -= 0.1 87 if to_string: 88 return self.read() 89 90 def quit(self): 91 self._run_thread = False 92 self._response_thread.join(timeout=1) 93 self.mi.exit()
LOGGER =
<Logger emdbg.debug.remote.mi (WARNING)>
17class Gdb(Interface): 18 """ 19 Provides access to the GDB command prompt using the [GDB/MI protocol][gdbmi]. 20 21 .. note:: The Machine Interface protocol is not easily human-readable. 22 The GDB output will be formatted using the GDB/MI protocol, which needs 23 (simple) post-processing to convert into a normal log again. 24 This is especially important when logging GDB output to a file using the 25 `set logging enabled on` GDB command. 26 27 [gdbmi]: https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI.html 28 """ 29 def __init__(self, backend: "emdbg.debug.ProbeBackend", mi: "pygdbmi.gdbcontroller.GdbController"): 30 """ 31 :param backend: a debug backend implementation. 32 :param mi: GdbController with the command string. 33 """ 34 super().__init__(backend) 35 self.mi = mi 36 self.type = "mi" 37 38 self._run_thread = True 39 self._command_is_done = False 40 self._stopped = False 41 self._payloads = [] 42 self._continue_timeout = -1 43 self._response_thread = threading.Thread(target=self._handle_responses) 44 self._response_thread.start() 45 46 def _handle_responses(self): 47 while self._run_thread: 48 responses = self.mi.io_manager.get_gdb_response(timeout_sec=0, raise_error_on_timeout=False) 49 for response in responses: 50 # print(response) 51 if response["type"] == "result" and response["message"] in ["done", "running"]: 52 self._command_is_done = True 53 elif response["type"] == "console": 54 if payload := response["payload"].encode("latin-1", "ignore").decode("unicode_escape"): 55 payload = payload.replace("\\e", "\033") 56 self._payloads.append(payload) 57 if "#" not in payload or VERBOSITY >= 3: 58 LOGGER.debug(payload) 59 elif response["type"] == "notify" and response["message"] == "stopped": 60 self._stopped = True 61 self.interrupted = True 62 time.sleep(0.01) 63 64 def read(self, clear: bool = True) -> list[str]: 65 p = self._payloads 66 if clear: self._payloads = [] 67 return "".join(p) 68 69 def continue_wait(self, timeout: float = 1) -> bool: 70 self._stopped = False 71 self.continue_nowait() 72 while(not self._stopped and timeout > 0): 73 time.sleep(0.1) 74 timeout -= 0.1 75 return self._stopped 76 77 def _write(self, cmd: str): 78 LOGGER.debug(f"(gdb) {cmd}") 79 self.mi.write(cmd, timeout_sec=0, raise_error_on_timeout=False, read_response=False) 80 81 def execute(self, cmd: str, timeout: float = 1, to_string: bool = False) -> str | None: 82 self._command_is_done = False 83 self._payloads = [] 84 self._write(cmd) 85 while(not self._command_is_done and timeout > 0): 86 time.sleep(0.1) 87 timeout -= 0.1 88 if to_string: 89 return self.read() 90 91 def quit(self): 92 self._run_thread = False 93 self._response_thread.join(timeout=1) 94 self.mi.exit()
Provides access to the GDB command prompt using the GDB/MI protocol.
The Machine Interface protocol is not easily human-readable.
The GDB output will be formatted using the GDB/MI protocol, which needs
(simple) post-processing to convert into a normal log again.
This is especially important when logging GDB output to a file using the
set logging enabled on
GDB command.
Gdb( backend: emdbg.debug.backend.ProbeBackend, mi: pygdbmi.gdbcontroller.GdbController)
29 def __init__(self, backend: "emdbg.debug.ProbeBackend", mi: "pygdbmi.gdbcontroller.GdbController"): 30 """ 31 :param backend: a debug backend implementation. 32 :param mi: GdbController with the command string. 33 """ 34 super().__init__(backend) 35 self.mi = mi 36 self.type = "mi" 37 38 self._run_thread = True 39 self._command_is_done = False 40 self._stopped = False 41 self._payloads = [] 42 self._continue_timeout = -1 43 self._response_thread = threading.Thread(target=self._handle_responses) 44 self._response_thread.start()
Parameters
- backend: a debug backend implementation.
- mi: GdbController with the command string.
def
execute( self, cmd: str, timeout: float = 1, to_string: bool = False) -> str | None:
81 def execute(self, cmd: str, timeout: float = 1, to_string: bool = False) -> str | None: 82 self._command_is_done = False 83 self._payloads = [] 84 self._write(cmd) 85 while(not self._command_is_done and timeout > 0): 86 time.sleep(0.1) 87 timeout -= 0.1 88 if to_string: 89 return self.read()
Executes a command on the GDB command prompt and waits for a response.
If to_string
is set, it will return the response as a string.
Parameters
- command: The command string to send to the GDB prompt.
- timeout: How long to wait for a response in seconds.
- to_string: Capture the response as a string and return it.