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 <sjg@chromium.org>
This commit is contained in:
Simon Glass 2021-10-08 09:15:23 -06:00 committed by Tom Rini
parent c3aea68705
commit 35839eda8b
2 changed files with 53 additions and 13 deletions

View file

@ -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
~~~~~~~~~~~~~~~~~~

View file

@ -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,6 +82,34 @@ class Spawn(object):
os.kill(self.pid, sig)
def checkalive(self):
"""Determine whether the child process is still running.
Returns:
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, self.exit_code, self.exit_info
w = os.waitpid(self.pid, os.WNOHANG)
if w[0] == 0:
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, self.exit_code, self.exit_info
def isalive(self):
"""Determine whether the child process is still running.
@ -89,16 +119,7 @@ class Spawn(object):
Returns:
Boolean indicating whether process is alive.
"""
if self.waited:
return False
w = os.waitpid(self.pid, os.WNOHANG)
if w[0] == 0:
return True
self.waited = True
return False
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()
try:
c = os.read(self.fd, 1024).decode(errors='replace')
if not c:
raise EOFError()
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