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)>
class Gdb(emdbg.debug.remote.gdb.Interface):
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.
mi
type
def read(self, clear: bool = True) -> list[str]:
64    def read(self, clear: bool = True) -> list[str]:
65        p = self._payloads
66        if clear: self._payloads = []
67        return "".join(p)
def continue_wait(self, timeout: float = 1) -> bool:
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
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.
def quit(self):
91    def quit(self):
92        self._run_thread = False
93        self._response_thread.join(timeout=1)
94        self.mi.exit()

Terminate GDB.