203 lines · cpp
1//===- unittests/Interpreter/OutOfProcessInterpreterTest.cpp --- Interpreter2// tests when Out-of-Process ----===//3//4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5// See https://llvm.org/LICENSE.txt for license information.6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7//8//===----------------------------------------------------------------------===//9//10// Unit tests for Clang's Interpreter library.11//12//===----------------------------------------------------------------------===//13 14#include "InterpreterTestFixture.h"15#include "clang/AST/Decl.h"16#include "clang/AST/DeclGroup.h"17#include "clang/AST/Mangle.h"18#include "clang/Basic/Version.h"19#include "clang/Config/config.h"20#include "clang/Frontend/CompilerInstance.h"21#include "clang/Frontend/TextDiagnosticPrinter.h"22#include "clang/Interpreter/Interpreter.h"23#include "clang/Interpreter/Value.h"24#include "clang/Sema/Lookup.h"25#include "clang/Sema/Sema.h"26#include "llvm/Support/Error.h"27#include "llvm/TargetParser/Host.h"28#include "gmock/gmock.h"29#include "gtest/gtest.h"30#include <memory>31#include <signal.h>32#include <sstream>33#include <unistd.h>34 35using namespace clang;36 37llvm::ExitOnError ExitOnError;38 39namespace {40 41using Args = std::vector<const char *>;42 43struct FileDeleter {44 void operator()(FILE *f) {45 if (f)46 fclose(f);47 }48};49 50struct IOContext {51 std::unique_ptr<FILE, FileDeleter> stdin_file;52 std::unique_ptr<FILE, FileDeleter> stdout_file;53 std::unique_ptr<FILE, FileDeleter> stderr_file;54 55 bool initializeTempFiles() {56 stdin_file.reset(tmpfile());57 stdout_file.reset(tmpfile());58 stderr_file.reset(tmpfile());59 return stdin_file && stdout_file && stderr_file;60 }61 62 std::string readStdoutContent() {63 if (!stdout_file)64 return "";65 rewind(stdout_file.get());66 std::ostringstream content;67 char buffer[1024];68 size_t bytes_read;69 while ((bytes_read = fread(buffer, 1, sizeof(buffer), stdout_file.get())) >70 0) {71 content.write(buffer, bytes_read);72 }73 return content.str();74 }75 76 std::string readStderrContent() {77 if (!stderr_file)78 return "";79 rewind(stderr_file.get());80 std::ostringstream content;81 char buffer[1024];82 size_t bytes_read;83 while ((bytes_read = fread(buffer, 1, sizeof(buffer), stderr_file.get())) >84 0) {85 content.write(buffer, bytes_read);86 }87 return content.str();88 }89};90 91static void removePathComponent(unsigned N, llvm::SmallString<256> &Path) {92 for (unsigned i = 0; i < N; ++i)93 llvm::sys::path::remove_filename(Path);94}95 96static std::string getExecutorPath() {97 llvm::SmallString<256> ExecutorPath(llvm::sys::fs::getMainExecutable(98 nullptr, reinterpret_cast<void *>(&getExecutorPath)));99 removePathComponent(5, ExecutorPath);100 llvm::sys::path::append(ExecutorPath, "bin", "llvm-jitlink-executor");101 return ExecutorPath.str().str();102}103 104static std::string getOrcRuntimePath() {105 llvm::SmallString<256> RuntimePath(llvm::sys::fs::getMainExecutable(106 nullptr, reinterpret_cast<void *>(&getOrcRuntimePath)));107 removePathComponent(5, RuntimePath);108 llvm::sys::path::append(RuntimePath, CLANG_INSTALL_LIBDIR_BASENAME, "clang",109 CLANG_VERSION_MAJOR_STRING, "lib");110 111 llvm::Triple SystemTriple(llvm::sys::getProcessTriple());112 if (SystemTriple.isOSBinFormatMachO()) {113 llvm::sys::path::append(RuntimePath, "darwin", "liborc_rt_osx.a");114 } else if (SystemTriple.isOSBinFormatELF()) {115 llvm::sys::path::append(RuntimePath, "x86_64-unknown-linux-gnu",116 "liborc_rt.a");117 }118 return RuntimePath.str().str();119}120 121static std::unique_ptr<Interpreter>122createInterpreterWithRemoteExecution(std::shared_ptr<IOContext> io_ctx,123 const Args &ExtraArgs = {}) {124 Args ClangArgs = {"-Xclang", "-emit-llvm-only"};125 llvm::append_range(ClangArgs, ExtraArgs);126 auto CB = clang::IncrementalCompilerBuilder();127 CB.SetCompilerArgs(ClangArgs);128 auto CI = cantFail(CB.CreateCpp());129 130 clang::Interpreter::JITConfig Config;131 llvm::Triple SystemTriple(llvm::sys::getProcessTriple());132 133 if (SystemTriple.isOSBinFormatELF() || SystemTriple.isOSBinFormatMachO()) {134 Config.IsOutOfProcess = true;135 Config.OOPExecutor = getExecutorPath();136 Config.UseSharedMemory = false;137 Config.SlabAllocateSize = 0;138 Config.OrcRuntimePath = getOrcRuntimePath();139 140 int stdin_fd = fileno(io_ctx->stdin_file.get());141 int stdout_fd = fileno(io_ctx->stdout_file.get());142 int stderr_fd = fileno(io_ctx->stderr_file.get());143 144 Config.CustomizeFork = [=] {145 auto redirect = [](int from, int to) {146 if (from != to) {147 dup2(from, to);148 close(from);149 }150 };151 152 redirect(stdin_fd, STDIN_FILENO);153 redirect(stdout_fd, STDOUT_FILENO);154 redirect(stderr_fd, STDERR_FILENO);155 156 setvbuf(stdout, nullptr, _IONBF, 0);157 setvbuf(stderr, nullptr, _IONBF, 0);158 159 printf("CustomizeFork executed\n");160 fflush(stdout);161 };162 }163 164 return cantFail(clang::Interpreter::create(std::move(CI), Config));165}166 167static size_t DeclsSize(TranslationUnitDecl *PTUDecl) {168 return std::distance(PTUDecl->decls().begin(), PTUDecl->decls().end());169}170 171TEST_F(InterpreterTestBase, SanityWithRemoteExecution) {172 if (!HostSupportsJIT())173 GTEST_SKIP();174 175 std::string OrcRuntimePath = getOrcRuntimePath();176 std::string ExecutorPath = getExecutorPath();177 178 if (!llvm::sys::fs::exists(OrcRuntimePath) ||179 !llvm::sys::fs::exists(ExecutorPath))180 GTEST_SKIP();181 182 auto io_ctx = std::make_shared<IOContext>();183 ASSERT_TRUE(io_ctx->initializeTempFiles());184 185 std::unique_ptr<Interpreter> Interp =186 createInterpreterWithRemoteExecution(io_ctx);187 ASSERT_TRUE(Interp);188 189 using PTU = PartialTranslationUnit;190 PTU &R1(cantFail(Interp->Parse("void g(); void g() {}")));191 EXPECT_EQ(2U, DeclsSize(R1.TUPart));192 193 PTU &R2(cantFail(Interp->Parse("int i = 42;")));194 EXPECT_EQ(1U, DeclsSize(R2.TUPart));195 196 std::string captured_stdout = io_ctx->readStdoutContent();197 std::string captured_stderr = io_ctx->readStderrContent();198 199 EXPECT_TRUE(captured_stdout.find("CustomizeFork executed") !=200 std::string::npos);201}202 203} // end anonymous namespace