From 35839eda8b17f85a94e8f83a681079ac2d30413f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 8 Oct 2021 09:15:23 -0600 Subject: [PATCH] pytest: Show a message when sandbox crashes When a test hands on a real board there is no way on the console to obtain any information about why it hung. With sandbox we can actually find out that it died and get a signal or exit code. Add this to make it easier to figure out what happened. So instead of: test/py/u_boot_spawn.py:171: in expect c = os.read(self.fd, 1024).decode(errors='replace') E OSError: [Errno 5] Input/output error We get: test/py/u_boot_spawn.py:171: in expect c = os.read(self.fd, 1024).decode(errors='replace') E ValueError: U-Boot exited with signal 11 (Signals.SIGSEGV) Signed-off-by: Simon Glass --- doc/develop/py_testing.rst | 8 ++++++ test/py/u_boot_spawn.py | 56 ++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/doc/develop/py_testing.rst b/doc/develop/py_testing.rst index 52238ca54d..06f919609b 100644 --- a/doc/develop/py_testing.rst +++ b/doc/develop/py_testing.rst @@ -103,6 +103,14 @@ will be written to `${build_dir}/test-log.html`. This is best viewed in a web browser, but may be read directly as plain text, perhaps with the aid of the `html2text` utility. +If sandbox crashes (e.g. with a segfault) you will see message like this:: + + + test/py/u_boot_spawn.py:171: in expect + c = os.read(self.fd, 1024).decode(errors='replace') + E ValueError: U-Boot exited with signal 11 (Signals.SIGSEGV) + + Controlling output ~~~~~~~~~~~~~~~~~~ diff --git a/test/py/u_boot_spawn.py b/test/py/u_boot_spawn.py index 6991b78cca..e34cb217e8 100644 --- a/test/py/u_boot_spawn.py +++ b/test/py/u_boot_spawn.py @@ -35,6 +35,8 @@ class Spawn(object): """ self.waited = False + self.exit_code = 0 + self.exit_info = '' self.buf = '' self.output = '' self.logfile_read = None @@ -80,25 +82,44 @@ class Spawn(object): os.kill(self.pid, sig) - def isalive(self): + def checkalive(self): """Determine whether the child process is still running. - Args: - None. - Returns: - Boolean indicating whether process is alive. + tuple: + True if process is alive, else False + 0 if process is alive, else exit code of process + string describing what happened ('' or 'status/signal n') """ if self.waited: - return False + return False, self.exit_code, self.exit_info w = os.waitpid(self.pid, os.WNOHANG) if w[0] == 0: - return True - + return True, 0, 'running' + status = w[1] + + if os.WIFEXITED(status): + self.exit_code = os.WEXITSTATUS(status) + self.exit_info = 'status %d' % self.exit_code + elif os.WIFSIGNALED(status): + signum = os.WTERMSIG(status) + self.exit_code = -signum + self.exit_info = 'signal %d (%s)' % (signum, signal.Signals(signum)) self.waited = True - return False + return False, self.exit_code, self.exit_info + + def isalive(self): + """Determine whether the child process is still running. + + Args: + None. + + Returns: + Boolean indicating whether process is alive. + """ + return self.checkalive()[0] def send(self, data): """Send data to the sub-process's stdin. @@ -168,9 +189,20 @@ class Spawn(object): events = self.poll.poll(poll_maxwait) if not events: raise Timeout() - c = os.read(self.fd, 1024).decode(errors='replace') - if not c: - raise EOFError() + try: + c = os.read(self.fd, 1024).decode(errors='replace') + except OSError as err: + # With sandbox, try to detect when U-Boot exits when it + # shouldn't and explain why. This is much more friendly than + # just dying with an I/O error + if err.errno == 5: # Input/output error + alive, exit_code, info = self.checkalive() + if alive: + raise + else: + raise ValueError('U-Boot exited with %s' % info) + else: + raise if self.logfile_read: self.logfile_read.write(c) self.buf += c -- 2.39.5