// Based on https://gist.github.com/jvranish/4441299
#ifdef _WIN32 // Pierre-Marie Baty -- added compile guard
// this has to be first
#include <windows.h>
#include <imagehlp.h>
#include "harness/os.h"
#include <assert.h>
#include <direct.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN64
#define Esp Rsp
#define Eip Rip
#define Ebp Rbp
#endif
void dr_dprintf(char* fmt_string, ...);
static int stack_nbr = 0;
static char windows_program_name[1024];
static char dirname_buf[_MAX_DIR];
static char fname_buf[_MAX_FNAME];
static int addr2line(char const* const program_name, void const* const addr) {
char addr2line_cmd[512] = { 0 };
sprintf(addr2line_cmd
, "addr2line -f -p -e %.256s %p", program_name
, addr
);
fprintf(stderr
, "%d: ", stack_nbr
++);
}
static void print_stacktrace(CONTEXT* context) {
#if 0 // Pierre-Marie Baty -- looks like this doesn't build right away on Windows
SymInitialize(GetCurrentProcess(), 0, true);
STACKFRAME frame = { 0 };
/* setup initial stack frame */
frame.AddrPC.Offset = context->Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrStack.Offset = context->Esp;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context->Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
while (StackWalk(IMAGE_FILE_MACHINE_I386,
GetCurrentProcess(),
GetCurrentThread(),
&frame,
context,
0,
SymFunctionTableAccess,
SymGetModuleBase,
0)) {
addr2line(windows_program_name, (void*)frame.AddrPC.Offset);
}
SymCleanup(GetCurrentProcess());
#endif // 0
}
static LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo) {
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
fputs("Error: EXCEPTION_ACCESS_VIOLATION\n", stderr
);
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
fputs("Error: EXCEPTION_ARRAY_BOUNDS_EXCEEDED\n", stderr
);
break;
case EXCEPTION_BREAKPOINT:
fputs("Error: EXCEPTION_BREAKPOINT\n", stderr
);
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
fputs("Error: EXCEPTION_DATATYPE_MISALIGNMENT\n", stderr
);
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
fputs("Error: EXCEPTION_FLT_DENORMAL_OPERAND\n", stderr
);
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
fputs("Error: EXCEPTION_FLT_DIVIDE_BY_ZERO\n", stderr
);
break;
case EXCEPTION_FLT_INEXACT_RESULT:
fputs("Error: EXCEPTION_FLT_INEXACT_RESULT\n", stderr
);
break;
case EXCEPTION_FLT_INVALID_OPERATION:
fputs("Error: EXCEPTION_FLT_INVALID_OPERATION\n", stderr
);
break;
case EXCEPTION_FLT_OVERFLOW:
fputs("Error: EXCEPTION_FLT_OVERFLOW\n", stderr
);
break;
case EXCEPTION_FLT_STACK_CHECK:
fputs("Error: EXCEPTION_FLT_STACK_CHECK\n", stderr
);
break;
case EXCEPTION_FLT_UNDERFLOW:
fputs("Error: EXCEPTION_FLT_UNDERFLOW\n", stderr
);
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
fputs("Error: EXCEPTION_ILLEGAL_INSTRUCTION\n", stderr
);
break;
case EXCEPTION_IN_PAGE_ERROR:
fputs("Error: EXCEPTION_IN_PAGE_ERROR\n", stderr
);
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
fputs("Error: EXCEPTION_INT_DIVIDE_BY_ZERO\n", stderr
);
break;
case EXCEPTION_INT_OVERFLOW:
fputs("Error: EXCEPTION_INT_OVERFLOW\n", stderr
);
break;
case EXCEPTION_INVALID_DISPOSITION:
fputs("Error: EXCEPTION_INVALID_DISPOSITION\n", stderr
);
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
fputs("Error: EXCEPTION_NONCONTINUABLE_EXCEPTION\n", stderr
);
break;
case EXCEPTION_PRIV_INSTRUCTION:
fputs("Error: EXCEPTION_PRIV_INSTRUCTION\n", stderr
);
break;
case EXCEPTION_SINGLE_STEP:
fputs("Error: EXCEPTION_SINGLE_STEP\n", stderr
);
break;
case EXCEPTION_STACK_OVERFLOW:
fputs("Error: EXCEPTION_STACK_OVERFLOW\n", stderr
);
break;
default:
fputs("Error: Unrecognized Exception\n", stderr
);
break;
}
/* If this is a stack overflow then we can't walk the stack, so just show
where the error happened */
if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) {
print_stacktrace(ExceptionInfo->ContextRecord);
} else {
addr2line(windows_program_name, (void*)ExceptionInfo->ContextRecord->Eip);
}
return EXCEPTION_EXECUTE_HANDLER;
}
void OS_InstallSignalHandler(char* program_name) {
strcpy(windows_program_name
, program_name
);
SetUnhandledExceptionFilter(windows_exception_handler);
}
FILE* OS_fopen(const char* pathname, const char* mode) {
FILE* f;
errno_t err;
f = NULL;
err = fopen_s(&f, pathname, mode);
if (err != 0) {
}
return f;
}
size_t OS_ConsoleReadPassword(char* pBuffer, size_t pBufferLen) {
// FIXME: unsafe implementation (echos the password)
pBuffer[0] = '\0';
fgets(pBuffer
, pBufferLen
, stdin
);
}
char* OS_Dirname(const char* path) {
_splitpath(path, NULL, dirname_buf, NULL, NULL);
return dirname_buf;
}
char* OS_Basename(const char* path) {
_splitpath(path, NULL, NULL, fname_buf, NULL);
return fname_buf;
}
#endif // _WIN32