]> git.saurik.com Git - wxWidgets.git/blob - src/msw/stackwalk.cpp
Don't document wxSortedArrayString as deriving from wxArrayString.
[wxWidgets.git] / src / msw / stackwalk.cpp
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
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
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
107 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
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
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
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
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
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
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
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
368 void wxStackFrame::OnGetName()
369 {
370 }
371
372 void wxStackFrame::OnGetLocation()
373 {
374 }
375
376 bool
377 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
385 void wxStackFrame::OnParam(_SYMBOL_INFO * WXUNUSED(pSymInfo))
386 {
387 }
388
389 void wxStackFrame::OnGetParam()
390 {
391 }
392
393 // ----------------------------------------------------------------------------
394 // wxStackWalker
395 // ----------------------------------------------------------------------------
396
397 void
398 wxStackWalker::WalkFrom(const CONTEXT * WXUNUSED(pCtx),
399 size_t WXUNUSED(skip),
400 size_t WXUNUSED(maxDepth))
401 {
402 }
403
404 void
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
412 void wxStackWalker::WalkFromException(size_t WXUNUSED(maxDepth))
413 {
414 }
415 #endif // wxUSE_ON_FATAL_EXCEPTION
416
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