Add debug folder

This commit is contained in:
izzy2lost
2025-07-27 14:45:15 +00:00
committed by GitHub
parent f2c4df6aa6
commit e06193947e
4 changed files with 626 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
#include "Console.h"
#include "utils/StringHelper.h"
#include "Context.h"
#include <spdlog/spdlog.h>
namespace Ship {
Console::Console() {
}
Console::~Console() {
SPDLOG_TRACE("destruct console");
}
void Console::Init() {
}
std::string Console::BuildUsage(const CommandEntry& entry) {
std::string usage;
for (const auto& arg : entry.Arguments) {
usage += StringHelper::Sprintf(arg.Optional ? "[%s] " : "<%s> ", arg.Info.c_str());
}
return usage;
}
std::string Console::BuildUsage(const std::string& command) {
return BuildUsage(GetCommand(command));
}
int32_t Console::Run(const std::string& command, std::string* output) {
const std::vector<std::string> cmdArgs = StringHelper::Split(command, " ");
if (cmdArgs.empty()) {
SPDLOG_INFO("Could not parse command: {}", command);
return false;
}
const std::string& commandName = cmdArgs[0];
if (!mCommands.contains(commandName)) {
SPDLOG_INFO("Command handler not found: {}", commandName);
return false;
}
const CommandEntry entry = mCommands[commandName];
int32_t commandResult = entry.Handler(Context::GetInstance()->GetConsole(), cmdArgs, output);
if (output) {
SPDLOG_INFO("Command \"{}\" returned {} with output: {}", command, commandResult, *output);
} else {
SPDLOG_INFO("Command \"{}\" returned {}", command, commandResult);
}
return commandResult;
}
bool Console::HasCommand(const std::string& command) {
for (const auto& value : mCommands) {
if (value.first == command) {
return true;
}
}
return false;
}
void Console::AddCommand(const std::string& command, CommandEntry entry) {
if (!HasCommand(command)) {
mCommands[command] = entry;
} else {
SPDLOG_WARN("Attempting to add command {} that already exists", command);
}
}
std::map<std::string, CommandEntry>& Console::GetCommands() {
return mCommands;
}
CommandEntry& Console::GetCommand(const std::string& command) {
return mCommands[command];
}
} // namespace Ship
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include <stdint.h>
#include <string>
#include <memory>
#include <vector>
#include <functional>
#include <map>
#include <imgui.h>
namespace Ship {
class Console;
typedef std::function<int32_t(std::shared_ptr<Console> console, std::vector<std::string> args, std::string* output)>
CommandHandler;
enum class ArgumentType { TEXT, NUMBER };
struct CommandArgument {
std::string Info;
ArgumentType Type = ArgumentType::NUMBER;
bool Optional = false;
};
struct CommandEntry {
CommandHandler Handler;
std::string Description;
std::vector<CommandArgument> Arguments;
};
class Console {
public:
Console();
~Console();
void Init();
int32_t Run(const std::string& command, std::string* output);
bool HasCommand(const std::string& command);
void AddCommand(const std::string& command, CommandEntry entry);
std::string BuildUsage(const std::string& command);
std::string BuildUsage(const CommandEntry& entry);
CommandEntry& GetCommand(const std::string& command);
std::map<std::string, CommandEntry>& GetCommands();
protected:
private:
std::map<std::string, CommandEntry> mCommands;
};
} // namespace Ship
+449
View File
@@ -0,0 +1,449 @@
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#include "CrashHandler.h"
#include "Context.h"
#ifdef _WIN32
#include <windows.h>
#include <DbgHelp.h>
#include <errhandlingapi.h>
#include <inttypes.h>
#include <excpt.h>
#pragma comment(lib, "Dbghelp.lib")
#endif
namespace Ship {
#define WRITE_VAR_LINE(handler, varName, varValue) \
handler->AppendStr(varName); \
handler->AppendLine(varValue);
#define WRITE_VAR(handler, varName, varValue) \
handler->AppendStr(varName); \
handler->AppendStr(varValue);
#define WRITE_VAR_LINE_M(varName, varValue) \
AppendStr(varName); \
AppendLine(varValue);
#define WRITE_VAR_M(varName, varValue) \
AppendStr(varName); \
AppendStr(varValue);
bool CrashHandler::CheckStrLen(const char* str) {
if (strlen(str) + mOutBuffersize >= gMaxBufferSize) {
return false;
}
return true;
}
void CrashHandler::AppendStrTrunc(const char* str) {
while (mOutBuffersize < gMaxBufferSize - 1) {
mOutBuffer[mOutBuffersize++] = *str++;
}
mOutBuffer[mOutBuffersize] = '\0';
}
void CrashHandler::AppendStr(const char* str) {
if (!CheckStrLen(str)) {
AppendStrTrunc(str);
return;
}
while (*str != '\0') {
mOutBuffer[mOutBuffersize++] = *str++;
}
}
void CrashHandler::AppendLine(const char* str) {
AppendStr(str);
mOutBuffer[mOutBuffersize++] = '\n';
}
/**
* @brief Prints common data relevant to the crash
*
* @param buffer
*/
void CrashHandler::PrintCommon() {
if (mCallback != nullptr) {
mCallback(mOutBuffer, &mOutBuffersize);
}
SPDLOG_CRITICAL(mOutBuffer);
}
#if defined(__linux__) && !defined(__ANDROID__)
void CrashHandler::PrintRegisters(ucontext_t* ctx) {
char regbuffer[30];
AppendLine("Registers:");
#if defined(__x86_64__)
snprintf(regbuffer, std::size(regbuffer), "RAX: 0x%016llX", ctx->uc_mcontext.gregs[REG_RAX]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "RDI: 0x%016llX", ctx->uc_mcontext.gregs[REG_RDI]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "RSI: 0x%016llX", ctx->uc_mcontext.gregs[REG_RSI]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "RDX: 0x%016llX", ctx->uc_mcontext.gregs[REG_RDX]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "RCX: 0x%016llX", ctx->uc_mcontext.gregs[REG_RCX]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "R8: 0x%016llX", ctx->uc_mcontext.gregs[REG_R8]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "R9: 0x%016llX", ctx->uc_mcontext.gregs[REG_R9]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "R10: 0x%016llX", ctx->uc_mcontext.gregs[REG_R10]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "R11: 0x%016llX", ctx->uc_mcontext.gregs[REG_R11]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "RSP: 0x%016llX", ctx->uc_mcontext.gregs[REG_RSP]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "RBX: 0x%016llX", ctx->uc_mcontext.gregs[REG_RBX]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "RBP: 0x%016llX", ctx->uc_mcontext.gregs[REG_RBP]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "R12: 0x%016llX", ctx->uc_mcontext.gregs[REG_R12]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "R13: 0x%016llX", ctx->uc_mcontext.gregs[REG_R13]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "R14: 0x%016llX", ctx->uc_mcontext.gregs[REG_R14]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "R15: 0x%016llX", ctx->uc_mcontext.gregs[REG_R15]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "RIP: 0x%016llX", ctx->uc_mcontext.gregs[REG_RIP]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "EFL: 0x%016llX", ctx->uc_mcontext.gregs[REG_EFL]);
AppendLine(regbuffer);
#elif defined(__i386__)
snprintf(regbuffer, std::size(regbuffer), "EDI: 0x%08lX", ctx->uc_mcontext.gregs[REG_EDI]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "ESI: 0x%08lX", ctx->uc_mcontext.gregs[REG_ESI]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "EBP: 0x%08lX", ctx->uc_mcontext.gregs[REG_EBP]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "ESP: 0x%08lX", ctx->uc_mcontext.gregs[REG_ESP]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "EBX: 0x%08lX", ctx->uc_mcontext.gregs[REG_EBX]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "EDX: 0x%08lX", ctx->uc_mcontext.gregs[REG_EDX]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "ECX: 0x%08lX", ctx->uc_mcontext.gregs[REG_ECX]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "EAX: 0x%08lX", ctx->uc_mcontext.gregs[REG_EAX]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "EIP: 0x%08lX", ctx->uc_mcontext.gregs[REG_EIP]);
AppendLine(regbuffer);
snprintf(regbuffer, std::size(regbuffer), "EFL: 0x%08lX", ctx->uc_mcontext.gregs[REG_EFL]);
AppendLine(regbuffer);
#endif
}
static void ErrorHandler(int sig, siginfo_t* sigInfo, void* data) {
std::shared_ptr<CrashHandler> crashHandler = Context::GetInstance()->GetCrashHandler();
char intToCharBuffer[16];
std::array<void*, 4096> arr;
ucontext_t* ctx = static_cast<ucontext_t*>(data);
constexpr size_t nMaxFrames = arr.size();
size_t size = backtrace(arr.data(), nMaxFrames);
char** symbols = backtrace_symbols(arr.data(), nMaxFrames);
snprintf(intToCharBuffer, sizeof(intToCharBuffer), "Signal: %i", sig);
crashHandler->AppendLine(intToCharBuffer);
switch (sig) {
case SIGILL:
crashHandler->AppendLine("ILLEGAL INSTRUCTION");
break;
case SIGABRT:
crashHandler->AppendLine("ABORT");
break;
case SIGFPE:
crashHandler->AppendLine("ERRONEUS ARITHEMETIC OPERATION");
break;
case SIGSEGV:
crashHandler->AppendLine("INVALID ACCESS TO STORAGE");
break;
}
crashHandler->PrintRegisters(ctx);
crashHandler->AppendLine("Traceback:");
for (size_t i = 1; i < size; i++) {
Dl_info info;
int gotAddress = dladdr(arr[i], &info);
std::string functionName(symbols[i]);
if (gotAddress != 0 && info.dli_sname != nullptr) {
FILE* pipe;
int32_t status;
char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
const char* nameFound = info.dli_sname;
if (status == 0) {
nameFound = demangled;
}
functionName = StringHelper::Sprintf("%s (+0x%X)", nameFound, (char*)arr[i] - (char*)info.dli_saddr);
free(demangled);
}
snprintf(intToCharBuffer, sizeof(intToCharBuffer), "%i ", (int)i);
WRITE_VAR_LINE(crashHandler, intToCharBuffer, functionName.c_str());
}
SDL_ShowSimpleMessageBox(
SDL_MESSAGEBOX_ERROR, (Context::GetInstance()->GetName() + " has crashed").c_str(),
(Context::GetInstance()->GetName() + " has crashed. Please upload the logs to the support channel in discord.")
.c_str(),
nullptr);
free(symbols);
crashHandler->PrintCommon();
Context::GetInstance()->GetLogger()->flush();
spdlog::shutdown();
exit(1);
}
static void ShutdownHandler(int sig, siginfo_t* sigInfo, void* data) {
exit(1);
}
#elif _WIN32
#if defined(_WIN32) && !defined(_WIN64)
#define WINDOWS_32_BIT
#endif
void CrashHandler::PrintRegisters(CONTEXT* ctx) {
AppendLine("Registers: ");
char regBuff[25];
#if defined(_M_AMD64)
sprintf_s(regBuff, std::size(regBuff), "RAX: 0x%016llX", ctx->Rax);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "RCX: 0x%016llX", ctx->Rcx);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "RDX: 0x%016llX", ctx->Rdx);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "RBX: 0x%016llX", ctx->Rbx);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "RSP: 0x%016llX", ctx->Rsp);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "RBP: 0x%016llX", ctx->Rbp);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "RSI: 0x%016llX", ctx->Rsi);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "RDI: 0x%016llX", ctx->Rdi);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "R9: 0x%016llX", ctx->R9);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "R10: 0x%016llX", ctx->R10);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "R11: 0x%016llX", ctx->R11);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "R12: 0x%016llX", ctx->R12);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "R13: 0x%016llX", ctx->R13);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "R14: 0x%016llX", ctx->R14);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "R15: 0x%016llX", ctx->R15);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "RIP: 0x%016llX", ctx->Rip);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "EFLAGS: 0x%08lX", ctx->EFlags);
AppendLine(regBuff);
#elif defined(WINDOWS_32_BIT)
sprintf_s(regBuff, std::size(regBuff), "EDI: 0x%08lX", ctx->Edi);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "ESI: 0x%08lX", ctx->Esi);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "EBX: 0x%08lX", ctx->Ebx);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "ECX: 0x%08lX", ctx->Ecx);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "EAX: 0x%08lX", ctx->Eax);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "EBP: 0x%08lX", ctx->Ebp);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "ESP: 0x%08lX", ctx->Esp);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "EFLAGS: 0x%08lX", ctx->EFlags);
AppendLine(regBuff);
sprintf_s(regBuff, std::size(regBuff), "EIP: 0x%08lX", ctx->Eip);
AppendLine(regBuff);
#endif
}
void CrashHandler::PrintStack(CONTEXT* ctx) {
BOOL result;
HANDLE process;
HANDLE thread;
HMODULE hModule;
ULONG frame;
DWORD64 displacement;
DWORD disp;
#if defined(_M_AMD64)
STACKFRAME64 stack;
memset(&stack, 0, sizeof(STACKFRAME64));
#elif defined(WINDOWS_32_BIT)
STACKFRAME stack;
memset(&stack, 0, sizeof(STACKFRAME));
stack.AddrPC.Offset = (*ctx).Eip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = (*ctx).Esp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = (*ctx).Ebp;
stack.AddrFrame.Mode = AddrModeFlat;
#endif
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME + sizeof(TCHAR)];
char module[512];
PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
CONTEXT ctx2;
memcpy(&ctx2, ctx, sizeof(CONTEXT));
PrintRegisters(&ctx2);
process = GetCurrentProcess();
thread = GetCurrentThread();
SymSetOptions(SYMOPT_NO_IMAGE_SEARCH | SYMOPT_IGNORE_IMAGEDIR);
SymInitialize(process, "debug", true);
constexpr DWORD machineType =
#if defined(_M_AMD64)
IMAGE_FILE_MACHINE_AMD64;
#elif defined(WINDOWS_32_BIT)
IMAGE_FILE_MACHINE_I386;
#endif
displacement = 0;
for (frame = 0;; frame++) {
result = StackWalk(machineType, process, thread, &stack, &ctx2, nullptr, SymFunctionTableAccess,
SymGetModuleBase, nullptr);
if (!result) {
break;
}
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
SymFromAddr(process, (ULONG64)stack.AddrPC.Offset, &displacement, symbol);
#if defined(_M_AMD64)
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
#elif defined(WINDOWS_32_BIT)
IMAGEHLP_LINE line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
#endif
if (SymGetLineFromAddr(process, stack.AddrPC.Offset, &disp, &line)) {
char lineNumberStr[16];
sprintf_s(lineNumberStr, sizeof(lineNumberStr), "Line: %d", line.LineNumber);
AppendStr(symbol->Name);
AppendStr(" in ");
AppendStr(line.FileName);
AppendStr(lineNumberStr);
// SPDLOG_CRITICAL("{} in {}: line: {}: ", symbol->Name, line.FileName, line.LineNumber);
} else {
char addrString[20];
sprintf_s(addrString, std::size(addrString), "0x%016llX", symbol->Address);
WRITE_VAR_M("At ", symbol->Name);
WRITE_VAR_LINE_M("Addr: ", addrString);
// SPDLOG_CRITICAL("at {}, addr 0x{}", symbol->Name, addrString);
hModule = nullptr;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)(stack.AddrPC.Offset), &hModule);
if (hModule != nullptr) {
GetModuleFileNameA(hModule, module, sizeof(module));
}
WRITE_VAR_LINE_M("In: ", module);
// SPDLOG_CRITICAL("In {}", module);
}
}
PrintCommon();
Context::GetInstance()->GetLogger()->flush();
spdlog::shutdown();
}
extern "C" LONG WINAPI seh_filter(PEXCEPTION_POINTERS ex) {
char exceptionString[20];
std::shared_ptr<CrashHandler> crashHandler = Context::GetInstance()->GetCrashHandler();
snprintf(exceptionString, std::size(exceptionString), "0x%x", ex->ExceptionRecord->ExceptionCode);
WRITE_VAR_LINE(crashHandler, "Exception: ", exceptionString);
crashHandler->PrintStack(ex->ContextRecord);
MessageBoxA(
nullptr,
(Context::GetInstance()->GetName() + " has crashed. Please upload the logs to the support channel in discord.")
.c_str(),
"Crash", MB_OK | MB_ICONERROR);
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
CrashHandler::CrashHandler() {
mOutBuffer = new char[gMaxBufferSize];
#if defined(__linux__) && !defined(__ANDROID__)
struct sigaction action = { 0 };
struct sigaction shutdownAction = { 0 };
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = ErrorHandler;
sigaction(SIGILL, &action, nullptr);
sigaction(SIGABRT, &action, nullptr);
sigaction(SIGFPE, &action, nullptr);
sigaction(SIGSEGV, &action, nullptr);
shutdownAction.sa_flags = SA_SIGINFO;
shutdownAction.sa_sigaction = ShutdownHandler;
sigaction(SIGINT, &shutdownAction, nullptr);
sigaction(SIGTERM, &shutdownAction, nullptr);
sigaction(SIGQUIT, &shutdownAction, nullptr);
sigaction(SIGKILL, &shutdownAction, nullptr);
#elif defined(_WIN32)
SetUnhandledExceptionFilter(seh_filter);
#endif
}
CrashHandler::CrashHandler(CrashHandlerCallback callback) {
mCallback = callback;
CrashHandler();
}
CrashHandler::~CrashHandler() {
SPDLOG_TRACE("destruct crash handler");
delete[] mOutBuffer;
}
void CrashHandler::RegisterCallback(CrashHandlerCallback callback) {
mCallback = callback;
}
} // namespace Ship
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include <stddef.h>
#if (__linux__)
#include <csignal>
#include <cstdio>
#include <cxxabi.h> // for __cxa_demangle
#include <dlfcn.h> // for dladdr
#include <execinfo.h>
#include <unistd.h>
#include <SDL.h>
#endif
#if _WIN32
#include <windows.h>
#endif
namespace Ship {
typedef void (*CrashHandlerCallback)(char*, size_t*);
class CrashHandler {
public:
CrashHandler();
~CrashHandler();
CrashHandler(CrashHandlerCallback callback);
void RegisterCallback(CrashHandlerCallback callback);
void AppendLine(const char* str);
void AppendStr(const char* str);
void PrintCommon();
#ifdef __linux__
void PrintRegisters(ucontext_t* ctx);
#elif _WIN32
void PrintRegisters(CONTEXT* ctx);
void PrintStack(CONTEXT* ctx);
#endif
private:
CrashHandlerCallback mCallback = nullptr;
char* mOutBuffer = nullptr;
static constexpr size_t gMaxBufferSize = 32768;
size_t mOutBuffersize = 0;
void AppendStrTrunc(const char* str);
bool CheckStrLen(const char* str);
};
} // namespace Ship