| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmProcess.h" | |
| #include <csignal> | |
| #include <ratio> | |
| #include <string> | |
| #include <utility> | |
| #include <cmext/algorithm> | |
| #include "cmsys/Process.h" | |
| #include "cmCTest.h" | |
| #include "cmCTestRunTest.h" | |
| #include "cmCTestTestHandler.h" | |
| #include "cmGetPipes.h" | |
| #include "cmStringAlgorithms.h" | |
| #if defined(_WIN32) | |
| # include <cm3p/kwiml/int.h> | |
| #endif | |
| #define CM_PROCESS_BUF_SIZE 65536 | |
| cmProcess::cmProcess(std::unique_ptr<cmCTestRunTest> runner) | |
| : Runner(std::move(runner)) | |
| , Conv(cmProcessOutput::UTF8, CM_PROCESS_BUF_SIZE) | |
| { | |
| this->TotalTime = cmDuration::zero(); | |
| this->ExitValue = 0; | |
| this->Id = 0; | |
| this->StartTime = std::chrono::steady_clock::time_point(); | |
| } | |
| cmProcess::~cmProcess() = default; | |
| void cmProcess::SetCommand(std::string command) | |
| { | |
| this->Command = std::move(command); | |
| } | |
| void cmProcess::SetCommandArguments(std::vector<std::string> args) | |
| { | |
| this->Arguments = std::move(args); | |
| } | |
| void cmProcess::SetEnvironment(std::vector<std::string> env) | |
| { | |
| this->Environment = std::move(env); | |
| } | |
| void cmProcess::SetWorkingDirectory(std::string dir) | |
| { | |
| this->WorkingDirectory = std::move(dir); | |
| } | |
| bool cmProcess::StartProcess(uv_loop_t& loop, std::vector<size_t>* affinity) | |
| { | |
| this->ProcessState = cmProcess::State::Error; | |
| if (this->Command.empty()) { | |
| return false; | |
| } | |
| this->StartTime = std::chrono::steady_clock::now(); | |
| this->SystemStartTime = std::chrono::system_clock::now(); | |
| this->ProcessArgs.clear(); | |
| // put the command as arg0 | |
| this->ProcessArgs.push_back(this->Command.c_str()); | |
| // now put the command arguments in | |
| for (std::string const& arg : this->Arguments) { | |
| this->ProcessArgs.push_back(arg.c_str()); | |
| } | |
| this->ProcessArgs.push_back(nullptr); // null terminate the list | |
| cm::uv_timer_ptr timer; | |
| int status = timer.init(loop, this); | |
| if (status != 0) { | |
| cmCTestLog(this->Runner->GetCTest(), ERROR_MESSAGE, | |
| "Error initializing timer: ", uv_strerror(status), '\n'); | |
| return false; | |
| } | |
| cm::uv_pipe_ptr pipe_writer; | |
| cm::uv_pipe_ptr pipe_reader; | |
| pipe_writer.init(loop, 0); | |
| pipe_reader.init(loop, 0, this); | |
| int fds[2] = { -1, -1 }; | |
| status = cmGetPipes(fds); | |
| if (status != 0) { | |
| cmCTestLog(this->Runner->GetCTest(), ERROR_MESSAGE, | |
| "Error initializing pipe: ", uv_strerror(status), '\n'); | |
| return false; | |
| } | |
| uv_pipe_open(pipe_reader, fds[0]); | |
| uv_pipe_open(pipe_writer, fds[1]); | |
| uv_stdio_container_t stdio[3]; | |
| stdio[0].flags = UV_INHERIT_FD; | |
| stdio[0].data.fd = 0; | |
| stdio[1].flags = UV_INHERIT_STREAM; | |
| stdio[1].data.stream = pipe_writer; | |
| stdio[2] = stdio[1]; | |
| uv_process_options_t options = uv_process_options_t(); | |
| options.file = this->Command.data(); | |
| options.args = const_cast<char**>(this->ProcessArgs.data()); | |
| options.stdio_count = 3; // in, out and err | |
| options.exit_cb = &cmProcess::OnExitCB; | |
| options.stdio = stdio; | |
| #if UV_VERSION_MAJOR > 1 || !defined(CMAKE_USE_SYSTEM_LIBUV) | |
| if (!this->Runner->GetCTest()->GetInteractiveDebugMode()) { | |
| options.flags = UV_PROCESS_WINDOWS_USE_PARENT_ERROR_MODE; | |
| } | |
| #endif | |
| #if !defined(CMAKE_USE_SYSTEM_LIBUV) | |
| std::vector<char> cpumask; | |
| if (affinity && !affinity->empty()) { | |
| cpumask.resize(static_cast<size_t>(uv_cpumask_size()), 0); | |
| for (auto p : *affinity) { | |
| cpumask[p] = 1; | |
| } | |
| options.cpumask = cpumask.data(); | |
| options.cpumask_size = cpumask.size(); | |
| } else { | |
| options.cpumask = nullptr; | |
| options.cpumask_size = 0; | |
| } | |
| #else | |
| static_cast<void>(affinity); | |
| #endif | |
| if (!this->Environment.empty()) { | |
| this->Env.clear(); | |
| this->Env.reserve(this->Environment.size() + 1); | |
| for (auto const& var : this->Environment) { | |
| this->Env.push_back(var.c_str()); | |
| } | |
| this->Env.push_back(nullptr); | |
| options.env = const_cast<char**>(this->Env.data()); | |
| } | |
| status = | |
| uv_read_start(pipe_reader, &cmProcess::OnAllocateCB, &cmProcess::OnReadCB); | |
| if (status != 0) { | |
| cmCTestLog(this->Runner->GetCTest(), ERROR_MESSAGE, | |
| "Error starting read events: ", uv_strerror(status), '\n'); | |
| return false; | |
| } | |
| status = this->Process.spawn(loop, options, this); | |
| if (status != 0) { | |
| cmCTestLog(this->Runner->GetCTest(), ERROR_MESSAGE, | |
| "Process not started\n ", this->Command, "\n[", | |
| uv_strerror(status), "]\n"); | |
| return false; | |
| } | |
| this->PipeReader = std::move(pipe_reader); | |
| this->Timer = std::move(timer); | |
| this->StartTimer(); | |
| this->ProcessState = cmProcess::State::Executing; | |
| return true; | |
| } | |
| void cmProcess::StartTimer() | |
| { | |
| if (auto ctimeout = this->GetComputedTimeout()) { | |
| this->TimeoutReason_ = ctimeout->Reason; | |
| auto msec = std::chrono::duration_cast<std::chrono::milliseconds>( | |
| ctimeout->Duration); | |
| this->Timer.start(&cmProcess::OnTimeoutCB, | |
| static_cast<uint64_t>(msec.count()), 0, | |
| cm::uv_update_time::no); | |
| } | |
| } | |
| bool cmProcess::Buffer::GetLine(std::string& line) | |
| { | |
| // Scan for the next newline. | |
| for (size_type sz = this->size(); this->Last != sz; ++this->Last) { | |
| if ((*this)[this->Last] == '\n' || (*this)[this->Last] == '\0') { | |
| // Extract the range first..last as a line. | |
| char const* text = this->data() + this->First; | |
| size_type length = this->Last - this->First; | |
| while (length && text[length - 1] == '\r') { | |
| length--; | |
| } | |
| line.assign(text, length); | |
| // Start a new range for the next line. | |
| ++this->Last; | |
| this->First = this->Last; | |
| // Return the line extracted. | |
| return true; | |
| } | |
| } | |
| // Available data have been exhausted without a newline. | |
| if (this->First != 0) { | |
| // Move the partial line to the beginning of the buffer. | |
| this->erase(this->begin(), this->begin() + this->First); | |
| this->First = 0; | |
| this->Last = this->size(); | |
| } | |
| return false; | |
| } | |
| bool cmProcess::Buffer::GetLast(std::string& line) | |
| { | |
| // Return the partial last line, if any. | |
| if (!this->empty()) { | |
| line.assign(this->data(), this->size()); | |
| this->First = this->Last = 0; | |
| this->clear(); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void cmProcess::OnReadCB(uv_stream_t* stream, ssize_t nread, | |
| uv_buf_t const* buf) | |
| { | |
| auto* self = static_cast<cmProcess*>(stream->data); | |
| self->OnRead(nread, buf); | |
| } | |
| void cmProcess::OnRead(ssize_t nread, uv_buf_t const* buf) | |
| { | |
| std::string line; | |
| if (nread > 0) { | |
| std::string strdata; | |
| this->Conv.DecodeText(buf->base, static_cast<size_t>(nread), strdata); | |
| cm::append(this->Output, strdata); | |
| while (this->Output.GetLine(line)) { | |
| this->Runner->CheckOutput(line); | |
| line.clear(); | |
| } | |
| return; | |
| } | |
| if (nread == 0) { | |
| return; | |
| } | |
| // The process will provide no more data. | |
| if (nread != UV_EOF) { | |
| auto error = static_cast<int>(nread); | |
| cmCTestLog(this->Runner->GetCTest(), ERROR_MESSAGE, | |
| "Error reading stream: ", uv_strerror(error), '\n'); | |
| } | |
| // Look for partial last lines. | |
| if (this->Output.GetLast(line)) { | |
| this->Runner->CheckOutput(line); | |
| } | |
| this->ReadHandleClosed = true; | |
| this->PipeReader.reset(); | |
| if (this->ProcessHandleClosed) { | |
| uv_timer_stop(this->Timer); | |
| this->Finish(); | |
| } | |
| } | |
| void cmProcess::OnAllocateCB(uv_handle_t* handle, size_t suggested_size, | |
| uv_buf_t* buf) | |
| { | |
| auto* self = static_cast<cmProcess*>(handle->data); | |
| self->OnAllocate(suggested_size, buf); | |
| } | |
| void cmProcess::OnAllocate(size_t /*suggested_size*/, uv_buf_t* buf) | |
| { | |
| if (this->Buf.size() != CM_PROCESS_BUF_SIZE) { | |
| this->Buf.resize(CM_PROCESS_BUF_SIZE); | |
| } | |
| *buf = | |
| uv_buf_init(this->Buf.data(), static_cast<unsigned int>(this->Buf.size())); | |
| } | |
| void cmProcess::OnTimeoutCB(uv_timer_t* timer) | |
| { | |
| auto* self = static_cast<cmProcess*>(timer->data); | |
| self->OnTimeout(); | |
| } | |
| void cmProcess::OnTimeout() | |
| { | |
| bool const wasExecuting = this->ProcessState == cmProcess::State::Executing; | |
| this->ProcessState = cmProcess::State::Expired; | |
| // If the test process is still executing normally, and we timed out because | |
| // the test timeout was reached, send the custom timeout signal, if any. | |
| if (wasExecuting && this->TimeoutReason_ == TimeoutReason::Normal) { | |
| cmCTestTestHandler::cmCTestTestProperties* p = | |
| this->Runner->GetTestProperties(); | |
| if (p->TimeoutSignal) { | |
| this->TerminationStyle = Termination::Custom; | |
| uv_process_kill(this->Process, p->TimeoutSignal->Number); | |
| if (p->TimeoutGracePeriod) { | |
| this->Timeout = *p->TimeoutGracePeriod; | |
| } else { | |
| static cmDuration const defaultGracePeriod{ 1.0 }; | |
| this->Timeout = defaultGracePeriod; | |
| } | |
| this->StartTimer(); | |
| return; | |
| } | |
| } | |
| this->TerminationStyle = Termination::Forced; | |
| bool const was_still_reading = !this->ReadHandleClosed; | |
| if (!this->ReadHandleClosed) { | |
| this->ReadHandleClosed = true; | |
| this->PipeReader.reset(); | |
| } | |
| if (!this->ProcessHandleClosed) { | |
| // Kill the child and let our on-exit handler finish the test. | |
| cmsysProcess_KillPID(static_cast<unsigned long>(this->Process->pid)); | |
| } else if (was_still_reading) { | |
| // Our on-exit handler already ran but did not finish the test | |
| // because we were still reading output. We've just dropped | |
| // our read handler, so we need to finish the test now. | |
| this->Finish(); | |
| } | |
| } | |
| void cmProcess::OnExitCB(uv_process_t* process, int64_t exit_status, | |
| int term_signal) | |
| { | |
| auto* self = static_cast<cmProcess*>(process->data); | |
| self->OnExit(exit_status, term_signal); | |
| } | |
| void cmProcess::OnExit(int64_t exit_status, int term_signal) | |
| { | |
| if (this->ProcessState != cmProcess::State::Expired) { | |
| if ( | |
| #if defined(_WIN32) | |
| ((DWORD)exit_status & 0xF0000000) == 0xC0000000 | |
| #else | |
| term_signal != 0 | |
| #endif | |
| ) { | |
| this->ProcessState = cmProcess::State::Exception; | |
| } else { | |
| this->ProcessState = cmProcess::State::Exited; | |
| } | |
| } | |
| // Record exit information. | |
| this->ExitValue = exit_status; | |
| this->Signal = term_signal; | |
| this->ProcessHandleClosed = true; | |
| if (this->ReadHandleClosed) { | |
| uv_timer_stop(this->Timer); | |
| this->Finish(); | |
| } | |
| } | |
| void cmProcess::Finish() | |
| { | |
| this->TotalTime = std::chrono::steady_clock::now() - this->StartTime; | |
| // Because of a processor clock skew the runtime may become slightly | |
| // negative. If someone changed the system clock while the process was | |
| // running this may be even more. Make sure not to report a negative | |
| // duration here. | |
| if (this->TotalTime <= cmDuration::zero()) { | |
| this->TotalTime = cmDuration::zero(); | |
| } | |
| this->Runner->FinalizeTest(); | |
| } | |
| cmProcess::State cmProcess::GetProcessStatus() | |
| { | |
| return this->ProcessState; | |
| } | |
| cm::optional<cmProcess::ComputedTimeout> cmProcess::GetComputedTimeout() const | |
| { | |
| if (this->StopTimeout && this->Timeout) { | |
| if (*this->StopTimeout < *this->Timeout) { | |
| return ComputedTimeout{ TimeoutReason::StopTime, *this->StopTimeout }; | |
| } | |
| return ComputedTimeout{ TimeoutReason::Normal, *this->Timeout }; | |
| } | |
| if (this->StopTimeout) { | |
| return ComputedTimeout{ TimeoutReason::StopTime, *this->StopTimeout }; | |
| } | |
| if (this->Timeout) { | |
| return ComputedTimeout{ TimeoutReason::Normal, *this->Timeout }; | |
| } | |
| return cm::nullopt; | |
| } | |
| void cmProcess::ChangeTimeout(cmDuration t) | |
| { | |
| this->Timeout = t; | |
| this->StartTimer(); | |
| } | |
| void cmProcess::ResetStartTime() | |
| { | |
| this->StartTime = std::chrono::steady_clock::now(); | |
| } | |
| cmProcess::Exception cmProcess::GetExitException() const | |
| { | |
| auto exception = Exception::None; | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| auto exit_code = (DWORD)this->ExitValue; | |
| if ((exit_code & 0xF0000000) != 0xC0000000) { | |
| return exception; | |
| } | |
| if (exit_code) { | |
| switch (exit_code) { | |
| case STATUS_DATATYPE_MISALIGNMENT: | |
| case STATUS_ACCESS_VIOLATION: | |
| case STATUS_IN_PAGE_ERROR: | |
| case STATUS_INVALID_HANDLE: | |
| case STATUS_NONCONTINUABLE_EXCEPTION: | |
| case STATUS_INVALID_DISPOSITION: | |
| case STATUS_ARRAY_BOUNDS_EXCEEDED: | |
| case STATUS_STACK_OVERFLOW: | |
| exception = Exception::Fault; | |
| break; | |
| case STATUS_FLOAT_DENORMAL_OPERAND: | |
| case STATUS_FLOAT_DIVIDE_BY_ZERO: | |
| case STATUS_FLOAT_INEXACT_RESULT: | |
| case STATUS_FLOAT_INVALID_OPERATION: | |
| case STATUS_FLOAT_OVERFLOW: | |
| case STATUS_FLOAT_STACK_CHECK: | |
| case STATUS_FLOAT_UNDERFLOW: | |
| # ifdef STATUS_FLOAT_MULTIPLE_FAULTS | |
| case STATUS_FLOAT_MULTIPLE_FAULTS: | |
| # endif | |
| # ifdef STATUS_FLOAT_MULTIPLE_TRAPS | |
| case STATUS_FLOAT_MULTIPLE_TRAPS: | |
| # endif | |
| case STATUS_INTEGER_DIVIDE_BY_ZERO: | |
| case STATUS_INTEGER_OVERFLOW: | |
| exception = Exception::Numerical; | |
| break; | |
| case STATUS_CONTROL_C_EXIT: | |
| exception = Exception::Interrupt; | |
| break; | |
| case STATUS_ILLEGAL_INSTRUCTION: | |
| case STATUS_PRIVILEGED_INSTRUCTION: | |
| exception = Exception::Illegal; | |
| break; | |
| default: | |
| exception = Exception::Other; | |
| } | |
| } | |
| #else | |
| if (this->Signal) { | |
| switch (this->Signal) { | |
| case SIGSEGV: | |
| exception = Exception::Fault; | |
| break; | |
| case SIGFPE: | |
| exception = Exception::Numerical; | |
| break; | |
| case SIGINT: | |
| exception = Exception::Interrupt; | |
| break; | |
| case SIGILL: | |
| exception = Exception::Illegal; | |
| break; | |
| default: | |
| exception = Exception::Other; | |
| } | |
| } | |
| #endif | |
| return exception; | |
| } | |
| std::string cmProcess::GetExitExceptionString() const | |
| { | |
| std::string exception_str; | |
| #if defined(_WIN32) | |
| switch (this->ExitValue) { | |
| case STATUS_CONTROL_C_EXIT: | |
| exception_str = "User interrupt"; | |
| break; | |
| case STATUS_FLOAT_DENORMAL_OPERAND: | |
| exception_str = "Floating-point exception (denormal operand)"; | |
| break; | |
| case STATUS_FLOAT_DIVIDE_BY_ZERO: | |
| exception_str = "Divide-by-zero"; | |
| break; | |
| case STATUS_FLOAT_INEXACT_RESULT: | |
| exception_str = "Floating-point exception (inexact result)"; | |
| break; | |
| case STATUS_FLOAT_INVALID_OPERATION: | |
| exception_str = "Invalid floating-point operation"; | |
| break; | |
| case STATUS_FLOAT_OVERFLOW: | |
| exception_str = "Floating-point overflow"; | |
| break; | |
| case STATUS_FLOAT_STACK_CHECK: | |
| exception_str = "Floating-point stack check failed"; | |
| break; | |
| case STATUS_FLOAT_UNDERFLOW: | |
| exception_str = "Floating-point underflow"; | |
| break; | |
| # ifdef STATUS_FLOAT_MULTIPLE_FAULTS | |
| case STATUS_FLOAT_MULTIPLE_FAULTS: | |
| exception_str = "Floating-point exception (multiple faults)"; | |
| break; | |
| # endif | |
| # ifdef STATUS_FLOAT_MULTIPLE_TRAPS | |
| case STATUS_FLOAT_MULTIPLE_TRAPS: | |
| exception_str = "Floating-point exception (multiple traps)"; | |
| break; | |
| # endif | |
| case STATUS_INTEGER_DIVIDE_BY_ZERO: | |
| exception_str = "Integer divide-by-zero"; | |
| break; | |
| case STATUS_INTEGER_OVERFLOW: | |
| exception_str = "Integer overflow"; | |
| break; | |
| case STATUS_DATATYPE_MISALIGNMENT: | |
| exception_str = "Datatype misalignment"; | |
| break; | |
| case STATUS_ACCESS_VIOLATION: | |
| exception_str = "Access violation"; | |
| break; | |
| case STATUS_IN_PAGE_ERROR: | |
| exception_str = "In-page error"; | |
| break; | |
| case STATUS_INVALID_HANDLE: | |
| exception_str = "Invalid handle"; | |
| break; | |
| case STATUS_NONCONTINUABLE_EXCEPTION: | |
| exception_str = "Noncontinuable exception"; | |
| break; | |
| case STATUS_INVALID_DISPOSITION: | |
| exception_str = "Invalid disposition"; | |
| break; | |
| case STATUS_ARRAY_BOUNDS_EXCEEDED: | |
| exception_str = "Array bounds exceeded"; | |
| break; | |
| case STATUS_STACK_OVERFLOW: | |
| exception_str = "Stack overflow"; | |
| break; | |
| case STATUS_ILLEGAL_INSTRUCTION: | |
| exception_str = "Illegal instruction"; | |
| break; | |
| case STATUS_PRIVILEGED_INSTRUCTION: | |
| exception_str = "Privileged instruction"; | |
| break; | |
| case STATUS_NO_MEMORY: | |
| default: | |
| char buf[1024]; | |
| char const* fmt = "Exit code 0x%" KWIML_INT_PRIx64; | |
| snprintf(buf, sizeof(buf), fmt, this->ExitValue); | |
| exception_str.assign(buf); | |
| } | |
| #else | |
| switch (this->Signal) { | |
| # ifdef SIGSEGV | |
| case SIGSEGV: | |
| exception_str = "Segmentation fault"; | |
| break; | |
| # endif | |
| # ifdef SIGBUS | |
| # if !defined(SIGSEGV) || SIGBUS != SIGSEGV | |
| case SIGBUS: | |
| exception_str = "Bus error"; | |
| break; | |
| # endif | |
| # endif | |
| # ifdef SIGFPE | |
| case SIGFPE: | |
| exception_str = "Floating-point exception"; | |
| break; | |
| # endif | |
| # ifdef SIGILL | |
| case SIGILL: | |
| exception_str = "Illegal instruction"; | |
| break; | |
| # endif | |
| # ifdef SIGINT | |
| case SIGINT: | |
| exception_str = "User interrupt"; | |
| break; | |
| # endif | |
| # ifdef SIGABRT | |
| case SIGABRT: | |
| exception_str = "Subprocess aborted"; | |
| break; | |
| # endif | |
| # ifdef SIGKILL | |
| case SIGKILL: | |
| exception_str = "Subprocess killed"; | |
| break; | |
| # endif | |
| # ifdef SIGTERM | |
| case SIGTERM: | |
| exception_str = "Subprocess terminated"; | |
| break; | |
| # endif | |
| # ifdef SIGHUP | |
| case SIGHUP: | |
| exception_str = "SIGHUP"; | |
| break; | |
| # endif | |
| # ifdef SIGQUIT | |
| case SIGQUIT: | |
| exception_str = "SIGQUIT"; | |
| break; | |
| # endif | |
| # ifdef SIGTRAP | |
| case SIGTRAP: | |
| exception_str = "SIGTRAP"; | |
| break; | |
| # endif | |
| # ifdef SIGIOT | |
| # if !defined(SIGABRT) || SIGIOT != SIGABRT | |
| case SIGIOT: | |
| exception_str = "SIGIOT"; | |
| break; | |
| # endif | |
| # endif | |
| # ifdef SIGUSR1 | |
| case SIGUSR1: | |
| exception_str = "SIGUSR1"; | |
| break; | |
| # endif | |
| # ifdef SIGUSR2 | |
| case SIGUSR2: | |
| exception_str = "SIGUSR2"; | |
| break; | |
| # endif | |
| # ifdef SIGPIPE | |
| case SIGPIPE: | |
| exception_str = "SIGPIPE"; | |
| break; | |
| # endif | |
| # ifdef SIGALRM | |
| case SIGALRM: | |
| exception_str = "SIGALRM"; | |
| break; | |
| # endif | |
| # ifdef SIGSTKFLT | |
| case SIGSTKFLT: | |
| exception_str = "SIGSTKFLT"; | |
| break; | |
| # endif | |
| # ifdef SIGCHLD | |
| case SIGCHLD: | |
| exception_str = "SIGCHLD"; | |
| break; | |
| # elif defined(SIGCLD) | |
| case SIGCLD: | |
| exception_str = "SIGCLD"; | |
| break; | |
| # endif | |
| # ifdef SIGCONT | |
| case SIGCONT: | |
| exception_str = "SIGCONT"; | |
| break; | |
| # endif | |
| # ifdef SIGSTOP | |
| case SIGSTOP: | |
| exception_str = "SIGSTOP"; | |
| break; | |
| # endif | |
| # ifdef SIGTSTP | |
| case SIGTSTP: | |
| exception_str = "SIGTSTP"; | |
| break; | |
| # endif | |
| # ifdef SIGTTIN | |
| case SIGTTIN: | |
| exception_str = "SIGTTIN"; | |
| break; | |
| # endif | |
| # ifdef SIGTTOU | |
| case SIGTTOU: | |
| exception_str = "SIGTTOU"; | |
| break; | |
| # endif | |
| # ifdef SIGURG | |
| case SIGURG: | |
| exception_str = "SIGURG"; | |
| break; | |
| # endif | |
| # ifdef SIGXCPU | |
| case SIGXCPU: | |
| exception_str = "SIGXCPU"; | |
| break; | |
| # endif | |
| # ifdef SIGXFSZ | |
| case SIGXFSZ: | |
| exception_str = "SIGXFSZ"; | |
| break; | |
| # endif | |
| # ifdef SIGVTALRM | |
| case SIGVTALRM: | |
| exception_str = "SIGVTALRM"; | |
| break; | |
| # endif | |
| # ifdef SIGPROF | |
| case SIGPROF: | |
| exception_str = "SIGPROF"; | |
| break; | |
| # endif | |
| # ifdef SIGWINCH | |
| case SIGWINCH: | |
| exception_str = "SIGWINCH"; | |
| break; | |
| # endif | |
| # ifdef SIGPOLL | |
| case SIGPOLL: | |
| exception_str = "SIGPOLL"; | |
| break; | |
| # endif | |
| # ifdef SIGIO | |
| # if !defined(SIGPOLL) || SIGIO != SIGPOLL | |
| case SIGIO: | |
| exception_str = "SIGIO"; | |
| break; | |
| # endif | |
| # endif | |
| # ifdef SIGPWR | |
| case SIGPWR: | |
| exception_str = "SIGPWR"; | |
| break; | |
| # endif | |
| # ifdef SIGSYS | |
| case SIGSYS: | |
| exception_str = "SIGSYS"; | |
| break; | |
| # endif | |
| # ifdef SIGUNUSED | |
| # if !defined(SIGSYS) || SIGUNUSED != SIGSYS | |
| case SIGUNUSED: | |
| exception_str = "SIGUNUSED"; | |
| break; | |
| # endif | |
| # endif | |
| default: | |
| exception_str = cmStrCat("Signal ", this->Signal); | |
| } | |
| #endif | |
| return exception_str; | |
| } |