/*++ Copyright (c) 1994 Microsoft Corporation All rights reserved. Module Name: Debug.cxx Abstract: Debug support Author: Albert Ting (AlbertT) 28-May-1994 Revision History: --*/ #include "spllibp.hxx" #pragma hdrstop #include "trace.hxx" #define DEFAULT_TRACE_TYPE TBackTraceMem //#define DEFAULT_TRACE_TYPE TBackTraceFile // For tracing to file. #define DEFAULT_MEM_TRACE_TYPE TBackTraceMem extern HANDLE ghMemHeap; extern HANDLE ghDbgMemHeap; extern pfCreateThread gpfSafeCreateThread; #if DBG UINT gLogFilter = (UINT)-1; VBackTrace* gpbtErrLog; VBackTrace* gpbtTraceLog; MODULE_DEBUG_INIT( DBG_ERROR, DBG_ERROR ); DBG_POINTERS gDbgPointers; PDBG_POINTERS gpDbgPointers; extern VBackTrace* gpbtAlloc; extern VBackTrace* gpbtFree; /******************************************************************** Single thread checking. This is used to verify that a set of functions are called from only one thread. This is for debugging purposes only. ********************************************************************/ VOID vDbgSingleThread( PDWORD pdwThreadId ) { EnterCriticalSection( &gcsBackTrace ); if (!*pdwThreadId) { *pdwThreadId = (DWORD)GetCurrentThreadId(); } SPLASSERT( *pdwThreadId == (DWORD)GetCurrentThreadId() ); LeaveCriticalSection( &gcsBackTrace ); } VOID vDbgSingleThreadReset( PDWORD pdwThreadId ) { *pdwThreadId = 0; } VOID vDbgSingleThreadNot( PDWORD pdwThreadId ) { SPLASSERT( *pdwThreadId != (DWORD)GetCurrentThreadId() ); } /******************************************************************** TStatus automated error logging and codepath testing. ********************************************************************/ TStatusBase& TStatusBase:: pNoChk( VOID ) { _pszFileA = NULL; return (TStatusBase&)*this; } TStatusBase& TStatusBase:: pSetInfo( UINT uDbg, UINT uLine, LPCSTR pszFileA, LPCSTR pszModuleA ) { _uDbg = uDbg; _uLine = uLine; _pszFileA = pszFileA; SPLASSERT( pszFileA ); _pszModuleA = pszModuleA; return (TStatusBase&)*this; } DWORD TStatus:: dwGetStatus( VOID ) { // // For now, return error code. Later it will return the actual // error code. // return _dwStatus; } DWORD TStatusBase:: operator=( DWORD dwStatus ) { // // Check if we have an error, and it's not one of the two // accepted "safe" errors. // // If pszFileA is not set, then we can safely ignore the // error as one the client intended. // if( _pszFileA && dwStatus != ERROR_SUCCESS && dwStatus != _dwStatusSafe1 && dwStatus != _dwStatusSafe2 && dwStatus != _dwStatusSafe3 ){ #ifdef DBGLOG // // An unexpected error occured. Log an error and continue. // vDbgLogError( _uDbg, _uDbgLevel, _uLine, _pszFileA, _pszModuleA, pszDbgAllocMsgA( "TStatus set to %d\nLine %d, %hs\n", dwStatus, _uLine, _pszFileA )); #else DBGMSG( DBG_WARN, ( "TStatus set to %d\nLine %d, %hs\n", dwStatus, _uLine, _pszFileA )); #endif } return _dwStatus = dwStatus; } /******************************************************************** Same, but for HRESULTs. ********************************************************************/ TStatusHBase& TStatusHBase:: pNoChk( VOID ) { _pszFileA = NULL; return (TStatusH&)*this; } TStatusHBase& TStatusHBase:: pSetInfo( UINT uDbg, UINT uLine, LPCSTR pszFileA, LPCSTR pszModuleA ) { _uDbg = uDbg; _uLine = uLine; _pszFileA = pszFileA; SPLASSERT( pszFileA ); _pszModuleA = pszModuleA; return (TStatusH&)*this; } HRESULT TStatusHBase:: operator=( HRESULT hrStatus ) { // // Check if we have an error, and it's not one of the two // accepted "safe" errors. // // If pszFileA is not set, then we can safely ignore the // error as one the client intended. // if( _pszFileA && FAILED(hrStatus) && hrStatus != _hrStatusSafe1 && hrStatus != _hrStatusSafe2 && hrStatus != _hrStatusSafe3 ){ #ifdef DBGLOG // // An unexpected error occured. Log an error and continue. // vDbgLogError( _uDbg, _uDbgLevel, _uLine, _pszFileA, _pszModuleA, pszDbgAllocMsgA( "TStatusH set to %x\nLine %d, %hs\n", hrStatus, _uLine, _pszFileA )); #else DBGMSG( DBG_WARN, ( "TStatusH set to %x\nLine %d, %hs\n", hrStatus, _uLine, _pszFileA )); #endif } return _hrStatus = hrStatus; } HRESULT TStatusH:: hrGetStatus( VOID ) { // // For now, return error code. Later it will return the actual // error code. // return _hrStatus; } /******************************************************************** Same, but for BOOLs. ********************************************************************/ TStatusBBase& TStatusBBase:: pNoChk( VOID ) { _pszFileA = NULL; return (TStatusBBase&)*this; } TStatusBBase& TStatusBBase:: pSetInfo( UINT uDbg, UINT uLine, LPCSTR pszFileA, LPCSTR pszModuleA ) { _uDbg = uDbg; _uLine = uLine; _pszFileA = pszFileA; SPLASSERT( pszFileA ); _pszModuleA = pszModuleA; return (TStatusBBase&)*this; } BOOL TStatusB:: bGetStatus( VOID ) { // // For now, return error code. Later it will return the actual // error code. // return _bStatus; } BOOL TStatusBBase:: operator=( BOOL bStatus ) { // // Check if we have an error, and it's not one of the two // accepted "safe" errors. // // If pszFileA is not set, then we can safely ignore the // error as one the client intended. // if( _pszFileA && !bStatus ){ DWORD dwLastError = GetLastError(); if( dwLastError != _dwStatusSafe1 && dwLastError != _dwStatusSafe2 && dwLastError != _dwStatusSafe3 ){ #ifdef DBGLOG // // An unexpected error occured. Log an error and continue. // vDbgLogError( _uDbg, _uDbgLevel, _uLine, _pszFileA, _pszModuleA, pszDbgAllocMsgA( "TStatusB set to FALSE, LastError = %d\nLine %d, %hs\n", GetLastError(), _uLine, _pszFileA )); #else DBGMSG( DBG_WARN, ( "TStatusB set to FALSE, LastError = %d\nLine %d, %hs\n", GetLastError(), _uLine, _pszFileA )); #endif } } return _bStatus = bStatus; } VOID vWarnInvalid( PVOID pvObject, UINT uDbg, UINT uLine, LPCSTR pszFileA, LPCSTR pszModuleA ) /*++ Routine Description: Warns that an object is invalid. Arguments: Return Value: --*/ { #if DBGLOG vDbgLogError( uDbg, DBG_WARN, uLine, pszFileA, pszModuleA, pszDbgAllocMsgA( "Invalid Object %x LastError = %d\nLine %d, %hs\n", (ULONG_PTR)pvObject, GetLastError(), uLine, pszFileA )); #else DBGMSG( DBG_WARN, ( "Invalid Object %x LastError = %d\nLine %d, %hs\n", (DWORD)pvObject, GetLastError(), uLine, pszFileA )); #endif } /******************************************************************** Generic Error logging package. ********************************************************************/ VOID DbgMsg( LPCSTR pszMsgFormat, ... ) { CHAR szMsgText[1024]; va_list vargs; va_start( vargs, pszMsgFormat ); wvsprintfA( szMsgText, pszMsgFormat, vargs ); va_end( vargs ); #ifndef DBGLOG // // Prefix the string if the first character isn't a space: // if( szMsgText[0] && szMsgText[0] != ' ' ){ OutputDebugStringA( MODULE ); } #endif OutputDebugStringA( szMsgText ); } #ifdef DBGLOG LPSTR pszDbgAllocMsgA( LPCSTR pszMsgFormatA, ... ) { CHAR szMsgTextA[1024]; UINT cbStr; LPSTR pszMsgA; va_list vargs; va_start(vargs, pszMsgFormatA); __try { wvsprintfA( szMsgTextA, pszMsgFormatA, vargs ); } __except(( GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION || GetExceptionCode() == EXCEPTION_DATATYPE_MISALIGNMENT) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { OutputDebugStringA( "SPL: " ); OutputDebugStringA( pszMsgFormatA ); } va_end(vargs); cbStr = ( lstrlenA( szMsgTextA ) + 1 ) * sizeof( szMsgTextA[0] ); pszMsgA = (LPSTR)DbgAllocMem( cbStr ); if( pszMsgA ){ CopyMemory( pszMsgA, szMsgTextA, cbStr ); } return pszMsgA; } VOID vDbgLogError( UINT uDbg, UINT uDbgLevel, UINT uLine, LPCSTR pszFileA, LPCSTR pszModuleA, LPCSTR pszMsgA ) { DWORD dwLastError = GetLastError(); VBackTrace* pBackTrace = gpbtTraceLog; if(( uDbgLevel & DBG_PRINT_MASK & uDbg ) && pszMsgA ){ if( !( uDbgLevel & DBG_NOHEAD )){ OutputDebugStringA( pszModuleA ); } OutputDebugStringA( pszMsgA ); } if(( uDbgLevel << DBG_BREAK_SHIFT ) & uDbg ){ DebugBreak(); } if( gLogFilter & uDbgLevel ) { // // Log the failure. // // // Capture significant errors in separate error log. // if( uDbgLevel & DBG_ERRLOG_CAPTURE ){ pBackTrace = gpbtErrLog; } if (pBackTrace) { pBackTrace->hCapture( (ULONG_PTR)pszMsgA, uLine | ( uDbgLevel << DBG_BREAK_SHIFT ), (ULONG_PTR)pszFileA ); } else { // // Backtracing is not enabled, free the message string that is // passed in. // if(pszMsgA) { DbgFreeMem((PVOID)pszMsgA); } } } else { // // Just free up the memory if this line is not captured // actually this happens when uDbgLevel == DBG_NONE (0) // if( pszMsgA ) { DbgFreeMem( (PVOID)pszMsgA ); } } SetLastError( dwLastError ); } #endif // def DBGLOG #endif // DBG /******************************************************************** Initialization ********************************************************************/ #if DBG BOOL bSplLibInit( pfCreateThread pfSafeCreateThread ) { BOOL bValid; bValid = (ghMemHeap = HeapCreate( 0, 1024*4, 0 )) && (ghDbgMemHeap = HeapCreate( 0, 1024*4, 0 )) && (VBackTrace::bInit( )) && #ifdef TRACE_ENABLED (gpbtAlloc = new DEFAULT_MEM_TRACE_TYPE) && (gpbtFree = new DEFAULT_MEM_TRACE_TYPE) && (gpbtErrLog = new DEFAULT_TRACE_TYPE( VBackTrace::kString )) && (gpbtTraceLog = new DEFAULT_TRACE_TYPE( VBackTrace::kString )) && #endif (MRefCom::gpcsCom = new MCritSec) && MRefCom::gpcsCom->bValid(); gpfSafeCreateThread = ( pfSafeCreateThread ) ? pfSafeCreateThread : CreateThread; if( bValid ){ gDbgPointers.pfnAllocBackTrace = &DbgAllocBackTrace; gDbgPointers.pfnAllocBackTraceMem = &DbgAllocBackTraceMem; gDbgPointers.pfnAllocBackTraceFile = &DbgAllocBackTraceFile; gDbgPointers.pfnFreeBackTrace = &DbgFreeBackTrace; gDbgPointers.pfnCaptureBackTrace = &DbgCaptureBackTrace; gDbgPointers.pfnAllocCritSec = &DbgAllocCritSec; gDbgPointers.pfnFreeCritSec = &DbgFreeCritSec; gDbgPointers.pfnInsideCritSec = &DbgInsideCritSec; gDbgPointers.pfnOutsideCritSec = &DbgOutsideCritSec; gDbgPointers.pfnEnterCritSec = &DbgEnterCritSec; gDbgPointers.pfnLeaveCritSec = &DbgLeaveCritSec; gDbgPointers.pfnSetAllocFail = &DbgSetAllocFail; gDbgPointers.hMemHeap = ghMemHeap; gDbgPointers.hDbgMemHeap = ghDbgMemHeap; gDbgPointers.pbtAlloc = gpbtAlloc; gDbgPointers.pbtFree = gpbtFree; gDbgPointers.pbtErrLog = gpbtErrLog; gDbgPointers.pbtTraceLog = gpbtTraceLog; gpDbgPointers = &gDbgPointers; } return bValid; } VOID vSplLibFree( VOID ) { SPLASSERT( MRefCom::gpcsCom->bOutside( )); delete MRefCom::gpcsCom; VBackTrace::vDone(); if (ghMemHeap) { HeapDestroy( ghMemHeap ); ghMemHeap = NULL; } if (ghDbgMemHeap) { HeapDestroy( ghDbgMemHeap ); ghDbgMemHeap = NULL; } } #else BOOL bSplLibInit( pfCreateThread pfSafeCreateThread ) { gpfSafeCreateThread = ( pfSafeCreateThread ) ? pfSafeCreateThread : CreateThread; return ( ghMemHeap = HeapCreate( 0, 1024*4, 0 )) ? TRUE : FALSE; } VOID vSplLibFree( VOID ) { if (ghMemHeap) { HeapDestroy( ghMemHeap ); ghMemHeap = NULL; } } #endif /******************************************************************** Stub these out so non-debug builds will find them. ********************************************************************/ #if !DBG #ifdef DBGLOG LPSTR pszDbgAllocMsgA( LPCSTR pszMsgFormatA, ... ) { return NULL; } VOID vDbgLogError( UINT uDbg, UINT uDbgLevel, UINT uLine, LPCSTR pszFileA, LPCSTR pszModuleA, LPCSTR pszMsgA ) { } #else VOID vDbgMsg2( LPCTSTR pszMsgFormat, ... ) { } #endif // ndef DBGLOG #endif // !DBG