1
0
forked from 0ad/0ad

wdbg_heap: disable leak reporting to make debug mode bearable

wdbg_sym: numerous fixes and improvements
- all values are (also) displayed in hex form
- more correct calculation of symbol addresses (workaround for apparent
PDB bug)

This was SVN commit r6399.
This commit is contained in:
janwas 2008-09-18 18:06:48 +00:00
parent 982156e3f5
commit 0d79703fb0
4 changed files with 214 additions and 201 deletions

View File

@ -29,7 +29,7 @@
ERROR_ASSOCIATE(ERR::SYM_NO_STACK_FRAMES_FOUND, "No stack frames found", -1);
ERROR_ASSOCIATE(ERR::SYM_UNRETRIEVABLE_STATIC, "Value unretrievable (stored in external module)", -1);
ERROR_ASSOCIATE(ERR::SYM_UNRETRIEVABLE_REG, "Value unretrievable (stored in register)", -1);
ERROR_ASSOCIATE(ERR::SYM_UNRETRIEVABLE, "Value unretrievable", -1);
ERROR_ASSOCIATE(ERR::SYM_TYPE_INFO_UNAVAILABLE, "Error getting type_info", -1);
ERROR_ASSOCIATE(ERR::SYM_INTERNAL_ERROR, "Exception raised while processing a symbol", -1);
ERROR_ASSOCIATE(ERR::SYM_UNSUPPORTED, "Symbol type not (fully) supported", -1);
@ -177,15 +177,15 @@ void debug_printf(const wchar_t* fmt, ...)
va_list ap;
va_start(ap, fmt);
const int len = vswprintf(buf, DEBUG_PRINTF_MAX_CHARS, fmt, ap);
debug_assert(len >= 0);
const int numChars = vswprintf(buf, DEBUG_PRINTF_MAX_CHARS, fmt, ap);
debug_assert(numChars >= 0);
va_end(ap);
char buf2[DEBUG_PRINTF_MAX_CHARS];
size_t charsConverted;
errno_t ret = wcstombs_s(&charsConverted, buf2, DEBUG_PRINTF_MAX_CHARS, buf, DEBUG_PRINTF_MAX_CHARS);
size_t bytesWritten;
errno_t ret = wcstombs_s(&bytesWritten, buf2, DEBUG_PRINTF_MAX_CHARS, buf, DEBUG_PRINTF_MAX_CHARS);
debug_assert(ret == 0);
debug_assert(charsConverted == wcslen(buf));
debug_assert(bytesWritten-1 == (size_t)numChars);
if(debug_filter_allows(buf2))
debug_puts(buf2);
@ -261,7 +261,9 @@ static bool should_suppress_error(u8* suppress)
}
static const size_t message_size_bytes = 256*KiB; // enough
// (NB: this may appear obscene, but deep stack traces have been
// observed to take up > 256 KiB)
static const size_t message_size_bytes = 512*KiB;
void debug_error_message_free(ErrorMessageMem* emm)
{

View File

@ -374,7 +374,7 @@ namespace ERR
{
const LibError SYM_NO_STACK_FRAMES_FOUND = -100400;
const LibError SYM_UNRETRIEVABLE_STATIC = -100401;
const LibError SYM_UNRETRIEVABLE_REG = -100402;
const LibError SYM_UNRETRIEVABLE = -100402;
const LibError SYM_TYPE_INFO_UNAVAILABLE = -100403;
const LibError SYM_INTERNAL_ERROR = -100404;
const LibError SYM_UNSUPPORTED = -100405;

View File

@ -62,8 +62,8 @@ void wdbg_heap_Validate()
// (this relies on the debug CRT; not compiling it at all in release builds
// avoids unreferenced local function warnings)
// (this has only been tested on IA32 and seems to have trouble with larger
// pointers, so it's disabled for now.)
#if !defined(NDEBUG) && ARCH_IA32
// pointers and is horribly expensive, so it's disabled for now.)
#if !defined(NDEBUG) && ARCH_IA32 && 0
# define ENABLE_LEAK_INSTRUMENTATION 1
#else
# define ENABLE_LEAK_INSTRUMENTATION 0

View File

@ -28,6 +28,7 @@
#endif
#include "lib/external_libraries/dbghelp.h"
#include "winit.h"
#include "wdbg.h"
#include "wutil.h"
@ -97,6 +98,8 @@ struct SYMBOL_INFO_PACKAGEW2 : public SYMBOL_INFO_PACKAGEW
}
};
#pragma pack(push, 1)
// note: we can't derive from TI_FINDCHILDREN_PARAMS because its members
// aren't guaranteed to precede ours (although they do in practice).
struct TI_FINDCHILDREN_PARAMS2
@ -107,11 +110,13 @@ struct TI_FINDCHILDREN_PARAMS2
p.Count = std::min(num_children, MAX_CHILDREN);
}
static const DWORD MAX_CHILDREN = 400;
static const DWORD MAX_CHILDREN = 300;
TI_FINDCHILDREN_PARAMS p;
DWORD additional_children[MAX_CHILDREN-1];
};
#pragma pack(pop)
// actual implementation; made available so that functions already under
// the lock don't have to unlock (slow) to avoid recursive locking.
@ -228,6 +233,12 @@ func2:
*/
#if ARCH_IA32 && !CONFIG_OMIT_FP
# define IA32_STACK_WALK_ENABLED 1
#else
# define IA32_STACK_WALK_ENABLED 0
#endif
#if IA32_STACK_WALK_ENABLED
static LibError ia32_walk_stack(_tagSTACKFRAME64* sf)
{
@ -268,7 +279,7 @@ static LibError ia32_walk_stack(_tagSTACKFRAME64* sf)
return INFO::OK;
}
#endif // #if ARCH_IA32 && !CONFIG_OMIT_FP
#endif
static void skip_this_frame(size_t& skip, void* context)
@ -278,7 +289,7 @@ static void skip_this_frame(size_t& skip, void* context)
}
typedef VOID (*PRtlCaptureContext)(PCONTEXT);
typedef VOID (WINAPI *PRtlCaptureContext)(PCONTEXT);
static PRtlCaptureContext s_RtlCaptureContext;
LibError wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, size_t skip, const CONTEXT* pcontext)
@ -301,25 +312,22 @@ LibError wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, size_t skip
// - asm (easy to use but currently only implemented on IA32)
// - RtlCaptureContext (only available on WinXP or above)
// - intentionally raise an SEH exception and capture its context
// (spams us with "first chance exception")
// - GetThreadContext while suspended* (a bit tricky + slow).
//
// * it used to be common practice to query the current thread's context,
// but WinXP SP2 and above require it be suspended.
// (causes annoying "first chance exception" messages and
// can't co-exist with WinScopedLock's destructor)
// - GetThreadContext while suspended (a bit tricky + slow).
// note: it used to be common practice to query the current thread
// context, but WinXP SP2 and above require it be suspended.
//
// this MUST be done inline and not in an external function because
// compiler-generated prolog code trashes some registers.
#if ARCH_IA32
#if ARCH_IA32 && !ARCH_AMD64
ia32_asm_GetCurrentContext(&context);
#else
// we no longer bother supporting stack walks on pre-WinXP systems
// that lack RtlCaptureContext. at least importing dynamically does
// allow the application to run on such systems.
// (note: the RaiseException method is annoying due to output in
// the debugger window and interaction with WinScopedLock's dtor)
if(!s_RtlCaptureContext)
return ERR::SYM_UNSUPPORTED; // NOWARN
return ERR::NOT_SUPPORTED; // NOWARN
memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER;
s_RtlCaptureContext(&context);
#endif
}
@ -340,6 +348,10 @@ LibError wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, size_t skip
sf.AddrStack.Offset = pcontext->Esp;
#endif
#if !IA32_STACK_WALK_ENABLED
sym_init();
#endif
// for each stack frame found:
LibError ret = ERR::SYM_NO_STACK_FRAMES_FOUND;
for(;;)
@ -349,7 +361,7 @@ LibError wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, size_t skip
// stack walks (e.g. to determine callers of malloc) do not
// require firing up dbghelp. that takes tens of seconds when
// OS symbols are installed (because symserv is wanting to access
// inet), which is entirely unacceptable.
// the internet), which is entirely unacceptable.
// - VC7.1 sometimes generates stack frames despite /Oy ;
// ia32_walk_stack may appear to work, but it isn't reliable in
// this case and therefore must not be used!
@ -358,19 +370,21 @@ LibError wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, size_t skip
// code is authoritative provided its prerequisite (FP not omitted)
// is met, otherwise totally unusable.
LibError err;
#if ARCH_IA32 && !CONFIG_OMIT_FP
#if IA32_STACK_WALK_ENABLED
err = ia32_walk_stack(&sf);
#else
WinScopedLock lock(WDBG_SYM_CS);
sym_init();
// note: unfortunately StackWalk64 doesn't always SetLastError,
// so we have to reset it and check for 0. *sigh*
SetLastError(0);
const HANDLE hThread = GetCurrentThread();
BOOL ok = StackWalk64(machine, hProcess, hThread, &sf, (PVOID)pcontext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0);
// note: don't use LibError_from_win32 because it raises a warning,
// and this "fails" commonly (when no stack frames are left).
err = ok? INFO::OK : ERR::FAIL;
{
WinScopedLock lock(WDBG_SYM_CS);
// note: unfortunately StackWalk64 doesn't always SetLastError,
// so we have to reset it and check for 0. *sigh*
SetLastError(0);
const HANDLE hThread = GetCurrentThread();
const BOOL ok = StackWalk64(machine, hProcess, hThread, &sf, (PVOID)pcontext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0);
// note: don't use LibError_from_win32 because it raises a warning,
// and this "fails" commonly (when no stack frames are left).
err = ok? INFO::OK : ERR::FAIL;
}
#endif
// no more frames found - abort. note: also test FP because
@ -428,16 +442,14 @@ void* debug_get_nth_caller(size_t skip, void* pcontext)
// helper routines for symbol value dump
//-----------------------------------------------------------------------------
// overflow is impossible in practice, but check for robustness.
// keep in sync with DumpState.
// infinite recursion has never happened, but we check for it anyway.
static const size_t MAX_INDIRECTION = 255;
static const size_t MAX_LEVEL = 255;
struct DumpState
{
// keep in sync with MAX_* above
size_t level : 8;
size_t indirection : 8;
size_t level;
size_t indirection;
DumpState()
{
@ -449,10 +461,11 @@ struct DumpState
//----------------------------------------------------------------------------
static size_t out_chars_left;
static bool out_have_warned_of_overflow;
// only do so once until next out_init to avoid flood of messages.
static wchar_t* out_pos;
// (only warn once until next out_init to avoid flood of messages.)
static bool out_have_warned_of_overflow;
// some top-level (*) symbols cause tons of output - so much that they may
// single-handedly overflow the buffer (e.g. pointer to a tree of huge UDTs).
// we can't have that, so there is a limit in place as to how much a
@ -623,7 +636,9 @@ enum CV_HREG_e
CV_REG_EDI = 24
};
#if 0
// (no longer needed - reg was always 0 (unknown), so there's no point in
// cluttering the stack trace with register strings)
static const wchar_t* string_for_register(CV_HREG_e reg)
{
switch(reg)
@ -652,9 +667,9 @@ static const wchar_t* string_for_register(CV_HREG_e reg)
}
}
}
#endif
static void dump_error(LibError err, const u8* p)
static void dump_error(LibError err)
{
switch(err)
{
@ -667,8 +682,8 @@ static void dump_error(LibError err, const u8* p)
case ERR::SYM_UNRETRIEVABLE_STATIC:
out(L"(unavailable - located in another module)");
break;
case ERR::SYM_UNRETRIEVABLE_REG:
out(L"(unavailable - stored in register %s)", string_for_register((CV_HREG_e)(uintptr_t)p));
case ERR::SYM_UNRETRIEVABLE:
out(L"(unavailable)");
break;
case ERR::SYM_TYPE_INFO_UNAVAILABLE:
out(L"(unavailable - type info request failed (GLE=%d))", GetLastError());
@ -718,8 +733,7 @@ static LibError dump_string(const u8* p, size_t el_size)
// split out of dump_sequence.
static void seq_determine_formatting(size_t el_size, size_t el_count,
bool* fits_on_one_line, size_t* num_elements_to_show)
static void seq_determine_formatting(size_t el_size, size_t el_count, bool* fits_on_one_line, size_t* num_elements_to_show)
{
if(el_size == sizeof(char))
{
@ -744,8 +758,7 @@ static void seq_determine_formatting(size_t el_size, size_t el_count,
}
static LibError dump_sequence(DebugStlIterator el_iterator, void* internal,
size_t el_count, DWORD el_type_id, size_t el_size, DumpState state)
static LibError dump_sequence(DebugStlIterator el_iterator, void* internal, size_t el_count, DWORD el_type_id, size_t el_size, DumpState state)
{
const u8* el_p = 0; // avoid "uninitialized" warning
@ -787,7 +800,7 @@ static LibError dump_sequence(DebugStlIterator el_iterator, void* internal,
continue;
}
dump_error(err, el_p); // nop if err == INFO::OK
dump_error(err); // nop if err == INFO::OK
// add separator unless this is the last element (can't just
// erase below due to additional "...").
if(i != num_elements_to_show-1)
@ -817,8 +830,7 @@ static const u8* array_iterator(void* internal, size_t el_size)
}
static LibError dump_array(const u8* p,
size_t el_count, DWORD el_type_id, size_t el_size, DumpState state)
static LibError dump_array(const u8* p, size_t el_count, DWORD el_type_id, size_t el_size, DumpState state)
{
const u8* iterator_internal_pos = p;
return dump_sequence(array_iterator, &iterator_internal_pos,
@ -828,91 +840,105 @@ static LibError dump_array(const u8* p,
static const _tagSTACKFRAME64* current_stackframe64;
static LibError determine_symbol_address(DWORD id, DWORD UNUSED(type_id), const u8** pp)
static LibError CanHandleDataKind(DWORD dataKind)
{
const _tagSTACKFRAME64* sf = current_stackframe64;
DWORD data_kind;
if(!SymGetTypeInfo(hProcess, mod_base, id, TI_GET_DATAKIND, &data_kind))
WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);
switch(data_kind)
switch(dataKind)
{
// SymFromIndex will fail
case DataIsMember:
// pp is already correct (udt_dump_normal retrieved the offset;
// address is already correct (udt_dump_normal retrieved the offset;
// we do it that way so we can check it against the total
// UDT size for safety).
return INFO::OK;
// UDT size for safety) and SymFromIndex would fail
return INFO::SKIPPED;
case DataIsUnknown:
WARN_RETURN(ERR::FAIL);
// this symbol is defined as static in another module =>
// there's nothing we can do.
case DataIsStaticMember:
// this symbol is defined as static in another module =>
// there's nothing we can do.
return ERR::SYM_UNRETRIEVABLE_STATIC; // NOWARN
// ok; will handle below
case DataIsLocal:
case DataIsStaticLocal:
case DataIsParam:
case DataIsObjectPtr:
case DataIsFileStatic:
case DataIsGlobal:
break;
NODEFAULT;
case DataIsConstant:
// ok, can handle
return INFO::OK;
}
// get SYMBOL_INFO (we need .Flags)
SYMBOL_INFO_PACKAGEW2 sp;
SYMBOL_INFOW* sym = &sp.si;
if(!SymFromIndexW(hProcess, mod_base, id, sym))
WARN_RETURN(ERR::LOGIC); // UNREACHABLE
}
static bool IsRelativeToFramePointer(DWORD flags, DWORD reg)
{
if(flags & SYMFLAG_FRAMEREL) // note: this is apparently obsolete
return true;
if(flags & SYMFLAG_REGREL && reg == CV_REG_EBP)
return true;
return false;
}
static bool IsUnretrievable(DWORD flags)
{
// note: it is unlikely that the crashdump register context
// contains the correct values for this scope, so symbols
// stored in or relative to a general register are unavailable.
if(flags & SYMFLAG_REGISTER)
return true;
// note: IsRelativeToFramePointer is called first, so if we still
// see this flag, the base register is not the frame pointer.
// since we most probably don't know its value in the current
// scope (see above), the symbol is inaccessible.
if(flags & SYMFLAG_REGREL)
return true;
return false;
}
static LibError DetermineSymbolAddress(DWORD id, const SYMBOL_INFOW* sym, const u8** pp)
{
const _tagSTACKFRAME64* sf = current_stackframe64;
DWORD dataKind;
if(!SymGetTypeInfo(hProcess, mod_base, id, TI_GET_DATAKIND, &dataKind))
WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);
LibError ret = CanHandleDataKind(dataKind);
RETURN_ERR(ret);
if(ret == INFO::SKIPPED)
return INFO::OK; // pp is already correct
DWORD addrofs = 0;
ULONG64 addr2 = 0;
DWORD ofs2 = 0;
SymGetTypeInfo(hProcess, mod_base, id, TI_GET_ADDRESSOFFSET, &addrofs);
SymGetTypeInfo(hProcess, mod_base, id, TI_GET_ADDRESS, &addr2);
SymGetTypeInfo(hProcess, mod_base, id, TI_GET_OFFSET, &ofs2);
// note: we have not yet observed a non-zero TI_GET_ADDRESSOFFSET or
// TI_GET_ADDRESS, and TI_GET_OFFSET is apparently equal to sym->Address.
// get address
uintptr_t addr = sym->Address;
// .. relative to a register
// note: we only have the FP (not SP)
if(sym->Flags & SYMFLAG_REGREL)
if(IsRelativeToFramePointer(sym->Flags, sym->Register))
{
if(sym->Register == CV_REG_EBP)
goto fp_rel;
else
goto in_register;
}
// .. relative to FP (appears to be obsolete)
else if(sym->Flags & SYMFLAG_FRAMEREL)
{
fp_rel:
addr += sf->AddrFrame.Offset;
// HACK: reg-relative symbols (params and locals, but not
// static) appear to be off by 4 bytes in release builds.
// no idea as to the cause, but this "fixes" it.
#ifdef NDEBUG
addr += sizeof(void*);
// NB: the addresses of register-relative symbols are apparently
// incorrect [VC8, 32-bit Wow64]. the problem occurs regardless of
// IA32_STACK_WALK_ENABLED and with both ia32_asm_GetCurrentContext
// and RtlCaptureContext. the EBP, ESP and EIP values returned by
// ia32_asm_GetCurrentContext match those reported by the IDE, so
// the problem appears to lie in the offset values stored in the PDB.
if(sym->Flags & SYMFLAG_PARAMETER)
addr += sizeof(void*);
else
addr += sizeof(void*) * 2;
#endif
}
// .. in register (this happens when optimization is enabled,
// but we can't do anything; see SymbolInfoRegister)
else if(sym->Flags & SYMFLAG_REGISTER)
{
in_register:
*pp = (const u8*)(uintptr_t)sym->Register;
return ERR::SYM_UNRETRIEVABLE_REG; // NOWARN
}
else if(IsUnretrievable(sym->Flags))
return ERR::SYM_UNRETRIEVABLE; // NOWARN
*pp = (const u8*)(uintptr_t)addr;
debug_printf("SYM| %ws at %p flags=%X dk=%d sym->addr=%I64X addrofs=%X addr2=%I64X ofs2=%X\n", sym->Name, *pp, sym->Flags, data_kind, sym->Address, addrofs, addr2, ofs2);
debug_printf("SYM| %ws at %p flags=%X dk=%d sym->addr=%I64X fp=%I64x\n", sym->Name, *pp, sym->Flags, dataKind, sym->Address, sf->AddrFrame.Offset);
return INFO::OK;
}
@ -953,6 +979,20 @@ static LibError dump_sym_array(DWORD type_id, const u8* p, DumpState state)
//-----------------------------------------------------------------------------
// if the current value is a printable character, display in that form.
// this isn't only done in btChar because characters are sometimes stored
// in integers.
static void AppendCharacterIfPrintable(u64 data)
{
if(data < 0x100)
{
int c = (int)data;
if(isprint(c))
out(L" ('%hc')", c);
}
}
static LibError dump_sym_base_type(DWORD type_id, const u8* p, DumpState state)
{
DWORD base_type;
@ -982,12 +1022,6 @@ static LibError dump_sym_base_type(DWORD type_id, const u8* p, DumpState state)
switch(base_type)
{
// boolean
case btBool:
debug_assert(size == sizeof(bool));
out(L"%hs", data? "true " : "false");
return INFO::OK;
// floating-point
case btFloat:
if(size == sizeof(float))
@ -997,28 +1031,29 @@ static LibError dump_sym_base_type(DWORD type_id, const u8* p, DumpState state)
// merely a zero-extended 32-bit representation of the float.
float value;
memcpy(&value, p, sizeof(value));
out(L"%f (0x%08X)", value, value);
out(L"%f (0x%08I64X)", value, data);
}
else if(size == sizeof(double))
out(L"%g (0x%016X)", data, data);
out(L"%g (0x%016I64X)", data, data);
else
debug_assert(0); // invalid float size
return INFO::OK;
// signed integers (displayed as decimal)
case btInt:
case btLong:
if(size != 1 && size != 2 && size != 4 && size != 8)
debug_assert(0); // invalid int size
// need to re-load and sign-extend, because we output 64 bits.
data = movsx_le64(p, size);
fmt = L"%I64d";
break;
// unsigned integers (displayed as hex)
// boolean
case btBool:
debug_assert(size == sizeof(bool));
if(data == 0 || data == 1)
out(L"%hs", data? "true " : "false");
else
out(L"(bool)0x%02I64X", data);
break;
// integers (displayed as decimal and hex)
// note: 0x00000000 can get annoying (0 would be nicer),
// but it indicates the variable size and makes for consistently
// formatted structs/arrays. (0x1234 0 0x5678 is ugly)
case btInt:
case btLong:
case btUInt:
case btULong:
display_as_hex:
@ -1030,16 +1065,17 @@ display_as_hex:
state.indirection = 0;
return dump_array(p, 8, type_id, size, state);
}
fmt = L"0x%02X";
fmt = L"%I64d (0x%02I64X)";
}
else if(size == 2)
fmt = L"0x%04X";
fmt = L"%I64d (0x%04I64X)";
else if(size == 4)
fmt = L"0x%08X";
fmt = L"%I64d (0x%08I64X)";
else if(size == 8)
fmt = L"0x%016I64X";
fmt = L"%I64d (0x%016I64X)";
else
debug_assert(0); // invalid size_t size
debug_assert(0); // invalid size for integers
out(fmt, data, data);
break;
// character
@ -1052,9 +1088,8 @@ display_as_hex:
state.indirection = 0;
return dump_array(p, 8, type_id, size, state);
}
// either integer or character;
// if printable, the character will be appended below.
fmt = L"%d";
out(L"%d", data);
AppendCharacterIfPrintable(data);
break;
// note: void* is sometimes indicated as (pointer, btNoType).
@ -1071,8 +1106,8 @@ display_as_hex:
break;
default:
debug_warn("dump_sym_base_type: unknown type");
//-fallthrough
debug_assert(0); // unknown type
break;
// unsupported complex types
case btBCD:
@ -1086,17 +1121,6 @@ display_as_hex:
return ERR::SYM_UNSUPPORTED; // NOWARN
}
out(fmt, data);
// if the current value is a printable character, display in that form.
// this isn't only done in btChar because sometimes ints store characters.
if(data < 0x100)
{
int c = (int)data;
if(isprint(c))
out(L" ('%hc')", c);
}
return INFO::OK;
}
@ -1124,23 +1148,18 @@ static LibError dump_sym_base_class(DWORD type_id, const u8* p, DumpState state)
static LibError dump_sym_data(DWORD id, const u8* p, DumpState state)
{
// display name (of variable/member)
const wchar_t* name;
if(!SymGetTypeInfo(hProcess, mod_base, id, TI_GET_SYMNAME, &name))
SYMBOL_INFO_PACKAGEW2 sp;
SYMBOL_INFOW* sym = &sp.si;
if(!SymFromIndexW(hProcess, mod_base, id, sym))
WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);
out(L"%s = ", name);
LocalFree((HLOCAL)name);
out(L"%ws = ", sym->Name);
__try
{
// get type_id and address
DWORD type_id;
if(!SymGetTypeInfo(hProcess, mod_base, id, TI_GET_TYPEID, &type_id))
WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);
RETURN_ERR(determine_symbol_address(id, type_id, &p));
RETURN_ERR(DetermineSymbolAddress(id, sym, &p));
// display value recursively
return dump_sym(type_id, p, state);
return dump_sym(sym->TypeIndex, p, state);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
@ -1209,8 +1228,7 @@ static LibError dump_sym_enum(DWORD type_id, const u8* p, DumpState UNUSED(state
//-----------------------------------------------------------------------------
static LibError dump_sym_function(DWORD UNUSED(type_id), const u8* UNUSED(p),
DumpState UNUSED(state))
static LibError dump_sym_function(DWORD UNUSED(type_id), const u8* UNUSED(p), DumpState UNUSED(state))
{
return INFO::SYM_SUPPRESS_OUTPUT;
}
@ -1218,7 +1236,7 @@ static LibError dump_sym_function(DWORD UNUSED(type_id), const u8* UNUSED(p),
//-----------------------------------------------------------------------------
static LibError dump_sym_function_type(DWORD UNUSED(type_id), const u8* p, DumpState UNUSED(state))
static LibError dump_sym_function_type(DWORD UNUSED(type_id), const u8* p, DumpState state)
{
// this symbol gives class parent, return type, and parameter count.
// unfortunately the one thing we care about, its name,
@ -1227,9 +1245,10 @@ static LibError dump_sym_function_type(DWORD UNUSED(type_id), const u8* p, DumpS
char name[DBG_SYMBOL_LEN];
LibError err = debug_resolve_symbol_lk((void*)p, name, 0, 0);
out(L"0x%p", p);
if(state.indirection == 0)
out(L"0x%p ", p);
if(err == INFO::OK)
out(L" (%hs)", name);
out(L"(%hs)", name);
return INFO::OK;
}
@ -1342,47 +1361,41 @@ static LibError dump_sym_typedef(DWORD type_id, const u8* p, DumpState state)
// determine type and size of the given child in a UDT.
// useful for UDTs that contain typedefs describing their contents,
// e.g. value_type in STL containers.
static LibError udt_get_child_type(const wchar_t* child_name,
ULONG num_children, const DWORD* children,
DWORD* el_type_id, size_t* el_size)
static LibError udt_get_child_type(const wchar_t* child_name, ULONG num_children, const DWORD* children, DWORD* el_type_id, size_t* el_size)
{
const DWORD lastError = GetLastError();
*el_type_id = 0;
*el_size = 0;
for(ULONG i = 0; i < num_children; i++)
{
DWORD child_id = children[i];
const DWORD child_id = children[i];
// find the desired child
wchar_t* this_child_name;
if(!SymGetTypeInfo(hProcess, mod_base, child_id, TI_GET_SYMNAME, &this_child_name))
SYMBOL_INFO_PACKAGEW2 sp;
SYMBOL_INFOW* sym = &sp.si;
if(!SymFromIndexW(hProcess, mod_base, child_id, sym))
{
// this happens for several UDTs; cause is unknown.
debug_assert(GetLastError() == ERROR_NOT_FOUND);
continue;
const bool found_it = !wcscmp(this_child_name, child_name);
LocalFree(this_child_name);
if(!found_it)
continue;
// .. its type information is what we want.
DWORD type_id;
if(!SymGetTypeInfo(hProcess, mod_base, child_id, TI_GET_TYPEID, &type_id))
WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);
ULONG64 size;
if(!SymGetTypeInfo(hProcess, mod_base, child_id, TI_GET_LENGTH, &size))
WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE);
*el_type_id = type_id;
*el_size = (size_t)size;
return INFO::OK;
}
if(!wcscmp(sym->Name, child_name))
{
*el_type_id = sym->TypeIndex;
*el_size = (size_t)sym->Size;
return INFO::OK;
}
}
SetLastError(lastError);
// (happens if called for containers that are treated as STL but are not)
return ERR::SYM_CHILD_NOT_FOUND; // NOWARN
}
static LibError udt_dump_std(const wchar_t* wtype_name, const u8* p, size_t size, DumpState state,
ULONG num_children, const DWORD* children)
static LibError udt_dump_std(const wchar_t* wtype_name, const u8* p, size_t size, DumpState state, ULONG num_children, const DWORD* children)
{
LibError err;
@ -1492,8 +1505,7 @@ not_handle:
}
static LibError udt_dump_suppressed(const wchar_t* type_name, const u8* UNUSED(p), size_t UNUSED(size),
DumpState state, ULONG UNUSED(num_children), const DWORD* UNUSED(children))
static LibError udt_dump_suppressed(const wchar_t* type_name, const u8* UNUSED(p), size_t UNUSED(size), DumpState state, ULONG UNUSED(num_children), const DWORD* UNUSED(children))
{
if(!udt_should_suppress(type_name))
return INFO::CANNOT_HANDLE;
@ -1545,8 +1557,7 @@ static bool udt_fits_on_one_line(const wchar_t* type_name, size_t child_count, s
}
static LibError udt_dump_normal(const wchar_t* type_name, const u8* p, size_t size,
DumpState state, ULONG num_children, const DWORD* children)
static LibError udt_dump_normal(const wchar_t* type_name, const u8* p, size_t size, DumpState state, ULONG num_children, const DWORD* children)
{
const bool fits_on_one_line = udt_fits_on_one_line(type_name, num_children, size);
@ -1585,7 +1596,7 @@ static LibError udt_dump_normal(const wchar_t* type_name, const u8* p, size_t si
}
displayed_anything = true;
dump_error(err, el_p); // nop if err == INFO::OK
dump_error(err); // nop if err == INFO::OK
out(fits_on_one_line? L", " : L"\r\n");
if(err == ERR::SYM_SINGLE_SYMBOL_LIMIT)
@ -1751,7 +1762,7 @@ struct IMAGEHLP_STACK_FRAME2 : public IMAGEHLP_STACK_FRAME
static bool ShouldSkipSymbol(const wchar_t* name)
{
if(!wcscmp(name, L"__suppress"))
if(!wcscmp(name, L"suppress__"))
return true;
if(!wcscmp(name, L"__profile"))
return true;
@ -1772,7 +1783,7 @@ static BOOL CALLBACK dump_sym_cb(SYMBOL_INFOW* sym, ULONG UNUSED(size), void* UN
INDENT;
LibError err = dump_sym(sym->Index, p, state);
dump_error(err, p);
dump_error(err);
if(err == INFO::SYM_SUPPRESS_OUTPUT)
UNINDENT;
else