//------------------------------------------------------------*
//  File name:    DEBUG.C
//
//  Description:  Debug helper code for System control panel
//                applet
//
//
//  Microsoft Confidential
//  Copyright (c) Microsoft Corporation 1992-1996
//  All rights reserved
//
//  History
//      10-Nov-1996 JonPa       Created it.
//
//------------------------------------------------------------*
#include <windows.h>
#include "debug.h"

///////////////////////////////////////////////////////////////
//      Constants
///////////////////////////////////////////////////////////////

#ifdef DBG_CODE

#define CCH_LABEL (sizeof(DWORD) * 2)   // 64 BITS == 8 ANSI chars

#define CB_TAG     sizeof(DWORD)
#define DW_TAG      ((DWORD)(0x434C4143))   // 'CALC'

#define DW_TAG2     ((DWORD)(0x444F4F47))   // 'GOOD'

#define CH_FILL     '*'

///////////////////////////////////////////////////////////////
//      Structures and Types
///////////////////////////////////////////////////////////////

//
// NOTE!!!!
//
// The HOBJHDR structure MUST be a multiple of 8 bytes (64bits) in len!
// otherwise this code will *FAULT* on ALPHA machines!
//

typedef struct HHO *PHHO;

struct HHO {
    PHHO    phhoNext;
    PHHO    phhoPrev;
    CHAR    szFile[CCH_LABEL];
    DWORD   iLine;
    DWORD   cBytesData;
    DWORD   dwTmp;
    DWORD   dwTag2;
};

typedef struct HHO HOBJHDR;

typedef struct {
    LPVOID  pvPtr;
    CHAR    szFile[CCH_LABEL];
    DWORD   iLine;
    CHAR    szFreedBy[CCH_LABEL];
    DWORD   iLineFreed;
} FREELOGREC, *PFREELOGREC;

///////////////////////////////////////////////////////////////
//      Global variables
///////////////////////////////////////////////////////////////

//
// Root of memory chain

HOBJHDR ghhoRoot = { &ghhoRoot, &ghhoRoot, { 'R', 'O', 'O', 'T' }, 0, sizeof(ghhoRoot) };


//
// Buffer used for OutputDebugString formatting (See DbgPrintf and DbgStopX)

TCHAR szDbgOutBuffer[1024];

//
// Buffer used for logging

#define CFLR_MAX    1024
FREELOGREC aflrFreeLog[CFLR_MAX];
PFREELOGREC g_pflrUnused = NULL;

#define NextFreeLogRec( pflr )    ((pflr >= &aflrFreeLog[CFLR_MAX-1]) ? aflrFreeLog : pflr+1)
#define PrevFreeLogRec( pflr )    ((pflr <= aflrFreeLog) ? &aflrFreeLog[CFLR_MAX-1] : pflr-1)

//---------------------------------------------------------------
//
// void DbgPrintf( LPTSTR szFmt, ... )
//
//  Formatted version of OutputDebugString
//
//  Parameters: Same as printf()
//
//  History:
//      18-Jan-1996 JonPa       Wrote it
//---------------------------------------------------------------
void DbgPrintf( LPTSTR szFmt, ... ) {
    va_list marker;

    va_start( marker, szFmt );

    wvsprintf( szDbgOutBuffer, szFmt, marker );
    OutputDebugString( szDbgOutBuffer );

    va_end( marker );
}


//---------------------------------------------------------------
//
// void DbgStopX(LPSTR mszFile, int iLine, LPTSTR szText )
//
//  Print a string (with location id) and then break
//
//  Parameters:
//      mszFile     ANSI filename (__FILE__)
//      iLine       line number   (__LINE__)
//      szText      Text string to send to debug port
//
//  History:
//      18-Jan-1996 JonPa       Wrote it
//---------------------------------------------------------------
void DbgStopX(LPSTR mszFile, int iLine, LPTSTR szText ) {
    int cch;

    wsprintf( szDbgOutBuffer, TEXT("RATPAK (%hs %d) : %s\n"), mszFile, iLine, szText );

    OutputDebugString(szDbgOutBuffer);

    DebugBreak();
}

