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