From 1a551739b42b6f6913017d84ec600c4da7cb2143 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 7 Sep 2024 00:26:48 +0200 Subject: [PATCH 1/7] Core: fix incorrect typedef for f64 (double-precision floating point) f64 was unused but will be used. --- src/common/sdkint.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/sdkint.h b/src/common/sdkint.h index 0fd178a3..1f72374b 100644 --- a/src/common/sdkint.h +++ b/src/common/sdkint.h @@ -53,7 +53,7 @@ typedef float float32; typedef double float64; typedef float32 f32; -typedef float32 f64; +typedef float64 f64; //----------------------------------------------------------------------------- // 8-bit <--> 64-bit wide boolean type typedef int8 b8; From 0f89a4e3db83e58c8e6e36d84fe76ab5981429ad Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:59:16 +0200 Subject: [PATCH 2/7] Tier0: only ABS on signed types in ExtremeElementABS Bug fix. --- src/public/tier0/utility.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/public/tier0/utility.h b/src/public/tier0/utility.h index 3e4c47fa..107daba8 100644 --- a/src/public/tier0/utility.h +++ b/src/public/tier0/utility.h @@ -94,9 +94,17 @@ string Format(const char* szFormat, ...); template Iter ExtremeElementABS(Iter first, Iter last, Compare compare) { - auto abs_compare = [compare](LONG a, LONG b) + using ValueType = typename std::iterator_traits::value_type; + auto abs_compare = [compare](ValueType a, ValueType b) { - return compare(abs(a), abs(b)); + if constexpr (std::is_signed_v) + { + return compare(abs(a), abs(b)); + } + else + { + return compare(a, b); + } }; return std::min_element(first, last, abs_compare); From 9f935ec4b473feca0a6fabffcd06b96a00be5206 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 13 Sep 2024 19:34:33 +0200 Subject: [PATCH 3/7] Tier2: implement cURL upload file utility --- src/public/tier2/curlutils.h | 13 ++- src/tier2/curlutils.cpp | 183 +++++++++++++++++++++++++++++------ 2 files changed, 161 insertions(+), 35 deletions(-) diff --git a/src/public/tier2/curlutils.h b/src/public/tier2/curlutils.h index 66419dd8..6c5e55f7 100644 --- a/src/public/tier2/curlutils.h +++ b/src/public/tier2/curlutils.h @@ -12,14 +12,15 @@ struct CURLProgress CURL* curl; const char* name; - void* cust; // custom pointer to anything. + void* cust; // custom pointer to anything, todo(amos): rename to 'user'. size_t size; }; struct CURLParams { CURLParams() - : writeFunction(nullptr) + : readFunction(nullptr) + , writeFunction(nullptr) , statusFunction(nullptr) , timeout(0) , verifyPeer(false) @@ -27,6 +28,7 @@ struct CURLParams , verbose(false) {} + void* readFunction; void* writeFunction; void* statusFunction; @@ -36,9 +38,14 @@ struct CURLParams bool verbose; }; +size_t CURLReadFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream); +size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream); size_t CURLWriteStringCallback(char* contents, const size_t size, const size_t nmemb, string* userp); -size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* userp); +curl_slist* CURLSlistAppend(curl_slist* slist, const char* string); + +bool CURLUploadFile(const char* remote, const char* filePath, const char* options, + void* customPointer, const bool usePost, const curl_slist* slist, const CURLParams& params); bool CURLDownloadFile(const char* remote, const char* savePath, const char* fileName, const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params); diff --git a/src/tier2/curlutils.cpp b/src/tier2/curlutils.cpp index fd6f08ca..4ac8d991 100644 --- a/src/tier2/curlutils.cpp +++ b/src/tier2/curlutils.cpp @@ -6,20 +6,27 @@ #include "tier1/cvar.h" #include "tier2/curlutils.h" +size_t CURLReadFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream) +{ + const size_t numBytesRead = fread(data, size, nmemb, stream); + return numBytesRead; +} + +size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream) +{ + const size_t numBytesWritten = fwrite(data, size, nmemb, stream); + return numBytesWritten; +} + size_t CURLWriteStringCallback(char* data, const size_t size, const size_t nmemb, string* userp) { userp->append(data, size * nmemb); return size * nmemb; } -size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* userp) -{ - const size_t numBytesWritten = fwrite(data, size, nmemb, userp); - return numBytesWritten; -} - void CURLInitCommonOptions(CURL* curl, const char* remote, - const void* writeData, const CURLParams& params) + const void* readData, const void* writeData, + const CURLParams& params, const CURLProgress* progressData) { curl_easy_setopt(curl, CURLOPT_TIMEOUT, params.timeout); curl_easy_setopt(curl, CURLOPT_VERBOSE, params.verbose); @@ -29,17 +36,146 @@ void CURLInitCommonOptions(CURL* curl, const char* remote, curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); curl_easy_setopt(curl, CURLOPT_URL, remote); curl_easy_setopt(curl, CURLOPT_WRITEDATA, writeData); + curl_easy_setopt(curl, CURLOPT_READDATA, readData); curl_easy_setopt(curl, CURLOPT_USERAGENT, "R5R HTTPS/1.0"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, params.writeFunction); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, params.readFunction); + + if (params.statusFunction) + { + Assert(progressData); + + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0l); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progressData); + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, params.statusFunction); + } } -bool CURLDownloadFile(const char* remote, const char* savePath, const char* fileName, - const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params) +static CURL* EasyInit() { CURL* curl = curl_easy_init(); if (!curl) { Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Easy init failed"); + } + + return curl; +} + +static FILE* OpenFile(const char* filePath, const char* options) +{ + FILE* file = fopen(filePath, options); + if (!file) + { + Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Open file failed"); + } + + return file; +} + +static bool FileStat(const char* fileName, struct _stat64& stat) +{ + if (_stat64(fileName, &stat) != 0) + { + Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "File status query failed"); + return false; + } + + return true; +} + +curl_slist* CURLSlistAppend(curl_slist* slist, const char* string) +{ + slist = curl_slist_append(slist, string); + if (!slist) + { + Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Slist append failed"); + } + + return slist; +} + +bool CURLUploadFile(const char* remote, const char* filePath, + const char* options, void* customPointer, const bool usePost, + const curl_slist* slist, const CURLParams& params) +{ + CURL* curl = EasyInit(); + if (!curl) + { + return false; + } + + FILE* file = OpenFile(filePath, options); + if (!file) + { + return false; + } + + struct _stat64 fileStatus; + if (!FileStat(filePath, fileStatus)) + { + fclose(file); + return false; + } + + CURLProgress progressData; + + progressData.curl = curl; + progressData.name = filePath; + progressData.cust = customPointer; + progressData.size = fileStatus.st_size; + + string response; + CURLInitCommonOptions(curl, remote, file, &response, params, &progressData); + + if (slist) + { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); + } + + const bool largeFile = fileStatus.st_size > INT_MAX; + CURLoption fileSizeOption; + + if (usePost) + { + curl_easy_setopt(curl, CURLOPT_POST, 1L); + + fileSizeOption = largeFile + ? CURLOPT_POSTFIELDSIZE_LARGE + : CURLOPT_POSTFIELDSIZE; + } + else + { + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + fileSizeOption = largeFile + ? CURLOPT_INFILESIZE_LARGE + : CURLOPT_INFILESIZE; + } + + curl_easy_setopt(curl, fileSizeOption, (curl_off_t)fileStatus.st_size); + + CURLcode res = curl_easy_perform(curl); + const bool success = res == CURLE_OK; + + if (!success) + { + Error(eDLL_T::COMMON, NO_ERROR, "CURL: Upload of file '%s' failed; %s\n", + filePath, curl_easy_strerror(res)); + } + + curl_easy_cleanup(curl); + fclose(file); + + return success; +} + +bool CURLDownloadFile(const char* remote, const char* savePath, const char* fileName, + const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params) +{ + CURL* curl = EasyInit(); + if (!curl) + { return false; } @@ -48,12 +184,10 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file AppendSlash(filePath); filePath.append(fileName); - FILE* file = fopen(filePath.c_str(), options); + FILE* file = OpenFile(filePath.c_str(), options); if (!file) { - Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Open file failed"); curl_easy_cleanup(curl); - return false; } @@ -64,14 +198,7 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file progressData.cust = customPointer; progressData.size = dataSize; - CURLInitCommonOptions(curl, remote, file, params); - - if (params.statusFunction) - { - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0l); - curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progressData); - curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, params.statusFunction); - } + CURLInitCommonOptions(curl, remote, nullptr, file, params, &progressData); CURLcode res = curl_easy_perform(curl); @@ -95,33 +222,25 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file CURL* CURLInitRequest(const char* remote, const char* request, string& outResponse, curl_slist*& slist, const CURLParams& params) { - std::function fnError = [&](const char* errorMsg) - { - Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", errorMsg); - curl_slist_free_all(slist); - }; - - slist = curl_slist_append(slist, "Content-Type: application/json"); + slist = CURLSlistAppend(slist, "Content-Type: application/json"); if (!slist) { - fnError("Slist init failed"); return nullptr; } - CURL* curl = curl_easy_init(); + CURL* curl = EasyInit(); if (!curl) { - fnError("Easy init failed"); return nullptr; } - CURLInitCommonOptions(curl, remote, &outResponse, params); + CURLInitCommonOptions(curl, remote, nullptr, &outResponse, params, nullptr); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); if (request) { - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); } return curl; From fa5a8f8be882fa220a43fcf663ccfea1c96142d6 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 13 Sep 2024 19:36:13 +0200 Subject: [PATCH 4/7] Tier2: add option to fail on error in cURL params --- src/public/tier2/curlutils.h | 2 ++ src/tier2/curlutils.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/public/tier2/curlutils.h b/src/public/tier2/curlutils.h index 6c5e55f7..e74c628e 100644 --- a/src/public/tier2/curlutils.h +++ b/src/public/tier2/curlutils.h @@ -26,6 +26,7 @@ struct CURLParams , verifyPeer(false) , followRedirect(false) , verbose(false) + , failOnError(true) {} void* readFunction; @@ -36,6 +37,7 @@ struct CURLParams bool verifyPeer; bool followRedirect; bool verbose; + bool failOnError; }; size_t CURLReadFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream); diff --git a/src/tier2/curlutils.cpp b/src/tier2/curlutils.cpp index 4ac8d991..0fa74a9b 100644 --- a/src/tier2/curlutils.cpp +++ b/src/tier2/curlutils.cpp @@ -30,6 +30,7 @@ void CURLInitCommonOptions(CURL* curl, const char* remote, { curl_easy_setopt(curl, CURLOPT_TIMEOUT, params.timeout); curl_easy_setopt(curl, CURLOPT_VERBOSE, params.verbose); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, params.failOnError); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, params.followRedirect); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, params.verifyPeer); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); From 7ef0a77bb90c42924b1566c1fd7035db75936848 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 13 Sep 2024 19:44:18 +0200 Subject: [PATCH 5/7] Core: improve CPU clock speeds formatting Same length but the fractional component shows clock speed with more accuracy now. --- src/core/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/init.cpp b/src/core/init.cpp index 11138c1a..a45be9dc 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -401,7 +401,7 @@ void QuerySystemInfo() Msg(eDLL_T::NONE, "%-25s: '%s'\n","CPU model identifier", pi.m_szProcessorBrand); Msg(eDLL_T::NONE, "%-25s: '%s'\n","CPU vendor tag", pi.m_szProcessorID); Msg(eDLL_T::NONE, "%-25s: '%12hhu' ('%2hhu' %s)\n", "CPU core count", pi.m_nPhysicalProcessors, pi.m_nLogicalProcessors, "logical"); - Msg(eDLL_T::NONE, "%-25s: '%12lld' ('%6.1f' %s)\n", "CPU core speed", pi.m_Speed, float(pi.m_Speed / 1000000), "MHz"); + Msg(eDLL_T::NONE, "%-25s: '%12lld' ('%.1lf' %s)\n", "CPU core speed", pi.m_Speed, f64(pi.m_Speed / 1000000.0), "MHz"); Msg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L1 cache", "(KiB)", pi.m_nL1CacheSizeKb, pi.m_nL1CacheDesc); Msg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L2 cache", "(KiB)", pi.m_nL2CacheSizeKb, pi.m_nL2CacheDesc); Msg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L3 cache", "(KiB)", pi.m_nL3CacheSizeKb, pi.m_nL3CacheDesc); From 98f4f57afea15ef703e8a9814931b7f283233c75 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 13 Sep 2024 20:01:40 +0200 Subject: [PATCH 6/7] 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. --- src/core/dllmain.cpp | 2 +- src/public/tier0/crashhandler.h | 35 ++++++++++++++++++++++++++++--- src/tier0/crashhandler.cpp | 37 ++++++++++++++++++++++----------- 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/core/dllmain.cpp b/src/core/dllmain.cpp index 819030ac..673940e2 100644 --- a/src/core/dllmain.cpp +++ b/src/core/dllmain.cpp @@ -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(); diff --git a/src/public/tier0/crashhandler.h b/src/public/tier0/crashhandler.h index c07527bb..e94fe96c 100644 --- a/src/public/tier0/crashhandler.h +++ b/src/public/tier0/crashhandler.h @@ -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; }; diff --git a/src/tier0/crashhandler.cpp b/src/tier0/crashhandler.cpp index ff6da03a..32d225b0 100644 --- a/src/tier0/crashhandler.cpp +++ b/src/tier0/crashhandler.cpp @@ -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, §orsPerCluster, &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(&nVec[2]), *reinterpret_cast(&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; } From cb61ff287574deeb25362e7d44658f16e5f91bd3 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 13 Sep 2024 20:17:54 +0200 Subject: [PATCH 7/7] Tier2: implement crash reporter Submit all relevant crash details to the error collection server (backtrace), this allows us to catch all remaining bugs in the engine and sdk and fix them more effectively. The old local log file containing details of the crashing thread and system details are still logged since this system can be disabled with the console variable 'backtrace_enabled'. --- src/core/dllmain.cpp | 7 ++-- src/public/tier2/crashreporter.h | 11 ++++++ src/tier2/CMakeLists.txt | 1 + src/tier2/crashreporter.cpp | 61 ++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/public/tier2/crashreporter.h create mode 100644 src/tier2/crashreporter.cpp diff --git a/src/core/dllmain.cpp b/src/core/dllmain.cpp index 673940e2..e680bd62 100644 --- a/src/core/dllmain.cpp +++ b/src/core/dllmain.cpp @@ -7,6 +7,7 @@ #include "tier0/basetypes.h" #include "tier0/crashhandler.h" #include "tier0/commandline.h" +#include "tier2/crashreporter.h" /*****************************************************************************/ #ifndef DEDICATED #include "windows/id3dx.h" @@ -40,10 +41,8 @@ static HMODULE s_hModuleHandle = NULL; void Crash_Callback(const CCrashHandler* handler) { - // Shutdown SpdLog to flush all buffers. - SpdLog_Shutdown(); - - // TODO[ AMOS ]: This is where we want to call backtrace from. + CrashReporter_SubmitToCollector(handler); + SpdLog_Shutdown(); // Shutdown SpdLog to flush all buffers. } void Show_Emblem() diff --git a/src/public/tier2/crashreporter.h b/src/public/tier2/crashreporter.h new file mode 100644 index 00000000..892c11b2 --- /dev/null +++ b/src/public/tier2/crashreporter.h @@ -0,0 +1,11 @@ +//=============================================================================// +// +// Purpose: Post-mortem crash reporter +// +//=============================================================================// +#ifndef TIER2_CRASHREPORTER_H +#define TIER2_CRASHREPORTER_H + +extern void CrashReporter_SubmitToCollector(const CCrashHandler* const handler); + +#endif // TIER2_CRASHREPORTER_H diff --git a/src/tier2/CMakeLists.txt b/src/tier2/CMakeLists.txt index 2cca1d97..2c3b4b0b 100644 --- a/src/tier2/CMakeLists.txt +++ b/src/tier2/CMakeLists.txt @@ -4,6 +4,7 @@ add_module( "lib" "tier2" "vpc" ${FOLDER_CONTEXT} TRUE TRUE ) start_sources() add_sources( SOURCE_GROUP "Utility" + "crashreporter.cpp" "cryptutils.cpp" "curlutils.cpp" "fileutils.cpp" diff --git a/src/tier2/crashreporter.cpp b/src/tier2/crashreporter.cpp new file mode 100644 index 00000000..81a9b338 --- /dev/null +++ b/src/tier2/crashreporter.cpp @@ -0,0 +1,61 @@ +//=============================================================================// +// +// Purpose: Post-mortem crash reporter +// +//=============================================================================// +#include "tier0/crashhandler.h" +#include "tier0/cpu.h" +#include "tier2/curlutils.h" +#include "tier2/crashreporter.h" + +static ConVar backtrace_enabled("backtrace_enabled", "1", FCVAR_RELEASE, "Whether to report fatal errors to the collection server"); +static ConVar backtrace_hostname("backtrace_hostname", "submit.backtrace.io", FCVAR_RELEASE, "Holds the error collection server hostname"); +static ConVar backtrace_universe("backtrace_universe", "r5reloaded", FCVAR_RELEASE, "Holds the error collection server hosted instance"); +static ConVar backtrace_token("backtrace_token", "f178fd48d89c8fec7f8b6404ae6dae591c330fd3e2599cab888788033944ec98", FCVAR_RELEASE, "Holds the error collection server submission token"); + +static inline string CrashReporter_FormatAttributes(const CCrashHandler* const handler) +{ + const CPUInformation& pi = GetCPUInformation(); + const CrashHardWareInfo_s& hi = handler->GetHardwareInfo(); + + const char* const format = "uuid=%s&" "build_id=%lld&" + "cpu_model=%s&" "cpu_speed=%lf GHz&" "gpu_model=%s&" "gpu_flags=%lu&" + "ram_phys_total=%.2lf MiB&" "ram_phys_avail=%.2lf MiB&" + "ram_virt_total=%.2lf MiB&" "ram_virt_avail=%.2lf MiB&" + "disk_total=%.2lf MiB&" "disk_avail=%.2lf MiB"; + + const DISPLAY_DEVICE& dd = hi.displayDevice; + const MEMORYSTATUSEX& ms = hi.memoryStatus; + + return Format(format, g_LogSessionUUID.c_str(), g_SDKDll.GetNTHeaders()->FileHeader.TimeDateStamp, + pi.m_szProcessorBrand, (f64)(pi.m_Speed / 1000000000.0), dd.DeviceString, dd.StateFlags, + (f64)(ms.ullTotalPhys / (1024.0*1024.0)), (f64)(ms.ullAvailPhys / (1024.0*1024.0)), + (f64)(ms.ullTotalVirtual / (1024.0*1024.0)), (f64)(ms.ullAvailVirtual / (1024.0*1024.0)), + (f64)(hi.totalDiskSpace / (1024.0*1024.0)), (f64)(hi.availDiskSpace / (1024.0*1024.0))); +} + +void CrashReporter_SubmitToCollector(const CCrashHandler* const handler) +{ + if (!backtrace_enabled.GetBool()) + return; + + curl_slist* slist = nullptr; + slist = CURLSlistAppend(slist, "Expect:"); + + if (!slist) + return; // failure. + + const string attributes = CrashReporter_FormatAttributes(handler); + const string hostName = Format("%s%s/%s/%s/%s&%s", + "https://", backtrace_hostname.GetString(), backtrace_universe.GetString(), backtrace_token.GetString(), "minidump", attributes.c_str()); + const string miniDumpPath = Format("%s/%s.dmp", g_LogSessionDirectory.c_str(), "minidump"); + + CURLParams params; + params.readFunction = &CURLReadFileCallback; + params.writeFunction = &CURLWriteStringCallback; + params.timeout = curl_timeout.GetInt(); + params.verifyPeer = ssl_verify_peer.GetBool(); + params.verbose = curl_debug.GetBool(); + + CURLUploadFile(hostName.c_str(), miniDumpPath.c_str(), "rb", nullptr, true, slist, params); +}