1+ // ===- unittests/Interpreter/OutOfProcessInterpreterTest.cpp --- Interpreter
2+ // 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-exception
7+ //
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+
35+ using namespace clang ;
36+
37+ llvm::ExitOnError ExitOnError;
38+
39+ namespace {
40+
41+ using Args = std::vector<const char *>;
42+
43+ struct FileDeleter {
44+ void operator ()(FILE *f) {
45+ if (f)
46+ fclose (f);
47+ }
48+ };
49+
50+ struct 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+
91+ static 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+
96+ static 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+
104+ static 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+
121+ static std::unique_ptr<Interpreter>
122+ createInterpreterWithRemoteExecution (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+
167+ static size_t DeclsSize (TranslationUnitDecl *PTUDecl) {
168+ return std::distance (PTUDecl->decls ().begin (), PTUDecl->decls ().end ());
169+ }
170+
171+ TEST_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
0 commit comments