wxWidgets/Win32:

* Fixed a bug that caused all but the first stackframe walk to fail with an error.
 * Improved thread safety of stack walking.

DevNotes: When walking stackframes multiple times from the same process, only the first stackframe walk would work and all others would fail with an error because of calling SymInitialize multiple times.  So now SymInitialize is only called once and SymRefreshModuleList is used prior to walking, if available.  (only available in winDbgHlp.dll v6.5 or later)

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3546 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-07-21 16:57:58 +00:00
parent 915983cc71
commit 9c22dc4f73
3 changed files with 89 additions and 25 deletions

View File

@ -140,6 +140,7 @@ public:
typedef DWORD (WINAPI *SymGetOptions_t)();
typedef DWORD (WINAPI *SymSetOptions_t)(DWORD);
typedef BOOL (WINAPI *SymInitialize_t)(HANDLE, LPSTR, BOOL);
typedef BOOL (WINAPI *SymRefreshModuleList_t)(HANDLE);
typedef BOOL (WINAPI *StackWalk_t)(DWORD, HANDLE, HANDLE, LPSTACKFRAME,
LPVOID, PREAD_PROCESS_MEMORY_ROUTINE,
PFUNCTION_TABLE_ACCESS_ROUTINE,
@ -184,11 +185,13 @@ public:
wxDO_FOR_ALL_SYM_FUNCS(wxDECLARE_SYM_FUNCTION);
#undef wxDECLARE_SYM_FUNCTION
// load all functions from DLL, return true if ok
static bool Init();
// *PCSX2* Should be called before performing a stack trace, as apps may load/unload plugins during
// program execution.
static bool RefreshModuleList(HANDLE hProcess);
// return the string with the error message explaining why Init() failed
static const wxString& GetErrorMessage();
@ -202,6 +205,14 @@ public:
static wxString GetSymbolName(PSYMBOL_INFO pSymInfo);
private:
// Declared separately since it requires an especially new version of WinDbgHlp
// (v6.5 or later).
wxDECLARE_SYM_FUNCTION(SymRefreshModuleList);
static bool DoInit();
static bool BindFunctions(const wxDynamicLibrary& dllDbgHelp);
// dereference the given symbol, i.e. return symbol which is not a
// pointer/reference any more
//
@ -222,6 +233,8 @@ private:
unsigned level = 0);
};
#undef wxDECLARE_SYM_FUNCTION
#endif // wxUSE_DBGHELP
#endif // _WX_MSW_DEBUGHLPH_H_

View File

