/* Copyright (c) Microsoft Corporation. All rights reserved. */

#include <mmlite.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <heaps/dbg_heap.h>
#include <util/list-double2.h>
#include "heap.h"

#define UNSAFE_THREAD_STATS
#ifdef UNSAFE_THREAD_STATS
#ifdef _OLDSCHED
#include <base/thread.h>
#include "../base/process.h"
#else
#include <base/thread2.h>
#include <base/process2.h>
#endif
#endif

#include <s_DebugHeap_v.c>
#include <s_DebugHeapFactory_v.c>

#define DPRINT(x)
#define EPRINT(x)

extern PIBASERTL pTheBaseRtl;

/* Debug heap.
 *
 */

/* An in-use block contains:
 * - A preable, as defined below, of 8 bytes
 * - The usable memory block
 * - A sloop, to roundup the usable part to a 16bit word boundary
 * - A trailer of at least 8 bytes
 */
typedef struct _BLOCK_PREAMBLE {
    UINT32 BeginCookie;  /* check we did not get clobbered         */
#define PRE_COOKIE 0xbeefbeef
#define POST_COOKIE 0xbaba
    ADDRESS pc, pc2;     /* Where was the block alloced/etc from */
    HEAP_FLAGS Flags;    /* Flags this block was allocated with */
    struct _BLOCK_PREAMBLE *BlockLink;  /* List of blocks in this heap */
    UINT32 UsableSize;   /* how much can the user have of it */
    UINT16 UserData[1];  /* variable size [NB: type known in code below!]  */
} BLOCK_PREAMBLE, *PBLOCK_PREAMBLE;

#define PREAMBLE_SIZE  (UINT)offsetof(BLOCK_PREAMBLE,UserData)
#define DEFAULT_TRAILER_SIZE 8

#define align16(_x_)  ((((UINT)_x_)+1)&~1)

/* Standard info
 */
typedef struct {
    ADDR_SIZE Reserve;  /* amount of address space reserved for the heap */
    ADDR_SIZE Used;     /* of that, how much is actually allocated       */
    ADDR_SIZE MaxUsed;
    ADDR_SIZE Avail;    /* ..and how much is left free.                  */
} HEAP_STATUS;

/* Some more, look at with debugger
 */
typedef struct {
    UINT AllocCount;
    UINT ReallocCount;
    UINT FreeCount;
    UINT SizeCount;
    UINT ValidateCount;
    UINT ExtractCount;
    UINT StatusCount;
    /* BUGBUG do the IUNKNOWNs too ? nyaa */
} HEAP_STATUS2;

/* Heap header
 */
typedef struct _DHEAPINFO {  /* Per-heap information                      */
    const struct IHeapVtbl *Vtbl; /* Reserved for IHEAP vtbl             */
    UINT Refs;              /* Number of references                      */
    UINT Flags;             /* Passed to CreatHeap                       */
    UINT TrailerSize;       /* User option                               */
    PIHEAP MasterHeap;      /* Where to get/give memory blocks           */
    const _TCHAR *Name;     /* The name of the heap, for debug print use */
    PBLOCK_PREAMBLE BlockList;  /* List of blocks in this heap */
    MUTEX Mutex;                /* For blocklist */
    HEAP_STATUS Status;     /* Reserve, Commit, etc fields               */
    HEAP_STATUS2 Status2;   /* Perf fields                               */
    UINT HeapCookie;        /* Second check field, against corrupt       */
#define HEAP_COOKIE 0xbabebabe
} DHEAPINFO, *PDHEAPINFO;

PRIVATE void DHPDumpContent(PDHEAPINFO phi, BOOL MustBeEmpty);

/* Size accounting
 */
void IncUsedCnt(PDHEAPINFO phi, UINT size)
{
    phi->Status.Used += size;
    phi->Status.Avail -= size;
#if 0
    /* Dump when maximum use seen */
    if (!(phi->Flags & HEAP_NESTED_HEAP)
        && phi->Status.Used > phi->Status.MaxUsed
        && phi->Status.Used > 70000
        )
        DHPDumpContent(phi, FALSE);
#endif
}

#define DecUsedCnt(_phi_,_size_) \
{ \
      (_phi_)->Status.Used -= (_size_); \
      (_phi_)->Status.Avail += (_size_); \
}

