diff --git a/llvm/test/tools/llvm-exegesis/X86/latency/memory-annotations-livein.s b/llvm/test/tools/llvm-exegesis/X86/latency/memory-annotations-livein.s --- a/llvm/test/tools/llvm-exegesis/X86/latency/memory-annotations-livein.s +++ b/llvm/test/tools/llvm-exegesis/X86/latency/memory-annotations-livein.s @@ -1,4 +1,4 @@ -# REQUIRES: exegesis-can-execute-in-subprocess, x86_64-linux +# REQUIRES: exegesis-can-measure-latency, x86_64-linux # Test that we can use the subprocess executor mode with memory annotations # while having live-ins still work as expected. diff --git a/llvm/test/tools/llvm-exegesis/X86/latency/memory-annotations.s b/llvm/test/tools/llvm-exegesis/X86/latency/memory-annotations.s --- a/llvm/test/tools/llvm-exegesis/X86/latency/memory-annotations.s +++ b/llvm/test/tools/llvm-exegesis/X86/latency/memory-annotations.s @@ -1,4 +1,4 @@ -# REQUIRES: exegesis-can-execute-in-subprocess, x86_64-linux +# REQUIRES: exegesis-can-measure-latency, x86_64-linux # Test the basic functionality of memory annotations, namely that we can # specify a memory definition, map it into the process, and then use the diff --git a/llvm/test/tools/llvm-exegesis/X86/latency/subprocess-abnormal-exit-code.s b/llvm/test/tools/llvm-exegesis/X86/latency/subprocess-abnormal-exit-code.s --- a/llvm/test/tools/llvm-exegesis/X86/latency/subprocess-abnormal-exit-code.s +++ b/llvm/test/tools/llvm-exegesis/X86/latency/subprocess-abnormal-exit-code.s @@ -1,4 +1,4 @@ -# REQUIRES: exegesis-can-execute-in-subprocess, x86_64-linux +# REQUIRES: exegesis-can-measure-latency, x86_64-linux # RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mode=latency -snippets-file=%s -execution-mode=subprocess | FileCheck %s diff --git a/llvm/test/tools/llvm-exegesis/X86/latency/subprocess-segfault.s b/llvm/test/tools/llvm-exegesis/X86/latency/subprocess-segfault.s --- a/llvm/test/tools/llvm-exegesis/X86/latency/subprocess-segfault.s +++ b/llvm/test/tools/llvm-exegesis/X86/latency/subprocess-segfault.s @@ -1,4 +1,4 @@ -# REQUIRES: exegesis-can-execute-in-subprocess, x86_64-linux +# REQUIRES: exegesis-can-measure-latency, x86_64-linux # RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mode=latency -snippets-file=%s -execution-mode=subprocess | FileCheck %s diff --git a/llvm/test/tools/llvm-exegesis/X86/latency/subprocess.s b/llvm/test/tools/llvm-exegesis/X86/latency/subprocess.s --- a/llvm/test/tools/llvm-exegesis/X86/latency/subprocess.s +++ b/llvm/test/tools/llvm-exegesis/X86/latency/subprocess.s @@ -1,4 +1,4 @@ -# REQUIRES: exegesis-can-execute-in-subprocess, x86_64-linux +# REQUIRES: exegesis-can-measure-latency, x86_64-linux # RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mode=latency -snippets-file=%s -execution-mode=subprocess | FileCheck %s diff --git a/llvm/test/tools/llvm-exegesis/lit.local.cfg b/llvm/test/tools/llvm-exegesis/lit.local.cfg --- a/llvm/test/tools/llvm-exegesis/lit.local.cfg +++ b/llvm/test/tools/llvm-exegesis/lit.local.cfg @@ -31,35 +31,6 @@ return False -def can_execute_in_subprocess(): - # We need certain Linux system calls present in order to run the subprocess - # executor mode, so check that we can use the subprocess mode to prevent - # test failures on platforms running older kernels. - llvm_exegesis_exe = lit.util.which("llvm-exegesis", config.llvm_tools_dir) - if llvm_exegesis_exe is None: - print("could not find llvm-exegesis") - return False - try: - command_vector = [ - llvm_exegesis_exe, - "-mode=latency", - "--execution-mode=subprocess", - "-snippets-file=/dev/null", - ] - with subprocess.Popen( - command_vector, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL - ) as exegesis_subprocess: - stdout, stderr = exegesis_subprocess.communicate() - exegesis_output = stdout.decode("utf-8") - # Return true if we have an empty error section as llvm-exegesis - # doesn't change the return code if there's a snippet crash. - return re.search("error:\s*''", exegesis_output) is not None - - except OSError: - print("Could not execute llvm-exegesis in subprocess executor mode") - return False - - for arch in ["aarch64", "mips", "powerpc", "x86_64"]: if can_execute_generated_snippets(arch): config.available_features.add("exegesis-can-execute-%s" % arch) @@ -76,6 +47,3 @@ "latency", ["-x86-lbr-sample-period", "123", "-repetition-mode", "loop"] ): config.available_features.add("exegesis-can-measure-latency-lbr") - -if can_execute_in_subprocess(): - config.available_features.add("exegesis-can-execute-in-subprocess") diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp --- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -34,6 +34,7 @@ #endif #include #include +#include #include #include #include @@ -169,22 +170,15 @@ private: enum ChildProcessExitCodeE { CounterFDReadFailed = 1, - TranslatingCounterFDFailed, RSeqDisableFailed, FunctionDataMappingFailed, AuxiliaryMemorySetupFailed - }; StringRef childProcessExitCodeToString(int ExitCode) const { switch (ExitCode) { case ChildProcessExitCodeE::CounterFDReadFailed: return "Counter file descriptor read failed"; - case ChildProcessExitCodeE::TranslatingCounterFDFailed: - return "Translating counter file descriptor into a file descriptor in " - "the child process failed. This might be due running an older " - "Linux kernel that doesn't support the pidfd_getfd system call " - "(anything before Linux 5.6)."; case ChildProcessExitCodeE::RSeqDisableFailed: return "Disabling restartable sequences failed"; case ChildProcessExitCodeE::FunctionDataMappingFailed: @@ -196,10 +190,53 @@ } } + Error sendFileDescriptorThroughSocket(int SocketFD, int FD) const { + struct msghdr Message = {}; + char Buffer[CMSG_SPACE(sizeof(FD))]; + memset(Buffer, 0, sizeof(Buffer)); + Message.msg_control = Buffer; + Message.msg_controllen = sizeof(Buffer); + + struct cmsghdr *ControlMessage = CMSG_FIRSTHDR(&Message); + ControlMessage->cmsg_level = SOL_SOCKET; + ControlMessage->cmsg_type = SCM_RIGHTS; + ControlMessage->cmsg_len = CMSG_LEN(sizeof(FD)); + + *((int *)CMSG_DATA(ControlMessage)) = FD; + + Message.msg_controllen = CMSG_SPACE(sizeof(FD)); + + ssize_t BytesWritten = sendmsg(SocketFD, &Message, 0); + + if (BytesWritten < 0) + return make_error("Failed to write FD to socket"); + + return Error::success(); + } + + Expected getFileDescriptorFromSocket(int SocketFD) const { + struct msghdr Message = {}; + + char ControlBuffer[256]; + Message.msg_control = ControlBuffer; + Message.msg_controllen = sizeof(ControlBuffer); + + size_t BytesRead = recvmsg(SocketFD, &Message, 0); + + if (BytesRead < 0) + return make_error("Failed to read FD from socket"); + + struct cmsghdr *ControlMessage = CMSG_FIRSTHDR(&Message); + + unsigned char *RawData = CMSG_DATA(ControlMessage); + + return *((int *)RawData); + } + Error createSubProcessAndRunBenchmark( StringRef CounterName, SmallVectorImpl &CounterValues) const { int PipeFiles[2]; - int PipeSuccessOrErr = pipe(PipeFiles); + int PipeSuccessOrErr = socketpair(AF_UNIX, SOCK_DGRAM, 0, PipeFiles); if (PipeSuccessOrErr != 0) { return make_error( "Failed to create a pipe for interprocess communication between " @@ -241,13 +278,11 @@ close(PipeFiles[0]); int CounterFileDescriptor = Counter->getFileDescriptor(); - ssize_t BytesWritten = - write(PipeFiles[1], &CounterFileDescriptor, sizeof(int)); + Error SendError = + sendFileDescriptorThroughSocket(PipeFiles[1], CounterFileDescriptor); - if (BytesWritten != sizeof(int)) - return make_error("Writing peformance counter file descriptor " - "to child process failed: " + - Twine(strerror(errno))); + if (SendError) + return SendError; if (ptrace(PTRACE_SEIZE, ParentOrChildPID, NULL, NULL) != 0) return make_error("Failed to seize the child process: " + @@ -290,31 +325,15 @@ [[noreturn]] void prepareAndRunBenchmark(int Pipe, const BenchmarkKey &Key) const { // The following occurs within the benchmarking subprocess + pid_t ParentPID = getppid(); - int ParentCounterFileDescriptor = -1; - ssize_t BytesRead = read(Pipe, &ParentCounterFileDescriptor, sizeof(int)); + Expected CounterFileDescriptorOrError = + getFileDescriptorFromSocket(Pipe); - if (BytesRead != sizeof(int)) { + if (!CounterFileDescriptorOrError) exit(ChildProcessExitCodeE::CounterFDReadFailed); - } - pid_t ParentPID = getppid(); - - // Make sure the following two syscalls are defined on the platform that - // we're building on as they were introduced to the kernel fairly recently - // (v5.6 for the second one). -#if defined(SYS_pidfd_open) && defined(SYS_pidfd_getfd) - int ParentPIDFD = syscall(SYS_pidfd_open, ParentPID, 0); - int CounterFileDescriptor = - syscall(SYS_pidfd_getfd, ParentPIDFD, ParentCounterFileDescriptor, 0); -#else - int CounterFileDescriptor = 0; - exit(ChildProcessExitCodeE::TranslatingCounterFDFailed); -#endif - - if (CounterFileDescriptor == -1) { - exit(ChildProcessExitCodeE::TranslatingCounterFDFailed); - } + int CounterFileDescriptor = *CounterFileDescriptorOrError; // Glibc versions greater than 2.35 automatically call rseq during // initialization. Unmapping the region that glibc sets up for this causes