using namespace llvm;
+extern cl::OptionCategory LLVMReduceOptions;
+static cl::opt<unsigned> PollInterval("process-poll-interval",
+ cl::desc("child process wait polling"),
+ cl::init(5), cl::Hidden,
+ cl::cat(LLVMReduceOptions));
+
TestRunner::TestRunner(StringRef TestName,
const std::vector<std::string> &TestArgs,
std::unique_ptr<ReducerWorkItem> Program,
/// Runs the interestingness test, passes file to be tested as first argument
/// and other specified test arguments after that.
-int TestRunner::run(StringRef Filename) const {
+int TestRunner::run(StringRef Filename, const std::atomic<bool> &Killed) const {
std::vector<StringRef> ProgramArgs;
ProgramArgs.push_back(TestName);
ProgramArgs.push_back(Filename);
std::string ErrMsg;
+ bool ExecutionFailed;
+ sys::ProcessInfo PI =
+ sys::ExecuteNoWait(TestName, ProgramArgs, /*Env=*/std::nullopt,
+ Verbose ? DefaultRedirects : NullRedirects,
+ /*MemoryLimit=*/0, &ErrMsg, &ExecutionFailed);
- int Result =
- sys::ExecuteAndWait(TestName, ProgramArgs, /*Env=*/std::nullopt,
- Verbose ? DefaultRedirects : NullRedirects,
- /*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg);
-
- if (Result < 0) {
+ if (ExecutionFailed) {
Error E = make_error<StringError>("Error running interesting-ness test: " +
ErrMsg,
inconvertibleErrorCode());
exit(1);
}
- return !Result;
+ // Poll every few seconds, taking a break to check if we should try to kill
+ // the process. We're trying to early exit on long running parallel reductions
+ // once we know they don't matter.
+ std::optional<unsigned> SecondsToWait(PollInterval);
+ bool Polling = true;
+ sys::ProcessInfo WaitPI;
+
+ while (WaitPI.Pid == 0) { // Process has not changed state.
+ WaitPI = sys::Wait(PI, SecondsToWait, &ErrMsg, nullptr, Polling);
+ // TODO: This should probably be std::atomic_flag
+ if (Killed) {
+ // The current Program API does not have a way to directly kill, but we
+ // can timeout after 0 seconds.
+ SecondsToWait = 0;
+ Polling = false;
+ }
+ }
+
+ return !WaitPI.ReturnCode;
}
void TestRunner::setProgram(std::unique_ptr<ReducerWorkItem> P) {
void readBitcode(ReducerWorkItem &M, MemoryBufferRef Data, LLVMContext &Ctx,
StringRef ToolName);
-bool isReduced(ReducerWorkItem &M, const TestRunner &Test) {
+bool isReduced(ReducerWorkItem &M, const TestRunner &Test,
+ const std::atomic<bool> &Killed) {
const bool UseBitcode = Test.inputIsBitcode() || TmpFilesAsBitcode;
SmallString<128> CurrentFilepath;
}
// Current Chunks aren't interesting
- return Test.run(CurrentFilepath);
+ return Test.run(CurrentFilepath, Killed);
}
/// Splits Chunks in half and prints them.
std::unique_ptr<ReducerWorkItem> Clone, const TestRunner &Test,
ReductionFunc ExtractChunksFromModule,
const DenseSet<Chunk> &UninterestingChunks,
- const std::vector<Chunk> &ChunksStillConsideredInteresting) {
+ const std::vector<Chunk> &ChunksStillConsideredInteresting,
+ const std::atomic<bool> &Killed) {
// Take all of ChunksStillConsideredInteresting chunks, except those we've
// already deemed uninteresting (UninterestingChunks) but didn't remove
// from ChunksStillConsideredInteresting yet, and additionally ignore
errs() << "\n";
}
- if (!isReduced(*Clone, Test)) {
+ if (!isReduced(*Clone, Test, Killed)) {
// Program became non-reduced, so this chunk appears to be interesting.
if (Verbose)
errs() << "\n";
Chunk &ChunkToCheckForUninterestingness, TestRunner &Test,
ReductionFunc ExtractChunksFromModule, DenseSet<Chunk> &UninterestingChunks,
std::vector<Chunk> &ChunksStillConsideredInteresting,
- SmallString<0> &OriginalBC, std::atomic<bool> &AnyReduced) {
+ SmallString<0> &OriginalBC, std::atomic<bool> &AnyReduced,
+ const std::atomic<bool> &Killed) {
LLVMContext Ctx;
auto CloneMMM = std::make_unique<ReducerWorkItem>();
MemoryBufferRef Data(StringRef(OriginalBC), "<bc file>");
if (std::unique_ptr<ReducerWorkItem> ChunkResult =
CheckChunk(ChunkToCheckForUninterestingness, std::move(CloneMMM),
Test, ExtractChunksFromModule, UninterestingChunks,
- ChunksStillConsideredInteresting)) {
+ ChunksStillConsideredInteresting, Killed)) {
raw_svector_ostream BCOS(Result);
writeBitcode(*ChunkResult, BCOS);
// Communicate that the task reduced a chunk.
assert(!verifyReducerWorkItem(Test.getProgram(), &errs()) &&
"input module is broken after counting chunks");
- assert(isReduced(Test.getProgram(), Test) &&
+ assert(isReduced(Test.getProgram(), Test, std::atomic<bool>()) &&
"input module no longer interesting after counting chunks");
#ifndef NDEBUG
writeBitcode(Test.getProgram(), BCOS);
}
+ // If doing parallel reduction, signal to running workitem threads that we
+ // no longer care about their results. They should try to kill the reducer
+ // workitem process and exit.
+ std::atomic<bool> Killed = false;
+
SharedTaskQueue TaskQueue;
for (auto I = ChunksStillConsideredInteresting.rbegin(),
E = ChunksStillConsideredInteresting.rend();
for (unsigned J = 0; J < NumInitialTasks; ++J) {
TaskQueue.emplace_back(ChunkThreadPool.async(
[J, I, &Test, &ExtractChunksFromModule, &UninterestingChunks,
- &ChunksStillConsideredInteresting, &OriginalBC, &AnyReduced]() {
+ &ChunksStillConsideredInteresting, &OriginalBC, &AnyReduced,
+ &Killed]() {
return ProcessChunkFromSerializedBitcode(
*(I + J), Test, ExtractChunksFromModule,
UninterestingChunks, ChunksStillConsideredInteresting,
- OriginalBC, AnyReduced);
+ OriginalBC, AnyReduced, Killed);
}));
}
TaskQueue.emplace_back(ChunkThreadPool.async(
[&Test, &ExtractChunksFromModule, &UninterestingChunks,
&ChunksStillConsideredInteresting, &OriginalBC,
- &ChunkToCheck, &AnyReduced]() {
+ &ChunkToCheck, &AnyReduced, &Killed]() {
return ProcessChunkFromSerializedBitcode(
ChunkToCheck, Test, ExtractChunksFromModule,
UninterestingChunks, ChunksStillConsideredInteresting,
- OriginalBC, AnyReduced);
+ OriginalBC, AnyReduced, Killed);
}));
}
continue;
break;
}
+ Killed = true;
+
// If we broke out of the loop, we still need to wait for everything to
// avoid race access to the chunk set.
//
*I,
cloneReducerWorkItem(Test.getProgram(), Test.getTargetMachine()),
Test, ExtractChunksFromModule, UninterestingChunks,
- ChunksStillConsideredInteresting);
+ ChunksStillConsideredInteresting, Killed);
}
if (!Result)