created a reusable interface to dbghelp API
[wxWidgets.git] / src / msw / debughlp.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/debughlp.cpp
3 // Purpose: various Win32 debug helpers
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 2005-01-08 (extracted from crashrpt.cpp)
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 #include "wx/msw/debughlp.h"
27
28 #if wxUSE_DBGHELP
29
30 // ----------------------------------------------------------------------------
31 // globals
32 // ----------------------------------------------------------------------------
33
34 // error message from Init()
35 static wxString gs_errMsg;
36
37 // ============================================================================
38 // wxDbgHelpDLL implementation
39 // ============================================================================
40
41 // ----------------------------------------------------------------------------
42 // static members
43 // ----------------------------------------------------------------------------
44
45 #define DEFINE_SYM_FUNCTION(func) wxDbgHelpDLL::func ## _t wxDbgHelpDLL::func = 0
46
47 wxDO_FOR_ALL_SYM_FUNCS(DEFINE_SYM_FUNCTION);
48
49 #undef DEFINE_SYM_FUNCTION
50
51 // ----------------------------------------------------------------------------
52 // initialization methods
53 // ----------------------------------------------------------------------------
54
55 // load all function we need from the DLL
56
57 static bool BindDbgHelpFunctions(const wxDynamicLibrary& dllDbgHelp)
58 {
59 #define LOAD_SYM_FUNCTION(name) \
60 wxDbgHelpDLL::name = (wxDbgHelpDLL::name ## _t) \
61 dllDbgHelp.GetSymbol(_T(#name)); \
62 if ( !wxDbgHelpDLL::name ) \
63 { \
64 gs_errMsg += _T("Function ") _T(#name) _T("() not found.\n"); \
65 return false; \
66 }
67
68 wxDO_FOR_ALL_SYM_FUNCS(LOAD_SYM_FUNCTION);
69
70 #undef LOAD_SYM_FUNCTION
71
72 return true;
73 }
74
75 // called by Init() if we hadn't done this before
76 static bool DoInit()
77 {
78 wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM);
79 if ( dllDbgHelp.IsLoaded() )
80 {
81 if ( BindDbgHelpFunctions(dllDbgHelp) )
82 {
83 // turn on default options
84 DWORD options = wxDbgHelpDLL::SymGetOptions();
85
86 options |= SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_DEBUG;
87
88 wxDbgHelpDLL::SymSetOptions(options);
89
90 dllDbgHelp.Detach();
91 return true;
92 }
93
94 gs_errMsg += _T("\nPlease update your dbghelp.dll version, ")
95 _T("at least version 5.1 is needed!\n")
96 _T("(if you already have a new version, please ")
97 _T("put it in the same directory where the program is.)\n");
98 }
99 else // failed to load dbghelp.dll
100 {
101 gs_errMsg += _T("Please install dbghelp.dll available free of charge ")
102 _T("from Microsoft to get more detailed crash information!");
103 }
104
105 gs_errMsg += _T("\nLatest dbghelp.dll is available at ")
106 _T("http://www.microsoft.com/whdc/ddk/debugging/\n");
107
108 return false;
109 }
110
111 /* static */
112 bool wxDbgHelpDLL::Init()
113 {
114 // this flag is -1 until Init() is called for the first time, then it's set
115 // to either false or true depending on whether we could load the functions
116 static int s_loaded = -1;
117
118 if ( s_loaded == -1 )
119 {
120 s_loaded = DoInit();
121 }
122
123 return s_loaded != 0;
124 }
125
126 // ----------------------------------------------------------------------------
127 // error handling
128 // ----------------------------------------------------------------------------
129
130 /* static */
131 const wxString& wxDbgHelpDLL::GetErrorMessage()
132 {
133 return gs_errMsg;
134 }
135
136 /* static */
137 void wxDbgHelpDLL::LogError(const wxChar *func)
138 {
139 ::OutputDebugString(wxString::Format(_T("dbghelp: %s() failed: %s\r\n"),
140 func, wxSysErrorMsg(::GetLastError())));
141 }
142
143 // ----------------------------------------------------------------------------
144 // data dumping
145 // ----------------------------------------------------------------------------
146
147 static inline
148 bool
149 DoGetTypeInfo(DWORD64 base, ULONG ti, IMAGEHLP_SYMBOL_TYPE_INFO type, void *rc)
150 {
151 static HANDLE s_hProcess = ::GetCurrentProcess();
152
153 return wxDbgHelpDLL::SymGetTypeInfo
154 (
155 s_hProcess,
156 base,
157 ti,
158 type,
159 rc
160 ) != 0;
161 }
162
163 static inline
164 bool
165 DoGetTypeInfo(PSYMBOL_INFO pSym, IMAGEHLP_SYMBOL_TYPE_INFO type, void *rc)
166 {
167 return DoGetTypeInfo(pSym->ModBase, pSym->TypeIndex, type, rc);
168 }
169
170 static inline
171 wxDbgHelpDLL::BasicType GetBasicType(PSYMBOL_INFO pSym)
172 {
173 wxDbgHelpDLL::BasicType bt;
174 return DoGetTypeInfo(pSym, TI_GET_BASETYPE, &bt)
175 ? bt
176 : wxDbgHelpDLL::BASICTYPE_NOTYPE;
177 }
178
179 /* static */
180 wxString wxDbgHelpDLL::GetSymbolName(PSYMBOL_INFO pSym)
181 {
182 wxString s;
183
184 WCHAR *pwszTypeName;
185 if ( SymGetTypeInfo
186 (
187 GetCurrentProcess(),
188 pSym->ModBase,
189 pSym->TypeIndex,
190 TI_GET_SYMNAME,
191 &pwszTypeName
192 ) )
193 {
194 s = wxConvCurrent->cWC2WX(pwszTypeName);
195
196 ::LocalFree(pwszTypeName);
197 }
198
199 return s;
200 }
201
202 /* static */ wxString
203 wxDbgHelpDLL::DumpBaseType(BasicType bt, DWORD64 length, PVOID pAddress)
204 {
205 if ( !pAddress )
206 {
207 return _T("null");
208 }
209
210 if ( ::IsBadReadPtr(pAddress, length) != 0 )
211 {
212 return _T("BAD");
213 }
214
215
216 wxString s;
217 s.reserve(256);
218
219 if ( length == 1 )
220 {
221 const BYTE b = *(PBYTE)pAddress;
222
223 if ( bt == BASICTYPE_BOOL )
224 s = b ? _T("true") : _T("false");
225 else
226 s.Printf(_T("%#04x"), b);
227 }
228 else if ( length == 2 )
229 {
230 s.Printf(bt == BASICTYPE_UINT ? _T("%#06x") : _T("%d"),
231 *(PWORD)pAddress);
232 }
233 else if ( length == 4 )
234 {
235 bool handled = false;
236
237 if ( bt == BASICTYPE_FLOAT )
238 {
239 s.Printf(_T("%f"), *(PFLOAT)pAddress);
240
241 handled = true;
242 }
243 else if ( bt == BASICTYPE_CHAR )
244 {
245 // don't take more than 32 characters of a string
246 static const size_t NUM_CHARS = 64;
247
248 const char *pc = *(PSTR *)pAddress;
249 if ( ::IsBadStringPtrA(pc, NUM_CHARS) == 0 )
250 {
251 s += _T('"');
252 for ( size_t n = 0; n < NUM_CHARS && *pc; n++, pc++ )
253 {
254 s += *pc;
255 }
256 s += _T('"');
257
258 handled = true;
259 }
260 }
261
262 if ( !handled )
263 {
264 // treat just as an opaque DWORD
265 s.Printf(_T("%#x"), *(PDWORD)pAddress);
266 }
267 }
268 else if ( length == 8 )
269 {
270 if ( bt == BASICTYPE_FLOAT )
271 {
272 s.Printf(_T("%lf"), *(double *)pAddress);
273 }
274 else // opaque 64 bit value
275 {
276 s.Printf(_T("%#" wxLongLongFmtSpec _T("x")), *(PDWORD *)pAddress);
277 }
278 }
279
280 return s;
281 }
282
283 wxString
284 wxDbgHelpDLL::DumpField(PSYMBOL_INFO pSym, void *pVariable, unsigned level)
285 {
286 wxString s;
287
288 // avoid infinite recursion
289 if ( level > 100 )
290 {
291 return s;
292 }
293
294 SymbolTag tag = SYMBOL_TAG_NULL;
295 if ( !DoGetTypeInfo(pSym, TI_GET_SYMTAG, &tag) )
296 {
297 return s;
298 }
299
300 switch ( tag )
301 {
302 case SYMBOL_TAG_UDT:
303 case SYMBOL_TAG_BASE_CLASS:
304 s = DumpUDT(pSym, pVariable, level);
305 break;
306
307 case SYMBOL_TAG_DATA:
308 wxDbgHelpDLL::DataKind kind;
309 if ( !DoGetTypeInfo(pSym, TI_GET_DATAKIND, &kind) ||
310 kind != DATA_MEMBER )
311 {
312 // maybe it's a static member? we're not interested in them...
313 break;
314 }
315
316 // get the offset of the child member, relative to its parent
317 DWORD ofs = 0;
318 if ( !DoGetTypeInfo(pSym, TI_GET_OFFSET, &ofs) )
319 break;
320
321 pVariable = (void *)((DWORD_PTR)pVariable + ofs);
322
323
324 // now pass to the type representing the type of this member
325 SYMBOL_INFO sym = *pSym;
326 if ( !DoGetTypeInfo(pSym, TI_GET_TYPEID, &sym.TypeIndex) )
327 break;
328
329 ULONG64 size;
330 DoGetTypeInfo(&sym, TI_GET_LENGTH, &size);
331
332 switch ( DereferenceSymbol(&sym, &pVariable) )
333 {
334 case SYMBOL_TAG_BASE_TYPE:
335 {
336 BasicType bt = GetBasicType(&sym);
337 if ( bt )
338 {
339 s = DumpBaseType(bt, size, pVariable);
340 }
341 }
342 break;
343
344 case SYMBOL_TAG_UDT:
345 case SYMBOL_TAG_BASE_CLASS:
346 s = DumpUDT(&sym, pVariable, level);
347 break;
348 }
349
350 if ( !s.empty() )
351 {
352 s = GetSymbolName(pSym) + _T(" = ") + s;
353 }
354 break;
355 }
356
357 if ( !s.empty() )
358 {
359 s = wxString(_T('\t'), level + 1) + s + _T('\n');
360 }
361
362 return s;
363 }
364
365 /* static */ wxString
366 wxDbgHelpDLL::DumpUDT(PSYMBOL_INFO pSym, void *pVariable, unsigned level)
367 {
368 wxString s;
369 s.reserve(512);
370 s = GetSymbolName(pSym);
371
372 #if !wxUSE_STL
373 // special handling for ubiquitous wxString: although the code below works
374 // for it as well, it shows the wxStringBase class and takes 4 lines
375 // instead of only one as this branch
376 if ( s == _T("wxString") )
377 {
378 wxString *ps = (wxString *)pVariable;
379 s << _T("(\"") << *ps << _T(")\"");
380 }
381 else // any other UDT
382 #endif // !wxUSE_STL
383 {
384 // Determine how many children this type has.
385 DWORD dwChildrenCount = 0;
386 DoGetTypeInfo(pSym, TI_GET_CHILDRENCOUNT, &dwChildrenCount);
387
388 // Prepare to get an array of "TypeIds", representing each of the children.
389 TI_FINDCHILDREN_PARAMS *children = (TI_FINDCHILDREN_PARAMS *)
390 malloc(sizeof(TI_FINDCHILDREN_PARAMS) +
391 (dwChildrenCount - 1)*sizeof(ULONG));
392 if ( !children )
393 return s;
394
395 children->Count = dwChildrenCount;
396 children->Start = 0;
397
398 // Get the array of TypeIds, one for each child type
399 if ( !DoGetTypeInfo(pSym, TI_FINDCHILDREN, children) )
400 {
401 free(children);
402 return s;
403 }
404
405 s << _T(" {\n");
406
407 // Iterate through all children
408 SYMBOL_INFO sym;
409 wxZeroMemory(sym);
410 sym.ModBase = pSym->ModBase;
411 for ( unsigned i = 0; i < dwChildrenCount; i++ )
412 {
413 sym.TypeIndex = children->ChildId[i];
414
415 // children here are in lexicographic sense, i.e. we get all our nested
416 // classes and not only our member fields, but we can't get the values
417 // for the members of the nested classes, of course!
418 DWORD nested;
419 if ( DoGetTypeInfo(&sym, TI_GET_NESTED, &nested) && nested )
420 continue;
421
422 // avoid infinite recursion: this does seem to happen sometimes with
423 // complex typedefs...
424 if ( sym.TypeIndex == pSym->TypeIndex )
425 continue;
426
427 s += DumpField(&sym, pVariable, level + 1);
428 }
429
430 free(children);
431
432 s << wxString(_T('\t'), level + 1) << _T('}');
433 }
434
435 return s;
436 }
437
438 /* static */
439 wxDbgHelpDLL::SymbolTag
440 wxDbgHelpDLL::DereferenceSymbol(PSYMBOL_INFO pSym, void **ppData)
441 {
442 SymbolTag tag = SYMBOL_TAG_NULL;
443 for ( ;; )
444 {
445 if ( !DoGetTypeInfo(pSym, TI_GET_SYMTAG, &tag) )
446 break;
447
448 if ( tag != SYMBOL_TAG_POINTER_TYPE )
449 break;
450
451 ULONG tiNew;
452 if ( !DoGetTypeInfo(pSym, TI_GET_TYPEID, &tiNew) ||
453 tiNew == pSym->TypeIndex )
454 break;
455
456 pSym->TypeIndex = tiNew;
457
458 // remove one level of indirection except for the char strings: we want
459 // to dump "char *" and not a single "char" for them
460 if ( ppData && *ppData && GetBasicType(pSym) != BASICTYPE_CHAR )
461 *ppData = (void *)*((DWORD_PTR *)*ppData);
462 }
463
464 return tag;
465 }
466
467 /* static */ wxString
468 wxDbgHelpDLL::DumpSymbol(PSYMBOL_INFO pSym, void *pVariable)
469 {
470 wxString s;
471 SYMBOL_INFO symDeref = *pSym;
472 switch ( DereferenceSymbol(&symDeref, &pVariable) )
473 {
474 case SYMBOL_TAG_UDT:
475 // show UDT recursively
476 s = DumpUDT(&symDeref, pVariable);
477 break;
478
479 case SYMBOL_TAG_BASE_TYPE:
480 // variable of simple type, show directly
481 BasicType bt = GetBasicType(&symDeref);
482 if ( bt )
483 {
484 s = DumpBaseType(bt, pSym->Size, pVariable);
485 }
486 break;
487 }
488
489 return s;
490 }
491
492 // ----------------------------------------------------------------------------
493 // debugging helpers
494 // ----------------------------------------------------------------------------
495
496 #ifndef NDEBUG
497
498 static wxString TagString(wxDbgHelpDLL::SymbolTag tag)
499 {
500 static const wxChar *tags[] =
501 {
502 _T("null"),
503 _T("exe"),
504 _T("compiland"),
505 _T("compiland details"),
506 _T("compiland env"),
507 _T("function"),
508 _T("block"),
509 _T("data"),
510 _T("annotation"),
511 _T("label"),
512 _T("public symbol"),
513 _T("udt"),
514 _T("enum"),
515 _T("function type"),
516 _T("pointer type"),
517 _T("array type"),
518 _T("base type"),
519 _T("typedef"),
520 _T("base class"),
521 _T("friend"),
522 _T("function arg type"),
523 _T("func debug start"),
524 _T("func debug end"),
525 _T("using namespace"),
526 _T("vtable shape"),
527 _T("vtable"),
528 _T("custom"),
529 _T("thunk"),
530 _T("custom type"),
531 _T("managed type"),
532 _T("dimension"),
533 };
534
535 wxCOMPILE_TIME_ASSERT( WXSIZEOF(tags) == wxDbgHelpDLL::SYMBOL_TAG_MAX,
536 SymbolTagStringMismatch );
537
538 wxString s;
539 if ( tag < WXSIZEOF(tags) )
540 s = tags[tag];
541 else
542 s.Printf(_T("unrecognized tag (%d)"), tag);
543
544 return s;
545 }
546
547 static wxString KindString(wxDbgHelpDLL::DataKind kind)
548 {
549 static const wxChar *kinds[] =
550 {
551 _T("unknown"),
552 _T("local"),
553 _T("static local"),
554 _T("param"),
555 _T("object ptr"),
556 _T("file static"),
557 _T("global"),
558 _T("member"),
559 _T("static member"),
560 _T("constant"),
561 };
562
563 wxCOMPILE_TIME_ASSERT( WXSIZEOF(kinds) == wxDbgHelpDLL::DATA_MAX,
564 DataKindStringMismatch );
565
566 wxString s;
567 if ( kind < WXSIZEOF(kinds) )
568 s = kinds[kind];
569 else
570 s.Printf(_T("unrecognized kind (%d)"), kind);
571
572 return s;
573 }
574
575 static wxString UdtKindString(wxDbgHelpDLL::UdtKind kind)
576 {
577 static const wxChar *kinds[] =
578 {
579 _T("struct"),
580 _T("class"),
581 _T("union"),
582 };
583
584 wxCOMPILE_TIME_ASSERT( WXSIZEOF(kinds) == wxDbgHelpDLL::UDT_MAX,
585 UDTKindStringMismatch );
586
587 wxString s;
588 if ( kind < WXSIZEOF(kinds) )
589 s = kinds[kind];
590 else
591 s.Printf(_T("unrecognized UDT (%d)"), kind);
592
593 return s;
594 }
595
596 static wxString TypeString(wxDbgHelpDLL::BasicType bt)
597 {
598 static const wxChar *types[] =
599 {
600 _T("no type"),
601 _T("void"),
602 _T("char"),
603 _T("wchar"),
604 _T(""),
605 _T(""),
606 _T("int"),
607 _T("uint"),
608 _T("float"),
609 _T("bcd"),
610 _T("bool"),
611 _T(""),
612 _T(""),
613 _T("long"),
614 _T("ulong"),
615 _T(""),
616 _T(""),
617 _T(""),
618 _T(""),
619 _T(""),
620 _T(""),
621 _T(""),
622 _T(""),
623 _T(""),
624 _T(""),
625 _T("CURRENCY"),
626 _T("DATE"),
627 _T("VARIANT"),
628 _T("complex"),
629 _T("bit"),
630 _T("BSTR"),
631 _T("HRESULT"),
632 };
633
634 wxCOMPILE_TIME_ASSERT( WXSIZEOF(types) == wxDbgHelpDLL::BASICTYPE_MAX,
635 BasicTypeStringMismatch );
636
637 wxString s;
638 if ( bt < WXSIZEOF(types) )
639 s = types[bt];
640
641 if ( s.empty() )
642 s.Printf(_T("unrecognized type (%d)"), bt);
643
644 return s;
645 }
646
647 // this function is meant to be called from under debugger to see the
648 // proprieties of the given type id
649 extern "C" void DumpTI(ULONG ti)
650 {
651 SYMBOL_INFO sym = { sizeof(SYMBOL_INFO) };
652 sym.ModBase = 0x400000; // it's a constant under Win32
653 sym.TypeIndex = ti;
654
655 wxDbgHelpDLL::SymbolTag tag = wxDbgHelpDLL::SYMBOL_TAG_NULL;
656 DoGetTypeInfo(&sym, TI_GET_SYMTAG, &tag);
657 DoGetTypeInfo(&sym, TI_GET_TYPEID, &ti);
658
659 OutputDebugString(wxString::Format(_T("Type 0x%x: "), sym.TypeIndex));
660 wxString name = wxDbgHelpDLL::GetSymbolName(&sym);
661 if ( !name.empty() )
662 {
663 OutputDebugString(wxString::Format(_T("name=\"%s\", "), name.c_str()));
664 }
665
666 DWORD nested;
667 if ( !DoGetTypeInfo(&sym, TI_GET_NESTED, &nested) )
668 {
669 nested = FALSE;
670 }
671
672 OutputDebugString(wxString::Format(_T("tag=%s%s"),
673 nested ? _T("nested ") : _T(""),
674 TagString(tag).c_str()));
675 if ( tag == wxDbgHelpDLL::SYMBOL_TAG_UDT )
676 {
677 wxDbgHelpDLL::UdtKind udtKind;
678 if ( DoGetTypeInfo(&sym, TI_GET_UDTKIND, &udtKind) )
679 {
680 OutputDebugString(_T(" (") + UdtKindString(udtKind) + _T(')'));
681 }
682 }
683
684 wxDbgHelpDLL::DataKind kind = wxDbgHelpDLL::DATA_UNKNOWN;
685 if ( DoGetTypeInfo(&sym, TI_GET_DATAKIND, &kind) )
686 {
687 OutputDebugString(wxString::Format(
688 _T(", kind=%s"), KindString(kind).c_str()));
689 if ( kind == wxDbgHelpDLL::DATA_MEMBER )
690 {
691 DWORD ofs = 0;
692 if ( DoGetTypeInfo(&sym, TI_GET_OFFSET, &ofs) )
693 {
694 OutputDebugString(wxString::Format(_T(" (ofs=0x%x)"), ofs));
695 }
696 }
697 }
698
699 wxDbgHelpDLL::BasicType bt = GetBasicType(&sym);
700 if ( bt )
701 {
702 OutputDebugString(wxString::Format(_T(", type=%s"),
703 TypeString(bt).c_str()));
704 }
705
706 if ( ti != sym.TypeIndex )
707 {
708 OutputDebugString(wxString::Format(_T(", next ti=0x%x"), ti));
709 }
710
711 OutputDebugString("\r\n");
712 }
713
714 #endif // NDEBUG
715
716 #endif // wxUSE_DBGHELP