mirror of
https://github.com/clearlinux/graphene.git
synced 2026-07-27 07:07:08 +00:00
09c6307631
- Extract parts that are common for all hosts - Remove some outdated/unnecessary options, we should now be closer to default configuration - Disable libthread_db loading (does not work and crashes GDB 9.2) - Disable pagination when loading debug maps
137 lines
4.8 KiB
Python
137 lines
4.8 KiB
Python
import contextlib
|
|
import os
|
|
import pathlib
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
import unittest
|
|
|
|
# pylint: disable=subprocess-popen-preexec-fn,subprocess-run-check
|
|
|
|
HAS_SGX = os.environ.get('SGX') == '1'
|
|
ON_X86 = os.uname().machine in ['x86_64']
|
|
|
|
def expectedFailureIf(predicate):
|
|
if predicate:
|
|
return unittest.expectedFailure
|
|
return lambda func: func
|
|
|
|
class RegressionTestCase(unittest.TestCase):
|
|
LOADER_ENV = 'PAL_LOADER'
|
|
LIBPAL_PATH_ENV = 'LIBPAL_PATH'
|
|
HOST_PAL_PATH_ENV = 'HOST_PAL_PATH'
|
|
DEFAULT_TIMEOUT = (20 if HAS_SGX else 10)
|
|
|
|
def get_env(self, name):
|
|
try:
|
|
return os.environ[name]
|
|
except KeyError:
|
|
self.fail('environment variable {} not set'.format(name))
|
|
|
|
def has_debug(self):
|
|
libpal = self.get_env(self.LIBPAL_PATH_ENV)
|
|
p = subprocess.run(['objdump', '-x', libpal], check=True, stdout=subprocess.PIPE)
|
|
dump = p.stdout.decode()
|
|
return '.debug_info' in dump
|
|
|
|
def run_gdb(self, args, gdb_script, **kwds):
|
|
host_pal_path = self.get_env(self.HOST_PAL_PATH_ENV)
|
|
|
|
# See also pal_loader.
|
|
prefix = ['gdb', '-q']
|
|
env = os.environ.copy()
|
|
if HAS_SGX:
|
|
prefix += ['-x', os.path.join(host_pal_path, 'gdb_integration/graphene_sgx_gdb.py')]
|
|
sgx_gdb = os.path.join(host_pal_path, 'gdb_integration/sgx_gdb.so')
|
|
env['LD_PRELOAD'] = sgx_gdb + ':' + env.get('LD_PRELOAD', '')
|
|
else:
|
|
prefix += ['-x', os.path.join(host_pal_path, 'gdb_integration/graphene_linux_gdb.py')]
|
|
|
|
# Override TTY, as apparently os.setpgrp() confuses GDB and causes it to hang.
|
|
prefix += ['-x', gdb_script, '-batch', '-tty=/dev/null']
|
|
prefix += ['--args']
|
|
|
|
return self.run_binary(args, prefix=prefix, env=env, **kwds)
|
|
|
|
def run_binary(self, args, *, timeout=None, prefix=None, **kwds):
|
|
timeout = (max(self.DEFAULT_TIMEOUT, timeout) if timeout is not None
|
|
else self.DEFAULT_TIMEOUT)
|
|
|
|
loader = self.get_env(self.LOADER_ENV)
|
|
if not pathlib.Path(loader).exists():
|
|
self.skipTest('loader ({}) not found'.format(loader))
|
|
|
|
libpal = self.get_env(self.LIBPAL_PATH_ENV)
|
|
if not pathlib.Path(libpal).exists():
|
|
self.skipTest('libpal ({}) not found'.format(libpal))
|
|
|
|
if prefix is None:
|
|
prefix = []
|
|
|
|
with subprocess.Popen([*prefix, loader, libpal, 'init', *args],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
preexec_fn=os.setpgrp,
|
|
**kwds) as process:
|
|
try:
|
|
stdout, stderr = process.communicate(timeout=timeout)
|
|
except subprocess.TimeoutExpired:
|
|
os.killpg(process.pid, signal.SIGKILL)
|
|
self.fail('timeout ({} s) expired'.format(timeout))
|
|
|
|
self.print_output(stdout, stderr)
|
|
|
|
if process.returncode:
|
|
raise subprocess.CalledProcessError(
|
|
process.returncode, args, stdout, stderr)
|
|
|
|
return stdout.decode(), stderr.decode()
|
|
|
|
@classmethod
|
|
def run_native_binary(cls, args, timeout=None, libpath=None, **kwds):
|
|
timeout = (max(cls.DEFAULT_TIMEOUT, timeout) if timeout is not None
|
|
else cls.DEFAULT_TIMEOUT)
|
|
|
|
my_env = os.environ.copy()
|
|
if not libpath is None:
|
|
my_env["LD_LIBRARY_PATH"] = libpath
|
|
|
|
with subprocess.Popen(args,
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
env=my_env,
|
|
preexec_fn=os.setpgrp,
|
|
**kwds) as process:
|
|
try:
|
|
stdout, stderr = process.communicate(timeout=timeout)
|
|
except subprocess.TimeoutExpired:
|
|
os.killpg(process.pid, signal.SIGKILL)
|
|
raise AssertionError('timeout ({} s) expired'.format(timeout))
|
|
|
|
cls.print_output(stdout, stderr)
|
|
|
|
if process.returncode:
|
|
raise subprocess.CalledProcessError(
|
|
process.returncode, args, stdout, stderr)
|
|
|
|
return stdout.decode(), stderr.decode()
|
|
|
|
@staticmethod
|
|
def print_output(stdout: bytes, stderr: bytes):
|
|
'''
|
|
Print command output (stdout, stderr) so that pytest can capture it.
|
|
'''
|
|
|
|
sys.stdout.write(stdout.decode(errors='surrogateescape'))
|
|
sys.stderr.write(stderr.decode(errors='surrogateescape'))
|
|
|
|
@contextlib.contextmanager
|
|
def expect_returncode(self, returncode):
|
|
if returncode == 0:
|
|
raise ValueError('expected returncode should be nonzero')
|
|
try:
|
|
yield
|
|
self.fail('did not fail (expected {})'.format(returncode))
|
|
except subprocess.CalledProcessError as e:
|
|
self.assertEqual(e.returncode, returncode,
|
|
'failed with returncode {} (expected {})'.format(
|
|
e.returncode, returncode))
|