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/std/string/string_view.h>
#include <ctype.h>
#include <execinfo.h>
#include <signal.h>
#include <unistd.h>
namespace AZ::Debug::Platform
namespace AZ::Debug
{
#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 (!processStatusFile.Open("/proc/self/status", AZ::IO::SystemFile::SF_OPEN_READ_ONLY))
#if defined(AZ_ENABLE_DEBUG_TOOLS)
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;
}
char buffer[4096];
AZ::IO::SystemFile::SizeType numRead = processStatusFile.Read(sizeof(buffer), buffer);
bool IsDebuggerPresent()
{
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);
constexpr AZStd::string_view tracerPidString = "TracerPid:";
const size_t tracerPidOffset = processStatusView.find(tracerPidString);
if (tracerPidOffset == AZStd::string_view::npos)
bool AttachDebugger()
{
// Not supported yet
AZ_Assert(false, "AttachDebugger() is not supported for Unix platform yet");
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()
{
static bool s_detectionPerformed = false;
static bool s_debuggerDetected = false;
if (!s_detectionPerformed)
void DebugBreak()
{
s_debuggerDetected = performDebuggerDetection();
s_detectionPerformed = true;
raise(SIGINT);
}
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
AZ_Assert(false, "AttachDebugger() is not supported for Unix platform yet");
return false;
}
char message[MaxMessageLength];
Debug::Trace::Instance().Output(nullptr, "==================================================================\n");
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()
{
raise(SIGINT);
Debug::Trace::Instance().Output(nullptr, "==================================================================\n");
}
#endif // AZ_ENABLE_DEBUG_TOOLS
#endif
void Terminate(int exitCode)
{
_exit(exitCode);
}
} // namespace AZ::Debug::Platform
} // namespace AZ::Debug

@ -115,43 +115,6 @@ extern LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExce
#include AZ_RESTRICTED_FILE(SystemInit_cpp)
#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"
@ -697,11 +660,6 @@ public:
/////////////////////////////////////////////////////////////////////////////////
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
// global gEnv, instead its resolving an some uninitialized gEnv elsewhere (NULL). Since gEnv is

Loading…
Cancel
Save