diff options
author | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
---|---|---|
committer | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
commit | abef6da56913f1c55528103e60a50451a39628b1 (patch) | |
tree | b3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/Logger.c |
initial commit
Diffstat (limited to 'src/Logger.c')
-rw-r--r-- | src/Logger.c | 1386 |
1 files changed, 1386 insertions, 0 deletions
diff --git a/src/Logger.c b/src/Logger.c new file mode 100644 index 0000000..d3107a4 --- /dev/null +++ b/src/Logger.c @@ -0,0 +1,1386 @@ +#include "Logger.h" +#include "String.h" +#include "Platform.h" +#include "Window.h" +#include "Funcs.h" +#include "Stream.h" +#include "Errors.h" +#include "Utils.h" + +#if defined CC_BUILD_WEB + /* Can't see native CPU state with javascript */ +#elif defined CC_BUILD_WIN + #define WIN32_LEAN_AND_MEAN + #define NOSERVICE + #define NOMCX + #define NOIME + #define CUR_PROCESS_HANDLE ((HANDLE)-1) /* GetCurrentProcess() always returns -1 */ + + #include <windows.h> + #include <imagehlp.h> + static HANDLE curProcess = CUR_PROCESS_HANDLE; +#elif defined CC_BUILD_OPENBSD || defined CC_BUILD_HAIKU || defined CC_BUILD_SERENITY + #include <signal.h> + /* These operating systems don't provide sys/ucontext.h */ + /* But register constants be found from includes in <signal.h> */ + #elif defined CC_BUILD_OS2 + #include <signal.h> + #include <386/ucontext.h> +#elif defined CC_BUILD_LINUX || defined CC_BUILD_ANDROID + /* Need to define this to get REG_ constants */ + #define _GNU_SOURCE + #include <sys/ucontext.h> + #include <signal.h> +#elif defined CC_BUILD_POSIX + #include <signal.h> + #include <sys/ucontext.h> +#endif + +#ifdef CC_BUILD_DARWIN +/* Need this to detect macOS < 10.4, and switch to NS* api instead if so */ +#include <AvailabilityMacros.h> +#endif +/* Only show up to 50 frames in backtrace */ +#define MAX_BACKTRACE_FRAMES 50 + +static void AbortCommon(cc_result result, const char* raw_msg, void* ctx); + + +/*########################################################################################################################* +*----------------------------------------------------------Warning--------------------------------------------------------* +*#########################################################################################################################*/ +void Logger_DialogWarn(const cc_string* msg) { + cc_string dst; char dstBuffer[512]; + String_InitArray_NT(dst, dstBuffer); + + String_Copy(&dst, msg); + dst.buffer[dst.length] = '\0'; + Window_ShowDialog(Logger_DialogTitle, dst.buffer); +} +const char* Logger_DialogTitle = "Error"; +Logger_DoWarn Logger_WarnFunc = Logger_DialogWarn; + +/* Returns a description for some ClassiCube specific error codes */ +static const char* GetCCErrorDesc(cc_result res) { + switch (res) { + case ERR_END_OF_STREAM: return "End of stream"; + case ERR_NOT_SUPPORTED: return "Operation not supported"; + case ERR_INVALID_ARGUMENT: return "Invalid argument"; + case ERR_OUT_OF_MEMORY: return "Out of memory"; + + case OGG_ERR_INVALID_SIG: return "Only OGG music files supported"; + case OGG_ERR_VERSION: return "Invalid OGG format version"; + case AUDIO_ERR_MP3_SIG: return "MP3 audio files are not supported"; + + case WAV_ERR_STREAM_HDR: return "Only WAV sound files supported"; + case WAV_ERR_STREAM_TYPE: return "Invalid WAV type"; + case WAV_ERR_DATA_TYPE: return "Unsupported WAV audio format"; + + case ZIP_ERR_TOO_MANY_ENTRIES: return "Cannot load .zip files with over 1024 entries"; + + case PNG_ERR_INVALID_SIG: return "Only PNG images supported"; + case PNG_ERR_INVALID_HDR_SIZE: return "Invalid PNG header size"; + case PNG_ERR_TOO_WIDE: return "PNG image too wide"; + case PNG_ERR_TOO_TALL: return "PNG image too tall"; + case PNG_ERR_INTERLACED: return "Interlaced PNGs unsupported"; + case PNG_ERR_REACHED_IEND: return "Incomplete PNG image data"; + case PNG_ERR_NO_DATA: return "No image in PNG"; + case PNG_ERR_INVALID_SCANLINE: return "Invalid PNG scanline type"; + case PNG_ERR_16BITSAMPLES: return "16 bpp PNGs unsupported"; + + case NBT_ERR_UNKNOWN: return "Unknown NBT tag type"; + case CW_ERR_ROOT_TAG: return "Invalid root NBT tag"; + case CW_ERR_STRING_LEN: return "NBT string too long"; + + case ERR_DOWNLOAD_INVALID: return "Website denied download or doesn't exist"; + case ERR_NO_AUDIO_OUTPUT: return "No audio output devices plugged in"; + case ERR_INVALID_DATA_URL: return "Cannot download from invalid URL"; + case ERR_INVALID_OPEN_URL: return "Cannot navigate to invalid URL"; + + case NBT_ERR_EXPECTED_I8: return "Expected Int8 NBT tag"; + case NBT_ERR_EXPECTED_I16: return "Expected Int16 NBT tag"; + case NBT_ERR_EXPECTED_I32: return "Expected Int32 NBT tag"; + case NBT_ERR_EXPECTED_F32: return "Expected Float32 NBT tag"; + case NBT_ERR_EXPECTED_STR: return "Expected String NBT tag"; + case NBT_ERR_EXPECTED_ARR: return "Expected ByteArray NBT tag"; + case NBT_ERR_ARR_TOO_SMALL:return "ByteArray NBT tag too small"; + + case HTTP_ERR_NO_SSL: return "HTTPS URLs are not currently supported"; + case SOCK_ERR_UNKNOWN_HOST: return "Host could not be resolved to an IP address"; + } + return NULL; +} + +/* Appends more detailed information about an error if possible */ +static void AppendErrorDesc(cc_string* msg, cc_result res, Logger_DescribeError describeErr) { + const char* cc_err; + cc_string err; char errBuffer[128]; + String_InitArray(err, errBuffer); + + cc_err = GetCCErrorDesc(res); + if (cc_err) { + String_Format1(msg, "\n Error meaning: %c", cc_err); + } else if (describeErr(res, &err)) { + String_Format1(msg, "\n Error meaning: %s", &err); + } +} + +void Logger_FormatWarn(cc_string* msg, cc_result res, const char* action, Logger_DescribeError describeErr) { + String_Format2(msg, "Error %e when %c", &res, action); + AppendErrorDesc(msg, res, describeErr); +} + +void Logger_FormatWarn2(cc_string* msg, cc_result res, const char* action, const cc_string* path, Logger_DescribeError describeErr) { + String_Format3(msg, "Error %e when %c '%s'", &res, action, path); + AppendErrorDesc(msg, res, describeErr); +} + +static cc_bool DescribeSimple(cc_result res, cc_string* dst) { return false; } +void Logger_SimpleWarn(cc_result res, const char* action) { + Logger_Warn(res, action, DescribeSimple); +} +void Logger_SimpleWarn2(cc_result res, const char* action, const cc_string* path) { + Logger_Warn2(res, action, path, DescribeSimple); +} + +void Logger_Warn(cc_result res, const char* action, Logger_DescribeError describeErr) { + cc_string msg; char msgBuffer[256]; + String_InitArray(msg, msgBuffer); + + Logger_FormatWarn(&msg, res, action, describeErr); + Logger_WarnFunc(&msg); +} + +void Logger_Warn2(cc_result res, const char* action, const cc_string* path, Logger_DescribeError describeErr) { + cc_string msg; char msgBuffer[256]; + String_InitArray(msg, msgBuffer); + + Logger_FormatWarn2(&msg, res, action, path, describeErr); + Logger_WarnFunc(&msg); +} + +void Logger_DynamicLibWarn(const char* action, const cc_string* path) { + cc_string err; char errBuffer[128]; + cc_string msg; char msgBuffer[256]; + String_InitArray(msg, msgBuffer); + String_InitArray(err, errBuffer); + + String_Format2(&msg, "Error %c '%s'", action, path); + if (DynamicLib_DescribeError(&err)) { + String_Format1(&msg, ":\n %s", &err); + } + Logger_WarnFunc(&msg); +} + +void Logger_SysWarn(cc_result res, const char* action) { + Logger_Warn(res, action, Platform_DescribeError); +} +void Logger_SysWarn2(cc_result res, const char* action, const cc_string* path) { + Logger_Warn2(res, action, path, Platform_DescribeError); +} + + +/*########################################################################################################################* +*------------------------------------------------------Frame dumping------------------------------------------------------* +*#########################################################################################################################*/ +static void PrintFrame(cc_string* str, cc_uintptr addr, cc_uintptr symAddr, const char* symName, const char* modName) { + cc_string module; + int offset; + if (!modName) modName = "???"; + + module = String_FromReadonly(modName); + Utils_UNSAFE_GetFilename(&module); + String_Format2(str, "%x - %s", &addr, &module); + + if (symName && symName[0]) { + offset = (int)(addr - symAddr); + String_Format2(str, "(%c+%i)" _NL, symName, &offset); + } else { + String_AppendConst(str, _NL); + } +} + +#if defined CC_BUILD_WIN +struct SymbolAndName { IMAGEHLP_SYMBOL symbol; char name[256]; }; +static void DumpFrame(HANDLE process, cc_string* trace, cc_uintptr addr) { + char strBuffer[512]; cc_string str; + String_InitArray(str, strBuffer); + + struct SymbolAndName s = { 0 }; + s.symbol.MaxNameLength = 255; + s.symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); + SymGetSymFromAddr(process, addr, NULL, &s.symbol); + + IMAGEHLP_MODULE m = { 0 }; + m.SizeOfStruct = sizeof(IMAGEHLP_MODULE); + SymGetModuleInfo(process, addr, &m); + + PrintFrame(&str, addr, s.symbol.Address, s.symbol.Name, m.ModuleName); + String_AppendString(trace, &str); + + /* This function only works for .pdb debug info anyways */ + /* This function is also missing on Windows 9X */ +#if _MSC_VER + IMAGEHLP_LINE line = { 0 }; DWORD lineOffset; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE); + if (SymGetLineFromAddr(process, addr, &lineOffset, &line)) { + String_Format2(&str, " line %i in %c\r\n", &line.LineNumber, line.FileName); + } +#endif + Logger_Log(&str); +} +#elif defined MAC_OS_X_VERSION_MIN_REQUIRED && (MAC_OS_X_VERSION_MIN_REQUIRED < 1040) +/* dladdr does not exist prior to macOS tiger */ +static void DumpFrame(cc_string* trace, void* addr) { + cc_string str; char strBuffer[384]; + String_InitArray(str, strBuffer); + /* alas NSModuleForSymbol doesn't work with raw addresses */ + + PrintFrame(&str, (cc_uintptr)addr, 0, NULL, NULL); + String_AppendString(trace, &str); + Logger_Log(&str); +} +#elif defined CC_BUILD_IRIX +/* IRIX doesn't expose a nice interface for dladdr */ +static void DumpFrame(cc_string* trace, void* addr) { + cc_uintptr addr_ = (cc_uintptr)addr; + String_Format1(trace, "%x", &addr_); +} +#elif defined CC_BUILD_POSIX && !defined CC_BUILD_OS2 +/* need to define __USE_GNU for dladdr */ +#ifndef __USE_GNU +#define __USE_GNU +#endif +#include <dlfcn.h> +#undef __USE_GNU + +static void DumpFrame(cc_string* trace, void* addr) { + cc_string str; char strBuffer[384]; + Dl_info s; + + String_InitArray(str, strBuffer); + s.dli_sname = NULL; + s.dli_fname = NULL; + dladdr(addr, &s); + + PrintFrame(&str, (cc_uintptr)addr, (cc_uintptr)s.dli_saddr, s.dli_sname, s.dli_fname); + String_AppendString(trace, &str); + Logger_Log(&str); +} +#else +/* No backtrace support implemented */ +#endif + + +/*########################################################################################################################* +*-------------------------------------------------------Backtracing-------------------------------------------------------* +*#########################################################################################################################*/ +#if defined CC_BUILD_WIN +/* This callback function is used so stack Walking works using StackWalk properly on Windows 9x: */ +/* - on Windows 9x process ID is passed instead of process handle as the "process" argument */ +/* - the SymXYZ functions expect a process ID on Windows 9x, so that works fine */ +/* - if NULL is passed as the "ReadMemory" argument, the default callback using ReadProcessMemory is used */ +/* - however, ReadProcessMemory expects a process handle, and so that will fail since it's given a process ID */ +/* So to work around this, instead manually call ReadProcessMemory with the current process handle */ +static BOOL __stdcall ReadMemCallback(HANDLE process, DWORD_PTR baseAddress, PVOID buffer, DWORD size, PDWORD numBytesRead) { + SIZE_T numRead = 0; + BOOL ok = ReadProcessMemory(CUR_PROCESS_HANDLE, (LPCVOID)baseAddress, buffer, size, &numRead); + + *numBytesRead = (DWORD)numRead; /* DWORD always 32 bits */ + return ok; +} +static cc_uintptr spRegister; + +static int GetFrames(CONTEXT* ctx, cc_uintptr* addrs, int max) { + STACKFRAME frame = { 0 }; + int count, type; + HANDLE thread; + + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Mode = AddrModeFlat; + +#if defined _M_IX86 + type = IMAGE_FILE_MACHINE_I386; + frame.AddrPC.Offset = ctx->Eip; + frame.AddrFrame.Offset = ctx->Ebp; + frame.AddrStack.Offset = ctx->Esp; + spRegister = ctx->Esp; +#elif defined _M_X64 + type = IMAGE_FILE_MACHINE_AMD64; + frame.AddrPC.Offset = ctx->Rip; + frame.AddrFrame.Offset = ctx->Rsp; + frame.AddrStack.Offset = ctx->Rsp; + spRegister = ctx->Rsp; +#else + /* Always available after XP, so use that */ + return RtlCaptureStackBackTrace(0, max, (void**)addrs, NULL); +#endif + thread = GetCurrentThread(); + + for (count = 0; count < max; count++) + { + if (!StackWalk(type, curProcess, thread, &frame, ctx, ReadMemCallback, SymFunctionTableAccess, SymGetModuleBase, NULL)) break; + if (!frame.AddrFrame.Offset) break; + addrs[count] = frame.AddrPC.Offset; + } + return count; +} + +void Logger_Backtrace(cc_string* trace, void* ctx) { + cc_uintptr addrs[MAX_BACKTRACE_FRAMES]; + int i, frames; + + SymInitialize(curProcess, NULL, TRUE); /* TODO only in MSVC.. */ + frames = GetFrames((CONTEXT*)ctx, addrs, MAX_BACKTRACE_FRAMES); + + for (i = 0; i < frames; i++) { + DumpFrame(curProcess, trace, addrs[i]); + } + String_AppendConst(trace, _NL); +} +#elif defined CC_BUILD_ANDROID + #define CC_BACKTRACE_UNWIND +#elif defined CC_BUILD_DARWIN +/* backtrace is only available on macOS since 10.5 */ +void Logger_Backtrace(cc_string* trace, void* ctx) { + void* addrs[MAX_BACKTRACE_FRAMES]; + unsigned i, frames; + /* See lldb/tools/debugserver/source/MacOSX/stack_logging.h */ + /* backtrace uses this internally too, and exists since macOS 10.1 */ + extern void thread_stack_pcs(void** buffer, unsigned max, unsigned* nb); + + thread_stack_pcs(addrs, MAX_BACKTRACE_FRAMES, &frames); + /* Skip frames don't want to include in backtrace */ + /* frame 0 = thread_stack_pcs */ + /* frame 1 = Logger_Backtrace */ + for (i = 2; i < frames; i++) + { + DumpFrame(trace, addrs[i]); + } + String_AppendConst(trace, _NL); +} +#elif defined CC_BUILD_SERENITY +void Logger_Backtrace(cc_string* trace, void* ctx) { + String_AppendConst(trace, "-- backtrace unimplemented --"); + /* TODO: Backtrace using LibSymbolication */ +} +#elif defined CC_BUILD_IRIX +void Logger_Backtrace(cc_string* trace, void* ctx) { + String_AppendConst(trace, "-- backtrace unimplemented --"); + /* TODO implement backtrace using exc_unwind https://nixdoc.net/man-pages/IRIX/man3/exception.3.html */ +} +#elif defined CC_BACKTRACE_BUILTIN +/* Implemented later at end of the file */ +#elif defined CC_BUILD_POSIX && defined _GLIBC_ +#include <execinfo.h> +void Logger_Backtrace(cc_string* trace, void* ctx) { + void* addrs[MAX_BACKTRACE_FRAMES]; + int i, frames = backtrace(addrs, MAX_BACKTRACE_FRAMES); + + for (i = 0; i < frames; i++) + { + DumpFrame(trace, addrs[i]); + } + String_AppendConst(trace, _NL); +} +#elif defined CC_BUILD_POSIX +/* musl etc - rely on unwind from GCC instead */ + #define CC_BACKTRACE_UNWIND +#else +void Logger_Backtrace(cc_string* trace, void* ctx) { } +#endif + + +/*########################################################################################################################* +*-----------------------------------------------------CPU registers-------------------------------------------------------* +*#########################################################################################################################*/ +/* Unfortunately, operating systems vary wildly in how they name and access registers for dumping */ +/* So this is the simplest way to avoid duplicating code on each platform */ +#define Dump_X86() \ +String_Format3(str, "eax=%x ebx=%x ecx=%x" _NL, REG_GET(ax,AX), REG_GET(bx,BX), REG_GET(cx,CX));\ +String_Format3(str, "edx=%x esi=%x edi=%x" _NL, REG_GET(dx,DX), REG_GET(si,SI), REG_GET(di,DI));\ +String_Format3(str, "eip=%x ebp=%x esp=%x" _NL, REG_GET(ip,IP), REG_GET(bp,BP), REG_GET(sp,SP)); + +#define Dump_X64() \ +String_Format3(str, "rax=%x rbx=%x rcx=%x" _NL, REG_GET(ax,AX), REG_GET(bx,BX), REG_GET(cx,CX));\ +String_Format3(str, "rdx=%x rsi=%x rdi=%x" _NL, REG_GET(dx,DX), REG_GET(si,SI), REG_GET(di,DI));\ +String_Format3(str, "rip=%x rbp=%x rsp=%x" _NL, REG_GET(ip,IP), REG_GET(bp,BP), REG_GET(sp,SP));\ +String_Format3(str, "r8 =%x r9 =%x r10=%x" _NL, REG_GET(8,8), REG_GET(9,9), REG_GET(10,10));\ +String_Format3(str, "r11=%x r12=%x r13=%x" _NL, REG_GET(11,11), REG_GET(12,12), REG_GET(13,13));\ +String_Format2(str, "r14=%x r15=%x" _NL, REG_GET(14,14), REG_GET(15,15)); + +#define Dump_PPC() \ +String_Format4(str, "r0 =%x r1 =%x r2 =%x r3 =%x" _NL, REG_GNUM(0), REG_GNUM(1), REG_GNUM(2), REG_GNUM(3)); \ +String_Format4(str, "r4 =%x r5 =%x r6 =%x r7 =%x" _NL, REG_GNUM(4), REG_GNUM(5), REG_GNUM(6), REG_GNUM(7)); \ +String_Format4(str, "r8 =%x r9 =%x r10=%x r11=%x" _NL, REG_GNUM(8), REG_GNUM(9), REG_GNUM(10), REG_GNUM(11)); \ +String_Format4(str, "r12=%x r13=%x r14=%x r15=%x" _NL, REG_GNUM(12), REG_GNUM(13), REG_GNUM(14), REG_GNUM(15)); \ +String_Format4(str, "r16=%x r17=%x r18=%x r19=%x" _NL, REG_GNUM(16), REG_GNUM(17), REG_GNUM(18), REG_GNUM(19)); \ +String_Format4(str, "r20=%x r21=%x r22=%x r23=%x" _NL, REG_GNUM(20), REG_GNUM(21), REG_GNUM(22), REG_GNUM(23)); \ +String_Format4(str, "r24=%x r25=%x r26=%x r27=%x" _NL, REG_GNUM(24), REG_GNUM(25), REG_GNUM(26), REG_GNUM(27)); \ +String_Format4(str, "r28=%x r29=%x r30=%x r31=%x" _NL, REG_GNUM(28), REG_GNUM(29), REG_GNUM(30), REG_GNUM(31)); \ +String_Format3(str, "pc =%x lr =%x ctr=%x" _NL, REG_GET_PC(), REG_GET_LR(), REG_GET_CTR()); + +#define Dump_ARM32() \ +String_Format3(str, "r0 =%x r1 =%x r2 =%x" _NL, REG_GNUM(0), REG_GNUM(1), REG_GNUM(2));\ +String_Format3(str, "r3 =%x r4 =%x r5 =%x" _NL, REG_GNUM(3), REG_GNUM(4), REG_GNUM(5));\ +String_Format3(str, "r6 =%x r7 =%x r8 =%x" _NL, REG_GNUM(6), REG_GNUM(7), REG_GNUM(8));\ +String_Format3(str, "r9 =%x r10=%x fp =%x" _NL, REG_GNUM(9), REG_GNUM(10), REG_GET_FP());\ +String_Format3(str, "ip =%x sp =%x lr =%x" _NL, REG_GET_IP(), REG_GET_SP(), REG_GET_LR());\ +String_Format1(str, "pc =%x" _NL, REG_GET_PC()); + +#define Dump_ARM64() \ +String_Format4(str, "r0 =%x r1 =%x r2 =%x r3 =%x" _NL, REG_GNUM(0), REG_GNUM(1), REG_GNUM(2), REG_GNUM(3)); \ +String_Format4(str, "r4 =%x r5 =%x r6 =%x r7 =%x" _NL, REG_GNUM(4), REG_GNUM(5), REG_GNUM(6), REG_GNUM(7)); \ +String_Format4(str, "r8 =%x r9 =%x r10=%x r11=%x" _NL, REG_GNUM(8), REG_GNUM(9), REG_GNUM(10), REG_GNUM(11)); \ +String_Format4(str, "r12=%x r13=%x r14=%x r15=%x" _NL, REG_GNUM(12), REG_GNUM(13), REG_GNUM(14), REG_GNUM(15)); \ +String_Format4(str, "r16=%x r17=%x r18=%x r19=%x" _NL, REG_GNUM(16), REG_GNUM(17), REG_GNUM(18), REG_GNUM(19)); \ +String_Format4(str, "r20=%x r21=%x r22=%x r23=%x" _NL, REG_GNUM(20), REG_GNUM(21), REG_GNUM(22), REG_GNUM(23)); \ +String_Format4(str, "r24=%x r25=%x r26=%x r27=%x" _NL, REG_GNUM(24), REG_GNUM(25), REG_GNUM(26), REG_GNUM(27)); \ +String_Format4(str, "r28=%x fp =%x lr =%x sp =%x" _NL, REG_GNUM(28), REG_GET_FP(), REG_GET_LR(), REG_GET_SP()); \ +String_Format1(str, "pc =%x" _NL, REG_GET_PC()); + +#define Dump_Alpha() \ +String_Format4(str, "v0 =%x t0 =%x t1 =%x t2 =%x" _NL, REG_GNUM(0), REG_GNUM(1), REG_GNUM(2), REG_GNUM(3)); \ +String_Format4(str, "t3 =%x t4 =%x t5 =%x t6 =%x" _NL, REG_GNUM(4), REG_GNUM(5), REG_GNUM(6), REG_GNUM(7)); \ +String_Format4(str, "t7 =%x s0 =%x s1 =%x s2 =%x" _NL, REG_GNUM(8), REG_GNUM(9), REG_GNUM(10), REG_GNUM(11)); \ +String_Format4(str, "s3 =%x s4 =%x s5 =%x a0 =%x" _NL, REG_GNUM(12), REG_GNUM(13), REG_GNUM(14), REG_GNUM(16)); \ +String_Format4(str, "a1 =%x a2 =%x a3 =%x a4 =%x" _NL, REG_GNUM(17), REG_GNUM(18), REG_GNUM(19), REG_GNUM(20)); \ +String_Format4(str, "a5 =%x t8 =%x t9 =%x t10=%x" _NL, REG_GNUM(21), REG_GNUM(22), REG_GNUM(23), REG_GNUM(24)); \ +String_Format4(str, "t11=%x ra =%x pv =%x at =%x" _NL, REG_GNUM(25), REG_GNUM(26), REG_GNUM(27), REG_GNUM(28)); \ +String_Format4(str, "gp =%x fp =%x sp =%x pc =%x" _NL, REG_GNUM(29), REG_GET_FP(), REG_GET_SP(), REG_GET_PC()); + +#define Dump_SPARC() \ +String_Format4(str, "o0=%x o1=%x o2=%x o3=%x" _NL, REG_GET(o0,O0), REG_GET(o1,O1), REG_GET(o2,O2), REG_GET(o3,O3)); \ +String_Format4(str, "o4=%x o5=%x o6=%x o7=%x" _NL, REG_GET(o4,O4), REG_GET(o5,O5), REG_GET(o6,O6), REG_GET(o7,O7)); \ +String_Format4(str, "g1=%x g2=%x g3=%x g4=%x" _NL, REG_GET(g1,G1), REG_GET(g2,G2), REG_GET(g3,G3), REG_GET(g4,G4)); \ +String_Format4(str, "g5=%x g6=%x g7=%x y =%x" _NL, REG_GET(g5,G5), REG_GET(g6,G6), REG_GET(g7,G7), REG_GET( y, Y)); \ +String_Format2(str, "pc=%x nc=%x" _NL, REG_GET(pc,PC), REG_GET(npc,nPC)); + +#define Dump_RISCV() \ +String_Format4(str, "pc =%x r1 =%x r2 =%x r3 =%x" _NL, REG_GET_PC(), REG_GNUM(1), REG_GNUM(2), REG_GNUM(3)); \ +String_Format4(str, "r4 =%x r5 =%x r6 =%x r7 =%x" _NL, REG_GNUM(4), REG_GNUM(5), REG_GNUM(6), REG_GNUM(7)); \ +String_Format4(str, "r8 =%x r9 =%x r10=%x r11=%x" _NL, REG_GNUM(8), REG_GNUM(9), REG_GNUM(10), REG_GNUM(11)); \ +String_Format4(str, "r12=%x r13=%x r14=%x r15=%x" _NL, REG_GNUM(12), REG_GNUM(13), REG_GNUM(14), REG_GNUM(15)); \ +String_Format4(str, "r16=%x r17=%x r18=%x r19=%x" _NL, REG_GNUM(16), REG_GNUM(17), REG_GNUM(18), REG_GNUM(19)); \ +String_Format4(str, "r20=%x r21=%x r22=%x r23=%x" _NL, REG_GNUM(20), REG_GNUM(21), REG_GNUM(22), REG_GNUM(23)); \ +String_Format4(str, "r24=%x r25=%x r26=%x r27=%x" _NL, REG_GNUM(24), REG_GNUM(25), REG_GNUM(26), REG_GNUM(27)); \ +String_Format4(str, "r28=%x r29=%x r30=%x r31=%x" _NL, REG_GNUM(28), REG_GNUM(29), REG_GNUM(30), REG_GNUM(31)); + +#define Dump_MIPS() \ +String_Format4(str, "r0 =%x r1 =%x r2 =%x r3 =%x" _NL, REG_GNUM(0), REG_GNUM(1), REG_GNUM(2), REG_GNUM(3)); \ +String_Format4(str, "r4 =%x r5 =%x r6 =%x r7 =%x" _NL, REG_GNUM(4), REG_GNUM(5), REG_GNUM(6), REG_GNUM(7)); \ +String_Format4(str, "r8 =%x r9 =%x r10=%x r11=%x" _NL, REG_GNUM(8), REG_GNUM(9), REG_GNUM(10), REG_GNUM(11)); \ +String_Format4(str, "r12=%x r13=%x r14=%x r15=%x" _NL, REG_GNUM(12), REG_GNUM(13), REG_GNUM(14), REG_GNUM(15)); \ +String_Format4(str, "r16=%x r17=%x r18=%x r19=%x" _NL, REG_GNUM(16), REG_GNUM(17), REG_GNUM(18), REG_GNUM(19)); \ +String_Format4(str, "r20=%x r21=%x r22=%x r23=%x" _NL, REG_GNUM(20), REG_GNUM(21), REG_GNUM(22), REG_GNUM(23)); \ +String_Format4(str, "r24=%x r25=%x r26=%x r27=%x" _NL, REG_GNUM(24), REG_GNUM(25), REG_GNUM(26), REG_GNUM(27)); \ +String_Format4(str, "r28=%x sp =%x fp =%x ra =%x" _NL, REG_GNUM(28), REG_GNUM(29), REG_GNUM(30), REG_GNUM(31)); \ +String_Format3(str, "pc =%x lo =%x hi =%x" _NL, REG_GET_PC(), REG_GET_LO(), REG_GET_HI()); + +#if defined CC_BUILD_WIN +/* See CONTEXT in WinNT.h */ +static void PrintRegisters(cc_string* str, void* ctx) { + CONTEXT* r = (CONTEXT*)ctx; +#if defined _M_IX86 + #define REG_GET(reg, ign) &r->E ## reg + Dump_X86() +#elif defined _M_X64 + #define REG_GET(reg, ign) &r->R ## reg + Dump_X64() +#elif defined _M_ARM + #define REG_GNUM(num) &r->R ## num + #define REG_GET_FP() &r->R11 + #define REG_GET_IP() &r->R12 + #define REG_GET_SP() &r->Sp + #define REG_GET_LR() &r->Lr + #define REG_GET_PC() &r->Pc + Dump_ARM32() +#elif defined _M_ARM64 + #define REG_GNUM(num) &r->X[num] + #define REG_GET_FP() &r->Fp + #define REG_GET_LR() &r->Lr + #define REG_GET_SP() &r->Sp + #define REG_GET_PC() &r->Pc + Dump_ARM64() +#else + #error "Unknown CPU architecture" +#endif +} +#elif defined CC_BUILD_DARWIN && __DARWIN_UNIX03 +/* See /usr/include/mach/i386/_structs.h (macOS 10.5+) */ +static void PrintRegisters(cc_string* str, void* ctx) { + mcontext_t r = ((ucontext_t*)ctx)->uc_mcontext; +#if defined __i386__ + #define REG_GET(reg, ign) &r->__ss.__e##reg + Dump_X86() +#elif defined __x86_64__ + #define REG_GET(reg, ign) &r->__ss.__r##reg + Dump_X64() +#elif defined __aarch64__ + #define REG_GNUM(num) &r->__ss.__x[num] + #define REG_GET_FP() &r->__ss.__fp + #define REG_GET_LR() &r->__ss.__lr + #define REG_GET_SP() &r->__ss.__sp + #define REG_GET_PC() &r->__ss.__pc + Dump_ARM64() +#elif defined __arm__ + #define REG_GNUM(num) &r->__ss.__r[num] + #define REG_GET_FP() &r->__ss.__r[11] + #define REG_GET_IP() &r->__ss.__r[12] + #define REG_GET_SP() &r->__ss.__sp + #define REG_GET_LR() &r->__ss.__lr + #define REG_GET_PC() &r->__ss.__pc + Dump_ARM32() +#elif defined __ppc__ + #define REG_GNUM(num) &r->__ss.__r##num + #define REG_GET_PC() &r->__ss.__srr0 + #define REG_GET_LR() &r->__ss.__lr + #define REG_GET_CTR() &r->__ss.__ctr + Dump_PPC() +#else + #error "Unknown CPU architecture" +#endif +} +#elif defined CC_BUILD_DARWIN +/* See /usr/include/mach/i386/thread_status.h (macOS 10.4) */ +static void PrintRegisters(cc_string* str, void* ctx) { + mcontext_t r = ((ucontext_t*)ctx)->uc_mcontext; +#if defined __i386__ + #define REG_GET(reg, ign) &r->ss.e##reg + Dump_X86() +#elif defined __x86_64__ + #define REG_GET(reg, ign) &r->ss.r##reg + Dump_X64() +#elif defined __ppc__ + #define REG_GNUM(num) &r->ss.r##num + #define REG_GET_PC() &r->ss.srr0 + #define REG_GET_LR() &r->ss.lr + #define REG_GET_CTR() &r->ss.ctr + Dump_PPC() +#else + #error "Unknown CPU architecture" +#endif +} +#elif defined CC_BUILD_LINUX || defined CC_BUILD_ANDROID +/* See /usr/include/sys/ucontext.h */ +static void PrintRegisters(cc_string* str, void* ctx) { +#if __PPC__ && __WORDSIZE == 32 + /* See sysdeps/unix/sysv/linux/powerpc/sys/ucontext.h in glibc */ + mcontext_t r = *((ucontext_t*)ctx)->uc_mcontext.uc_regs; +#else + mcontext_t r = ((ucontext_t*)ctx)->uc_mcontext; +#endif + +#if defined __i386__ + #define REG_GET(ign, reg) &r.gregs[REG_E##reg] + Dump_X86() +#elif defined __x86_64__ + #define REG_GET(ign, reg) &r.gregs[REG_R##reg] + Dump_X64() +#elif defined __aarch64__ + #define REG_GNUM(num) &r.regs[num] + #define REG_GET_FP() &r.regs[29] + #define REG_GET_LR() &r.regs[30] + #define REG_GET_SP() &r.sp + #define REG_GET_PC() &r.pc + Dump_ARM64() +#elif defined __arm__ + #define REG_GNUM(num) &r.arm_r##num + #define REG_GET_FP() &r.arm_fp + #define REG_GET_IP() &r.arm_ip + #define REG_GET_SP() &r.arm_sp + #define REG_GET_LR() &r.arm_lr + #define REG_GET_PC() &r.arm_pc + Dump_ARM32() +#elif defined __alpha__ + #define REG_GNUM(num) &r.sc_regs[num] + #define REG_GET_FP() &r.sc_regs[15] + #define REG_GET_PC() &r.sc_pc + #define REG_GET_SP() &r.sc_regs[30] + Dump_Alpha() +#elif defined __sparc__ + #define REG_GET(ign, reg) &r.gregs[REG_##reg] + Dump_SPARC() +#elif defined __PPC__ && __WORDSIZE == 32 + #define REG_GNUM(num) &r.gregs[num] + #define REG_GET_PC() &r.gregs[32] + #define REG_GET_LR() &r.gregs[35] + #define REG_GET_CTR() &r.gregs[34] + Dump_PPC() +#elif defined __PPC__ + #define REG_GNUM(num) &r.gp_regs[num] + #define REG_GET_PC() &r.gp_regs[32] + /* TODO this might be wrong, compare with PT_LNK in */ + /* https://elixir.bootlin.com/linux/v4.19.122/source/arch/powerpc/include/uapi/asm/ptrace.h#L102 */ + #define REG_GET_LR() &r.gp_regs[35] + #define REG_GET_CTR() &r.gp_regs[34] + Dump_PPC() +#elif defined __mips__ + #define REG_GNUM(num) &r.gregs[num] + #define REG_GET_PC() &r.pc + #define REG_GET_LO() &r.mdlo + #define REG_GET_HI() &r.mdhi + Dump_MIPS() +#elif defined __riscv + #define REG_GNUM(num) &r.__gregs[num] + #define REG_GET_PC() &r.__gregs[REG_PC] + Dump_RISCV() +#else + #error "Unknown CPU architecture" +#endif +} +#elif defined CC_BUILD_SOLARIS +/* See /usr/include/sys/regset.h */ +/* -> usr/src/uts/[ARCH]/sys/mcontext.h */ +/* -> usr/src/uts/[ARCH]/sys/regset.h */ +static void PrintRegisters(cc_string* str, void* ctx) { + mcontext_t* r = &((ucontext_t*)ctx)->uc_mcontext; + +#if defined __i386__ + #define REG_GET(ign, reg) &r->gregs[E##reg] + Dump_X86() +#elif defined __x86_64__ + #define REG_GET(ign, reg) &r->gregs[REG_R##reg] + Dump_X64() +#elif defined __sparc__ + #define REG_GET(ign, reg) &r->gregs[REG_##reg] + Dump_SPARC() +#else + #error "Unknown CPU architecture" +#endif +} +#elif defined CC_BUILD_NETBSD +/* See /usr/include/[ARCH]/mcontext.h */ +/* -> src/sys/arch/[ARCH]/include/mcontext.h */ +static void PrintRegisters(cc_string* str, void* ctx) { + mcontext_t* r = &((ucontext_t*)ctx)->uc_mcontext; +#if defined __i386__ + #define REG_GET(ign, reg) &r->__gregs[_REG_E##reg] + Dump_X86() +#elif defined __x86_64__ + #define REG_GET(ign, reg) &r->__gregs[_REG_R##reg] + Dump_X64() +#elif defined __aarch64__ + #define REG_GNUM(num) &r->__gregs[num] + #define REG_GET_FP() &r->__gregs[_REG_FP] + #define REG_GET_LR() &r->__gregs[_REG_LR] + #define REG_GET_SP() &r->__gregs[_REG_SP] + #define REG_GET_PC() &r->__gregs[_REG_PC] + Dump_ARM64() +#elif defined __arm__ + #define REG_GNUM(num) &r->__gregs[num] + #define REG_GET_FP() &r->__gregs[_REG_FP] + #define REG_GET_IP() &r->__gregs[12] + #define REG_GET_SP() &r->__gregs[_REG_SP] + #define REG_GET_LR() &r->__gregs[_REG_LR] + #define REG_GET_PC() &r->__gregs[_REG_PC] + Dump_ARM32() +#elif defined __powerpc__ + #define REG_GNUM(num) &r->__gregs[num] + #define REG_GET_PC() &r->__gregs[_REG_PC] + #define REG_GET_LR() &r->__gregs[_REG_LR] + #define REG_GET_CTR() &r->__gregs[_REG_CTR] + Dump_PPC() +#elif defined __mips__ + #define REG_GNUM(num) &r->__gregs[num] + #define REG_GET_PC() &r->__gregs[_REG_EPC] + #define REG_GET_LO() &r->__gregs[_REG_MDLO] + #define REG_GET_HI() &r->__gregs[_REG_MDHI] + Dump_MIPS() +#elif defined __riscv + #define REG_GNUM(num) &r->__gregs[num] + #define REG_GET_PC() &r->__gregs[_REG_PC] + Dump_RISCV() +#else + #error "Unknown CPU architecture" +#endif +} +#elif defined CC_BUILD_FREEBSD +/* See /usr/include/machine/ucontext.h */ +/* -> src/sys/[ARCH]/include/ucontext.h */ +static void PrintRegisters(cc_string* str, void* ctx) { + mcontext_t* r = &((ucontext_t*)ctx)->uc_mcontext; +#if defined __i386__ + #define REG_GET(reg, ign) &r->mc_e##reg + Dump_X86() +#elif defined __x86_64__ + #define REG_GET(reg, ign) &r->mc_r##reg + Dump_X64() +#elif defined __aarch64__ + #define REG_GNUM(num) &r->mc_gpregs.gp_x[num] + #define REG_GET_FP() &r->mc_gpregs.gp_x[29] + #define REG_GET_LR() &r->mc_gpregs.gp_lr + #define REG_GET_SP() &r->mc_gpregs.gp_sp + #define REG_GET_PC() &r->mc_gpregs.gp_elr + Dump_ARM64() +#elif defined __arm__ + #define REG_GNUM(num) &r->__gregs[num] + #define REG_GET_FP() &r->__gregs[_REG_FP] + #define REG_GET_IP() &r->__gregs[12] + #define REG_GET_SP() &r->__gregs[_REG_SP] + #define REG_GET_LR() &r->__gregs[_REG_LR] + #define REG_GET_PC() &r->__gregs[_REG_PC] + Dump_ARM32() +#elif defined __powerpc__ + #define REG_GNUM(num) &r->mc_frame[##num] + #define REG_GET_PC() &r->mc_srr0 + #define REG_GET_LR() &r->mc_lr + #define REG_GET_CTR() &r->mc_ctr + Dump_PPC() +#elif defined __mips__ + #define REG_GNUM(num) &r->mc_regs[num] + #define REG_GET_PC() &r->mc_pc + #define REG_GET_LO() &r->mullo + #define REG_GET_HI() &r->mulhi + Dump_MIPS() +#else + #error "Unknown CPU architecture" +#endif +} +#elif defined CC_BUILD_OPENBSD +/* See /usr/include/machine/signal.h */ +/* -> src/sys/arch/[ARCH]/include/signal.h */ +static void PrintRegisters(cc_string* str, void* ctx) { + ucontext_t* r = (ucontext_t*)ctx; +#if defined __i386__ + #define REG_GET(reg, ign) &r->sc_e##reg + Dump_X86() +#elif defined __x86_64__ + #define REG_GET(reg, ign) &r->sc_r##reg + Dump_X64() +#elif defined __aarch64__ + #define REG_GNUM(num) &r->sc_x[num] + #define REG_GET_FP() &r->sc_x[29] + #define REG_GET_LR() &r->sc_lr + #define REG_GET_SP() &r->sc_sp + #define REG_GET_PC() &r->sc_elr + Dump_ARM64() +#elif defined __arm__ + #define REG_GNUM(num) &r->sc_r##num + #define REG_GET_FP() &r->sc_r11 + #define REG_GET_IP() &r->sc_r12 + #define REG_GET_SP() &r->sc_usr_sp + #define REG_GET_LR() &r->sc_usr_lr + #define REG_GET_PC() &r->sc_pc + Dump_ARM32() +#elif defined __powerpc__ + #define REG_GNUM(num) &r->sc_frame.fixreg[num] + #define REG_GET_PC() &r->sc_frame.srr0 + #define REG_GET_LR() &r->sc_frame.lr + #define REG_GET_CTR() &r->sc_frame.ctr + Dump_PPC() +#elif defined __mips__ + #define REG_GNUM(num) &r->sc_regs[num] + #define REG_GET_PC() &r->sc_pc + #define REG_GET_LO() &r->mullo + #define REG_GET_HI() &r->mulhi + Dump_MIPS() +#else + #error "Unknown CPU architecture" +#endif +} +#elif defined CC_BUILD_HAIKU +/* See /headers/posix/arch/[ARCH]/signal.h */ +static void PrintRegisters(cc_string* str, void* ctx) { + mcontext_t* r = &((ucontext_t*)ctx)->uc_mcontext; +#if defined __i386__ + #define REG_GET(reg, ign) &r->e##reg + Dump_X86() +#elif defined __x86_64__ + #define REG_GET(reg, ign) &r->r##reg + Dump_X64() +#elif defined __aarch64__ + #define REG_GNUM(num) &r->x[num] + #define REG_GET_FP() &r->x[29] + #define REG_GET_LR() &r->lr + #define REG_GET_SP() &r->sp + #define REG_GET_PC() &r->elr + Dump_ARM64() +#elif defined __arm__ + #define REG_GNUM(num) &r->r##num + #define REG_GET_FP() &r->r11 + #define REG_GET_IP() &r->r12 + #define REG_GET_SP() &r->r13 + #define REG_GET_LR() &r->r14 + #define REG_GET_PC() &r->r15 + Dump_ARM32() +#elif defined __riscv + #define REG_GNUM(num) &r->x[num - 1] + #define REG_GET_PC() &r->pc + Dump_RISCV() +#else + #error "Unknown CPU architecture" +#endif +} +#elif defined CC_BUILD_SERENITY +static void PrintRegisters(cc_string* str, void* ctx) { + mcontext_t* r = &((ucontext_t*)ctx)->uc_mcontext; +#if defined __i386__ + #define REG_GET(reg, ign) &r->e##reg + Dump_X86() +#elif defined __x86_64__ + #define REG_GET(reg, ign) &r->r##reg + Dump_X64() +#else + #error "Unknown CPU architecture" +#endif +} +#elif defined CC_BUILD_IRIX +/* See /usr/include/sys/ucontext.h */ +/* https://nixdoc.net/man-pages/IRIX/man5/UCONTEXT.5.html */ +static void PrintRegisters(cc_string* str, void* ctx) { + mcontext_t* r = &((ucontext_t*)ctx)->uc_mcontext; + + #define REG_GNUM(num) &r->__gregs[CTX_EPC] + #define REG_GET_PC() &r->__gregs[CTX_MDLO] + #define REG_GET_LO() &r->__gregs[CTX_MDLO] + #define REG_GET_HI() &r->__gregs[CTX_MDHI] + Dump_MIPS() +} +#else +static void PrintRegisters(cc_string* str, void* ctx) { + /* Register dumping not implemented */ +} +#endif + +static void DumpRegisters(void* ctx) { + cc_string str; char strBuffer[768]; + String_InitArray(str, strBuffer); + + String_AppendConst(&str, "-- registers --" _NL); + PrintRegisters(&str, ctx); + Logger_Log(&str); +} + + +/*########################################################################################################################* +*------------------------------------------------Module/Memory map handling-----------------------------------------------* +*#########################################################################################################################*/ +#if defined CC_BUILD_WIN +static void DumpStack(void) { + static const cc_string stack = String_FromConst("-- stack --\r\n"); + cc_string str; char strBuffer[128]; + cc_uint8 buffer[0x10]; + SIZE_T numRead; + int i, j; + + Logger_Log(&stack); + spRegister &= ~0x0F; + spRegister -= 0x40; + + /* Dump 128 bytes near stack pointer */ + for (i = 0; i < 8; i++, spRegister += 0x10) + { + String_InitArray(str, strBuffer); + String_Format1(&str, "0x%x)", &spRegister); + ReadProcessMemory(CUR_PROCESS_HANDLE, (LPCVOID)spRegister, buffer, 0x10, &numRead); + + for (j = 0; j < 0x10; j++) + { + if ((j & 0x03) == 0) String_Append(&str, ' '); + String_AppendHex(&str, buffer[j]); + String_Append(&str, ' '); + } + String_AppendConst(&str, "\r\n"); + Logger_Log(&str); + } +} + +static BOOL CALLBACK DumpModule(const char* name, ULONG_PTR base, ULONG size, void* userCtx) { + cc_string str; char strBuffer[256]; + cc_uintptr beg, end; + + beg = base; end = base + (size - 1); + String_InitArray(str, strBuffer); + + String_Format3(&str, "%c = %x-%x\r\n", name, &beg, &end); + Logger_Log(&str); + return true; +} + +static BOOL (WINAPI *_EnumerateLoadedModules)(HANDLE process, PENUMLOADED_MODULES_CALLBACK callback, PVOID userContext); +static void DumpMisc(void) { + static const cc_string modules = String_FromConst("-- modules --\r\n"); + if (spRegister >= 0xFFFF) DumpStack(); + + if (!_EnumerateLoadedModules) return; + Logger_Log(&modules); + _EnumerateLoadedModules(curProcess, DumpModule, NULL); +} + +#elif defined CC_BUILD_LINUX || defined CC_BUILD_SOLARIS || defined CC_BUILD_ANDROID +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#ifdef CC_BUILD_ANDROID +static int SkipRange(const cc_string* str) { + /* Android has a lot of ranges in /maps, which produces 100-120 kb of logs for one single crash! */ + /* Example of different types of ranges: + 7a2df000-7a2eb000 r-xp 00000000 fd:01 419 /vendor/lib/libdrm.so + 7a2eb000-7a2ec000 r--p 0000b000 fd:01 419 /vendor/lib/libdrm.so + 7a2ec000-7a2ed000 rw-p 0000c000 fd:01 419 /vendor/lib/libdrm.so + 7a3d5000-7a4d1000 rw-p 00000000 00:00 0 + 7a4d1000-7a4d2000 ---p 00000000 00:00 0 [anon:thread stack guard] + To cut down crash logs to more relevant information, unnecessary '/' entries are ignored */ + cc_string path; + + /* Always include ranges without a / */ + int idx = String_IndexOf(str, '/'); + if (idx == -1) return false; + path = String_UNSAFE_SubstringAt(str, idx); + + return + /* Ignore fonts */ + String_ContainsConst(&path, "/system/fonts/") + /* Ignore shared memory (e.g. '/dev/ashmem/dalvik-thread local mark stack (deleted)') */ + || String_ContainsConst(&path, "/dev/ashmem/") + /* Ignore /dev/mali0 ranges (~200 entries on some devices) */ + || String_ContainsConst(&path, "/dev/mali0") + /* Ignore /system/lib/ (~350 entries) and /system/lib64 (~700 entries) */ + || String_ContainsConst(&path, "/system/lib") + /* Ignore /system/framework (~130 to ~160 entries) */ + || String_ContainsConst(&path, "/system/framework/") + /* Ignore /apex/com.android.art/javalib/ (~240 entries) */ + || String_ContainsConst(&path, "/apex/com.") + /* Ignore /dev/dri/renderD128 entries (~100 to ~120 entries) */ + || String_ContainsConst(&path, "/dri/renderD128") + /* Ignore /data/dalvik-cache entries (~80 entries) */ + || String_ContainsConst(&path, "/data/dalvik-cache/") + /* Ignore /vendor/lib entries (~80 entries) */ + || String_ContainsConst(&path, "/vendor/lib"); +} +#else +static int SkipRange(const cc_string* str) { + return + /* Ignore GPU iris driver i915 GEM buffers (~60,000 entries for one user) */ + String_ContainsConst(str, "anon_inode:i915.gem"); +} +#endif + +static void DumpMisc(void) { + static const cc_string memMap = String_FromConst("-- memory map --\n"); + cc_string str; char strBuffer[320]; + int n, fd; + + Logger_Log(&memMap); + /* dumps all known ranges of memory */ + fd = open("/proc/self/maps", O_RDONLY); + if (fd < 0) return; + String_InitArray(str, strBuffer); + + while ((n = read(fd, str.buffer, str.capacity)) > 0) { + str.length = n; + if (!SkipRange(&str)) Logger_Log(&str); + } + + close(fd); +} +#elif defined CC_BUILD_DARWIN +#include <mach-o/dyld.h> + +static void DumpMisc(void) { + static const cc_string modules = String_FromConst("-- modules --\n"); + static const cc_string newLine = String_FromConst(_NL); + cc_uint32 i, count; + const char* path; + cc_string str; + + /* TODO: Add Logger_LogRaw / Logger_LogConst too */ + Logger_Log(&modules); + count = _dyld_image_count(); + /* dumps absolute path of each module */ + + for (i = 0; i < count; i++) { + path = _dyld_get_image_name(i); + if (!path) continue; + + str = String_FromReadonly(path); + Logger_Log(&str); + Logger_Log(&newLine); + } +} +#else +static void DumpMisc(void) { } +#endif + + +/*########################################################################################################################* +*--------------------------------------------------Unhandled error logging------------------------------------------------* +*#########################################################################################################################*/ +#if defined CC_BUILD_WIN +static const char* ExceptionDescribe(cc_uint32 code) { + switch (code) { + case EXCEPTION_ACCESS_VIOLATION: return "ACCESS_VIOLATION"; + case EXCEPTION_ILLEGAL_INSTRUCTION: return "ILLEGAL_INSTRUCTION"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: return "DIVIDE_BY_ZERO"; + } + return NULL; +} + +static LONG WINAPI UnhandledFilter(struct _EXCEPTION_POINTERS* info) { + cc_string msg; char msgBuffer[128 + 1]; + const char* desc; + cc_uint32 code; + cc_uintptr addr; + DWORD i, numArgs; + + code = (cc_uint32)info->ExceptionRecord->ExceptionCode; + addr = (cc_uintptr)info->ExceptionRecord->ExceptionAddress; + desc = ExceptionDescribe(code); + + String_InitArray_NT(msg, msgBuffer); + if (desc) { + String_Format2(&msg, "Unhandled %c error at %x", desc, &addr); + } else { + String_Format2(&msg, "Unhandled exception 0x%h at %x", &code, &addr); + } + + numArgs = info->ExceptionRecord->NumberParameters; + if (numArgs) { + numArgs = min(numArgs, EXCEPTION_MAXIMUM_PARAMETERS); + String_AppendConst(&msg, " ["); + + for (i = 0; i < numArgs; i++) { + String_Format1(&msg, "0x%x,", &info->ExceptionRecord->ExceptionInformation[i]); + } + String_Append(&msg, ']'); + } + + msg.buffer[msg.length] = '\0'; + AbortCommon(0, msg.buffer, info->ContextRecord); + return EXCEPTION_EXECUTE_HANDLER; /* TODO: different flag */ +} + +void Logger_Hook(void) { + static const struct DynamicLibSym funcs[] = { + #ifdef _IMAGEHLP64 + { "EnumerateLoadedModules64", (void**)&_EnumerateLoadedModules}, + #else + { "EnumerateLoadedModules", (void**)&_EnumerateLoadedModules }, + #endif + }; + static const cc_string imagehlp = String_FromConst("IMAGEHLP.DLL"); + OSVERSIONINFOA osInfo; + void* lib; + + SetUnhandledExceptionFilter(UnhandledFilter); + DynamicLib_LoadAll(&imagehlp, funcs, Array_Elems(funcs), &lib); + + /* Windows 9x requires process IDs instead - see old DBGHELP docs */ + /* https://documentation.help/DbgHelp/documentation.pdf */ + osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + osInfo.dwPlatformId = 0; + GetVersionExA(&osInfo); + + if (osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { + curProcess = (HANDLE)((cc_uintptr)GetCurrentProcessId()); + } +} +#elif defined CC_BUILD_POSIX +static const char* SignalDescribe(int type) { + switch (type) { + case SIGSEGV: return "SIGSEGV"; + case SIGBUS: return "SIGBUS"; + case SIGILL: return "SIGILL"; + case SIGABRT: return "SIGABRT"; + case SIGFPE: return "SIGFPE"; + } + return NULL; +} + +static void SignalHandler(int sig, siginfo_t* info, void* ctx) { + cc_string msg; char msgBuffer[128 + 1]; + const char* desc; + int type, code; + cc_uintptr addr; + + /* Uninstall handler to avoid chance of infinite loop */ + signal(SIGSEGV, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGILL, SIG_DFL); + signal(SIGABRT, SIG_DFL); + signal(SIGFPE, SIG_DFL); + + type = info->si_signo; + code = info->si_code; + addr = (cc_uintptr)info->si_addr; + desc = SignalDescribe(type); + + String_InitArray_NT(msg, msgBuffer); + if (desc) { + String_Format3(&msg, "Unhandled signal %c (code %i) at %x", desc, &code, &addr); + } else { + String_Format3(&msg, "Unhandled signal %i (code %i) at %x", &type, &code, &addr); + } + msg.buffer[msg.length] = '\0'; + + #if defined CC_BUILD_ANDROID + /* deliberate Dalvik VM abort, try to log a nicer error for this */ + if (type == SIGSEGV && addr == 0xDEADD00D) Platform_TryLogJavaError(); + #endif + AbortCommon(0, msg.buffer, ctx); +} + +void Logger_Hook(void) { + struct sigaction sa, old; + sa.sa_sigaction = SignalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_SIGINFO; + + sigaction(SIGSEGV, &sa, &old); + sigaction(SIGBUS, &sa, &old); + sigaction(SIGILL, &sa, &old); + sigaction(SIGABRT, &sa, &old); + sigaction(SIGFPE, &sa, &old); +} +#else +void Logger_Hook(void) { + /* TODO can signals be supported somehow for PSP/3DS? */ +} +#endif + + +/*########################################################################################################################* +*-------------------------------------------------Deliberate crash logging------------------------------------------------* +*#########################################################################################################################*/ +#if defined CC_BUILD_WIN +#if __GNUC__ +/* Don't want compiler doing anything fancy with registers */ +void __attribute__((optimize("O0"))) Logger_Abort2(cc_result result, const char* raw_msg) { +#else +void Logger_Abort2(cc_result result, const char* raw_msg) { +#endif + CONTEXT ctx; + #if _M_IX86 && __GNUC__ + /* Stack frame layout on x86: */ + /* [ebp] is previous frame's EBP */ + /* [ebp+4] is previous frame's EIP (return address) */ + /* address of [ebp+8] is previous frame's ESP */ + __asm__( + "mov 0(%%ebp), %%eax \n\t" /* mov eax, [ebp] */ + "mov %%eax, %0 \n\t" /* mov [ctx.Ebp], eax */ + "mov 4(%%ebp), %%eax \n\t" /* mov eax, [ebp+4] */ + "mov %%eax, %1 \n\t" /* mov [ctx.Eip], eax */ + "lea 8(%%ebp), %%eax \n\t" /* lea eax, [ebp+8] */ + "mov %%eax, %2" /* mov [ctx.Esp], eax */ + : "=m" (ctx.Ebp), "=m" (ctx.Eip), "=m" (ctx.Esp) + : + : "eax", "memory" + ); + ctx.ContextFlags = CONTEXT_CONTROL; + #else + /* This method is guaranteed to exist on 64 bit windows. */ + /* NOTE: This is missing in 32 bit Windows 2000 however, */ + /* so an alternative is provided for MinGW above so that */ + /* the game can be cross-compiled for Windows 98 / 2000 */ + RtlCaptureContext(&ctx); + #endif + AbortCommon(result, raw_msg, &ctx); +} +#else +void Logger_Abort2(cc_result result, const char* raw_msg) { + AbortCommon(result, raw_msg, NULL); +} +#endif + + +/*########################################################################################################################* +*----------------------------------------------------------Common---------------------------------------------------------* +*#########################################################################################################################*/ +#ifdef CC_BUILD_MINFILES +void Logger_Log(const cc_string* msg) { + Platform_Log(msg->buffer, msg->length); +} +static void LogCrashHeader(void) { } +static void CloseLogFile(void) { } +#else +static struct Stream logStream; +static cc_bool logOpen; + +void Logger_Log(const cc_string* msg) { + static const cc_string path = String_FromConst("client.log"); + if (!logOpen) { + logOpen = true; + Stream_AppendFile(&logStream, &path); + } + + if (!logStream.meta.file) return; + Stream_Write(&logStream, (const cc_uint8*)msg->buffer, msg->length); +} + +static void LogCrashHeader(void) { + cc_string msg; char msgBuffer[96]; + struct DateTime now; + + String_InitArray(msg, msgBuffer); + String_AppendConst(&msg, _NL "----------------------------------------" _NL); + Logger_Log(&msg); + msg.length = 0; + + DateTime_CurrentLocal(&now); + String_Format3(&msg, "Crash time: %p2/%p2/%p4 ", &now.day, &now.month, &now.year); + String_Format3(&msg, "%p2:%p2:%p2" _NL, &now.hour, &now.minute, &now.second); + Logger_Log(&msg); +} + +static void CloseLogFile(void) { + if (logStream.meta.file) logStream.Close(&logStream); +} +#endif + +#if CC_GFX_BACKEND == CC_GFX_BACKEND_D3D11 + #define GFX_BACKEND " (Direct3D11)" +#elif CC_GFX_BACKEND == CC_GFX_BACKEND_D3D9 + #define GFX_BACKEND " (Direct3D9)" +#elif CC_GFX_BACKEND == CC_GFX_BACKEND_GL2 + #define GFX_BACKEND " (ModernGL)" +#elif CC_GFX_BACKEND == CC_GFX_BACKEND_GL1 + #define GFX_BACKEND " (OpenGL)" +#else + #define GFX_BACKEND " (Unknown)" +#endif + +static void AbortCommon(cc_result result, const char* raw_msg, void* ctx) { + static const cc_string backtrace = String_FromConst("-- backtrace --" _NL); + cc_string msg; char msgBuffer[3070 + 1]; + String_InitArray_NT(msg, msgBuffer); + + String_AppendConst(&msg, "ClassiCube crashed." _NL); + if (raw_msg) String_Format1(&msg, "Reason: %c" _NL, raw_msg); + #ifdef CC_COMMIT_SHA + String_AppendConst(&msg, "Commit SHA: " CC_COMMIT_SHA GFX_BACKEND _NL); + #endif + + if (result) { + String_Format1(&msg, "%h" _NL, &result); + } else { result = 1; } + + LogCrashHeader(); + Logger_Log(&msg); + + String_AppendConst(&msg, "Full details of the crash have been logged to 'client.log'.\n"); + String_AppendConst(&msg, "Please report this on the ClassiCube forums or to UnknownShadow200.\n\n"); + if (ctx) DumpRegisters(ctx); + + /* These two function calls used to be in a separate DumpBacktrace function */ + /* However that was not always inlined by the compiler, which resulted in a */ + /* useless strackframe being logged - so manually inline Logger_Backtrace call */ + Logger_Log(&backtrace); + Logger_Backtrace(&msg, ctx); + + DumpMisc(); + CloseLogFile(); + + msg.buffer[msg.length] = '\0'; + Window_ShowDialog("We're sorry", msg.buffer); + Process_Exit(result); +} + +void Logger_Abort(const char* raw_msg) { Logger_Abort2(0, raw_msg); } + +void Logger_FailToStart(const char* raw_msg) { + cc_string msg = String_FromReadonly(raw_msg); + + Window_ShowDialog("Failed to start ClassiCube", raw_msg); + LogCrashHeader(); + Logger_Log(&msg); + Process_Exit(1); +} + + +#if defined CC_BACKTRACE_UNWIND +#include <unwind.h> + +static _Unwind_Reason_Code UnwindFrame(struct _Unwind_Context* ctx, void* arg) { + cc_uintptr addr = _Unwind_GetIP(ctx); + if (!addr) return _URC_END_OF_STACK; + + DumpFrame((cc_string*)arg, (void*)addr); + return _URC_NO_REASON; +} + +void Logger_Backtrace(cc_string* trace, void* ctx) { + _Unwind_Backtrace(UnwindFrame, trace); + String_AppendConst(trace, _NL); +} +#endif + +#if defined CC_BACKTRACE_BUILTIN +static CC_NOINLINE void* GetReturnAddress(int level) { + /* "... a value of 0 yields the return address of the current function, a value of 1 yields the return address of the caller of the current function" */ + switch(level) { + case 0: return __builtin_return_address(1); + case 1: return __builtin_return_address(2); + case 2: return __builtin_return_address(3); + case 3: return __builtin_return_address(4); + case 4: return __builtin_return_address(5); + case 5: return __builtin_return_address(6); + case 6: return __builtin_return_address(7); + case 7: return __builtin_return_address(8); + case 8: return __builtin_return_address(9); + case 9: return __builtin_return_address(10); + case 10: return __builtin_return_address(11); + case 11: return __builtin_return_address(12); + case 12: return __builtin_return_address(13); + case 13: return __builtin_return_address(14); + case 14: return __builtin_return_address(15); + case 15: return __builtin_return_address(16); + case 16: return __builtin_return_address(17); + case 17: return __builtin_return_address(18); + case 18: return __builtin_return_address(19); + case 19: return __builtin_return_address(20); + case 20: return __builtin_return_address(21); + default: return NULL; + } +} + +static CC_NOINLINE void* GetFrameAddress(int level) { + /* "... a value of 0 yields the frame address of the current function, a value of 1 yields the frame address of the caller of the current function, and so forth." */ + switch(level) { + case 0: return __builtin_frame_address(1); + case 1: return __builtin_frame_address(2); + case 2: return __builtin_frame_address(3); + case 3: return __builtin_frame_address(4); + case 4: return __builtin_frame_address(5); + case 5: return __builtin_frame_address(6); + case 6: return __builtin_frame_address(7); + case 7: return __builtin_frame_address(8); + case 8: return __builtin_frame_address(9); + case 9: return __builtin_frame_address(10); + case 10: return __builtin_frame_address(11); + case 11: return __builtin_frame_address(12); + case 12: return __builtin_frame_address(13); + case 13: return __builtin_frame_address(14); + case 14: return __builtin_frame_address(15); + case 15: return __builtin_frame_address(16); + case 16: return __builtin_frame_address(17); + case 17: return __builtin_frame_address(18); + case 18: return __builtin_frame_address(19); + case 19: return __builtin_frame_address(20); + case 20: return __builtin_frame_address(21); + default: return NULL; + } +} + +void Logger_Backtrace(cc_string* trace, void* ctx) { + void* addrs[MAX_BACKTRACE_FRAMES]; + int i, frames; + + /* See https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */ + /* Note "Calling this function with a nonzero argument can have unpredictable effects, including crashing the calling program" */ + /* So this probably only works on x86/x86_64 */ + for (i = 0; GetFrameAddress(i + 1) && i < MAX_BACKTRACE_FRAMES; i++) + { + addrs[i] = GetReturnAddress(i); + if (!addrs[i]) break; + } + + frames = i; + for (i = 0; i < frames; i++) { + DumpFrame(trace, addrs[i]); + } + String_AppendConst(trace, _NL); +} +#endif |