Core: Refactor code to install new and SIGSEGV handlers

This commit is contained in:
wmayer
2026-02-28 10:11:00 +01:00
committed by Ladislav Michl
parent 821596140d
commit be5e657a61
4 changed files with 308 additions and 169 deletions
+42 -169
View File
@@ -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
+6
View File
@@ -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
+222
View File
@@ -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
}
+38
View File
@@ -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