mirror of
https://github.com/Kitware/CMake.git
synced 2026-05-01 03:29:18 -05:00
cmUVProcessChain: Add Status::GetException() method
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmConfigure.h"
|
||||
|
||||
#include "cmUVProcessChain.h"
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <istream> // IWYU pragma: keep
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/memory>
|
||||
@@ -425,3 +431,245 @@ bool cmUVProcessChain::Finished() const
|
||||
{
|
||||
return this->Data->ProcessesCompleted >= this->Data->Processes.size();
|
||||
}
|
||||
|
||||
std::pair<cmUVProcessChain::ExceptionCode, std::string>
|
||||
cmUVProcessChain::Status::GetException() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if ((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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(ExceptionCode::None, "");
|
||||
#else
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(ExceptionCode::None, "");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cm3p/uv.h>
|
||||
@@ -66,10 +67,22 @@ private:
|
||||
class cmUVProcessChain
|
||||
{
|
||||
public:
|
||||
enum class ExceptionCode
|
||||
{
|
||||
None,
|
||||
Fault,
|
||||
Illegal,
|
||||
Interrupt,
|
||||
Numerical,
|
||||
Other,
|
||||
};
|
||||
|
||||
struct Status
|
||||
{
|
||||
int64_t ExitStatus;
|
||||
int TermSignal;
|
||||
|
||||
std::pair<ExceptionCode, std::string> GetException() const;
|
||||
};
|
||||
|
||||
cmUVProcessChain(const cmUVProcessChain& other) = delete;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cm/memory>
|
||||
@@ -22,30 +24,62 @@ struct ExpectedStatus
|
||||
bool MatchExitStatus;
|
||||
bool MatchTermSignal;
|
||||
cmUVProcessChain::Status Status;
|
||||
cmUVProcessChain::ExceptionCode ExceptionCode;
|
||||
std::string ExceptionString;
|
||||
};
|
||||
|
||||
static const std::vector<ExpectedStatus> status1 = {
|
||||
{ false, false, false, { 0, 0 } },
|
||||
{ false, false, false, { 0, 0 } },
|
||||
{ false, false, false, { 0, 0 } },
|
||||
{ false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
|
||||
{ false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
|
||||
{ false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
|
||||
};
|
||||
|
||||
static const std::vector<ExpectedStatus> status2 = {
|
||||
{ true, true, true, { 0, 0 } },
|
||||
{ false, false, false, { 0, 0 } },
|
||||
{ false, false, false, { 0, 0 } },
|
||||
{ true, true, true, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
|
||||
{ false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
|
||||
{ false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
|
||||
};
|
||||
|
||||
static const std::vector<ExpectedStatus> status3 = {
|
||||
{ true, true, true, { 0, 0 } },
|
||||
{ true, true, true, { 1, 0 } },
|
||||
{ true, true, true, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
|
||||
{ true, true, true, { 1, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
|
||||
#ifdef _WIN32
|
||||
{ true, true, true, { 2, 0 } },
|
||||
{ true,
|
||||
true,
|
||||
true,
|
||||
{ STATUS_ACCESS_VIOLATION, 0 },
|
||||
cmUVProcessChain::ExceptionCode::Fault,
|
||||
"Access violation" },
|
||||
#else
|
||||
{ true, false, true, { 0, SIGABRT } },
|
||||
{ true,
|
||||
false,
|
||||
true,
|
||||
{ 0, SIGABRT },
|
||||
cmUVProcessChain::ExceptionCode::Other,
|
||||
"Subprocess aborted" },
|
||||
#endif
|
||||
};
|
||||
|
||||
static const char* ExceptionCodeToString(cmUVProcessChain::ExceptionCode code)
|
||||
{
|
||||
switch (code) {
|
||||
case cmUVProcessChain::ExceptionCode::None:
|
||||
return "None";
|
||||
case cmUVProcessChain::ExceptionCode::Fault:
|
||||
return "Fault";
|
||||
case cmUVProcessChain::ExceptionCode::Illegal:
|
||||
return "Illegal";
|
||||
case cmUVProcessChain::ExceptionCode::Interrupt:
|
||||
return "Interrupt";
|
||||
case cmUVProcessChain::ExceptionCode::Numerical:
|
||||
return "Numerical";
|
||||
case cmUVProcessChain::ExceptionCode::Other:
|
||||
return "Other";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const cmUVProcessChain::Status* actual,
|
||||
const ExpectedStatus& expected)
|
||||
{
|
||||
@@ -62,6 +96,11 @@ bool operator==(const cmUVProcessChain::Status* actual,
|
||||
expected.Status.TermSignal != actual->TermSignal) {
|
||||
return false;
|
||||
}
|
||||
if (expected.Finished &&
|
||||
std::make_pair(expected.ExceptionCode, expected.ExceptionString) !=
|
||||
actual->GetException()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -116,6 +155,11 @@ static void printResults(
|
||||
<< printExpected(e.MatchExitStatus, e.Status.ExitStatus)
|
||||
<< ", TermSignal: "
|
||||
<< printExpected(e.MatchTermSignal, e.Status.TermSignal)
|
||||
<< ", ExceptionCode: "
|
||||
<< printExpected(e.Finished,
|
||||
ExceptionCodeToString(e.ExceptionCode))
|
||||
<< ", ExceptionString: \""
|
||||
<< printExpected(e.Finished, e.ExceptionString) << '"'
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << " null" << std::endl;
|
||||
@@ -124,8 +168,12 @@ static void printResults(
|
||||
std::cout << "Actual:" << std::endl;
|
||||
for (auto const& a : actual) {
|
||||
if (a) {
|
||||
auto exception = a->GetException();
|
||||
std::cout << " ExitStatus: " << a->ExitStatus
|
||||
<< ", TermSignal: " << a->TermSignal << std::endl;
|
||||
<< ", TermSignal: " << a->TermSignal << ", ExceptionCode: "
|
||||
<< ExceptionCodeToString(exception.first)
|
||||
<< ", ExceptionString: \"" << exception.second << '"'
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << " null" << std::endl;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "cmSystemTools.h"
|
||||
|
||||
static std::string getStdin()
|
||||
@@ -61,10 +65,9 @@ int main(int argc, char** argv)
|
||||
}
|
||||
|
||||
// On Windows, the exit code of abort() is different between debug and
|
||||
// release builds, and does not yield a term_signal in libuv in either
|
||||
// case. For the sake of simplicity, we just return another non-zero code.
|
||||
// release builds. Instead, simulate an access violation.
|
||||
#ifdef _WIN32
|
||||
return 2;
|
||||
return STATUS_ACCESS_VIOLATION;
|
||||
#else
|
||||
std::abort();
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user