summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKyle Edwards <kyle.edwards@kitware.com>2023-06-01 15:52:40 (GMT)
committerKyle Edwards <kyle.edwards@kitware.com>2023-06-05 15:27:45 (GMT)
commit154fe00ca56710d6fced4ab5c51f7edb162ea991 (patch)
treed08d2ce6d426343d2599d0124e94325a955300ea
parent17a43ee192116f82b9dae494333f5e14b4553f87 (diff)
downloadCMake-154fe00ca56710d6fced4ab5c51f7edb162ea991.zip
CMake-154fe00ca56710d6fced4ab5c51f7edb162ea991.tar.gz
CMake-154fe00ca56710d6fced4ab5c51f7edb162ea991.tar.bz2
cmUVProcessChain: Add Status::GetException() method
-rw-r--r--Source/cmUVProcessChain.cxx248
-rw-r--r--Source/cmUVProcessChain.h13
-rw-r--r--Tests/CMakeLib/testUVProcessChain.cxx70
-rw-r--r--Tests/CMakeLib/testUVProcessChainHelper.cxx9
4 files changed, 326 insertions, 14 deletions
diff --git a/Source/cmUVProcessChain.cxx b/Source/cmUVProcessChain.cxx
index c2bb11e..ed5f38b 100644
--- a/Source/cmUVProcessChain.cxx
+++ b/Source/cmUVProcessChain.cxx
@@ -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
+}
diff --git a/Source/cmUVProcessChain.h b/Source/cmUVProcessChain.h
index 9e4558e..f92742f 100644
--- a/Source/cmUVProcessChain.h
+++ b/Source/cmUVProcessChain.h
@@ -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;
diff --git a/Tests/CMakeLib/testUVProcessChain.cxx b/Tests/CMakeLib/testUVProcessChain.cxx
index ce6cd6d..7027689 100644
--- a/Tests/CMakeLib/testUVProcessChain.cxx
+++ b/Tests/CMakeLib/testUVProcessChain.cxx
@@ -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;
}
diff --git a/Tests/CMakeLib/testUVProcessChainHelper.cxx b/Tests/CMakeLib/testUVProcessChainHelper.cxx
index 82dafd2..99743e7 100644
--- a/Tests/CMakeLib/testUVProcessChainHelper.cxx
+++ b/Tests/CMakeLib/testUVProcessChainHelper.cxx
@@ -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