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)
7 // Copyright: (c) 2003-2005 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 #include "wx/wxprec.h"
25 #include "wx/msw/debughlp.h"
27 #if wxUSE_DBGHELP && wxUSE_DYNLIB_CLASS
29 // ----------------------------------------------------------------------------
31 // ----------------------------------------------------------------------------
33 // to prevent recursion which could result from corrupted data we limit
34 // ourselves to that many levels of embedded fields inside structs
35 static const unsigned MAX_DUMP_DEPTH
= 20;
37 // ----------------------------------------------------------------------------
39 // ----------------------------------------------------------------------------
41 // error message from Init()
42 static wxString gs_errMsg
;
44 // ============================================================================
45 // wxDbgHelpDLL implementation
46 // ============================================================================
48 // ----------------------------------------------------------------------------
50 // ----------------------------------------------------------------------------
52 #define DEFINE_SYM_FUNCTION(func, name) \
53 wxDbgHelpDLL::func ## _t wxDbgHelpDLL::func = 0
55 wxDO_FOR_ALL_SYM_FUNCS(DEFINE_SYM_FUNCTION
);
57 #undef DEFINE_SYM_FUNCTION
59 // ----------------------------------------------------------------------------
60 // initialization methods
61 // ----------------------------------------------------------------------------
63 // load all function we need from the DLL
65 static bool BindDbgHelpFunctions(const wxDynamicLibrary
& dllDbgHelp
)
67 #define LOAD_SYM_FUNCTION(func, name) \
68 wxDbgHelpDLL::func = (wxDbgHelpDLL::func ## _t) \
69 dllDbgHelp.GetSymbol(wxT(#name)); \
70 if ( !wxDbgHelpDLL::func ) \
72 gs_errMsg += wxT("Function ") wxT(#name) wxT("() not found.\n"); \
76 wxDO_FOR_ALL_SYM_FUNCS(LOAD_SYM_FUNCTION
);
78 #undef LOAD_SYM_FUNCTION
83 // called by Init() if we hadn't done this before
86 wxDynamicLibrary
dllDbgHelp(wxT("dbghelp.dll"), wxDL_VERBATIM
);
87 if ( dllDbgHelp
.IsLoaded() )
89 if ( BindDbgHelpFunctions(dllDbgHelp
) )
91 // turn on default options
92 DWORD options
= wxDbgHelpDLL::SymGetOptions();
94 options
|= SYMOPT_DEFERRED_LOADS
| SYMOPT_UNDNAME
| SYMOPT_DEBUG
;
96 wxDbgHelpDLL::SymSetOptions(options
);
102 gs_errMsg
+= wxT("\nPlease update your dbghelp.dll version, ")
103 wxT("at least version 5.1 is needed!\n")
104 wxT("(if you already have a new version, please ")
105 wxT("put it in the same directory where the program is.)\n");
107 else // failed to load dbghelp.dll
109 gs_errMsg
+= wxT("Please install dbghelp.dll available free of charge ")
110 wxT("from Microsoft to get more detailed crash information!");
113 gs_errMsg
+= wxT("\nLatest dbghelp.dll is available at ")
114 wxT("http://www.microsoft.com/whdc/ddk/debugging/\n");
120 bool wxDbgHelpDLL::Init()
122 // this flag is -1 until Init() is called for the first time, then it's set
123 // to either false or true depending on whether we could load the functions
124 static int s_loaded
= -1;
126 if ( s_loaded
== -1 )
131 return s_loaded
!= 0;
134 // ----------------------------------------------------------------------------
136 // ----------------------------------------------------------------------------
139 const wxString
& wxDbgHelpDLL::GetErrorMessage()
145 void wxDbgHelpDLL::LogError(const wxChar
*func
)
147 ::OutputDebugString(wxString::Format(wxT("dbghelp: %s() failed: %s\r\n"),
148 func
, wxSysErrorMsg(::GetLastError())).t_str());
151 // ----------------------------------------------------------------------------
153 // ----------------------------------------------------------------------------
157 DoGetTypeInfo(DWORD64 base
, ULONG ti
, IMAGEHLP_SYMBOL_TYPE_INFO type
, void *rc
)
159 static HANDLE s_hProcess
= ::GetCurrentProcess();
161 return wxDbgHelpDLL::SymGetTypeInfo
173 DoGetTypeInfo(PSYMBOL_INFO pSym
, IMAGEHLP_SYMBOL_TYPE_INFO type
, void *rc
)
175 return DoGetTypeInfo(pSym
->ModBase
, pSym
->TypeIndex
, type
, rc
);
179 wxDbgHelpDLL::BasicType
GetBasicType(PSYMBOL_INFO pSym
)
181 wxDbgHelpDLL::BasicType bt
;
182 return DoGetTypeInfo(pSym
, TI_GET_BASETYPE
, &bt
)
184 : wxDbgHelpDLL::BASICTYPE_NOTYPE
;
188 wxString
wxDbgHelpDLL::GetSymbolName(PSYMBOL_INFO pSym
)
202 s
= wxConvCurrent
->cWC2WX(pwszTypeName
);
204 ::LocalFree(pwszTypeName
);
210 /* static */ wxString
211 wxDbgHelpDLL::DumpBaseType(BasicType bt
, DWORD64 length
, PVOID pAddress
)
218 if ( ::IsBadReadPtr(pAddress
, length
) != 0 )
229 const BYTE b
= *(PBYTE
)pAddress
;
231 if ( bt
== BASICTYPE_BOOL
)
232 s
= b
? wxT("true") : wxT("false");
234 s
.Printf(wxT("%#04x"), b
);
236 else if ( length
== 2 )
238 s
.Printf(bt
== BASICTYPE_UINT
? wxT("%#06x") : wxT("%d"),
241 else if ( length
== 4 )
243 bool handled
= false;
245 if ( bt
== BASICTYPE_FLOAT
)
247 s
.Printf(wxT("%f"), *(PFLOAT
)pAddress
);
251 else if ( bt
== BASICTYPE_CHAR
)
253 // don't take more than 32 characters of a string
254 static const size_t NUM_CHARS
= 64;
256 const char *pc
= *(PSTR
*)pAddress
;
257 if ( ::IsBadStringPtrA(pc
, NUM_CHARS
) == 0 )
260 for ( size_t n
= 0; n
< NUM_CHARS
&& *pc
; n
++, pc
++ )
272 // treat just as an opaque DWORD
273 s
.Printf(wxT("%#x"), *(PDWORD
)pAddress
);
276 else if ( length
== 8 )
278 if ( bt
== BASICTYPE_FLOAT
)
280 s
.Printf(wxT("%lf"), *(double *)pAddress
);
282 else // opaque 64 bit value
284 s
.Printf("%#" wxLongLongFmtSpec
"x", *(wxLongLong_t
*)pAddress
);
292 wxDbgHelpDLL::DumpField(PSYMBOL_INFO pSym
, void *pVariable
, unsigned level
)
296 // avoid infinite recursion
297 if ( level
> MAX_DUMP_DEPTH
)
302 SymbolTag tag
= SYMBOL_TAG_NULL
;
303 if ( !DoGetTypeInfo(pSym
, TI_GET_SYMTAG
, &tag
) )
311 case SYMBOL_TAG_BASE_CLASS
:
312 s
= DumpUDT(pSym
, pVariable
, level
);
315 case SYMBOL_TAG_DATA
:
320 else // valid location
322 wxDbgHelpDLL::DataKind kind
;
323 if ( !DoGetTypeInfo(pSym
, TI_GET_DATAKIND
, &kind
) ||
324 kind
!= DATA_MEMBER
)
326 // maybe it's a static member? we're not interested in them...
330 // get the offset of the child member, relative to its parent
332 if ( !DoGetTypeInfo(pSym
, TI_GET_OFFSET
, &ofs
) )
335 pVariable
= (void *)((DWORD_PTR
)pVariable
+ ofs
);
338 // now pass to the type representing the type of this member
339 SYMBOL_INFO sym
= *pSym
;
340 if ( !DoGetTypeInfo(pSym
, TI_GET_TYPEID
, &sym
.TypeIndex
) )
344 DoGetTypeInfo(&sym
, TI_GET_LENGTH
, &size
);
346 switch ( DereferenceSymbol(&sym
, &pVariable
) )
348 case SYMBOL_TAG_BASE_TYPE
:
350 BasicType bt
= GetBasicType(&sym
);
353 s
= DumpBaseType(bt
, size
, pVariable
);
359 case SYMBOL_TAG_BASE_CLASS
:
360 s
= DumpUDT(&sym
, pVariable
, level
);
364 // Suppress gcc warnings about unhandled enum values.
371 s
= GetSymbolName(pSym
) + wxT(" = ") + s
;
376 // Suppress gcc warnings about unhandled enum values, don't assert
377 // to avoid problems during fatal crash generation.
383 s
= wxString(wxT('\t'), level
+ 1) + s
+ wxT('\n');
389 /* static */ wxString
390 wxDbgHelpDLL::DumpUDT(PSYMBOL_INFO pSym
, void *pVariable
, unsigned level
)
394 // we have to limit the depth of UDT dumping as otherwise we get in
395 // infinite loops trying to dump linked lists... 10 levels seems quite
396 // reasonable, full information is in minidump file anyhow
401 s
= GetSymbolName(pSym
);
403 #if !wxUSE_STD_STRING
404 // special handling for ubiquitous wxString: although the code below works
405 // for it as well, it shows the wxStringBase class and takes 4 lines
406 // instead of only one as this branch
407 if ( s
== wxT("wxString") )
409 wxString
*ps
= (wxString
*)pVariable
;
411 // we can't just dump wxString directly as it could be corrupted or
412 // invalid and it could also be locked for writing (i.e. if we're
413 // between GetWriteBuf() and UngetWriteBuf() calls) and assert when we
414 // try to access it contents using public methods, so instead use our
415 // knowledge of its internals
416 const wxChar
*p
= NULL
;
417 if ( !::IsBadReadPtr(ps
, sizeof(wxString
)) )
420 wxStringData
*data
= (wxStringData
*)p
- 1;
421 if ( ::IsBadReadPtr(data
, sizeof(wxStringData
)) ||
422 ::IsBadReadPtr(p
, sizeof(wxChar
*)*data
->nAllocLength
) )
424 p
= NULL
; // don't touch this pointer with 10 feet pole
428 s
<< wxT("(\"") << (p
? p
: wxT("???")) << wxT(")\"");
430 else // any other UDT
431 #endif // !wxUSE_STD_STRING
433 // Determine how many children this type has.
434 DWORD dwChildrenCount
= 0;
435 DoGetTypeInfo(pSym
, TI_GET_CHILDRENCOUNT
, &dwChildrenCount
);
437 // Prepare to get an array of "TypeIds", representing each of the children.
438 TI_FINDCHILDREN_PARAMS
*children
= (TI_FINDCHILDREN_PARAMS
*)
439 malloc(sizeof(TI_FINDCHILDREN_PARAMS
) +
440 (dwChildrenCount
- 1)*sizeof(ULONG
));
444 children
->Count
= dwChildrenCount
;
447 // Get the array of TypeIds, one for each child type
448 if ( !DoGetTypeInfo(pSym
, TI_FINDCHILDREN
, children
) )
456 // Iterate through all children
459 sym
.ModBase
= pSym
->ModBase
;
460 for ( unsigned i
= 0; i
< dwChildrenCount
; i
++ )
462 sym
.TypeIndex
= children
->ChildId
[i
];
464 // children here are in lexicographic sense, i.e. we get all our nested
465 // classes and not only our member fields, but we can't get the values
466 // for the members of the nested classes, of course!
468 if ( DoGetTypeInfo(&sym
, TI_GET_NESTED
, &nested
) && nested
)
471 // avoid infinite recursion: this does seem to happen sometimes with
472 // complex typedefs...
473 if ( sym
.TypeIndex
== pSym
->TypeIndex
)
476 s
+= DumpField(&sym
, pVariable
, level
+ 1);
481 s
<< wxString(wxT('\t'), level
+ 1) << wxT('}');
488 wxDbgHelpDLL::SymbolTag
489 wxDbgHelpDLL::DereferenceSymbol(PSYMBOL_INFO pSym
, void **ppData
)
491 SymbolTag tag
= SYMBOL_TAG_NULL
;
494 if ( !DoGetTypeInfo(pSym
, TI_GET_SYMTAG
, &tag
) )
497 if ( tag
!= SYMBOL_TAG_POINTER_TYPE
)
501 if ( !DoGetTypeInfo(pSym
, TI_GET_TYPEID
, &tiNew
) ||
502 tiNew
== pSym
->TypeIndex
)
505 pSym
->TypeIndex
= tiNew
;
507 // remove one level of indirection except for the char strings: we want
508 // to dump "char *" and not a single "char" for them
509 if ( ppData
&& *ppData
&& GetBasicType(pSym
) != BASICTYPE_CHAR
)
511 DWORD_PTR
*pData
= (DWORD_PTR
*)*ppData
;
513 if ( ::IsBadReadPtr(pData
, sizeof(DWORD_PTR
*)) )
518 *ppData
= (void *)*pData
;
525 /* static */ wxString
526 wxDbgHelpDLL::DumpSymbol(PSYMBOL_INFO pSym
, void *pVariable
)
529 SYMBOL_INFO symDeref
= *pSym
;
530 switch ( DereferenceSymbol(&symDeref
, &pVariable
) )
533 // Suppress gcc warnings about unhandled enum values, don't assert
534 // to avoid problems during fatal crash generation.
538 // show UDT recursively
539 s
= DumpUDT(&symDeref
, pVariable
);
542 case SYMBOL_TAG_BASE_TYPE
:
543 // variable of simple type, show directly
544 BasicType bt
= GetBasicType(&symDeref
);
547 s
= DumpBaseType(bt
, pSym
->Size
, pVariable
);
555 // ----------------------------------------------------------------------------
557 // ----------------------------------------------------------------------------
559 // this code is very useful when debugging debughlp.dll-related code but
560 // probably not worth having compiled in normally, please do not remove it!
563 static wxString
TagString(wxDbgHelpDLL::SymbolTag tag
)
565 static const wxChar
*tags
[] =
570 wxT("compiland details"),
571 wxT("compiland env"),
577 wxT("public symbol"),
580 wxT("function type"),
587 wxT("function arg type"),
588 wxT("func debug start"),
589 wxT("func debug end"),
590 wxT("using namespace"),
600 wxCOMPILE_TIME_ASSERT( WXSIZEOF(tags
) == wxDbgHelpDLL::SYMBOL_TAG_MAX
,
601 SymbolTagStringMismatch
);
604 if ( tag
< WXSIZEOF(tags
) )
607 s
.Printf(wxT("unrecognized tag (%d)"), tag
);
612 static wxString
KindString(wxDbgHelpDLL::DataKind kind
)
614 static const wxChar
*kinds
[] =
624 wxT("static member"),
628 wxCOMPILE_TIME_ASSERT( WXSIZEOF(kinds
) == wxDbgHelpDLL::DATA_MAX
,
629 DataKindStringMismatch
);
632 if ( kind
< WXSIZEOF(kinds
) )
635 s
.Printf(wxT("unrecognized kind (%d)"), kind
);
640 static wxString
UdtKindString(wxDbgHelpDLL::UdtKind kind
)
642 static const wxChar
*kinds
[] =
649 wxCOMPILE_TIME_ASSERT( WXSIZEOF(kinds
) == wxDbgHelpDLL::UDT_MAX
,
650 UDTKindStringMismatch
);
653 if ( kind
< WXSIZEOF(kinds
) )
656 s
.Printf(wxT("unrecognized UDT (%d)"), kind
);
661 static wxString
TypeString(wxDbgHelpDLL::BasicType bt
)
663 static const wxChar
*types
[] =
699 wxCOMPILE_TIME_ASSERT( WXSIZEOF(types
) == wxDbgHelpDLL::BASICTYPE_MAX
,
700 BasicTypeStringMismatch
);
703 if ( bt
< WXSIZEOF(types
) )
707 s
.Printf(wxT("unrecognized type (%d)"), bt
);
712 // this function is meant to be called from under debugger to see the
713 // proprieties of the given type id
714 extern "C" void DumpTI(ULONG ti
)
716 SYMBOL_INFO sym
= { sizeof(SYMBOL_INFO
) };
717 sym
.ModBase
= 0x400000; // it's a constant under Win32
720 wxDbgHelpDLL::SymbolTag tag
= wxDbgHelpDLL::SYMBOL_TAG_NULL
;
721 DoGetTypeInfo(&sym
, TI_GET_SYMTAG
, &tag
);
722 DoGetTypeInfo(&sym
, TI_GET_TYPEID
, &ti
);
724 OutputDebugString(wxString::Format(wxT("Type 0x%x: "), sym
.TypeIndex
));
725 wxString name
= wxDbgHelpDLL::GetSymbolName(&sym
);
728 OutputDebugString(wxString::Format(wxT("name=\"%s\", "), name
.c_str()));
732 if ( !DoGetTypeInfo(&sym
, TI_GET_NESTED
, &nested
) )
737 OutputDebugString(wxString::Format(wxT("tag=%s%s"),
738 nested
? wxT("nested ") : wxEmptyString
,
739 TagString(tag
).c_str()));
740 if ( tag
== wxDbgHelpDLL::SYMBOL_TAG_UDT
)
742 wxDbgHelpDLL::UdtKind udtKind
;
743 if ( DoGetTypeInfo(&sym
, TI_GET_UDTKIND
, &udtKind
) )
745 OutputDebugString(wxT(" (") + UdtKindString(udtKind
) + wxT(')'));
749 wxDbgHelpDLL::DataKind kind
= wxDbgHelpDLL::DATA_UNKNOWN
;
750 if ( DoGetTypeInfo(&sym
, TI_GET_DATAKIND
, &kind
) )
752 OutputDebugString(wxString::Format(
753 wxT(", kind=%s"), KindString(kind
).c_str()));
754 if ( kind
== wxDbgHelpDLL::DATA_MEMBER
)
757 if ( DoGetTypeInfo(&sym
, TI_GET_OFFSET
, &ofs
) )
759 OutputDebugString(wxString::Format(wxT(" (ofs=0x%x)"), ofs
));
764 wxDbgHelpDLL::BasicType bt
= GetBasicType(&sym
);
767 OutputDebugString(wxString::Format(wxT(", type=%s"),
768 TypeString(bt
).c_str()));
771 if ( ti
!= sym
.TypeIndex
)
773 OutputDebugString(wxString::Format(wxT(", next ti=0x%x"), ti
));
776 OutputDebugString(wxT("\r\n"));
781 #endif // wxUSE_DBGHELP