Files
wxWidgets/src/msw/stackwalk.cpp
Suzumizaki-Kimitaka 28587c97d8 Add support for Unicode to wxStackWalker.
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.
2015-07-27 02:44:36 +02:00

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