1/////////////////////////////////////////////////////////////////////////////
2// Name:        msw/stackwalk.cpp
3// Purpose:     wxStackWalker implementation for Win32
4// Author:      Vadim Zeitlin
5// Modified by:
6// Created:     2005-01-08
7// RCS-ID:      $Id: stackwalk.cpp 61341 2009-07-07 09:35:56Z VZ $
8// Copyright:   (c) 2003-2005 Vadim Zeitlin <vadim@wxwindows.org>
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23    #pragma hdrstop
24#endif
25
26#if wxUSE_STACKWALKER
27
28#ifndef WX_PRECOMP
29    #include "wx/string.h"
30#endif
31
32#include "wx/stackwalk.h"
33
34#include "wx/msw/debughlp.h"
35
36#if wxUSE_DBGHELP
37
38// ============================================================================
39// implementation
40// ============================================================================
41
42// ----------------------------------------------------------------------------
43// wxStackFrame
44// ----------------------------------------------------------------------------
45
46void wxStackFrame::OnGetName()
47{
48    if ( m_hasName )
49        return;
50
51    m_hasName = true;
52
53    // get the name of the function for this stack frame entry
54    static const size_t MAX_NAME_LEN = 1024;
55    BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_NAME_LEN];
56    wxZeroMemory(symbolBuffer);
57
58    PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
59    pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
60    pSymbol->MaxNameLen = MAX_NAME_LEN;
61
62    DWORD64 symDisplacement = 0;
63    if ( !wxDbgHelpDLL::SymFromAddr
64                        (
65                            ::GetCurrentProcess(),
66                            GetSymAddr(),
67                            &symDisplacement,
68                            pSymbol
69                        ) )
70    {
71        wxDbgHelpDLL::LogError(_T("SymFromAddr"));
72        return;
73    }
74
75    m_name = wxString::FromAscii(pSymbol->Name);
76    m_offset = symDisplacement;
77}
78
79void wxStackFrame::OnGetLocation()
80{
81    if ( m_hasLocation )
82        return;
83
84    m_hasLocation = true;
85
86    // get the source line for this stack frame entry
87    IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
88    DWORD dwLineDisplacement;
89    if ( !wxDbgHelpDLL::SymGetLineFromAddr
90                        (
91                            ::GetCurrentProcess(),
92                            GetSymAddr(),
93                            &dwLineDisplacement,
94                            &lineInfo
95                        ) )
96    {
97        // it is normal that we don't have source info for some symbols,
98        // notably all the ones from the system DLLs...
99        //wxDbgHelpDLL::LogError(_T("SymGetLineFromAddr"));
100        return;
101    }
102
103    m_filename = wxString::FromAscii(lineInfo.FileName);
104    m_line = lineInfo.LineNumber;
105}
106
107bool
108wxStackFrame::GetParam(size_t n,
109                       wxString *type,
110                       wxString *name,
111                       wxString *value) const
112{
113    if ( !DoGetParamCount() )
114        ConstCast()->OnGetParam();
115
116    if ( n >= DoGetParamCount() )
117        return false;
118
119    if ( type )
120        *type = m_paramTypes[n];
121    if ( name )
122        *name = m_paramNames[n];
123    if ( value )
124        *value = m_paramValues[n];
125
126    return true;
127}
128
129void wxStackFrame::OnParam(PSYMBOL_INFO pSymInfo)
130{
131    m_paramTypes.Add(wxEmptyString);
132
133    m_paramNames.Add(wxString::FromAscii(pSymInfo->Name));
134
135    // if symbol information is corrupted and we crash, the exception is going
136    // to be ignored when we're called from WalkFromException() because of the
137    // exception handler there returning EXCEPTION_CONTINUE_EXECUTION, but we'd
138    // be left in an inconsistent state, so deal with it explicitly here (even
139    // if normally we should never crash, of course...)
140#ifdef _CPPUNWIND
141    try
142#else
143    __try
144#endif
145    {
146        // as it is a parameter (and not a global var), it is always offset by
147        // the frame address
148        DWORD_PTR pValue = m_addrFrame + pSymInfo->Address;
149        m_paramValues.Add(wxDbgHelpDLL::DumpSymbol(pSymInfo, (void *)pValue));
150    }
151#ifdef _CPPUNWIND
152    catch ( ... )
153#else
154    __except ( EXCEPTION_EXECUTE_HANDLER )
155#endif
156    {
157        m_paramValues.Add(wxEmptyString);
158    }
159}
160
161BOOL CALLBACK
162EnumSymbolsProc(PSYMBOL_INFO pSymInfo, ULONG WXUNUSED(SymSize), PVOID data)
163{
164    wxStackFrame *frame = wx_static_cast(wxStackFrame *, data);
165
166    // we're only interested in parameters
167    if ( pSymInfo->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER )
168    {
169        frame->OnParam(pSymInfo);
170    }
171
172    // return true to continue enumeration, false would have stopped it
173    return TRUE;
174}
175
176void wxStackFrame::OnGetParam()
177{
178    // use SymSetContext to get just the locals/params for this frame
179    IMAGEHLP_STACK_FRAME imagehlpStackFrame;
180    wxZeroMemory(imagehlpStackFrame);
181    imagehlpStackFrame.InstructionOffset = GetSymAddr();
182    if ( !wxDbgHelpDLL::SymSetContext
183                        (
184                            ::GetCurrentProcess(),
185                            &imagehlpStackFrame,
186                            0           // unused
187                        ) )
188    {
189        // for symbols from kernel DLL we might not have access to their
190        // address, this is not a real error
191        if ( ::GetLastError() != ERROR_INVALID_ADDRESS )
192        {
193            wxDbgHelpDLL::LogError(_T("SymSetContext"));
194        }
195
196        return;
197    }
198
199    if ( !wxDbgHelpDLL::SymEnumSymbols
200                        (
201                            ::GetCurrentProcess(),
202                            NULL,               // DLL base: use current context
203                            NULL,               // no mask, get all symbols
204                            EnumSymbolsProc,    // callback
205                            this                // data to pass to it
206                        ) )
207    {
208        wxDbgHelpDLL::LogError(_T("SymEnumSymbols"));
209    }
210}
211
212
213// ----------------------------------------------------------------------------
214// wxStackWalker
215// ----------------------------------------------------------------------------
216
217void wxStackWalker::WalkFrom(const CONTEXT *pCtx, size_t skip)
218{
219    if ( !wxDbgHelpDLL::Init() )
220    {
221        // don't log a user-visible error message here because the stack trace
222        // is only needed for debugging/diagnostics anyhow and we shouldn't
223        // confuse the user by complaining that we couldn't generate it
224        wxLogDebug(_T("Failed to get stack backtrace: %s"),
225                   wxDbgHelpDLL::GetErrorMessage().c_str());
226        return;
227    }
228
229    // according to MSDN, the first parameter should be just a unique value and
230    // not process handle (although the parameter is prototyped as "HANDLE
231    // hProcess") and actually it advises to use the process id and not handle
232    // for Win9x, but then we need to use the same value in StackWalk() call
233    // below which should be a real handle... so this is what we use
234    const HANDLE hProcess = ::GetCurrentProcess();
235
236    if ( !wxDbgHelpDLL::SymInitialize
237                        (
238                            hProcess,
239                            NULL,   // use default symbol search path
240                            TRUE    // load symbols for all loaded modules
241                        ) )
242    {
243        wxDbgHelpDLL::LogError(_T("SymInitialize"));
244
245        return;
246    }
247
248    CONTEXT ctx = *pCtx; // will be modified by StackWalk()
249
250    DWORD dwMachineType;
251
252    // initialize the initial frame: currently we can do it for x86 only
253    STACKFRAME sf;
254    wxZeroMemory(sf);
255
256#ifdef _M_IX86
257    sf.AddrPC.Offset       = ctx.Eip;
258    sf.AddrPC.Mode         = AddrModeFlat;
259    sf.AddrStack.Offset    = ctx.Esp;
260    sf.AddrStack.Mode      = AddrModeFlat;
261    sf.AddrFrame.Offset    = ctx.Ebp;
262    sf.AddrFrame.Mode      = AddrModeFlat;
263
264    dwMachineType = IMAGE_FILE_MACHINE_I386;
265#else
266    #error "Need to initialize STACKFRAME on non x86"
267#endif // _M_IX86
268
269    // iterate over all stack frames (but stop after 200 to avoid entering
270    // infinite loop if the stack is corrupted)
271    for ( size_t nLevel = 0; nLevel < 200; nLevel++ )
272    {
273        // get the next stack frame
274        if ( !wxDbgHelpDLL::StackWalk
275                            (
276                                dwMachineType,
277                                hProcess,
278                                ::GetCurrentThread(),
279                                &sf,
280                                &ctx,
281                                NULL,       // read memory function (default)
282                                wxDbgHelpDLL::SymFunctionTableAccess,
283                                wxDbgHelpDLL::SymGetModuleBase,
284                                NULL        // address translator for 16 bit
285                            ) )
286        {
287            if ( ::GetLastError() )
288                wxDbgHelpDLL::LogError(_T("StackWalk"));
289
290            break;
291        }
292
293        // don't show this frame itself in the output
294        if ( nLevel >= skip )
295        {
296            wxStackFrame frame(nLevel - skip,
297                               (void *)sf.AddrPC.Offset,
298                               sf.AddrFrame.Offset);
299
300            OnStackFrame(frame);
301        }
302    }
303
304    // this results in crashes inside ntdll.dll when called from
305    // exception handler ...
306#if 0
307    if ( !wxDbgHelpDLL::SymCleanup(hProcess) )
308    {
309        wxDbgHelpDLL::LogError(_T("SymCleanup"));
310    }
311#endif
312}
313
314void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS *ep, size_t skip)
315{
316    WalkFrom(ep->ContextRecord, skip);
317}
318
319void wxStackWalker::WalkFromException()
320{
321    // wxGlobalSEInformation is unavailable if wxUSE_ON_FATAL_EXCEPTION==0
322#if wxUSE_ON_FATAL_EXCEPTION
323    extern EXCEPTION_POINTERS *wxGlobalSEInformation;
324
325    wxCHECK_RET( wxGlobalSEInformation,
326                 _T("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") );
327
328    // don't skip any frames, the first one is where we crashed
329    WalkFrom(wxGlobalSEInformation, 0);
330#endif // wxUSE_ON_FATAL_EXCEPTION
331}
332
333void wxStackWalker::Walk(size_t skip, size_t WXUNUSED(maxDepth))
334{
335    // to get a CONTEXT for the current location, simply force an exception and
336    // get EXCEPTION_POINTERS from it
337    //
338    // note:
339    //  1. we additionally skip RaiseException() and WalkFromException() frames
340    //  2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it
341    //     shouldn't have been according to the docs
342    __try
343    {
344        RaiseException(0x1976, 0, 0, NULL);
345    }
346    __except( WalkFrom((EXCEPTION_POINTERS *)GetExceptionInformation(),
347                       skip + 2), EXCEPTION_CONTINUE_EXECUTION )
348    {
349        // never executed because of WalkFromException() return value
350    }
351}
352
353#else // !wxUSE_DBGHELP
354
355// ============================================================================
356// stubs
357// ============================================================================
358
359// ----------------------------------------------------------------------------
360// wxStackFrame
361// ----------------------------------------------------------------------------
362
363void wxStackFrame::OnGetName()
364{
365}
366
367void wxStackFrame::OnGetLocation()
368{
369}
370
371bool
372wxStackFrame::GetParam(size_t WXUNUSED(n),
373                       wxString * WXUNUSED(type),
374                       wxString * WXUNUSED(name),
375                       wxString * WXUNUSED(value)) const
376{
377    return false;
378}
379
380void wxStackFrame::OnParam(_SYMBOL_INFO * WXUNUSED(pSymInfo))
381{
382}
383
384void wxStackFrame::OnGetParam()
385{
386}
387
388// ----------------------------------------------------------------------------
389// wxStackWalker
390// ----------------------------------------------------------------------------
391
392void
393wxStackWalker::WalkFrom(const CONTEXT * WXUNUSED(pCtx), size_t WXUNUSED(skip))
394{
395}
396
397void
398wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS * WXUNUSED(ep),
399                        size_t WXUNUSED(skip))
400{
401}
402
403void wxStackWalker::WalkFromException()
404{
405}
406
407void wxStackWalker::Walk(size_t WXUNUSED(skip), size_t WXUNUSED(maxDepth))
408{
409}
410
411#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
412
413#endif // wxUSE_STACKWALKER
414
415