cmUVProcessChain: Add Status::GetException() method

This commit is contained in:
Kyle Edwards
2023-06-01 11:52:40 -04:00
parent 17a43ee192
commit 154fe00ca5
4 changed files with 326 additions and 14 deletions
+248
View File
@@ -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
}
+13
View File
@@ -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;
+59 -11
View File
@@ -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;
}
+6 -3
View File
@@ -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