You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
630 lines
15 KiB
630 lines
15 KiB
/****************************************************************************
|
|
|
|
Copyright (c) Microsoft Corporation 1997
|
|
All rights reserved
|
|
|
|
File: DEBUG.CPP
|
|
|
|
Debugging utilities
|
|
|
|
***************************************************************************/
|
|
|
|
#include "pch.h"
|
|
#include <shlwapi.h>
|
|
|
|
DEFINE_MODULE("Debug");
|
|
|
|
#ifdef DEBUG
|
|
|
|
// Constants
|
|
#define DEBUG_OUTPUT_BUFFER_SIZE 512
|
|
|
|
// Globals
|
|
DWORD g_TraceMemoryIndex = 0;
|
|
DWORD g_dwCounter = 0;
|
|
DWORD g_dwTraceFlags = 0;
|
|
CRITICAL_SECTION g_DebugCS;
|
|
BOOL g_fDebugInitialized = FALSE;
|
|
|
|
// Statics
|
|
const TCHAR g_szNULL[] = TEXT("");
|
|
const TCHAR g_szTrue[] = TEXT("True");
|
|
const TCHAR g_szFalse[] = TEXT("False");
|
|
const TCHAR g_szFormat[] = TEXT("%-50s %-10.10s ");
|
|
const TCHAR g_szUnknown[] = TEXT("<unknown>");
|
|
|
|
//
|
|
// Adds 'g_dwCounter' spaces to debug spew
|
|
//
|
|
void
|
|
dbgspace( void )
|
|
{
|
|
for( DWORD dw = 1; dw < g_dwCounter; dw++ )
|
|
DebugMsg( "| " );
|
|
}
|
|
|
|
//
|
|
// Makes sure multiple threads don't trample debugging output.
|
|
//
|
|
void
|
|
dbgEnterCS( void )
|
|
{
|
|
if ( !g_fDebugInitialized )
|
|
{
|
|
//
|
|
// There is NO matching DeleteCriticalSection( ) since
|
|
// this is only used for debugging purposes.
|
|
//
|
|
InitializeCriticalSection( &g_DebugCS );
|
|
g_fDebugInitialized = TRUE;
|
|
}
|
|
|
|
EnterCriticalSection( &g_DebugCS );
|
|
}
|
|
|
|
void
|
|
dbgExitCS( void )
|
|
{
|
|
LeaveCriticalSection( &g_DebugCS );
|
|
}
|
|
|
|
//
|
|
// Takes the filename and line number and put them into a string buffer.
|
|
//
|
|
// NOTE: the buffer is assumed to be of size DEBUG_OUTPUT_BUFFER_SIZE.
|
|
//
|
|
LPTSTR
|
|
dbgmakefilelinestring(
|
|
LPTSTR pszBuf,
|
|
LPCTSTR pszFile,
|
|
const int uLine )
|
|
{
|
|
LPVOID args[2];
|
|
|
|
args[0] = (LPVOID) pszFile;
|
|
args[1] = IntToPtr(uLine);
|
|
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
TEXT("%1(%2!u!):"),
|
|
0, // error code
|
|
0, // default language
|
|
(LPTSTR) pszBuf, // output buffer
|
|
DEBUG_OUTPUT_BUFFER_SIZE, // size of buffer
|
|
(va_list*) args ); // arguments
|
|
|
|
return pszBuf;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// TraceMsg() - ascii
|
|
//
|
|
void
|
|
TraceMsg(
|
|
DWORD dwCheckFlags,
|
|
LPCSTR pszFormat,
|
|
... )
|
|
{
|
|
va_list valist;
|
|
|
|
if (( dwCheckFlags == TF_ALWAYS
|
|
|| !!( g_dwTraceFlags & dwCheckFlags ) ))
|
|
{
|
|
TCHAR szBuf[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
|
|
TCHAR szFormat[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
mbstowcs( szFormat, pszFormat, lstrlenA( pszFormat ) + 1 );
|
|
|
|
va_start( valist, pszFormat );
|
|
wvsprintf( szBuf, szFormat, valist );
|
|
va_end( valist );
|
|
|
|
dbgEnterCS( );
|
|
|
|
OutputDebugString( szBuf );
|
|
|
|
dbgExitCS( );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// TraceMessage()
|
|
//
|
|
void
|
|
TraceMessage(
|
|
LPCTSTR pszFile,
|
|
const int uLine,
|
|
LPCTSTR pszModule,
|
|
DWORD dwCheckFlags,
|
|
LPCTSTR pszFormat,
|
|
... )
|
|
{
|
|
va_list valist;
|
|
|
|
if (( dwCheckFlags == TF_ALWAYS
|
|
|| !!( g_dwTraceFlags & dwCheckFlags ) ))
|
|
{
|
|
TCHAR szBuf[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
|
|
if ( !pszModule )
|
|
{
|
|
pszModule = g_szUnknown;
|
|
}
|
|
|
|
if ( !pszFile )
|
|
{
|
|
wsprintf( szBuf, g_szFormat, g_szNULL, pszModule );
|
|
}
|
|
else
|
|
{
|
|
TCHAR szFileLine[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
|
|
dbgmakefilelinestring( szFileLine, pszFile, uLine );
|
|
wsprintf( szBuf, g_szFormat, szFileLine, pszModule );
|
|
}
|
|
|
|
dbgEnterCS( );
|
|
|
|
OutputDebugString( szBuf );
|
|
|
|
dbgspace( );
|
|
|
|
va_start( valist, pszFormat );
|
|
wvsprintf( szBuf, pszFormat, valist );
|
|
va_end( valist );
|
|
|
|
OutputDebugString( szBuf );
|
|
|
|
dbgExitCS( );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// DebugMsg()
|
|
//
|
|
void
|
|
DebugMsg(
|
|
LPCSTR pszFormat,
|
|
... )
|
|
{
|
|
va_list valist;
|
|
TCHAR szBuf[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
TCHAR szFormat[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
|
|
mbstowcs( szFormat, pszFormat, lstrlenA( pszFormat ) + 1 );
|
|
|
|
va_start( valist, pszFormat );
|
|
wvsprintf( szBuf, szFormat, valist);
|
|
va_end( valist );
|
|
|
|
dbgEnterCS( );
|
|
|
|
OutputDebugString( szBuf );
|
|
|
|
dbgExitCS( );
|
|
}
|
|
|
|
//
|
|
// Displays a dialog box with the failed assertion. User has the option of
|
|
// breaking.
|
|
//
|
|
BOOL
|
|
AssertMessage(
|
|
LPCTSTR pszFile,
|
|
const int uLine,
|
|
LPCTSTR pszModule,
|
|
LPCTSTR pszfn,
|
|
BOOL fTrue )
|
|
{
|
|
if ( !fTrue )
|
|
{
|
|
TCHAR szBuf[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
TCHAR szFileLine[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
|
|
// Make sure everything is cool before we blow up somewhere else.
|
|
if ( pszFile == NULL )
|
|
{
|
|
pszFile = g_szNULL;
|
|
}
|
|
|
|
if ( pszModule == NULL )
|
|
{
|
|
pszModule = g_szNULL;
|
|
}
|
|
|
|
if ( pszfn == NULL )
|
|
{
|
|
pszfn = g_szNULL;
|
|
}
|
|
|
|
dbgmakefilelinestring( szFileLine, pszFile, uLine );
|
|
|
|
wsprintf( szBuf, TEXT("%-50s %-10s ASSERT: %s\n"),
|
|
szFileLine, pszModule, pszfn );
|
|
|
|
dbgEnterCS( );
|
|
|
|
OutputDebugString( szBuf );
|
|
|
|
dbgExitCS( );
|
|
|
|
wsprintf( szBuf, TEXT("Module:\t%s\t\nLine:\t%u\t\nFile:\t%s\t\n\nAssertion:\t%s\t\n\nDo you want to break here?"),
|
|
pszModule, uLine, pszFile, pszfn );
|
|
|
|
if ( IDNO == MessageBox( NULL, szBuf, TEXT("Assertion Failed!"),
|
|
MB_YESNO|MB_ICONWARNING ) )
|
|
fTrue = !FALSE; // don't break
|
|
|
|
}
|
|
|
|
return !fTrue;
|
|
|
|
}
|
|
|
|
//
|
|
// Traces HRESULT errors. A dialog will appear is there is an error
|
|
// in the hr.
|
|
//
|
|
HRESULT
|
|
TraceHR(
|
|
LPCTSTR pszFile,
|
|
const int uLine,
|
|
LPCTSTR pszModule,
|
|
LPCTSTR pszfn,
|
|
HRESULT hr )
|
|
{
|
|
static const LPTSTR pcszFalse = TEXT("S_FALSE\n");
|
|
|
|
if ( !( g_dwTraceFlags & TF_HRESULTS ) )
|
|
return hr; // bail
|
|
|
|
if ( hr != NOERROR )
|
|
{
|
|
TCHAR szBuf[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
TCHAR szFileLine[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
PTSTR pszMsgBuf = NULL;
|
|
|
|
switch ( hr )
|
|
{
|
|
case S_FALSE:
|
|
pszMsgBuf = pcszFalse;
|
|
break;
|
|
|
|
default:
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
| FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
hr,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
(LPTSTR)&pszMsgBuf,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
// Make sure everything is cool before we blow up somewhere else.
|
|
if ( pszMsgBuf == NULL )
|
|
{
|
|
pszMsgBuf = TEXT("<unknown error code returned>\n");
|
|
}
|
|
Assert( pszFile != NULL );
|
|
Assert( pszModule != NULL );
|
|
Assert( pszfn != NULL );
|
|
|
|
dbgmakefilelinestring( szFileLine, pszFile, uLine );
|
|
|
|
wsprintf( szBuf, TEXT("%-50s %-10s HRESULT: hr = 0x%08x - %s"),
|
|
szFileLine, pszModule, hr, pszMsgBuf );
|
|
|
|
dbgEnterCS( );
|
|
|
|
OutputDebugString( szBuf );
|
|
|
|
dbgExitCS( );
|
|
|
|
wsprintf( szBuf, TEXT("Module:\t%s\t\nLine:\t%u\t\nFile:\t%s\t\n\nFunction:\t%s\t\nhr =\t0x%08x - %s\t\nDo you want to break here?"),
|
|
pszModule, uLine, pszFile, pszfn, hr, pszMsgBuf );
|
|
|
|
if ( IDYES == MessageBox( NULL, szBuf, TEXT("Trace HRESULT"),
|
|
MB_YESNO|MB_ICONWARNING ) )
|
|
DEBUG_BREAK;
|
|
|
|
if ( pszMsgBuf != pcszFalse ) {
|
|
LocalFree( pszMsgBuf );
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Memory allocation and tracking
|
|
//
|
|
|
|
typedef struct _MEMORYBLOCK {
|
|
HGLOBAL hglobal;
|
|
DWORD dwBytes;
|
|
UINT uFlags;
|
|
LPCTSTR pszFile;
|
|
UINT uLine;
|
|
LPCTSTR pszModule;
|
|
LPCTSTR pszComment;
|
|
_MEMORYBLOCK *pNext;
|
|
} MEMORYBLOCK, *LPMEMORYBLOCK;
|
|
|
|
//
|
|
// Adds a MEMORYBLOCK to the memory tracking list.
|
|
//
|
|
HGLOBAL
|
|
DebugMemoryAdd(
|
|
HGLOBAL hglobal,
|
|
LPCTSTR pszFile,
|
|
const int uLine,
|
|
LPCTSTR pszModule,
|
|
UINT uFlags,
|
|
DWORD dwBytes,
|
|
LPCTSTR pszComment )
|
|
{
|
|
if ( hglobal )
|
|
{
|
|
LPMEMORYBLOCK pmbHead = (LPMEMORYBLOCK) TlsGetValue( g_TraceMemoryIndex );
|
|
LPMEMORYBLOCK pmb = (LPMEMORYBLOCK) GlobalAlloc(
|
|
GMEM_FIXED,
|
|
sizeof(MEMORYBLOCK) );
|
|
|
|
if ( !pmb )
|
|
{
|
|
GlobalFree( hglobal );
|
|
return NULL;
|
|
}
|
|
|
|
pmb->hglobal = hglobal;
|
|
pmb->dwBytes = dwBytes;
|
|
pmb->uFlags = uFlags;
|
|
pmb->pszFile = pszFile;
|
|
pmb->uLine = uLine;
|
|
pmb->pszModule = pszModule;
|
|
pmb->pszComment = pszComment;
|
|
pmb->pNext = pmbHead;
|
|
|
|
TlsSetValue( g_TraceMemoryIndex, pmb );
|
|
TraceMessage( pmb->pszFile,
|
|
pmb->uLine,
|
|
pmb->pszModule,
|
|
TF_MEMORYALLOCS,
|
|
L"Alloced %s - %u bytes at 0x%08x (pmb=0x%08x)\n",
|
|
pszComment,
|
|
dwBytes,
|
|
pmb->hglobal,
|
|
pmb );
|
|
}
|
|
|
|
return hglobal;
|
|
}
|
|
|
|
//
|
|
// Removes a MEMORYBLOCK to the memory tracking list.
|
|
//
|
|
void
|
|
DebugMemoryDelete(
|
|
HGLOBAL hglobal )
|
|
{
|
|
if ( hglobal )
|
|
{
|
|
LPMEMORYBLOCK pmbHead = (LPMEMORYBLOCK) TlsGetValue( g_TraceMemoryIndex );
|
|
LPMEMORYBLOCK pmbLast = NULL;
|
|
|
|
while ( pmbHead && pmbHead->hglobal != hglobal )
|
|
{
|
|
pmbLast = pmbHead;
|
|
pmbHead = pmbLast->pNext;
|
|
}
|
|
|
|
if ( pmbHead )
|
|
{
|
|
if ( pmbLast )
|
|
{
|
|
pmbLast->pNext = pmbHead->pNext;
|
|
}
|
|
else
|
|
{
|
|
TlsSetValue( g_TraceMemoryIndex, pmbHead->pNext );
|
|
}
|
|
|
|
TraceMessage( pmbHead->pszFile,
|
|
pmbHead->uLine,
|
|
pmbHead->pszModule,
|
|
TF_MEMORYALLOCS,
|
|
L"Freeing %s - %u bytes from 0x%08x (pmb=0x%08x)\n",
|
|
pmbHead->pszComment,
|
|
pmbHead->dwBytes,
|
|
pmbHead->hglobal,
|
|
pmbHead );
|
|
GlobalFree( pmbHead );
|
|
}
|
|
else
|
|
{
|
|
DebugMsg( "\n**** Attempted to free memory at 0x%08x (ThreadID = 0x%08x) ****\n\n",
|
|
hglobal, GetCurrentThreadId( ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocates memory and adds the MEMORYBLOCK to the memory tracking list.
|
|
//
|
|
HGLOBAL
|
|
DebugAlloc(
|
|
LPCTSTR pszFile,
|
|
const int uLine,
|
|
LPCTSTR pszModule,
|
|
UINT uFlags,
|
|
DWORD dwBytes,
|
|
LPCTSTR pszComment )
|
|
{
|
|
HGLOBAL hglobal = GlobalAlloc( uFlags, dwBytes );
|
|
|
|
return DebugMemoryAdd( hglobal, pszFile, uLine, pszModule, uFlags, dwBytes, pszComment );
|
|
}
|
|
|
|
//
|
|
// Remove the MEMORYBLOCK to the memory tracking list, memsets the
|
|
// memory to 0xFE and then frees the memory.
|
|
//
|
|
HGLOBAL
|
|
DebugFree(
|
|
HGLOBAL hglobal )
|
|
{
|
|
DebugMemoryDelete( hglobal );
|
|
|
|
return GlobalFree( hglobal );
|
|
}
|
|
|
|
//
|
|
// Checks the memory tracking list. If it is not empty, it will dump the
|
|
// list and break.
|
|
//
|
|
void
|
|
DebugMemoryCheck( )
|
|
{
|
|
BOOL fFoundLeak = FALSE;
|
|
LPMEMORYBLOCK pmb = (LPMEMORYBLOCK) TlsGetValue( g_TraceMemoryIndex );
|
|
|
|
dbgEnterCS( );
|
|
|
|
while ( pmb )
|
|
{
|
|
LPVOID args[ 5 ];
|
|
TCHAR szOutput[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
TCHAR szFileLine[ DEBUG_OUTPUT_BUFFER_SIZE ];
|
|
|
|
if ( fFoundLeak == FALSE )
|
|
{
|
|
DebugMsg("\n******** Memory leak detected ******** ThreadID = 0x%08x ******** \n\n", GetCurrentThreadId( ) );
|
|
//OutputDebugString("12345678901234567890123456789012345678901234567890 1234567890 X 0x12345678 12345 1...");
|
|
OutputDebugString(TEXT("Filename(Line Number): Module Addr/HGLOBAL Size String\n"));
|
|
fFoundLeak = TRUE;
|
|
}
|
|
|
|
if ( StrCmp( pmb->pszComment, TEXT("new( )" ) ) == 0 )
|
|
{
|
|
wsprintf(
|
|
szFileLine,
|
|
TEXT("Caller unknown - (Module %s, line %d)"),
|
|
pmb->pszModule,
|
|
pmb->uLine );
|
|
}
|
|
else
|
|
{
|
|
dbgmakefilelinestring( szFileLine, pmb->pszFile, pmb->uLine );
|
|
}
|
|
|
|
args[0] = (LPVOID) pmb->hglobal;
|
|
args[1] = (LPVOID) szFileLine;
|
|
args[2] = (LPVOID) pmb->pszComment;
|
|
args[3] = UlongToPtr(pmb->dwBytes);
|
|
args[4] = (LPVOID) pmb->pszModule;
|
|
|
|
if ( !!(pmb->uFlags & GMEM_MOVEABLE) )
|
|
{
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
TEXT("%2!-50s! %5!-10s! H 0x%1!08x! %4!-5u! \"%3\"\n"),
|
|
0, // error code
|
|
0, // default language
|
|
(LPTSTR) szOutput, // output buffer
|
|
ARRAYSIZE( szOutput ), // size of buffer
|
|
(va_list*) args ); // arguments
|
|
}
|
|
else
|
|
{
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
TEXT("%2!-50s! %5!-10s! A 0x%1!08x! %4!-5u! \"%3\"\n"),
|
|
0, // error code
|
|
0, // default language
|
|
(LPTSTR) szOutput, // output buffer
|
|
ARRAYSIZE( szOutput ), // size of buffer
|
|
(va_list*) args ); // arguments
|
|
}
|
|
|
|
OutputDebugString( szOutput );
|
|
|
|
pmb = pmb->pNext;
|
|
}
|
|
|
|
if ( fFoundLeak == TRUE )
|
|
{
|
|
OutputDebugString(TEXT("\n***************************** Memory leak detected *****************************\n\n"));
|
|
}
|
|
|
|
dbgExitCS( );
|
|
|
|
Assert( !fFoundLeak );
|
|
|
|
}
|
|
|
|
//
|
|
// Global Management Functions -
|
|
//
|
|
// These are in debug and retail but internally they change
|
|
// depending on the build.
|
|
//
|
|
#undef new
|
|
void* __cdecl operator new( size_t nSize, LPCTSTR pszFile, const int iLine, LPCTSTR pszModule )
|
|
{
|
|
return DebugAlloc( pszFile, iLine, pszModule, GPTR, (DWORD)nSize, TEXT("new( )") );
|
|
}
|
|
|
|
void * __cdecl operator new(size_t t_size )
|
|
{
|
|
UNREFERENCED_PARAMETER(t_size);
|
|
AssertMsg( 0, "Macro failure" );
|
|
return NULL;
|
|
}
|
|
|
|
void __cdecl operator delete(void *pv)
|
|
{
|
|
TraceFree( pv );
|
|
}
|
|
|
|
int __cdecl _purecall(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#else // ! DEBUG -- It's retail
|
|
|
|
//
|
|
// Global Management Functions -
|
|
//
|
|
// These are in debug and retail but are internally they change
|
|
// depending on the build.
|
|
//
|
|
|
|
void * __cdecl operator new(size_t t_size )
|
|
{
|
|
return LocalAlloc( GPTR, t_size );
|
|
}
|
|
|
|
void __cdecl operator delete(void *pv)
|
|
{
|
|
LocalFree( pv );
|
|
}
|
|
|
|
int __cdecl _purecall(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif // DEBUG
|