mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Merge pull request #126 from Mauler125/backtrace
Implement crash reporting system (Backtrace)
This commit is contained in:
commit
ae60220a00
@ -53,7 +53,7 @@ typedef float float32;
|
|||||||
typedef double float64;
|
typedef double float64;
|
||||||
|
|
||||||
typedef float32 f32;
|
typedef float32 f32;
|
||||||
typedef float32 f64;
|
typedef float64 f64;
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// 8-bit <--> 64-bit wide boolean type
|
// 8-bit <--> 64-bit wide boolean type
|
||||||
typedef int8 b8;
|
typedef int8 b8;
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "tier0/basetypes.h"
|
#include "tier0/basetypes.h"
|
||||||
#include "tier0/crashhandler.h"
|
#include "tier0/crashhandler.h"
|
||||||
#include "tier0/commandline.h"
|
#include "tier0/commandline.h"
|
||||||
|
#include "tier2/crashreporter.h"
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
#ifndef DEDICATED
|
#ifndef DEDICATED
|
||||||
#include "windows/id3dx.h"
|
#include "windows/id3dx.h"
|
||||||
@ -38,12 +39,10 @@ static HMODULE s_hModuleHandle = NULL;
|
|||||||
// UTILITY
|
// UTILITY
|
||||||
//#############################################################################
|
//#############################################################################
|
||||||
|
|
||||||
void Crash_Callback()
|
void Crash_Callback(const CCrashHandler* handler)
|
||||||
{
|
{
|
||||||
// Shutdown SpdLog to flush all buffers.
|
CrashReporter_SubmitToCollector(handler);
|
||||||
SpdLog_Shutdown();
|
SpdLog_Shutdown(); // Shutdown SpdLog to flush all buffers.
|
||||||
|
|
||||||
// TODO[ AMOS ]: This is where we want to call backtrace from.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Show_Emblem()
|
void Show_Emblem()
|
||||||
|
@ -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 model identifier", pi.m_szProcessorBrand);
|
||||||
Msg(eDLL_T::NONE, "%-25s: '%s'\n","CPU vendor tag", pi.m_szProcessorID);
|
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: '%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", "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", "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);
|
Msg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L3 cache", "(KiB)", pi.m_nL3CacheSizeKb, pi.m_nL3CacheDesc);
|
||||||
|
@ -4,6 +4,26 @@
|
|||||||
|
|
||||||
#define CRASHMESSAGE_MSG_EXECUTABLE "bin\\crashmsg.exe"
|
#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:
|
// Purpose:
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -29,6 +49,12 @@ public:
|
|||||||
|
|
||||||
inline bool IsValid() const { return m_hExceptionHandler != nullptr; };
|
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:
|
// Formatters:
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
@ -46,13 +72,13 @@ public:
|
|||||||
const char* ExceptionToString(const DWORD nExceptionCode) const;
|
const char* ExceptionToString(const DWORD nExceptionCode) const;
|
||||||
|
|
||||||
void SetExceptionPointers(EXCEPTION_POINTERS* const pExceptionPointers) { m_pExceptionPointers = pExceptionPointers; }
|
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 CaptureCallStack();
|
||||||
void WriteFile();
|
void WriteFile();
|
||||||
|
|
||||||
void CreateMessageProcess();
|
void CreateMessageProcess();
|
||||||
void CrashCallback();
|
void CrashCallback() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -83,7 +109,7 @@ private:
|
|||||||
HMODULE m_ppModuleHandles[MAX_MODULE_HANDLES];
|
HMODULE m_ppModuleHandles[MAX_MODULE_HANDLES];
|
||||||
|
|
||||||
// Custom crash callback that's called after the logs have been written.
|
// 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
|
// Current exception handler, only kept here for tracking as we need the
|
||||||
// handle if we want to remove this handler.
|
// handle if we want to remove this handler.
|
||||||
@ -110,6 +136,9 @@ private:
|
|||||||
// Set when crashmsg.exe is created to prevent recursive messages.
|
// Set when crashmsg.exe is created to prevent recursive messages.
|
||||||
bool m_bMessageCreated;
|
bool m_bMessageCreated;
|
||||||
|
|
||||||
|
// Cached hardware info this application crashed on.
|
||||||
|
CrashHardWareInfo_s m_HardWareInfo;
|
||||||
|
|
||||||
mutable RTL_SRWLOCK m_Lock;
|
mutable RTL_SRWLOCK m_Lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,9 +94,17 @@ string Format(const char* szFormat, ...);
|
|||||||
template <typename Iter, typename Compare>
|
template <typename Iter, typename Compare>
|
||||||
Iter ExtremeElementABS(Iter first, Iter last, Compare compare)
|
Iter ExtremeElementABS(Iter first, Iter last, Compare compare)
|
||||||
{
|
{
|
||||||
auto abs_compare = [compare](LONG a, LONG b)
|
using ValueType = typename std::iterator_traits<Iter>::value_type;
|
||||||
|
auto abs_compare = [compare](ValueType a, ValueType b)
|
||||||
{
|
{
|
||||||
return compare(abs(a), abs(b));
|
if constexpr (std::is_signed_v<ValueType>)
|
||||||
|
{
|
||||||
|
return compare(abs(a), abs(b));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return compare(a, b);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return std::min_element(first, last, abs_compare);
|
return std::min_element(first, last, abs_compare);
|
||||||
|
11
src/public/tier2/crashreporter.h
Normal file
11
src/public/tier2/crashreporter.h
Normal 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
|
@ -12,21 +12,24 @@ struct CURLProgress
|
|||||||
|
|
||||||
CURL* curl;
|
CURL* curl;
|
||||||
const char* name;
|
const char* name;
|
||||||
void* cust; // custom pointer to anything.
|
void* cust; // custom pointer to anything, todo(amos): rename to 'user'.
|
||||||
size_t size;
|
size_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CURLParams
|
struct CURLParams
|
||||||
{
|
{
|
||||||
CURLParams()
|
CURLParams()
|
||||||
: writeFunction(nullptr)
|
: readFunction(nullptr)
|
||||||
|
, writeFunction(nullptr)
|
||||||
, statusFunction(nullptr)
|
, statusFunction(nullptr)
|
||||||
, timeout(0)
|
, timeout(0)
|
||||||
, verifyPeer(false)
|
, verifyPeer(false)
|
||||||
, followRedirect(false)
|
, followRedirect(false)
|
||||||
, verbose(false)
|
, verbose(false)
|
||||||
|
, failOnError(true)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
void* readFunction;
|
||||||
void* writeFunction;
|
void* writeFunction;
|
||||||
void* statusFunction;
|
void* statusFunction;
|
||||||
|
|
||||||
@ -34,11 +37,17 @@ struct CURLParams
|
|||||||
bool verifyPeer;
|
bool verifyPeer;
|
||||||
bool followRedirect;
|
bool followRedirect;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
|
bool failOnError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 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,
|
bool CURLDownloadFile(const char* remote, const char* savePath, const char* fileName,
|
||||||
const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params);
|
const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params);
|
||||||
|
|
||||||
|
@ -171,9 +171,10 @@ void CCrashHandler::FormatSystemInfo()
|
|||||||
m_Buffer.AppendFormat("\tcpu_model = \"%s\"\n", pi.m_szProcessorBrand);
|
m_Buffer.AppendFormat("\tcpu_model = \"%s\"\n", pi.m_szProcessorBrand);
|
||||||
m_Buffer.AppendFormat("\tcpu_speed = %010lld // clock cycles\n", pi.m_Speed);
|
m_Buffer.AppendFormat("\tcpu_speed = %010lld // clock cycles\n", pi.m_Speed);
|
||||||
|
|
||||||
|
DISPLAY_DEVICE& dd = m_HardWareInfo.displayDevice;
|
||||||
|
|
||||||
for (DWORD i = 0; ; i++)
|
for (DWORD i = 0; ; i++)
|
||||||
{
|
{
|
||||||
DISPLAY_DEVICE dd = { sizeof(dd), {0} };
|
|
||||||
const BOOL f = EnumDisplayDevices(NULL, i, &dd, EDD_GET_DEVICE_INTERFACE_NAME);
|
const BOOL f = EnumDisplayDevices(NULL, i, &dd, EDD_GET_DEVICE_INTERFACE_NAME);
|
||||||
|
|
||||||
if (!f)
|
if (!f)
|
||||||
@ -185,16 +186,28 @@ void CCrashHandler::FormatSystemInfo()
|
|||||||
{
|
{
|
||||||
m_Buffer.AppendFormat("\tgpu_model = \"%s\"\n", dd.DeviceString);
|
m_Buffer.AppendFormat("\tgpu_model = \"%s\"\n", dd.DeviceString);
|
||||||
m_Buffer.AppendFormat("\tgpu_flags = 0x%08X // primary device\n", dd.StateFlags);
|
m_Buffer.AppendFormat("\tgpu_flags = 0x%08X // primary device\n", dd.StateFlags);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MEMORYSTATUSEX statex{};
|
MEMORYSTATUSEX& statex = m_HardWareInfo.memoryStatus;
|
||||||
statex.dwLength = sizeof(statex);
|
|
||||||
|
|
||||||
if (GlobalMemoryStatusEx(&statex))
|
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_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 = [%010d, %010d] // physical/virtual (MiB)\n", (statex.ullAvailPhys / 1024) / 1024, (statex.ullAvailVirtual / 1024) / 1024);
|
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");
|
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[2]),
|
||||||
*reinterpret_cast<const FLOAT*>(&nVec[3]));
|
*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)
|
if (nHighest >= 1000000)
|
||||||
{
|
{
|
||||||
@ -531,11 +544,11 @@ void CCrashHandler::CreateMessageProcess()
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose: calls the crash callback
|
// Purpose: calls the crash callback
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void CCrashHandler::CrashCallback()
|
void CCrashHandler::CrashCallback() const
|
||||||
{
|
{
|
||||||
if (m_pCrashCallback)
|
if (m_pCrashCallback)
|
||||||
{
|
{
|
||||||
((void(*)(void))m_pCrashCallback)();
|
m_pCrashCallback(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,11 +561,9 @@ long __stdcall BottomLevelExceptionFilter(EXCEPTION_POINTERS* const pExceptionIn
|
|||||||
{
|
{
|
||||||
g_CrashHandler.Start();
|
g_CrashHandler.Start();
|
||||||
|
|
||||||
// If the exception couldn't be handled, run the crash callback and
|
// If the exception couldn't be handled, terminate the process
|
||||||
// terminate the process
|
|
||||||
if (g_CrashHandler.GetExit())
|
if (g_CrashHandler.GetExit())
|
||||||
{
|
{
|
||||||
g_CrashHandler.CrashCallback();
|
|
||||||
ExitProcess(EXIT_FAILURE);
|
ExitProcess(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,6 +601,9 @@ long __stdcall BottomLevelExceptionFilter(EXCEPTION_POINTERS* const pExceptionIn
|
|||||||
// Display the message to the user.
|
// Display the message to the user.
|
||||||
g_CrashHandler.CreateMessageProcess();
|
g_CrashHandler.CreateMessageProcess();
|
||||||
|
|
||||||
|
// Run the crash callback
|
||||||
|
g_CrashHandler.CrashCallback();
|
||||||
|
|
||||||
// End it here, the next recursive call terminates the process.
|
// End it here, the next recursive call terminates the process.
|
||||||
g_CrashHandler.End();
|
g_CrashHandler.End();
|
||||||
|
|
||||||
@ -625,7 +639,6 @@ void CCrashHandler::Reset()
|
|||||||
m_Buffer.Clear();
|
m_Buffer.Clear();
|
||||||
m_CrashingModule.Clear();
|
m_CrashingModule.Clear();
|
||||||
m_MessageCmdLine.Clear();
|
m_MessageCmdLine.Clear();
|
||||||
|
|
||||||
m_nCrashMsgFlags = 0;
|
m_nCrashMsgFlags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ add_module( "lib" "tier2" "vpc" ${FOLDER_CONTEXT} TRUE TRUE )
|
|||||||
start_sources()
|
start_sources()
|
||||||
|
|
||||||
add_sources( SOURCE_GROUP "Utility"
|
add_sources( SOURCE_GROUP "Utility"
|
||||||
|
"crashreporter.cpp"
|
||||||
"cryptutils.cpp"
|
"cryptutils.cpp"
|
||||||
"curlutils.cpp"
|
"curlutils.cpp"
|
||||||
"fileutils.cpp"
|
"fileutils.cpp"
|
||||||
|
61
src/tier2/crashreporter.cpp
Normal file
61
src/tier2/crashreporter.cpp
Normal 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);
|
||||||
|
}
|
@ -6,40 +6,177 @@
|
|||||||
#include "tier1/cvar.h"
|
#include "tier1/cvar.h"
|
||||||
#include "tier2/curlutils.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)
|
size_t CURLWriteStringCallback(char* data, const size_t size, const size_t nmemb, string* userp)
|
||||||
{
|
{
|
||||||
userp->append(data, size * nmemb);
|
userp->append(data, size * nmemb);
|
||||||
return 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,
|
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_TIMEOUT, params.timeout);
|
||||||
curl_easy_setopt(curl, CURLOPT_VERBOSE, params.verbose);
|
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_FOLLOWLOCATION, params.followRedirect);
|
||||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, params.verifyPeer);
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, params.verifyPeer);
|
||||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
|
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
|
||||||
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
|
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, remote);
|
curl_easy_setopt(curl, CURLOPT_URL, remote);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, writeData);
|
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_USERAGENT, "R5R HTTPS/1.0");
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, params.writeFunction);
|
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,
|
static CURL* EasyInit()
|
||||||
const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params)
|
|
||||||
{
|
{
|
||||||
CURL* curl = curl_easy_init();
|
CURL* curl = curl_easy_init();
|
||||||
if (!curl)
|
if (!curl)
|
||||||
{
|
{
|
||||||
Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Easy init failed");
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,12 +185,10 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file
|
|||||||
AppendSlash(filePath);
|
AppendSlash(filePath);
|
||||||
filePath.append(fileName);
|
filePath.append(fileName);
|
||||||
|
|
||||||
FILE* file = fopen(filePath.c_str(), options);
|
FILE* file = OpenFile(filePath.c_str(), options);
|
||||||
if (!file)
|
if (!file)
|
||||||
{
|
{
|
||||||
Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Open file failed");
|
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,14 +199,7 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file
|
|||||||
progressData.cust = customPointer;
|
progressData.cust = customPointer;
|
||||||
progressData.size = dataSize;
|
progressData.size = dataSize;
|
||||||
|
|
||||||
CURLInitCommonOptions(curl, remote, file, params);
|
CURLInitCommonOptions(curl, remote, nullptr, file, params, &progressData);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
CURLcode res = curl_easy_perform(curl);
|
CURLcode res = curl_easy_perform(curl);
|
||||||
|
|
||||||
@ -95,33 +223,25 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file
|
|||||||
CURL* CURLInitRequest(const char* remote, const char* request,
|
CURL* CURLInitRequest(const char* remote, const char* request,
|
||||||
string& outResponse, curl_slist*& slist, const CURLParams& params)
|
string& outResponse, curl_slist*& slist, const CURLParams& params)
|
||||||
{
|
{
|
||||||
std::function<void(const char*)> fnError = [&](const char* errorMsg)
|
slist = CURLSlistAppend(slist, "Content-Type: application/json");
|
||||||
{
|
|
||||||
Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", errorMsg);
|
|
||||||
curl_slist_free_all(slist);
|
|
||||||
};
|
|
||||||
|
|
||||||
slist = curl_slist_append(slist, "Content-Type: application/json");
|
|
||||||
if (!slist)
|
if (!slist)
|
||||||
{
|
{
|
||||||
fnError("Slist init failed");
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CURL* curl = curl_easy_init();
|
CURL* curl = EasyInit();
|
||||||
if (!curl)
|
if (!curl)
|
||||||
{
|
{
|
||||||
fnError("Easy init failed");
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CURLInitCommonOptions(curl, remote, &outResponse, params);
|
CURLInitCommonOptions(curl, remote, nullptr, &outResponse, params, nullptr);
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
|
||||||
if (request)
|
if (request)
|
||||||
{
|
{
|
||||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
return curl;
|
return curl;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user