/* Perf accounting
 */
#define IncPerfCount(_phi_,_field_) {(_phi_)->Status2._field_##Count++;}

/* Safely typecast
 */
PRIVATE PDHEAPINFO DHPCheckHeap(PIHEAP pHeap)
{
     PDHEAPINFO phi = (PDHEAPINFO) pHeap;

     if ((phi->Vtbl != &DebugHeapVtbl) ||
         (phi->HeapCookie != HEAP_COOKIE)) {
         DebugBreak();
         return NULL;
     }
     return phi;
}

/* Safely typecast
 */
PRIVATE PBLOCK_PREAMBLE DHPCheckBlock(PDHEAPINFO phi, UINT8 *pMem, BOOL Crash)
{
    BLOCK_PREAMBLE *pBlk = NULL;
    UINT Size, i;

    /* Sanity
     */
    if (pMem == NULL) {
        if (Crash)
            DebugBreak();
        return NULL;
    }

    /* Cast
     */
    pBlk = (BLOCK_PREAMBLE *) (pMem - PREAMBLE_SIZE);

    /* Check header
     */
    if (pBlk->BeginCookie != PRE_COOKIE) {
        if (Crash)
            DebugBreak();
        return NULL;
    }

    /* Check trailer
     */
    Size = pBlk->UsableSize;
    if (Size & 1) {
        if (((UINT8*)pBlk->UserData)[Size] != (UINT8) POST_COOKIE) {
            if (Crash)
                DebugBreak();
            return NULL;
        }
        Size++;
    }

    for (i = Size; i < (Size + phi->TrailerSize); i += 2)
        if (pBlk->UserData[i>>1] != POST_COOKIE) {
            if (Crash)
                DebugBreak();
            return NULL;
        }

    /* All is well */
    return pBlk;
}

/* Setup our check info
 */
PRIVATE PTR DHPSetBlock(PDHEAPINFO phi, PBLOCK_PREAMBLE pBlk,
                        UINT Size, ADDRESS pc, ADDRESS pc2,
                        HEAP_FLAGS Flags)
{
    UINT i;

    /* Administratrivia
     */
    IncUsedCnt(phi,Size);
    if (phi->Status.Used > phi->Status.MaxUsed)
        phi->Status.MaxUsed = phi->Status.Used;

    /* Wrap usable memory
     */
    pBlk->BeginCookie = PRE_COOKIE;
    pBlk->UsableSize = Size;
    pBlk->Flags = Flags;
    if (pc) {
        pBlk->pc = pc;
        pBlk->pc2 = pc2;
    }

    if (Size & 1) {
        ((UINT8*)pBlk->UserData)[Size] = (UINT8) POST_COOKIE;
        Size++;
    }

    for (i = Size; i < (Size + phi->TrailerSize); i += 2)
        pBlk->UserData[i>>1] = POST_COOKIE;

    Mutex_Lock(&phi->Mutex);
    /* Add block to list of blocks in this heap */
    pBlk->BlockLink = phi->BlockList;
    phi->BlockList = pBlk;
    Mutex_Unlock(&phi->Mutex);

    return (PTR) &pBlk->UserData[0];
}

PRIVATE void DHPUnsetBlock(PDHEAPINFO phi, PBLOCK_PREAMBLE pBlk)
{
    /* Remove block from blocklist (walk entrire list until found) */
    PBLOCK_PREAMBLE *pp;
    Mutex_Lock(&phi->Mutex);
    for (pp = &phi->BlockList; *pp; pp = &(*pp)->BlockLink) {
        if (*pp == pBlk) {
            *pp = pBlk->BlockLink;
            Mutex_Unlock(&phi->Mutex);
            return;
        }
    }
    Mutex_Unlock(&phi->Mutex);
    DBG(("DHPUnsetBlock: used block list doesn't have the block!\n"));
    DebugBreak();
}

extern PTR pDelayedFree;

