blob: 704ddc37e642ef80b4b17d8ae9e571bae486be3f [file] [log] [blame]
//===- 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 <memory>
#include <signal.h>
#include <sstream>
#include <unistd.h>
using namespace clang;
llvm::ExitOnError ExitOnError;
namespace {
using Args = std::vector<const char *>;
struct FileDeleter {
void operator()(FILE *f) {
if (f)
fclose(f);
}
};
struct IOContext {
std::unique_ptr<FILE, FileDeleter> stdin_file;
std::unique_ptr<FILE, FileDeleter> stdout_file;
std::unique_ptr<FILE, FileDeleter> 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<void *>(&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<void *>(&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<Interpreter>
createInterpreterWithRemoteExecution(std::shared_ptr<IOContext> 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<IOContext>();
ASSERT_TRUE(io_ctx->initializeTempFiles());
std::unique_ptr<Interpreter> 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