diff --git a/pygdbmi/IoManager.py b/pygdbmi/IoManager.py index dbd7276..57b0424 100644 --- a/pygdbmi/IoManager.py +++ b/pygdbmi/IoManager.py @@ -74,6 +74,7 @@ def get_gdb_response( self, timeout_sec: float = DEFAULT_GDB_TIMEOUT_SEC, raise_error_on_timeout: bool = True, + return_on_first_response: bool = False, ) -> List[Dict]: """Get response from GDB, and block while doing so. If GDB does not have any response ready to be read by timeout_sec, an exception is raised. @@ -96,9 +97,9 @@ def get_gdb_response( timeout_sec = 0 if USING_WINDOWS: - retval = self._get_responses_windows(timeout_sec) + retval = self._get_responses_windows(timeout_sec, return_on_first_response) else: - retval = self._get_responses_unix(timeout_sec) + retval = self._get_responses_unix(timeout_sec, return_on_first_response) if not retval and raise_error_on_timeout: raise GdbTimeoutError( @@ -108,7 +109,9 @@ def get_gdb_response( else: return retval - def _get_responses_windows(self, timeout_sec: float) -> List[Dict]: + def _get_responses_windows( + self, timeout_sec: float, return_on_first_response: bool + ) -> List[Dict]: """Get responses on windows. Assume no support for select and use a while loop.""" timeout_time_sec = time.time() + timeout_sec responses = [] @@ -132,6 +135,8 @@ def _get_responses_windows(self, timeout_sec: float) -> List[Dict]: responses += responses_list if timeout_sec == 0: break + elif return_on_first_response is True and responses: + break elif responses_list and self._allow_overwrite_timeout_times: timeout_time_sec = min( time.time() + self.time_to_check_for_additional_output_sec, @@ -142,7 +147,9 @@ def _get_responses_windows(self, timeout_sec: float) -> List[Dict]: return responses - def _get_responses_unix(self, timeout_sec: float) -> List[Dict]: + def _get_responses_unix( + self, timeout_sec: float, return_on_first_response: bool + ) -> List[Dict]: """Get responses on unix-like system. Use select to wait for output.""" timeout_time_sec = time.time() + timeout_sec responses = [] @@ -174,7 +181,8 @@ def _get_responses_unix(self, timeout_sec: float) -> List[Dict]: if timeout_sec == 0: # just exit immediately break - + elif return_on_first_response is True and responses: + break elif responses_list and self._allow_overwrite_timeout_times: # update timeout time to potentially be closer to now to avoid lengthy wait times when nothing is being output by gdb timeout_time_sec = min( @@ -197,7 +205,10 @@ def _get_responses_list( """ responses: List[Dict[Any, Any]] = [] - (_new_output, self._incomplete_output[stream],) = _buffer_incomplete_responses( + ( + _new_output, + self._incomplete_output[stream], + ) = _buffer_incomplete_responses( raw_output, self._incomplete_output.get(stream) ) @@ -228,6 +239,7 @@ def write( timeout_sec: float = DEFAULT_GDB_TIMEOUT_SEC, raise_error_on_timeout: bool = True, read_response: bool = True, + return_on_first_response: bool = False, ) -> List[Dict]: """Write to gdb process. Block while parsing responses from gdb for a maximum of timeout_sec. @@ -282,7 +294,9 @@ def write( if read_response is True: return self.get_gdb_response( - timeout_sec=timeout_sec, raise_error_on_timeout=raise_error_on_timeout + timeout_sec=timeout_sec, + raise_error_on_timeout=raise_error_on_timeout, + return_on_first_response=return_on_first_response, ) else: diff --git a/pygdbmi/gdbcontroller.py b/pygdbmi/gdbcontroller.py index d4fa59a..84124de 100644 --- a/pygdbmi/gdbcontroller.py +++ b/pygdbmi/gdbcontroller.py @@ -110,9 +110,12 @@ def get_gdb_response( self, timeout_sec: float = DEFAULT_GDB_TIMEOUT_SEC, raise_error_on_timeout: bool = True, + return_on_first_response: bool = False, ) -> List[Dict]: """Get gdb response. See IoManager.get_gdb_response() for details""" - return self.io_manager.get_gdb_response(timeout_sec, raise_error_on_timeout) + return self.io_manager.get_gdb_response( + timeout_sec, raise_error_on_timeout, return_on_first_response + ) def write( self, @@ -120,10 +123,15 @@ def write( timeout_sec: float = DEFAULT_GDB_TIMEOUT_SEC, raise_error_on_timeout: bool = True, read_response: bool = True, + return_on_first_response: bool = False, ) -> List[Dict]: """Write command to gdb. See IoManager.write() for details""" return self.io_manager.write( - mi_cmd_to_write, timeout_sec, raise_error_on_timeout, read_response + mi_cmd_to_write=mi_cmd_to_write, + timeout_sec=timeout_sec, + raise_error_on_timeout=raise_error_on_timeout, + read_response=read_response, + return_on_first_response=return_on_first_response, ) def exit(self) -> None: diff --git a/tests/test_gdbcontroller.py b/tests/test_gdbcontroller.py index c567ac5..e0d1504 100755 --- a/tests/test_gdbcontroller.py +++ b/tests/test_gdbcontroller.py @@ -9,6 +9,7 @@ import random import shutil import subprocess +import time import pytest @@ -97,6 +98,26 @@ def test_controller() -> None: responses = gdbmi.write(["-break-insert main", "-exec-run"]) +def test_return_on_first_response() -> None: + gdbmi = GdbController(time_to_check_for_additional_output_sec=0) + c_hello_world_binary = _get_c_program("hello", "pygdbmiapp.a") + + if USING_WINDOWS: + c_hello_world_binary = c_hello_world_binary.replace("\\", "/") + # Load the binary and its symbols in the gdb subprocess + responses = gdbmi.write( + "-file-exec-and-symbols %s" % c_hello_world_binary, timeout_sec=1 + ) + responses = gdbmi.write( + "-file-exec-and-symbols %s" % c_hello_world_binary, timeout_sec=1 + ) + timeout_sec = 10.0 + start_time = time.time() + responses = gdbmi.write("stepi", timeout_sec=timeout_sec, return_on_first_response=True) + endtime = time.time() + assert(start_time + timeout_sec > endtime) + + @pytest.mark.skip() def test_controller_buffer_randomized() -> None: """