PRIVATE void DHPDumpContent(PDHEAPINFO phi, BOOL MustBeEmpty)
{
    HEAP_STATUS2 *st2 = &phi->Status2;
    PBLOCK_PREAMBLE *pp;
    UINT IgnoredSize = 0;
    PITHREAD myth = CurrentThread();

    Mutex_Lock(&phi->Mutex);

    printf("-------------------------------------------\n");
    printf("DHP%s(%s) Reserve=%d Used=%d MaxUsed=%d Avail=%d\n"
         "  OP count: alloc=%d re=%d free=%d sz=%d val=%d ex=%d stat=%d\n",
         MustBeEmpty ? "Release" : "Status",
         phi->Name, phi->Status.Reserve, phi->Status.Used,
         phi->Status.MaxUsed, phi->Status.Avail,
         st2->AllocCount, st2->ReallocCount, st2->FreeCount,
         st2->SizeCount, st2->ValidateCount, st2->ExtractCount,
         st2->StatusCount);

    for (pp = &phi->BlockList; *pp; pp = &(*pp)->BlockLink) {
        char *descr = (char*)((ADDRESS)(*pp)->Flags >> HEAP_FLAGS_SHIFT);
        if ((UINT) descr < 0x1000)
            descr = 0;

        if ((*pp)->UserData == (PTR) myth) {
            /* Current thread can't be freed, don't barf about it */
            IgnoredSize += (*pp)->UsableSize;
        }

        if ((*pp)->Flags & HEAP_PERMANENT) {
            /* Indicate permanent blocks with some stars */
            printf("***");
        }

        printf("BLOCK 0x%08x pc=0x%08x pc2=0x%08x size=%d fl=%x %s",
               (ADDRESS) (*pp)->UserData, (*pp)->pc, (*pp)->pc2,
               (*pp)->UsableSize,
               (*pp)->Flags /*& HEAP_FLAGS_MASK*/, descr ? descr : "?");
#ifdef UNSAFE_THREAD_STATS /* XXX. Extremely unsafe code */
        if (descr && !strcmp("IThread", descr)) {
            /* More debug magic: Thread->Release prints stack stats */
            PITHREAD th = (PITHREAD) (*pp)->UserData; /* XXX knows too much */
            PIPROCESS prc, prcme;
            PIMODULE mod, modme;
            SCODE sc;
            /* There is no lock so there is a race w/ termination here XXX */
            th->v->AddRef(th);
            prc = pTH(th)->StartedIn;
            sc = th->v->GetProcess(th, &prc);
            GetCurrentModule(&modme);
            modme->v->QueryInterface(modme, &IID_IProcess, (void **) &prcme);
            modme->v->Release(modme);

            /* Fish out the name of the process and print it */
            if (prc->v == prcme->v) {/* make sure the process is valid */
                sc = prc->v->QueryInterface(prc, &IID_IModule, (void **) &mod);
                if (SUCCEEDED(sc)) {
                    _TCHAR buf[21];
                    mod->v->GetName(mod, buf, 20);
                    buf[20] = '\0';
                    mod->v->Release(mod);
                    printf(" %s ", buf);
                }
            }

            if (th == myth)
                printf("(current) ");

            prcme->v->Release(prcme);
            th->v->Release(th);
        } else if (descr && !strcmp("IModule", descr)) {
            PIMODULE mod = (PIMODULE) (*pp)->UserData; /* XXX */
            printf(" %s\n", pMD(mod)->pName);
        } else if (descr && !strcmp("nsentry", descr)) {
            typedef struct _NAMEENTRY {
                PIUNKNOWN pObj;    /* pointer to object */
                NAME_SPACE_FLAGS Flags;     /* Flags associated with object */
                struct _LIST_NODE_LINK Link;
                UINT16 StrLen;     /* length of name string */
                _TCHAR Str[1];     /* first character of name string */
            } *xxxne;
            xxxne ne = (xxxne) (*pp)->UserData;
            printf(" %s\n", ne->Str);
        } else if (descr && !strcmp("cobinfo", descr)) {
            typedef struct _COBINFO
            {
                const struct IUnknownVtbl *v;
                PIMODULE pIModule;
                PIUNKNOWN pICob;
            } *xxxce;
            xxxce ce = (xxxce) (*pp)->UserData;
            PMODULE mod = (PMODULE) ce->pIModule;
            if (mod == 0)
                printf(" 0\n");
//            else if (mod == (void*) 0xcccccccc || mod == (void*) 0xbaadf00d)

//                printf(" ?\n");

            else
                printf(" %s\n", mod->pName);
        } else
#endif
            printf("\n");
    }

    if (phi->Status.Used - IgnoredSize && MustBeEmpty) {
        printf("Heap \"%s\" leaked %d bytes\n",
               phi->Name, phi->Status.Used);
        DebugBreak();
    }

    Mutex_Unlock(&phi->Mutex);
}

