|
@ -63,16 +63,22 @@ void trace(String* string) { |
|
|
#define BACKTRACE_MAX_FUNCTION_NAME_LENGTH 1024
|
|
|
#define BACKTRACE_MAX_FUNCTION_NAME_LENGTH 1024
|
|
|
HANDLE processHandle = GetCurrentProcess(); |
|
|
HANDLE processHandle = GetCurrentProcess(); |
|
|
SymInitialize(processHandle, null, true); |
|
|
SymInitialize(processHandle, null, true); |
|
|
|
|
|
SymSetOptions(SYMOPT_LOAD_LINES); |
|
|
|
|
|
|
|
|
void* stack[BACKTRACE_MAX_FRAMES]; |
|
|
void* stack[BACKTRACE_MAX_FRAMES]; |
|
|
unsigned short numFrames = CaptureStackBackTrace(0, BACKTRACE_MAX_FRAMES, stack, null); |
|
|
unsigned short numFrames = CaptureStackBackTrace(0, BACKTRACE_MAX_FRAMES, stack, null); |
|
|
|
|
|
|
|
|
char buffer[sizeof(SYMBOL_INFO) + (BACKTRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR)]; |
|
|
|
|
|
|
|
|
char buffer[sizeof(SYMBOL_INFO) + BACKTRACE_MAX_FUNCTION_NAME_LENGTH*sizeof(TCHAR)]; |
|
|
SYMBOL_INFO* symbol = (SYMBOL_INFO*) buffer; |
|
|
SYMBOL_INFO* symbol = (SYMBOL_INFO*) buffer; |
|
|
symbol->MaxNameLen = BACKTRACE_MAX_FUNCTION_NAME_LENGTH; |
|
|
symbol->MaxNameLen = BACKTRACE_MAX_FUNCTION_NAME_LENGTH; |
|
|
symbol->SizeOfStruct = sizeof(SYMBOL_INFO); |
|
|
symbol->SizeOfStruct = sizeof(SYMBOL_INFO); |
|
|
|
|
|
|
|
|
DWORD displacement; |
|
|
|
|
|
|
|
|
// @TODO I believe that 'displacement' and line.LineNumber are supposed to be source code column numbers
|
|
|
|
|
|
// and line numbers respectively, but displacement doesn't work at all (seems like) and line.LineNumber
|
|
|
|
|
|
// is consistently off by a few lines. Perhaps it's post-processed source? I don't know. For now,
|
|
|
|
|
|
// filename + line.LineNUmber are printed and we hope that's enough, and understand the line #s are only
|
|
|
|
|
|
// approximate.
|
|
|
|
|
|
DWORD displacement; |
|
|
IMAGEHLP_LINE64 line; |
|
|
IMAGEHLP_LINE64 line; |
|
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); |
|
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); |
|
|
for (u32 i = 0; i < numFrames; i++) { |
|
|
for (u32 i = 0; i < numFrames; i++) { |
|
@ -81,25 +87,53 @@ void trace(String* string) { |
|
|
|
|
|
|
|
|
if (SymGetLineFromAddr64(processHandle, address, &displacement, &line)) { |
|
|
if (SymGetLineFromAddr64(processHandle, address, &displacement, &line)) { |
|
|
if (string == null) { |
|
|
if (string == null) { |
|
|
print("\tat %s in %s: line: %lu: address %0x%0X\n", symbol->Name, line.FileName, line.LineNumber, symbol->Address); |
|
|
|
|
|
|
|
|
print(" %-30s %s:%u\n", symbol->Name, line.FileName, line.LineNumber); |
|
|
|
|
|
|
|
|
} else { |
|
|
} else { |
|
|
string->appendf("\tat %s in %s: line: %lu: address %0x%0X\n", symbol->Name, line.FileName, line.LineNumber, symbol->Address); |
|
|
|
|
|
|
|
|
string->appendf(" %-30s %s:%u\n", symbol->Name, line.FileName, line.LineNumber); |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
if (string == null) { |
|
|
if (string == null) { |
|
|
print("\tSymGetLineFromAddr64 returned error code %lu.\n", GetLastError()); |
|
|
|
|
|
print("\tat %s, address 0x%0X.\n", symbol->Name, symbol->Address); |
|
|
|
|
|
|
|
|
warn("SymGetLineFromAddr64 returned error code %lu.\n", GetLastError()); |
|
|
|
|
|
print(" %-30s unknown file\n", symbol->Name); |
|
|
|
|
|
|
|
|
} else { |
|
|
} else { |
|
|
string->appendf("\tSymGetLineFromAddr64 returned error code %lu.\n", GetLastError()); |
|
|
|
|
|
string->appendf("\tat %s, address 0x%0X.\n", symbol->Name, symbol->Address); |
|
|
|
|
|
|
|
|
warn("SymGetLineFromAddr64 returned error code %lu.\n", GetLastError()); |
|
|
|
|
|
string->appendf(" %-30s unknown file\n", symbol->Name); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
#undef BACKTRACE_MAX_FUNCTION_NAME_LENGTH
|
|
|
#undef BACKTRACE_MAX_FUNCTION_NAME_LENGTH
|
|
|
} |
|
|
} |
|
|
|
|
|
#include <Minidumpapiset.h>
|
|
|
|
|
|
#include <tchar.h>
|
|
|
|
|
|
void writeMinidump(void* exceptionPointers) { // 'EXCEPTION_POINTERS*' actually
|
|
|
|
|
|
// create a file
|
|
|
|
|
|
EXCEPTION_POINTERS* ep = (EXCEPTION_POINTERS*) exceptionPointers; |
|
|
|
|
|
HANDLE hFile = CreateFile(_T("MiniDump.dmp"), GENERIC_READ | GENERIC_WRITE, 0, null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, null); |
|
|
|
|
|
|
|
|
|
|
|
if ((hFile != null) && (hFile != INVALID_HANDLE_VALUE)) { |
|
|
|
|
|
// carry on with creating the minidump
|
|
|
|
|
|
MINIDUMP_EXCEPTION_INFORMATION mdei; |
|
|
|
|
|
mdei.ThreadId = GetCurrentThreadId(); |
|
|
|
|
|
mdei.ExceptionPointers = ep; |
|
|
|
|
|
mdei.ClientPointers = FALSE; |
|
|
|
|
|
|
|
|
|
|
|
MINIDUMP_TYPE mdt = MiniDumpNormal; |
|
|
|
|
|
BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt, (ep != 0) ? &mdei : 0, 0, 0); |
|
|
|
|
|
if (!rv) { |
|
|
|
|
|
println(_T("MiniDumpWriteDump failed. Error: %u"), GetLastError()); |
|
|
|
|
|
} else { |
|
|
|
|
|
println(_T("MiniDump created.")); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
CloseHandle(hFile); |
|
|
|
|
|
} else { |
|
|
|
|
|
println(_T("Failed to CreateFile for MiniDump. Error: %u"), GetLastError()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
#else
|
|
|
#else
|
|
|
|
|
|
void writeMinidump(void* exceptionPointers) {} // stub... does nothing on Unix
|
|
|
// OSX and Linux stacktrace stuff.
|
|
|
// OSX and Linux stacktrace stuff.
|
|
|
#include <execinfo.h> // backtrace, backtrace_symbols
|
|
|
#include <execinfo.h> // backtrace, backtrace_symbols
|
|
|
#include <cxxabi.h> // abi::__cxa_demangle
|
|
|
#include <cxxabi.h> // abi::__cxa_demangle
|
|
@ -169,17 +203,17 @@ void trace(String* string) { |
|
|
continue; |
|
|
continue; |
|
|
|
|
|
|
|
|
} else { |
|
|
} else { |
|
|
println("warning: failed to demangle name: %s, exit status of attempt: %d", traces[i], status); |
|
|
|
|
|
|
|
|
warn("warning: failed to demangle name: %s, exit status of attempt: %d", traces[i], status); |
|
|
// just write back the original trace, un-demangled.
|
|
|
// just write back the original trace, un-demangled.
|
|
|
trace = traces[i]; |
|
|
trace = traces[i]; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (string == null) { |
|
|
if (string == null) { |
|
|
print("%s\n", trace); |
|
|
|
|
|
|
|
|
print(" %s\n", trace); |
|
|
|
|
|
|
|
|
} else { |
|
|
} else { |
|
|
string->appendf("%s\n", trace); |
|
|
|
|
|
|
|
|
string->appendf(" %s\n", trace); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -227,35 +261,6 @@ void setCustomDieBehavior(void (*dieBehavior)(const char* string)) { |
|
|
customDie = dieBehavior; |
|
|
customDie = dieBehavior; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
#include <Minidumpapiset.h>
|
|
|
|
|
|
#include <tchar.h>
|
|
|
|
|
|
static void writeMinidump(EXCEPTION_POINTERS* pep) { |
|
|
|
|
|
// create a file
|
|
|
|
|
|
HANDLE hFile = CreateFile(_T("MiniDump.dmp"), GENERIC_READ | GENERIC_WRITE, 0, null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, null); |
|
|
|
|
|
|
|
|
|
|
|
if ((hFile != null) && (hFile != INVALID_HANDLE_VALUE)) { |
|
|
|
|
|
// carry on with creating the minidump
|
|
|
|
|
|
MINIDUMP_EXCEPTION_INFORMATION mdei; |
|
|
|
|
|
mdei.ThreadId = GetCurrentThreadId(); |
|
|
|
|
|
mdei.ExceptionPointers = pep; |
|
|
|
|
|
mdei.ClientPointers = FALSE; |
|
|
|
|
|
|
|
|
|
|
|
MINIDUMP_TYPE mdt = MiniDumpNormal; |
|
|
|
|
|
BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt, (pep != 0) ? &mdei : 0, 0, 0); |
|
|
|
|
|
if (!rv) { |
|
|
|
|
|
println(_T("MiniDumpWriteDump failed. Error: %u"), GetLastError()); |
|
|
|
|
|
} else { |
|
|
|
|
|
println(_T("MiniDump created.")); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
CloseHandle(hFile); |
|
|
|
|
|
} else { |
|
|
|
|
|
println(_T("Failed to CreateFile for MiniDump. Error: %u"), GetLastError()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// for fatal errors which may occur at runtime, even on a release binary.
|
|
|
// for fatal errors which may occur at runtime, even on a release binary.
|
|
|
// if a fatal error should not occur at runtime on a release binary, consider preferring 'massert'
|
|
|
// if a fatal error should not occur at runtime on a release binary, consider preferring 'massert'
|
|
|
// it's unclear when you should use asserts vs. die actually. idk man, they kinda do the same thing right now
|
|
|
// it's unclear when you should use asserts vs. die actually. idk man, they kinda do the same thing right now
|
|
|