From 0d79703fb037578f722251fa709000935ba60139 Mon Sep 17 00:00:00 2001 From: janwas Date: Thu, 18 Sep 2008 18:06:48 +0000 Subject: [PATCH] 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. --- source/lib/debug.cpp | 16 +- source/lib/debug.h | 2 +- source/lib/sysdep/os/win/wdbg_heap.cpp | 4 +- source/lib/sysdep/os/win/wdbg_sym.cpp | 393 +++++++++++++------------ 4 files changed, 214 insertions(+), 201 deletions(-) diff --git a/source/lib/debug.cpp b/source/lib/debug.cpp index 5dac14337a..8f133e6239 100644 --- a/source/lib/debug.cpp +++ b/source/lib/debug.cpp @@ -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) { diff --git a/source/lib/debug.h b/source/lib/debug.h index cfb33a2328..bba0043dd2 100644 --- a/source/lib/debug.h +++ b/source/lib/debug.h @@ -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; diff --git a/source/lib/sysdep/os/win/wdbg_heap.cpp b/source/lib/sysdep/os/win/wdbg_heap.cpp index 86c25ed35f..4ae7904bd6 100644 --- a/source/lib/sysdep/os/win/wdbg_heap.cpp +++ b/source/lib/sysdep/os/win/wdbg_heap.cpp @@ -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 diff --git a/source/lib/sysdep/os/win/wdbg_sym.cpp b/source/lib/sysdep/os/win/wdbg_sym.cpp index 8c61bb6f24..f2a10bc5c9 100644 --- a/source/lib/sysdep/os/win/wdbg_sym.cpp +++ b/source/lib/sysdep/os/win/wdbg_sym.cpp @@ -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