mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-21 07:43:59 -07:00
(see http://www.wildfiregames.com/forum/index.php?showtopic=11450&hl= ) clean up debug module . no longer include platform-dependent header (-> less rebuilds) . DISPLAY_ERROR -> DEBUG_DISPLAY_ERROR . parts of config.h that don't affect all files moved to config.2 (-> fewer full rebuilds) . remove creaky symbol cache (no longer needed for mmgr) . remove TLS thread naming stuff (can use debugger's thread window instead; no need for platform independence there) wdbg: remove thread suspension and breakpoint APIs (not needed) acpi: fix: u64 -> uintptr_t wutil: fix WinScopedLock, use that instead of direct lock() functions misc: . get rid of SAFE_STRCPY, replace with strcpy_s . remove _getcwd (shouldn't be used) This was SVN commit r5563.
176 lines
4.2 KiB
C++
176 lines
4.2 KiB
C++
/**
|
|
* =========================================================================
|
|
* File : wdbg.cpp
|
|
* Project : 0 A.D.
|
|
* Description : Win32 debug support code.
|
|
* =========================================================================
|
|
*/
|
|
|
|
// license: GPL; see lib/license.txt
|
|
|
|
#include "precompiled.h"
|
|
#include "lib/debug.h"
|
|
|
|
#include "lib/bits.h"
|
|
#include "win.h"
|
|
#include "wutil.h"
|
|
#include "winit.h"
|
|
|
|
WINIT_REGISTER_CRITICAL_INIT(wdbg_Init);
|
|
|
|
|
|
static NT_TIB* get_tib()
|
|
{
|
|
#if ARCH_IA32
|
|
NT_TIB* tib;
|
|
// ICC 10 doesn't support the NT_TIB.Self syntax, so we have to use
|
|
// a constant (asm code isn't 64-bit safe anyway).
|
|
__asm
|
|
{
|
|
mov eax, fs:[NT_TIB.Self]
|
|
mov [tib], eax
|
|
}
|
|
return tib;
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// debug heap
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void debug_heap_init()
|
|
{
|
|
uint flags = 0;
|
|
flags |= _CRTDBG_ALLOC_MEM_DF; // enable checks at deallocation time
|
|
flags |= _CRTDBG_LEAK_CHECK_DF; // report leaks at exit
|
|
#if CONFIG_PARANOIA
|
|
flags |= _CRTDBG_CHECK_ALWAYS_DF; // check during every heap operation (slow!)
|
|
flags |= _CRTDBG_DELAY_FREE_MEM_DF; // blocks cannot be reused
|
|
#endif
|
|
_CrtSetDbgFlag(flags);
|
|
}
|
|
|
|
|
|
void debug_heap_check()
|
|
{
|
|
int ret;
|
|
__try
|
|
{
|
|
// NB: this is a no-op if !_CRTDBG_ALLOC_MEM_DF.
|
|
// we could call _heapchk but that would catch fewer errors.
|
|
ret = _CrtCheckMemory();
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ret = _HEAPBADNODE;
|
|
}
|
|
|
|
if(ret != _HEAPOK)
|
|
DEBUG_DISPLAY_ERROR(L"debug_heap_check: heap is corrupt");
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// return 1 if the pointer appears to be totally bogus, otherwise 0.
|
|
// this check is not authoritative (the pointer may be "valid" but incorrect)
|
|
// but can be used to filter out obviously wrong values in a portable manner.
|
|
int debug_is_pointer_bogus(const void* p)
|
|
{
|
|
#if ARCH_IA32
|
|
if(p < (void*)0x10000)
|
|
return true;
|
|
if(p >= (void*)(uintptr_t)0x80000000)
|
|
return true;
|
|
#endif
|
|
|
|
// notes:
|
|
// - we don't check alignment because nothing can be assumed about a
|
|
// string pointer and we mustn't reject any actually valid pointers.
|
|
// - nor do we bother checking the address against known stack/heap areas
|
|
// because that doesn't cover everything (e.g. DLLs, VirtualAlloc).
|
|
// - cannot use IsBadReadPtr because it accesses the mem
|
|
// (false alarm for reserved address space).
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool debug_is_code_ptr(void* p)
|
|
{
|
|
uintptr_t addr = (uintptr_t)p;
|
|
// totally invalid pointer
|
|
if(debug_is_pointer_bogus(p))
|
|
return false;
|
|
// comes before load address
|
|
static const HMODULE base = GetModuleHandle(0);
|
|
if(addr < (uintptr_t)base)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool debug_is_stack_ptr(void* p)
|
|
{
|
|
uintptr_t addr = (uintptr_t)p;
|
|
// totally invalid pointer
|
|
if(debug_is_pointer_bogus(p))
|
|
return false;
|
|
// not aligned
|
|
if(addr % sizeof(void*))
|
|
return false;
|
|
// out of bounds (note: IA-32 stack grows downwards)
|
|
NT_TIB* tib = get_tib();
|
|
if(!(tib->StackLimit < p && p < tib->StackBase))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void debug_puts(const char* text)
|
|
{
|
|
OutputDebugStringA(text);
|
|
}
|
|
|
|
|
|
// inform the debugger of the current thread's description, which it then
|
|
// displays instead of just the thread handle.
|
|
//
|
|
// see "Setting a Thread Name (Unmanaged)": http://msdn2.microsoft.com/en-us/library/xcb2z8hs(vs.71).aspx
|
|
void debug_set_thread_name(const char* name)
|
|
{
|
|
// we pass information to the debugger via a special exception it
|
|
// swallows. if not running under one, bail now to avoid
|
|
// "first chance exception" warnings.
|
|
if(!IsDebuggerPresent())
|
|
return;
|
|
|
|
// presented by Jay Bazuzi (from the VC debugger team) at TechEd 1999.
|
|
const struct ThreadNameInfo
|
|
{
|
|
DWORD type;
|
|
const char* name;
|
|
DWORD thread_id; // any valid ID or -1 for current thread
|
|
DWORD flags;
|
|
}
|
|
info = { 0x1000, name, (DWORD)-1, 0 };
|
|
__try
|
|
{
|
|
RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
// if we get here, the debugger didn't handle the exception.
|
|
debug_assert(0); // thread name hack doesn't work under this debugger
|
|
}
|
|
}
|
|
|
|
|
|
static LibError wdbg_Init()
|
|
{
|
|
debug_heap_init();
|
|
return INFO::OK;
|
|
}
|