feature: add Exception Handler support for unix

REF: https://github.com/o3de/o3de/issues/5886

Signed-off-by: Michael Pollind <mpollind@gmail.com>
monroegm-disable-blank-issue-2
Michael Pollind 4 years ago
parent da0a10bb4c
commit ada7c41a34

@ -6,74 +6,129 @@
* *
*/ */
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/IO/SystemFile.h> #include <AzCore/IO/SystemFile.h>
#include <AzCore/std/string/string_view.h> #include <AzCore/std/string/string_view.h>
#include <ctype.h> #include <ctype.h>
#include <execinfo.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
namespace AZ::Debug::Platform namespace AZ::Debug
{ {
#if defined(AZ_ENABLE_DEBUG_TOOLS) #if defined(AZ_ENABLE_DEBUG_TOOLS)
bool performDebuggerDetection() void ExceptionHandler(int signal);
#endif
constexpr int MaxMessageLength = 4096;
constexpr int MaxStackLines = 100;
namespace Platform
{ {
AZ::IO::SystemFile processStatusFile; #if defined(AZ_ENABLE_DEBUG_TOOLS)
if (!processStatusFile.Open("/proc/self/status", AZ::IO::SystemFile::SF_OPEN_READ_ONLY)) bool performDebuggerDetection()
{ {
AZ::IO::SystemFile processStatusFile;
if (!processStatusFile.Open("/proc/self/status", AZ::IO::SystemFile::SF_OPEN_READ_ONLY))
{
return false;
}
char buffer[4096];
AZ::IO::SystemFile::SizeType numRead = processStatusFile.Read(sizeof(buffer), buffer);
const AZStd::string_view processStatusView(buffer, buffer + numRead);
constexpr AZStd::string_view tracerPidString = "TracerPid:";
const size_t tracerPidOffset = processStatusView.find(tracerPidString);
if (tracerPidOffset == AZStd::string_view::npos)
{
return false;
}
for (size_t i = tracerPidOffset + tracerPidString.length(); i < numRead; ++i)
{
if (!::isspace(processStatusView[i]))
{
return processStatusView[i] != '0';
}
}
return false; return false;
} }
char buffer[4096]; bool IsDebuggerPresent()
AZ::IO::SystemFile::SizeType numRead = processStatusFile.Read(sizeof(buffer), buffer); {
static bool s_detectionPerformed = false;
static bool s_debuggerDetected = false;
if (!s_detectionPerformed)
{
s_debuggerDetected = performDebuggerDetection();
s_detectionPerformed = true;
}
return s_debuggerDetected;
}
const AZStd::string_view processStatusView(buffer, buffer + numRead); bool AttachDebugger()
constexpr AZStd::string_view tracerPidString = "TracerPid:";
const size_t tracerPidOffset = processStatusView.find(tracerPidString);
if (tracerPidOffset == AZStd::string_view::npos)
{ {
// Not supported yet
AZ_Assert(false, "AttachDebugger() is not supported for Unix platform yet");
return false; return false;
} }
for (size_t i = tracerPidOffset + tracerPidString.length(); i < numRead; ++i)
void SignalHandler(int handler)
{
}
void HandleExceptions(bool isEnabled)
{ {
if (!::isspace(processStatusView[i])) if (isEnabled)
{ {
return processStatusView[i] != '0'; signal(SIGSEGV, ExceptionHandler);
signal(SIGTRAP, ExceptionHandler);
signal(SIGILL, ExceptionHandler);
}
else
{
signal(SIGSEGV, SIG_DFL);
signal(SIGTRAP, SIG_DFL);
signal(SIGILL, SIG_DFL);
} }
} }
return false;
}
bool IsDebuggerPresent() void DebugBreak()
{
static bool s_detectionPerformed = false;
static bool s_debuggerDetected = false;
if (!s_detectionPerformed)
{ {
s_debuggerDetected = performDebuggerDetection(); raise(SIGINT);
s_detectionPerformed = true;
} }
return s_debuggerDetected; #endif // AZ_ENABLE_DEBUG_TOOLS
}
bool AttachDebugger() void Terminate(int exitCode)
{
_exit(exitCode);
}
} // namespace Platform
#if defined(AZ_ENABLE_DEBUG_TOOLS)
void ExceptionHandler(int signal)
{ {
// Not supported yet char message[MaxMessageLength];
AZ_Assert(false, "AttachDebugger() is not supported for Unix platform yet"); Debug::Trace::Instance().Output(nullptr, "==================================================================\n");
return false; azsnprintf(message, MaxMessageLength, "Error: signal %s: \n", strsignal(signal));
} Debug::Trace::Instance().Output(nullptr, message);
void HandleExceptions(bool) void* buffers[MaxStackLines];
{} int numberBacktraceStrings = backtrace(buffers, MaxStackLines);
char** backtraceResults = backtrace_symbols(buffers, numberBacktraceStrings);
if (backtraceResults == nullptr)
{
Debug::Trace::Instance().Output(nullptr, "==================================================================\n");
return;
}
for (int j = 0; j < numberBacktraceStrings; j++)
{
Debug::Trace::Instance().Output(nullptr, backtraceResults[j]);
}
void DebugBreak() Debug::Trace::Instance().Output(nullptr, "==================================================================\n");
{
raise(SIGINT);
} }
#endif // AZ_ENABLE_DEBUG_TOOLS #endif
void Terminate(int exitCode) } // namespace AZ::Debug
{
_exit(exitCode);
}
} // namespace AZ::Debug::Platform

