Merge pull request #27854 from 3x380V/signal
Part: Use signal handlers based on OCC's implementation
This commit is contained in:
+42
-169
@@ -96,6 +96,7 @@
|
||||
#include <Base/ProgressIndicatorPy.h>
|
||||
#include <Base/RotationPy.h>
|
||||
#include <Base/UniqueNameManager.h>
|
||||
#include <Base/SystemHandler.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Translate.h>
|
||||
#include <Base/Type.h>
|
||||
@@ -164,18 +165,6 @@
|
||||
|
||||
#include "SafeMode.h"
|
||||
|
||||
#ifdef _MSC_VER // New handler for Microsoft Visual C++ compiler
|
||||
# pragma warning( disable : 4535 )
|
||||
# if !defined(_DEBUG) && defined(HAVE_SEH)
|
||||
# define FC_SE_TRANSLATOR
|
||||
# endif
|
||||
|
||||
# include <new.h>
|
||||
# include <eh.h> // VC exception handling
|
||||
#else // Ansi C/C++ new handler
|
||||
# include <new>
|
||||
#endif
|
||||
|
||||
#ifdef FC_OS_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
@@ -1774,175 +1763,59 @@ void Application::destructObserver()
|
||||
}
|
||||
}
|
||||
|
||||
/** freecadNewHandler()
|
||||
* prints an error message and throws an exception
|
||||
*/
|
||||
#ifdef _MSC_VER // New handler for Microsoft Visual C++ compiler
|
||||
int __cdecl freecadNewHandler(size_t size )
|
||||
namespace
|
||||
{
|
||||
// throw an exception
|
||||
throw Base::MemoryException();
|
||||
return 0;
|
||||
}
|
||||
#else // Ansi C/C++ new handler
|
||||
static void freecadNewHandler ()
|
||||
void initExceptions()
|
||||
{
|
||||
// throw an exception
|
||||
throw Base::MemoryException();
|
||||
// register exception producer types
|
||||
// NOLINTBEGIN
|
||||
new Base::ExceptionProducer<Base::AbortException>;
|
||||
new Base::ExceptionProducer<Base::XMLBaseException>;
|
||||
new Base::ExceptionProducer<Base::XMLParseException>;
|
||||
new Base::ExceptionProducer<Base::XMLAttributeError>;
|
||||
new Base::ExceptionProducer<Base::FileException>;
|
||||
new Base::ExceptionProducer<Base::FileSystemError>;
|
||||
new Base::ExceptionProducer<Base::BadFormatError>;
|
||||
new Base::ExceptionProducer<Base::MemoryException>;
|
||||
new Base::ExceptionProducer<Base::AccessViolation>;
|
||||
new Base::ExceptionProducer<Base::AbnormalProgramTermination>;
|
||||
new Base::ExceptionProducer<Base::UnknownProgramOption>;
|
||||
new Base::ExceptionProducer<Base::ProgramInformation>;
|
||||
new Base::ExceptionProducer<Base::TypeError>;
|
||||
new Base::ExceptionProducer<Base::ValueError>;
|
||||
new Base::ExceptionProducer<Base::IndexError>;
|
||||
new Base::ExceptionProducer<Base::NameError>;
|
||||
new Base::ExceptionProducer<Base::ImportError>;
|
||||
new Base::ExceptionProducer<Base::AttributeError>;
|
||||
new Base::ExceptionProducer<Base::RuntimeError>;
|
||||
new Base::ExceptionProducer<Base::BadGraphError>;
|
||||
new Base::ExceptionProducer<Base::NotImplementedError>;
|
||||
new Base::ExceptionProducer<Base::ZeroDivisionError>;
|
||||
new Base::ExceptionProducer<Base::ReferenceError>;
|
||||
new Base::ExceptionProducer<Base::ExpressionError>;
|
||||
new Base::ExceptionProducer<Base::ParserError>;
|
||||
new Base::ExceptionProducer<Base::UnicodeError>;
|
||||
new Base::ExceptionProducer<Base::OverflowError>;
|
||||
new Base::ExceptionProducer<Base::UnderflowError>;
|
||||
new Base::ExceptionProducer<Base::UnitsMismatchError>;
|
||||
new Base::ExceptionProducer<Base::CADKernelError>;
|
||||
new Base::ExceptionProducer<Base::RestoreError>;
|
||||
new Base::ExceptionProducer<Base::PropertyError>;
|
||||
// NOLINTEND
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FC_OS_LINUX)
|
||||
#include <execinfo.h>
|
||||
#include <dlfcn.h>
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
// This function produces a stack backtrace with demangled function & method names.
|
||||
void printBacktrace(size_t skip=0)
|
||||
{
|
||||
#if defined HAVE_BACKTRACE_SYMBOLS
|
||||
void *callstack[128];
|
||||
size_t nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
|
||||
size_t nFrames = backtrace(callstack, nMaxFrames);
|
||||
char **symbols = backtrace_symbols(callstack, nFrames);
|
||||
|
||||
for (size_t i = skip; i < nFrames; i++) {
|
||||
char *demangled = nullptr;
|
||||
int status = -1;
|
||||
Dl_info info;
|
||||
if (dladdr(callstack[i], &info) && info.dli_sname && info.dli_fname) {
|
||||
if (info.dli_sname[0] == '_') {
|
||||
demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream str;
|
||||
if (status == 0) {
|
||||
void* offset = (void*)((char*)callstack[i] - (char*)info.dli_saddr);
|
||||
str << "#" << (i-skip) << " " << callstack[i] << " in " << demangled << " from " << info.dli_fname << "+" << offset << '\n';
|
||||
free(demangled);
|
||||
}
|
||||
else {
|
||||
str << "#" << (i-skip) << " " << symbols[i] << '\n';
|
||||
}
|
||||
|
||||
// cannot directly print to cerr when using --write-log
|
||||
std::cerr << str.str();
|
||||
}
|
||||
|
||||
free(symbols);
|
||||
#else //HAVE_BACKTRACE_SYMBOLS
|
||||
(void)skip;
|
||||
std::cerr << "Cannot print the stacktrace because the C runtime library doesn't provide backtrace or backtrace_symbols\n";
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void segmentation_fault_handler(int sig)
|
||||
{
|
||||
#if defined(FC_OS_LINUX)
|
||||
(void)sig;
|
||||
std::cerr << "Program received signal SIGSEGV, Segmentation fault.\n";
|
||||
printBacktrace(2);
|
||||
#if defined(FC_DEBUG)
|
||||
abort();
|
||||
#else
|
||||
_exit(1);
|
||||
#endif
|
||||
#else
|
||||
switch (sig) {
|
||||
case SIGSEGV:
|
||||
std::cerr << "Illegal storage access..." << '\n';
|
||||
#if !defined(_DEBUG)
|
||||
throw Base::AccessViolation("Illegal storage access! Please save your work under a new file name and restart the application!");
|
||||
#endif
|
||||
break;
|
||||
case SIGABRT:
|
||||
std::cerr << "Abnormal program termination..." << '\n';
|
||||
#if !defined(_DEBUG)
|
||||
throw Base::AbnormalProgramTermination("Break signal occurred");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Unknown error occurred..." << '\n';
|
||||
break;
|
||||
}
|
||||
#endif // FC_OS_LINUX
|
||||
}
|
||||
|
||||
void unhandled_exception_handler()
|
||||
{
|
||||
std::cerr << "Terminating..." << '\n';
|
||||
}
|
||||
|
||||
void unexpection_error_handler()
|
||||
{
|
||||
std::cerr << "Unexpected error occurred..." << '\n';
|
||||
// try to throw an exception and give the user chance to save their work
|
||||
#if !defined(_DEBUG)
|
||||
throw Base::AbnormalProgramTermination("Unexpected error occurred! Please save your work under a new file name and restart the application!");
|
||||
#else
|
||||
terminate();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(FC_SE_TRANSLATOR) // Microsoft compiler
|
||||
void my_se_translator_filter(unsigned int code, EXCEPTION_POINTERS* pExp)
|
||||
{
|
||||
Q_UNUSED(pExp)
|
||||
switch (code)
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
throw Base::AccessViolation();
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
Base::Console().error("SEH exception (%u): Division by zero\n", code);
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream str;
|
||||
str << "SEH exception of type: " << code;
|
||||
// general C++ SEH exception for things we don't need to handle separately....
|
||||
throw Base::RuntimeError(str.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
void Application::init(int argc, char ** argv)
|
||||
{
|
||||
try {
|
||||
// install our own new handler
|
||||
#ifdef _MSC_VER // Microsoft compiler
|
||||
_set_new_handler ( freecadNewHandler ); // Setup new handler
|
||||
_set_new_mode( 1 ); // Re-route malloc failures to new handler !
|
||||
#else // Ansi compiler
|
||||
std::set_new_handler (freecadNewHandler); // ANSI new handler
|
||||
#endif
|
||||
// if an unexpected crash occurs we can install a handler function to
|
||||
// write some additional information
|
||||
#if defined (_MSC_VER) // Microsoft compiler
|
||||
std::signal(SIGSEGV,segmentation_fault_handler);
|
||||
std::signal(SIGABRT,segmentation_fault_handler);
|
||||
std::set_terminate(unhandled_exception_handler);
|
||||
::set_unexpected(unexpection_error_handler);
|
||||
#elif defined(FC_OS_LINUX)
|
||||
std::signal(SIGSEGV,segmentation_fault_handler);
|
||||
#endif
|
||||
#if defined(FC_SE_TRANSLATOR)
|
||||
_set_se_translator(my_se_translator_filter);
|
||||
#endif
|
||||
Base::SystemHandler::installNewHandler();
|
||||
Base::SystemHandler::installSegfaultHandler();
|
||||
|
||||
initTypes();
|
||||
|
||||
initConfig(argc,argv);
|
||||
initApplication();
|
||||
initExceptions();
|
||||
}
|
||||
catch (...) {
|
||||
// force the log to flush
|
||||
|
||||
@@ -13,6 +13,10 @@ if(BUILD_TRACY_FRAME_PROFILER)
|
||||
add_definitions(-DBUILD_TRACY_FRAME_PROFILER)
|
||||
endif()
|
||||
|
||||
if(FREECAD_RELEASE_SEH)
|
||||
add_definitions(-DHAVE_SEH)
|
||||
endif(FREECAD_RELEASE_SEH)
|
||||
|
||||
target_include_directories(
|
||||
FreeCADBase
|
||||
PRIVATE
|
||||
@@ -225,6 +229,7 @@ SET(FreeCADBase_CPP_SRCS
|
||||
Stream.cpp
|
||||
Swap.cpp
|
||||
${SWIG_SRCS}
|
||||
SystemHandler.cpp
|
||||
Tools.cpp
|
||||
Tools2D.cpp
|
||||
Tools3D.cpp
|
||||
@@ -291,6 +296,7 @@ SET(FreeCADBase_HPP_SRCS
|
||||
Stream.h
|
||||
Swap.h
|
||||
${SWIG_HEADERS}
|
||||
SystemHandler.h
|
||||
TimeInfo.h
|
||||
Tools.h
|
||||
Tools2D.h
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2025 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "SystemHandler.h"
|
||||
#include "Console.h"
|
||||
#include "Exception.h"
|
||||
|
||||
#ifdef FC_OS_WIN32
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER // New handler for Microsoft Visual C++ compiler
|
||||
# pragma warning(disable : 4535)
|
||||
# if !defined(_DEBUG) && defined(HAVE_SEH)
|
||||
# define FC_SE_TRANSLATOR
|
||||
# endif
|
||||
|
||||
# include <new.h>
|
||||
# include <eh.h> // VC exception handling
|
||||
#else // Ansi C/C++ new handler
|
||||
# include <new>
|
||||
#endif
|
||||
|
||||
using namespace Base;
|
||||
|
||||
/** freecadNewHandler()
|
||||
* prints an error message and throws an exception
|
||||
*/
|
||||
#ifdef _MSC_VER // New handler for Microsoft Visual C++ compiler
|
||||
int __cdecl freecadNewHandler(size_t size)
|
||||
{
|
||||
// throw an exception
|
||||
throw Base::MemoryException();
|
||||
return 0;
|
||||
}
|
||||
#else // Ansi C/C++ new handler
|
||||
static void freecadNewHandler()
|
||||
{
|
||||
// throw an exception
|
||||
throw Base::MemoryException();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FC_OS_LINUX)
|
||||
# include <unistd.h>
|
||||
# include <execinfo.h>
|
||||
# include <dlfcn.h>
|
||||
# include <cxxabi.h>
|
||||
|
||||
# include <cstdio>
|
||||
# include <cstdlib>
|
||||
# include <string>
|
||||
|
||||
# if HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# endif // HAVE_CONFIG_H
|
||||
|
||||
// This function produces a stack backtrace with demangled function & method names.
|
||||
static void printBacktrace([[maybe_unused]] size_t skip = 0)
|
||||
{
|
||||
# if defined HAVE_BACKTRACE_SYMBOLS
|
||||
void* callstack[128];
|
||||
size_t nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
|
||||
size_t nFrames = backtrace(callstack, nMaxFrames);
|
||||
char** symbols = backtrace_symbols(callstack, nFrames);
|
||||
|
||||
for (size_t i = skip; i < nFrames; i++) {
|
||||
char* demangled = nullptr;
|
||||
int status = -1;
|
||||
Dl_info info;
|
||||
if (dladdr(callstack[i], &info) && info.dli_sname && info.dli_fname) {
|
||||
if (info.dli_sname[0] == '_') {
|
||||
demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream str;
|
||||
if (status == 0) {
|
||||
void* offset = (void*)((char*)callstack[i] - (char*)info.dli_saddr);
|
||||
str << "#" << (i - skip) << " " << callstack[i] << " in " << demangled << " from "
|
||||
<< info.dli_fname << "+" << offset << '\n';
|
||||
free(demangled);
|
||||
}
|
||||
else {
|
||||
str << "#" << (i - skip) << " " << symbols[i] << '\n';
|
||||
}
|
||||
|
||||
// cannot directly print to cerr when using --write-log
|
||||
std::cerr << str.str();
|
||||
}
|
||||
|
||||
free(symbols);
|
||||
# else // HAVE_BACKTRACE_SYMBOLS
|
||||
std::cerr << "Cannot print the stacktrace because the C runtime library doesn't provide "
|
||||
"backtrace or backtrace_symbols\n";
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void segmentation_fault_handler([[maybe_unused]] int sig)
|
||||
{
|
||||
#if defined(FC_OS_LINUX)
|
||||
std::cerr << "Program received signal SIGSEGV, Segmentation fault.\n";
|
||||
printBacktrace(2);
|
||||
# if defined(FC_DEBUG)
|
||||
abort();
|
||||
# else
|
||||
_exit(1);
|
||||
# endif
|
||||
#else
|
||||
switch (sig) {
|
||||
case SIGSEGV:
|
||||
std::cerr << "Illegal storage access..." << '\n';
|
||||
# if !defined(_DEBUG)
|
||||
throw Base::AccessViolation(
|
||||
"Illegal storage access! Please save your work under a new "
|
||||
"file name and restart the application!"
|
||||
);
|
||||
# endif
|
||||
break;
|
||||
case SIGABRT:
|
||||
std::cerr << "Abnormal program termination..." << '\n';
|
||||
# if !defined(_DEBUG)
|
||||
throw Base::AbnormalProgramTermination("Break signal occurred");
|
||||
# endif
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Unknown error occurred..." << '\n';
|
||||
break;
|
||||
}
|
||||
#endif // FC_OS_LINUX
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
static void unhandled_exception_handler()
|
||||
{
|
||||
std::cerr << "Terminating..." << '\n';
|
||||
}
|
||||
|
||||
static void unexpection_error_handler()
|
||||
{
|
||||
std::cerr << "Unexpected error occurred..." << '\n';
|
||||
// try to throw an exception and give the user chance to save their work
|
||||
# if !defined(_DEBUG)
|
||||
throw Base::AbnormalProgramTermination(
|
||||
"Unexpected error occurred! Please save your work under "
|
||||
"a new file name and restart the application!"
|
||||
);
|
||||
# else
|
||||
terminate();
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FC_SE_TRANSLATOR) // Microsoft compiler
|
||||
void my_se_translator_filter(unsigned int code, EXCEPTION_POINTERS* /*pExp*/)
|
||||
{
|
||||
switch (code) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
throw Base::AccessViolation();
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
Base::Console().error("SEH exception (%u): Division by zero\n", code);
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream str;
|
||||
str << "SEH exception of type: " << code;
|
||||
// general C++ SEH exception for things we don't need to handle separately....
|
||||
throw Base::RuntimeError(str.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
void SystemHandler::installNewHandler()
|
||||
{
|
||||
#ifdef _MSC_VER // Microsoft compiler
|
||||
_set_new_handler(freecadNewHandler); // Setup new handler
|
||||
_set_new_mode(1); // Re-route malloc failures to new handler !
|
||||
#else // Ansi compiler
|
||||
std::set_new_handler(freecadNewHandler); // ANSI new handler
|
||||
#endif
|
||||
}
|
||||
|
||||
void SystemHandler::installSegfaultHandler()
|
||||
{
|
||||
// if an unexpected crash occurs we can install a handler function to
|
||||
// write some additional information
|
||||
#if defined(_MSC_VER) // Microsoft compiler
|
||||
std::signal(SIGSEGV, segmentation_fault_handler);
|
||||
std::signal(SIGABRT, segmentation_fault_handler);
|
||||
std::set_terminate(unhandled_exception_handler);
|
||||
::set_unexpected(unexpection_error_handler);
|
||||
#elif defined(FC_OS_LINUX)
|
||||
std::signal(SIGSEGV, segmentation_fault_handler);
|
||||
#endif
|
||||
#if defined(FC_SE_TRANSLATOR)
|
||||
_set_se_translator(my_se_translator_filter);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2025 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <FCGlobal.h>
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
class BaseExport SystemHandler
|
||||
{
|
||||
public:
|
||||
static void installNewHandler();
|
||||
static void installSegfaultHandler();
|
||||
};
|
||||
|
||||
} // namespace Base
|
||||
@@ -569,6 +569,8 @@ SET(Part_SRCS
|
||||
PreCompiled.h
|
||||
Services.cpp
|
||||
Services.h
|
||||
SignalException.cpp
|
||||
SignalException.h
|
||||
TopoShape.cpp
|
||||
TopoShape.h
|
||||
TopoShapeCache.cpp
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <TopoDS_Iterator.hxx>
|
||||
#include <Precision.hxx>
|
||||
#include <FuzzyHelper.h>
|
||||
#include <SignalException.h>
|
||||
#include <Base/Console.h>
|
||||
|
||||
FCBRepAlgoAPI_BooleanOperation::FCBRepAlgoAPI_BooleanOperation()
|
||||
@@ -103,6 +104,7 @@ void FCBRepAlgoAPI_BooleanOperation::Build()
|
||||
|
||||
void FCBRepAlgoAPI_BooleanOperation::Build(const Message_ProgressRange& progressRange)
|
||||
{
|
||||
Part::SignalException sig;
|
||||
if (progressRange.UserBreak()) {
|
||||
Standard_ConstructionError::Raise("User aborted");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2025 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#include "SignalException.h"
|
||||
#include <FCConfig.h>
|
||||
#if defined(__GNUC__) && defined(FC_OS_LINUX)
|
||||
# include <array>
|
||||
# include <boost/stacktrace.hpp>
|
||||
# include <stdexcept>
|
||||
# include <iostream>
|
||||
# include <csignal>
|
||||
# include <OSD.hxx>
|
||||
# include <OSD_WhoAmI.hxx>
|
||||
# include <OSD_SIGHUP.hxx>
|
||||
# include <OSD_SIGINT.hxx>
|
||||
# include <OSD_SIGQUIT.hxx>
|
||||
# include <OSD_SIGILL.hxx>
|
||||
# include <OSD_SIGKILL.hxx>
|
||||
# include <OSD_SIGBUS.hxx>
|
||||
# include <OSD_SIGSEGV.hxx>
|
||||
# include <OSD_SIGSYS.hxx>
|
||||
# include <Standard.hxx>
|
||||
# include <Standard_NumericError.hxx>
|
||||
# include <Standard_ErrorHandler.hxx>
|
||||
# include <Standard_Assert.hxx>
|
||||
#endif
|
||||
#include <Base/SystemHandler.h>
|
||||
|
||||
using namespace Part;
|
||||
|
||||
// NOLINTBEGIN(concurrency-mt-unsafe, cppcoreguidelines-pro-type-member-init)
|
||||
#if defined(__GNUC__) && defined(FC_OS_LINUX)
|
||||
static OSD_SignalMode OSD_WasSetSignal = OSD_SignalMode_AsIs; // NOLINT
|
||||
|
||||
static void SegvHandler(const int theSignal, siginfo_t* theSigInfo, const Standard_Address /*theContext*/)
|
||||
{
|
||||
std::cerr << "\nStacktrace:" << std::endl;
|
||||
std::cerr << "SIGSEGV signal raised: " << theSignal << std::endl;
|
||||
std::cerr << boost::stacktrace::stacktrace() << std::endl;
|
||||
std::cerr << "\n" << std::endl;
|
||||
if (theSigInfo != nullptr) {
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGSEGV);
|
||||
sigprocmask(SIG_UNBLOCK, &set, nullptr);
|
||||
OSD_SIGSEGV::NewInstance("SIGSEGV 'segmentation violation' detected.")->Jump();
|
||||
}
|
||||
exit(SIGSEGV);
|
||||
}
|
||||
|
||||
static void throw_exc(const int theSignal, siginfo_t* /*theSigInfo*/, const Standard_Address /*theContext*/)
|
||||
{
|
||||
struct sigaction oldact;
|
||||
struct sigaction act;
|
||||
// re-install the signal
|
||||
if (!sigaction(theSignal, nullptr, &oldact)) {
|
||||
if (sigaction(theSignal, &oldact, &act)) {
|
||||
perror("sigaction");
|
||||
}
|
||||
}
|
||||
else {
|
||||
perror("sigaction");
|
||||
}
|
||||
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
switch (theSignal) {
|
||||
case SIGHUP:
|
||||
OSD_SIGHUP::NewInstance("SIGHUP 'hangup' detected.")->Jump();
|
||||
exit(SIGHUP);
|
||||
break;
|
||||
case SIGINT:
|
||||
// For safe handling of Control-C as stop event, arm a variable but do not
|
||||
// generate longjump (we are out of context anyway)
|
||||
OSD_SIGINT::NewInstance("SIGINT 'interrupt' detected.")->Jump();
|
||||
exit(SIGINT);
|
||||
break;
|
||||
case SIGQUIT:
|
||||
OSD_SIGQUIT::NewInstance("SIGQUIT 'quit' detected.")->Jump();
|
||||
exit(SIGQUIT);
|
||||
break;
|
||||
case SIGILL:
|
||||
OSD_SIGILL::NewInstance("SIGILL 'illegal instruction' detected.")->Jump();
|
||||
exit(SIGILL);
|
||||
break;
|
||||
case SIGKILL:
|
||||
OSD_SIGKILL::NewInstance("SIGKILL 'kill' detected.")->Jump();
|
||||
exit(SIGKILL);
|
||||
break;
|
||||
case SIGBUS:
|
||||
sigaddset(&set, SIGBUS);
|
||||
sigprocmask(SIG_UNBLOCK, &set, nullptr);
|
||||
OSD_SIGBUS::NewInstance("SIGBUS 'bus error' detected.")->Jump();
|
||||
exit(SIGBUS);
|
||||
break;
|
||||
case SIGSEGV:
|
||||
OSD_SIGSEGV::NewInstance("SIGSEGV 'segmentation violation' detected.")->Jump();
|
||||
exit(SIGSEGV);
|
||||
break;
|
||||
# ifdef SIGSYS
|
||||
case SIGSYS:
|
||||
OSD_SIGSYS::NewInstance("SIGSYS 'bad argument to system call' detected.")->Jump();
|
||||
exit(SIGSYS);
|
||||
break;
|
||||
# endif
|
||||
case SIGFPE:
|
||||
sigaddset(&set, SIGFPE);
|
||||
sigprocmask(SIG_UNBLOCK, &set, nullptr);
|
||||
OSD::SetFloatingSignal(Standard_True);
|
||||
Standard_NumericError::NewInstance("SIGFPE Arithmetic exception detected")->Jump();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void setSignal(OSD_SignalMode theSignalMode)
|
||||
{
|
||||
OSD_WasSetSignal = theSignalMode;
|
||||
if (theSignalMode == OSD_SignalMode_AsIs) {
|
||||
return; // nothing to be done with signal handlers
|
||||
}
|
||||
|
||||
// Prepare signal descriptors
|
||||
struct sigaction anActSet;
|
||||
struct sigaction anActDfl;
|
||||
struct sigaction anActOld;
|
||||
sigemptyset(&anActSet.sa_mask);
|
||||
sigemptyset(&anActDfl.sa_mask);
|
||||
sigemptyset(&anActOld.sa_mask);
|
||||
# ifdef SA_RESTART
|
||||
anActSet.sa_flags = anActDfl.sa_flags = anActOld.sa_flags = SA_RESTART;
|
||||
# else
|
||||
anActSet.sa_flags = anActDfl.sa_flags = anActOld.sa_flags = 0;
|
||||
# endif
|
||||
# ifdef SA_SIGINFO
|
||||
anActSet.sa_flags = anActSet.sa_flags | SA_SIGINFO;
|
||||
anActSet.sa_sigaction = throw_exc;
|
||||
# else
|
||||
anActSet.sa_handler = throw_exc;
|
||||
# endif
|
||||
anActDfl.sa_handler = SIG_DFL;
|
||||
|
||||
// Set signal handlers; NB: SIGSEGV must be the last one!
|
||||
const int NBSIG = 8;
|
||||
constexpr static std::array<int, NBSIG> aSignalTypes
|
||||
= {SIGFPE, SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGSYS, SIGSEGV};
|
||||
for (int aSignalType : aSignalTypes) {
|
||||
// SIGSEGV has special handler
|
||||
if (aSignalType == SIGSEGV) {
|
||||
# ifdef SA_SIGINFO
|
||||
anActSet.sa_sigaction = /*(void(*)(int, siginfo_t *, void*))*/ SegvHandler;
|
||||
# else
|
||||
anActSet.sa_handler = /*(SIG_PFV)*/ SegvHandler;
|
||||
# endif
|
||||
}
|
||||
|
||||
// set handler according to specified mode and current handler
|
||||
int retcode = -1;
|
||||
if (theSignalMode == OSD_SignalMode_Set || theSignalMode == OSD_SignalMode_SetUnhandled) {
|
||||
retcode = sigaction(aSignalType, &anActSet, &anActOld);
|
||||
}
|
||||
else if (theSignalMode == OSD_SignalMode_Unset) {
|
||||
retcode = sigaction(aSignalType, &anActDfl, &anActOld);
|
||||
}
|
||||
if (theSignalMode == OSD_SignalMode_SetUnhandled && retcode == 0
|
||||
&& anActOld.sa_handler != SIG_DFL) {
|
||||
struct sigaction anActOld2;
|
||||
sigemptyset(&anActOld2.sa_mask);
|
||||
retcode = sigaction(aSignalType, &anActOld, &anActOld2);
|
||||
}
|
||||
Standard_ASSERT(
|
||||
retcode == 0,
|
||||
"sigaction() failed",
|
||||
std::cout << "OSD::SetSignal(): sigaction() failed for " << aSignalType << std::endl
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if defined(__GNUC__) && defined(FC_OS_LINUX)
|
||||
static OSD_SignalMode currentSignalMode = OSD_SignalMode_Unset; // NOLINT
|
||||
#endif
|
||||
|
||||
SignalException::SignalException()
|
||||
{
|
||||
#if defined(__GNUC__) && defined(FC_OS_LINUX)
|
||||
if (currentSignalMode == OSD_SignalMode_Unset) {
|
||||
currentSignalMode = OSD_SignalMode_Set;
|
||||
setSignal(currentSignalMode);
|
||||
enabled = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SignalException::~SignalException()
|
||||
{
|
||||
#if defined(__GNUC__) && defined(FC_OS_LINUX)
|
||||
if (enabled && currentSignalMode == OSD_SignalMode_Set) {
|
||||
currentSignalMode = OSD_SignalMode_Unset;
|
||||
setSignal(currentSignalMode);
|
||||
// this is the default handler
|
||||
Base::SystemHandler::installSegfaultHandler();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// NOLINTEND(concurrency-mt-unsafe, cppcoreguidelines-pro-type-member-init)
|
||||
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2025 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Mod/Part/PartGlobal.h>
|
||||
|
||||
namespace Part
|
||||
{
|
||||
|
||||
class PartExport SignalException
|
||||
{
|
||||
public:
|
||||
SignalException();
|
||||
~SignalException();
|
||||
|
||||
SignalException(const SignalException&) = default;
|
||||
SignalException(SignalException&&) = default;
|
||||
SignalException& operator=(const SignalException&) = default;
|
||||
SignalException& operator=(SignalException&&) = default;
|
||||
|
||||
private:
|
||||
bool enabled {false};
|
||||
};
|
||||
|
||||
} // namespace Part
|
||||
Reference in New Issue
Block a user