/*************************************************************************
 *                        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)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)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