diff --git a/configure b/configure index dbe140008e..0b45fb4c8f 100755 --- a/configure +++ b/configure @@ -1195,6 +1195,7 @@ enable_protocol_http enable_protocol_ftp enable_protocol_file enable_threads +enable_dbghelp enable_iniconf enable_regkey enable_docview @@ -2127,6 +2128,7 @@ Optional Features: --enable-protocol-ftp FTP support in wxProtocol --enable-protocol-file FILE support in wxProtocol --enable-threads use threads + --enable-dbghelp use dbghelp.dll API (Win32 only) --enable-iniconf use wxIniConfig (Win32 only) --enable-regkey use wxRegKey class (Win32 only) --enable-docview use document view architecture @@ -7745,6 +7747,36 @@ fi eval "$wx_cv_use_threads" +if test "$wxUSE_MSW" = 1 ; then + + enablestring=disable + defaultval= + if test -z "$defaultval"; then + if test x"$enablestring" = xdisable; then + defaultval=yes + else + defaultval=no + fi + fi + + # Check whether --enable-dbghelp was given. +if test "${enable_dbghelp+set}" = set; then : + enableval=$enable_dbghelp; + if test "$enableval" = yes; then + wx_cv_use_dbghelp='wxUSE_DBGHELP=yes' + else + wx_cv_use_dbghelp='wxUSE_DBGHELP=no' + fi + +else + + wx_cv_use_dbghelp='wxUSE_DBGHELP=${'DEFAULT_wxUSE_DBGHELP":-$defaultval}" + +fi + + + eval "$wx_cv_use_dbghelp" + enablestring= defaultval= @@ -7803,6 +7835,7 @@ fi eval "$wx_cv_use_regkey" +fi if test "$wxUSE_GUI" = "yes"; then @@ -35882,6 +35915,63 @@ if test "$wxUSE_AUTOID_MANAGEMENT" = "yes"; then fi if test "$USE_WIN32" = 1 ; then + if test "$wxUSE_DBGHELP" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if debug help API is available" >&5 +$as_echo_n "checking if debug help API is available... " >&6; } +if ${wx_cv_lib_debughlp+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include +int +main () +{ + + #ifndef API_VERSION_NUMBER + #error API_VERSION_NUMBER not defined! + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + wx_cv_lib_debughlp=yes +else + wx_cv_lib_debughlp=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $wx_cv_lib_debughlp" >&5 +$as_echo "$wx_cv_lib_debughlp" >&6; } + + if test "$wx_cv_lib_debughlp" = yes; then + $as_echo "#define wxUSE_DBGHELP 1" >>confdefs.h + + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Debug help API and wxStackWalker won't be available" >&5 +$as_echo "$as_me: WARNING: Debug help API and wxStackWalker won't be available" >&2;} + fi + fi + if test "$wxUSE_INICONF" = "yes"; then $as_echo "#define wxUSE_INICONF 1" >>confdefs.h diff --git a/configure.in b/configure.in index 41d34cdcc2..3b30d65837 100644 --- a/configure.in +++ b/configure.in @@ -743,8 +743,11 @@ WX_ARG_FEATURE(protocol_file, [ --enable-protocol-file FILE support in wxProto WX_ARG_FEATURE(threads, [ --enable-threads use threads], wxUSE_THREADS) +if test "$wxUSE_MSW" = 1 ; then +WX_ARG_DISABLE(dbghelp, [ --enable-dbghelp use dbghelp.dll API (Win32 only)], wxUSE_DBGHELP) WX_ARG_ENABLE(iniconf, [ --enable-iniconf use wxIniConfig (Win32 only)], wxUSE_INICONF) WX_ARG_FEATURE(regkey, [ --enable-regkey use wxRegKey class (Win32 only)], wxUSE_REGKEY) +fi if test "$wxUSE_GUI" = "yes"; then @@ -7157,6 +7160,34 @@ if test "$wxUSE_AUTOID_MANAGEMENT" = "yes"; then fi if test "$USE_WIN32" = 1 ; then + if test "$wxUSE_DBGHELP" = "yes"; then + AC_CACHE_CHECK([if debug help API is available], wx_cv_lib_debughlp, + [ + dnl we need just the header, not the library, as we load the + dnl DLL dynamically anyhow during run-time + AC_LANG_PUSH(C++) + AC_TRY_COMPILE( + [#include + #include ], + [ + #ifndef API_VERSION_NUMBER + #error API_VERSION_NUMBER not defined! + #endif + ], + wx_cv_lib_debughlp=yes, + wx_cv_lib_debughlp=no + ) + AC_LANG_POP() + ] + ) + + if test "$wx_cv_lib_debughlp" = yes; then + AC_DEFINE(wxUSE_DBGHELP) + else + AC_MSG_WARN([Debug help API and wxStackWalker won't be available]) + fi + fi + if test "$wxUSE_INICONF" = "yes"; then AC_DEFINE(wxUSE_INICONF) fi diff --git a/docs/changes.txt b/docs/changes.txt index 26b24e25d3..91016e75e1 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -75,6 +75,7 @@ wxGTK: wxMSW: +- Enable wxStackWalker in MinGW64 builds. - Fix crash when using wxCHMHelpController() in 64 bit builds (Xlord2). - Fix MDI menu display after failure to create a child frame (troelsk). diff --git a/include/wx/gtk/setup0.h b/include/wx/gtk/setup0.h index 0ab4718bdf..277c2e4fd8 100644 --- a/include/wx/gtk/setup0.h +++ b/include/wx/gtk/setup0.h @@ -1668,6 +1668,18 @@ // Crash debugging helpers // ---------------------------------------------------------------------------- +// Set this to 1 to use dbghelp.dll for providing stack traces in crash +// reports. +// +// Default is 1 if the compiler supports it, 0 for old MinGW. +// +// Recommended setting: 1, there is not much gain in disabling this +#if defined(__VISUALC__) || defined(__MINGW64_TOOLCHAIN__) + #define wxUSE_DBGHELP 1 +#else + #define wxUSE_DBGHELP 0 +#endif + // Set this to 1 to be able to use wxCrashReport::Generate() to create mini // dumps of your program when it crashes (or at any other moment) // diff --git a/include/wx/msw/chkconf.h b/include/wx/msw/chkconf.h index 656ef98017..8571cc4607 100644 --- a/include/wx/msw/chkconf.h +++ b/include/wx/msw/chkconf.h @@ -38,6 +38,14 @@ # endif #endif /* !defined(wxUSE_CRASHREPORT) */ +#ifndef wxUSE_DBGHELP +# ifdef wxABORT_ON_CONFIG_ERROR +# error "wxUSE_DBGHELP must be defined" +# else +# define wxUSE_DBGHELP 1 +# endif +#endif /* wxUSE_DBGHELP */ + #ifndef wxUSE_DC_CACHEING # ifdef wxABORT_ON_CONFIG_ERROR # error "wxUSE_DC_CACHEING must be defined" @@ -146,9 +154,6 @@ # undef wxUSE_CRASHREPORT # define wxUSE_CRASHREPORT 0 - -# undef wxUSE_STACKWALKER -# define wxUSE_STACKWALKER 0 #endif /* compiler doesn't support SEH */ #if defined(__GNUWIN32__) @@ -292,6 +297,14 @@ #endif /* !wxUSE_DYNAMIC_LOADER */ #if !wxUSE_DYNLIB_CLASS +# if wxUSE_DBGHELP +# ifdef wxABORT_ON_CONFIG_ERROR +# error "wxUSE_DBGHELP requires wxUSE_DYNLIB_CLASS" +# else +# undef wxUSE_DBGHELP +# define wxUSE_DBGHELP 0 +# endif +# endif # if wxUSE_DC_TRANSFORM_MATRIX # ifdef wxABORT_ON_CONFIG_ERROR # error "wxUSE_DC_TRANSFORM_MATRIX requires wxUSE_DYNLIB_CLASS" @@ -383,6 +396,22 @@ # endif #endif /* wxUSE_ACTIVITYINDICATOR */ +#if wxUSE_STACKWALKER && !wxUSE_DBGHELP + /* + Don't give an error in this case because wxUSE_DBGHELP could be 0 + because the compiler just doesn't support it, there is really no other + choice than to disable wxUSE_STACKWALKER too in this case. + + Unfortunately we can't distinguish between the missing compiler support + and explicitly disabling wxUSE_DBGHELP (which would ideally result in + an error if wxUSE_STACKWALKER is not disabled too), but it's better to + avoid giving a compiler error in the former case even if it means not + giving it neither in the latter one. + */ + #undef wxUSE_STACKWALKER + #define wxUSE_STACKWALKER 0 +#endif /* wxUSE_STACKWALKER && !wxUSE_DBGHELP */ + #if !wxUSE_THREADS # if wxUSE_FSWATCHER # ifdef wxABORT_ON_CONFIG_ERROR diff --git a/include/wx/msw/debughlp.h b/include/wx/msw/debughlp.h index b224c19a23..44d6d1ece2 100644 --- a/include/wx/msw/debughlp.h +++ b/include/wx/msw/debughlp.h @@ -10,8 +10,11 @@ #ifndef _WX_MSW_DEBUGHLPH_H_ #define _WX_MSW_DEBUGHLPH_H_ -#include "wx/dynlib.h" +#include "wx/defs.h" +#if wxUSE_DBGHELP + +#include "wx/dynlib.h" #include "wx/msw/wrapwin.h" #ifdef __VISUALC__ @@ -31,26 +34,6 @@ #include "wx/msw/private.h" -// 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__ - // 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 -#endif - -#if wxUSE_DBGHELP - /* The table below shows which functions are exported by dbghelp.dll. diff --git a/include/wx/msw/setup0.h b/include/wx/msw/setup0.h index 8ade54c2e9..abee9b51d4 100644 --- a/include/wx/msw/setup0.h +++ b/include/wx/msw/setup0.h @@ -1560,7 +1560,7 @@ // SDK components manually, you need to change this setting. // // Recommended setting: 1 -#if defined(_MSC_VER) && _MSC_VER >= 1700 && !defined(_USING_V110_SDK71_) +#if defined(_MSC_VER) && _MSC_VER >= 1700 #define wxUSE_WINRT 1 #else #define wxUSE_WINRT 0 @@ -1668,6 +1668,18 @@ // Crash debugging helpers // ---------------------------------------------------------------------------- +// Set this to 1 to use dbghelp.dll for providing stack traces in crash +// reports. +// +// Default is 1 if the compiler supports it, 0 for old MinGW. +// +// Recommended setting: 1, there is not much gain in disabling this +#if defined(__VISUALC__) || defined(__MINGW64_TOOLCHAIN__) + #define wxUSE_DBGHELP 1 +#else + #define wxUSE_DBGHELP 0 +#endif + // Set this to 1 to be able to use wxCrashReport::Generate() to create mini // dumps of your program when it crashes (or at any other moment) // diff --git a/include/wx/msw/setup_inc.h b/include/wx/msw/setup_inc.h index 639fbe6523..ff5fc00102 100644 --- a/include/wx/msw/setup_inc.h +++ b/include/wx/msw/setup_inc.h @@ -180,6 +180,18 @@ // Crash debugging helpers // ---------------------------------------------------------------------------- +// Set this to 1 to use dbghelp.dll for providing stack traces in crash +// reports. +// +// Default is 1 if the compiler supports it, 0 for old MinGW. +// +// Recommended setting: 1, there is not much gain in disabling this +#if defined(__VISUALC__) || defined(__MINGW64_TOOLCHAIN__) + #define wxUSE_DBGHELP 1 +#else + #define wxUSE_DBGHELP 0 +#endif + // Set this to 1 to be able to use wxCrashReport::Generate() to create mini // dumps of your program when it crashes (or at any other moment) // diff --git a/include/wx/stackwalk.h b/include/wx/stackwalk.h index 1133d6ef7c..c0ba151d75 100644 --- a/include/wx/stackwalk.h +++ b/include/wx/stackwalk.h @@ -15,6 +15,8 @@ #if wxUSE_STACKWALKER +#include "wx/string.h" + class WXDLLIMPEXP_FWD_BASE wxStackFrame; #define wxSTACKWALKER_MAX_DEPTH (200) diff --git a/interface/wx/stackwalk.h b/interface/wx/stackwalk.h index c27e9158da..808796583f 100644 --- a/interface/wx/stackwalk.h +++ b/interface/wx/stackwalk.h @@ -25,13 +25,18 @@ class from it and override this method. This class will not return anything except raw stack frame addresses if the - debug information is not available. Under Win32 this means that the PDB file - matching the program being executed should be present. + debug information is not available. Under Microsoft Windows this means that + the PDB file matching the program being executed should be present. Note that if you use Microsoft Visual C++ compiler, you can create PDB files even for the programs built in release mode and it doesn't affect the program size (at least if you don't forget to add @c /opt:ref option which is suppressed by using @c /debug linker option by default but should be always enabled for - release builds). + release builds). If your compiler doesn't provide a direct way of + generating PDB files but does produce debug information in the older "Code + View" format or compatible, which is the case for MinGW, you can use @c + cv2pdb tool available at https://github.com/rainers/cv2pdb to create PDB + for your binaries which will work well when using this class. + Under Unix, you need to compile your program with debugging information (usually using @c -g compiler and linker options) to get the file and line numbers information, however function names should be available even without it. diff --git a/setup.h.in b/setup.h.in index 6929ae0a48..bca48a99f8 100644 --- a/setup.h.in +++ b/setup.h.in @@ -691,6 +691,12 @@ #define wxUSE_TIMEPICKCTRL_GENERIC 0 +#if defined(__VISUALC__) || defined(__MINGW64_TOOLCHAIN__) + #define wxUSE_DBGHELP 0 +#else + #define wxUSE_DBGHELP 0 +#endif + #define wxUSE_CRASHREPORT 0 /* --- end MSW options --- */ diff --git a/src/msw/dlmsw.cpp b/src/msw/dlmsw.cpp index dedc04ce2a..ff0bb127aa 100644 --- a/src/msw/dlmsw.cpp +++ b/src/msw/dlmsw.cpp @@ -24,6 +24,8 @@ #if wxUSE_DYNLIB_CLASS +#include "wx/dynlib.h" + #include "wx/msw/private.h" #include "wx/msw/debughlp.h" #include "wx/filename.h" diff --git a/src/msw/stackwalk.cpp b/src/msw/stackwalk.cpp index 4b59c4a381..4c86de102c 100644 --- a/src/msw/stackwalk.cpp +++ b/src/msw/stackwalk.cpp @@ -31,8 +31,7 @@ #include "wx/stackwalk.h" #include "wx/msw/debughlp.h" - -#if wxUSE_DBGHELP +#include "wx/msw/seh.h" // ============================================================================ // implementation @@ -103,6 +102,8 @@ void wxStackFrame::OnParam(wxSYMBOL_INFO *pSymInfo) m_paramTypes.Add(wxEmptyString); m_paramNames.Add(pSymInfo->Name); + wxString value; + // if symbol information is corrupted and we crash, the exception is going // to be ignored when we're called from WalkFromException() because of the // exception handler there returning EXCEPTION_CONTINUE_EXECUTION, but we'd @@ -111,22 +112,23 @@ void wxStackFrame::OnParam(wxSYMBOL_INFO *pSymInfo) #ifdef _CPPUNWIND try #else - __try + wxSEH_TRY #endif { // as it is a parameter (and not a global var), it is always offset by // the frame address DWORD_PTR pValue = m_addrFrame + pSymInfo->Address; - m_paramValues.Add(wxDbgHelpDLL::DumpSymbol(pSymInfo, (void *)pValue)); + value = wxDbgHelpDLL::DumpSymbol(pSymInfo, (void *)pValue); } #ifdef _CPPUNWIND catch ( ... ) -#else - __except ( EXCEPTION_EXECUTE_HANDLER ) -#endif { - m_paramValues.Add(wxEmptyString); } +#else + wxSEH_IGNORE +#endif + + m_paramValues.Add(value); } BOOL CALLBACK @@ -170,7 +172,7 @@ void wxStackFrame::OnGetParam() if ( !wxDbgHelpDLL::CallSymEnumSymbols ( ::GetCurrentProcess(), - NULL, // DLL base: use current context + 0, // DLL base: use current context EnumSymbolsProc, // callback this // data to pass to it ) ) @@ -304,91 +306,47 @@ void wxStackWalker::WalkFromException(size_t maxDepth) #endif // wxUSE_ON_FATAL_EXCEPTION -void wxStackWalker::Walk(size_t skip, size_t WXUNUSED(maxDepth)) +void wxStackWalker::Walk(size_t skip, size_t maxDepth) { - // to get a CONTEXT for the current location, simply force an exception and - // get EXCEPTION_POINTERS from it - // - // note: - // 1. we additionally skip RaiseException() and WalkFrom() frames - // 2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it - // shouldn't have been according to the docs - __try - { - RaiseException(0x1976, 0, 0, NULL); - } - __except( WalkFrom((EXCEPTION_POINTERS *)GetExceptionInformation(), - skip + 2), EXCEPTION_CONTINUE_EXECUTION ) - { - // never executed because the above expression always evaluates to - // EXCEPTION_CONTINUE_EXECUTION - } + // This code is based on frames.cpp from Edd Dawson's dbg library + // (https://bitbucket.org/edd/dbg/) which is distributed under Boost + // Software License. + + CONTEXT ctx; +#ifdef __WIN64__ + RtlCaptureContext(&ctx); +#else // Win32 + // RtlCaptureContext() is not implemented correctly for x86 and can even + // crash when frame pointer is omitted, don't use it. + wxZeroMemory(ctx); + ctx.ContextFlags = CONTEXT_CONTROL; + + #ifdef __GNUC__ + DWORD regEip, regEsp, regEbp; + + asm volatile ("call 1f\n\t" "1: pop %0" : "=g"(regEip)); + asm volatile ("movl %%esp, %0" : "=g"(regEsp)); + asm volatile ("movl %%ebp, %0" : "=g"(regEbp)); + + ctx.Eip = regEip; + ctx.Esp = regEsp; + ctx.Ebp = regEbp; + #elif __VISUALC__ + __asm + { + Here: + mov [ctx.Ebp], ebp + mov [ctx.Esp], esp + mov eax, [Here] + mov [ctx.Eip], eax + } + #else + #error Missing implementation of RtlCaptureContext() + #endif +#endif // Win64/32 + + WalkFrom(&ctx, skip, maxDepth); } -#else // !wxUSE_DBGHELP - -// ============================================================================ -// stubs -// ============================================================================ - -// ---------------------------------------------------------------------------- -// wxStackFrame -// ---------------------------------------------------------------------------- - -void wxStackFrame::OnGetName() -{ -} - -void wxStackFrame::OnGetLocation() -{ -} - -bool -wxStackFrame::GetParam(size_t WXUNUSED(n), - wxString * WXUNUSED(type), - wxString * WXUNUSED(name), - wxString * WXUNUSED(value)) const -{ - return false; -} - -void wxStackFrame::OnParam(wxSYMBOL_INFO * WXUNUSED(pSymInfo)) -{ -} - -void wxStackFrame::OnGetParam() -{ -} - -// ---------------------------------------------------------------------------- -// wxStackWalker -// ---------------------------------------------------------------------------- - -void -wxStackWalker::WalkFrom(const CONTEXT * WXUNUSED(pCtx), - size_t WXUNUSED(skip), - size_t WXUNUSED(maxDepth)) -{ -} - -void -wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS * WXUNUSED(ep), - size_t WXUNUSED(skip), - size_t WXUNUSED(maxDepth)) -{ -} - -#if wxUSE_ON_FATAL_EXCEPTION -void wxStackWalker::WalkFromException(size_t WXUNUSED(maxDepth)) -{ -} -#endif // wxUSE_ON_FATAL_EXCEPTION - -void wxStackWalker::Walk(size_t WXUNUSED(skip), size_t WXUNUSED(maxDepth)) -{ -} - -#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP - #endif // wxUSE_STACKWALKER