@ -42,6 +42,10 @@ static const unsigned MAX_DUMP_DEPTH = 20;
// error message from Init()
static wxString gs_errMsg;
#if wxUSE_THREADS
wxMutex s_mtx_DbgHelp;
#endif
// ============================================================================
// wxDbgHelpDLL implementation
// ============================================================================
@ -53,6 +57,7 @@ static wxString gs_errMsg;
#define DEFINE_SYM_FUNCTION(func) wxDbgHelpDLL::func ## _t wxDbgHelpDLL::func = 0
wxDO_FOR_ALL_SYM_FUNCS(DEFINE_SYM_FUNCTION);
DEFINE_SYM_FUNCTION(SymRefreshModuleList);
#undef DEFINE_SYM_FUNCTION
@ -62,12 +67,11 @@ wxDO_FOR_ALL_SYM_FUNCS(DEFINE_SYM_FUNCTION);
// load all function we need from the DLL
static bool BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp)
bool wxDbgHelpDLL::BindFunctions(const wxDynamicLibrary& dllDbgHelp)
{
#define LOAD_SYM_FUNCTION(name) \
wxDbgHelpDLL::name = (wxDbgHelpDLL::name ## _t) \
dllDbgHelp.GetSymbol(_T(#name)); \
if ( !wxDbgHelpDLL::name ) \
name = (name ## _t)dllDbgHelp.GetSymbol(_T(#name)); \
if ( !name ) \
{ \
gs_errMsg += _T("Function ") _T(#name) _T("() not found.\n"); \
return false; \
@ -75,25 +79,31 @@ static bool BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp)
wxDO_FOR_ALL_SYM_FUNCS(LOAD_SYM_FUNCTION);
#undef LOAD_SYM_FUNCTION
#undef LOAD_SYM_FUNCTION
// SymRefreshModuleList is bound separately since it requires an especially new version
// of WinDbgHlp (v6.5 or later). If it binds as NULL, that's ok. Its only needed in
// order to reload symbols for apps that dynamically unload/reload plugins.
SymRefreshModuleList = (SymRefreshModuleList_t)dllDbgHelp.GetSymbol(_T("SymRefreshModuleList"));
return true;
}
// called by Init() if we hadn't done this before
static bool DoInit()
bool wxDbgHelpDLL::DoInit()
{
wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM);
if ( dllDbgHelp.IsLoaded() )
{
if ( BindDbgHelpFunctions(dllDbgHelp) )
if ( BindFunctions(dllDbgHelp) )
{
// turn on default options
DWORD options = wxDbgHelpDLL::SymGetOptions();
DWORD options = SymGetOptions();
options |= SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_DEBUG;
wxDbgHelpDLL::SymSetOptions(options);
SymSetOptions(options);
dllDbgHelp.Detach();
return true;
@ -119,9 +129,13 @@ static bool DoInit()
/* static */
bool wxDbgHelpDLL::Init()
{
// this flag is -1 until Init() is called for the first time, then it's set
// to either false or true depending on whether we could load the functions
static int s_loaded = -1;
// this flag is -1 until Init() is called for the first time, then it's set
// to either false or true depending on whether we could load the functions
static int s_loaded = -1;
#if wxUSE_THREADS
wxMutexLocker lock(s_mtx_DbgHelp);
#endif
if ( s_loaded == -1 )
{
@ -131,6 +145,42 @@ bool wxDbgHelpDLL::Init()
return s_loaded != 0;
}
bool wxDbgHelpDLL::RefreshModuleList( HANDLE hProcess )
{
static bool s_syms_initialized = false;
#if wxUSE_THREADS
wxMutexLocker lock(s_mtx_DbgHelp);
#endif
if ( !s_syms_initialized )
{
if ( !SymInitialize(
hProcess,
NULL, // use default symbol search path
TRUE // load symbols for all loaded modules
) )
{
wxDbgHelpDLL::LogError(_T("SymInitialize"));
return false;
}
s_syms_initialized = true;
}
else if ( SymRefreshModuleList && !SymRefreshModuleList( hProcess ) )
{
// If winDbgHlp v6.5 or newer, we can use this to reload symbols on-the-fly.
// If not, then the app could have outdated or unloaded symbols if it dynamically
// loads and unloads modules *and* performs multiple stack traces during the course
// of program execution.
wxDbgHelpDLL::LogError(_T("SymRefreshModuleList"));
return false;
}
return true;
}
// ----------------------------------------------------------------------------
// error handling
// ----------------------------------------------------------------------------

View File

@ -209,6 +209,9 @@ void wxStackFrame::OnGetParam()
}
}
#if wxUSE_THREADS
static wxMutex s_mtx_StackWalker;
#endif
// ----------------------------------------------------------------------------
// wxStackWalker
@ -216,6 +219,14 @@ void wxStackFrame::OnGetParam()
void wxStackWalker::WalkFrom(const CONTEXT *pCtx, size_t skip)
{
#if wxUSE_THREADS
// wxDbgHelpDLL has its own mutex locks for some critical functions, but really it
// should have locks on ALL its functions. So until it gets fixed, we'll use a
// mutex here for stackframe walking. --air
wxMutexLocker lock(s_mtx_StackWalker);
#endif
if ( !wxDbgHelpDLL::Init() )
{
// don't log a user-visible error message here because the stack trace
@ -233,17 +244,7 @@ void wxStackWalker::WalkFrom(const CONTEXT *pCtx, size_t skip)
// below which should be a real handle... so this is what we use
const HANDLE hProcess = ::GetCurrentProcess();
if ( !wxDbgHelpDLL::SymInitialize
(
hProcess,
NULL, // use default symbol search path
TRUE // load symbols for all loaded modules
) )
{
wxDbgHelpDLL::LogError(_T("SymInitialize"));
return;
}
if ( !wxDbgHelpDLL::RefreshModuleList(hProcess) ) return;
CONTEXT ctx = *pCtx; // will be modified by StackWalk()