]> git.saurik.com Git - wxWidgets.git/blob - src/msw/stackwalk.cpp
disable an apparently harmless VC++ warning about /EHa being required with _set_se_tr...
[wxWidgets.git] / src / msw / stackwalk.cpp
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$
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 #include "wx/stackwalk.h"
29
30 #include "wx/msw/debughlp.h"
31
32 #if wxUSE_DBGHELP
33
34 // ============================================================================
35 // implementation
36 // ============================================================================
37
38 // ----------------------------------------------------------------------------
39 // wxStackFrame
40 // ----------------------------------------------------------------------------
41
42 void wxStackFrame::OnGetName()
43 {
44 if ( m_hasName )
45 return;
46
47 m_hasName = true;
48
49 // get the name of the function for this stack frame entry
50 static const size_t MAX_NAME_LEN = 1024;
51 BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_NAME_LEN];
52 wxZeroMemory(symbolBuffer);
53
54 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
55 pSymbol->SizeOfStruct = sizeof(symbolBuffer);
56 pSymbol->MaxNameLen = MAX_NAME_LEN;
57
58 DWORD64 symDisplacement = 0;
59 if ( !wxDbgHelpDLL::SymFromAddr
60 (
61 ::GetCurrentProcess(),
62 GetSymAddr(),
63 &symDisplacement,
64 pSymbol
65 ) )
66 {
67 wxDbgHelpDLL::LogError(_T("SymFromAddr"));
68 return;
69 }
70
71 m_name = wxString::FromAscii(pSymbol->Name);
72 m_offset = symDisplacement;
73 }
74
75 void wxStackFrame::OnGetLocation()
76 {
77 if ( m_hasLocation )
78 return;
79
80 m_hasLocation = true;
81
82 // get the source line for this stack frame entry
83 IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
84 DWORD dwLineDisplacement;
85 if ( !wxDbgHelpDLL::SymGetLineFromAddr
86 (
87 ::GetCurrentProcess(),
88 GetSymAddr(),
89 &dwLineDisplacement,
90 &lineInfo
91 ) )
92 {
93 // it is normal that we don't have source info for some symbols,
94 // notably all the ones from the system DLLs...
95 //wxDbgHelpDLL::LogError(_T("SymGetLineFromAddr"));
96 return;
97 }
98
99 m_filename = wxString::FromAscii(lineInfo.FileName);
100 m_line = lineInfo.LineNumber;
101 }
102
103 bool
104 wxStackFrame::GetParam(size_t n,
105 wxString *type,
106 wxString *name,
107 wxString *value) const
108 {
109 if ( !DoGetParamCount() )
110 ConstCast()->OnGetParam();
111
112 if ( n >= DoGetParamCount() )
113 return false;
114
115 if ( type )
116 *type = m_paramTypes[n];
117 if ( name )
118 *name = m_paramNames[n];
119 if ( value )
120 *value = m_paramValues[n];
121
122 return true;
123 }
124
125 void wxStackFrame::OnParam(PSYMBOL_INFO pSymInfo)
126 {
127 m_paramTypes.Add(_T(""));
128
129 m_paramNames.Add(wxString::FromAscii(pSymInfo->Name));
130
131 // if symbol information is corrupted and we crash, the exception is going
132 // to be ignored when we're called from WalkFromException() because of the
133 // except handler there returning EXCEPTION_CONTINUE_EXECUTION, but we'd be
134 // left in an inconsistent state, so deal with it explicitely here (even if
135 // normally we should never crash, of course...)
136 #ifdef _CPPUNWIND
137 try
138 #else
139 __try
140 #endif
141 {
142 // as it is a parameter (and not a global var), it is always offset by
143 // the frame address
144 DWORD_PTR pValue = m_addrFrame + pSymInfo->Address;
145 m_paramValues.Add(wxDbgHelpDLL::DumpSymbol(pSymInfo, (void *)pValue));
146 }
147 #ifdef _CPPUNWIND
148 catch ( ... )
149 #else
150 __except ( EXCEPTION_EXECUTE_HANDLER )
151 #endif
152 {
153 m_paramValues.Add(_T(""));
154 }
155 }
156
157 BOOL CALLBACK
158 EnumSymbolsProc(PSYMBOL_INFO pSymInfo, ULONG WXUNUSED(SymSize), PVOID data)
159 {
160 wxStackFrame *frame = wx_static_cast(wxStackFrame *, data);
161
162 // we're only interested in parameters
163 if ( pSymInfo->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER )
164 {
165 frame->OnParam(pSymInfo);
166 }
167
168 // return true to continue enumeration, false would have stopped it
169 return TRUE;
170 }
171
172 void wxStackFrame::OnGetParam()
173 {
174 // use SymSetContext to get just the locals/params for this frame
175 IMAGEHLP_STACK_FRAME imagehlpStackFrame;
176 wxZeroMemory(imagehlpStackFrame);
177 imagehlpStackFrame.InstructionOffset = GetSymAddr();
178 if ( !wxDbgHelpDLL::SymSetContext
179 (
180 ::GetCurrentProcess(),
181 &imagehlpStackFrame,
182 0 // unused
183 ) )
184 {
185 // for symbols from kernel DLL we might not have access to their
186 // address, this is not a real error
187 if ( ::GetLastError() != ERROR_INVALID_ADDRESS )
188 {
189 wxDbgHelpDLL::LogError(_T("SymSetContext"));
190 }
191
192 return;
193 }
194
195 if ( !wxDbgHelpDLL::SymEnumSymbols
196 (
197 ::GetCurrentProcess(),
198 NULL, // DLL base: use current context
199 NULL, // no mask, get all symbols
200 EnumSymbolsProc, // callback
201 this // data to pass to it
202 ) )
203 {
204 wxDbgHelpDLL::LogError(_T("SymEnumSymbols"));
205 }
206 }
207
208
209 // ----------------------------------------------------------------------------
210 // wxStackWalker
211 // ----------------------------------------------------------------------------
212
213 void wxStackWalker::WalkFrom(const CONTEXT *pCtx, size_t skip)
214 {
215 if ( !wxDbgHelpDLL::Init() )
216 {
217 wxLogError(_("Failed to get stack backtrace:\n%s"),
218 wxDbgHelpDLL::GetErrorMessage().c_str());
219 }
220
221 // according to MSDN, the first parameter should be just a unique value and
222 // not process handle (although the parameter is prototyped as "HANDLE
223 // hProcess") and actually it advises to use the process id and not handle
224 // for Win9x, but then we need to use the same value in StackWalk() call
225 // below which should be a real handle... so this is what we use
226 const HANDLE hProcess = ::GetCurrentProcess();
227
228 if ( !wxDbgHelpDLL::SymInitialize
229 (
230 hProcess,
231 NULL, // use default symbol search path
232 TRUE // load symbols for all loaded modules
233 ) )
234 {
235 wxDbgHelpDLL::LogError(_T("SymInitialize"));
236
237 return;
238 }
239
240 CONTEXT ctx = *pCtx; // will be modified by StackWalk()
241
242 DWORD dwMachineType;
243
244 // initialize the initial frame: currently we can do it for x86 only
245 STACKFRAME sf;
246 wxZeroMemory(sf);
247
248 #ifdef _M_IX86
249 sf.AddrPC.Offset = ctx.Eip;
250 sf.AddrPC.Mode = AddrModeFlat;
251 sf.AddrStack.Offset = ctx.Esp;
252 sf.AddrStack.Mode = AddrModeFlat;
253 sf.AddrFrame.Offset = ctx.Ebp;
254 sf.AddrFrame.Mode = AddrModeFlat;
255
256 dwMachineType = IMAGE_FILE_MACHINE_I386;
257 #else
258 #error "Need to initialize STACKFRAME on non x86"
259 #endif // _M_IX86
260
261 // iterate over all stack frames
262 for ( size_t nLevel = 0; ; nLevel++ )
263 {
264 // get the next stack frame
265 if ( !wxDbgHelpDLL::StackWalk
266 (
267 dwMachineType,
268 hProcess,
269 ::GetCurrentThread(),
270 &sf,
271 &ctx,
272 NULL, // read memory function (default)
273 wxDbgHelpDLL::SymFunctionTableAccess,
274 wxDbgHelpDLL::SymGetModuleBase,
275 NULL // address translator for 16 bit
276 ) )
277 {
278 if ( ::GetLastError() )
279 wxDbgHelpDLL::LogError(_T("StackWalk"));
280
281 break;
282 }
283
284 // don't show this frame itself in the output
285 if ( nLevel >= skip )
286 {
287 wxStackFrame frame(nLevel - skip,
288 (void *)sf.AddrPC.Offset,
289 sf.AddrFrame.Offset);
290
291 OnStackFrame(frame);
292 }
293 }
294
295 // this results in crashes inside ntdll.dll when called from
296 // exception handler ...
297 #if 0
298 if ( !wxDbgHelpDLL::SymCleanup(hProcess) )
299 {
300 wxDbgHelpDLL::LogError(_T("SymCleanup"));
301 }
302 #endif
303 }
304
305 void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS *ep, size_t skip)
306 {
307 WalkFrom(ep->ContextRecord, skip);
308 }
309
310 void wxStackWalker::WalkFromException()
311 {
312 extern EXCEPTION_POINTERS *wxGlobalSEInformation;
313
314 wxCHECK_RET( wxGlobalSEInformation,
315 _T("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") );
316
317 // don't skip any frames, the first one is where we crashed
318 WalkFrom(wxGlobalSEInformation, 0);
319 }
320
321 void wxStackWalker::Walk(size_t skip)
322 {
323 // to get a CONTEXT for the current location, simply force an exception and
324 // get EXCEPTION_POINTERS from it
325 //
326 // note:
327 // 1. we additionally skip RaiseException() and WalkFromException() frames
328 // 2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it
329 // shouldn't have been according to the docs
330 __try
331 {
332 RaiseException(0x1976, 0, 0, NULL);
333 }
334 __except( WalkFrom((EXCEPTION_POINTERS *)GetExceptionInformation(),
335 skip + 2), EXCEPTION_CONTINUE_EXECUTION )
336 {
337 // never executed because of WalkFromException() return value
338 }
339 }
340
341 #else // !wxUSE_DBGHELP
342
343 // TODO: implement stubs
344
345 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
346
347 #endif // wxUSE_STACKWALKER
348