723 lines
14 KiB
C++
723 lines
14 KiB
C++
/*++
|
|
|
|
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: <Bad DbgMsg !!> " );
|
|
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
|