]> git.saurik.com Git - wxWidgets.git/blame - src/msw/stackwalk.cpp
simplify code so it always returns the same object
[wxWidgets.git] / src / msw / stackwalk.cpp
CommitLineData
7beafee9 1/////////////////////////////////////////////////////////////////////////////
80fdcdb9 2// Name: src/msw/stackwalk.cpp
7beafee9
VZ
3// Purpose: wxStackWalker implementation for Win32
4// Author: Vadim Zeitlin
603c73db 5// Modified by: Artur Bac 2010-10-01 AMD64 Port
7beafee9
VZ
6// Created: 2005-01-08
7// RCS-ID: $Id$
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
60431236
WS
28#ifndef WX_PRECOMP
29 #include "wx/string.h"
30#endif
31
7beafee9
VZ
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;
d4762753 59 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
7beafee9
VZ
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 {
9a83f860 71 wxDbgHelpDLL::LogError(wxT("SymFromAddr"));
7beafee9
VZ
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...
9a83f860 99 //wxDbgHelpDLL::LogError(wxT("SymGetLineFromAddr"));
7beafee9
VZ
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{
f31a4098 131 m_paramTypes.Add(wxEmptyString);
7beafee9
VZ
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
3103e8a9
JS
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...)
7beafee9
VZ
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 {
f31a4098 157 m_paramValues.Add(wxEmptyString);
7beafee9
VZ
158 }
159}
160
161BOOL CALLBACK
162EnumSymbolsProc(PSYMBOL_INFO pSymInfo, ULONG WXUNUSED(SymSize), PVOID data)
163{
5c33522f 164 wxStackFrame *frame = static_cast<wxStackFrame *>(data);
7beafee9
VZ
165
166 // we're only interested in parameters
167 if ( pSymInfo->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER )
168 {
169 frame->OnParam(pSymInfo);
170 }
60431236 171
7beafee9
VZ
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 {
9a83f860 193 wxDbgHelpDLL::LogError(wxT("SymSetContext"));
7beafee9
VZ
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 {
9a83f860 208 wxDbgHelpDLL::LogError(wxT("SymEnumSymbols"));
7beafee9
VZ
209 }
210}
211
212
213// ----------------------------------------------------------------------------
214// wxStackWalker
215// ----------------------------------------------------------------------------
216
ef81fe8b 217void wxStackWalker::WalkFrom(const CONTEXT *pCtx, size_t skip, size_t maxDepth)
7beafee9
VZ
218{
219 if ( !wxDbgHelpDLL::Init() )
220 {
b3a6d269
VZ
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
9a83f860 224 wxLogDebug(wxT("Failed to get stack backtrace: %s"),
7beafee9 225 wxDbgHelpDLL::GetErrorMessage().c_str());
1d7ea62e 226 return;
7beafee9
VZ
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 {
9a83f860 243 wxDbgHelpDLL::LogError(wxT("SymInitialize"));
7beafee9
VZ
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
603c73db
VZ
256#if defined(_M_AMD64)
257 sf.AddrPC.Offset = ctx.Rip;
258 sf.AddrPC.Mode = AddrModeFlat;
259 sf.AddrStack.Offset = ctx.Rsp;
260 sf.AddrStack.Mode = AddrModeFlat;
261 sf.AddrFrame.Offset = ctx.Rbp;
262 sf.AddrFrame.Mode = AddrModeFlat;
263
264 dwMachineType = IMAGE_FILE_MACHINE_AMD64;
265#elif defined(_M_IX86)
7beafee9
VZ
266 sf.AddrPC.Offset = ctx.Eip;
267 sf.AddrPC.Mode = AddrModeFlat;
268 sf.AddrStack.Offset = ctx.Esp;
269 sf.AddrStack.Mode = AddrModeFlat;
270 sf.AddrFrame.Offset = ctx.Ebp;
271 sf.AddrFrame.Mode = AddrModeFlat;
272
273 dwMachineType = IMAGE_FILE_MACHINE_I386;
274#else
275 #error "Need to initialize STACKFRAME on non x86"
276#endif // _M_IX86
277
278 // iterate over all stack frames
ef81fe8b 279 for ( size_t nLevel = 0; nLevel < maxDepth; nLevel++ )
7beafee9
VZ
280 {
281 // get the next stack frame
282 if ( !wxDbgHelpDLL::StackWalk
283 (
284 dwMachineType,
285 hProcess,
286 ::GetCurrentThread(),
287 &sf,
288 &ctx,
289 NULL, // read memory function (default)
290 wxDbgHelpDLL::SymFunctionTableAccess,
291 wxDbgHelpDLL::SymGetModuleBase,
292 NULL // address translator for 16 bit
293 ) )
294 {
295 if ( ::GetLastError() )
9a83f860 296 wxDbgHelpDLL::LogError(wxT("StackWalk"));
7beafee9
VZ
297
298 break;
299 }
300
301 // don't show this frame itself in the output
302 if ( nLevel >= skip )
303 {
304 wxStackFrame frame(nLevel - skip,
3f49efdb 305 wxUIntToPtr(sf.AddrPC.Offset),
7beafee9
VZ
306 sf.AddrFrame.Offset);
307
308 OnStackFrame(frame);
309 }
310 }
311
7beafee9
VZ
312 if ( !wxDbgHelpDLL::SymCleanup(hProcess) )
313 {
9a83f860 314 wxDbgHelpDLL::LogError(wxT("SymCleanup"));
7beafee9 315 }
7beafee9
VZ
316}
317
ef81fe8b 318void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS *ep, size_t skip, size_t maxDepth)
7beafee9 319{
ef81fe8b 320 WalkFrom(ep->ContextRecord, skip, maxDepth);
7beafee9
VZ
321}
322
4db307e1
VZ
323#if wxUSE_ON_FATAL_EXCEPTION
324
ef81fe8b 325void wxStackWalker::WalkFromException(size_t maxDepth)
7beafee9
VZ
326{
327 extern EXCEPTION_POINTERS *wxGlobalSEInformation;
328
329 wxCHECK_RET( wxGlobalSEInformation,
9a83f860 330 wxT("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") );
7beafee9
VZ
331
332 // don't skip any frames, the first one is where we crashed
ef81fe8b 333 WalkFrom(wxGlobalSEInformation, 0, maxDepth);
7beafee9
VZ
334}
335
4db307e1
VZ
336#endif // wxUSE_ON_FATAL_EXCEPTION
337
a82c2299 338void wxStackWalker::Walk(size_t skip, size_t WXUNUSED(maxDepth))
7beafee9
VZ
339{
340 // to get a CONTEXT for the current location, simply force an exception and
341 // get EXCEPTION_POINTERS from it
342 //
343 // note:
4db307e1 344 // 1. we additionally skip RaiseException() and WalkFrom() frames
7beafee9
VZ
345 // 2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it
346 // shouldn't have been according to the docs
347 __try
348 {
349 RaiseException(0x1976, 0, 0, NULL);
350 }
351 __except( WalkFrom((EXCEPTION_POINTERS *)GetExceptionInformation(),
352 skip + 2), EXCEPTION_CONTINUE_EXECUTION )
353 {
4db307e1
VZ
354 // never executed because the above expression always evaluates to
355 // EXCEPTION_CONTINUE_EXECUTION
7beafee9
VZ
356 }
357}
358
359#else // !wxUSE_DBGHELP
360
2289dcea
VZ
361// ============================================================================
362// stubs
363// ============================================================================
364
365// ----------------------------------------------------------------------------
366// wxStackFrame
367// ----------------------------------------------------------------------------
368
369void wxStackFrame::OnGetName()
370{
371}
372
373void wxStackFrame::OnGetLocation()
374{
375}
376
377bool
378wxStackFrame::GetParam(size_t WXUNUSED(n),
379 wxString * WXUNUSED(type),
380 wxString * WXUNUSED(name),
381 wxString * WXUNUSED(value)) const
382{
383 return false;
384}
385
1b072e19 386void wxStackFrame::OnParam(_SYMBOL_INFO * WXUNUSED(pSymInfo))
2289dcea
VZ
387{
388}
389
390void wxStackFrame::OnGetParam()
391{
392}
393
394// ----------------------------------------------------------------------------
395// wxStackWalker
396// ----------------------------------------------------------------------------
397
398void
057bf0be
VZ
399wxStackWalker::WalkFrom(const CONTEXT * WXUNUSED(pCtx),
400 size_t WXUNUSED(skip),
401 size_t WXUNUSED(maxDepth))
2289dcea
VZ
402{
403}
404
405void
406wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS * WXUNUSED(ep),
057bf0be
VZ
407 size_t WXUNUSED(skip),
408 size_t WXUNUSED(maxDepth))
2289dcea
VZ
409{
410}
411
4db307e1 412#if wxUSE_ON_FATAL_EXCEPTION
ef81fe8b 413void wxStackWalker::WalkFromException(size_t WXUNUSED(maxDepth))
2289dcea
VZ
414{
415}
4db307e1 416#endif // wxUSE_ON_FATAL_EXCEPTION
2289dcea 417
0034bd13 418void wxStackWalker::Walk(size_t WXUNUSED(skip), size_t WXUNUSED(maxDepth))
2289dcea
VZ
419{
420}
7beafee9
VZ
421
422#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
423
424#endif // wxUSE_STACKWALKER
425