bool Test::testProcessKilled(RunContext &Ctx, testutils::FunctionCaller *Func,
int Signal, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line) {
- testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func);
+ testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func, 500);
+
+ if (const char *error = Result.getError()) {
+ Ctx.markFail();
+ llvm::outs() << File << ":" << Line << ": FAILURE\n" << error << '\n';
+ return false;
+ }
+
+ if (Result.timedOut()) {
+ Ctx.markFail();
+ llvm::outs() << File << ":" << Line << ": FAILURE\n"
+ << "Process timed out after " << 500 << " miliseconds.\n";
+ return false;
+ }
if (Result.exitedNormally()) {
Ctx.markFail();
int ExitCode, const char *LHSStr,
const char *RHSStr, const char *File,
unsigned long Line) {
- testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func);
+ testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func, 500);
+
+ if (const char *error = Result.getError()) {
+ Ctx.markFail();
+ llvm::outs() << File << ":" << Line << ": FAILURE\n" << error << '\n';
+ return false;
+ }
+
+ if (Result.timedOut()) {
+ Ctx.markFail();
+ llvm::outs() << File << ":" << Line << ": FAILURE\n"
+ << "Process timed out after " << 500 << " miliseconds.\n";
+ return false;
+ }
if (!Result.exitedNormally()) {
Ctx.markFail();
#ifndef LLVM_LIBC_UTILS_TESTUTILS_EXECUTEFUNCTION_H
#define LLVM_LIBC_UTILS_TESTUTILS_EXECUTEFUNCTION_H
+#include <stdint.h>
+
namespace __llvm_libc {
namespace testutils {
struct ProcessStatus {
int PlatformDefined;
-
- bool exitedNormally();
- int getExitCode();
- int getFatalSignal();
+ const char *failure = nullptr;
+
+ static constexpr uintptr_t timeout = -1L;
+
+ static ProcessStatus Error(const char *error) { return {0, error}; }
+ static ProcessStatus TimedOut() {
+ return {0, reinterpret_cast<const char *>(timeout)};
+ }
+
+ bool timedOut() const {
+ return failure == reinterpret_cast<const char *>(timeout);
+ }
+ const char *getError() const { return timedOut() ? nullptr : failure; }
+ bool exitedNormally() const;
+ int getExitCode() const;
+ int getFatalSignal() const;
};
-ProcessStatus invokeInSubprocess(FunctionCaller *Func);
+ProcessStatus invokeInSubprocess(FunctionCaller *Func, unsigned TimeoutMS = -1);
const char *signalAsString(int Signum);
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstdlib>
+#include <memory>
+#include <poll.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
namespace __llvm_libc {
namespace testutils {
-bool ProcessStatus::exitedNormally() { return WIFEXITED(PlatformDefined); }
+bool ProcessStatus::exitedNormally() const {
+ return WIFEXITED(PlatformDefined);
+}
-int ProcessStatus::getExitCode() {
+int ProcessStatus::getExitCode() const {
assert(exitedNormally() && "Abnormal termination, no exit code");
return WEXITSTATUS(PlatformDefined);
}
-int ProcessStatus::getFatalSignal() {
+int ProcessStatus::getFatalSignal() const {
if (exitedNormally())
return 0;
return WTERMSIG(PlatformDefined);
}
-ProcessStatus invokeInSubprocess(FunctionCaller *Func) {
+ProcessStatus invokeInSubprocess(FunctionCaller *Func, unsigned timeoutMS) {
+ std::unique_ptr<FunctionCaller> X(Func);
+ int pipeFDs[2];
+ if (::pipe(pipeFDs) == -1)
+ return ProcessStatus::Error("pipe(2) failed");
+
// Don't copy the buffers into the child process and print twice.
llvm::outs().flush();
llvm::errs().flush();
pid_t Pid = ::fork();
+ if (Pid == -1)
+ return ProcessStatus::Error("fork(2) failed");
+
if (!Pid) {
(*Func)();
std::exit(0);
}
+ ::close(pipeFDs[1]);
+
+ struct pollfd pollFD {
+ pipeFDs[0], 0, 0
+ };
+ // No events requested so this call will only return after the timeout or if
+ // the pipes peer was closed, signaling the process exited.
+ if (::poll(&pollFD, 1, timeoutMS) == -1)
+ return ProcessStatus::Error("poll(2) failed");
+ // If the pipe wasn't closed by the child yet then timeout has expired.
+ if (!(pollFD.revents & POLLHUP)) {
+ ::kill(Pid, SIGKILL);
+ return ProcessStatus::TimedOut();
+ }
- int WStatus;
- ::waitpid(Pid, &WStatus, 0);
- delete Func;
+ int WStatus = 0;
+ int status = ::waitpid(Pid, &WStatus, WNOHANG);
+ assert(status == Pid && "wait call should not block here");
+ (void)status;
return {WStatus};
}