From: David Gow <[email protected]> commit 8f260b02eeeffbf2263c2b82b6e3e32fd73cde2b upstream
kunit.py will attempt to catch SIGINT / ^C in order to ensure the TTY isn't messed up, but never actually attempts to terminate the running kernel (be it UML or QEMU). This can lead to a bit of frustration if the kernel has crashed or hung. Terminate the kernel process in the signal handler, if it's running. This requires plumbing through the process handle in a few more places (and having some checks to see if the kernel is still running in places where it may have already been killed). Reported-by: Andy Shevchenko <[email protected]> Closes: https://lore.kernel.org/all/[email protected]/ Signed-off-by: David Gow <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Tested-by: Andy Shevchenko <[email protected]> Signed-off-by: Shuah Khan <[email protected]> [florian: resolved conflict in signal_handler(): upstream references _restore_terminal_if_tty() which is introduced by the following commit; retained subprocess.call(['stty', 'sane']) until that helper is available] Signed-off-by: Florian Fainelli <[email protected]> --- tools/testing/kunit/kunit_kernel.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index 12b0f2ee5665..3f2979f2f6ea 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -15,7 +15,7 @@ import shlex import shutil import signal import threading -from typing import Iterator, List, Optional, Tuple +from typing import Iterator, List, Optional, Tuple, Any from types import FrameType import kunit_config @@ -252,6 +252,7 @@ class LinuxSourceTree: if kconfig_add: kconfig = kunit_config.parse_from_string('\n'.join(kconfig_add)) self._kconfig.merge_in_entries(kconfig) + self._process : Optional[subprocess.Popen[Any]] = None def arch(self) -> str: return self._arch @@ -345,36 +346,45 @@ class LinuxSourceTree: args.append('kunit.filter_action=' + filter_action) args.append('kunit.enable=1') - process = self._ops.start(args, build_dir) - assert process.stdout is not None # tell mypy it's set + self._process = self._ops.start(args, build_dir) + assert self._process is not None # tell mypy it's set + assert self._process.stdout is not None # tell mypy it's set # Enforce the timeout in a background thread. def _wait_proc() -> None: try: - process.wait(timeout=timeout) + if self._process: + self._process.wait(timeout=timeout) except Exception as e: print(e) - process.terminate() - process.wait() + if self._process: + self._process.terminate() + self._process.wait() waiter = threading.Thread(target=_wait_proc) waiter.start() output = open(get_outfile_path(build_dir), 'w') try: # Tee the output to the file and to our caller in real time. - for line in process.stdout: + for line in self._process.stdout: output.write(line) yield line # This runs even if our caller doesn't consume every line. finally: # Flush any leftover output to the file - output.write(process.stdout.read()) + if self._process: + if self._process.stdout: + output.write(self._process.stdout.read()) + self._process.stdout.close() + self._process = None output.close() - process.stdout.close() waiter.join() subprocess.call(['stty', 'sane']) def signal_handler(self, unused_sig: int, unused_frame: Optional[FrameType]) -> None: logging.error('Build interruption occurred. Cleaning console.') + if self._process: + self._process.terminate() + self._process.wait() subprocess.call(['stty', 'sane']) -- 2.34.1