#ifdef i386
#define GetCallerPc(_pc_,_pc2_) __asm { __asm mov eax,4[ebp] __asm mov _pc_,eax __asm mov eax,0[ebp] __asm mov eax,4[eax] __asm mov _pc2_,eax __asm }
#elif defined(arm) && defined(__GNUC__)
#define GetCallerPc(_pc_,_pc2_) __asm ("mov %0, lr" : "=r" (_pc_) )
#else
#define GetCallerPc(_pc_,_pc2_) 0
#endif

/* *** DHPAlloc
 *
 * Allocates a heap block of at least Size bytes from the heap pointed
 * by pHeap.  If Flags contains HEAP_ZERO_MEMORY also zero fills the new block.
 * Returns pointer to the block of memory, NULL if error.
 */
PRIVATE PTR MCT DHPAlloc(IHeap *pHeap, UINT Flags, UINT Size, UINT Alignment)
{
    PDHEAPINFO phi = DHPCheckHeap(pHeap);
    PIHEAP MasterHeap;
    BLOCK_PREAMBLE *pBlk;
    UINT TrueSize;
    UINT pc = 0, pc2 = 0;

    GetCallerPc(pc, pc2);

    DPRINT(("DHPAlloc: pHeap %x, Flags %x, Size %x Align %x pc=%08x\n",
            (ADDRESS) pHeap, Flags, Size, Alignment, pc, pc2));

    IncPerfCount(phi,Alloc);

    /* Adjust args and allocate
     */
    MasterHeap = phi->MasterHeap;
    Flags |= phi->Flags;
    TrueSize = phi->TrailerSize + PREAMBLE_SIZE + align16(Size);

    pBlk = MasterHeap->v->Alloc(MasterHeap,
       (Flags&HEAP_FLAGS_MASK) | HEAP_NESTED_HEAP | HEAP_DEBUG_NAME(phi->Name),
                                TrueSize, Alignment);
    if (pBlk == NULL) {
        DebugBreak();
        return NULL;
    }
    MasterHeap->v->Validate(MasterHeap,0,pBlk);

    /* Setup our info and cookies, return proper pointer
     */
    return DHPSetBlock(phi,pBlk,Size,pc, pc2, Flags);
}

/* *** DHPRealloc
 *
 * Reallocates the memory block pointed by pMem, in the heap pointed by
 * pHeap to contain at least NewSize bytes.  If NewSize is greater then the
 * current size and Flags contain HEAP_ZERO_MEMORY zero fills new area.
 * If Flags doesn't contain HEAP_REALLOC_IN_PLACE_ONLY then the
 * block's address may change.
 * Returns pointer to the reallocated block, NULL if error.  Note that in
 * case of failure the original pointer still point to the original block.
 * As per ANSI specs, if PTR is NULL it should do an Alloc.
 * As per ANSI specs, if PTR is non null and NewSize is 0 it should do a Free.
 */