//---------------------------------------------------------------
//
// void MemAllocWorker(LPSTR szFile, int iLine, UINT uFlags, UINT cBytes)
//
//  Debug replacement for LocalAlloc
//
//  Parameters:
//      mszFile     ANSI filename (__FILE__)
//      iLine       line number   (__LINE__)
//      uFlags      same as LocalAlloc
//      cBytes      same as LocalAlloc
//
//  History:
//      18-Jan-1996 JonPa       Wrote it
//---------------------------------------------------------------
HLOCAL MemAllocWorker(LPSTR szFile, int iLine, UINT uFlags, UINT cBytes) {
    PHHO phhoNew;
    HLOCAL hMem;
    LPSTR psz;
    UINT i, cBytesAlloc;

    cBytesAlloc = cBytes;
    
    //
    // If fixed alloc...
    //
    if ((uFlags & (LMEM_MOVEABLE | LMEM_DISCARDABLE)) != 0) {
        DBGSTOPX( szFile, iLine, "Attempting to allocate movable memory... Returning NULL");
        return NULL;
    }

    cBytesAlloc = cBytes + sizeof(HOBJHDR);
    
    // DWORD align Tag
    cBytesAlloc = ((cBytesAlloc + 3) & ~3);
    cBytesAlloc += CB_TAG;


    hMem = LocalAlloc( uFlags, cBytesAlloc );
    
    //
    // If a valid pointer, and it is a fixed pointer...
    //
    phhoNew = (PHHO)hMem;

    if (hMem != NULL) {


        phhoNew->phhoNext = ghhoRoot.phhoNext;
        ghhoRoot.phhoNext = phhoNew;
        phhoNew->phhoNext->phhoPrev = phhoNew;
        phhoNew->phhoPrev = &ghhoRoot;

        phhoNew->dwTag2 = DW_TAG2;

        for( psz = szFile; *psz != '\0'; psz++ );

        for( ; psz != szFile && *psz != ':' && *psz != '/' && *psz != '\\'; psz--);
        if (*psz == ':' || *psz == '/' || *psz == '\\')
            psz++;

        for( i = 0; i < CCH_LABEL; i++ ) {
            phhoNew->szFile[i] = *psz;
            if (*psz) {
                psz++;
            }
        }

        phhoNew->iLine = iLine;

        phhoNew->cBytesData = cBytes;

        phhoNew += 1;   // point phhoNew to 1st byte after structure
        
        // round up to nearest DWORD
        { LPBYTE pb = (LPBYTE)phhoNew + cBytes;

            cBytesAlloc -= CB_TAG;
            cBytes += sizeof(HOBJHDR);

            while( cBytes < cBytesAlloc ) {
                *pb++ = CH_FILL;
                cBytes++;
            }

            *((LPDWORD)pb) = DW_TAG;
        }
    }

    return (HLOCAL)phhoNew;
}

