Skip to content

[llvm-exegesis] Refactor parent code to separate function #86232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 63 additions & 53 deletions llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,67 +278,28 @@ class SubProcessFunctionExecutorImpl
return FD;
}

Error createSubProcessAndRunBenchmark(
StringRef CounterName, SmallVectorImpl<int64_t> &CounterValues,
ArrayRef<const char *> ValidationCounters,
SmallVectorImpl<int64_t> &ValidationCounterValues) const {
int PipeFiles[2];
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 "
"llvm-exegesis and the benchmarking subprocess: " +
Twine(strerror(errno)));
}

SubprocessMemory SPMemory;
Error MemoryInitError = SPMemory.initializeSubprocessMemory(getpid());
if (MemoryInitError)
return MemoryInitError;

Error AddMemDefError =
SPMemory.addMemoryDefinition(Key.MemoryValues, getpid());
if (AddMemDefError)
return AddMemDefError;

pid_t ParentOrChildPID = fork();

if (ParentOrChildPID == -1) {
return make_error<Failure>("Failed to create child process: " +
Twine(strerror(errno)));
}

if (ParentOrChildPID == 0) {
// We are in the child process, close the write end of the pipe.
close(PipeFiles[1]);
// Unregister handlers, signal handling is now handled through ptrace in
// the host process.
sys::unregisterHandlers();
prepareAndRunBenchmark(PipeFiles[0], Key);
// The child process terminates in the above function, so we should never
// get to this point.
llvm_unreachable("Child process didn't exit when expected.");
}

Error
runParentProcess(pid_t ChildPID, int WriteFD, StringRef CounterName,
SmallVectorImpl<int64_t> &CounterValues,
ArrayRef<const char *> ValidationCounters,
SmallVectorImpl<int64_t> &ValidationCounterValues) const {
const ExegesisTarget &ET = State.getExegesisTarget();
auto CounterOrError = ET.createCounter(
CounterName, State, ValidationCounters, ParentOrChildPID);
auto CounterOrError =
ET.createCounter(CounterName, State, ValidationCounters, ChildPID);

if (!CounterOrError)
return CounterOrError.takeError();

pfm::CounterGroup *Counter = CounterOrError.get().get();

close(PipeFiles[0]);

// Make sure to attach to the process (and wait for the sigstop to be
// delivered and for the process to continue) before we write to the counter
// file descriptor. Attaching to the process before writing to the socket
// ensures that the subprocess at most has blocked on the read call. If we
// attach afterwards, the subprocess might exit before we get to the attach
// call due to effects like scheduler contention, introducing transient
// failures.
if (ptrace(PTRACE_ATTACH, ParentOrChildPID, NULL, NULL) != 0)
if (ptrace(PTRACE_ATTACH, ChildPID, NULL, NULL) != 0)
return make_error<Failure>("Failed to attach to the child process: " +
Twine(strerror(errno)));

Expand All @@ -348,14 +309,14 @@ class SubProcessFunctionExecutorImpl
Twine(strerror(errno)));
}

if (ptrace(PTRACE_CONT, ParentOrChildPID, NULL, NULL) != 0)
if (ptrace(PTRACE_CONT, ChildPID, NULL, NULL) != 0)
return make_error<Failure>(
"Failed to continue execution of the child process: " +
Twine(strerror(errno)));

int CounterFileDescriptor = Counter->getFileDescriptor();
Error SendError =
sendFileDescriptorThroughSocket(PipeFiles[1], CounterFileDescriptor);
sendFileDescriptorThroughSocket(WriteFD, CounterFileDescriptor);

if (SendError)
return SendError;
Expand Down Expand Up @@ -395,8 +356,7 @@ class SubProcessFunctionExecutorImpl

// An error was encountered running the snippet, process it
siginfo_t ChildSignalInfo;
if (ptrace(PTRACE_GETSIGINFO, ParentOrChildPID, NULL, &ChildSignalInfo) ==
-1) {
if (ptrace(PTRACE_GETSIGINFO, ChildPID, NULL, &ChildSignalInfo) == -1) {
return make_error<Failure>("Getting signal info from the child failed: " +
Twine(strerror(errno)));
}
Expand All @@ -408,15 +368,65 @@ class SubProcessFunctionExecutorImpl
return make_error<SnippetSignal>(ChildSignalInfo.si_signo);
}

Error createSubProcessAndRunBenchmark(
StringRef CounterName, SmallVectorImpl<int64_t> &CounterValues,
ArrayRef<const char *> ValidationCounters,
SmallVectorImpl<int64_t> &ValidationCounterValues) const {
int PipeFiles[2];
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 "
"llvm-exegesis and the benchmarking subprocess: " +
Twine(strerror(errno)));
}

SubprocessMemory SPMemory;
Error MemoryInitError = SPMemory.initializeSubprocessMemory(getpid());
if (MemoryInitError)
return MemoryInitError;

Error AddMemDefError =
SPMemory.addMemoryDefinition(Key.MemoryValues, getpid());
if (AddMemDefError)
return AddMemDefError;

pid_t ParentOrChildPID = fork();

if (ParentOrChildPID == -1) {
return make_error<Failure>("Failed to create child process: " +
Twine(strerror(errno)));
}

if (ParentOrChildPID == 0) {
// We are in the child process, close the write end of the pipe.
close(PipeFiles[1]);
// Unregister handlers, signal handling is now handled through ptrace in
// the host process.
sys::unregisterHandlers();
runChildSubprocess(PipeFiles[0], Key);
// The child process terminates in the above function, so we should never
// get to this point.
llvm_unreachable("Child process didn't exit when expected.");
}

// Close the read end of the pipe as we only need to write to the subprocess
// from the parent process.
close(PipeFiles[0]);
return runParentProcess(ParentOrChildPID, PipeFiles[1], CounterName,
CounterValues, ValidationCounters,
ValidationCounterValues);
}

void disableCoreDumps() const {
struct rlimit rlim;

rlim.rlim_cur = 0;
setrlimit(RLIMIT_CORE, &rlim);
}

[[noreturn]] void prepareAndRunBenchmark(int Pipe,
const BenchmarkKey &Key) const {
[[noreturn]] void runChildSubprocess(int Pipe,
const BenchmarkKey &Key) const {
// Disable core dumps in the child process as otherwise everytime we
// encounter an execution failure like a segmentation fault, we will create
// a core dump. We report the information directly rather than require the
Expand Down