PRIVATE PTR MCT DHPRealloc(IHeap *pHeap, UINT Flags, PTR pMem, UINT NewSize, UINT Alignment)
{
    PIHEAP MasterHeap;
    PDHEAPINFO phi = DHPCheckHeap(pHeap);
    BLOCK_PREAMBLE *pBlk, *pBlkOld;
    UINT TrueSize, OldSize;
    SCODE sc;
    UINT pc = 0, pc2 = 0;

    GetCallerPc(pc, pc2);

    IncPerfCount(phi,Realloc);

    Flags |= phi->Flags;

    /* Special cases if special
     */
    if (pMem == NULL)
        return pHeap->v->Alloc(pHeap,Flags,NewSize,Alignment);

    /* Do we have to free it
     */
    if (NewSize == 0) {
        if (pHeap->v->Free(pHeap,Flags,pMem))
            return NULL;
        return pMem;
    }

    /* Make sure its in use and its sane
     */
    pBlk = DHPCheckBlock(phi,pMem,TRUE);
    if (pBlk == NULL)
        return NULL;

    /* I mean.. *really* make sure
     */
    MasterHeap = phi->MasterHeap;
    sc = MasterHeap->v->Validate(MasterHeap,0,pBlk);
    if (FAILED(sc))
        goto Bad;

    /* Zap old block */
    pBlkOld = pBlk;
    OldSize = pBlk->UsableSize;
    DecUsedCnt(phi, OldSize);
    DHPUnsetBlock(phi, pBlk);

    /* Nope, truly realloc
     * BUGBUG could optimize a bit in some cases.
     */
    TrueSize = phi->TrailerSize + PREAMBLE_SIZE + align16(NewSize);
    pBlk = MasterHeap->v->Realloc(MasterHeap,
       (Flags&HEAP_FLAGS_MASK) | HEAP_NESTED_HEAP | HEAP_DEBUG_NAME(phi->Name),
                                  pBlk, TrueSize, Alignment);
    if (pBlk == NULL) {
        /* Whoops, reactivate old block */
        DHPSetBlock(phi,pBlkOld,OldSize, pc,pc2, Flags);
        goto Bad;
    }

    /* Setup our info and cookies, return proper pointer
     */
    return DHPSetBlock(phi,pBlk,NewSize, pc,pc2, Flags);

 Bad:
    DebugBreak();
    return NULL;
}

/* ** DHPFree
 *
 * Free the memory block pointed by pMem, back to the heap pointed
 * by pHeap.  Returns TRUE, FALSE if error.
 */
PRIVATE SCODE MCT DHPFree(IHeap *pHeap, UINT Flags, PTR pMem)
{
    PDHEAPINFO phi = DHPCheckHeap(pHeap);
    BLOCK_PREAMBLE *pBlk;
    UINT TrueSize;
    
    IncPerfCount(phi,Free);

    Flags |= phi->Flags;

    pBlk = DHPCheckBlock(phi,pMem,TRUE);
    if (pBlk != NULL) {
        /* Administratrivia
         */
        DecUsedCnt(phi,pBlk->UsableSize);
        DHPUnsetBlock(phi, pBlk);

        /* If possible, stick an illegal instruction in there so that
         * if someone tries to execute freed code ..poof.
         */
        TrueSize = align16(pBlk->UsableSize) + phi->TrailerSize + PREAMBLE_SIZE;
#if defined(arm)
        {
            UINT i;
            UINT32 *p = (UINT32*) pBlk;
            for (i = 0; i < (TrueSize/sizeof(UINT32)); i++)
                *p++ = 0xe7ffdefe; /* undefined instruction */
        }
#else
        /* NB: This just so happens to be an "INT 3" on x86..
         */
        memset(pBlk,0xcc,TrueSize);
#endif
        
        phi->MasterHeap->v->Validate(phi->MasterHeap,0,pBlk);
        return phi->MasterHeap->v->Free(phi->MasterHeap,Flags,pBlk);
    }

    return E_INVALID_PARAMETER;
}

/* *** DHPSize
 *
 * Returns size of chunk of memory pointed by pmem that has been allocated
 * out of the heap pointed by pHeap.  Returns 0 if error.
 * Note that there is no need to lock the mutex in this call: it doesn't
 * modify the heap. (It is like using the memory vs eg reallocating).
 */
PRIVATE ADDR_SIZE MCT DHPSize(IHeap *pHeap, UINT Flags, PTR pMem)
{
    PDHEAPINFO phi = DHPCheckHeap(pHeap);
    BLOCK_PREAMBLE *pBlk;
    
    UnusedParameter(Flags);

    IncPerfCount(phi,Size);
    pBlk = DHPCheckBlock(phi,pMem,TRUE);
    if (pBlk != NULL)
        return pBlk->UsableSize;
    return 0;
}

/* *** DHPValidate
 *
 * Validates the heap data structures.
 */
PRIVATE SCODE MCT DHPValidate(IHeap *pHeap, UINT Flags, PTR pMem)
{
    PDHEAPINFO phi = DHPCheckHeap(pHeap);
    BLOCK_PREAMBLE *pBlk;

    IncPerfCount(phi,Validate);
    Flags |= phi->Flags;

    if (pMem == NULL)
        return phi->MasterHeap->v->Validate(phi->MasterHeap,Flags,pMem);

    pBlk = DHPCheckBlock(phi,pMem,FALSE);

    return (pBlk) ? S_OK : E_FAIL;

}

