Tier0: crash handler reliability improvements

* Break out of loop when we found and cached the primary graphics device.
* Log total and available ram with more accuracy.
* (New) log total and available disk space the game is being ran from.
* Cache hardware details such as disk space, ram space and graphics device so the crash callback could also use it.
* Calculate highest XMM number as unsigned so an ABS call is not needed.
* Make the crash callback const.
* Provide pointer to instance to the crash callback.
* Fix crash callback not running properly; it should always be ran in the context of the current crash, not after. The callback is now ran after the crashmsg application has been started.
This commit is contained in:
Kawe Mazidjatari 2024-09-13 20:01:40 +02:00
parent 7ef0a77bb9
commit 98f4f57afe
3 changed files with 58 additions and 16 deletions

View File

@ -38,7 +38,7 @@ static HMODULE s_hModuleHandle = NULL;
// UTILITY
//#############################################################################
void Crash_Callback()
void Crash_Callback(const CCrashHandler* handler)
{
// Shutdown SpdLog to flush all buffers.
SpdLog_Shutdown();

View File

@ -4,6 +4,26 @@
#define CRASHMESSAGE_MSG_EXECUTABLE "bin\\crashmsg.exe"
class CCrashHandler;
typedef void (*CrashCallback_t)(const CCrashHandler* handler);
struct CrashHardWareInfo_s
{
CrashHardWareInfo_s()
{
displayDevice = { sizeof(displayDevice), {0} };
memoryStatus = { sizeof(memoryStatus) , {0} };
availDiskSpace = 0;
totalDiskSpace = 0;
}
DISPLAY_DEVICE displayDevice;
MEMORYSTATUSEX memoryStatus;
size_t totalDiskSpace;
size_t availDiskSpace;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
@ -29,6 +49,12 @@ public:
inline bool IsValid() const { return m_hExceptionHandler != nullptr; };
inline bool CrashingModuleNameKnown() const { return m_CrashingModule.Length() > 0; }
inline const char* GetCrashingModuleName() const { return m_CrashingModule.String(); }
inline const EXCEPTION_POINTERS* GetExceptionPointers() const { return m_pExceptionPointers; }
inline const CrashHardWareInfo_s& GetHardwareInfo() const { return m_HardWareInfo; }
//-------------------------------------------------------------------------
// Formatters:
//-------------------------------------------------------------------------
@ -46,13 +72,13 @@ public:
const char* ExceptionToString(const DWORD nExceptionCode) const;
void SetExceptionPointers(EXCEPTION_POINTERS* const pExceptionPointers) { m_pExceptionPointers = pExceptionPointers; }
void SetCrashCallback(PVOID pCrashCallback) { m_pCrashCallback = pCrashCallback; }
void SetCrashCallback(CrashCallback_t pCrashCallback) { m_pCrashCallback = pCrashCallback; }
void CaptureCallStack();
void WriteFile();
void CreateMessageProcess();
void CrashCallback();
void CrashCallback() const;
private:
@ -83,7 +109,7 @@ private:
HMODULE m_ppModuleHandles[MAX_MODULE_HANDLES];
// Custom crash callback that's called after the logs have been written.
PVOID m_pCrashCallback;
CrashCallback_t m_pCrashCallback;
// Current exception handler, only kept here for tracking as we need the
// handle if we want to remove this handler.
@ -109,6 +135,9 @@ private:
// Set when crashmsg.exe is created to prevent recursive messages.
bool m_bMessageCreated;
// Cached hardware info this application crashed on.
CrashHardWareInfo_s m_HardWareInfo;
mutable RTL_SRWLOCK m_Lock;
};

View File

@ -171,9 +171,10 @@ void CCrashHandler::FormatSystemInfo()
m_Buffer.AppendFormat("\tcpu_model = \"%s\"\n", pi.m_szProcessorBrand);
m_Buffer.AppendFormat("\tcpu_speed = %010lld // clock cycles\n", pi.m_Speed);
DISPLAY_DEVICE& dd = m_HardWareInfo.displayDevice;
for (DWORD i = 0; ; i++)
{
DISPLAY_DEVICE dd = { sizeof(dd), {0} };
const BOOL f = EnumDisplayDevices(NULL, i, &dd, EDD_GET_DEVICE_INTERFACE_NAME);
if (!f)
@ -185,16 +186,28 @@ void CCrashHandler::FormatSystemInfo()
{
m_Buffer.AppendFormat("\tgpu_model = \"%s\"\n", dd.DeviceString);
m_Buffer.AppendFormat("\tgpu_flags = 0x%08X // primary device\n", dd.StateFlags);
break;
}
}
MEMORYSTATUSEX statex{};
statex.dwLength = sizeof(statex);
MEMORYSTATUSEX& statex = m_HardWareInfo.memoryStatus;
if (GlobalMemoryStatusEx(&statex))
{
m_Buffer.AppendFormat("\tram_total = [%010d, %010d] // physical/virtual (MiB)\n", (statex.ullTotalPhys / 1024) / 1024, (statex.ullTotalVirtual / 1024) / 1024);
m_Buffer.AppendFormat("\tram_avail = [%010d, %010d] // physical/virtual (MiB)\n", (statex.ullAvailPhys / 1024) / 1024, (statex.ullAvailVirtual / 1024) / 1024);
m_Buffer.AppendFormat("\tram_total = [%.2lf, %.2lf] // physical/virtual (MiB)\n", (f64)(statex.ullTotalPhys / (1024.0 * 1024.0)), (f64)(statex.ullTotalVirtual / (1024.0 * 1024.0)));
m_Buffer.AppendFormat("\tram_avail = [%.2lf, %.2lf] // physical/virtual (MiB)\n", (f64)(statex.ullAvailPhys / (1024.0 * 1024.0)), (f64)(statex.ullAvailVirtual / (1024.0 * 1024.0)));
}
DWORD sectorsPerCluster, bytesPerSector, freeClusters, totalClusters;
if (GetDiskFreeSpaceA(NULL, &sectorsPerCluster, &bytesPerSector, &freeClusters, &totalClusters))
{
m_HardWareInfo.totalDiskSpace = (u64)totalClusters * sectorsPerCluster * bytesPerSector;
m_HardWareInfo.availDiskSpace = (u64)freeClusters * sectorsPerCluster * bytesPerSector;
m_Buffer.AppendFormat("\tdsk_total = %.2lf // (MiB)\n", (f64)(m_HardWareInfo.totalDiskSpace / (1024.0 * 1024.0)));
m_Buffer.AppendFormat("\tdsk_avail = %.2lf // (MiB)\n", (f64)(m_HardWareInfo.availDiskSpace / (1024.0 * 1024.0)));
}
m_Buffer.Append("}\n");
@ -366,7 +379,7 @@ void CCrashHandler::FormatFPU(const char* const pszRegister, const M128A* const
*reinterpret_cast<const FLOAT*>(&nVec[2]),
*reinterpret_cast<const FLOAT*>(&nVec[3]));
const LONG nHighest = abs(LONG(*MaxElementABS(std::begin(nVec), std::end(nVec))));
const DWORD nHighest = *MaxElementABS(std::begin(nVec), std::end(nVec));
if (nHighest >= 1000000)
{
@ -531,11 +544,11 @@ void CCrashHandler::CreateMessageProcess()
//-----------------------------------------------------------------------------
// Purpose: calls the crash callback
//-----------------------------------------------------------------------------
void CCrashHandler::CrashCallback()
void CCrashHandler::CrashCallback() const
{
if (m_pCrashCallback)
{
((void(*)(void))m_pCrashCallback)();
m_pCrashCallback(this);
}
}
@ -548,11 +561,9 @@ long __stdcall BottomLevelExceptionFilter(EXCEPTION_POINTERS* const pExceptionIn
{
g_CrashHandler.Start();
// If the exception couldn't be handled, run the crash callback and
// terminate the process
// If the exception couldn't be handled, terminate the process
if (g_CrashHandler.GetExit())
{
g_CrashHandler.CrashCallback();
ExitProcess(EXIT_FAILURE);
}
@ -590,6 +601,9 @@ long __stdcall BottomLevelExceptionFilter(EXCEPTION_POINTERS* const pExceptionIn
// Display the message to the user.
g_CrashHandler.CreateMessageProcess();
// Run the crash callback
g_CrashHandler.CrashCallback();
// End it here, the next recursive call terminates the process.
g_CrashHandler.End();
@ -625,7 +639,6 @@ void CCrashHandler::Reset()
m_Buffer.Clear();
m_CrashingModule.Clear();
m_MessageCmdLine.Clear();
m_nCrashMsgFlags = 0;
}