Skip to content

Commit b7f39b5

Browse files
abrachetmemfrob
authored and
memfrob
committed
[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
1 parent a132806 commit b7f39b5

File tree

3 files changed

+81
-14
lines changed

3 files changed

+81
-14
lines changed

libc/utils/UnitTest/Test.cpp

+28-2
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,20 @@ bool Test::testMatch(RunContext &Ctx, bool MatchResult, MatcherBase &Matcher,
250250
bool Test::testProcessKilled(RunContext &Ctx, testutils::FunctionCaller *Func,
251251
int Signal, const char *LHSStr, const char *RHSStr,
252252
const char *File, unsigned long Line) {
253-
testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func);
253+
testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func, 500);
254+
255+
if (const char *error = Result.getError()) {
256+
Ctx.markFail();
257+
llvm::outs() << File << ":" << Line << ": FAILURE\n" << error << '\n';
258+
return false;
259+
}
260+
261+
if (Result.timedOut()) {
262+
Ctx.markFail();
263+
llvm::outs() << File << ":" << Line << ": FAILURE\n"
264+
<< "Process timed out after " << 500 << " miliseconds.\n";
265+
return false;
266+
}
254267

255268
if (Result.exitedNormally()) {
256269
Ctx.markFail();
@@ -281,7 +294,20 @@ bool Test::testProcessExits(RunContext &Ctx, testutils::FunctionCaller *Func,
281294
int ExitCode, const char *LHSStr,
282295
const char *RHSStr, const char *File,
283296
unsigned long Line) {
284-
testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func);
297+
testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func, 500);
298+
299+
if (const char *error = Result.getError()) {
300+
Ctx.markFail();
301+
llvm::outs() << File << ":" << Line << ": FAILURE\n" << error << '\n';
302+
return false;
303+
}
304+
305+
if (Result.timedOut()) {
306+
Ctx.markFail();
307+
llvm::outs() << File << ":" << Line << ": FAILURE\n"
308+
<< "Process timed out after " << 500 << " miliseconds.\n";
309+
return false;
310+
}
285311

286312
if (!Result.exitedNormally()) {
287313
Ctx.markFail();

libc/utils/testutils/ExecuteFunction.h

+19-5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#ifndef LLVM_LIBC_UTILS_TESTUTILS_EXECUTEFUNCTION_H
1010
#define LLVM_LIBC_UTILS_TESTUTILS_EXECUTEFUNCTION_H
1111

12+
#include <stdint.h>
13+
1214
namespace __llvm_libc {
1315
namespace testutils {
1416

@@ -20,13 +22,25 @@ class FunctionCaller {
2022

2123
struct ProcessStatus {
2224
int PlatformDefined;
23-
24-
bool exitedNormally();
25-
int getExitCode();
26-
int getFatalSignal();
25+
const char *failure = nullptr;
26+
27+
static constexpr uintptr_t timeout = -1L;
28+
29+
static ProcessStatus Error(const char *error) { return {0, error}; }
30+
static ProcessStatus TimedOut() {
31+
return {0, reinterpret_cast<const char *>(timeout)};
32+
}
33+
34+
bool timedOut() const {
35+
return failure == reinterpret_cast<const char *>(timeout);
36+
}
37+
const char *getError() const { return timedOut() ? nullptr : failure; }
38+
bool exitedNormally() const;
39+
int getExitCode() const;
40+
int getFatalSignal() const;
2741
};
2842

29-
ProcessStatus invokeInSubprocess(FunctionCaller *Func);
43+
ProcessStatus invokeInSubprocess(FunctionCaller *Func, unsigned TimeoutMS = -1);
3044

3145
const char *signalAsString(int Signum);
3246

libc/utils/testutils/ExecuteFunctionUnix.cpp

+34-7
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,66 @@
1010
#include "llvm/Support/raw_ostream.h"
1111
#include <cassert>
1212
#include <cstdlib>
13+
#include <memory>
14+
#include <poll.h>
1315
#include <signal.h>
1416
#include <sys/wait.h>
1517
#include <unistd.h>
1618

1719
namespace __llvm_libc {
1820
namespace testutils {
1921

20-
bool ProcessStatus::exitedNormally() { return WIFEXITED(PlatformDefined); }
22+
bool ProcessStatus::exitedNormally() const {
23+
return WIFEXITED(PlatformDefined);
24+
}
2125

22-
int ProcessStatus::getExitCode() {
26+
int ProcessStatus::getExitCode() const {
2327
assert(exitedNormally() && "Abnormal termination, no exit code");
2428
return WEXITSTATUS(PlatformDefined);
2529
}
2630

27-
int ProcessStatus::getFatalSignal() {
31+
int ProcessStatus::getFatalSignal() const {
2832
if (exitedNormally())
2933
return 0;
3034
return WTERMSIG(PlatformDefined);
3135
}
3236

33-
ProcessStatus invokeInSubprocess(FunctionCaller *Func) {
37+
ProcessStatus invokeInSubprocess(FunctionCaller *Func, unsigned timeoutMS) {
38+
std::unique_ptr<FunctionCaller> X(Func);
39+
int pipeFDs[2];
40+
if (::pipe(pipeFDs) == -1)
41+
return ProcessStatus::Error("pipe(2) failed");
42+
3443
// Don't copy the buffers into the child process and print twice.
3544
llvm::outs().flush();
3645
llvm::errs().flush();
3746
pid_t Pid = ::fork();
47+
if (Pid == -1)
48+
return ProcessStatus::Error("fork(2) failed");
49+
3850
if (!Pid) {
3951
(*Func)();
4052
std::exit(0);
4153
}
54+
::close(pipeFDs[1]);
55+
56+
struct pollfd pollFD {
57+
pipeFDs[0], 0, 0
58+
};
59+
// No events requested so this call will only return after the timeout or if
60+
// the pipes peer was closed, signaling the process exited.
61+
if (::poll(&pollFD, 1, timeoutMS) == -1)
62+
return ProcessStatus::Error("poll(2) failed");
63+
// If the pipe wasn't closed by the child yet then timeout has expired.
64+
if (!(pollFD.revents & POLLHUP)) {
65+
::kill(Pid, SIGKILL);
66+
return ProcessStatus::TimedOut();
67+
}
4268

43-
int WStatus;
44-
::waitpid(Pid, &WStatus, 0);
45-
delete Func;
69+
int WStatus = 0;
70+
int status = ::waitpid(Pid, &WStatus, WNOHANG);
71+
assert(status == Pid && "wait call should not block here");
72+
(void)status;
4673
return {WStatus};
4774
}
4875

0 commit comments

Comments
 (0)