From be5e657a61b8346e2b9376bbf01df5f67570f64a Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 24 Mar 2025 16:31:41 +0100 Subject: [PATCH] Core: Refactor code to install new and SIGSEGV handlers --- src/App/Application.cpp | 211 +++++++---------------------------- src/Base/CMakeLists.txt | 6 + src/Base/SystemHandler.cpp | 222 +++++++++++++++++++++++++++++++++++++ src/Base/SystemHandler.h | 38 +++++++ 4 files changed, 308 insertions(+), 169 deletions(-) create mode 100644 src/Base/SystemHandler.cpp create mode 100644 src/Base/SystemHandler.h diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 2a357c793b..d06e9586f2 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -96,6 +96,7 @@ #include #include #include +#include #include #include #include @@ -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 -# include // VC exception handling -#else // Ansi C/C++ new handler -# include -#endif - #ifdef FC_OS_WIN32 #include #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; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + new Base::ExceptionProducer; + // NOLINTEND } -#endif - -#if defined(FC_OS_LINUX) -#include -#include -#include - -#include -#include -#include -#include - -#if HAVE_CONFIG_H -#include -#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 diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index 79904bb860..26448f0293 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -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 diff --git a/src/Base/SystemHandler.cpp b/src/Base/SystemHandler.cpp new file mode 100644 index 0000000000..64b536a2a8 --- /dev/null +++ b/src/Base/SystemHandler.cpp @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2025 Werner Mayer * + * * + * 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 * + * . * + * * + **************************************************************************/ + +#include +#include + +#include "SystemHandler.h" +#include "Console.h" +#include "Exception.h" + +#ifdef FC_OS_WIN32 +# include +#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 +# include // VC exception handling +#else // Ansi C/C++ new handler +# include +#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 +# include +# include +# include + +# include +# include +# include + +# if HAVE_CONFIG_H +# include +# 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 +} diff --git a/src/Base/SystemHandler.h b/src/Base/SystemHandler.h new file mode 100644 index 0000000000..e67e929b20 --- /dev/null +++ b/src/Base/SystemHandler.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2025 Werner Mayer * + * * + * 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 * + * . * + * * + **************************************************************************/ + +#pragma once + +#include + +namespace Base +{ + +class BaseExport SystemHandler +{ +public: + static void installNewHandler(); + static void installSegfaultHandler(); +}; + +} // namespace Base