[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