/* *** DHPExtract
 *
 * Allocates a memory block from the heap pointed by pHeap.  This routine
 * is similar to Alloc(), but a fourth argument, pMem, has been added.
 * The allocated block must contain the Size bytes of memory that begin
 * at address pMem, but the block may contain additional bytes.  If Flags
 * contains HEAP_ZERO_MEMORY, the routine also zero fills the new block.
 * If the function is successful, it returns a pointer to the block;
 * otherwise, it returns NULL to indicate an error.  The returned pointer
 * may not always be precisely equal to pMem; it may be less than pMem by
 * some number of bytes.  The block can later be freed by calling Free().
 */
PRIVATE PTR MCT DHPExtract(IHeap *pHeap, UINT Flags, PTR pMem, UINT Size)
{
    PDHEAPINFO phi = DHPCheckHeap(pHeap);
    BLOCK_PREAMBLE *pBlk;
    UINT pc = 0, pc2 = 0;

    GetCallerPc(pc,pc2);

    IncPerfCount(phi,Extract);
    Flags |= phi->Flags;

    /* Back the pointer enough to fit our stuff
     * Yes, this can screw them royally.
     */
    pMem = ((UINT8*)pMem - PREAMBLE_SIZE);
    
    /* Grab block, if possible
     */
    pBlk = phi->MasterHeap->v->Extract(phi->MasterHeap,
                         Flags | HEAP_NESTED_HEAP | HEAP_DEBUG_NAME(phi->Name),
                         pMem,
                         Size + PREAMBLE_SIZE);
    if (pBlk == NULL)
        return NULL;

    /* Account for whatever extra fluff we might have triggered
     */
    if (pBlk != pMem)
        Size += (ADDRESS)pMem - (ADDRESS)pBlk;

    /* Setup our info and cookies, return proper pointer
     */
    return DHPSetBlock(phi,pBlk,Size, pc,pc2, Flags);
}


/* *** DHPStatus
 *
 * Returns status information about this heap.
 */
PRIVATE SCODE MCT DHPStatus(IHeap *pHeap, ADDR_SIZE *pReserve, ADDR_SIZE *pCommit, ADDR_SIZE *pUsed, ADDR_SIZE *pMaxUsed)
{
    PDHEAPINFO phi = DHPCheckHeap(pHeap);

    IncPerfCount(phi,Status);

    /* Get the commit size from the real heap. */
    phi->MasterHeap->v->Status(phi->MasterHeap, pReserve, pCommit, NULL, NULL);

    if (pReserve && phi->Status.Reserve)
        *pReserve = phi->Status.Reserve;

    if (pUsed)
        *pUsed = phi->Status.Used;
    if (pMaxUsed) {
        *pMaxUsed = phi->Status.MaxUsed;
        phi->Status.MaxUsed = phi->Status.Used;
    }

    /* If all params are NULL, dump some stats on the screen */
    if (!pReserve && !pCommit && !pUsed && !pMaxUsed)
        DHPDumpContent(phi, FALSE);

    return NO_ERROR;
}

/* *** Constructor: CreateDebugHeap
 *
 * Creates heap from a block of memory.  pmem points to a block of memory
 * in the appropriate address space to be used.  InitSize is the number of
 * bytes already commited at the beginning of memory block.  MaxSize is
 * total number of bytes in memory block. Zero is the biggest possible.
 * Flags passes HEAP_ZERO_MEMORY etc.
 * Returns pointer to the heap, NULL if failed.
 *
 */
