[libc-commits] [libc] af0b0e0 - [libc] [UnitTest] Add timeout to death tests
Alex Brachet via libc-commits
libc-commits at lists.llvm.org
Wed Mar 11 20:58:47 PDT 2020
Author: Alex Brachet
Date: 2020-03-11T23:57:20-04:00
New Revision: af0b0e00fba9b13c23dabe19fcabde15672f9dc5
URL: https://github.com/llvm/llvm-project/commit/af0b0e00fba9b13c23dabe19fcabde15672f9dc5
DIFF: https://github.com/llvm/llvm-project/commit/af0b0e00fba9b13c23dabe19fcabde15672f9dc5.diff
LOG: [libc] [UnitTest] Add timeout to death tests
Summary:
This patch adds a timeout of 500ms to death tests. As we add multithreaded code and locks, deadlocks become more likely so timeout will be useful.
Additionally:
- Better error handling in `invokeSubprocess`
- Makes `ProcessStatus`'s methods const
Reviewers: sivachandra, MaskRay, gchatelet, PaulkaToast
Reviewed By: sivachandra, PaulkaToast
Subscribers: tschuett, libc-commits
Differential Revision: https://reviews.llvm.org/D75651
Added:
Modified:
libc/utils/UnitTest/Test.cpp
libc/utils/testutils/ExecuteFunction.h
libc/utils/testutils/ExecuteFunctionUnix.cpp
Removed:
################################################################################
diff --git a/libc/utils/UnitTest/Test.cpp b/libc/utils/UnitTest/Test.cpp
index 608a4df89979..8add925b760b 100644
--- a/libc/utils/UnitTest/Test.cpp
+++ b/libc/utils/UnitTest/Test.cpp
@@ -250,7 +250,20 @@ bool Test::testMatch(RunContext &Ctx, bool MatchResult, MatcherBase &Matcher,
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();
@@ -281,7 +294,20 @@ bool Test::testProcessExits(RunContext &Ctx, testutils::FunctionCaller *Func,
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();
diff --git a/libc/utils/testutils/ExecuteFunction.h b/libc/utils/testutils/ExecuteFunction.h
index f3ba4aeda59b..80da4ca9457f 100644
--- a/libc/utils/testutils/ExecuteFunction.h
+++ b/libc/utils/testutils/ExecuteFunction.h
@@ -9,6 +9,8 @@
#ifndef LLVM_LIBC_UTILS_TESTUTILS_EXECUTEFUNCTION_H
#define LLVM_LIBC_UTILS_TESTUTILS_EXECUTEFUNCTION_H
+#include <stdint.h>
+
namespace __llvm_libc {
namespace testutils {
@@ -20,13 +22,25 @@ class FunctionCaller {
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);
diff --git a/libc/utils/testutils/ExecuteFunctionUnix.cpp b/libc/utils/testutils/ExecuteFunctionUnix.cpp
index afc4ca1f1473..e920e50f7147 100644
--- a/libc/utils/testutils/ExecuteFunctionUnix.cpp
+++ b/libc/utils/testutils/ExecuteFunctionUnix.cpp
@@ -10,6 +10,8 @@
#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>
@@ -17,32 +19,57 @@
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};
}
More information about the libc-commits
mailing list