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'.
This commit is contained in:
Kawe Mazidjatari 2024-09-13 20:17:54 +02:00
parent 98f4f57afe
commit cb61ff2875
4 changed files with 76 additions and 4 deletions

View File

@ -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()

View File

@ -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

View File

@ -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"

View File

@ -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);
}