diff options
author | Kyle Edwards <kyle.edwards@kitware.com> | 2023-06-01 15:52:40 (GMT) |
---|---|---|
committer | Kyle Edwards <kyle.edwards@kitware.com> | 2023-06-05 15:27:45 (GMT) |
commit | 154fe00ca56710d6fced4ab5c51f7edb162ea991 (patch) | |
tree | d08d2ce6d426343d2599d0124e94325a955300ea | |
parent | 17a43ee192116f82b9dae494333f5e14b4553f87 (diff) | |
download | CMake-154fe00ca56710d6fced4ab5c51f7edb162ea991.zip CMake-154fe00ca56710d6fced4ab5c51f7edb162ea991.tar.gz CMake-154fe00ca56710d6fced4ab5c51f7edb162ea991.tar.bz2 |
cmUVProcessChain: Add Status::GetException() method
-rw-r--r-- | Source/cmUVProcessChain.cxx | 248 | ||||
-rw-r--r-- | Source/cmUVProcessChain.h | 13 | ||||
-rw-r--r-- | Tests/CMakeLib/testUVProcessChain.cxx | 70 | ||||
-rw-r--r-- | Tests/CMakeLib/testUVProcessChainHelper.cxx | 9 |
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 |