X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f83aa7771cc31b8de34beb4dc32709799f6d2281..05b2a432c6cecd70c825a99ff357ab0cde1877f8:/src/msw/crashrpt.cpp diff --git a/src/msw/crashrpt.cpp b/src/msw/crashrpt.cpp index 10996846cd..c3411045b4 100644 --- a/src/msw/crashrpt.cpp +++ b/src/msw/crashrpt.cpp @@ -10,8 +10,10 @@ ///////////////////////////////////////////////////////////////////////////// /* - The code in this file is heavily based on Matt Pietrek's column from - the March 2002 issue of MSDN Magazine. + The code generating the crash reports in this file is heavily based on + Matt Pietrek's column from the March 2002 issue of MSDN Magazine. Note + that this code is not currently used by default, however. In any case, + all bugs are my alone. */ // ============================================================================ @@ -34,8 +36,28 @@ #ifndef WX_PRECOMP #endif //WX_PRECOMP -#include "wx/longlong.h" +/* + We have two possibilities here: one, a priori more interesting, is to + generate the crash report ourselves and include the values of all the + variables in the dump. Unfortunately my code to do it doesn't work in + "real life" situations i.e. it works in small examples but invariably + gets confused by something in big programs which makes quite useless. + + The other possibility is to let dbghelp.dll to do the work for us and + analyze its results later using a debugger with knowledge about crash + dumps, such as (free!) WinDbg. This also has another advantage of not + needing to ship the .pdb file (containing debug info) to the user. So + this is the default now, but I keep the old code just in case, and if + you really want you can still use it. + */ +#define wxUSE_MINIDUMP 1 + +#if !wxUSE_MINIDUMP + #include "wx/longlong.h" +#endif // wxUSE_MINIDUMP + #include "wx/datetime.h" + #include "wx/dynload.h" #include "wx/msw/crashrpt.h" @@ -55,7 +77,7 @@ // // in any case, the user may override by defining wxUSE_DBGHELP himself #ifndef wxUSE_DBGHELP - #ifdef DBHLPAPI + #ifdef DBHLPAPI #define wxUSE_DBGHELP 1 #else #define wxUSE_DBGHELP 0 @@ -68,6 +90,14 @@ // types of imagehlp.h functions // ---------------------------------------------------------------------------- +#if wxUSE_MINIDUMP + +typedef BOOL (WINAPI *MiniDumpWriteDump_t)(HANDLE, DWORD, HANDLE, + MINIDUMP_TYPE, + CONST PMINIDUMP_EXCEPTION_INFORMATION, + CONST PMINIDUMP_USER_STREAM_INFORMATION, + CONST PMINIDUMP_CALLBACK_INFORMATION); +#else // !wxUSE_MINIDUMP typedef DWORD (WINAPI *SymSetOptions_t)(DWORD); typedef BOOL (WINAPI *SymInitialize_t)(HANDLE, LPSTR, BOOL); typedef BOOL (WINAPI *StackWalk_t)(DWORD, HANDLE, HANDLE, LPSTACKFRAME, @@ -86,11 +116,14 @@ typedef BOOL (WINAPI *SymEnumSymbols_t)(HANDLE, ULONG64, PCSTR, PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID); typedef BOOL (WINAPI *SymGetTypeInfo_t)(HANDLE, DWORD64, ULONG, IMAGEHLP_SYMBOL_TYPE_INFO, PVOID); +#endif // wxUSE_MINIDUMP // ---------------------------------------------------------------------------- -// constant +// constants // ---------------------------------------------------------------------------- +#if !wxUSE_MINIDUMP + // Stolen from CVCONST.H in the DIA 2.0 SDK enum BasicType { @@ -131,12 +164,37 @@ enum SymbolTag SYMBOL_TAG_BASECLASS }; +#endif // wxUSE_MINIDUMP + #endif // wxUSE_DBGHELP // ---------------------------------------------------------------------------- // classes // ---------------------------------------------------------------------------- +// low level wxBusyCursor replacement: we use Win32 API directly here instead +// of going through wxWidgets calls as this could be dangerous +class BusyCursor +{ +public: + BusyCursor() + { + HCURSOR hcursorBusy = ::LoadCursor(NULL, IDC_WAIT); + m_hcursorOld = ::SetCursor(hcursorBusy); + } + + ~BusyCursor() + { + if ( m_hcursorOld ) + { + ::SetCursor(m_hcursorOld); + } + } + +private: + HCURSOR m_hcursorOld; +}; + // the real crash report generator class wxCrashReportImpl { @@ -154,6 +212,7 @@ public: } private: + // formatted output to m_hFile void Output(const wxChar *format, ...); @@ -161,6 +220,8 @@ private: void OutputEndl() { Output(_T("\r\n")); } #if wxUSE_DBGHELP + +#if !wxUSE_MINIDUMP // translate exception code to its symbolic name static wxString GetExceptionString(DWORD dwCode); @@ -197,9 +258,6 @@ private: // outputs information about the given symbol void OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf); - // load all the functions we need from dbghelp.dll, return true if all ok - bool ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp); - // map address to module (and also section:offset), retunr true if ok static bool GetLogicalAddress(PVOID addr, PTSTR szModule, @@ -228,11 +286,18 @@ private: // the current stack frame (may be NULL) STACKFRAME *m_sfCurrent; +#endif // !wxUSE_MINIDUMP + + // load all the functions we need from dbghelp.dll, return true if all ok + bool BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp); // dynamically loaded dbghelp.dll functions #define DECLARE_SYM_FUNCTION(func) static func ## _t func +#if wxUSE_MINIDUMP + DECLARE_SYM_FUNCTION(MiniDumpWriteDump); +#else // !wxUSE_MINIDUMP DECLARE_SYM_FUNCTION(SymSetOptions); DECLARE_SYM_FUNCTION(SymInitialize); DECLARE_SYM_FUNCTION(StackWalk); @@ -243,6 +308,9 @@ private: DECLARE_SYM_FUNCTION(SymSetContext); DECLARE_SYM_FUNCTION(SymEnumSymbols); DECLARE_SYM_FUNCTION(SymGetTypeInfo); +#endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP + + #undef DECLARE_SYM_FUNCTION #endif // wxUSE_DBGHELP // the handle of the report file @@ -271,6 +339,9 @@ static wxChar gs_reportFilename[MAX_PATH]; #define DEFINE_SYM_FUNCTION(func) func ## _t wxCrashReportImpl::func = 0 +#if wxUSE_MINIDUMP +DEFINE_SYM_FUNCTION(MiniDumpWriteDump); +#else // !wxUSE_MINIDUMP DEFINE_SYM_FUNCTION(SymSetOptions); DEFINE_SYM_FUNCTION(SymInitialize); DEFINE_SYM_FUNCTION(StackWalk); @@ -281,6 +352,7 @@ DEFINE_SYM_FUNCTION(SymGetLineFromAddr); DEFINE_SYM_FUNCTION(SymSetContext); DEFINE_SYM_FUNCTION(SymEnumSymbols); DEFINE_SYM_FUNCTION(SymGetTypeInfo); +#endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP #undef DEFINE_SYM_FUNCTION @@ -292,7 +364,7 @@ DEFINE_SYM_FUNCTION(SymGetTypeInfo); wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename) { -#if wxUSE_DBGHELP +#if wxUSE_DBGHELP && !wxUSE_MINIDUMP m_sfCurrent = NULL; #endif // wxUSE_DBGHELP @@ -323,6 +395,8 @@ void wxCrashReportImpl::Output(const wxChar *format, ...) #if wxUSE_DBGHELP +#if !wxUSE_MINIDUMP + bool wxCrashReportImpl::GetLogicalAddress(PVOID addr, PTSTR szModule, @@ -432,10 +506,10 @@ wxCrashReportImpl::FormatSimpleValue(BasicType bt, { static const size_t NUM_CHARS = 32; - const wxChar * const pc = *(PSTR *)pAddress; - if ( !::IsBadStringPtr(pc, NUM_CHARS) ) + const char * const pc = *(PSTR *)pAddress; + if ( !::IsBadStringPtrA(pc, NUM_CHARS) ) { - s << _T('"') << wxString(pc, NUM_CHARS) << _T('"'); + s << _T('"') << wxString(pc, wxConvLibc, NUM_CHARS) << _T('"'); handled = true; } @@ -494,6 +568,12 @@ wxCrashReportImpl::FormatField(DWORD64 modBase, { wxString s; + // avoid infinite recursion + if ( level > 10 ) + { + return s; + } + const HANDLE hProcess = GetCurrentProcess(); DWORD dwTag = 0; @@ -674,7 +754,9 @@ wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf) pVariable = (DWORD_PTR)pSym->Address; } - s << pSym->Name << _T(" = ") << FormatAnyValue(pSym, (PVOID)pVariable); + s << wxString(pSym->Name, wxConvLibc) + << _T(" = ") + << FormatAnyValue(pSym, (PVOID)pVariable); return s; } @@ -913,17 +995,22 @@ void wxCrashReportImpl::OutputGlobals(HANDLE hModule) #endif // _M_IX86 } -bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp) +#endif // wxUSE_MINIDUMP + +bool wxCrashReportImpl::BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp) { #define LOAD_SYM_FUNCTION(name) \ - name = (name ## _t) dllDbgHelp.GetSymbol(#name); \ + name = (name ## _t) dllDbgHelp.GetSymbol(_T(#name)); \ if ( !name ) \ { \ - Output(_T("\r\nFunction ") __XFILE__(#name) \ + Output(_T("\r\nFunction ") _T(#name) \ _T("() not found.\r\n")); \ return false; \ } +#if wxUSE_MINIDUMP + LOAD_SYM_FUNCTION(MiniDumpWriteDump); +#else // !wxUSE_MINIDUMP LOAD_SYM_FUNCTION(SymSetOptions); LOAD_SYM_FUNCTION(SymInitialize); LOAD_SYM_FUNCTION(StackWalk); @@ -934,12 +1021,15 @@ bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp) LOAD_SYM_FUNCTION(SymSetContext); LOAD_SYM_FUNCTION(SymEnumSymbols); LOAD_SYM_FUNCTION(SymGetTypeInfo); +#endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP #undef LOAD_SYM_FUNCTION return true; } +#if !wxUSE_MINIDUMP + /* static */ wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode) { @@ -995,9 +1085,17 @@ wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode) return s; } +#endif // !wxUSE_MINIDUMP + #endif // wxUSE_DBGHELP -bool wxCrashReportImpl::Generate(int flags) +bool wxCrashReportImpl::Generate( +#if wxUSE_DBGHELP + int flags +#else + int WXUNUSED(flags) +#endif +) { if ( m_hFile == INVALID_HANDLE_VALUE ) return false; @@ -1006,6 +1104,7 @@ bool wxCrashReportImpl::Generate(int flags) if ( !wxGlobalSEInformation ) return false; +#if !wxUSE_MINIDUMP PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord; PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord; @@ -1013,14 +1112,80 @@ bool wxCrashReportImpl::Generate(int flags) return false; HANDLE hModuleCrash = OutputBasicContext(pExceptionRecord, pCtx); +#endif // !wxUSE_MINIDUMP + + // show to the user that we're doing something... + BusyCursor busyCursor; + + // user-specified crash report flags override those specified by the + // programmer + TCHAR envFlags[64]; + DWORD dwLen = ::GetEnvironmentVariable + ( + _T("WX_CRASH_FLAGS"), + envFlags, + WXSIZEOF(envFlags) + ); + + int flagsEnv; + if ( dwLen && dwLen < WXSIZEOF(envFlags) && + wxSscanf(envFlags, _T("%d"), &flagsEnv) == 1 ) + { + flags = flagsEnv; + } // for everything else we need dbghelp.dll wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM); if ( dllDbgHelp.IsLoaded() ) { - if ( ResolveSymFunctions(dllDbgHelp) ) + if ( BindDbgHelpFunctions(dllDbgHelp) ) { - SymSetOptions(SYMOPT_DEFERRED_LOADS); +#if wxUSE_MINIDUMP + MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo; + + minidumpExcInfo.ThreadId = ::GetCurrentThreadId(); + minidumpExcInfo.ExceptionPointers = wxGlobalSEInformation; + minidumpExcInfo.ClientPointers = FALSE; // in our own address space + + // do generate the dump + MINIDUMP_TYPE dumpFlags; + if ( flags & wxCRASH_REPORT_LOCALS ) + { + // the only way to get local variables is to dump the entire + // process memory space -- but this makes for huge (dozens or + // even hundreds of Mb) files + dumpFlags = MiniDumpWithFullMemory; + } + else if ( flags & wxCRASH_REPORT_GLOBALS ) + { + // MiniDumpWriteDump() has the option for dumping just the data + // segment which contains all globals -- exactly what we need + dumpFlags = MiniDumpWithDataSegs; + } + else // minimal dump + { + dumpFlags = MiniDumpNormal; + } + + if ( !MiniDumpWriteDump + ( + ::GetCurrentProcess(), + ::GetCurrentProcessId(), + m_hFile, // file to write to + dumpFlags, // kind of dump to craete + &minidumpExcInfo, + NULL, // no extra user-defined data + NULL // no callbacks + ) ) + { + Output(_T("MiniDumpWriteDump() failed.")); + + return false; + } + + return true; +#else // !wxUSE_MINIDUMP + SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME); // Initialize DbgHelp if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) ) @@ -1034,28 +1199,31 @@ bool wxCrashReportImpl::Generate(int flags) return true; } +#endif // !wxUSE_MINIDUMP } else { - Output(_T("Please update your dbghelp.dll version, " - "at least version 5.1 is needed!\r\n")); + Output(_T("\r\nPlease update your dbghelp.dll version, ") + _T("at least version 5.1 is needed!\r\n") + _T("(if you already have a new version, please ") + _T("put it in the same directory where the program is.)\r\n")); } } - else + else // failed to load dbghelp.dll { Output(_T("Please install dbghelp.dll available free of charge ") _T("from Microsoft to get more detailed crash information!")); } - Output(_T("\r\nLatest dbghelp.dll is available at " - "http://www.microsoft.com/whdc/ddk/debugging/\r\n")); + Output(_T("\r\nLatest dbghelp.dll is available at ") + _T("http://www.microsoft.com/whdc/ddk/debugging/\r\n")); #else // !wxUSE_DBGHELP Output(_T("Support for crash report generation was not included ") - _T("in this wxWindows version.")); + _T("in this wxWidgets version.")); #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP - return true; + return false; } // ---------------------------------------------------------------------------- @@ -1110,7 +1278,11 @@ bool wxHandleFatalExceptions(bool doit) // use PID and date to make the report file name more unique wxString fname = wxString::Format ( +#if wxUSE_MINIDUMP + _T("%s_%s_%lu.dmp"), +#else // !wxUSE_MINIDUMP _T("%s_%s_%lu.rpt"), +#endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP wxTheApp ? wxTheApp->GetAppName().c_str() : _T("wxwindows"), wxDateTime::Now().Format(_T("%Y%m%d")).c_str(), @@ -1118,7 +1290,7 @@ bool wxHandleFatalExceptions(bool doit) ); wxStrncat(gs_reportFilename, fname, - WXSIZEOF(gs_reportFilename) - strlen(gs_reportFilename) - 1); + WXSIZEOF(gs_reportFilename) - wxStrlen(gs_reportFilename) - 1); } return true; @@ -1132,7 +1304,16 @@ extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs) wxGlobalSEInformation = pExcPtrs; // give the user a chance to do something special about this - wxTheApp->OnFatalException(); + __try + { + wxTheApp->OnFatalException(); + } + __except ( EXCEPTION_EXECUTE_HANDLER ) + { + // nothing to do here, just ignore the exception inside the + // exception handler + ; + } wxGlobalSEInformation = NULL;