|
|
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
|
#include "cmUVProcessChain.h"
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
#include <csignal>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <istream> // IWYU pragma: keep
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include <cm/memory>
|
|
|
|
|
|
|
|
#include <cm3p/uv.h>
|
|
|
|
|
|
|
|
#include "cm_fileno.hxx"
|
|
|
|
|
|
|
|
#include "cmGetPipes.h"
|
|
|
|
#include "cmUVHandlePtr.h"
|
|
|
|
|
|
|
|
struct cmUVProcessChain::InternalData
|
|
|
|
{
|
|
|
|
struct StreamData
|
|
|
|
{
|
|
|
|
int BuiltinStream = -1;
|
|
|
|
uv_stdio_container_t Stdio;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ProcessData
|
|
|
|
{
|
|
|
|
cmUVProcessChain::InternalData* Data;
|
|
|
|
cm::uv_process_ptr Process;
|
|
|
|
cm::uv_pipe_ptr InputPipe;
|
|
|
|
cm::uv_pipe_ptr OutputPipe;
|
|
|
|
Status ProcessStatus;
|
|
|
|
|
|
|
|
void Finish();
|
|
|
|
};
|
|
|
|
|
|
|
|
const cmUVProcessChainBuilder* Builder = nullptr;
|
|
|
|
|
|
|
|
bool Valid = false;
|
|
|
|
|
|
|
|
cm::uv_loop_ptr BuiltinLoop;
|
|
|
|
uv_loop_t* Loop;
|
|
|
|
|
|
|
|
StreamData InputStreamData;
|
|
|
|
StreamData OutputStreamData;
|
|
|
|
StreamData ErrorStreamData;
|
|
|
|
cm::uv_pipe_ptr TempOutputPipe;
|
|
|
|
cm::uv_pipe_ptr TempErrorPipe;
|
|
|
|
|
|
|
|
unsigned int ProcessesCompleted = 0;
|
|
|
|
std::vector<std::unique_ptr<ProcessData>> Processes;
|
|
|
|
|
|
|
|
bool Prepare(const cmUVProcessChainBuilder* builder);
|
|
|
|
void SpawnProcess(
|
|
|
|
std::size_t index,
|
|
|
|
const cmUVProcessChainBuilder::ProcessConfiguration& config, bool first,
|
|
|
|
bool last);
|
|
|
|
void Finish();
|
|
|
|
};
|
|
|
|
|
|
|
|
cmUVProcessChainBuilder::cmUVProcessChainBuilder() = default;
|
|
|
|
|
|
|
|
cmUVProcessChainBuilder& cmUVProcessChainBuilder::AddCommand(
|
|
|
|
const std::vector<std::string>& arguments)
|
|
|
|
{
|
|
|
|
if (!arguments.empty()) {
|
|
|
|
this->Processes.emplace_back();
|
|
|
|
this->Processes.back().Arguments = arguments;
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetBuiltinLoop()
|
|
|
|
{
|
|
|
|
this->Loop = nullptr;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalLoop(
|
|
|
|
uv_loop_t& loop)
|
|
|
|
{
|
|
|
|
this->Loop = &loop;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetNoStream(Stream stdio)
|
|
|
|
{
|
|
|
|
switch (stdio) {
|
|
|
|
case Stream_INPUT:
|
|
|
|
case Stream_OUTPUT:
|
|
|
|
case Stream_ERROR: {
|
|
|
|
auto& streamData = this->Stdio[stdio];
|
|
|
|
streamData.Type = None;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetBuiltinStream(
|
|
|
|
Stream stdio)
|
|
|
|
{
|
|
|
|
switch (stdio) {
|
|
|
|
case Stream_INPUT:
|
|
|
|
// FIXME
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Stream_OUTPUT:
|
|
|
|
case Stream_ERROR: {
|
|
|
|
auto& streamData = this->Stdio[stdio];
|
|
|
|
streamData.Type = Builtin;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalStream(
|
|
|
|
Stream stdio, int fd)
|
|
|
|
{
|
|
|
|
switch (stdio) {
|
|
|
|
case Stream_INPUT:
|
|
|
|
case Stream_OUTPUT:
|
|
|
|
case Stream_ERROR: {
|
|
|
|
auto& streamData = this->Stdio[stdio];
|
|
|
|
streamData.Type = External;
|
|
|
|
streamData.FileDescriptor = fd;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalStream(
|
|
|
|
Stream stdio, FILE* stream)
|
|
|
|
{
|
|
|
|
int fd = cm_fileno(stream);
|
|
|
|
if (fd >= 0) {
|
|
|
|
return this->SetExternalStream(stdio, fd);
|
|
|
|
}
|
|
|
|
return this->SetNoStream(stdio);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetMergedBuiltinStreams()
|
|
|
|
{
|
|
|
|
this->MergedBuiltinStreams = true;
|
|
|
|
return this->SetBuiltinStream(Stream_OUTPUT).SetBuiltinStream(Stream_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetWorkingDirectory(
|
|
|
|
std::string dir)
|
|
|
|
{
|
|
|
|
this->WorkingDirectory = std::move(dir);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
uv_loop_t* cmUVProcessChainBuilder::GetLoop() const
|
|
|
|
{
|
|
|
|
return this->Loop;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChain cmUVProcessChainBuilder::Start() const
|
|
|
|
{
|
|
|
|
cmUVProcessChain chain;
|
|
|
|
|
|
|
|
if (!chain.Data->Prepare(this)) {
|
|
|
|
return chain;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < this->Processes.size(); i++) {
|
|
|
|
chain.Data->SpawnProcess(i, this->Processes[i], i == 0,
|
|
|
|
i == this->Processes.size() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
chain.Data->Finish();
|
|
|
|
|
|
|
|
return chain;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmUVProcessChain::InternalData::Prepare(
|
|
|
|
const cmUVProcessChainBuilder* builder)
|
|
|
|
{
|
|
|
|
this->Builder = builder;
|
|
|
|
|
|
|
|
if (this->Builder->Loop) {
|
|
|
|
this->Loop = this->Builder->Loop;
|
|
|
|
} else {
|
|
|
|
this->BuiltinLoop.init();
|
|
|
|
this->Loop = this->BuiltinLoop;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const& input =
|
|
|
|
this->Builder->Stdio[cmUVProcessChainBuilder::Stream_INPUT];
|
|
|
|
auto& inputData = this->InputStreamData;
|
|
|
|
switch (input.Type) {
|
|
|
|
case cmUVProcessChainBuilder::None:
|
|
|
|
inputData.Stdio.flags = UV_IGNORE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case cmUVProcessChainBuilder::Builtin: {
|
|
|
|
// FIXME
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case cmUVProcessChainBuilder::External:
|
|
|
|
inputData.Stdio.flags = UV_INHERIT_FD;
|
|
|
|
inputData.Stdio.data.fd = input.FileDescriptor;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const& error =
|
|
|
|
this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR];
|
|
|
|
auto& errorData = this->ErrorStreamData;
|
|
|
|
switch (error.Type) {
|
|
|
|
case cmUVProcessChainBuilder::None:
|
|
|
|
errorData.Stdio.flags = UV_IGNORE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case cmUVProcessChainBuilder::Builtin: {
|
|
|
|
int pipeFd[2];
|
|
|
|
if (cmGetPipes(pipeFd) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
errorData.BuiltinStream = pipeFd[0];
|
|
|
|
errorData.Stdio.flags = UV_INHERIT_FD;
|
|
|
|
errorData.Stdio.data.fd = pipeFd[1];
|
|
|
|
|
|
|
|
if (this->TempErrorPipe.init(*this->Loop, 0) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (uv_pipe_open(this->TempErrorPipe, errorData.Stdio.data.fd) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case cmUVProcessChainBuilder::External:
|
|
|
|
errorData.Stdio.flags = UV_INHERIT_FD;
|
|
|
|
errorData.Stdio.data.fd = error.FileDescriptor;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const& output =
|
|
|
|
this->Builder->Stdio[cmUVProcessChainBuilder::Stream_OUTPUT];
|
|
|
|
auto& outputData = this->OutputStreamData;
|
|
|
|
switch (output.Type) {
|
|
|
|
case cmUVProcessChainBuilder::None:
|
|
|
|
outputData.Stdio.flags = UV_IGNORE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case cmUVProcessChainBuilder::Builtin:
|
|
|
|
if (this->Builder->MergedBuiltinStreams) {
|
|
|
|
outputData.BuiltinStream = errorData.BuiltinStream;
|
|
|
|
outputData.Stdio.flags = UV_INHERIT_FD;
|
|
|
|
outputData.Stdio.data.fd = errorData.Stdio.data.fd;
|
|
|
|
} else {
|
|
|
|
int pipeFd[2];
|
|
|
|
if (cmGetPipes(pipeFd) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
outputData.BuiltinStream = pipeFd[0];
|
|
|
|
outputData.Stdio.flags = UV_INHERIT_FD;
|
|
|
|
outputData.Stdio.data.fd = pipeFd[1];
|
|
|
|
|
|
|
|
if (this->TempOutputPipe.init(*this->Loop, 0) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (uv_pipe_open(this->TempOutputPipe, outputData.Stdio.data.fd) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case cmUVProcessChainBuilder::External:
|
|
|
|
outputData.Stdio.flags = UV_INHERIT_FD;
|
|
|
|
outputData.Stdio.data.fd = output.FileDescriptor;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool first = true;
|
|
|
|
for (std::size_t i = 0; i < this->Builder->Processes.size(); i++) {
|
|
|
|
this->Processes.emplace_back(cm::make_unique<ProcessData>());
|
|
|
|
auto& process = *this->Processes.back();
|
|
|
|
process.Data = this;
|
|
|
|
process.ProcessStatus.Finished = false;
|
|
|
|
|
|
|
|
if (!first) {
|
|
|
|
auto& prevProcess = *this->Processes[i - 1];
|
|
|
|
|
|
|
|
int pipeFd[2];
|
|
|
|
if (cmGetPipes(pipeFd) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prevProcess.OutputPipe.init(*this->Loop, 0) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (uv_pipe_open(prevProcess.OutputPipe, pipeFd[1]) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (process.InputPipe.init(*this->Loop, 0) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (uv_pipe_open(process.InputPipe, pipeFd[0]) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmUVProcessChain::InternalData::SpawnProcess(
|
|
|
|
std::size_t index,
|
|
|
|
const cmUVProcessChainBuilder::ProcessConfiguration& config, bool first,
|
|
|
|
bool last)
|
|
|
|
{
|
|
|
|
auto& process = *this->Processes[index];
|
|
|
|
|
|
|
|
auto options = uv_process_options_t();
|
|
|
|
|
|
|
|
// Bounds were checked at add time, first element is guaranteed to exist
|
|
|
|
options.file = config.Arguments[0].c_str();
|
|
|
|
|
|
|
|
std::vector<const char*> arguments;
|
|
|
|
arguments.reserve(config.Arguments.size());
|
|
|
|
for (auto const& arg : config.Arguments) {
|
|
|
|
arguments.push_back(arg.c_str());
|
|
|
|
}
|
|
|
|
arguments.push_back(nullptr);
|
|
|
|
options.args = const_cast<char**>(arguments.data());
|
|
|
|
options.flags = UV_PROCESS_WINDOWS_HIDE;
|
|
|
|
#if UV_VERSION_MAJOR > 1 || \
|
|
|
|
(UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR >= 48) || \
|
|
|
|
!defined(CMAKE_USE_SYSTEM_LIBUV)
|
|
|
|
options.flags |= UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME;
|
|
|
|
#endif
|
|
|
|
if (!this->Builder->WorkingDirectory.empty()) {
|
|
|
|
options.cwd = this->Builder->WorkingDirectory.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::array<uv_stdio_container_t, 3> stdio;
|
|
|
|
if (first) {
|
|
|
|
stdio[0] = this->InputStreamData.Stdio;
|
|
|
|
} else {
|
|
|
|
stdio[0] = uv_stdio_container_t();
|
|
|
|
stdio[0].flags = UV_INHERIT_STREAM;
|
|
|
|
stdio[0].data.stream = process.InputPipe;
|
|
|
|
}
|
|
|
|
if (last) {
|
|
|
|
stdio[1] = this->OutputStreamData.Stdio;
|
|
|
|
} else {
|
|
|
|
stdio[1] = uv_stdio_container_t();
|
|
|
|
stdio[1].flags = UV_INHERIT_STREAM;
|
|
|
|
stdio[1].data.stream = process.OutputPipe;
|
|
|
|
}
|
|
|
|
stdio[2] = this->ErrorStreamData.Stdio;
|
|
|
|
|
|
|
|
options.stdio = stdio.data();
|
|
|
|
options.stdio_count = 3;
|
|
|
|
options.exit_cb = [](uv_process_t* handle, int64_t exitStatus,
|
|
|
|
int termSignal) {
|
|
|
|
auto* processData = static_cast<ProcessData*>(handle->data);
|
|
|
|
processData->ProcessStatus.ExitStatus = exitStatus;
|
|
|
|
processData->ProcessStatus.TermSignal = termSignal;
|
|
|
|
processData->Finish();
|
|
|
|
};
|
|
|
|
|
|
|
|
if ((process.ProcessStatus.SpawnResult =
|
|
|
|
process.Process.spawn(*this->Loop, options, &process)) < 0) {
|
|
|
|
process.Finish();
|
|
|
|
}
|
|
|
|
process.InputPipe.reset();
|
|
|
|
process.OutputPipe.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmUVProcessChain::InternalData::Finish()
|
|
|
|
{
|
|
|
|
this->TempOutputPipe.reset();
|
|
|
|
this->TempErrorPipe.reset();
|
|
|
|
this->Valid = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChain::cmUVProcessChain()
|
|
|
|
: Data(cm::make_unique<InternalData>())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChain::cmUVProcessChain(cmUVProcessChain&& other) noexcept
|
|
|
|
: Data(std::move(other.Data))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
cmUVProcessChain::~cmUVProcessChain() = default;
|
|
|
|
|
|
|
|
cmUVProcessChain& cmUVProcessChain::operator=(
|
|
|
|
cmUVProcessChain&& other) noexcept
|
|
|
|
{
|
|
|
|
this->Data = std::move(other.Data);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
uv_loop_t& cmUVProcessChain::GetLoop()
|
|
|
|
{
|
|
|
|
return *this->Data->Loop;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cmUVProcessChain::OutputStream()
|
|
|
|
{
|
|
|
|
return this->Data->OutputStreamData.BuiltinStream;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cmUVProcessChain::ErrorStream()
|
|
|
|
{
|
|
|
|
return this->Data->ErrorStreamData.BuiltinStream;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmUVProcessChain::Valid() const
|
|
|
|
{
|
|
|
|
return this->Data->Valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmUVProcessChain::Wait(uint64_t milliseconds)
|
|
|
|
{
|
|
|
|
bool timeout = false;
|
|
|
|
cm::uv_timer_ptr timer;
|
|
|
|
|
|
|
|
if (milliseconds > 0) {
|
|
|
|
timer.init(*this->Data->Loop, &timeout);
|
|
|
|
timer.start(
|
|
|
|
[](uv_timer_t* handle) {
|
|
|
|
auto* timeoutPtr = static_cast<bool*>(handle->data);
|
|
|
|
*timeoutPtr = true;
|
|
|
|
},
|
|
|
|
milliseconds, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!timeout &&
|
|
|
|
this->Data->ProcessesCompleted < this->Data->Processes.size()) {
|
|
|
|
uv_run(this->Data->Loop, UV_RUN_ONCE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return !timeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<const cmUVProcessChain::Status*> cmUVProcessChain::GetStatus()
|
|
|
|
const
|
|
|
|
{
|
|
|
|
std::vector<const cmUVProcessChain::Status*> statuses(
|
|
|
|
this->Data->Processes.size(), nullptr);
|
|
|
|
for (std::size_t i = 0; i < statuses.size(); i++) {
|
|
|
|
statuses[i] = &this->GetStatus(i);
|
|
|
|
}
|
|
|
|
return statuses;
|
|
|
|
}
|
|
|
|
|
|
|
|
const cmUVProcessChain::Status& cmUVProcessChain::GetStatus(
|
|
|
|
std::size_t index) const
|
|
|
|
{
|
|
|
|
return this->Data->Processes[index]->ProcessStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmUVProcessChain::Finished() const
|
|
|
|
{
|
|
|
|
return this->Data->ProcessesCompleted >= this->Data->Processes.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<cmUVProcessChain::ExceptionCode, std::string>
|
|
|
|
cmUVProcessChain::Status::GetException() const
|
|
|
|
{
|
|
|
|
if (this->SpawnResult) {
|
|
|
|
return std::make_pair(ExceptionCode::Spawn,
|
|
|
|
uv_strerror(this->SpawnResult));
|
|
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (this->Finished && (this->ExitStatus & 0xF0000000) == 0xC0000000) {
|
|
|
|
// Child terminated due to exceptional behavior.
|
|
|
|
switch (this->ExitStatus) {
|
|
|
|
case STATUS_CONTROL_C_EXIT:
|
|
|
|
return std::make_pair(ExceptionCode::Interrupt, "User interrupt");
|
|
|
|
|
|
|
|
case STATUS_FLOAT_DENORMAL_OPERAND:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical,
|
|
|
|
"Floating-point exception (denormal operand)");
|
|
|
|
case STATUS_FLOAT_DIVIDE_BY_ZERO:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical, "Divide-by-zero");
|
|
|
|
case STATUS_FLOAT_INEXACT_RESULT:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical,
|
|
|
|
"Floating-point exception (inexact result)");
|
|
|
|
case STATUS_FLOAT_INVALID_OPERATION:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical,
|
|
|
|
"Invalid floating-point operation");
|
|
|
|
case STATUS_FLOAT_OVERFLOW:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical,
|
|
|
|
"Floating-point overflow");
|
|
|
|
case STATUS_FLOAT_STACK_CHECK:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical,
|
|
|
|
"Floating-point stack check failed");
|
|
|
|
case STATUS_FLOAT_UNDERFLOW:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical,
|
|
|
|
"Floating-point underflow");
|
|
|
|
# ifdef STATUS_FLOAT_MULTIPLE_FAULTS
|
|
|
|
case STATUS_FLOAT_MULTIPLE_FAULTS:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical,
|
|
|
|
"Floating-point exception (multiple faults)");
|
|
|
|
# endif
|
|
|
|
# ifdef STATUS_FLOAT_MULTIPLE_TRAPS
|
|
|
|
case STATUS_FLOAT_MULTIPLE_TRAPS:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical,
|
|
|
|
"Floating-point exception (multiple traps)");
|
|
|
|
# endif
|
|
|
|
case STATUS_INTEGER_DIVIDE_BY_ZERO:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical,
|
|
|
|
"Integer divide-by-zero");
|
|
|
|
case STATUS_INTEGER_OVERFLOW:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical, "Integer overflow");
|
|
|
|
|
|
|
|
case STATUS_DATATYPE_MISALIGNMENT:
|
|
|
|
return std::make_pair(ExceptionCode::Fault, "Datatype misalignment");
|
|
|
|
case STATUS_ACCESS_VIOLATION:
|
|
|
|
return std::make_pair(ExceptionCode::Fault, "Access violation");
|
|
|
|
case STATUS_IN_PAGE_ERROR:
|
|
|
|
return std::make_pair(ExceptionCode::Fault, "In-page error");
|
|
|
|
case STATUS_INVALID_HANDLE:
|
|
|
|
return std::make_pair(ExceptionCode::Fault, "Invalid handle");
|
|
|
|
case STATUS_NONCONTINUABLE_EXCEPTION:
|
|
|
|
return std::make_pair(ExceptionCode::Fault,
|
|
|
|
"Noncontinuable exception");
|
|
|
|
case STATUS_INVALID_DISPOSITION:
|
|
|
|
return std::make_pair(ExceptionCode::Fault, "Invalid disposition");
|
|
|
|
case STATUS_ARRAY_BOUNDS_EXCEEDED:
|
|
|
|
return std::make_pair(ExceptionCode::Fault, "Array bounds exceeded");
|
|
|
|
case STATUS_STACK_OVERFLOW:
|
|
|
|
return std::make_pair(ExceptionCode::Fault, "Stack overflow");
|
|
|
|
|
|
|
|
case STATUS_ILLEGAL_INSTRUCTION:
|
|
|
|
return std::make_pair(ExceptionCode::Illegal, "Illegal instruction");
|
|
|
|
case STATUS_PRIVILEGED_INSTRUCTION:
|
|
|
|
return std::make_pair(ExceptionCode::Illegal,
|
|
|
|
"Privileged instruction");
|
|
|
|
|
|
|
|
case STATUS_NO_MEMORY:
|
|
|
|
default: {
|
|
|
|
char buf[256];
|
|
|
|
snprintf(buf, sizeof(buf), "Exit code 0x%x\n",
|
|
|
|
static_cast<unsigned int>(this->ExitStatus));
|
|
|
|
return std::make_pair(ExceptionCode::Other, buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (this->Finished && this->TermSignal) {
|
|
|
|
switch (this->TermSignal) {
|
|
|
|
# ifdef SIGSEGV
|
|
|
|
case SIGSEGV:
|
|
|
|
return std::make_pair(ExceptionCode::Fault, "Segmentation fault");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGBUS
|
|
|
|
# if !defined(SIGSEGV) || SIGBUS != SIGSEGV
|
|
|
|
case SIGBUS:
|
|
|
|
return std::make_pair(ExceptionCode::Fault, "Bus error");
|
|
|
|
# endif
|
|
|
|
# endif
|
|
|
|
# ifdef SIGFPE
|
|
|
|
case SIGFPE:
|
|
|
|
return std::make_pair(ExceptionCode::Numerical,
|
|
|
|
"Floating-point exception");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGILL
|
|
|
|
case SIGILL:
|
|
|
|
return std::make_pair(ExceptionCode::Illegal, "Illegal instruction");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGINT
|
|
|
|
case SIGINT:
|
|
|
|
return std::make_pair(ExceptionCode::Interrupt, "User interrupt");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGABRT
|
|
|
|
case SIGABRT:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "Subprocess aborted");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGKILL
|
|
|
|
case SIGKILL:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "Subprocess killed");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGTERM
|
|
|
|
case SIGTERM:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "Subprocess terminated");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGHUP
|
|
|
|
case SIGHUP:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGHUP");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGQUIT
|
|
|
|
case SIGQUIT:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGQUIT");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGTRAP
|
|
|
|
case SIGTRAP:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGTRAP");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGIOT
|
|
|
|
# if !defined(SIGABRT) || SIGIOT != SIGABRT
|
|
|
|
case SIGIOT:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGIOT");
|
|
|
|
# endif
|
|
|
|
# endif
|
|
|
|
# ifdef SIGUSR1
|
|
|
|
case SIGUSR1:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGUSR1");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGUSR2
|
|
|
|
case SIGUSR2:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGUSR2");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGPIPE
|
|
|
|
case SIGPIPE:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGPIPE");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGALRM
|
|
|
|
case SIGALRM:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGALRM");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGSTKFLT
|
|
|
|
case SIGSTKFLT:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGSTKFLT");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGCHLD
|
|
|
|
case SIGCHLD:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGCHLD");
|
|
|
|
# elif defined(SIGCLD)
|
|
|
|
case SIGCLD:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGCLD");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGCONT
|
|
|
|
case SIGCONT:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGCONT");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGSTOP
|
|
|
|
case SIGSTOP:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGSTOP");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGTSTP
|
|
|
|
case SIGTSTP:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGTSTP");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGTTIN
|
|
|
|
case SIGTTIN:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGTTIN");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGTTOU
|
|
|
|
case SIGTTOU:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGTTOU");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGURG
|
|
|
|
case SIGURG:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGURG");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGXCPU
|
|
|
|
case SIGXCPU:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGXCPU");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGXFSZ
|
|
|
|
case SIGXFSZ:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGXFSZ");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGVTALRM
|
|
|
|
case SIGVTALRM:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGVTALRM");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGPROF
|
|
|
|
case SIGPROF:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGPROF");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGWINCH
|
|
|
|
case SIGWINCH:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGWINCH");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGPOLL
|
|
|
|
case SIGPOLL:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGPOLL");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGIO
|
|
|
|
# if !defined(SIGPOLL) || SIGIO != SIGPOLL
|
|
|
|
case SIGIO:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGIO");
|
|
|
|
# endif
|
|
|
|
# endif
|
|
|
|
# ifdef SIGPWR
|
|
|
|
case SIGPWR:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGPWR");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGSYS
|
|
|
|
case SIGSYS:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGSYS");
|
|
|
|
# endif
|
|
|
|
# ifdef SIGUNUSED
|
|
|
|
# if !defined(SIGSYS) || SIGUNUSED != SIGSYS
|
|
|
|
case SIGUNUSED:
|
|
|
|
return std::make_pair(ExceptionCode::Other, "SIGUNUSED");
|
|
|
|
# endif
|
|
|
|
# endif
|
|
|
|
default: {
|
|
|
|
char buf[256];
|
|
|
|
snprintf(buf, sizeof(buf), "Signal %d", this->TermSignal);
|
|
|
|
return std::make_pair(ExceptionCode::Other, buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return std::make_pair(ExceptionCode::None, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
void cmUVProcessChain::InternalData::ProcessData::Finish()
|
|
|
|
{
|
|
|
|
this->ProcessStatus.Finished = true;
|
|
|
|
this->Data->ProcessesCompleted++;
|
|
|
|
}
|