123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- // Description : A multiplatform base class for handling errors and collecting call stacks
- #include "CrySystem_precompiled.h"
- #include "IDebugCallStack.h"
- #include "System.h"
- #include <AzFramework/IO/FileOperations.h>
- #include <AzCore/NativeUI/NativeUIRequests.h>
- #include <AzCore/StringFunc/StringFunc.h>
- #include <AzCore/Utils/Utils.h>
- //#if !defined(LINUX)
- #include <ISystem.h>
- const char* const IDebugCallStack::s_szFatalErrorCode = "FATAL_ERROR";
- IDebugCallStack::IDebugCallStack()
- : m_bIsFatalError(false)
- , m_postBackupProcess(0)
- , m_memAllocFileHandle(AZ::IO::InvalidHandle)
- {
- }
- IDebugCallStack::~IDebugCallStack()
- {
- StopMemLog();
- }
- #if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_SINGLETON
- IDebugCallStack* IDebugCallStack::instance()
- {
- static IDebugCallStack sInstance;
- return &sInstance;
- }
- #endif
- void IDebugCallStack::FileCreationCallback(void (* postBackupProcess)())
- {
- m_postBackupProcess = postBackupProcess;
- }
- //////////////////////////////////////////////////////////////////////////
- void IDebugCallStack::LogCallstack()
- {
- AZ::Debug::Trace::Instance().PrintCallstack("", 2);
- }
- const char* IDebugCallStack::TranslateExceptionCode(DWORD dwExcept)
- {
- switch (dwExcept)
- {
- #if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_TRANSLATE
- case EXCEPTION_ACCESS_VIOLATION:
- return "EXCEPTION_ACCESS_VIOLATION";
- break;
- case EXCEPTION_DATATYPE_MISALIGNMENT:
- return "EXCEPTION_DATATYPE_MISALIGNMENT";
- break;
- case EXCEPTION_BREAKPOINT:
- return "EXCEPTION_BREAKPOINT";
- break;
- case EXCEPTION_SINGLE_STEP:
- return "EXCEPTION_SINGLE_STEP";
- break;
- case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
- return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
- break;
- case EXCEPTION_FLT_DENORMAL_OPERAND:
- return "EXCEPTION_FLT_DENORMAL_OPERAND";
- break;
- case EXCEPTION_FLT_DIVIDE_BY_ZERO:
- return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
- break;
- case EXCEPTION_FLT_INEXACT_RESULT:
- return "EXCEPTION_FLT_INEXACT_RESULT";
- break;
- case EXCEPTION_FLT_INVALID_OPERATION:
- return "EXCEPTION_FLT_INVALID_OPERATION";
- break;
- case EXCEPTION_FLT_OVERFLOW:
- return "EXCEPTION_FLT_OVERFLOW";
- break;
- case EXCEPTION_FLT_STACK_CHECK:
- return "EXCEPTION_FLT_STACK_CHECK";
- break;
- case EXCEPTION_FLT_UNDERFLOW:
- return "EXCEPTION_FLT_UNDERFLOW";
- break;
- case EXCEPTION_INT_DIVIDE_BY_ZERO:
- return "EXCEPTION_INT_DIVIDE_BY_ZERO";
- break;
- case EXCEPTION_INT_OVERFLOW:
- return "EXCEPTION_INT_OVERFLOW";
- break;
- case EXCEPTION_PRIV_INSTRUCTION:
- return "EXCEPTION_PRIV_INSTRUCTION";
- break;
- case EXCEPTION_IN_PAGE_ERROR:
- return "EXCEPTION_IN_PAGE_ERROR";
- break;
- case EXCEPTION_ILLEGAL_INSTRUCTION:
- return "EXCEPTION_ILLEGAL_INSTRUCTION";
- break;
- case EXCEPTION_NONCONTINUABLE_EXCEPTION:
- return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
- break;
- case EXCEPTION_STACK_OVERFLOW:
- return "EXCEPTION_STACK_OVERFLOW";
- break;
- case EXCEPTION_INVALID_DISPOSITION:
- return "EXCEPTION_INVALID_DISPOSITION";
- break;
- case EXCEPTION_GUARD_PAGE:
- return "EXCEPTION_GUARD_PAGE";
- break;
- case EXCEPTION_INVALID_HANDLE:
- return "EXCEPTION_INVALID_HANDLE";
- break;
- //case EXCEPTION_POSSIBLE_DEADLOCK: return "EXCEPTION_POSSIBLE_DEADLOCK"; break ;
- case STATUS_FLOAT_MULTIPLE_FAULTS:
- return "STATUS_FLOAT_MULTIPLE_FAULTS";
- break;
- case STATUS_FLOAT_MULTIPLE_TRAPS:
- return "STATUS_FLOAT_MULTIPLE_TRAPS";
- break;
- #endif
- default:
- return "Unknown";
- break;
- }
- }
- void IDebugCallStack::PutVersion(char* str, size_t length)
- {
- AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
- if (!gEnv || !gEnv->pSystem)
- {
- return;
- }
- char sFileVersion[128];
- gEnv->pSystem->GetFileVersion().ToString(sFileVersion, sizeof(sFileVersion));
- char sProductVersion[128];
- gEnv->pSystem->GetProductVersion().ToString(sProductVersion, sizeof(sFileVersion));
- //! Get time.
- time_t ltime;
- time(<ime);
- tm* today = localtime(<ime);
- char s[1024];
- //! Use strftime to build a customized time string.
- strftime(s, 128, "Logged at %c\n", today);
- azstrcat(str, length, s);
- sprintf_s(s, "FileVersion: %s\n", sFileVersion);
- azstrcat(str, length, s);
- sprintf_s(s, "ProductVersion: %s\n", sProductVersion);
- azstrcat(str, length, s);
- if (gEnv->pLog)
- {
- const char* logfile = gEnv->pLog->GetFileName();
- if (logfile)
- {
- azsnprintf (s, AZ_ARRAY_SIZE(s), "LogFile: %s\n", logfile);
- azstrcat(str, length, s);
- }
- }
- AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
- azstrcat(str, length, "ProjectDir: ");
- azstrcat(str, length, projectPath.c_str());
- azstrcat(str, length, "\n");
- #if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_APPEND_MODULENAME
- AZ::Utils::GetExecutablePath(s, sizeof(s));
-
- // Log EXE filename only if possible (not full EXE path which could contain sensitive info)
- AZStd::string exeName;
- if (AZ::StringFunc::Path::GetFullFileName(s, exeName))
- {
- azstrcat(str, length, "Executable: ");
- azstrcat(str, length, exeName.c_str());
- # ifdef AZ_DEBUG_BUILD
- azstrcat(str, length, " (debug: yes");
- # else
- azstrcat(str, length, " (debug: no");
- # endif
- }
- #endif
- AZ_POP_DISABLE_WARNING
- }
- //Crash the application, in this way the debug callstack routine will be called and it will create all the necessary files (error.log, dump, and eventually screenshot)
- void IDebugCallStack::FatalError(const char* description)
- {
- m_bIsFatalError = true;
- WriteLineToLog(description);
- #ifndef _RELEASE
- bool bShowDebugScreen = g_cvars.sys_no_crash_dialog == 0;
- // showing the debug screen is not safe when not called from mainthread
- // it normally leads to a infinity recursion followed by a stack overflow, preventing
- // useful call stacks, thus they are disabled
- bShowDebugScreen = bShowDebugScreen && gEnv->mMainThreadId == CryGetCurrentThreadId();
- if (bShowDebugScreen)
- {
- EBUS_EVENT(AZ::NativeUI::NativeUIRequestBus, DisplayOkDialog, "Open 3D Engine Fatal Error", description, false);
- }
- #endif
- #if defined(WIN32) || !defined(_RELEASE)
- int* p = 0x0;
- *p = 1; // we're intentionally crashing here
- #endif
- }
- void IDebugCallStack::WriteLineToLog(const char* format, ...)
- {
- va_list ArgList;
- char szBuffer[MAX_WARNING_LENGTH];
- va_start(ArgList, format);
- vsnprintf_s(szBuffer, sizeof(szBuffer), sizeof(szBuffer) - 1, format, ArgList);
- azstrcat(szBuffer, MAX_WARNING_LENGTH, "\n");
- szBuffer[sizeof(szBuffer) - 1] = '\0';
- va_end(ArgList);
- AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
- AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle);
- if (fileHandle != AZ::IO::InvalidHandle)
- {
- AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, szBuffer, strlen(szBuffer));
- AZ::IO::FileIOBase::GetDirectInstance()->Flush(fileHandle);
- AZ::IO::FileIOBase::GetDirectInstance()->Close(fileHandle);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void IDebugCallStack::StartMemLog()
- {
- AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle);
- assert(m_memAllocFileHandle != AZ::IO::InvalidHandle);
- }
- //////////////////////////////////////////////////////////////////////////
- void IDebugCallStack::StopMemLog()
- {
- if (m_memAllocFileHandle != AZ::IO::InvalidHandle)
- {
- AZ::IO::FileIOBase::GetDirectInstance()->Close(m_memAllocFileHandle);
- m_memAllocFileHandle = AZ::IO::InvalidHandle;
- }
- }
- //#endif //!defined(LINUX)
|