diff --git a/docs/changes.txt b/docs/changes.txt index a7fb5917f5..67c5b5caac 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -74,6 +74,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/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/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/src/msw/stackwalk.cpp b/src/msw/stackwalk.cpp index 04b6cde175..c96a12bdb6 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 @@ -306,6 +308,8 @@ void wxStackWalker::WalkFromException(size_t maxDepth) void wxStackWalker::Walk(size_t skip, size_t maxDepth) { + // We use it as a proxy for SEH support here. +#if wxUSE_ON_FATAL_EXCEPTION // to get a CONTEXT for the current location, simply force an exception and // get EXCEPTION_POINTERS from it // @@ -323,72 +327,38 @@ void wxStackWalker::Walk(size_t skip, size_t maxDepth) // never executed because the above expression always evaluates to // EXCEPTION_CONTINUE_EXECUTION } -} +#else // !wxUSE_ON_FATAL_EXCEPTION + // 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. -#else // !wxUSE_DBGHELP + 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; -// ============================================================================ -// stubs -// ============================================================================ + #ifdef __GNUC__ + DWORD regEip, regEsp, regEbp; -// ---------------------------------------------------------------------------- -// wxStackFrame -// ---------------------------------------------------------------------------- + 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)); -void wxStackFrame::OnGetName() -{ -} + ctx.Eip = regEip; + ctx.Esp = regEsp; + ctx.Ebp = regEbp; + #else + #error Missing implementation of RtlCaptureContext() + #endif +#endif // Win64/32 -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)) -{ -} + WalkFrom(&ctx, skip, 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