//===- unittests/Interpreter/OutOfProcessInterpreterTest.cpp --- Interpreter // tests when Out-of-Process ----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Unit tests for Clang's Interpreter library. // //===----------------------------------------------------------------------===// #include "InterpreterTestFixture.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/Mangle.h" #include "clang/Basic/Version.h" #include "clang/Config/config.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Interpreter/Interpreter.h" #include "clang/Interpreter/Value.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" #include "llvm/Support/Error.h" #include "llvm/TargetParser/Host.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include #include using namespace clang; llvm::ExitOnError ExitOnError; namespace { using Args = std::vector; struct FileDeleter { void operator()(FILE *f) { if (f) fclose(f); } }; struct IOContext { std::unique_ptr stdin_file; std::unique_ptr stdout_file; std::unique_ptr stderr_file; bool initializeTempFiles() { stdin_file.reset(tmpfile()); stdout_file.reset(tmpfile()); stderr_file.reset(tmpfile()); return stdin_file && stdout_file && stderr_file; } std::string readStdoutContent() { if (!stdout_file) return ""; rewind(stdout_file.get()); std::ostringstream content; char buffer[1024]; size_t bytes_read; while ((bytes_read = fread(buffer, 1, sizeof(buffer), stdout_file.get())) > 0) { content.write(buffer, bytes_read); } return content.str(); } std::string readStderrContent() { if (!stderr_file) return ""; rewind(stderr_file.get()); std::ostringstream content; char buffer[1024]; size_t bytes_read; while ((bytes_read = fread(buffer, 1, sizeof(buffer), stderr_file.get())) > 0) { content.write(buffer, bytes_read); } return content.str(); } }; static void removePathComponent(unsigned N, llvm::SmallString<256> &Path) { for (unsigned i = 0; i < N; ++i) llvm::sys::path::remove_filename(Path); } static std::string getExecutorPath() { llvm::SmallString<256> ExecutorPath(llvm::sys::fs::getMainExecutable( nullptr, reinterpret_cast(&getExecutorPath))); removePathComponent(5, ExecutorPath); llvm::sys::path::append(ExecutorPath, "bin", "llvm-jitlink-executor"); return ExecutorPath.str().str(); } static std::string getOrcRuntimePath() { llvm::SmallString<256> RuntimePath(llvm::sys::fs::getMainExecutable( nullptr, reinterpret_cast(&getOrcRuntimePath))); removePathComponent(5, RuntimePath); llvm::sys::path::append(RuntimePath, CLANG_INSTALL_LIBDIR_BASENAME, "clang", CLANG_VERSION_MAJOR_STRING, "lib"); llvm::Triple SystemTriple(llvm::sys::getProcessTriple()); if (SystemTriple.isOSBinFormatMachO()) { llvm::sys::path::append(RuntimePath, "darwin", "liborc_rt_osx.a"); } else if (SystemTriple.isOSBinFormatELF()) { llvm::sys::path::append(RuntimePath, "x86_64-unknown-linux-gnu", "liborc_rt.a"); } return RuntimePath.str().str(); } static std::unique_ptr createInterpreterWithRemoteExecution(std::shared_ptr io_ctx, const Args &ExtraArgs = {}) { Args ClangArgs = {"-Xclang", "-emit-llvm-only"}; llvm::append_range(ClangArgs, ExtraArgs); auto CB = clang::IncrementalCompilerBuilder(); CB.SetCompilerArgs(ClangArgs); auto CI = cantFail(CB.CreateCpp()); clang::Interpreter::JITConfig Config; llvm::Triple SystemTriple(llvm::sys::getProcessTriple()); if (SystemTriple.isOSBinFormatELF() || SystemTriple.isOSBinFormatMachO()) { Config.IsOutOfProcess = true; Config.OOPExecutor = getExecutorPath(); Config.UseSharedMemory = false; Config.SlabAllocateSize = 0; Config.OrcRuntimePath = getOrcRuntimePath(); int stdin_fd = fileno(io_ctx->stdin_file.get()); int stdout_fd = fileno(io_ctx->stdout_file.get()); int stderr_fd = fileno(io_ctx->stderr_file.get()); Config.CustomizeFork = [=] { auto redirect = [](int from, int to) { if (from != to) { dup2(from, to); close(from); } }; redirect(stdin_fd, STDIN_FILENO); redirect(stdout_fd, STDOUT_FILENO); redirect(stderr_fd, STDERR_FILENO); setvbuf(stdout, nullptr, _IONBF, 0); setvbuf(stderr, nullptr, _IONBF, 0); printf("CustomizeFork executed\n"); fflush(stdout); }; } return cantFail(clang::Interpreter::create(std::move(CI), Config)); } static size_t DeclsSize(TranslationUnitDecl *PTUDecl) { return std::distance(PTUDecl->decls().begin(), PTUDecl->decls().end()); } TEST_F(InterpreterTestBase, SanityWithRemoteExecution) { if (!HostSupportsJIT()) GTEST_SKIP(); std::string OrcRuntimePath = getOrcRuntimePath(); std::string ExecutorPath = getExecutorPath(); if (!llvm::sys::fs::exists(OrcRuntimePath) || !llvm::sys::fs::exists(ExecutorPath)) GTEST_SKIP(); auto io_ctx = std::make_shared(); ASSERT_TRUE(io_ctx->initializeTempFiles()); std::unique_ptr Interp = createInterpreterWithRemoteExecution(io_ctx); ASSERT_TRUE(Interp); using PTU = PartialTranslationUnit; PTU &R1(cantFail(Interp->Parse("void g(); void g() {}"))); EXPECT_EQ(2U, DeclsSize(R1.TUPart)); PTU &R2(cantFail(Interp->Parse("int i = 42;"))); EXPECT_EQ(1U, DeclsSize(R2.TUPart)); std::string captured_stdout = io_ctx->readStdoutContent(); std::string captured_stderr = io_ctx->readStderrContent(); EXPECT_TRUE(captured_stdout.find("CustomizeFork executed") != std::string::npos); } } // end anonymous namespace