SCODE DebugHeapFactoryCreateNested(
    PIHEAPFACTORY iThis,
    PIHEAP MasterHeap,
    UINT Flags,
    UINT MaxSize,
    UINT TrailerSize,
    PIHEAP *ppNewHeap)
{
    PDHEAPINFO phi;
    SCODE sc = E_INVALID_PARAMETER;

    DBG(("Enter CreateDebugHeap MaxSize %08x, Flags %08x Hp %x\n",
         MaxSize,Flags, (UINT) MasterHeap));

    if (MasterHeap == NULL)
        goto ErrorExit;

    /* Allocate&initialize DHEAPINFO structure (per-heap information).
     */
    // XXX HEAP_NO_SERIALIZE required at boot time, should not be used later.

    phi = (PDHEAPINFO) MasterHeap->v->Alloc(MasterHeap,
                                HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY | HEAP_DEBUG_NAME("dbg_heap"),
                                sizeof *phi,0);
    if (phi == NULL)
        goto ErrorExit;

    phi->Vtbl = &DebugHeapVtbl;
    phi->Refs = 1;
    phi->Flags = Flags;
    phi->Name = _T("subheap");
    phi->TrailerSize = (TrailerSize) ? align16(TrailerSize) : DEFAULT_TRAILER_SIZE;
    phi->MasterHeap = MasterHeap;
    phi->HeapCookie = HEAP_COOKIE;

    phi->Status.Reserve = MaxSize;
    phi->Status.Used    = 0;
    phi->Status.MaxUsed = 0;
    phi->Status.Avail   = MaxSize;
    Mutex_Init(&phi->Mutex);

    DPRINT(("Exit CreatHeap\n"));
    *ppNewHeap = (PIHEAP) phi;
    return S_OK;

ErrorExit:
    EPRINT(("Error exit CreatHeap\n"));
    return sc;
}

SCODE DHPCreateHeap(PIHEAP pHeap,
                    UINT Flags, UINT InitialSize, UINT MaxSize,
                    PIHEAP *ppHeap)
{
#if 1
    PDHEAPINFO phi = DHPCheckHeap(pHeap);
    return phi->MasterHeap->v->CreateHeap(phi->MasterHeap, Flags,
                                          InitialSize, MaxSize, ppHeap);
#else
    /* with this heaptstx.exe gets into trouble. Investigate XXX */
    return CreateDebugHeap("subheap", Flags, MaxSize, DEFAULT_TRAILER_SIZE);
#endif
}

/*
 * Exported methods
 */
PRIVATE UINT MCT DHPAddRef(PIHEAP pThis)
{
    PDHEAPINFO phi = DHPCheckHeap(pThis);

    return AtomicInc(&phi->Refs);
}

PRIVATE UINT MCT DHPRelease(PIHEAP pThis)
{
    PDHEAPINFO phi = DHPCheckHeap(pThis);
    UINT newrefs;

    newrefs = AtomicDec(&phi->Refs);

    if (newrefs == 0) {
        PIHEAP MasterHeap = phi->MasterHeap;

        DHPDumpContent(phi, TRUE);
        (void) MasterHeap->v->Free(MasterHeap,0,pThis);
    }

    return newrefs;
}

PRIVATE SCODE MCT DHPQueryInterface(PIHEAP pThis, REFIID pIid, void* *ppUnk)
{
    PDHEAPINFO phi = DHPCheckHeap(pThis);
    return GenericQueryInterface((IUnknown *)phi,pIid,ppUnk,&IID_IHeap);
}

SCODE DebugHeapFactoryQueryInterface (
                         /*in*/ PIHEAPFACTORY pThis,
                         /*in*/ REFIID iid,
                         /*out*/ void** ppObject
)
{
    return GenericQueryInterface((PIUNKNOWN) pThis, iid, ppObject, &IID_IHeapFactory);
}

UINT DebugHeapFactoryAddRef (
        /*in*/ PIHEAPFACTORY pThis
)
{
    PHEAPCOB DbgHeapFactory = (PHEAPCOB) pThis;
    return AtomicInc(&DbgHeapFactory->RefCnt);
}

UINT DebugHeapFactoryRelease (
                 /*in*/ PIHEAPFACTORY pThis
)
{
    PHEAPCOB DbgHeapFactory = (PHEAPCOB) pThis;
    UINT Refs;

    Refs = AtomicDec(&DbgHeapFactory->RefCnt);
    /* Static object, no free */
    return Refs;
}

PRIVATE struct HEAPCOB DbgHeapCobObject = {
    &DebugHeapFactoryVtbl,
    1
};

PIUNKNOWN DbgHeapCobMain(void)
{
    return (PIUNKNOWN) &DbgHeapCobObject;
}