[llvm] f3dfcc5 - [llvm-exegesis] Support older kernel versions in subprocess executor

Aiden Grossman via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 18 10:45:27 PDT 2023


Author: Aiden Grossman
Date: 2023-07-18T10:42:45-07:00
New Revision: f3dfcc5053ada898f0327ee2371d53be0f71f0a6

URL: https://github.com/llvm/llvm-project/commit/f3dfcc5053ada898f0327ee2371d53be0f71f0a6
DIFF: https://github.com/llvm/llvm-project/commit/f3dfcc5053ada898f0327ee2371d53be0f71f0a6.diff

LOG: [llvm-exegesis] Support older kernel versions in subprocess executor

This patch switches from moving the performance counter file descriptor
to the child process to socket calls rather than using the pidfd_getfd
system call which was introduced in kernel 5.6. This significantly
expands the range of kernel versions that are supported.

Reviewed By: courbet

Differential Revision: https://reviews.llvm.org/D154275

Added: 
    

Modified: 
    llvm/test/tools/llvm-exegesis/lit.local.cfg
    llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-exegesis/lit.local.cfg b/llvm/test/tools/llvm-exegesis/lit.local.cfg
index 1cb43f539c292d..b09003d1e23591 100644
--- a/llvm/test/tools/llvm-exegesis/lit.local.cfg
+++ b/llvm/test/tools/llvm-exegesis/lit.local.cfg
@@ -31,35 +31,6 @@ def can_use_perf_counters(mode, extra_options=[]):
         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)
@@ -79,5 +50,6 @@ if can_execute_generated_snippets("x86_64"):
 
 # The subprocess tests are flaky on some of the builders that support them, so
 # they are disabled currently.
-#if can_execute_in_subprocess():
+#if True:
 #    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
index 370eb886069ca1..5acc8856d95405 100644
--- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
+++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
@@ -34,6 +34,7 @@
 #endif
 #include <sys/mman.h>
 #include <sys/ptrace.h>
+#include <sys/socket.h>
 #include <sys/syscall.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -169,22 +170,15 @@ class SubProcessFunctionExecutorImpl
 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,59 @@ class SubProcessFunctionExecutorImpl
     }
   }
 
+  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));
+
+    memcpy(CMSG_DATA(ControlMessage), &FD, sizeof(FD));
+
+    Message.msg_controllen = CMSG_SPACE(sizeof(FD));
+
+    ssize_t BytesWritten = sendmsg(SocketFD, &Message, 0);
+
+    if (BytesWritten < 0)
+      return make_error<Failure>("Failed to write FD to socket");
+
+    return Error::success();
+  }
+
+  Expected<int> 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<Failure>("Failed to read FD from socket");
+
+    struct cmsghdr *ControlMessage = CMSG_FIRSTHDR(&Message);
+
+    int FD;
+
+    if (ControlMessage->cmsg_len != CMSG_LEN(sizeof(FD)))
+      return make_error<Failure>("Failed to get correct number of bytes for "
+                                 "file descriptor from socket.");
+
+    memcpy(&FD, CMSG_DATA(ControlMessage), sizeof(FD));
+
+    return FD;
+  }
+
   Error createSubProcessAndRunBenchmark(
       StringRef CounterName, SmallVectorImpl<int64_t> &CounterValues) const {
     int PipeFiles[2];
-    int PipeSuccessOrErr = pipe(PipeFiles);
+    int PipeSuccessOrErr = socketpair(AF_UNIX, SOCK_DGRAM, 0, PipeFiles);
     if (PipeSuccessOrErr != 0) {
       return make_error<Failure>(
           "Failed to create a pipe for interprocess communication between "
@@ -241,13 +284,11 @@ class SubProcessFunctionExecutorImpl
     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<Failure>("Writing peformance counter file descriptor "
-                                 "to child process failed: " +
-                                 Twine(strerror(errno)));
+    if (SendError)
+      return SendError;
 
     if (ptrace(PTRACE_ATTACH, ParentOrChildPID, NULL, NULL) != 0)
       return make_error<Failure>("Failed to attach to the child process: " +
@@ -301,31 +342,15 @@ class SubProcessFunctionExecutorImpl
   [[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<int> 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


        


More information about the llvm-commits mailing list