diff --git a/docs/changes.txt b/docs/changes.txt index 2696c5b934..8707982594 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -134,6 +134,7 @@ wxMSW: - Improve wxMimeTypesManager open command detection (Eric Jensen). - Make wxFILTER_INCLUDE_LIST in wxTextValidator actually usable. - Fix handling crashes in wxEVT_PAINT event handlers. +- Fix wxStackWalker to work with Unicode identifiers (Suzumizaki-kimitaka). - Fix appearance of toggled wxToggleButtons with bitmap (tm). - Fix setting menu item bitmaps after appending them (Artur Wieczorek). - Fix setting label of submenu items (Artur Wieczorek). diff --git a/include/wx/msw/debughlp.h b/include/wx/msw/debughlp.h index 3d94ee2168..d63a213cb9 100644 --- a/include/wx/msw/debughlp.h +++ b/include/wx/msw/debughlp.h @@ -1,8 +1,7 @@ /////////////////////////////////////////////////////////////////////////////// // Name: wx/msw/debughlp.h // Purpose: wraps dbghelp.h standard file -// Author: Vadim Zeitlin -// Modified by: +// Author: Vadim Zeitlin, Suzumizaki-kimitaka // Created: 2005-01-08 (extracted from msw/crashrpt.cpp) // Copyright: (c) 2003-2005 Vadim Zeitlin // Licence: wxWindows licence @@ -19,12 +18,19 @@ #endif // __WXWINCE__ #include "wx/msw/private.h" -// wxUSE_DBGHELP can be predefined as 0 to avoid the use of dbghelp.dll if this -// is undesirable for some reason. +// wxUSE_DBGHELP can be predefined on the compiler command line to force using +// dbghelp.dll even if it's not detected or, on the contrary, avoid using even +// if it's available. #ifndef wxUSE_DBGHELP // The only compiler which is known to have the necessary headers is MSVC. #ifdef __VISUALC__ - #define wxUSE_DBGHELP 1 + // MSVC7.1 shipped with API v9 and we don't support anything earlier + // anyhow. + #if API_VERSION_NUMBER >= 9 + #define wxUSE_DBGHELP 1 + #else + #define wxUSE_DBGHELP 0 + #endif #else #define wxUSE_DBGHELP 0 #endif @@ -32,6 +38,117 @@ #if wxUSE_DBGHELP +/* + +The table below shows which functions are exported by dbghelp.dll. + +On 64 bit Windows, there seems to be no difference between 32bit dll and 64bit +one. Vista-64 and Win8-64 look the same, but "Ex" and "ExW" versions exist only +in Windows 8. + +Note that SymGetLineFromAddrW and EnumerateLoadedModulesW DON'T exist at all. + +function | Windows | XP-32 Vista-64 Win8-64 +SymEnumSymbolsW n/a v v +SymFromAddrW n/a v v +SymInitializeW n/a v v + +SymEnumSymbols v v v +SymFromAddr v v v +SymInitialize v v v + +SymGetLineFromAddrW64 n/a v v +SymGetLineFromAddr64 v v v +SymGetLineFromAddrW n/a n/a n/a +SymGetLineFromAddr v v v + +EnumerateLoadedModulesW64 n/a v v +EnumerateLoadedModules64 v v v +EnumerateLoadedModulesW n/a n/a n/a +EnumerateLoadedModules v v v + +*/ + +// It's not really clear whether API v10 is used by anything as VC8 still used +// v9, just as MSVC7.1, while VC9 already used v11, but provide support for it +// just in case. +#if API_VERSION_NUMBER < 10/*{{{*/ + +typedef BOOL +(CALLBACK *PENUMLOADED_MODULES_CALLBACKW64)(PWSTR ModuleName, + DWORD64 ModuleBase, + ULONG ModuleSize, + PVOID UserContext); + +typedef struct _IMAGEHLP_LINEW64 +{ + DWORD SizeOfStruct; + PVOID Key; + DWORD LineNumber; + PWSTR FileName; + DWORD64 Address; +} IMAGEHLP_LINEW64, *PIMAGEHLP_LINEW64; + +typedef struct _SYMBOL_INFOW +{ + ULONG SizeOfStruct; + ULONG TypeIndex; + ULONG64 Reserved[2]; + ULONG Index; + ULONG Size; + ULONG64 ModBase; + ULONG Flags; + ULONG64 Value; + ULONG64 Address; + ULONG Register; + ULONG Scope; + ULONG Tag; + ULONG NameLen; + ULONG MaxNameLen; + WCHAR Name[1]; +} SYMBOL_INFOW, *PSYMBOL_INFOW; + +typedef BOOL +(CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACKW)(PSYMBOL_INFOW pSymInfo, + ULONG SymbolSize, + PVOID UserContext); + +typedef BOOL +(CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACK)(PSYMBOL_INFO pSymInfo, + ULONG SymbolSize, + PVOID UserContext); + +#endif // API_VERSION_NUMBER < 10/*}}}*/ + +// wx-prefixed types map to either the ANSI or Unicode ("W") version depending +// on the build of wx itself. +#ifdef UNICODE + #define wxPSYM_ENUMERATESYMBOLS_CALLBACK PSYM_ENUMERATESYMBOLS_CALLBACKW +#else // !UNICODE + #define wxPSYM_ENUMERATESYMBOLS_CALLBACK PSYM_ENUMERATESYMBOLS_CALLBACK +#endif // UNICODE/!UNICODE + +// This one could be already defined by wx/msw/stackwalk.h +#ifndef wxSYMBOL_INFO + #ifdef UNICODE + #define wxSYMBOL_INFO SYMBOL_INFOW + #else // !UNICODE + #define wxSYMBOL_INFO SYMBOL_INFO + #endif // UNICODE/!UNICODE +#endif // !defined(wxSYMBOL_INFO) + +typedef wxSYMBOL_INFO* wxPSYMBOL_INFO; + +// This differs from PENUMLOADED_MODULES_CALLBACK[W]64 in that it always uses +// "const" for its first argument when the SDK used to pass a non-const string +// here until API_VERSION_NUMBER==11, so we can't just define it as an existing +// typedef. +typedef BOOL +(CALLBACK *wxPENUMLOADED_MODULES_CALLBACK)(const wxChar* moduleName, + DWORD64 moduleBase, + ULONG moduleSize, + void *userContext); + // ---------------------------------------------------------------------------- // wxDbgHelpDLL: dynamically load dbghelp.dll functions // ---------------------------------------------------------------------------- @@ -131,31 +248,61 @@ public: // function types typedef DWORD (WINAPI *SymGetOptions_t)(); typedef DWORD (WINAPI *SymSetOptions_t)(DWORD); - typedef BOOL (WINAPI *SymInitialize_t)(HANDLE, LPSTR, BOOL); + typedef BOOL (WINAPI *SymInitialize_t)(HANDLE, LPCSTR, BOOL); + typedef BOOL (WINAPI *SymInitializeW_t)(HANDLE, LPCWSTR, BOOL); typedef BOOL (WINAPI *StackWalk_t)(DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID, PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE); typedef BOOL (WINAPI *SymFromAddr_t)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO); + typedef BOOL (WINAPI *SymFromAddrW_t)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFOW); typedef LPVOID (WINAPI *SymFunctionTableAccess_t)(HANDLE, DWORD_PTR); typedef DWORD_PTR (WINAPI *SymGetModuleBase_t)(HANDLE, DWORD_PTR); - typedef BOOL (WINAPI *SymGetLineFromAddr_t)(HANDLE, DWORD_PTR, + typedef BOOL (WINAPI *SymGetLineFromAddr_t)(HANDLE, DWORD, PDWORD, PIMAGEHLP_LINE); + typedef BOOL (WINAPI *SymGetLineFromAddr64_t)(HANDLE, DWORD64, + PDWORD, PIMAGEHLP_LINE64); + typedef BOOL (WINAPI *SymGetLineFromAddrW64_t)(HANDLE, DWORD64, + PDWORD, PIMAGEHLP_LINEW64); typedef BOOL (WINAPI *SymSetContext_t)(HANDLE, PIMAGEHLP_STACK_FRAME, PIMAGEHLP_CONTEXT); typedef BOOL (WINAPI *SymEnumSymbols_t)(HANDLE, ULONG64, PCSTR, - PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID); + PSYM_ENUMERATESYMBOLS_CALLBACK, + const PVOID); + typedef BOOL (WINAPI *SymEnumSymbolsW_t)(HANDLE, ULONG64, PCWSTR, + PSYM_ENUMERATESYMBOLS_CALLBACKW, + const PVOID); typedef BOOL (WINAPI *SymGetTypeInfo_t)(HANDLE, DWORD64, ULONG, IMAGEHLP_SYMBOL_TYPE_INFO, PVOID); typedef BOOL (WINAPI *SymCleanup_t)(HANDLE); typedef BOOL (WINAPI *EnumerateLoadedModules_t)(HANDLE, PENUMLOADED_MODULES_CALLBACK, PVOID); + typedef BOOL (WINAPI *EnumerateLoadedModules64_t)(HANDLE, PENUMLOADED_MODULES_CALLBACK64, PVOID); + typedef BOOL (WINAPI *EnumerateLoadedModulesW64_t)(HANDLE, PENUMLOADED_MODULES_CALLBACKW64, PVOID); typedef BOOL (WINAPI *MiniDumpWriteDump_t)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, CONST PMINIDUMP_EXCEPTION_INFORMATION, CONST PMINIDUMP_USER_STREAM_INFORMATION, CONST PMINIDUMP_CALLBACK_INFORMATION); + // Higher level functions selecting the right debug help library function + // to call: for CallFoo(), it can be Foo(), Foo64(), FooW() or FooW64() + // depending on the build options and function availability. + // + // They also provide more convenient to use wx-specific API, e.g. work with + // wxString instead of char/wchar_t pointers and omit the arguments we + // don't need. + static BOOL CallSymInitialize(HANDLE, BOOL); + static BOOL CallEnumerateLoadedModules(HANDLE, wxPENUMLOADED_MODULES_CALLBACK, PVOID); + static BOOL CallSymFromAddr(HANDLE, DWORD64, + size_t* offset, wxString* name); + static BOOL CallSymGetLineFromAddr(HANDLE, DWORD64, + wxString* fileName, size_t* line); + static BOOL CallSymEnumSymbols(HANDLE hProcess, + ULONG64 baseOfDll, + wxPSYM_ENUMERATESYMBOLS_CALLBACK callback, + const PVOID callbackParam); + // The macro called by wxDO_FOR_ALL_SYM_FUNCS() below takes 2 arguments: // the name of the function in the program code, which never has "64" // suffix, and the name of the function in the DLL which can have "64" @@ -176,26 +323,56 @@ public: #define wxSYM_CALL_64(what, name) what(name, name) #endif - #define wxDO_FOR_ALL_SYM_FUNCS(what) \ - wxSYM_CALL_64(what, StackWalk); \ - wxSYM_CALL_64(what, SymFunctionTableAccess); \ - wxSYM_CALL_64(what, SymGetModuleBase); \ - wxSYM_CALL_64(what, SymGetLineFromAddr); \ - wxSYM_CALL_64(what, EnumerateLoadedModules); \ - \ - wxSYM_CALL(what, SymGetOptions); \ - wxSYM_CALL(what, SymSetOptions); \ - wxSYM_CALL(what, SymInitialize); \ - wxSYM_CALL(what, SymFromAddr); \ - wxSYM_CALL(what, SymSetContext); \ - wxSYM_CALL(what, SymEnumSymbols); \ - wxSYM_CALL(what, SymGetTypeInfo); \ - wxSYM_CALL(what, SymCleanup); \ + #define wxSYM_CALL_ALWAYS_W(what, name) what(name ## W, name ## W) + + #define wxSYM_CALL_ALTERNATIVES(what, name) \ + what(name, name); \ + what(name ## 64, name ## 64); \ + what(name ## W64, name ## W64) + + #define wxDO_FOR_ALL_SYM_FUNCS_REQUIRED_PUBLIC(what) \ + wxSYM_CALL_64(what, StackWalk); \ + wxSYM_CALL_64(what, SymFunctionTableAccess); \ + wxSYM_CALL_64(what, SymGetModuleBase); \ + \ + wxSYM_CALL(what, SymGetOptions); \ + wxSYM_CALL(what, SymSetOptions); \ + wxSYM_CALL(what, SymSetContext); \ + wxSYM_CALL(what, SymGetTypeInfo); \ + wxSYM_CALL(what, SymCleanup); \ wxSYM_CALL(what, MiniDumpWriteDump) + #define wxDO_FOR_ALL_SYM_FUNCS_REQUIRED_PRIVATE(what) \ + wxSYM_CALL(what, SymInitialize); \ + wxSYM_CALL(what, SymFromAddr); \ + wxSYM_CALL(what, SymEnumSymbols) + + #define wxDO_FOR_ALL_SYM_FUNCS_REQUIRED(what) \ + wxDO_FOR_ALL_SYM_FUNCS_REQUIRED_PRIVATE(what); \ + wxDO_FOR_ALL_SYM_FUNCS_REQUIRED_PUBLIC(what) + + // Alternation will work when the following functions are not found, + // therefore they are not included in REQUIRED version. + #define wxDO_FOR_ALL_SYM_FUNCS_OPTIONAL(what) \ + wxSYM_CALL_ALTERNATIVES(what, SymGetLineFromAddr); \ + wxSYM_CALL_ALTERNATIVES(what, EnumerateLoadedModules); \ + wxSYM_CALL_ALWAYS_W(what, SymInitialize); \ + wxSYM_CALL_ALWAYS_W(what, SymFromAddr); \ + wxSYM_CALL_ALWAYS_W(what, SymEnumSymbols) + + #define wxDO_FOR_ALL_SYM_FUNCS(what) \ + wxDO_FOR_ALL_SYM_FUNCS_REQUIRED(what); \ + wxDO_FOR_ALL_SYM_FUNCS_OPTIONAL(what) + #define wxDECLARE_SYM_FUNCTION(func, name) static func ## _t func - wxDO_FOR_ALL_SYM_FUNCS(wxDECLARE_SYM_FUNCTION); + wxDO_FOR_ALL_SYM_FUNCS_REQUIRED_PUBLIC(wxDECLARE_SYM_FUNCTION); + +private: + wxDO_FOR_ALL_SYM_FUNCS_REQUIRED_PRIVATE(wxDECLARE_SYM_FUNCTION); + wxDO_FOR_ALL_SYM_FUNCS_OPTIONAL(wxDECLARE_SYM_FUNCTION); + +public: #undef wxDECLARE_SYM_FUNCTION @@ -209,10 +386,10 @@ public: static void LogError(const wxChar *func); // return textual representation of the value of given symbol - static wxString DumpSymbol(PSYMBOL_INFO pSymInfo, void *pVariable); + static wxString DumpSymbol(wxPSYMBOL_INFO pSymInfo, void *pVariable); // return the name of the symbol with given type index - static wxString GetSymbolName(PSYMBOL_INFO pSymInfo); + static wxString GetSymbolName(wxPSYMBOL_INFO pSymInfo); private: // dereference the given symbol, i.e. return symbol which is not a @@ -222,17 +399,20 @@ private: // dereferenced the symbol // // return the tag of the dereferenced symbol - static SymbolTag DereferenceSymbol(PSYMBOL_INFO pSymInfo, void **ppData); + static SymbolTag DereferenceSymbol(wxPSYMBOL_INFO pSymInfo, void **ppData); - static wxString DumpField(PSYMBOL_INFO pSymInfo, + static wxString DumpField(wxPSYMBOL_INFO pSymInfo, void *pVariable, unsigned level); static wxString DumpBaseType(BasicType bt, DWORD64 length, void *pVariable); - static wxString DumpUDT(PSYMBOL_INFO pSymInfo, + static wxString DumpUDT(wxPSYMBOL_INFO pSymInfo, void *pVariable, unsigned level = 0); + + static bool BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp); + static bool DoInit(); }; #endif // wxUSE_DBGHELP diff --git a/include/wx/msw/stackwalk.h b/include/wx/msw/stackwalk.h index 6ff467205e..e068ea2d95 100644 --- a/include/wx/msw/stackwalk.h +++ b/include/wx/msw/stackwalk.h @@ -19,6 +19,13 @@ struct _EXCEPTION_POINTERS; // and these in dbghelp.h struct _SYMBOL_INFO; +struct _SYMBOL_INFOW; + +#if wxUSE_UNICODE + #define wxSYMBOL_INFO _SYMBOL_INFOW +#else // !wxUSE_UNICODE + #define wxSYMBOL_INFO _SYMBOL_INFO +#endif // wxUSE_UNICODE/!wxUSE_UNICODE // ---------------------------------------------------------------------------- // wxStackFrame @@ -52,7 +59,7 @@ public: GetParam(size_t n, wxString *type, wxString *name, wxString *value) const; // callback used by OnGetParam(), don't call directly - void OnParam(_SYMBOL_INFO *pSymInfo); + void OnParam(wxSYMBOL_INFO *pSymInfo); protected: virtual void OnGetName(); diff --git a/src/msw/debughlp.cpp b/src/msw/debughlp.cpp index 161a532933..592595d4a5 100644 --- a/src/msw/debughlp.cpp +++ b/src/msw/debughlp.cpp @@ -1,8 +1,7 @@ ///////////////////////////////////////////////////////////////////////////// // Name: src/msw/debughlp.cpp // Purpose: various Win32 debug helpers -// Author: Vadim Zeitlin -// Modified by: +// Author: Vadim Zeitlin, Suzumizaki-kimitaka // Created: 2005-01-08 (extracted from crashrpt.cpp) // Copyright: (c) 2003-2005 Vadim Zeitlin // Licence: wxWindows licence @@ -26,6 +25,8 @@ #if wxUSE_DBGHELP && wxUSE_DYNLIB_CLASS +#include "wx/scopedarray.h" + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -34,6 +35,59 @@ // ourselves to that many levels of embedded fields inside structs static const unsigned MAX_DUMP_DEPTH = 20; +// ---------------------------------------------------------------------------- +// local helpers +// ---------------------------------------------------------------------------- + +namespace +{ + +// Automatically initialize SizeOfStruct field of debug help structs to the +// correct value. +template +struct SizedStruct : public T +{ + SizedStruct() + { + ::ZeroMemory(this, sizeof(T)); + this->SizeOfStruct = sizeof(T); + } +}; + +// This is used for working with variable-sized SYMBOL_INFO[W] structs that +// have a variable length array as their last field. +template +class VarSizedStruct +{ +public: + VarSizedStruct() + { + ::ZeroMemory(m_buffer, sizeof(T) + MAX_NAME_LEN); + + (*this)->SizeOfStruct = sizeof(T); + (*this)->MaxNameLen = MAX_NAME_LEN; + } + + operator T*() + { + return static_cast(static_cast(m_buffer)); + } + + T* operator->() + { + return *this; + } + +private: + // Currently this is just a constant, we could make it a template parameter + // if we wanted. + enum { MAX_NAME_LEN = 1024 }; + + BYTE m_buffer[sizeof(T) + MAX_NAME_LEN]; +}; + +} // anonymous namespace + // ---------------------------------------------------------------------------- // globals // ---------------------------------------------------------------------------- @@ -62,7 +116,8 @@ wxDO_FOR_ALL_SYM_FUNCS(DEFINE_SYM_FUNCTION); // load all function we need from the DLL -static bool BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp) +/* static */ +bool wxDbgHelpDLL::BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp) { #define LOAD_SYM_FUNCTION(func, name) \ wxDbgHelpDLL::func = (wxDbgHelpDLL::func ## _t) \ @@ -73,15 +128,27 @@ static bool BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp) return false; \ } - wxDO_FOR_ALL_SYM_FUNCS(LOAD_SYM_FUNCTION); + wxDO_FOR_ALL_SYM_FUNCS_REQUIRED(LOAD_SYM_FUNCTION); #undef LOAD_SYM_FUNCTION + #define LOAD_SYM_FUNCTION_OPTIONAL(func, name) \ + if ( dllDbgHelp.HasSymbol(wxT(#name)) ) \ + { \ + wxDbgHelpDLL::func = (wxDbgHelpDLL::func ## _t) \ + dllDbgHelp.GetSymbol(wxT(#name)); \ + } + + wxDO_FOR_ALL_SYM_FUNCS_OPTIONAL(LOAD_SYM_FUNCTION_OPTIONAL); + + #undef LOAD_SYM_FUNCTION_CAN_FAIL + return true; } // called by Init() if we hadn't done this before -static bool DoInit() +/* static */ +bool wxDbgHelpDLL::DoInit() { wxDynamicLibrary dllDbgHelp(wxT("dbghelp.dll"), wxDL_VERBATIM); if ( dllDbgHelp.IsLoaded() ) @@ -170,13 +237,13 @@ DoGetTypeInfo(DWORD64 base, ULONG ti, IMAGEHLP_SYMBOL_TYPE_INFO type, void *rc) static inline bool -DoGetTypeInfo(PSYMBOL_INFO pSym, IMAGEHLP_SYMBOL_TYPE_INFO type, void *rc) +DoGetTypeInfo(wxPSYMBOL_INFO pSym, IMAGEHLP_SYMBOL_TYPE_INFO type, void *rc) { return DoGetTypeInfo(pSym->ModBase, pSym->TypeIndex, type, rc); } static inline -wxDbgHelpDLL::BasicType GetBasicType(PSYMBOL_INFO pSym) +wxDbgHelpDLL::BasicType GetBasicType(wxPSYMBOL_INFO pSym) { wxDbgHelpDLL::BasicType bt; return DoGetTypeInfo(pSym, TI_GET_BASETYPE, &bt) @@ -185,7 +252,7 @@ wxDbgHelpDLL::BasicType GetBasicType(PSYMBOL_INFO pSym) } /* static */ -wxString wxDbgHelpDLL::GetSymbolName(PSYMBOL_INFO pSym) +wxString wxDbgHelpDLL::GetSymbolName(wxPSYMBOL_INFO pSym) { wxString s; @@ -289,7 +356,7 @@ wxDbgHelpDLL::DumpBaseType(BasicType bt, DWORD64 length, PVOID pAddress) } wxString -wxDbgHelpDLL::DumpField(PSYMBOL_INFO pSym, void *pVariable, unsigned level) +wxDbgHelpDLL::DumpField(wxPSYMBOL_INFO pSym, void *pVariable, unsigned level) { wxString s; @@ -336,7 +403,7 @@ wxDbgHelpDLL::DumpField(PSYMBOL_INFO pSym, void *pVariable, unsigned level) // now pass to the type representing the type of this member - SYMBOL_INFO sym = *pSym; + wxSYMBOL_INFO sym = *pSym; if ( !DoGetTypeInfo(pSym, TI_GET_TYPEID, &sym.TypeIndex) ) break; @@ -387,7 +454,7 @@ wxDbgHelpDLL::DumpField(PSYMBOL_INFO pSym, void *pVariable, unsigned level) } /* static */ wxString -wxDbgHelpDLL::DumpUDT(PSYMBOL_INFO pSym, void *pVariable, unsigned level) +wxDbgHelpDLL::DumpUDT(wxPSYMBOL_INFO pSym, void *pVariable, unsigned level) { wxString s; @@ -454,7 +521,7 @@ wxDbgHelpDLL::DumpUDT(PSYMBOL_INFO pSym, void *pVariable, unsigned level) s << wxT(" {\n"); // Iterate through all children - SYMBOL_INFO sym; + wxSYMBOL_INFO sym; wxZeroMemory(sym); sym.ModBase = pSym->ModBase; for ( unsigned i = 0; i < dwChildrenCount; i++ ) @@ -486,7 +553,7 @@ wxDbgHelpDLL::DumpUDT(PSYMBOL_INFO pSym, void *pVariable, unsigned level) /* static */ wxDbgHelpDLL::SymbolTag -wxDbgHelpDLL::DereferenceSymbol(PSYMBOL_INFO pSym, void **ppData) +wxDbgHelpDLL::DereferenceSymbol(wxPSYMBOL_INFO pSym, void **ppData) { SymbolTag tag = SYMBOL_TAG_NULL; for ( ;; ) @@ -523,10 +590,10 @@ wxDbgHelpDLL::DereferenceSymbol(PSYMBOL_INFO pSym, void **ppData) } /* static */ wxString -wxDbgHelpDLL::DumpSymbol(PSYMBOL_INFO pSym, void *pVariable) +wxDbgHelpDLL::DumpSymbol(wxPSYMBOL_INFO pSym, void *pVariable) { wxString s; - SYMBOL_INFO symDeref = *pSym; + wxSYMBOL_INFO symDeref = *pSym; switch ( DereferenceSymbol(&symDeref, &pVariable) ) { default: @@ -552,6 +619,322 @@ wxDbgHelpDLL::DumpSymbol(PSYMBOL_INFO pSym, void *pVariable) return s; } +// ---------------------------------------------------------------------------- +// Helpers for selecting the best available function on the current platform. +// ---------------------------------------------------------------------------- + +// Store the original callback and the data for it: objects of this type are +// used as bridges for the enumeration functions taking callbacks of different +// types. +struct wxEnumLoadedCallbackBridge +{ +public: + wxEnumLoadedCallbackBridge(wxPENUMLOADED_MODULES_CALLBACK ptr, PVOID content) + : m_callback(ptr), m_data(content) + { + } + + wxPENUMLOADED_MODULES_CALLBACK m_callback; + PVOID m_data; +}; + +// As mentioned near wxPENUMLOADED_MODULES_CALLBACK definition, until v11 of +// the API the parameters were non-const. +#if API_VERSION_NUMBER < 11 + #define wxHAS_NON_CONST_MODULE_NAME + + #define wxMODULE_NAMEA PSTR +#else + #define wxMODULE_NAMEA PCSTR +#endif + +#if defined(UNICODE) && defined(wxHAS_NON_CONST_MODULE_NAME) + +static BOOL CALLBACK +wxEnumLoadedW64Callback(PWSTR ModuleName, + DWORD64 ModuleBase, + ULONG ModuleSize, + PVOID UserContext) +{ + wxEnumLoadedCallbackBridge& + bridge = *static_cast(UserContext); + + return (*bridge.m_callback)(ModuleName, ModuleBase, ModuleSize, bridge.m_data); +} + +#endif // UNICODE && wxHAS_NON_CONST_MODULE_NAME + +static BOOL CALLBACK +wxEnumLoaded64Callback(wxMODULE_NAMEA ModuleName, + DWORD64 ModuleBase, + ULONG ModuleSize, + PVOID UserContext) +{ + wxEnumLoadedCallbackBridge& + bridge = *static_cast(UserContext); + + return (*bridge.m_callback) + ( +#ifdef UNICODE + wxConvLocal.cMB2WC(ModuleName), +#else // !UNICODE + ModuleName, +#endif // UNICODE + ModuleBase, ModuleSize, bridge.m_data + ); +} + +static BOOL CALLBACK +wxEnumLoadedCallback(wxMODULE_NAMEA ModuleName, + DWORD_PTR ModuleBase, + ULONG ModuleSize, + PVOID UserContext) +{ + wxEnumLoadedCallbackBridge& + bridge = *static_cast(UserContext); + + return (*bridge.m_callback) + ( +#ifdef UNICODE + wxConvLocal.cMB2WC(ModuleName), +#else // !UNICODE + ModuleName, +#endif // UNICODE + ModuleBase, ModuleSize, bridge.m_data + ); +} + +/* static */ +BOOL +wxDbgHelpDLL::CallEnumerateLoadedModules(HANDLE handle, + wxPENUMLOADED_MODULES_CALLBACK callback, + PVOID callbackParam) +{ +#ifdef UNICODE + if ( EnumerateLoadedModulesW64 ) + { +#ifdef wxHAS_NON_CONST_MODULE_NAME + // We need to pass by a bridge just to convert non-const module name to + // the const one taken by our callback. + wxEnumLoadedCallbackBridge br(callback, callbackParam); + if ( EnumerateLoadedModulesW64(handle, &wxEnumLoadedW64Callback, &br) ) + return TRUE; +#else // new SDK + // We can use our callback directly. + if ( EnumerateLoadedModulesW64(handle, callback, callbackParam) ) + return TRUE; +#endif // old/new SDK + } +#endif // UNICODE + + if ( EnumerateLoadedModules64 ) + { + // We need a bridge if we need to convert to Unicode or if we're using + // an older SDK with a non-const first argument, so just use it always + // to avoid having too many complicated preprocessor checks. + wxEnumLoadedCallbackBridge br(callback, callbackParam); + if ( EnumerateLoadedModules64(handle, &wxEnumLoaded64Callback, &br) ) + return TRUE; + } + + if ( EnumerateLoadedModules ) + { + // With this function we need a bridge in any case because the type of + // module base argument used by EnumerateLoadedModules() differs from + // that used by EnumerateLoadedModules[W]64(). + wxEnumLoadedCallbackBridge br(callback, callbackParam); + if ( EnumerateLoadedModules(handle, &wxEnumLoadedCallback, &br) ) + return TRUE; + } + + return FALSE; +} + +/* static */ +BOOL +wxDbgHelpDLL::CallSymInitialize(HANDLE hProcess, BOOL fInvadeProcess) +{ +#ifdef UNICODE + if ( SymInitializeW ) + { + if ( SymInitializeW(hProcess, NULL, fInvadeProcess) ) + return TRUE; + } +#endif // UNICODE + + if ( SymInitialize ) + { + if ( SymInitialize(hProcess, NULL, fInvadeProcess) ) + return TRUE; + } + + return FALSE; +} + +/* static */ +BOOL +wxDbgHelpDLL::CallSymFromAddr(HANDLE hProcess, + DWORD64 Address, + size_t* offset, + wxString* name) +{ + DWORD64 dwDisplacement; + +#ifdef UNICODE + if ( SymFromAddrW ) + { + VarSizedStruct infoW; + if ( SymFromAddrW(hProcess, Address, &dwDisplacement, infoW) ) + { + *offset = dwDisplacement; + *name = infoW->Name; + return TRUE; + } + } +#endif // UNICODE + + if ( SymFromAddr ) + { + VarSizedStruct info; + if ( SymFromAddr(hProcess, Address, &dwDisplacement, info) ) + { + *offset = dwDisplacement; + *name = info->Name; + return TRUE; + } + } + + return FALSE; +} + +/* static */ +BOOL +wxDbgHelpDLL::CallSymGetLineFromAddr(HANDLE hProcess, + DWORD64 dwAddr, + wxString* fileName, + size_t* line) +{ + DWORD dwDisplacement; + +#ifdef UNICODE + if ( SymGetLineFromAddrW64 ) + { + SizedStruct lineW64; + if ( SymGetLineFromAddrW64(hProcess, dwAddr, &dwDisplacement, &lineW64) ) + { + *fileName = lineW64.FileName; + *line = lineW64.LineNumber; + return TRUE; + } + } +#endif // UNICODE + + if ( SymGetLineFromAddr64 ) + { + SizedStruct line64; + if ( SymGetLineFromAddr64(hProcess, dwAddr, &dwDisplacement, &line64) ) + { + *fileName = line64.FileName; + *line = line64.LineNumber; + return TRUE; + } + } + + if ( SymGetLineFromAddr ) + { + SizedStruct line32; + if ( SymGetLineFromAddr(hProcess, dwAddr, &dwDisplacement, &line32) ) + { + *fileName = line32.FileName; + *line = line32.LineNumber; + return TRUE; + } + } + + return FALSE; +} + +#ifdef UNICODE + +// Allow to adapt callback supposed to be used with SymEnumSymbolsW() with +// SymEnumSymbols(). +struct wxEnumSymbolsCallbackBridge +{ +public: + wxEnumSymbolsCallbackBridge(PSYM_ENUMERATESYMBOLS_CALLBACKW ptr, PVOID content) + : m_callback(ptr), m_data(content) + { + } + + PSYM_ENUMERATESYMBOLS_CALLBACKW m_callback; + PVOID m_data; +}; + +static BOOL CALLBACK +wxEnumSymbolsCallback(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext) +{ + wxEnumSymbolsCallbackBridge& + bridge = *static_cast(UserContext); + + const wxWCharBuffer buf = wxConvLocal.cMB2WC(pSymInfo->Name); + const size_t len = buf.length(); + + // Allocate enough space for the wide char version of the struct. + // + // Note that there is no +1 here because sizeof(SYMBOL_INFOW) already + // includes 1 byte of the name. + wxScopedArray symbolBuffer(sizeof(SYMBOL_INFOW) + len*sizeof(WCHAR)); + + SYMBOL_INFOW* const infoW = (SYMBOL_INFOW*)symbolBuffer.get(); + + // Copy the original struct contents except for the last byte, which is the + // first byte of its (narrow) name that we don't need. + CopyMemory(infoW, pSymInfo, sizeof(SYMBOL_INFO) - sizeof(CHAR)); + + // Size is different for narrow and wide variants of the struct, so fix it + // up now. + infoW->SizeOfStruct = sizeof(SYMBOL_INFOW); + + // Finally copy the converted name, which is the reason for doing all this. + wxStrncpy(infoW->Name, buf.data(), len); + + return (*bridge.m_callback)(infoW, SymbolSize, bridge.m_data); +} + +#endif // UNICODE + +/* static */ +BOOL +wxDbgHelpDLL::CallSymEnumSymbols(HANDLE hProcess, + ULONG64 baseOfDll, + wxPSYM_ENUMERATESYMBOLS_CALLBACK callback, + const PVOID callbackParam) +{ +#ifdef UNICODE + if ( SymEnumSymbolsW ) + { + if ( SymEnumSymbolsW(hProcess, baseOfDll, NULL, callback, callbackParam) ) + return TRUE; + } + + if ( SymEnumSymbols ) + { + wxEnumSymbolsCallbackBridge br(callback, callbackParam); + + if ( SymEnumSymbols(hProcess, baseOfDll, NULL, wxEnumSymbolsCallback, &br) ) + return TRUE; + } +#else // !UNICODE + if ( SymEnumSymbols ) + { + if ( SymEnumSymbols(hProcess, baseOfDll, NULL, callback, callbackParam) ) + return TRUE; + } +#endif // UNICODE/!UNICODE + + return FALSE; +} + // ---------------------------------------------------------------------------- // debugging helpers // ---------------------------------------------------------------------------- diff --git a/src/msw/dlmsw.cpp b/src/msw/dlmsw.cpp index 9bdbae9c13..0118a3ff22 100644 --- a/src/msw/dlmsw.cpp +++ b/src/msw/dlmsw.cpp @@ -77,15 +77,8 @@ public: wxVersionDLL *verDLL; }; - // TODO: fix EnumerateLoadedModules() to use EnumerateLoadedModules64() - #ifdef __WIN64__ - typedef DWORD64 DWORD_32_64; - #else - typedef DWORD DWORD_32_64; - #endif - static BOOL CALLBACK - EnumModulesProc(PCSTR name, DWORD_32_64 base, ULONG size, void *data); + EnumModulesProc(const wxChar* name, DWORD64 base, ULONG size, PVOID data); }; // ============================================================================ @@ -172,8 +165,8 @@ wxString wxVersionDLL::GetFileVersion(const wxString& filename) const /* static */ BOOL CALLBACK -wxDynamicLibraryDetailsCreator::EnumModulesProc(PCSTR name, - DWORD_32_64 base, +wxDynamicLibraryDetailsCreator::EnumModulesProc(const wxChar* name, + DWORD64 base, ULONG size, void *data) { @@ -276,15 +269,9 @@ wxDynamicLibraryDetailsArray wxDynamicLibrary::ListLoaded() params.dlls = &dlls; params.verDLL = &verDLL; - // Note that the cast of EnumModulesProc is needed because the type of - // PENUMLOADED_MODULES_CALLBACK changed: in old SDK versions its first - // argument was non-const PSTR while now it's PCSTR. By explicitly - // casting to whatever the currently used headers require we ensure - // that the code compilers in any case. - if ( !wxDbgHelpDLL::EnumerateLoadedModules + if ( !wxDbgHelpDLL::CallEnumerateLoadedModules ( ::GetCurrentProcess(), - (PENUMLOADED_MODULES_CALLBACK) wxDynamicLibraryDetailsCreator::EnumModulesProc, ¶ms ) ) diff --git a/src/msw/stackwalk.cpp b/src/msw/stackwalk.cpp index f9c8ca986e..4b59c4a381 100644 --- a/src/msw/stackwalk.cpp +++ b/src/msw/stackwalk.cpp @@ -50,29 +50,16 @@ void wxStackFrame::OnGetName() m_hasName = true; // get the name of the function for this stack frame entry - static const size_t MAX_NAME_LEN = 1024; - BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_NAME_LEN]; - wxZeroMemory(symbolBuffer); - - PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer; - pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); - pSymbol->MaxNameLen = MAX_NAME_LEN; - - DWORD64 symDisplacement = 0; - if ( !wxDbgHelpDLL::SymFromAddr + if ( !wxDbgHelpDLL::CallSymFromAddr ( ::GetCurrentProcess(), GetSymAddr(), - &symDisplacement, - pSymbol + &m_offset, + &m_name ) ) { wxDbgHelpDLL::LogError(wxT("SymFromAddr")); - return; } - - m_name = wxString::FromAscii(pSymbol->Name); - m_offset = symDisplacement; } void wxStackFrame::OnGetLocation() @@ -82,25 +69,11 @@ void wxStackFrame::OnGetLocation() m_hasLocation = true; - // get the source line for this stack frame entry - IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) }; - DWORD dwLineDisplacement; - if ( !wxDbgHelpDLL::SymGetLineFromAddr - ( - ::GetCurrentProcess(), - GetSymAddr(), - &dwLineDisplacement, - &lineInfo - ) ) - { - // it is normal that we don't have source info for some symbols, - // notably all the ones from the system DLLs... - //wxDbgHelpDLL::LogError(wxT("SymGetLineFromAddr")); - return; - } - - m_filename = wxString::FromAscii(lineInfo.FileName); - m_line = lineInfo.LineNumber; + // get the source line for this stack frame entry ignoring possible errors + // (it's normal that we don't have source info for some symbols, e.g. all + // those from the system DLLs) + wxDbgHelpDLL::CallSymGetLineFromAddr(::GetCurrentProcess(), GetSymAddr(), + &m_filename, &m_line); } bool @@ -125,11 +98,10 @@ wxStackFrame::GetParam(size_t n, return true; } -void wxStackFrame::OnParam(PSYMBOL_INFO pSymInfo) +void wxStackFrame::OnParam(wxSYMBOL_INFO *pSymInfo) { m_paramTypes.Add(wxEmptyString); - - m_paramNames.Add(wxString::FromAscii(pSymInfo->Name)); + m_paramNames.Add(pSymInfo->Name); // if symbol information is corrupted and we crash, the exception is going // to be ignored when we're called from WalkFromException() because of the @@ -158,7 +130,7 @@ void wxStackFrame::OnParam(PSYMBOL_INFO pSymInfo) } BOOL CALLBACK -EnumSymbolsProc(PSYMBOL_INFO pSymInfo, ULONG WXUNUSED(SymSize), PVOID data) +EnumSymbolsProc(wxPSYMBOL_INFO pSymInfo, ULONG WXUNUSED(SymSize), PVOID data) { wxStackFrame *frame = static_cast(data); @@ -195,11 +167,10 @@ void wxStackFrame::OnGetParam() return; } - if ( !wxDbgHelpDLL::SymEnumSymbols + if ( !wxDbgHelpDLL::CallSymEnumSymbols ( ::GetCurrentProcess(), NULL, // DLL base: use current context - NULL, // no mask, get all symbols EnumSymbolsProc, // callback this // data to pass to it ) ) @@ -232,10 +203,9 @@ void wxStackWalker::WalkFrom(const CONTEXT *pCtx, size_t skip, size_t maxDepth) // below which should be a real handle... so this is what we use const HANDLE hProcess = ::GetCurrentProcess(); - if ( !wxDbgHelpDLL::SymInitialize + if ( !wxDbgHelpDLL::CallSymInitialize ( hProcess, - NULL, // use default symbol search path TRUE // load symbols for all loaded modules ) ) { @@ -382,7 +352,7 @@ wxStackFrame::GetParam(size_t WXUNUSED(n), return false; } -void wxStackFrame::OnParam(_SYMBOL_INFO * WXUNUSED(pSymInfo)) +void wxStackFrame::OnParam(wxSYMBOL_INFO * WXUNUSED(pSymInfo)) { }