mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
The NexusDashboard-parity dashboard (dDashboardServer) and everything built on it on the experimental branch: accounts, characters, properties and moderation tools, permissions shared with in-game slash commands, economy reports, World 3D and property 3D views with client scenery, scheduled events (features, vanity changes, live events, announcements, restarts), vanity files and events, the CDClient browser, the message inspector with saved captures, chat filter tools, community challenges, live ops, the AI moderator helper, and the server-side changes they need. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
276 lines
7.5 KiB
C++
276 lines
7.5 KiB
C++
#include "Diagnostics.h"
|
|
|
|
#include <filesystem>
|
|
#include "Game.h"
|
|
#include "Logger.h"
|
|
|
|
// If we're on Win32, we'll include our minidump writer
|
|
#ifdef _WIN32
|
|
|
|
#include <Windows.h>
|
|
#include <Dbghelp.h>
|
|
|
|
#include "Game.h"
|
|
#include "Logger.h"
|
|
|
|
void make_minidump(EXCEPTION_POINTERS* e) {
|
|
auto hDbgHelp = LoadLibraryA("dbghelp");
|
|
if (hDbgHelp == nullptr)
|
|
return;
|
|
auto pMiniDumpWriteDump = (decltype(&MiniDumpWriteDump))GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
|
|
if (pMiniDumpWriteDump == nullptr)
|
|
return;
|
|
|
|
char name[MAX_PATH];
|
|
{
|
|
auto nameEnd = name + GetModuleFileNameA(GetModuleHandleA(0), name, MAX_PATH);
|
|
SYSTEMTIME t;
|
|
GetSystemTime(&t);
|
|
wsprintfA(nameEnd - strlen(".exe"),
|
|
"_%4d%02d%02d_%02d%02d%02d.dmp",
|
|
t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);
|
|
}
|
|
LOG("Creating crash dump %s", name);
|
|
auto hFile = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
return;
|
|
|
|
MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
|
|
exceptionInfo.ThreadId = GetCurrentThreadId();
|
|
exceptionInfo.ExceptionPointers = e;
|
|
exceptionInfo.ClientPointers = FALSE;
|
|
|
|
auto dumped = pMiniDumpWriteDump(
|
|
GetCurrentProcess(),
|
|
GetCurrentProcessId(),
|
|
hFile,
|
|
MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory),
|
|
e ? &exceptionInfo : nullptr,
|
|
nullptr,
|
|
nullptr);
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return;
|
|
}
|
|
|
|
LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e) {
|
|
make_minidump(e);
|
|
if (Game::logger)
|
|
Game::logger->Flush(); // Flush our log if we have one, before exiting.
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
#endif
|
|
|
|
#if defined(__linux__) //&& !defined(__clang__) // backtrace is a gcc exclusive system library
|
|
#include <execinfo.h>
|
|
#include <ucontext.h>
|
|
#include <unistd.h>
|
|
|
|
#include <csignal>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <exception>
|
|
|
|
#if defined(INCLUDE_BACKTRACE)
|
|
#include <backtrace.h>
|
|
|
|
#include <backtrace-supported.h>
|
|
|
|
struct bt_ctx {
|
|
struct backtrace_state* state;
|
|
int error;
|
|
};
|
|
|
|
static inline void Bt(struct backtrace_state* state) {
|
|
std::string fileName = Diagnostics::GetOutDirectory() + "Crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
|
|
LOG("backtrace is enabled, crash dump located at %s", fileName.c_str());
|
|
FILE* file = fopen(fileName.c_str(), "w+");
|
|
if (file != nullptr) {
|
|
backtrace_print(state, 2, file);
|
|
fclose(file);
|
|
}
|
|
|
|
backtrace_print(state, 2, stdout);
|
|
}
|
|
|
|
static void ErrorCallback(void* data, const char* msg, int errnum) {
|
|
auto* ctx = (struct bt_ctx*)data;
|
|
fprintf(stderr, "ERROR: %s (%d)", msg, errnum);
|
|
ctx->error = 1;
|
|
|
|
std::string fileName = Diagnostics::GetOutDirectory() + "Crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
|
|
FILE* file = fopen(fileName.c_str(), "w+");
|
|
if (file != nullptr) {
|
|
fprintf(file, "ERROR: %s (%d)", msg, errnum);
|
|
fclose(file);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#include "Demangler.h"
|
|
|
|
void GenerateDump() {
|
|
std::string cmd = "sudo gcore " + std::to_string(getpid());
|
|
int ret = system(cmd.c_str()); // Saving a return just to prevent warning
|
|
}
|
|
|
|
void CatchUnhandled(int sig) {
|
|
std::exception_ptr eptr = std::current_exception();
|
|
try {
|
|
if (eptr) std::rethrow_exception(eptr);
|
|
} catch(const std::exception& e) {
|
|
LOG("Caught exception: '%s'", e.what());
|
|
} catch (...) {
|
|
LOG("Caught unknown exception.");
|
|
}
|
|
|
|
#ifndef INCLUDE_BACKTRACE
|
|
|
|
std::string fileName = Diagnostics::GetOutDirectory() + "Crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
|
|
LOG("Encountered signal %i, creating crash dump %s", sig, fileName.c_str());
|
|
if (Diagnostics::GetProduceMemoryDump()) {
|
|
GenerateDump();
|
|
}
|
|
constexpr uint8_t MaxStackTrace = 32;
|
|
void* array[MaxStackTrace];
|
|
size_t size;
|
|
|
|
// get void*'s for all entries on the stack
|
|
size = backtrace(array, MaxStackTrace);
|
|
|
|
# if defined(__GNUG__)
|
|
|
|
// Loop through the returned addresses, and get the symbols to be demangled
|
|
char** strings = backtrace_symbols(array, size);
|
|
|
|
FILE* file = fopen(fileName.c_str(), "w+");
|
|
if (file != NULL) {
|
|
fprintf(file, "Error: signal %d:\n", sig);
|
|
}
|
|
// Print the stack trace
|
|
for (size_t i = 0; i < size; i++) {
|
|
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]'
|
|
// and extract '_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress' from it to be demangled into a proper name
|
|
std::string functionName = strings[i];
|
|
std::string::size_type start = functionName.find('(');
|
|
std::string::size_type end = functionName.find('+');
|
|
if (start != std::string::npos && end != std::string::npos) {
|
|
std::string demangled = functionName.substr(start + 1, end - start - 1);
|
|
|
|
demangled = Demangler::Demangle(demangled.c_str());
|
|
|
|
// If the demangled string is not empty, then we can replace the mangled string with the demangled one
|
|
if (!demangled.empty()) {
|
|
demangled.push_back('(');
|
|
demangled += functionName.substr(end);
|
|
functionName = demangled;
|
|
}
|
|
}
|
|
|
|
LOG("[%02zu] %s", i, functionName.c_str());
|
|
if (file != NULL) {
|
|
fprintf(file, "[%02zu] %s\n", i, functionName.c_str());
|
|
}
|
|
}
|
|
# else // defined(__GNUG__)
|
|
backtrace_symbols_fd(array, size, STDOUT_FILENO);
|
|
# endif // defined(__GNUG__)
|
|
|
|
#else // INCLUDE_BACKTRACE
|
|
|
|
struct backtrace_state* state = backtrace_create_state(
|
|
Diagnostics::GetProcessFileName().c_str(),
|
|
BACKTRACE_SUPPORTS_THREADS,
|
|
ErrorCallback,
|
|
nullptr);
|
|
|
|
struct bt_ctx ctx = { state, 0 };
|
|
Bt(state);
|
|
|
|
#endif // INCLUDE_BACKTRACE
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
void CritErrHdlr(int sig_num, siginfo_t* info, void* ucontext) {
|
|
CatchUnhandled(sig_num);
|
|
}
|
|
|
|
void OnTerminate() {
|
|
CatchUnhandled(-1);
|
|
}
|
|
|
|
void MakeBacktrace() {
|
|
struct sigaction sigact{};
|
|
|
|
sigact.sa_sigaction = CritErrHdlr;
|
|
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
|
|
|
|
if (sigaction(SIGSEGV, &sigact, nullptr) != 0 ||
|
|
sigaction(SIGFPE, &sigact, nullptr) != 0 ||
|
|
sigaction(SIGABRT, &sigact, nullptr) != 0 ||
|
|
sigaction(SIGILL, &sigact, nullptr) != 0) {
|
|
fprintf(stderr, "error setting signal handler for %d (%s)\n",
|
|
SIGSEGV,
|
|
strsignal(SIGSEGV));
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
std::set_terminate(OnTerminate);
|
|
}
|
|
#endif
|
|
|
|
void Diagnostics::Initialize() {
|
|
#ifdef _WIN32
|
|
SetUnhandledExceptionFilter(unhandled_handler);
|
|
#elif defined(__linux__) //&& !defined(__clang__)
|
|
MakeBacktrace();
|
|
#else
|
|
fprintf(stderr, "Diagnostics not supported on this platform.\n");
|
|
#endif
|
|
}
|
|
|
|
std::string Diagnostics::m_ProcessName{};
|
|
std::string Diagnostics::m_ProcessFileName{};
|
|
std::string Diagnostics::m_OutDirectory{};
|
|
bool Diagnostics::m_ProduceMemoryDump{};
|
|
|
|
void Diagnostics::SetProcessName(const std::string& name) {
|
|
m_ProcessName = name;
|
|
}
|
|
|
|
void Diagnostics::SetProcessFileName(const std::string& name) {
|
|
m_ProcessFileName = name;
|
|
}
|
|
|
|
void Diagnostics::SetOutDirectory(const std::string& path) {
|
|
// Crash files are named by appending to this, so it must end in a separator; the folder may not exist yet
|
|
m_OutDirectory = path;
|
|
if (!m_OutDirectory.empty() && m_OutDirectory.back() != '/' && m_OutDirectory.back() != '\\') m_OutDirectory += '/';
|
|
std::error_code ec;
|
|
if (!m_OutDirectory.empty()) std::filesystem::create_directories(m_OutDirectory, ec);
|
|
}
|
|
|
|
void Diagnostics::SetProduceMemoryDump(bool value) {
|
|
m_ProduceMemoryDump = value;
|
|
}
|
|
|
|
const std::string& Diagnostics::GetProcessName() {
|
|
return m_ProcessName;
|
|
}
|
|
|
|
const std::string& Diagnostics::GetProcessFileName() {
|
|
return m_ProcessFileName;
|
|
}
|
|
|
|
const std::string& Diagnostics::GetOutDirectory() {
|
|
return m_OutDirectory;
|
|
}
|
|
|
|
bool Diagnostics::GetProduceMemoryDump() {
|
|
return m_ProduceMemoryDump;
|
|
}
|