Use wide-char versions of debug help functions if available, falling back to the narrow char ones otherwise. Also improve 64 bit support by using 64 bit versions of the functions if available as well. Closes #15138.
395 lines
12 KiB
C++
395 lines
12 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/stackwalk.cpp
|
|
// Purpose: wxStackWalker implementation for Win32
|
|
// Author: Vadim Zeitlin
|
|
// Modified by: Artur Bac 2010-10-01 AMD64 Port
|
|
// Created: 2005-01-08
|
|
// Copyright: (c) 2003-2005 Vadim Zeitlin <vadim@wxwindows.org>
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_STACKWALKER
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/string.h"
|
|
#endif
|
|
|
|
#include "wx/stackwalk.h"
|
|
|
|
#include "wx/msw/debughlp.h"
|
|
|
|
#if wxUSE_DBGHELP
|
|
|
|
// ============================================================================
|
|
// implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxStackFrame
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxStackFrame::OnGetName()
|
|
{
|
|
if ( m_hasName )
|
|
return;
|
|
|
|
m_hasName = true;
|
|
|
|
// get the name of the function for this stack frame entry
|
|
if ( !wxDbgHelpDLL::CallSymFromAddr
|
|
(
|
|
::GetCurrentProcess(),
|
|
GetSymAddr(),
|
|
&m_offset,
|
|
&m_name
|
|
) )
|
|
{
|
|
wxDbgHelpDLL::LogError(wxT("SymFromAddr"));
|
|
}
|
|
}
|
|
|
|
void wxStackFrame::OnGetLocation()
|
|
{
|
|
if ( m_hasLocation )
|
|
return;
|
|
|
|
m_hasLocation = true;
|
|
|
|
// get the source line for this stack frame entry ignoring possible errors
|
|
// (it's normal that we don't have source info for some symbols, e.g. all
|
|
// those from the system DLLs)
|
|
wxDbgHelpDLL::CallSymGetLineFromAddr(::GetCurrentProcess(), GetSymAddr(),
|
|
&m_filename, &m_line);
|
|
}
|
|
|
|
bool
|
|
wxStackFrame::GetParam(size_t n,
|
|
wxString *type,
|
|
wxString *name,
|
|
wxString *value) const
|
|
{
|
|
if ( !DoGetParamCount() )
|
|
ConstCast()->OnGetParam();
|
|
|
|
if ( n >= DoGetParamCount() )
|
|
return false;
|
|
|
|
if ( type )
|
|
*type = m_paramTypes[n];
|
|
if ( name )
|
|
*name = m_paramNames[n];
|
|
if ( value )
|
|
*value = m_paramValues[n];
|
|
|
|
return true;
|
|
}
|
|
|
|
void wxStackFrame::OnParam(wxSYMBOL_INFO *pSymInfo)
|
|
{
|
|
m_paramTypes.Add(wxEmptyString);
|
|
m_paramNames.Add(pSymInfo->Name);
|
|
|
|
// 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
|
|
// be left in an inconsistent state, so deal with it explicitly here (even
|
|
// if normally we should never crash, of course...)
|
|
#ifdef _CPPUNWIND
|
|
try
|
|
#else
|
|
__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));
|
|
}
|
|
#ifdef _CPPUNWIND
|
|
catch ( ... )
|
|
#else
|
|
__except ( EXCEPTION_EXECUTE_HANDLER )
|
|
#endif
|
|
{
|
|
m_paramValues.Add(wxEmptyString);
|
|
}
|
|
}
|
|
|
|
BOOL CALLBACK
|
|
EnumSymbolsProc(wxPSYMBOL_INFO pSymInfo, ULONG WXUNUSED(SymSize), PVOID data)
|
|
{
|
|
wxStackFrame *frame = static_cast<wxStackFrame *>(data);
|
|
|
|
// we're only interested in parameters
|
|
if ( pSymInfo->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER )
|
|
{
|
|
frame->OnParam(pSymInfo);
|
|
}
|
|
|
|
// return true to continue enumeration, false would have stopped it
|
|
return TRUE;
|
|
}
|
|
|
|
void wxStackFrame::OnGetParam()
|
|
{
|
|
// use SymSetContext to get just the locals/params for this frame
|
|
IMAGEHLP_STACK_FRAME imagehlpStackFrame;
|
|
wxZeroMemory(imagehlpStackFrame);
|
|
imagehlpStackFrame.InstructionOffset = GetSymAddr();
|
|
if ( !wxDbgHelpDLL::SymSetContext
|
|
(
|
|
::GetCurrentProcess(),
|
|
&imagehlpStackFrame,
|
|
0 // unused
|
|
) )
|
|
{
|
|
// for symbols from kernel DLL we might not have access to their
|
|
// address, this is not a real error
|
|
if ( ::GetLastError() != ERROR_INVALID_ADDRESS )
|
|
{
|
|
wxDbgHelpDLL::LogError(wxT("SymSetContext"));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if ( !wxDbgHelpDLL::CallSymEnumSymbols
|
|
(
|
|
::GetCurrentProcess(),
|
|
NULL, // DLL base: use current context
|
|
EnumSymbolsProc, // callback
|
|
this // data to pass to it
|
|
) )
|
|
{
|
|
wxDbgHelpDLL::LogError(wxT("SymEnumSymbols"));
|
|
}
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxStackWalker
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxStackWalker::WalkFrom(const CONTEXT *pCtx, size_t skip, size_t maxDepth)
|
|
{
|
|
if ( !wxDbgHelpDLL::Init() )
|
|
{
|
|
// don't log a user-visible error message here because the stack trace
|
|
// is only needed for debugging/diagnostics anyhow and we shouldn't
|
|
// confuse the user by complaining that we couldn't generate it
|
|
wxLogDebug(wxT("Failed to get stack backtrace: %s"),
|
|
wxDbgHelpDLL::GetErrorMessage().c_str());
|
|
return;
|
|
}
|
|
|
|
// according to MSDN, the first parameter should be just a unique value and
|
|
// not process handle (although the parameter is prototyped as "HANDLE
|
|
// hProcess") and actually it advises to use the process id and not handle
|
|
// for Win9x, but then we need to use the same value in StackWalk() call
|
|
// below which should be a real handle... so this is what we use
|
|
const HANDLE hProcess = ::GetCurrentProcess();
|
|
|
|
if ( !wxDbgHelpDLL::CallSymInitialize
|
|
(
|
|
hProcess,
|
|
TRUE // load symbols for all loaded modules
|
|
) )
|
|
{
|
|
wxDbgHelpDLL::LogError(wxT("SymInitialize"));
|
|
|
|
return;
|
|
}
|
|
|
|
CONTEXT ctx = *pCtx; // will be modified by StackWalk()
|
|
|
|
DWORD dwMachineType;
|
|
|
|
// initialize the initial frame: currently we can do it for x86 only
|
|
STACKFRAME sf;
|
|
wxZeroMemory(sf);
|
|
|
|
#if defined(_M_AMD64)
|
|
sf.AddrPC.Offset = ctx.Rip;
|
|
sf.AddrPC.Mode = AddrModeFlat;
|
|
sf.AddrStack.Offset = ctx.Rsp;
|
|
sf.AddrStack.Mode = AddrModeFlat;
|
|
sf.AddrFrame.Offset = ctx.Rbp;
|
|
sf.AddrFrame.Mode = AddrModeFlat;
|
|
|
|
dwMachineType = IMAGE_FILE_MACHINE_AMD64;
|
|
#elif defined(_M_IX86)
|
|
sf.AddrPC.Offset = ctx.Eip;
|
|
sf.AddrPC.Mode = AddrModeFlat;
|
|
sf.AddrStack.Offset = ctx.Esp;
|
|
sf.AddrStack.Mode = AddrModeFlat;
|
|
sf.AddrFrame.Offset = ctx.Ebp;
|
|
sf.AddrFrame.Mode = AddrModeFlat;
|
|
|
|
dwMachineType = IMAGE_FILE_MACHINE_I386;
|
|
#else
|
|
#error "Need to initialize STACKFRAME on non x86"
|
|
#endif // _M_IX86
|
|
|
|
// iterate over all stack frames
|
|
for ( size_t nLevel = 0; nLevel < maxDepth; nLevel++ )
|
|
{
|
|
// get the next stack frame
|
|
if ( !wxDbgHelpDLL::StackWalk
|
|
(
|
|
dwMachineType,
|
|
hProcess,
|
|
::GetCurrentThread(),
|
|
&sf,
|
|
&ctx,
|
|
NULL, // read memory function (default)
|
|
wxDbgHelpDLL::SymFunctionTableAccess,
|
|
wxDbgHelpDLL::SymGetModuleBase,
|
|
NULL // address translator for 16 bit
|
|
) )
|
|
{
|
|
if ( ::GetLastError() )
|
|
wxDbgHelpDLL::LogError(wxT("StackWalk"));
|
|
|
|
break;
|
|
}
|
|
|
|
// don't show this frame itself in the output
|
|
if ( nLevel >= skip )
|
|
{
|
|
wxStackFrame frame(nLevel - skip,
|
|
wxUIntToPtr(sf.AddrPC.Offset),
|
|
sf.AddrFrame.Offset);
|
|
|
|
OnStackFrame(frame);
|
|
}
|
|
}
|
|
|
|
if ( !wxDbgHelpDLL::SymCleanup(hProcess) )
|
|
{
|
|
wxDbgHelpDLL::LogError(wxT("SymCleanup"));
|
|
}
|
|
}
|
|
|
|
void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS *ep, size_t skip, size_t maxDepth)
|
|
{
|
|
WalkFrom(ep->ContextRecord, skip, maxDepth);
|
|
}
|
|
|
|
#if wxUSE_ON_FATAL_EXCEPTION
|
|
|
|
void wxStackWalker::WalkFromException(size_t maxDepth)
|
|
{
|
|
extern EXCEPTION_POINTERS *wxGlobalSEInformation;
|
|
|
|
wxCHECK_RET( wxGlobalSEInformation,
|
|
wxT("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") );
|
|
|
|
// don't skip any frames, the first one is where we crashed
|
|
WalkFrom(wxGlobalSEInformation, 0, maxDepth);
|
|
}
|
|
|
|
#endif // wxUSE_ON_FATAL_EXCEPTION
|
|
|
|
void wxStackWalker::Walk(size_t skip, size_t WXUNUSED(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
|
|
}
|
|
}
|
|
|
|
#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
|
|
|