@ -115,43 +115,6 @@ extern LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExce
#include AZ_RESTRICTED_FILE(SystemInit_cpp) #include AZ_RESTRICTED_FILE(SystemInit_cpp)
#endif #endif
#if AZ_TRAIT_USE_CRY_SIGNAL_HANDLER
#include <execinfo.h>
#include <signal.h>
void CryEngineSignalHandler(int signal)
{
char resolvedPath[_MAX_PATH];
// it is assumed that @log@ points at the appropriate place (so for apple, to the user profile dir)
if (AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath("@log@/crash.log", resolvedPath, _MAX_PATH))
{
fprintf(stderr, "Crash Signal Handler - logged to %s\n", resolvedPath);
FILE* file = fopen(resolvedPath, "a");
if (file)
{
char sTime[128];
time_t ltime;
time(&ltime);
struct tm* today = localtime(&ltime);
strftime(sTime, 40, "<%Y-%m-%d %H:%M:%S> ", today);
fprintf(file, "%s: Error: signal %s:\n", sTime, strsignal(signal));
fflush(file);
void* array[100];
int s = backtrace(array, 100);
backtrace_symbols_fd(array, s, fileno(file));
fclose(file);
CryLogAlways("Successfully recorded crash file: '%s'", resolvedPath);
abort();
}
}
CryLogAlways("Could not record crash file...");
abort();
}
#endif // AZ_TRAIT_USE_CRY_SIGNAL_HANDLER
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
#define DEFAULT_LOG_FILENAME "@log@/Log.txt" #define DEFAULT_LOG_FILENAME "@log@/Log.txt"
@ -697,11 +660,6 @@ public:
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
bool CSystem::Init(const SSystemInitParams& startupParams) bool CSystem::Init(const SSystemInitParams& startupParams)
{ {
#if AZ_TRAIT_USE_CRY_SIGNAL_HANDLER
signal(SIGSEGV, CryEngineSignalHandler);
signal(SIGTRAP, CryEngineSignalHandler);
signal(SIGILL, CryEngineSignalHandler);
#endif // AZ_TRAIT_USE_CRY_SIGNAL_HANDLER
// Temporary Fix for an issue accessing gEnv from this object instance. The gEnv is not resolving to the // Temporary Fix for an issue accessing gEnv from this object instance. The gEnv is not resolving to the
// global gEnv, instead its resolving an some uninitialized gEnv elsewhere (NULL). Since gEnv is // global gEnv, instead its resolving an some uninitialized gEnv elsewhere (NULL). Since gEnv is

Loading…
Cancel
Save