1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/debughlp.cpp
3 // Purpose: various Win32 debug helpers
4 // Author: Vadim Zeitlin
6 // Created: 2005-01-08 (extracted from crashrpt.cpp)
8 // Copyright: (c) 2003-2005 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
26 #include "wx/msw/debughlp.h"
28 #if wxUSE_DBGHELP && wxUSE_DYNLIB_CLASS
30 // ----------------------------------------------------------------------------
32 // ----------------------------------------------------------------------------
34 // to prevent recursion which could result from corrupted data we limit
35 // ourselves to that many levels of embedded fields inside structs
36 static const unsigned MAX_DUMP_DEPTH
= 20;
38 // ----------------------------------------------------------------------------
40 // ----------------------------------------------------------------------------
42 // error message from Init()
43 static wxString gs_errMsg
;
45 // ============================================================================
46 // wxDbgHelpDLL implementation
47 // ============================================================================
49 // ----------------------------------------------------------------------------
51 // ----------------------------------------------------------------------------
53 #define DEFINE_SYM_FUNCTION(func, name) \
54 wxDbgHelpDLL::func ## _t wxDbgHelpDLL::func = 0
56 wxDO_FOR_ALL_SYM_FUNCS(DEFINE_SYM_FUNCTION
);
58 #undef DEFINE_SYM_FUNCTION
60 // ----------------------------------------------------------------------------
61 // initialization methods
62 // ----------------------------------------------------------------------------
64 // load all function we need from the DLL
66 static bool BindDbgHelpFunctions(const wxDynamicLibrary
& dllDbgHelp
)
68 #define LOAD_SYM_FUNCTION(func, name) \
69 wxDbgHelpDLL::func = (wxDbgHelpDLL::func ## _t) \
70 dllDbgHelp.GetSymbol(wxT(#name)); \
71 if ( !wxDbgHelpDLL::func ) \
73 gs_errMsg += wxT("Function ") wxT(#name) wxT("() not found.\n"); \
77 wxDO_FOR_ALL_SYM_FUNCS(LOAD_SYM_FUNCTION
);
79 #undef LOAD_SYM_FUNCTION
84 // called by Init() if we hadn't done this before
87 wxDynamicLibrary
dllDbgHelp(wxT("dbghelp.dll"), wxDL_VERBATIM
);
88 if ( dllDbgHelp
.IsLoaded() )
90 if ( BindDbgHelpFunctions(dllDbgHelp
) )
92 // turn on default options
93 DWORD options
= wxDbgHelpDLL::SymGetOptions();
95 options
|= SYMOPT_DEFERRED_LOADS
| SYMOPT_UNDNAME
| SYMOPT_DEBUG
;
97 wxDbgHelpDLL::SymSetOptions(options
);
103 gs_errMsg
+= wxT("\nPlease update your dbghelp.dll version, ")
104 wxT("at least version 5.1 is needed!\n")
105 wxT("(if you already have a new version, please ")
106 wxT("put it in the same directory where the program is.)\n");
108 else // failed to load dbghelp.dll
110 gs_errMsg
+= wxT("Please install dbghelp.dll available free of charge ")
111 wxT("from Microsoft to get more detailed crash information!");
114 gs_errMsg
+= wxT("\nLatest dbghelp.dll is available at ")
115 wxT("http://www.microsoft.com/whdc/ddk/debugging/\n");
121 bool wxDbgHelpDLL::Init()
123 // this flag is -1 until Init() is called for the first time, then it's set
124 // to either false or true depending on whether we could load the functions
125 static int s_loaded
= -1;
127 if ( s_loaded
== -1 )
132 return s_loaded
!= 0;
135 // ----------------------------------------------------------------------------
137 // ----------------------------------------------------------------------------
140 const wxString
& wxDbgHelpDLL::GetErrorMessage()
146 void wxDbgHelpDLL::LogError(const wxChar
*func
)
148 ::OutputDebugString(wxString::Format(wxT("dbghelp: %s() failed: %s\r\n"),
149 func
, wxSysErrorMsg(::GetLastError())).t_str());
152 // ----------------------------------------------------------------------------
154 // ----------------------------------------------------------------------------
158 DoGetTypeInfo(DWORD64 base
, ULONG ti
, IMAGEHLP_SYMBOL_TYPE_INFO type
, void *rc
)
160 static HANDLE s_hProcess
= ::GetCurrentProcess();
162 return wxDbgHelpDLL::SymGetTypeInfo
174 DoGetTypeInfo(PSYMBOL_INFO pSym
, IMAGEHLP_SYMBOL_TYPE_INFO type
, void *rc
)
176 return DoGetTypeInfo(pSym
->ModBase
, pSym
->TypeIndex
, type
, rc
);
180 wxDbgHelpDLL::BasicType
GetBasicType(PSYMBOL_INFO pSym
)
182 wxDbgHelpDLL::BasicType bt
;
183 return DoGetTypeInfo(pSym
, TI_GET_BASETYPE
, &bt
)
185 : wxDbgHelpDLL::BASICTYPE_NOTYPE
;
189 wxString
wxDbgHelpDLL::GetSymbolName(PSYMBOL_INFO pSym
)
203 s
= wxConvCurrent
->cWC2WX(pwszTypeName
);
205 ::LocalFree(pwszTypeName
);
211 /* static */ wxString
212 wxDbgHelpDLL::DumpBaseType(BasicType bt
, DWORD64 length
, PVOID pAddress
)
219 if ( ::IsBadReadPtr(pAddress
, length
) != 0 )
230 const BYTE b
= *(PBYTE
)pAddress
;
232 if ( bt
== BASICTYPE_BOOL
)
233 s
= b
? wxT("true") : wxT("false");
235 s
.Printf(wxT("%#04x"), b
);
237 else if ( length
== 2 )
239 s
.Printf(bt
== BASICTYPE_UINT
? wxT("%#06x") : wxT("%d"),
242 else if ( length
== 4 )
244 bool handled
= false;
246 if ( bt
== BASICTYPE_FLOAT
)
248 s
.Printf(wxT("%f"), *(PFLOAT
)pAddress
);
252 else if ( bt
== BASICTYPE_CHAR
)
254 // don't take more than 32 characters of a string
255 static const size_t NUM_CHARS
= 64;
257 const char *pc
= *(PSTR
*)pAddress
;
258 if ( ::IsBadStringPtrA(pc
, NUM_CHARS
) == 0 )
261 for ( size_t n
= 0; n
< NUM_CHARS
&& *pc
; n
++, pc
++ )
273 // treat just as an opaque DWORD
274 s
.Printf(wxT("%#x"), *(PDWORD
)pAddress
);
277 else if ( length
== 8 )
279 if ( bt
== BASICTYPE_FLOAT
)
281 s
.Printf(wxT("%lf"), *(double *)pAddress
);
283 else // opaque 64 bit value
285 s
.Printf("%#" wxLongLongFmtSpec
"x", *(wxLongLong_t
*)pAddress
);
293 wxDbgHelpDLL::DumpField(PSYMBOL_INFO pSym
, void *pVariable
, unsigned level
)
297 // avoid infinite recursion
298 if ( level
> MAX_DUMP_DEPTH
)
303 SymbolTag tag
= SYMBOL_TAG_NULL
;
304 if ( !DoGetTypeInfo(pSym
, TI_GET_SYMTAG
, &tag
) )
312 case SYMBOL_TAG_BASE_CLASS
:
313 s
= DumpUDT(pSym
, pVariable
, level
);
316 case SYMBOL_TAG_DATA
:
321 else // valid location
323 wxDbgHelpDLL::DataKind kind
;
324 if ( !DoGetTypeInfo(pSym
, TI_GET_DATAKIND
, &kind
) ||
325 kind
!= DATA_MEMBER
)
327 // maybe it's a static member? we're not interested in them...
331 // get the offset of the child member, relative to its parent
333 if ( !DoGetTypeInfo(pSym
, TI_GET_OFFSET
, &ofs
) )
336 pVariable
= (void *)((DWORD_PTR
)pVariable
+ ofs
);
339 // now pass to the type representing the type of this member
340 SYMBOL_INFO sym
= *pSym
;
341 if ( !DoGetTypeInfo(pSym
, TI_GET_TYPEID
, &sym
.TypeIndex
) )
345 DoGetTypeInfo(&sym
, TI_GET_LENGTH
, &size
);
347 switch ( DereferenceSymbol(&sym
, &pVariable
) )
349 case SYMBOL_TAG_BASE_TYPE
:
351 BasicType bt
= GetBasicType(&sym
);
354 s
= DumpBaseType(bt
, size
, pVariable
);
360 case SYMBOL_TAG_BASE_CLASS
:
361 s
= DumpUDT(&sym
, pVariable
, level
);
365 // Suppress gcc warnings about unhandled enum values.
372 s
= GetSymbolName(pSym
) + wxT(" = ") + s
;
377 // Suppress gcc warnings about unhandled enum values, don't assert
378 // to avoid problems during fatal crash generation.
384 s
= wxString(wxT('\t'), level
+ 1) + s
+ wxT('\n');
390 /* static */ wxString
391 wxDbgHelpDLL::DumpUDT(PSYMBOL_INFO pSym
, void *pVariable
, unsigned level
)
395 // we have to limit the depth of UDT dumping as otherwise we get in
396 // infinite loops trying to dump linked lists... 10 levels seems quite
397 // reasonable, full information is in minidump file anyhow
402 s
= GetSymbolName(pSym
);
404 #if !wxUSE_STD_STRING
405 // special handling for ubiquitous wxString: although the code below works
406 // for it as well, it shows the wxStringBase class and takes 4 lines
407 // instead of only one as this branch
408 if ( s
== wxT("wxString") )
410 wxString
*ps
= (wxString
*)pVariable
;
412 // we can't just dump wxString directly as it could be corrupted or
413 // invalid and it could also be locked for writing (i.e. if we're
414 // between GetWriteBuf() and UngetWriteBuf() calls) and assert when we
415 // try to access it contents using public methods, so instead use our
416 // knowledge of its internals
417 const wxChar
*p
= NULL
;
418 if ( !::IsBadReadPtr(ps
, sizeof(wxString
)) )
421 wxStringData
*data
= (wxStringData
*)p
- 1;
422 if ( ::IsBadReadPtr(data
, sizeof(wxStringData
)) ||
423 ::IsBadReadPtr(p
, sizeof(wxChar
*)*data
->nAllocLength
) )
425 p
= NULL
; // don't touch this pointer with 10 feet pole
429 s
<< wxT("(\"") << (p
? p
: wxT("???")) << wxT(")\"");
431 else // any other UDT
432 #endif // !wxUSE_STD_STRING
434 // Determine how many children this type has.
435 DWORD dwChildrenCount
= 0;
436 DoGetTypeInfo(pSym
, TI_GET_CHILDRENCOUNT
, &dwChildrenCount
);
438 // Prepare to get an array of "TypeIds", representing each of the children.
439 TI_FINDCHILDREN_PARAMS
*children
= (TI_FINDCHILDREN_PARAMS
*)
440 malloc(sizeof(TI_FINDCHILDREN_PARAMS
) +
441 (dwChildrenCount
- 1)*sizeof(ULONG
));
445 children
->Count
= dwChildrenCount
;
448 // Get the array of TypeIds, one for each child type
449 if ( !DoGetTypeInfo(pSym
, TI_FINDCHILDREN
, children
) )
457 // Iterate through all children
460 sym
.ModBase
= pSym
->ModBase
;
461 for ( unsigned i
= 0; i
< dwChildrenCount
; i
++ )
463 sym
.TypeIndex
= children
->ChildId
[i
];
465 // children here are in lexicographic sense, i.e. we get all our nested
466 // classes and not only our member fields, but we can't get the values
467 // for the members of the nested classes, of course!
469 if ( DoGetTypeInfo(&sym
, TI_GET_NESTED
, &nested
) && nested
)
472 // avoid infinite recursion: this does seem to happen sometimes with
473 // complex typedefs...
474 if ( sym
.TypeIndex
== pSym
->TypeIndex
)
477 s
+= DumpField(&sym
, pVariable
, level
+ 1);
482 s
<< wxString(wxT('\t'), level
+ 1) << wxT('}');
489 wxDbgHelpDLL::SymbolTag
490 wxDbgHelpDLL::DereferenceSymbol(PSYMBOL_INFO pSym
, void **ppData
)
492 SymbolTag tag
= SYMBOL_TAG_NULL
;
495 if ( !DoGetTypeInfo(pSym
, TI_GET_SYMTAG
, &tag
) )
498 if ( tag
!= SYMBOL_TAG_POINTER_TYPE
)
502 if ( !DoGetTypeInfo(pSym
, TI_GET_TYPEID
, &tiNew
) ||
503 tiNew
== pSym
->TypeIndex
)
506 pSym
->TypeIndex
= tiNew
;
508 // remove one level of indirection except for the char strings: we want
509 // to dump "char *" and not a single "char" for them
510 if ( ppData
&& *ppData
&& GetBasicType(pSym
) != BASICTYPE_CHAR
)
512 DWORD_PTR
*pData
= (DWORD_PTR
*)*ppData
;
514 if ( ::IsBadReadPtr(pData
, sizeof(DWORD_PTR
*)) )
519 *ppData
= (void *)*pData
;
526 /* static */ wxString
527 wxDbgHelpDLL::DumpSymbol(PSYMBOL_INFO pSym
, void *pVariable
)
530 SYMBOL_INFO symDeref
= *pSym
;
531 switch ( DereferenceSymbol(&symDeref
, &pVariable
) )
534 // Suppress gcc warnings about unhandled enum values, don't assert
535 // to avoid problems during fatal crash generation.
539 // show UDT recursively
540 s
= DumpUDT(&symDeref
, pVariable
);
543 case SYMBOL_TAG_BASE_TYPE
:
544 // variable of simple type, show directly
545 BasicType bt
= GetBasicType(&symDeref
);
548 s
= DumpBaseType(bt
, pSym
->Size
, pVariable
);
556 // ----------------------------------------------------------------------------
558 // ----------------------------------------------------------------------------
560 // this code is very useful when debugging debughlp.dll-related code but
561 // probably not worth having compiled in normally, please do not remove it!
564 static wxString
TagString(wxDbgHelpDLL::SymbolTag tag
)
566 static const wxChar
*tags
[] =
571 wxT("compiland details"),
572 wxT("compiland env"),
578 wxT("public symbol"),
581 wxT("function type"),
588 wxT("function arg type"),
589 wxT("func debug start"),
590 wxT("func debug end"),
591 wxT("using namespace"),
601 wxCOMPILE_TIME_ASSERT( WXSIZEOF(tags
) == wxDbgHelpDLL::SYMBOL_TAG_MAX
,
602 SymbolTagStringMismatch
);
605 if ( tag
< WXSIZEOF(tags
) )
608 s
.Printf(wxT("unrecognized tag (%d)"), tag
);
613 static wxString
KindString(wxDbgHelpDLL::DataKind kind
)
615 static const wxChar
*kinds
[] =
625 wxT("static member"),
629 wxCOMPILE_TIME_ASSERT( WXSIZEOF(kinds
) == wxDbgHelpDLL::DATA_MAX
,
630 DataKindStringMismatch
);
633 if ( kind
< WXSIZEOF(kinds
) )
636 s
.Printf(wxT("unrecognized kind (%d)"), kind
);
641 static wxString
UdtKindString(wxDbgHelpDLL::UdtKind kind
)
643 static const wxChar
*kinds
[] =
650 wxCOMPILE_TIME_ASSERT( WXSIZEOF(kinds
) == wxDbgHelpDLL::UDT_MAX
,
651 UDTKindStringMismatch
);
654 if ( kind
< WXSIZEOF(kinds
) )
657 s
.Printf(wxT("unrecognized UDT (%d)"), kind
);
662 static wxString
TypeString(wxDbgHelpDLL::BasicType bt
)
664 static const wxChar
*types
[] =
700 wxCOMPILE_TIME_ASSERT( WXSIZEOF(types
) == wxDbgHelpDLL::BASICTYPE_MAX
,
701 BasicTypeStringMismatch
);
704 if ( bt
< WXSIZEOF(types
) )
708 s
.Printf(wxT("unrecognized type (%d)"), bt
);
713 // this function is meant to be called from under debugger to see the
714 // proprieties of the given type id
715 extern "C" void DumpTI(ULONG ti
)
717 SYMBOL_INFO sym
= { sizeof(SYMBOL_INFO
) };
718 sym
.ModBase
= 0x400000; // it's a constant under Win32
721 wxDbgHelpDLL::SymbolTag tag
= wxDbgHelpDLL::SYMBOL_TAG_NULL
;
722 DoGetTypeInfo(&sym
, TI_GET_SYMTAG
, &tag
);
723 DoGetTypeInfo(&sym
, TI_GET_TYPEID
, &ti
);
725 OutputDebugString(wxString::Format(wxT("Type 0x%x: "), sym
.TypeIndex
));
726 wxString name
= wxDbgHelpDLL::GetSymbolName(&sym
);
729 OutputDebugString(wxString::Format(wxT("name=\"%s\", "), name
.c_str()));
733 if ( !DoGetTypeInfo(&sym
, TI_GET_NESTED
, &nested
) )
738 OutputDebugString(wxString::Format(wxT("tag=%s%s"),
739 nested
? wxT("nested ") : wxEmptyString
,
740 TagString(tag
).c_str()));
741 if ( tag
== wxDbgHelpDLL::SYMBOL_TAG_UDT
)
743 wxDbgHelpDLL::UdtKind udtKind
;
744 if ( DoGetTypeInfo(&sym
, TI_GET_UDTKIND
, &udtKind
) )
746 OutputDebugString(wxT(" (") + UdtKindString(udtKind
) + wxT(')'));
750 wxDbgHelpDLL::DataKind kind
= wxDbgHelpDLL::DATA_UNKNOWN
;
751 if ( DoGetTypeInfo(&sym
, TI_GET_DATAKIND
, &kind
) )
753 OutputDebugString(wxString::Format(
754 wxT(", kind=%s"), KindString(kind
).c_str()));
755 if ( kind
== wxDbgHelpDLL::DATA_MEMBER
)
758 if ( DoGetTypeInfo(&sym
, TI_GET_OFFSET
, &ofs
) )
760 OutputDebugString(wxString::Format(wxT(" (ofs=0x%x)"), ofs
));
765 wxDbgHelpDLL::BasicType bt
= GetBasicType(&sym
);
768 OutputDebugString(wxString::Format(wxT(", type=%s"),
769 TypeString(bt
).c_str()));
772 if ( ti
!= sym
.TypeIndex
)
774 OutputDebugString(wxString::Format(wxT(", next ti=0x%x"), ti
));
777 OutputDebugString(wxT("\r\n"));
782 #endif // wxUSE_DBGHELP