//---------------------------------------------------------------
//
// void MemFreeWorker( LPSTR szFile, int iLine, HLOCAL hMem )
//
//  Debug replacement for LocalFree
//
//  Parameters:
//      mszFile     ANSI filename (__FILE__)
//      iLine       line number   (__LINE__)
//      hMem        same as LocalAlloc
//
//  History:
//      18-Jan-1996 JonPa       Wrote it
//---------------------------------------------------------------
HLOCAL MemFreeWorker( LPSTR szFile, int iLine, HLOCAL hMem ) {
    PHHO phhoMem;
    UINT uFlags;
    UINT cBytes, cBytesAlloc;
    LPSTR psz;
    INT  i;


    if (g_pflrUnused == NULL) {
        ZeroMemory( aflrFreeLog, sizeof(aflrFreeLog) );
        g_pflrUnused = aflrFreeLog;
    }

    if (hMem == NULL) {
        DBGSTOPX( szFile, iLine, "Freeing NULL handle!");
        return LocalFree(hMem);
    }

    phhoMem = (PHHO)hMem - 1;

    if (phhoMem->dwTag2 != DW_TAG2) {
        PFREELOGREC pflr;
        //
        // Our tag has been stompped on, see if we have already freed this object
        //
        for( pflr = PrevFreeLogRec(g_pflrUnused); pflr != g_pflrUnused; pflr = PrevFreeLogRec(pflr) ) {
            if (pflr->pvPtr == phhoMem) {
                DBGPRINTF((TEXT("RATPAK: Object may have already been freed by %.8hs line %d\n(that obj was allocated by %.8hs line %d)\n"),
                    pflr->szFreedBy, pflr->iLineFreed, pflr->szFile, pflr->iLine));
                break;
            }
        }

        DBGPRINTF((TEXT("RATPAK: Trashed memory object (0x%X%08X) was allocated in %.8hs line %d (%d bytes)\n"), (DWORD)(((DWORDLONG)hMem) >> 32), PtrToUlong(hMem), phhoMem->szFile, phhoMem->iLine, phhoMem->cBytesData));
        DBGSTOPX( szFile, iLine, "Either heap object trashed or not allocated object");
    }

    cBytes = phhoMem->cBytesData;

#if 0
    if (cBytes < 0) {
        // Not our object?
        DBGSTOPX( szFile, iLine, "Either heap object trashed or not allocated object");
        return LocalFree(hMem);
    }
#endif

    cBytes += sizeof(HOBJHDR);
    
    // DWORD align
    cBytesAlloc = (cBytes + 3) & ~3;

    { LPBYTE pb = (LPBYTE)(phhoMem);
        pb += cBytes;
        while( cBytes < cBytesAlloc ) {
            if (*pb++ != CH_FILL) {
                DBGPRINTF((TEXT("RATPAK: Trashed memory object (0x%08X) was allocated in %.8hs line %d (%d bytes)\n"),
                        hMem, phhoMem->szFile, phhoMem->iLine, phhoMem->cBytesData));
                DBGSTOPX( szFile, iLine, "End of structure overwritten");
            }
            cBytes++;
        }

        if (*((LPDWORD)pb) != DW_TAG) {
            DBGPRINTF((TEXT("RATPAK: Memory object (0x%08X) was not allocated!\n"), hMem));
            DBGSTOPX( szFile, iLine, "Freeing structure that was not allocated!");
            
            // Not our structure
            return LocalFree(hMem);
        }
    }
    
    // Our structure, check header
    if (phhoMem->phhoNext->phhoPrev != phhoMem || phhoMem->phhoPrev->phhoNext != phhoMem ) {
        DBGPRINTF((TEXT("RATPAK: Orphaned memory object (0x%08X) was allocated in %.8hs line %d (%d bytes)\n"),
                hMem, phhoMem->szFile, phhoMem->iLine, phhoMem->cBytesData));
        DBGSTOPX( szFile, iLine, "Attempting to free orphaned memory object");
    }

    phhoMem->phhoPrev->phhoNext = phhoMem->phhoNext;
    phhoMem->phhoNext->phhoPrev = phhoMem->phhoPrev;
    
    //
    // Log this free, incase we try and free it twice
    //
    
    // Mark as freed
    phhoMem->dwTag2 = 0;
    
    // Remember who alloc'ed obj
    g_pflrUnused->pvPtr = phhoMem;
    CopyMemory( g_pflrUnused->szFile, phhoMem->szFile, sizeof(g_pflrUnused->szFile) );
    g_pflrUnused->iLine = phhoMem->iLine;
    
    // Remember who freed the obj
    for( psz = szFile; *psz != '\0'; psz++ );

    for( ; psz != szFile && *psz != ':' && *psz != '/' && *psz != '\\'; psz--);
    if (*psz == ':' || *psz == '/' || *psz == '\\')
        psz++;

    for( i = 0; i < CCH_LABEL; i++ ) {
        g_pflrUnused->szFreedBy[i] = *psz;
        if (*psz) {
            psz++;
        }
    }
    g_pflrUnused->iLineFreed = iLine;
    
    // Point roaming ptr to next record and mark as unused
    g_pflrUnused = NextFreeLogRec(g_pflrUnused);
    ZeroMemory( g_pflrUnused, sizeof(*g_pflrUnused) );

    return LocalFree(phhoMem);
}

//---------------------------------------------------------------
//
//  void MemExitCheckWorker() {
//
//  Debug replacement for LocalFree
//
//  Parameters:
//      mszFile     ANSI filename (__FILE__)
//      iLine       line number   (__LINE__)
//      hMem        same as LocalAlloc
//
//  History:
//      18-Jan-1996 JonPa       Wrote it
//---------------------------------------------------------------
void MemExitCheckWorker( void ) {
    PHHO phho;

    for( phho = ghhoRoot.phhoNext; phho != &ghhoRoot; phho = phho->phhoNext ) {
        DBGPRINTF((TEXT("RATPAK: Exiting with out freeing object (Header=0x%08X) allocated in %.8hs line %d (%d bytes)\n"),
                phho, phho->szFile, phho->iLine, phho->cBytesData));
    }
}

#endif // DBG_CODE