twister: Ensure serial-pty process termination

Make sure Twster DeviceHandler serial-pty process is terminated
with all its remaining children to avoid Twister hanging on it
infinitely.
The reolved issue occurs sometimes, for example when serial-pty
script is used for serial port tunneling over network.

Signed-off-by: Dmitrii Golovanov <dmitrii.golovanov@intel.com>
This commit is contained in:
Dmitrii Golovanov 2024-06-03 19:06:43 +02:00 committed by David Leach
commit 4dc2a98d0c
2 changed files with 17 additions and 8 deletions

View file

@ -569,6 +569,16 @@ class DeviceHandler(Handler):
if self.instance.status in ["error", "failed"]: if self.instance.status in ["error", "failed"]:
self.instance.add_missing_case_status("blocked", self.instance.reason) self.instance.add_missing_case_status("blocked", self.instance.reason)
def _terminate_pty(self, ser_pty, ser_pty_process):
logger.debug(f"Terminating serial-pty:'{ser_pty}'")
terminate_process(ser_pty_process)
try:
(stdout, stderr) = ser_pty_process.communicate(timeout=self.get_test_timeout())
logger.debug(f"Terminated serial-pty:'{ser_pty}', stdout:'{stdout}', stderr:'{stderr}'")
except subprocess.TimeoutExpired:
logger.debug(f"Terminated serial-pty:'{ser_pty}'")
#
def _create_serial_connection(self, serial_device, hardware_baud, def _create_serial_connection(self, serial_device, hardware_baud,
flash_timeout, serial_pty, ser_pty_process): flash_timeout, serial_pty, ser_pty_process):
try: try:
@ -588,9 +598,7 @@ class DeviceHandler(Handler):
self.instance.add_missing_case_status("blocked", "Serial Device Error") self.instance.add_missing_case_status("blocked", "Serial Device Error")
if serial_pty and ser_pty_process: if serial_pty and ser_pty_process:
ser_pty_process.terminate() self._terminate_pty(serial_pty, ser_pty_process)
outs, errs = ser_pty_process.communicate()
logger.debug("Process {} terminated outs: {} errs {}".format(serial_pty, outs, errs))
if serial_pty: if serial_pty:
self.make_device_available(serial_pty) self.make_device_available(serial_pty)
@ -754,9 +762,7 @@ class DeviceHandler(Handler):
ser.close() ser.close()
if serial_pty: if serial_pty:
ser_pty_process.terminate() self._terminate_pty(serial_pty, ser_pty_process)
outs, errs = ser_pty_process.communicate()
logger.debug("Process {} terminated outs: {} errs {}".format(serial_pty, outs, errs))
handler_time = time.time() - start_time handler_time = time.time() - start_time

View file

@ -1169,6 +1169,7 @@ def test_devicehandler_create_serial_connection(
available_mock = mock.Mock() available_mock = mock.Mock()
handler.make_device_available = available_mock handler.make_device_available = available_mock
handler.options = mock.Mock(timeout_multiplier=1) handler.options = mock.Mock(timeout_multiplier=1)
twisterlib.handlers.terminate_process = mock.Mock()
hardware_baud = 14400 hardware_baud = 14400
flash_timeout = 60 flash_timeout = 60
@ -1191,7 +1192,7 @@ def test_devicehandler_create_serial_connection(
missing_mock.assert_called_once_with('blocked', 'Serial Device Error') missing_mock.assert_called_once_with('blocked', 'Serial Device Error')
if terminate_ser_pty_process: if terminate_ser_pty_process:
ser_pty_process.terminate.assert_called_once() twisterlib.handlers.terminate_process.assert_called_once()
ser_pty_process.communicate.assert_called_once() ser_pty_process.communicate.assert_called_once()
if make_available: if make_available:
@ -1252,7 +1253,8 @@ TESTDATA_17 = [
(True, False, False, False, 0, True, False, (True, False, False, False, 0, True, False,
None, None, ['Timed out while monitoring serial output on IPName']), None, None, ['Timed out while monitoring serial output on IPName']),
(True, False, False, False, 0, False, True, (True, False, False, False, 0, False, True,
None, None, ['Process Serial PTY terminated outs: errs ']), None, None, ["Terminating serial-pty:'Serial PTY'",
"Terminated serial-pty:'Serial PTY', stdout:'', stderr:''"]),
] ]
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -1351,6 +1353,7 @@ def test_devicehandler_handle(
handler._update_instance_info = mock.Mock() handler._update_instance_info = mock.Mock()
handler._final_handle_actions = mock.Mock() handler._final_handle_actions = mock.Mock()
handler.make_device_available = mock.Mock() handler.make_device_available = mock.Mock()
twisterlib.handlers.terminate_process = mock.Mock()
handler.instance.platform.name = 'IPName' handler.instance.platform.name = 'IPName'
harness = mock.Mock() harness = mock.Mock()