Save
This commit is contained in:
@@ -1,470 +1,470 @@
|
||||
#include "core_platform.h"
|
||||
#include "core_ctx.h"
|
||||
|
||||
#if PLATFORM_WASM && PLATFORM_EMSCRIPTEN == 0
|
||||
#define gb_wasm_export __attribute__((visibility("default")))
|
||||
#define fn_wasm_export __attribute__((visibility("default")))
|
||||
#define fn_wasm_import
|
||||
|
||||
fn_wasm_import void wasm_alert(isize ptr, i32 len);
|
||||
fn_wasm_import f64 wasm_parse_float(isize str, i32 len);
|
||||
fn_wasm_import void wasm_write_to_console(isize str, i32 len);
|
||||
|
||||
extern char __heap_base;
|
||||
|
||||
fn void *os_vmem_reserve(usize size) {
|
||||
unused(size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_commit(void *p, usize size) {
|
||||
unused(p);
|
||||
unused(size);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_release(void *p) {
|
||||
unused(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_decommit(void *p, usize size) {
|
||||
unused(p);
|
||||
unused(size);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void os_error_box(char *str) {
|
||||
wasm_alert((isize)str, str_len(str));
|
||||
}
|
||||
|
||||
fn void os_console_log(char *str) {
|
||||
s8_t string = s8_from_char(str);
|
||||
if (s8_ends_with(string, s8("\n"))) {
|
||||
string = s8_chop(string, 1);
|
||||
}
|
||||
wasm_write_to_console((isize)string.str, (i32)string.len);
|
||||
}
|
||||
|
||||
fn f64 os_parse_float(char *str) {
|
||||
return wasm_parse_float((isize)str, str_len((char *)str));
|
||||
}
|
||||
|
||||
fn void os_core_small_init(ma_arena_t *arena) {
|
||||
tcx = ma_push_type(arena, thread_ctx_t);
|
||||
tcx->log.break_on_error = true;
|
||||
tcx->log.break_on_fatal = true;
|
||||
tcx->log.break_on_warning = true;
|
||||
tcx->log.print_file_and_line = true;
|
||||
tcx->log.log_proc = default_log_proc;
|
||||
}
|
||||
|
||||
fn void os_core_init(void) {
|
||||
ma_arena_t perm = {0};
|
||||
isize page_size = kib(64);
|
||||
isize page_count = __builtin_wasm_memory_size(0);
|
||||
u8 *memory = (u8 *)&__heap_base;
|
||||
usize memory_size = page_count * (page_size) - (isize)memory;
|
||||
perm.data = memory;
|
||||
perm.commit = perm.reserve = memory_size;
|
||||
os_core_small_init(&perm);
|
||||
tcx->perm = perm;
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->temp, mib(16));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[0], mib(2));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[1], kib(256));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[2], kib(64));
|
||||
debugf("memory size = %lld", memory_size);
|
||||
}
|
||||
#elif PLATFORM_WINDOWS
|
||||
#pragma comment(lib, "user32.lib")
|
||||
#pragma comment(lib, "DbgHelp.lib")
|
||||
#pragma comment(lib, "Shell32.lib")
|
||||
#pragma comment(lib, "Ole32.lib")
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <Shlobj.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
typedef struct b_stacktrace_tag* b_stacktrace_handle;
|
||||
|
||||
/*
|
||||
Returns a stack-trace handle from the point of view of the caller which
|
||||
can be expanded to a string via b_stacktrace_to_string.
|
||||
The handle is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
fn b_stacktrace_handle b_stacktrace_get();
|
||||
|
||||
/*
|
||||
Converts a stack-trace handle to a human-readable string.
|
||||
The string is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
fn char* b_stacktrace_to_string(b_stacktrace_handle stacktrace);
|
||||
|
||||
/*
|
||||
Returns a human-readable stack-trace string from the point of view of the
|
||||
caller.
|
||||
The string is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
fn char* b_stacktrace_get_string(void);
|
||||
|
||||
/* version */
|
||||
|
||||
typedef struct print_buf {
|
||||
char* buf;
|
||||
int pos;
|
||||
int size;
|
||||
} print_buf;
|
||||
|
||||
static print_buf buf_init(void) {
|
||||
print_buf ret = {
|
||||
(char*) malloc(1024),
|
||||
0,
|
||||
1024
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void buf_printf(print_buf* b, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const int len = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
const int new_end = b->pos + len;
|
||||
|
||||
if (new_end > b->size) {
|
||||
while (new_end > b->size) b->size *= 2;
|
||||
b->buf = (char*)realloc(b->buf, b->size);
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
b->pos += vsnprintf(b->buf + b->pos, len, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
char* b_stacktrace_get_string(void) {
|
||||
b_stacktrace_handle h = b_stacktrace_get();
|
||||
char* ret = b_stacktrace_to_string(h);
|
||||
free(h);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define B_STACKTRACE_MAX_DEPTH 1024
|
||||
|
||||
#define B_STACKTRACE_ERROR_FLAG ((DWORD64)1 << 63)
|
||||
|
||||
typedef struct b_stacktrace_entry {
|
||||
DWORD64 AddrPC_Offset;
|
||||
DWORD64 AddrReturn_Offset;
|
||||
} b_stacktrace_entry;
|
||||
|
||||
static int SymInitialize_called = 0;
|
||||
|
||||
b_stacktrace_handle b_stacktrace_get(void) {
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
CONTEXT context;
|
||||
STACKFRAME64 frame; /* in/out stackframe */
|
||||
DWORD imageType;
|
||||
b_stacktrace_entry* ret = (b_stacktrace_entry*)malloc(B_STACKTRACE_MAX_DEPTH * sizeof(b_stacktrace_entry));
|
||||
int i = 0;
|
||||
|
||||
if (!SymInitialize_called) {
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
SymInitialize_called = 1;
|
||||
}
|
||||
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
#ifdef _M_IX86
|
||||
imageType = IMAGE_FILE_MACHINE_I386;
|
||||
frame.AddrPC.Offset = context.Eip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Ebp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Esp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Rsp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_IA64
|
||||
imageType = IMAGE_FILE_MACHINE_IA64;
|
||||
frame.AddrPC.Offset = context.StIIP;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.IntSp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrBStore.Offset = context.RsBSP;
|
||||
frame.AddrBStore.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.IntSp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#else
|
||||
#error "Platform not supported!"
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
b_stacktrace_entry* cur = ret + i++;
|
||||
if (i == B_STACKTRACE_MAX_DEPTH) {
|
||||
cur->AddrPC_Offset = 0;
|
||||
cur->AddrReturn_Offset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!StackWalk64(imageType, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
|
||||
cur->AddrPC_Offset = frame.AddrPC.Offset;
|
||||
cur->AddrReturn_Offset = B_STACKTRACE_ERROR_FLAG; /* mark error */
|
||||
cur->AddrReturn_Offset |= GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
cur->AddrPC_Offset = frame.AddrPC.Offset;
|
||||
cur->AddrReturn_Offset = frame.AddrReturn.Offset;
|
||||
|
||||
if (frame.AddrReturn.Offset == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (b_stacktrace_handle)(ret);
|
||||
}
|
||||
|
||||
char* b_stacktrace_to_string(b_stacktrace_handle h) {
|
||||
const b_stacktrace_entry* entries = (b_stacktrace_entry*)h;
|
||||
int i = 0;
|
||||
HANDLE process = GetCurrentProcess();
|
||||
print_buf out = buf_init();
|
||||
IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + 1024);
|
||||
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||
symbol->MaxNameLength = 1024;
|
||||
|
||||
while (1) {
|
||||
IMAGEHLP_LINE64 lineData;
|
||||
DWORD lineOffset = 0;
|
||||
DWORD64 symOffset = 0;
|
||||
const b_stacktrace_entry* cur = entries + i++;
|
||||
|
||||
if (cur->AddrReturn_Offset & B_STACKTRACE_ERROR_FLAG) {
|
||||
DWORD error = cur->AddrReturn_Offset & 0xFFFFFFFF;
|
||||
buf_printf(&out, "StackWalk64 error: %d @ %p\n", error, cur->AddrPC_Offset);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cur->AddrPC_Offset == cur->AddrReturn_Offset) {
|
||||
buf_printf(&out, "Stack overflow @ %p\n", cur->AddrPC_Offset);
|
||||
break;
|
||||
}
|
||||
|
||||
SymGetLineFromAddr64(process, cur->AddrPC_Offset, &lineOffset, &lineData);
|
||||
buf_printf(&out, "%s(%d): ", lineData.FileName, lineData.LineNumber);
|
||||
|
||||
if (SymGetSymFromAddr64(process, cur->AddrPC_Offset, &symOffset, symbol)) {
|
||||
buf_printf(&out, "%s\n", symbol->Name);
|
||||
}
|
||||
else {
|
||||
buf_printf(&out, " Unkown symbol @ %p\n", cur->AddrPC_Offset);
|
||||
}
|
||||
|
||||
if (cur->AddrReturn_Offset == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(symbol);
|
||||
return out.buf;
|
||||
}
|
||||
|
||||
fn void *os_vmem_reserve(usize size) {
|
||||
void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_commit(void *p, usize size) {
|
||||
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
return result ? true : false;
|
||||
}
|
||||
fn b32 os_vmem_release(void *p) {
|
||||
BOOL result = VirtualFree(p, 0, MEM_RELEASE);
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_decommit(void *p, usize size) {
|
||||
BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
fn void os_error_box(char *str) {
|
||||
OutputDebugStringA(str);
|
||||
fprintf(stderr, "%s", str);
|
||||
fflush(stderr);
|
||||
MessageBoxA(NULL, str, "fatal error", MB_OK);
|
||||
}
|
||||
|
||||
fn void os_console_log(char *str) {
|
||||
OutputDebugStringA(str);
|
||||
fputs(str, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
fn f64 os_parse_float(char *str) {
|
||||
return strtod(str, NULL);
|
||||
}
|
||||
|
||||
fn void os_win32_crash_handler(int signal) {
|
||||
printf("signal: %d\n", signal);
|
||||
puts(b_stacktrace_get_string());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fn void os_win32_register_crash_handler() {
|
||||
signal(SIGSEGV, os_win32_crash_handler);
|
||||
signal(SIGABRT, os_win32_crash_handler);
|
||||
}
|
||||
|
||||
fn void os_core_small_init(ma_arena_t *arena) {
|
||||
tcx = ma_push_type(arena, thread_ctx_t);
|
||||
tcx->log.break_on_error = true;
|
||||
tcx->log.break_on_fatal = true;
|
||||
tcx->log.break_on_warning = true;
|
||||
tcx->log.print_file_and_line = true;
|
||||
tcx->log.log_proc = default_log_proc;
|
||||
os_win32_register_crash_handler();
|
||||
}
|
||||
|
||||
fn void os_core_init(void) {
|
||||
ma_arena_t perm = {0};
|
||||
os_core_small_init(&perm);
|
||||
tcx->perm = perm;
|
||||
}
|
||||
#elif PLATFORM_POSIX
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#if !PLATFORM_EMSCRIPTEN
|
||||
#include <execinfo.h>
|
||||
#include <backtrace.h>
|
||||
#endif
|
||||
fn void *os_vmem_reserve(usize size) {
|
||||
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
return result == MAP_FAILED ? NULL : result;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_commit(void *ptr, usize size) {
|
||||
mprotect(ptr, size, PROT_READ|PROT_WRITE);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_release(void *ptr) {
|
||||
int result = munmap(ptr, 0);
|
||||
return result == 0 ? true : false;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_decommit(void *ptr, usize size) {
|
||||
madvise(ptr, size, MADV_DONTNEED);
|
||||
mprotect(ptr, size, PROT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void os_error_box(char *str) {
|
||||
fprintf(stderr, "%s", str);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
fn void os_console_log(char *str) {
|
||||
fputs(str, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
fn f64 os_parse_float(char *str) {
|
||||
return strtod(str, NULL);
|
||||
}
|
||||
|
||||
gb struct backtrace_state *backtrace_state = NULL;
|
||||
|
||||
fn void os_core_backtrace_error_callback(void *data, const char *msg, int errnum) {
|
||||
unused(data);
|
||||
errorf("libbacktrace error: %s (errnum: %d)\n", msg, errnum);
|
||||
}
|
||||
|
||||
fn int os_core_backtrace_print_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {
|
||||
unused(data);
|
||||
unused(pc);
|
||||
b32 printed = false;
|
||||
if (filename != NULL) {
|
||||
char buffer[512];
|
||||
char *f = realpath(filename, buffer);
|
||||
printf("%s:%d:1: ", f, lineno);
|
||||
printed = true;
|
||||
}
|
||||
if (function != NULL) {
|
||||
printf("%s", function);
|
||||
printed = true;
|
||||
}
|
||||
if (printed) {
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !PLATFORM_EMSCRIPTEN
|
||||
fn void os_unix_crash_handler(int signal, siginfo_t* info, void* context) {
|
||||
unused(context);
|
||||
unused(signal);
|
||||
unused(info);
|
||||
backtrace_full(backtrace_state, 2, os_core_backtrace_print_callback, os_core_backtrace_error_callback, NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fn void os_unix_register_crash_handler() {
|
||||
backtrace_state = backtrace_create_state(NULL, 1, os_core_backtrace_error_callback, NULL);
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_sigaction = os_unix_crash_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
sigaction(SIGABRT, &sa, NULL);
|
||||
sigaction(SIGBUS, &sa, NULL);
|
||||
sigaction(SIGILL, &sa, NULL);
|
||||
sigaction(SIGFPE, &sa, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
fn void os_core_small_init(ma_arena_t *arena) {
|
||||
tcx = ma_push_type(arena, thread_ctx_t);
|
||||
tcx->log.break_on_error = true;
|
||||
tcx->log.break_on_fatal = true;
|
||||
tcx->log.break_on_warning = true;
|
||||
tcx->log.print_file_and_line = true;
|
||||
tcx->log.log_proc = default_log_proc;
|
||||
#if !PLATFORM_EMSCRIPTEN
|
||||
os_unix_register_crash_handler();
|
||||
#endif
|
||||
}
|
||||
|
||||
fn void os_core_init(void) {
|
||||
ma_arena_t perm = {0};
|
||||
os_core_small_init(&perm);
|
||||
tcx->perm = perm;
|
||||
}
|
||||
#endif
|
||||
#include "core_platform.h"
|
||||
#include "core_ctx.h"
|
||||
#include "stb_sprintf.h"
|
||||
|
||||
#if PLATFORM_WASM && PLATFORM_EMSCRIPTEN == 0
|
||||
#define gb_wasm_export __attribute__((visibility("default")))
|
||||
#define fn_wasm_export __attribute__((visibility("default")))
|
||||
#define fn_wasm_import
|
||||
|
||||
fn_wasm_import void wasm_alert(isize ptr, i32 len);
|
||||
fn_wasm_import f64 wasm_parse_float(isize str, i32 len);
|
||||
fn_wasm_import void wasm_write_to_console(isize str, i32 len);
|
||||
|
||||
extern char __heap_base;
|
||||
|
||||
fn void *os_vmem_reserve(usize size) {
|
||||
unused(size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_commit(void *p, usize size) {
|
||||
unused(p);
|
||||
unused(size);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_release(void *p) {
|
||||
unused(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_decommit(void *p, usize size) {
|
||||
unused(p);
|
||||
unused(size);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void os_error_box(char *str) {
|
||||
wasm_alert((isize)str, str_len(str));
|
||||
}
|
||||
|
||||
fn void os_console_log(char *str) {
|
||||
s8_t string = s8_from_char(str);
|
||||
if (s8_ends_with(string, s8("\n"))) {
|
||||
string = s8_chop(string, 1);
|
||||
}
|
||||
wasm_write_to_console((isize)string.str, (i32)string.len);
|
||||
}
|
||||
|
||||
fn f64 os_parse_float(char *str) {
|
||||
return wasm_parse_float((isize)str, str_len((char *)str));
|
||||
}
|
||||
|
||||
fn void os_core_small_init(ma_arena_t *arena) {
|
||||
tcx = ma_push_type(arena, thread_ctx_t);
|
||||
tcx->log.break_on_error = true;
|
||||
tcx->log.break_on_fatal = true;
|
||||
tcx->log.break_on_warning = true;
|
||||
tcx->log.print_file_and_line = true;
|
||||
tcx->log.log_proc = default_log_proc;
|
||||
}
|
||||
|
||||
fn void os_core_init(void) {
|
||||
ma_arena_t perm = {0};
|
||||
isize page_size = kib(64);
|
||||
isize page_count = __builtin_wasm_memory_size(0);
|
||||
u8 *memory = (u8 *)&__heap_base;
|
||||
usize memory_size = page_count * (page_size) - (isize)memory;
|
||||
perm.data = memory;
|
||||
perm.commit = perm.reserve = memory_size;
|
||||
os_core_small_init(&perm);
|
||||
tcx->perm = perm;
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->temp, mib(64));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[0], mib(2));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[1], kib(256));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[2], kib(64));
|
||||
debugf("memory size = %lld", memory_size);
|
||||
}
|
||||
#elif PLATFORM_WINDOWS
|
||||
#pragma comment(lib, "user32.lib")
|
||||
#pragma comment(lib, "DbgHelp.lib")
|
||||
#pragma comment(lib, "Shell32.lib")
|
||||
#pragma comment(lib, "Ole32.lib")
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <Shlobj.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
typedef struct b_stacktrace_tag* b_stacktrace_handle;
|
||||
|
||||
/*
|
||||
Returns a stack-trace handle from the point of view of the caller which
|
||||
can be expanded to a string via b_stacktrace_to_string.
|
||||
The handle is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
fn b_stacktrace_handle b_stacktrace_get();
|
||||
|
||||
/*
|
||||
Converts a stack-trace handle to a human-readable string.
|
||||
The string is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
fn char* b_stacktrace_to_string(b_stacktrace_handle stacktrace);
|
||||
|
||||
/*
|
||||
Returns a human-readable stack-trace string from the point of view of the
|
||||
caller.
|
||||
The string is allocated with `malloc` and needs to be freed with `free`
|
||||
*/
|
||||
fn char* b_stacktrace_get_string(void);
|
||||
|
||||
/* version */
|
||||
|
||||
typedef struct print_buf {
|
||||
char* buf;
|
||||
int pos;
|
||||
int size;
|
||||
} print_buf;
|
||||
|
||||
static print_buf buf_init(void) {
|
||||
print_buf ret = {
|
||||
(char*) malloc(1024),
|
||||
0,
|
||||
1024
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void buf_printf(print_buf* b, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const int len = stbsp_vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
const int new_end = b->pos + len;
|
||||
|
||||
if (new_end > b->size) {
|
||||
while (new_end > b->size) b->size *= 2;
|
||||
b->buf = (char*)realloc(b->buf, b->size);
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
b->pos += stbsp_vsnprintf(b->buf + b->pos, len, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
char* b_stacktrace_get_string(void) {
|
||||
b_stacktrace_handle h = b_stacktrace_get();
|
||||
char* ret = b_stacktrace_to_string(h);
|
||||
free(h);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define B_STACKTRACE_MAX_DEPTH 1024
|
||||
#define B_STACKTRACE_ERROR_FLAG ((DWORD64)1 << 63)
|
||||
|
||||
typedef struct b_stacktrace_entry {
|
||||
DWORD64 AddrPC_Offset;
|
||||
DWORD64 AddrReturn_Offset;
|
||||
} b_stacktrace_entry;
|
||||
|
||||
static int SymInitialize_called = 0;
|
||||
|
||||
b_stacktrace_handle b_stacktrace_get(void) {
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
CONTEXT context;
|
||||
STACKFRAME64 frame; /* in/out stackframe */
|
||||
DWORD imageType;
|
||||
b_stacktrace_entry* ret = (b_stacktrace_entry*)malloc(B_STACKTRACE_MAX_DEPTH * sizeof(b_stacktrace_entry));
|
||||
int i = 0;
|
||||
|
||||
if (!SymInitialize_called) {
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
SymInitialize_called = 1;
|
||||
}
|
||||
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
#ifdef _M_IX86
|
||||
imageType = IMAGE_FILE_MACHINE_I386;
|
||||
frame.AddrPC.Offset = context.Eip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Ebp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Esp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.Rsp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_IA64
|
||||
imageType = IMAGE_FILE_MACHINE_IA64;
|
||||
frame.AddrPC.Offset = context.StIIP;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context.IntSp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrBStore.Offset = context.RsBSP;
|
||||
frame.AddrBStore.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context.IntSp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
#else
|
||||
#error "Platform not supported!"
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
b_stacktrace_entry* cur = ret + i++;
|
||||
if (i == B_STACKTRACE_MAX_DEPTH) {
|
||||
cur->AddrPC_Offset = 0;
|
||||
cur->AddrReturn_Offset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!StackWalk64(imageType, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
|
||||
cur->AddrPC_Offset = frame.AddrPC.Offset;
|
||||
cur->AddrReturn_Offset = B_STACKTRACE_ERROR_FLAG; /* mark error */
|
||||
cur->AddrReturn_Offset |= GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
cur->AddrPC_Offset = frame.AddrPC.Offset;
|
||||
cur->AddrReturn_Offset = frame.AddrReturn.Offset;
|
||||
|
||||
if (frame.AddrReturn.Offset == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (b_stacktrace_handle)(ret);
|
||||
}
|
||||
|
||||
char* b_stacktrace_to_string(b_stacktrace_handle h) {
|
||||
const b_stacktrace_entry* entries = (b_stacktrace_entry*)h;
|
||||
int i = 0;
|
||||
HANDLE process = GetCurrentProcess();
|
||||
print_buf out = buf_init();
|
||||
IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + 1024);
|
||||
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||
symbol->MaxNameLength = 1024;
|
||||
|
||||
while (1) {
|
||||
IMAGEHLP_LINE64 lineData;
|
||||
DWORD lineOffset = 0;
|
||||
DWORD64 symOffset = 0;
|
||||
const b_stacktrace_entry* cur = entries + i++;
|
||||
|
||||
if (cur->AddrReturn_Offset & B_STACKTRACE_ERROR_FLAG) {
|
||||
DWORD error = cur->AddrReturn_Offset & 0xFFFFFFFF;
|
||||
buf_printf(&out, "StackWalk64 error: %d @ %p\n", error, cur->AddrPC_Offset);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cur->AddrPC_Offset == cur->AddrReturn_Offset) {
|
||||
buf_printf(&out, "Stack overflow @ %p\n", cur->AddrPC_Offset);
|
||||
break;
|
||||
}
|
||||
|
||||
SymGetLineFromAddr64(process, cur->AddrPC_Offset, &lineOffset, &lineData);
|
||||
buf_printf(&out, "%s(%d): ", lineData.FileName, lineData.LineNumber);
|
||||
|
||||
if (SymGetSymFromAddr64(process, cur->AddrPC_Offset, &symOffset, symbol)) {
|
||||
buf_printf(&out, "%s\n", symbol->Name);
|
||||
}
|
||||
else {
|
||||
buf_printf(&out, " Unkown symbol @ %p\n", cur->AddrPC_Offset);
|
||||
}
|
||||
|
||||
if (cur->AddrReturn_Offset == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(symbol);
|
||||
return out.buf;
|
||||
}
|
||||
|
||||
fn void *os_vmem_reserve(usize size) {
|
||||
void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_commit(void *p, usize size) {
|
||||
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
return result ? true : false;
|
||||
}
|
||||
fn b32 os_vmem_release(void *p) {
|
||||
BOOL result = VirtualFree(p, 0, MEM_RELEASE);
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_decommit(void *p, usize size) {
|
||||
BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
fn void os_error_box(char *str) {
|
||||
OutputDebugStringA(str);
|
||||
fprintf(stderr, "%s", str);
|
||||
fflush(stderr);
|
||||
MessageBoxA(NULL, str, "fatal error", MB_OK);
|
||||
}
|
||||
|
||||
fn void os_console_log(char *str) {
|
||||
OutputDebugStringA(str);
|
||||
fputs(str, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
fn f64 os_parse_float(char *str) {
|
||||
return strtod(str, NULL);
|
||||
}
|
||||
|
||||
fn void os_win32_crash_handler(int signal) {
|
||||
printf("signal: %d\n", signal);
|
||||
puts(b_stacktrace_get_string());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fn void os_win32_register_crash_handler() {
|
||||
signal(SIGSEGV, os_win32_crash_handler);
|
||||
signal(SIGABRT, os_win32_crash_handler);
|
||||
}
|
||||
|
||||
fn void os_core_small_init(ma_arena_t *arena) {
|
||||
tcx = ma_push_type(arena, thread_ctx_t);
|
||||
tcx->log.break_on_error = true;
|
||||
tcx->log.break_on_fatal = true;
|
||||
tcx->log.break_on_warning = true;
|
||||
tcx->log.print_file_and_line = true;
|
||||
tcx->log.log_proc = default_log_proc;
|
||||
os_win32_register_crash_handler();
|
||||
}
|
||||
|
||||
fn void os_core_init(void) {
|
||||
ma_arena_t perm = {0};
|
||||
os_core_small_init(&perm);
|
||||
tcx->perm = perm;
|
||||
}
|
||||
#elif PLATFORM_POSIX
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#if !PLATFORM_EMSCRIPTEN
|
||||
#include <execinfo.h>
|
||||
#include <backtrace.h>
|
||||
#endif
|
||||
fn void *os_vmem_reserve(usize size) {
|
||||
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
return result == MAP_FAILED ? NULL : result;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_commit(void *ptr, usize size) {
|
||||
mprotect(ptr, size, PROT_READ|PROT_WRITE);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_release(void *ptr) {
|
||||
int result = munmap(ptr, 0);
|
||||
return result == 0 ? true : false;
|
||||
}
|
||||
|
||||
fn b32 os_vmem_decommit(void *ptr, usize size) {
|
||||
madvise(ptr, size, MADV_DONTNEED);
|
||||
mprotect(ptr, size, PROT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void os_error_box(char *str) {
|
||||
fprintf(stderr, "%s", str);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
fn void os_console_log(char *str) {
|
||||
fputs(str, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
fn f64 os_parse_float(char *str) {
|
||||
return strtod(str, NULL);
|
||||
}
|
||||
|
||||
gb struct backtrace_state *backtrace_state = NULL;
|
||||
|
||||
fn void os_core_backtrace_error_callback(void *data, const char *msg, int errnum) {
|
||||
unused(data);
|
||||
errorf("libbacktrace error: %s (errnum: %d)\n", msg, errnum);
|
||||
}
|
||||
|
||||
fn int os_core_backtrace_print_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {
|
||||
unused(data);
|
||||
unused(pc);
|
||||
b32 printed = false;
|
||||
if (filename != NULL) {
|
||||
char buffer[512];
|
||||
char *f = realpath(filename, buffer);
|
||||
printf("%s:%d:1: ", f, lineno);
|
||||
printed = true;
|
||||
}
|
||||
if (function != NULL) {
|
||||
printf("%s", function);
|
||||
printed = true;
|
||||
}
|
||||
if (printed) {
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !PLATFORM_EMSCRIPTEN
|
||||
fn void os_unix_crash_handler(int signal, siginfo_t* info, void* context) {
|
||||
unused(context);
|
||||
unused(signal);
|
||||
unused(info);
|
||||
backtrace_full(backtrace_state, 2, os_core_backtrace_print_callback, os_core_backtrace_error_callback, NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fn void os_unix_register_crash_handler() {
|
||||
backtrace_state = backtrace_create_state(NULL, 1, os_core_backtrace_error_callback, NULL);
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_sigaction = os_unix_crash_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
sigaction(SIGABRT, &sa, NULL);
|
||||
sigaction(SIGBUS, &sa, NULL);
|
||||
sigaction(SIGILL, &sa, NULL);
|
||||
sigaction(SIGFPE, &sa, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
fn void os_core_small_init(ma_arena_t *arena) {
|
||||
tcx = ma_push_type(arena, thread_ctx_t);
|
||||
tcx->log.break_on_error = true;
|
||||
tcx->log.break_on_fatal = true;
|
||||
tcx->log.break_on_warning = true;
|
||||
tcx->log.print_file_and_line = true;
|
||||
tcx->log.log_proc = default_log_proc;
|
||||
#if !PLATFORM_EMSCRIPTEN
|
||||
os_unix_register_crash_handler();
|
||||
#endif
|
||||
}
|
||||
|
||||
fn void os_core_init(void) {
|
||||
ma_arena_t perm = {0};
|
||||
os_core_small_init(&perm);
|
||||
tcx->perm = perm;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user