X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/9ed0d735d588f42485461ea526596436c4c6ad4b..57c26f8210c40317d70d616a05198e042ee9175b:/src/msw/crashrpt.cpp diff --git a/src/msw/crashrpt.cpp b/src/msw/crashrpt.cpp index f49d2c0d48..9ba2dbbb06 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,9 +36,29 @@ #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/dynlib.h" #include "wx/msw/crashrpt.h" @@ -44,10 +66,38 @@ #include #include "wx/msw/private.h" +// we need to determine whether we have the declarations for the function in +// debughlp.dll version 5.81 (at least) and we check for DBHLPAPI to test this +// +// reasons: +// - VC6 version of imagehlp.h doesn't define it +// - VC7 one does +// - testing for compiler version doesn't work as you can install and use +// the new SDK headers with VC6 +// +// in any case, the user may override by defining wxUSE_DBGHELP himself +#ifndef wxUSE_DBGHELP + #ifdef DBHLPAPI + #define wxUSE_DBGHELP 1 + #else + #define wxUSE_DBGHELP 0 + #endif +#endif + +#if wxUSE_DBGHELP + // ---------------------------------------------------------------------------- // 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, @@ -66,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 { @@ -111,10 +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 { @@ -132,12 +212,16 @@ public: } private: + // formatted output to m_hFile void Output(const wxChar *format, ...); // output end of line void OutputEndl() { Output(_T("\r\n")); } +#if wxUSE_DBGHELP + +#if !wxUSE_MINIDUMP // translate exception code to its symbolic name static wxString GetExceptionString(DWORD dwCode); @@ -174,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, @@ -203,16 +284,20 @@ private: void OutputGlobals(HANDLE hModuleCrash); - // the handle of the report file - HANDLE m_hFile; - // 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); @@ -223,6 +308,13 @@ 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 + HANDLE m_hFile; }; // ---------------------------------------------------------------------------- @@ -243,8 +335,13 @@ static wxChar gs_reportFilename[MAX_PATH]; // implementation // ============================================================================ +#if wxUSE_DBGHELP + #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); @@ -255,16 +352,21 @@ 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 +#endif // wxUSE_DBGHELP + // ---------------------------------------------------------------------------- // wxCrashReportImpl // ---------------------------------------------------------------------------- wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename) { +#if wxUSE_DBGHELP && !wxUSE_MINIDUMP m_sfCurrent = NULL; +#endif // wxUSE_DBGHELP m_hFile = ::CreateFile ( @@ -291,6 +393,10 @@ void wxCrashReportImpl::Output(const wxChar *format, ...) va_end(argptr); } +#if wxUSE_DBGHELP + +#if !wxUSE_MINIDUMP + bool wxCrashReportImpl::GetLogicalAddress(PVOID addr, PTSTR szModule, @@ -400,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; } @@ -462,6 +568,12 @@ wxCrashReportImpl::FormatField(DWORD64 modBase, { wxString s; + // avoid infinite recursion + if ( level > 10 ) + { + return s; + } + const HANDLE hProcess = GetCurrentProcess(); DWORD dwTag = 0; @@ -642,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; } @@ -881,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); @@ -902,66 +1021,14 @@ 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; } -bool wxCrashReportImpl::Generate(int flags) -{ - if ( m_hFile == INVALID_HANDLE_VALUE ) - return false; - - if ( !wxGlobalSEInformation ) - return false; - - PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord; - PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord; - - if ( !pExceptionRecord || !pCtx ) - return false; - - HANDLE hModuleCrash = OutputBasicContext(pExceptionRecord, pCtx); - - // for everything else we need dbghelp.dll - wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM); - if ( dllDbgHelp.IsLoaded() ) - { - if ( ResolveSymFunctions(dllDbgHelp) ) - { - SymSetOptions(SYMOPT_DEFERRED_LOADS); - - // Initialize DbgHelp - if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) ) - { - OutputStack(pCtx, flags); - - if ( hModuleCrash && (flags & wxCRASH_REPORT_GLOBALS) ) - { - OutputGlobals(hModuleCrash); - } - - return true; - } - } - else - { - Output(_T("Please update your dbghelp.dll version, " - "at least version 5.1 is needed!\r\n")); - } - } - else - { - 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")); - - return true; -} +#if !wxUSE_MINIDUMP /* static */ wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode) @@ -1018,6 +1085,147 @@ wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode) return s; } +#endif // !wxUSE_MINIDUMP + +#endif // wxUSE_DBGHELP + +bool wxCrashReportImpl::Generate( +#if wxUSE_DBGHELP + int flags +#else + int WXUNUSED(flags) +#endif +) +{ + if ( m_hFile == INVALID_HANDLE_VALUE ) + return false; + +#if wxUSE_DBGHELP + if ( !wxGlobalSEInformation ) + return false; + +#if !wxUSE_MINIDUMP + PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord; + PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord; + + if ( !pExceptionRecord || !pCtx ) + 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 ( BindDbgHelpFunctions(dllDbgHelp) ) + { +#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 */) ) + { + OutputStack(pCtx, flags); + + if ( hModuleCrash && (flags & wxCRASH_REPORT_GLOBALS) ) + { + OutputGlobals(hModuleCrash); + } + + return true; + } +#endif // !wxUSE_MINIDUMP + } + else + { + 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 // 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 ") + _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 wxWidgets version.")); +#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP + + return false; +} + // ---------------------------------------------------------------------------- // wxCrashReport // ---------------------------------------------------------------------------- @@ -1070,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(), @@ -1078,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; @@ -1092,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;