Files
graphene/Scripts/regression.py
Paweł Marczewski 09c6307631 [Pal] Clean up GDB configuration
- 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
2021-02-01 22:37:53 +01:00

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))