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