diff --git a/lldb/test/API/lit.cfg.py b/lldb/test/API/lit.cfg.py --- a/lldb/test/API/lit.cfg.py +++ b/lldb/test/API/lit.cfg.py @@ -21,6 +21,17 @@ config.test_exec_root = config.test_source_root +def mkdir_p(path): + import errno + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + if not os.path.isdir(path): + raise OSError(errno.ENOTDIR, "%s is not a directory"%path) + + def find_sanitizer_runtime(name): import subprocess resource_dir = subprocess.check_output( @@ -38,6 +49,43 @@ yield 'PATH' +# On macOS, we can't do the DYLD_INSERT_LIBRARIES trick with a shim python +# binary as the ASan interceptors get loaded too late. Also, when SIP is +# enabled, we can't inject libraries into system binaries at all, so we need a +# copy of the "real" python to work with. +def find_python_interpreter(): + # Avoid doing any work if we already copied the binary. + copied_python = os.path.join(config.lldb_build_directory, 'copied-python') + if os.path.isfile(copied_python): + return copied_python + + # Find the "real" python binary. + import shutil, subprocess + real_python = subprocess.check_output([ + config.python_executable, + os.path.join(os.path.dirname(os.path.realpath(__file__)), + 'get_darwin_real_python.py') + ]).decode('utf-8').strip() + + shutil.copy(real_python, copied_python) + + # Now make sure the copied Python works. The Python in Xcode has a relative + # RPATH and cannot be copied. + try: + # We don't care about the output, just make sure it runs. + subprocess.check_output([copied_python, '-V'], stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + # The copied Python didn't work. Assume we're dealing with the Python + # interpreter in Xcode. Given that this is not a system binary SIP + # won't prevent us form injecting the interceptors so we get away with + # not copying the executable. + os.remove(copied_python) + return real_python + + # The copied Python works. + return copied_python + + if 'Address' in config.llvm_use_sanitizer: config.environment['ASAN_OPTIONS'] = 'detect_stack_use_after_return=1' if 'Darwin' in config.host_os and 'x86' in config.host_triple: @@ -49,6 +97,9 @@ config.environment['DYLD_INSERT_LIBRARIES'] = find_sanitizer_runtime( 'libclang_rt.tsan_osx_dynamic.dylib') +if 'DYLD_INSERT_LIBRARIES' in config.environment and platform.system() == 'Darwin': + config.python_executable = find_python_interpreter() + # Shared library build of LLVM may require LD_LIBRARY_PATH or equivalent. if config.shared_libs: for shlibpath_var in find_shlibpath_var(): diff --git a/lldb/test/API/lit.site.cfg.py.in b/lldb/test/API/lit.site.cfg.py.in --- a/lldb/test/API/lit.site.cfg.py.in +++ b/lldb/test/API/lit.site.cfg.py.in @@ -18,6 +18,7 @@ config.llvm_use_sanitizer = "@LLVM_USE_SANITIZER@" config.target_triple = "@TARGET_TRIPLE@" config.lldb_build_directory = "@LLDB_TEST_BUILD_DIRECTORY@" +config.lldb_reproducer_directory = os.path.join("@LLDB_TEST_BUILD_DIRECTORY@", "reproducers") config.python_executable = "@PYTHON_EXECUTABLE@" config.dotest_path = "@LLDB_SOURCE_DIR@/test/API/dotest.py" config.dotest_args_str = "@LLDB_DOTEST_ARGS@" diff --git a/lldb/test/API/lldbtest.py b/lldb/test/API/lldbtest.py --- a/lldb/test/API/lldbtest.py +++ b/lldb/test/API/lldbtest.py @@ -10,24 +10,6 @@ import lit.util from lit.formats.base import TestFormat -def getBuildDir(cmd): - found = False - for arg in cmd: - if found: - return arg - if arg == '--build-dir': - found = True - return None - -def mkdir_p(path): - import errno - try: - os.makedirs(path) - except OSError as e: - if e.errno != errno.EEXIST: - raise - if not os.path.isdir(path): - raise OSError(errno.ENOTDIR, "%s is not a directory"%path) class LLDBTest(TestFormat): def __init__(self, dotest_cmd): @@ -73,33 +55,10 @@ # python exe as the first parameter of the command. cmd = [executable] + self.dotest_cmd + [testPath, '-p', testFile] - builddir = getBuildDir(cmd) - mkdir_p(builddir) - - # On macOS, we can't do the DYLD_INSERT_LIBRARIES trick with a shim - # python binary as the ASan interceptors get loaded too late. Also, - # when SIP is enabled, we can't inject libraries into system binaries - # at all, so we need a copy of the "real" python to work with. - # - # Find the "real" python binary, copy it, and invoke it. - if 'DYLD_INSERT_LIBRARIES' in test.config.environment and \ - platform.system() == 'Darwin': - copied_python = os.path.join(builddir, 'copied-system-python') - if not os.path.isfile(copied_python): - import shutil, subprocess - python = subprocess.check_output([ - executable, - os.path.join(os.path.dirname(os.path.realpath(__file__)), - 'get_darwin_real_python.py') - ]).decode('utf-8').strip() - shutil.copy(python, copied_python) - cmd[0] = copied_python - if 'lldb-repro-capture' in test.config.available_features or \ 'lldb-repro-replay' in test.config.available_features: - reproducer_root = os.path.join(builddir, 'reproducers') - mkdir_p(reproducer_root) - reproducer_path = os.path.join(reproducer_root, testFile) + reproducer_path = os.path.join( + test.config.lldb_reproducer_directory, testFile) if 'lldb-repro-capture' in test.config.available_features: cmd.extend(['--capture-path', reproducer_path]) else: