|
|
/*************************************************************************
* Microsoft Windows NT * * * * Copyright(c) Microsoft Corp., 1994 * * * * Revision History: * * * * Jan. 24,94 Koti Created * * * * Description: * * * * This file contains debug support routines for the LPD Service. * * This file is based on (in fact, borrowed and then modified) on the * * debug.c in the ftpsvc module. * * * *************************************************************************/
#include <stdio.h>
#include "lpd.h"
#if DBG
//
// Private constants.
//
#define LPD_OUT_FILE "lpdout.log"
#define LPD_ERR_FILE "lpderr.log"
#define MAX_PRINTF_OUTPUT 1024 // characters
#define LPD_OUTPUT_LABEL "LPDSVC"
#define DEBUG_HEAP 0 // enable/disable heap debugging
//
// Private globals.
//
FILE * pErrFile; // Debug output log file.
FILE * pOutFile; // Debug output log file.
BOOL fFirstTimeErr=TRUE; BOOL fFirstTimeOut=TRUE;
//
// every LocalAlloc gets linked to this list and LocalFree gets unlinked
// (so that we can catch any memory leaks!)
//
LIST_ENTRY DbgMemList;
//
// synchronization for DbgMemList
//
CRITICAL_SECTION CS;
//
// Public functions.
//
/*******************************************************************
NAME: DbgInit
SYNOPSIS: Peforms initialization for debug memory allocator
ENTRY: void
HISTORY: Frankbee 05-Jun-1996 Created.
********************************************************************/
VOID DbgInit() { InitializeCriticalSection( &CS ); }
/*******************************************************************
NAME: DbgUninit
SYNOPSIS: Peforms cleanup for debug memory allocator
ENTRY: void
HISTORY: Frankbee 05-Jun-1996 Created.
********************************************************************/
VOID DbgUninit() { DeleteCriticalSection( &CS ); }
//
// Public functions.
//
/*******************************************************************
NAME: LpdAssert
SYNOPSIS: Called if an assertion fails. Displays the failed assertion, file name, and line number. Gives the user the opportunity to ignore the assertion or break into the debugger.
ENTRY: pAssertion - The text of the failed expression.
pFileName - The containing source file.
nLineNumber - The guilty line number.
HISTORY: KeithMo 07-Mar-1993 Created.
********************************************************************/ VOID LpdAssert( VOID * pAssertion, VOID * pFileName, ULONG nLineNumber ) { RtlAssert( pAssertion, pFileName, nLineNumber, NULL );
} // LpdAssert
/*******************************************************************
NAME: LpdPrintf
SYNOPSIS: Customized debug output routine.
ENTRY: Usual printf-style parameters.
HISTORY: KeithMo 07-Mar-1993 Created.
********************************************************************/ VOID LpdPrintf( CHAR * pszFormat, ... ) { CHAR szOutput[MAX_PRINTF_OUTPUT]; DWORD dwErrcode; va_list ArgList; DWORD cchOutputLength; PSTR pszErrorBuffer;
dwErrcode = GetLastError();
sprintf( szOutput, "%s (%lu): ", LPD_OUTPUT_LABEL, GetCurrentThreadId() );
va_start( ArgList, pszFormat ); vsprintf( szOutput + strlen(szOutput), pszFormat, ArgList ); va_end( ArgList );
cchOutputLength = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM + FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, dwErrcode, 0, (LPTSTR)&pszErrorBuffer, 1, NULL );
if ( cchOutputLength == 0 ) { sprintf( szOutput + strlen(szOutput), " Error = %ld\n",dwErrcode); pszErrorBuffer = NULL; } else { pszErrorBuffer[ cchOutputLength - 1 ] = '\0';
sprintf( szOutput + strlen(szOutput), " Error = %ld (%s)\n", dwErrcode, pszErrorBuffer ); }
if ( pszErrorBuffer != NULL ) { //
// Why is "LocalFree" in parentheses? Because LocalFree might be #define'd
// to a debugging function, but pszErrorBuffer was LocalAlloc()'d with the
// normal function. The parens prevent macro expansion and guarantee that
// we call the real LocalFree() function.
//
(LocalFree)( pszErrorBuffer ); }
if( pErrFile == NULL ) { if ( fFirstTimeErr ) { pErrFile = fopen( LPD_ERR_FILE, "w+" ); fFirstTimeErr = FALSE; } else pErrFile = fopen( LPD_ERR_FILE, "a+" ); }
if( pErrFile != NULL ) { fputs( szOutput, pErrFile ); fflush( pErrFile ); }
} // LpdPrintf
/*******************************************************************
NAME: StripPath
SYNOPSIS: Given a fully qualified filename, returns the filename sans path
ENTRY: char *szPath - filename, possibly including path
RETURNS: filename
HISTORY: Frankbee 6/18/96 Created.
********************************************************************/
char * StripPath( char *szPath ) { char *p;
p = szPath + strlen( szPath );
while( p != szPath && *p != '\\' ) p--;
if ( *p == '\\' ) ++p;
return p;
}
/*******************************************************************
NAME: DbgDumpLeaks
SYNOPSIS: Checks DbgMemList for memory that wasn't deallocated. For each leaked block, the following is written to the error log:
- Filename - Line # - Requested Size
ENTRY: VOID RETURNS: VOID
HISTORY: Frankbee 6/18/96 Created
********************************************************************/
void DbgDumpLeaks() { LIST_ENTRY *p = DbgMemList.Flink;
if ( IsListEmpty( &DbgMemList ) ) return; // no leaks
LPD_DEBUG("DbgDumpLeaks: memory leaks detected:\n");
while ( p != &DbgMemList ) { DbgMemBlkHdr *pHdr = (DbgMemBlkHdr*) p; LpdPrintf( "%s, line %d: %d byte block\n", pHdr->szFile, pHdr->dwLine, pHdr->ReqSize );
p = p->Flink; }
LPD_ASSERT(0);
}
/*******************************************************************
NAME: DbgAllocMem
SYNOPSIS: Keep track of all allocated memory so we can catch memory leak when we unload This is only on debug builds. On non-debug builds this function doesn't exist: calls directly go to LocalAlloc
ENTRY: pscConn - connection which is requesting memory flag - whatever flags are passed in ReqSize - how much memory is needed
RETURNS: PVOID - pointer to the memory block that client will use directly.
HISTORY: Koti 3-Dec-1994 Created.
********************************************************************/
//
// IMPORTANT: we are undef'ing LocalAlloc because we need to make a
// call to the actual function here!. That's why
// this function and this undef are at the end of the file.
//
#undef LocalAlloc
PVOID DbgAllocMem( PSOCKCONN pscConn, DWORD flag, DWORD ReqSize, DWORD dwLine, char *szFile ) {
DWORD ActualSize; PVOID pBuffer; DbgMemBlkHdr *pMemHdr; PVOID pRetAddr;
ActualSize = ReqSize + sizeof(DbgMemBlkHdr); pBuffer = LocalAlloc( flag, ActualSize ); if ( !pBuffer ) { LPD_DEBUG("DbgAllocMem: couldn't allocate memory: returning!\n"); return( NULL ); }
pMemHdr = (DbgMemBlkHdr *)pBuffer;
pMemHdr->Verify = DBG_MEMALLOC_VERIFY; pMemHdr->ReqSize = ReqSize; pMemHdr->dwLine = dwLine; strncpy( pMemHdr->szFile, StripPath( szFile ), DBG_MAXFILENAME ); pMemHdr->szFile[ DBG_MAXFILENAME - 1 ] = '\0';
pMemHdr->Owner[0] = (DWORD_PTR)pscConn;
//
// for private builds on x86 machines, remove the #if 0
// (this code saves stack trace as to exactly who allocated memory)
//
#if 0
pRetAddr = &pMemHdr->Owner[0];
_asm { push ebx push ecx push edx mov ebx, pRetAddr mov eax, ebp mov edx, dword ptr [eax+4] ; return address mov dword ptr [ebx], edx mov eax, dword ptr [eax] ; previous frame pointer pop edx pop ecx pop ebx } #endif
InitializeListHead(&pMemHdr->Linkage);
EnterCriticalSection( &CS ); InsertTailList(&DbgMemList, &pMemHdr->Linkage); LeaveCriticalSection( &CS );
return( (PCHAR)pBuffer + sizeof(DbgMemBlkHdr) ); }
/*******************************************************************
NAME: DbgReAllocMem
SYNOPSIS: Keep track of all allocated memory so we can catch memory leak when we unload This is only on debug builds. On non-debug builds this function doesn't exist: calls directly go to LocalReAlloc
ENTRY: pscConn - connection which is requesting memory pPartBuf - the originally allocated buffer ReqSize - how much memory is needed flag - whatever flags are passed in
RETURNS: PVOID - pointer to the memory block that client will use directly.
HISTORY: Koti 3-Dec-1994 Created.
********************************************************************/ //
// IMPORTANT: we are undef'ing LocalReAlloc because we need to make a
// call to the actual function here!. That's why
// this function and this undef are at the end of the file.
//
#undef LocalReAlloc
PVOID DbgReAllocMem( PSOCKCONN pscConn, PVOID pPartBuf, DWORD ReqSize, DWORD flag, DWORD dwLine, char *szFile ) {
DbgMemBlkHdr *pMemHdr; PVOID pRetAddr;
if ( !pPartBuf ) { LPD_DEBUG("DbgReAllocMem: invalid memory: returning!\n"); return( NULL ); }
pMemHdr = (DbgMemBlkHdr *)((PCHAR)pPartBuf - sizeof(DbgMemBlkHdr));
if( pMemHdr->Verify != DBG_MEMALLOC_VERIFY ) { LPD_DEBUG("DbgReAllocMem: invalid memory being realloced: returning!\n"); return( NULL ); }
EnterCriticalSection( &CS ); RemoveEntryList(&pMemHdr->Linkage); LeaveCriticalSection( &CS );
pMemHdr = LocalReAlloc((PCHAR)pMemHdr, ReqSize+sizeof(DbgMemBlkHdr), flag);
if ( !pMemHdr ) { LPD_DEBUG("DbgReAllocMem: LocalReAlloc failed: returning!\n"); return( NULL ); }
pMemHdr->Verify = DBG_MEMALLOC_VERIFY; pMemHdr->ReqSize = ReqSize; pMemHdr->dwLine = dwLine; strncpy( pMemHdr->szFile, StripPath( szFile ), DBG_MAXFILENAME ); pMemHdr->szFile[ DBG_MAXFILENAME - 1 ] = '\0';
pMemHdr->Owner[0] = (DWORD_PTR)pscConn;
//
// for private builds on x86 machines, remove the #if 0
// (this code saves stack trace as to exactly who allocated memory)
//
#if 0
pRetAddr = &pMemHdr->Owner[0];
_asm { push ebx push ecx push edx mov ebx, pRetAddr mov eax, ebp mov edx, dword ptr [eax+4] ; return address mov dword ptr [ebx], edx mov eax, dword ptr [eax] ; previous frame pointer pop edx pop ecx pop ebx } #endif
InitializeListHead(&pMemHdr->Linkage);
EnterCriticalSection( &CS ); InsertTailList(&DbgMemList, &pMemHdr->Linkage); LeaveCriticalSection( &CS );
return( (PCHAR)pMemHdr + sizeof(DbgMemBlkHdr) ); } /*******************************************************************
NAME: DbgFreeMem
SYNOPSIS: This routine removes the memory block from our list and frees the memory by calling the CTE function CTEFreeMem
ENTRY: pBufferToFree - memory to free (caller's buffer)
RETURNS: nothing
HISTORY: Koti 11-Nov-1994 Created.
********************************************************************/
//
// IMPORTANT: we are undef'ing CTEFreeMem because we need to make a
// call to the actual CTE function CTEFreeMem. That's why
// this function and this undef are at the end of the file.
//
#undef LocalFree
VOID DbgFreeMem( PVOID pBufferToFree ) {
DbgMemBlkHdr *pMemHdr;
if ( !pBufferToFree ) { return; }
pMemHdr = (DbgMemBlkHdr *)((PCHAR)pBufferToFree - sizeof(DbgMemBlkHdr));
if( pMemHdr->Verify != DBG_MEMALLOC_VERIFY ) { LPD_DEBUG("DbgFreeMem: attempt to free invalid memory: returning!\n"); LPD_ASSERT(0); return; }
//
// change our signature: if we are freeing some memory twice, we'll know!
//
pMemHdr->Verify -= 1;
EnterCriticalSection( &CS ); RemoveEntryList(&pMemHdr->Linkage); LeaveCriticalSection( &CS );
LocalFree( (PVOID)pMemHdr ); }
#endif // DBG
|