1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        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) 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(name)                                           \ 
  68         wxDbgHelpDLL::name = (wxDbgHelpDLL::name ## _t)                       \ 
  69                                 dllDbgHelp.GetSymbol(_T(#name));              \ 
  70         if ( !wxDbgHelpDLL::name )                                            \ 
  72             gs_errMsg += _T("Function ") _T(#name) _T("() 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(_T("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 
+= _T("\nPlease update your dbghelp.dll version, ") 
 103                      _T("at least version 5.1 is needed!\n") 
 104                      _T("(if you already have a new version, please ") 
 105                      _T("put it in the same directory where the program is.)\n"); 
 107     else // failed to load dbghelp.dll 
 109         gs_errMsg 
+= _T("Please install dbghelp.dll available free of charge ") 
 110                      _T("from Microsoft to get more detailed crash information!"); 
 113     gs_errMsg 
+= _T("\nLatest dbghelp.dll is available at ") 
 114                  _T("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(_T("dbghelp: %s() failed: %s\r\n"), 
 148                         func
, wxSysErrorMsg(::GetLastError())).wx_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 
? _T("true") : _T("false"); 
 234             s
.Printf(_T("%#04x"), b
); 
 236     else if ( length 
== 2 ) 
 238         s
.Printf(bt 
== BASICTYPE_UINT 
? _T("%#06x") : _T("%d"), 
 241     else if ( length 
== 4 ) 
 243         bool handled 
= false; 
 245         if ( bt 
== BASICTYPE_FLOAT 
) 
 247             s
.Printf(_T("%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(_T("%#x"), *(PDWORD
)pAddress
); 
 276     else if ( length 
== 8 ) 
 278         if ( bt 
== BASICTYPE_FLOAT 
) 
 280             s
.Printf(_T("%lf"), *(double *)pAddress
); 
 282         else // opaque 64 bit value 
 284             s
.Printf(_T("%#" wxLongLongFmtSpec 
_T("x")), *(PDWORD 
*)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
); 
 367                 s 
= GetSymbolName(pSym
) + _T(" = ") + s
; 
 374         s 
= wxString(_T('\t'), level 
+ 1) + s 
+ _T('\n'); 
 380 /* static */ wxString
 
 381 wxDbgHelpDLL::DumpUDT(PSYMBOL_INFO pSym
, void *pVariable
, unsigned level
) 
 385     // we have to limit the depth of UDT dumping as otherwise we get in 
 386     // infinite loops trying to dump linked lists... 10 levels seems quite 
 387     // reasonable, full information is in minidump file anyhow 
 392     s 
= GetSymbolName(pSym
); 
 395     // special handling for ubiquitous wxString: although the code below works 
 396     // for it as well, it shows the wxStringBase class and takes 4 lines 
 397     // instead of only one as this branch 
 398     if ( s 
== _T("wxString") ) 
 400         wxString 
*ps 
= (wxString 
*)pVariable
; 
 402         // we can't just dump wxString directly as it could be corrupted or 
 403         // invalid and it could also be locked for writing (i.e. if we're 
 404         // between GetWriteBuf() and UngetWriteBuf() calls) and assert when we 
 405         // try to access it contents using public methods, so instead use our 
 406         // knowledge of its internals 
 407         const wxChar 
*p 
= NULL
; 
 408         if ( !::IsBadReadPtr(ps
, sizeof(wxString
)) ) 
 411             wxStringData 
*data 
= (wxStringData 
*)p 
- 1; 
 412             if ( ::IsBadReadPtr(data
, sizeof(wxStringData
)) || 
 413                     ::IsBadReadPtr(p
, sizeof(wxChar 
*)*data
->nAllocLength
) ) 
 415                 p 
= NULL
; // don't touch this pointer with 10 feet pole 
 419         s 
<< _T("(\"") << (p 
? p 
: _T("???")) << _T(")\""); 
 421     else // any other UDT 
 424         // Determine how many children this type has. 
 425         DWORD dwChildrenCount 
= 0; 
 426         DoGetTypeInfo(pSym
, TI_GET_CHILDRENCOUNT
, &dwChildrenCount
); 
 428         // Prepare to get an array of "TypeIds", representing each of the children. 
 429         TI_FINDCHILDREN_PARAMS 
*children 
= (TI_FINDCHILDREN_PARAMS 
*) 
 430             malloc(sizeof(TI_FINDCHILDREN_PARAMS
) + 
 431                         (dwChildrenCount 
- 1)*sizeof(ULONG
)); 
 435         children
->Count 
= dwChildrenCount
; 
 438         // Get the array of TypeIds, one for each child type 
 439         if ( !DoGetTypeInfo(pSym
, TI_FINDCHILDREN
, children
) ) 
 447         // Iterate through all children 
 450         sym
.ModBase 
= pSym
->ModBase
; 
 451         for ( unsigned i 
= 0; i 
< dwChildrenCount
; i
++ ) 
 453             sym
.TypeIndex 
= children
->ChildId
[i
]; 
 455             // children here are in lexicographic sense, i.e. we get all our nested 
 456             // classes and not only our member fields, but we can't get the values 
 457             // for the members of the nested classes, of course! 
 459             if ( DoGetTypeInfo(&sym
, TI_GET_NESTED
, &nested
) && nested 
) 
 462             // avoid infinite recursion: this does seem to happen sometimes with 
 463             // complex typedefs... 
 464             if ( sym
.TypeIndex 
== pSym
->TypeIndex 
) 
 467             s 
+= DumpField(&sym
, pVariable
, level 
+ 1); 
 472         s 
<< wxString(_T('\t'), level 
+ 1) << _T('}'); 
 479 wxDbgHelpDLL::SymbolTag
 
 480 wxDbgHelpDLL::DereferenceSymbol(PSYMBOL_INFO pSym
, void **ppData
) 
 482     SymbolTag tag 
= SYMBOL_TAG_NULL
; 
 485         if ( !DoGetTypeInfo(pSym
, TI_GET_SYMTAG
, &tag
) ) 
 488         if ( tag 
!= SYMBOL_TAG_POINTER_TYPE 
) 
 492         if ( !DoGetTypeInfo(pSym
, TI_GET_TYPEID
, &tiNew
) || 
 493                 tiNew 
== pSym
->TypeIndex 
) 
 496         pSym
->TypeIndex 
= tiNew
; 
 498         // remove one level of indirection except for the char strings: we want 
 499         // to dump "char *" and not a single "char" for them 
 500         if ( ppData 
&& *ppData 
&& GetBasicType(pSym
) != BASICTYPE_CHAR 
) 
 502             DWORD_PTR 
*pData 
= (DWORD_PTR 
*)*ppData
; 
 504             if ( ::IsBadReadPtr(pData
, sizeof(DWORD_PTR 
*)) ) 
 509             *ppData 
= (void *)*pData
; 
 516 /* static */ wxString
 
 517 wxDbgHelpDLL::DumpSymbol(PSYMBOL_INFO pSym
, void *pVariable
) 
 520     SYMBOL_INFO symDeref 
= *pSym
; 
 521     switch ( DereferenceSymbol(&symDeref
, &pVariable
) ) 
 524             // show UDT recursively 
 525             s 
= DumpUDT(&symDeref
, pVariable
); 
 528         case SYMBOL_TAG_BASE_TYPE
: 
 529             // variable of simple type, show directly 
 530             BasicType bt 
= GetBasicType(&symDeref
); 
 533                 s 
= DumpBaseType(bt
, pSym
->Size
, pVariable
); 
 541 // ---------------------------------------------------------------------------- 
 543 // ---------------------------------------------------------------------------- 
 545 // this code is very useful when debugging debughlp.dll-related code but 
 546 // probably not worth having compiled in normally, please do not remove it! 
 549 static wxString 
TagString(wxDbgHelpDLL::SymbolTag tag
) 
 551     static const wxChar 
*tags
[] = 
 556         _T("compiland details"), 
 573         _T("function arg type"), 
 574         _T("func debug start"), 
 575         _T("func debug end"), 
 576         _T("using namespace"), 
 586     wxCOMPILE_TIME_ASSERT( WXSIZEOF(tags
) == wxDbgHelpDLL::SYMBOL_TAG_MAX
, 
 587                                 SymbolTagStringMismatch 
); 
 590     if ( tag 
< WXSIZEOF(tags
) ) 
 593         s
.Printf(_T("unrecognized tag (%d)"), tag
); 
 598 static wxString 
KindString(wxDbgHelpDLL::DataKind kind
) 
 600     static const wxChar 
*kinds
[] = 
 614     wxCOMPILE_TIME_ASSERT( WXSIZEOF(kinds
) == wxDbgHelpDLL::DATA_MAX
, 
 615                                 DataKindStringMismatch 
); 
 618     if ( kind 
< WXSIZEOF(kinds
) ) 
 621         s
.Printf(_T("unrecognized kind (%d)"), kind
); 
 626 static wxString 
UdtKindString(wxDbgHelpDLL::UdtKind kind
) 
 628     static const wxChar 
*kinds
[] = 
 635     wxCOMPILE_TIME_ASSERT( WXSIZEOF(kinds
) == wxDbgHelpDLL::UDT_MAX
, 
 636                                 UDTKindStringMismatch 
); 
 639     if ( kind 
< WXSIZEOF(kinds
) ) 
 642         s
.Printf(_T("unrecognized UDT (%d)"), kind
); 
 647 static wxString 
TypeString(wxDbgHelpDLL::BasicType bt
) 
 649     static const wxChar 
*types
[] = 
 685     wxCOMPILE_TIME_ASSERT( WXSIZEOF(types
) == wxDbgHelpDLL::BASICTYPE_MAX
, 
 686                                 BasicTypeStringMismatch 
); 
 689     if ( bt 
< WXSIZEOF(types
) ) 
 693         s
.Printf(_T("unrecognized type (%d)"), bt
); 
 698 // this function is meant to be called from under debugger to see the 
 699 // proprieties of the given type id 
 700 extern "C" void DumpTI(ULONG ti
) 
 702     SYMBOL_INFO sym 
= { sizeof(SYMBOL_INFO
) }; 
 703     sym
.ModBase 
= 0x400000; // it's a constant under Win32 
 706     wxDbgHelpDLL::SymbolTag tag 
= wxDbgHelpDLL::SYMBOL_TAG_NULL
; 
 707     DoGetTypeInfo(&sym
, TI_GET_SYMTAG
, &tag
); 
 708     DoGetTypeInfo(&sym
, TI_GET_TYPEID
, &ti
); 
 710     OutputDebugString(wxString::Format(_T("Type 0x%x: "), sym
.TypeIndex
)); 
 711     wxString name 
= wxDbgHelpDLL::GetSymbolName(&sym
); 
 714         OutputDebugString(wxString::Format(_T("name=\"%s\", "), name
.c_str())); 
 718     if ( !DoGetTypeInfo(&sym
, TI_GET_NESTED
, &nested
) ) 
 723     OutputDebugString(wxString::Format(_T("tag=%s%s"), 
 724                       nested 
? _T("nested ") : wxEmptyString
, 
 725                       TagString(tag
).c_str())); 
 726     if ( tag 
== wxDbgHelpDLL::SYMBOL_TAG_UDT 
) 
 728         wxDbgHelpDLL::UdtKind udtKind
; 
 729         if ( DoGetTypeInfo(&sym
, TI_GET_UDTKIND
, &udtKind
) ) 
 731             OutputDebugString(_T(" (") + UdtKindString(udtKind
) + _T(')')); 
 735     wxDbgHelpDLL::DataKind kind 
= wxDbgHelpDLL::DATA_UNKNOWN
; 
 736     if ( DoGetTypeInfo(&sym
, TI_GET_DATAKIND
, &kind
) ) 
 738         OutputDebugString(wxString::Format( 
 739             _T(", kind=%s"), KindString(kind
).c_str())); 
 740         if ( kind 
== wxDbgHelpDLL::DATA_MEMBER 
) 
 743             if ( DoGetTypeInfo(&sym
, TI_GET_OFFSET
, &ofs
) ) 
 745                 OutputDebugString(wxString::Format(_T(" (ofs=0x%x)"), ofs
)); 
 750     wxDbgHelpDLL::BasicType bt 
= GetBasicType(&sym
); 
 753         OutputDebugString(wxString::Format(_T(", type=%s"), 
 754                                 TypeString(bt
).c_str())); 
 757     if ( ti 
!= sym
.TypeIndex 
) 
 759         OutputDebugString(wxString::Format(_T(", next ti=0x%x"), ti
)); 
 762     OutputDebugString(_T("\r\n")); 
 767 #endif // wxUSE_DBGHELP