debug functions (superassert with stack trace)
This was SVN commit r561.
This commit is contained in:
parent
a9864a5e66
commit
ed17ed9fb4
22
source/lib/sysdep/win/assert_dlg.h
Executable file
22
source/lib/sysdep/win/assert_dlg.h
Executable file
@ -0,0 +1,22 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by assert_dlg.rc
|
||||
//
|
||||
#define IDD_DIALOG1 101
|
||||
#define IDC_EDIT1 1001
|
||||
#define IDC_COPY 1002
|
||||
#define IDC_CONTINUE 2000
|
||||
#define IDC_SUPPRESS 2001
|
||||
#define IDC_BREAK 2002
|
||||
#define IDC_EXIT 2003
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1003
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
91
source/lib/sysdep/win/assert_dlg.rc
Executable file
91
source/lib/sysdep/win/assert_dlg.rc
Executable file
@ -0,0 +1,91 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "assert_dlg.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
#include <windows.h>
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"assert_dlg.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_DIALOG1 DIALOGEX 0, 0, 327, 260
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE |
|
||||
WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Program Error"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
PUSHBUTTON "&Continue",IDC_CONTINUE,7,239,50,14
|
||||
EDITTEXT IDC_EDIT1,7,7,313,204,ES_MULTILINE | ES_AUTOHSCROLL |
|
||||
ES_READONLY | ES_WANTRETURN | WS_VSCROLL
|
||||
PUSHBUTTON "Copy",IDC_COPY,270,212,50,14
|
||||
PUSHBUTTON "&Suppress",IDC_SUPPRESS,59,239,50,14
|
||||
PUSHBUTTON "&Break",IDC_BREAK,111,239,50,14
|
||||
PUSHBUTTON "E&xit",IDC_EXIT,163,239,50,14
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO
|
||||
BEGIN
|
||||
IDD_DIALOG1, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 320
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 253
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
13
source/lib/sysdep/win/dbghelp_funcs.h
Executable file
13
source/lib/sysdep/win/dbghelp_funcs.h
Executable file
@ -0,0 +1,13 @@
|
||||
FUNC(void, SymCleanup, (HANDLE))
|
||||
FUNC(BOOL, SymInitialize, (HANDLE, char*, int))
|
||||
FUNC(DWORD, SymSetOptions, (DWORD))
|
||||
FUNC(DWORD64, SymGetModuleBase64, (HANDLE, DWORD64))
|
||||
FUNC(PVOID, SymFunctionTableAccess64, (HANDLE, DWORD64))
|
||||
FUNC(BOOL, StackWalk64, (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64))
|
||||
FUNC(BOOL, SymGetLineFromAddr64, (HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*))
|
||||
FUNC(IMAGE_NT_HEADERS*, ImageNtHeader, (void*))
|
||||
|
||||
FUNC(BOOL, SymGetTypeInfo, (HANDLE, DWORD64, ULONG, IMAGEHLP_SYMBOL_TYPE_INFO, void*))
|
||||
FUNC(BOOL, SymFromAddr, (HANDLE, DWORD64, DWORD64*, SYMBOL_INFO*))
|
||||
FUNC(ULONG, SymSetContext, (HANDLE, IMAGEHLP_STACK_FRAME*, IMAGEHLP_CONTEXT*))
|
||||
FUNC(BOOL, SymEnumSymbols, (HANDLE, ULONG64, const char*, PSYM_ENUMERATESYMBOLS_CALLBACK, void*))
|
602
source/lib/sysdep/win/wdbg.cpp
Executable file
602
source/lib/sysdep/win/wdbg.cpp
Executable file
@ -0,0 +1,602 @@
|
||||
/*
|
||||
* stack walk, improved assert and exception handler
|
||||
* Copyright (c) 2002 Jan Wassenberg
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* Contact info:
|
||||
* Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||
* http://www.stud.uni-karlsruhe.de/~urkt/
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lib.h"
|
||||
|
||||
#include "win_internal.h"
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/*
|
||||
* voodoo programming. PDB debug info is a poorly documented mess.
|
||||
* see http://msdn.microsoft.com/msdnmag/issues/02/03/hood/default.aspx
|
||||
*/
|
||||
|
||||
|
||||
/* from DIA cvconst.h */
|
||||
typedef enum
|
||||
{
|
||||
btNoType = 0,
|
||||
btVoid = 1,
|
||||
btChar = 2,
|
||||
btWChar = 3,
|
||||
btInt = 6,
|
||||
btUInt = 7,
|
||||
btFloat = 8,
|
||||
btBCD = 9,
|
||||
btBool = 10,
|
||||
btLong = 13,
|
||||
btULong = 14,
|
||||
btCurrency = 25,
|
||||
btDate = 26,
|
||||
btVariant = 27,
|
||||
btComplex = 28,
|
||||
btBit = 29,
|
||||
btBSTR = 30,
|
||||
btHresult = 31
|
||||
}
|
||||
BasicType;
|
||||
|
||||
/*
|
||||
* function pointers
|
||||
*
|
||||
* we need to link against dbghelp dynamically - some systems may not
|
||||
* have v5.1. (if <= 5.0, type information is unavailable).
|
||||
* can't skip the dbghelp.h function prototypes, so we need to
|
||||
* name the function pointers differently (prepend _). Grr.
|
||||
*/
|
||||
#define FUNC(ret, name, params) ret (WINAPI *_##name) params;
|
||||
#include "dbghelp_funcs.h"
|
||||
#undef FUNC
|
||||
|
||||
|
||||
static HANDLE hProcess;
|
||||
static uintptr_t mod_base;
|
||||
|
||||
static char buf[8192]; /* buffer for stack trace */
|
||||
static char* out; /* current pos in buf */
|
||||
|
||||
|
||||
static void dbg_cleanup(void)
|
||||
{
|
||||
_SymCleanup(hProcess);
|
||||
}
|
||||
|
||||
|
||||
static void dbg_init()
|
||||
{
|
||||
/* already initialized */
|
||||
if(hProcess)
|
||||
return;
|
||||
|
||||
HMODULE hdbghelp = LoadLibrary("dbghelp.dll");
|
||||
|
||||
/* import functions */
|
||||
#define FUNC(ret, name, params) *(void**)&_##name = (void*)GetProcAddress(hdbghelp, #name);
|
||||
#include "dbghelp_funcs.h"
|
||||
#undef FUNC
|
||||
|
||||
hProcess = GetCurrentProcess();
|
||||
|
||||
_SymSetOptions(SYMOPT_DEFERRED_LOADS);
|
||||
_SymInitialize(hProcess, 0, TRUE);
|
||||
|
||||
atexit(dbg_cleanup);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* guess if addr points to a string
|
||||
* algorithm:
|
||||
* scan string; count # text chars vs. garbage
|
||||
*/
|
||||
static bool is_string_ptr(u64 addr)
|
||||
{
|
||||
/* early out for x86 */
|
||||
#ifdef _WIN32
|
||||
if(addr < 0x10000 || addr > 0x80000000)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
const char* str = (const char*)addr;
|
||||
|
||||
__try
|
||||
{
|
||||
int score = 0;
|
||||
for(;;)
|
||||
{
|
||||
const int c = *str++ & 0xff; /* signed char => promoted */
|
||||
if(isalnum(c))
|
||||
score += 1;
|
||||
else if(!c)
|
||||
return (score > 0); /* end of string reached */
|
||||
else if(!isprint(c))
|
||||
score -= 5;
|
||||
|
||||
/* give up */
|
||||
if(score <= -10)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
__except(1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* output the value of 'basic' types: string, (array of) float, int
|
||||
*
|
||||
* not reliably supported, due to broken debug info / bad documentation:
|
||||
* - multidimensional arrays (e.g. int[2][2] => len 16, elements 2)
|
||||
* - arrays of character arrays (would have rely on is_string_ptr)
|
||||
* - nested structures (invalid base type index)
|
||||
*/
|
||||
static void print_var(u32 type_idx, uint level, u64 addr)
|
||||
{
|
||||
// ?
|
||||
BasicType type;
|
||||
u32 base_idx = 0;
|
||||
if(!_SymGetTypeInfo(hProcess, mod_base, type_idx, TI_GET_BASETYPE, &type))
|
||||
if(_SymGetTypeInfo(hProcess, mod_base, type_idx, TI_GET_TYPEID, &base_idx))
|
||||
_SymGetTypeInfo(hProcess, mod_base, base_idx, TI_GET_BASETYPE, &type);
|
||||
|
||||
u64 len;
|
||||
_SymGetTypeInfo(hProcess, mod_base, level? base_idx : type_idx, TI_GET_LENGTH, &len);
|
||||
|
||||
u32 elements;
|
||||
_SymGetTypeInfo(hProcess, mod_base, level? base_idx : type_idx, TI_GET_COUNT, &elements);
|
||||
|
||||
char* fmt = "%I64X"; /* default: 64 bit integer */
|
||||
|
||||
out += sprintf(out, "= ");
|
||||
|
||||
/*
|
||||
* single string (character array)
|
||||
* len == elements => bool array | character array (probably string)
|
||||
*/
|
||||
if(len == elements && is_string_ptr(addr))
|
||||
{
|
||||
out += sprintf(out, "\"%s\"\r\n", (char*)addr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* array */
|
||||
if(elements)
|
||||
{
|
||||
len /= elements;
|
||||
out += sprintf(out, "{ ");
|
||||
}
|
||||
|
||||
next_element:
|
||||
|
||||
/* zero extend to 64 bit (little endian) */
|
||||
u64 data = 0;
|
||||
for(u64 i = 0; i < MIN(len,8); i++)
|
||||
data |= (u64)(*(u8*)(addr+i)) << (i*8);
|
||||
|
||||
/* float / double */
|
||||
if(type == btFloat)
|
||||
{
|
||||
/* convert floats to double (=> single printf call) */
|
||||
if(len == 4)
|
||||
{
|
||||
double double_data = (double)(*(float*)addr);
|
||||
data = *(u64*)&double_data;
|
||||
}
|
||||
fmt = "%lf";
|
||||
}
|
||||
/* string (char*) */
|
||||
else if(len == sizeof(void*) && is_string_ptr(data))
|
||||
{
|
||||
fmt = "\"%s\"";
|
||||
}
|
||||
|
||||
out += sprintf(out, fmt, data);
|
||||
|
||||
/* ascii char */
|
||||
if(data < 0x100 && isprint((int)data))
|
||||
out += sprintf(out, " ('%c')", (char)data);
|
||||
|
||||
if(elements)
|
||||
{
|
||||
addr += len; /* add stride */
|
||||
elements--;
|
||||
|
||||
if(elements == 0)
|
||||
out += sprintf(out, " }");
|
||||
else
|
||||
{
|
||||
out += sprintf(out, ", ");
|
||||
goto next_element;
|
||||
}
|
||||
}
|
||||
|
||||
out += sprintf(out, "\r\n");
|
||||
}
|
||||
|
||||
|
||||
/* print sym name; if not a basic type, recurse for each child */
|
||||
static void dump_sym(u32 type_idx, uint level, u64 addr)
|
||||
{
|
||||
u32 num_children = 0;
|
||||
_SymGetTypeInfo(hProcess, mod_base, type_idx, TI_GET_CHILDRENCOUNT, &num_children);
|
||||
|
||||
/* print type name / member name */
|
||||
WCHAR* type_name;
|
||||
if(_SymGetTypeInfo(hProcess, mod_base, type_idx, TI_GET_SYMNAME, &type_name))
|
||||
{
|
||||
out += sprintf(out, "%ls ", (char*)type_name); /* HACK: get rid of ICC warning */
|
||||
LocalFree(type_name);
|
||||
}
|
||||
|
||||
/* built-in type? yes - show its value, terminate recursion */
|
||||
if(!num_children)
|
||||
{
|
||||
print_var(type_idx, level, addr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* get children's type indices */
|
||||
static char child_buf[sizeof(TI_FINDCHILDREN_PARAMS) + 256*4];
|
||||
TI_FINDCHILDREN_PARAMS* children = (TI_FINDCHILDREN_PARAMS*)child_buf;
|
||||
children->Count = num_children;
|
||||
_SymGetTypeInfo(hProcess, mod_base, type_idx, TI_FINDCHILDREN, children);
|
||||
|
||||
out += sprintf(out, "\r\n");
|
||||
|
||||
/* recurse for each child */
|
||||
for(uint child_idx = 0; child_idx < num_children; child_idx++)
|
||||
{
|
||||
u32 child_id = children->ChildId[child_idx];
|
||||
|
||||
/* indent */
|
||||
for(uint i = 0; i < level+3; i++)
|
||||
out += sprintf(out, " ");
|
||||
|
||||
u32 member_ofs;
|
||||
_SymGetTypeInfo(hProcess, mod_base, child_id, TI_GET_OFFSET, &member_ofs);
|
||||
|
||||
dump_sym(child_id, level+1, addr+member_ofs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static enum
|
||||
{
|
||||
NONE,
|
||||
PARAMS,
|
||||
LOCALS
|
||||
}
|
||||
var_type;
|
||||
|
||||
|
||||
#define SYM_FLAG(flag) (sym->Flags & (IMAGEHLP_SYMBOL_INFO_##flag))
|
||||
|
||||
|
||||
/* called for each local symbol */
|
||||
static BOOL CALLBACK sym_callback(SYMBOL_INFO* sym, ULONG /* SymbolSize */, void* ctx)
|
||||
{
|
||||
STACKFRAME64* frame = (STACKFRAME64*)ctx;
|
||||
|
||||
mod_base = (uintptr_t)sym->ModBase;
|
||||
|
||||
__try
|
||||
{
|
||||
if(SYM_FLAG(PARAMETER))
|
||||
{
|
||||
if(var_type != PARAMS)
|
||||
{
|
||||
var_type = PARAMS;
|
||||
out += sprintf(out, " params:\r\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
if(var_type != LOCALS)
|
||||
{
|
||||
var_type = LOCALS;
|
||||
out += sprintf(out, " locals:\r\n");
|
||||
}
|
||||
|
||||
out += sprintf(out, " %s ", sym->Name);
|
||||
|
||||
u64 addr = sym->Address; /* -> var's contents */
|
||||
if(SYM_FLAG(REGRELATIVE))
|
||||
addr += frame->AddrFrame.Offset;
|
||||
else if(SYM_FLAG(REGISTER))
|
||||
return 0;
|
||||
|
||||
dump_sym(sym->TypeIndex, 0, addr);
|
||||
}
|
||||
__except(1)
|
||||
{
|
||||
out += sprintf(out, "\r\nError reading symbol %s!\r\n", sym->Name);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* most recent <skip> stack frames will be skipped
|
||||
* (we don't want to show e.g. GetThreadContext / this call)
|
||||
*/
|
||||
static void walk_stack(int skip, char* out_buf)
|
||||
{
|
||||
dbg_init();
|
||||
|
||||
bool type_avail = _SymFromAddr != 0;
|
||||
|
||||
out = out_buf;
|
||||
out += sprintf(out, "\r\nCall stack:\r\n");
|
||||
|
||||
if(!type_avail)
|
||||
out += sprintf(out, "warning: older dbghelp.dll loaded; no type information available.\r\n");
|
||||
|
||||
HANDLE hThread = GetCurrentThread();
|
||||
|
||||
static CONTEXT context;
|
||||
context.ContextFlags = CONTEXT_FULL;
|
||||
GetThreadContext(hThread, &context);
|
||||
|
||||
static STACKFRAME64 frame;
|
||||
|
||||
#ifdef _M_IX86
|
||||
/* x86 only: init STACKFRAME for first StackWalk call (undocumented) */
|
||||
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;
|
||||
#endif
|
||||
|
||||
u64 base = _SymGetModuleBase64(hProcess, (u64)&walk_stack);
|
||||
IMAGE_NT_HEADERS* header = _ImageNtHeader((void*)base);
|
||||
u32 machine = header->FileHeader.Machine;
|
||||
|
||||
static char sym_buf[sizeof(SYMBOL_INFO) + 1024];
|
||||
SYMBOL_INFO* sym = (SYMBOL_INFO*)sym_buf;
|
||||
sym->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
sym->MaxNameLen = 1024;
|
||||
|
||||
static IMAGEHLP_LINE64 line = { sizeof(IMAGEHLP_LINE64) };
|
||||
static IMAGEHLP_STACK_FRAME imghlp_frame;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if(!_StackWalk64(machine, hProcess, hThread, &frame, &context, 0, _SymFunctionTableAccess64, _SymGetModuleBase64, 0))
|
||||
break;
|
||||
|
||||
if(skip-- > 0)
|
||||
continue;
|
||||
|
||||
u64 pc = frame.AddrPC.Offset;
|
||||
|
||||
if(!type_avail)
|
||||
{
|
||||
out += sprintf(out, "%I64X\r\n", pc);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* function name / address */
|
||||
u64 sym_disp;
|
||||
if(type_avail && _SymFromAddr(hProcess, pc, &sym_disp, sym))
|
||||
out += sprintf(out, "%s", sym->Name);
|
||||
else
|
||||
out += sprintf(out, "%I64X", pc);
|
||||
|
||||
/* source file + line number */
|
||||
DWORD line_disp;
|
||||
if(_SymGetLineFromAddr64(hProcess, pc, &line_disp, &line))
|
||||
out += sprintf(out, " (%s, line %lu)", line.FileName, line.LineNumber);
|
||||
|
||||
out += sprintf(out, "\r\n");
|
||||
|
||||
/* params + vars */
|
||||
var_type = NONE;
|
||||
|
||||
imghlp_frame.InstructionOffset = pc;
|
||||
_SymSetContext(hProcess, &imghlp_frame, 0);
|
||||
_SymEnumSymbols(hProcess, 0, 0, sym_callback, &frame);
|
||||
|
||||
out += sprintf(out, "\r\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ASSERT,
|
||||
EXCEPTION
|
||||
}
|
||||
DLG_TYPE;
|
||||
|
||||
|
||||
static int CALLBACK dlgproc(HWND hDlg, unsigned int msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch(msg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
DLG_TYPE type = (DLG_TYPE)lParam;
|
||||
|
||||
/* disable inappropriate buttons */
|
||||
if(type != ASSERT)
|
||||
{
|
||||
HWND h;
|
||||
h = GetDlgItem(hDlg, IDC_CONTINUE);
|
||||
EnableWindow(h, 0);
|
||||
h = GetDlgItem(hDlg, IDC_SUPPRESS);
|
||||
EnableWindow(h, 0);
|
||||
h = GetDlgItem(hDlg, IDC_BREAK);
|
||||
EnableWindow(h, 0);
|
||||
}
|
||||
|
||||
SetDlgItemText(hDlg, IDC_EDIT1, buf);
|
||||
return 1; /* allow focus to be set */
|
||||
}
|
||||
|
||||
case WM_SYSCOMMAND:
|
||||
if(wParam == SC_CLOSE)
|
||||
EndDialog(hDlg, 0);
|
||||
return 0; /* allows dragging; don't understand why. */
|
||||
|
||||
case WM_COMMAND:
|
||||
switch(wParam)
|
||||
{
|
||||
case IDC_COPY:
|
||||
if(OpenClipboard(NULL))
|
||||
{
|
||||
EmptyClipboard();
|
||||
HANDLE text = GlobalAlloc(GMEM_FIXED, strlen(buf)+1);
|
||||
strcpy((char*)text, buf);
|
||||
SetClipboardData(CF_TEXT, text);
|
||||
CloseClipboard();
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_CONTINUE: /* 2000 */
|
||||
case IDC_SUPPRESS:
|
||||
case IDC_BREAK: /* 2002 */
|
||||
EndDialog(hDlg, wParam-2000);
|
||||
break;
|
||||
|
||||
case IDC_EXIT:
|
||||
exit(0);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0; /* do default processing for msg */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* show error dialog, with stack trace (must be stored in buf[])
|
||||
* exits directly if 'exit' is clicked.
|
||||
*
|
||||
* return values:
|
||||
* 0 - continue
|
||||
* 1 - suppress
|
||||
* 2 - break
|
||||
*/
|
||||
static int dialog(DLG_TYPE type)
|
||||
{
|
||||
return (int)DialogBoxParam(0, MAKEINTRESOURCE(IDD_DIALOG1), 0, dlgproc, (LPARAM)type);
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static PTOP_LEVEL_EXCEPTION_FILTER prev_except_filter;
|
||||
static long CALLBACK except_filter(EXCEPTION_POINTERS* except);
|
||||
|
||||
|
||||
void dbg_init_except_handler()
|
||||
{
|
||||
prev_except_filter = SetUnhandledExceptionFilter(except_filter);
|
||||
}
|
||||
|
||||
|
||||
static long CALLBACK except_filter(EXCEPTION_POINTERS* except)
|
||||
{
|
||||
PEXCEPTION_RECORD except_record = except->ExceptionRecord;
|
||||
uintptr_t addr = (uintptr_t)except_record->ExceptionAddress;
|
||||
|
||||
DWORD code = except_record->ExceptionCode;
|
||||
char* except_str = 0;
|
||||
#define X(e) case EXCEPTION_##e: except_str = #e;
|
||||
switch(code)
|
||||
{
|
||||
X(ACCESS_VIOLATION)
|
||||
X(DATATYPE_MISALIGNMENT)
|
||||
X(BREAKPOINT)
|
||||
X(SINGLE_STEP)
|
||||
X(ARRAY_BOUNDS_EXCEEDED)
|
||||
X(FLT_DENORMAL_OPERAND)
|
||||
X(FLT_DIVIDE_BY_ZERO)
|
||||
X(FLT_INEXACT_RESULT)
|
||||
X(FLT_INVALID_OPERATION)
|
||||
X(FLT_OVERFLOW)
|
||||
X(FLT_STACK_CHECK)
|
||||
X(FLT_UNDERFLOW)
|
||||
X(INT_DIVIDE_BY_ZERO)
|
||||
X(INT_OVERFLOW)
|
||||
X(PRIV_INSTRUCTION)
|
||||
X(IN_PAGE_ERROR)
|
||||
X(ILLEGAL_INSTRUCTION)
|
||||
X(NONCONTINUABLE_EXCEPTION)
|
||||
X(STACK_OVERFLOW)
|
||||
X(INVALID_DISPOSITION)
|
||||
X(GUARD_PAGE)
|
||||
X(INVALID_HANDLE)
|
||||
}
|
||||
#undef X
|
||||
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
char module_buf[100];
|
||||
char* module = "???";
|
||||
uintptr_t base = 0;
|
||||
|
||||
if(VirtualQuery((void*)addr, &mbi, sizeof(mbi)))
|
||||
{
|
||||
base = (uintptr_t)mbi.AllocationBase;
|
||||
if(GetModuleFileName((HMODULE)base, module_buf, sizeof(module_buf)))
|
||||
module = strrchr(module_buf, '\\');
|
||||
}
|
||||
|
||||
|
||||
int pos = sprintf(buf, "Exception %s at %s!%08lX\r\n", except_str, module, addr-base);
|
||||
walk_stack(0, buf+pos);
|
||||
|
||||
dialog(EXCEPTION);
|
||||
|
||||
if(prev_except_filter)
|
||||
return prev_except_filter(except);
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
|
||||
int show_assert_dlg(char* file, int line, char* expr)
|
||||
{
|
||||
int pos = sprintf(buf, "Assertion failed in %s, line %d: %s\r\n", file, line, expr);
|
||||
walk_stack(2, buf+pos);
|
||||
|
||||
return dialog(ASSERT);
|
||||
}
|
43
source/lib/sysdep/win/wdbg.h
Executable file
43
source/lib/sysdep/win/wdbg.h
Executable file
@ -0,0 +1,43 @@
|
||||
#ifndef __DEBUG_H__
|
||||
#define __DEBUG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* exception handler and assert with stack trace
|
||||
* (including variable / parameter types and values)
|
||||
* shown in a dialog, which offers
|
||||
* continue, break, suppress (ignore this assert), and exit
|
||||
*/
|
||||
|
||||
/* register exception handler */
|
||||
extern void dbg_init_except_handler();
|
||||
|
||||
/* recommended use: assert2(expr && "descriptive string") */
|
||||
#define assert2(expr)\
|
||||
{\
|
||||
static int suppress;\
|
||||
if(!suppress && !expr)\
|
||||
switch(show_assert_dlg(__FILE__, __LINE__, #expr))\
|
||||
{\
|
||||
case 1:\
|
||||
suppress = 1;\
|
||||
break;\
|
||||
\
|
||||
case 2:\
|
||||
__asm { int 3 } /* x86 specific; Win alternative: DebugBreak */\
|
||||
break;\
|
||||
}\
|
||||
}
|
||||
|
||||
extern int show_assert_dlg(char* file, int line, char* expr);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // #ifndef __DEBUG_H__
|
Loading…
Reference in New Issue
Block a user