kerneltest/e32test/heap/t_heapslab.cpp
changeset 109 b3a1d9898418
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/heap/t_heapslab.cpp	Fri May 14 17:13:29 2010 +0300
@@ -0,0 +1,1056 @@
+// Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32test\heap\t_heapslab.cpp
+// Overview:
+// Tests RHybridHeap class: slab allocator
+// API Information:
+// RHybridHeap/RHeap
+// Details:
+//- Starts with empty allocator configured to use slab allocation
+//  on all cell sizes less than slab threshold (49).
+//- Allocate enough cells of the same size to fill 128 slabs.
+//- Check the number of pages used corresponds to the number of slabs.
+//- Check that a new slab is taken from a partially filled page if available.
+//- Check that a partially filled slab is used if available.
+//- Check that if all four slabs in a page are free, the page is freed.
+//- Free cells to give empty slab.
+//- Free cells to give partial slab.
+//- Reallocate cells.
+//- RAllocator::Check() is used to check internal consistency.
+// Platforms/Drives/Compatibility:
+// All
+// Assumptions/Requirement/Pre-requisites:
+// Failures and causes:
+// Base Port information:
+// 
+//
+
+#include <e32test.h>
+#include <e32hal.h>
+#include <e32def.h>
+#include <e32def_private.h>
+#include "dla.h"
+#include "slab.h"
+#include "page_alloc.h"
+#include "heap_hybrid.h"
+
+#define MAX_THREADS 4
+#define MAX_ALLOCS 20000 // 16128, if slab count is 128 and alloc size is 8
+
+//#define TSTSLAB_DBG(a) a
+#define TSTSLAB_DBG(a)
+
+struct TSlabTestThreadParm
+    {
+    RHeap*  iHeap;
+    TInt    iAllocSize;
+    TInt    iInitSlabCount;
+    TBool   iUseRandomSize;
+    TInt    iThreadCount;
+    TInt    iThreadIndex;
+    };
+
+struct TMetaData
+    {
+    TBool           iDLOnly;
+    RFastLock*      iLock;
+    TInt            iChunkSize;
+    TInt            iSlabThreshold;
+    unsigned        iSlabInitThreshold;
+    unsigned        iSlabConfigBits;
+    slab*           iPartialPage;
+    slab*           iFullSlab;
+    page*           iSparePage;
+    TUint8*         iMemBase;
+    unsigned char   iSizeMap[(MAXSLABSIZE>>2)+1];
+    slabset         iSlabAlloc[MAXSLABSIZE>>2];
+    slab**          iSlabAllocRealRootAddress[MAXSLABSIZE>>2];
+    };
+
+class TestHybridHeap
+    {
+public:
+    static void GetHeapMetaData(RHeap& aHeap, TMetaData& aMeta);
+    };
+
+LOCAL_D RTest test(_L("T_HEAPSLAB"));
+
+LOCAL_D TInt PageSize;
+
+LOCAL_D TAny* PtrBuf[MAX_THREADS][MAX_ALLOCS];
+LOCAL_D TSlabTestThreadParm ThreadParm[MAX_THREADS];
+
+enum TTestWalkFunc {ETestWalk, ETestFindSlab};
+
+
+static unsigned SlabHeaderPagemap(unsigned h) {return (h&0x00000f00)>>8;}
+
+void TestHybridHeap::GetHeapMetaData(RHeap& aHeap, TMetaData& aMeta)
+{
+    RHybridHeap::STestCommand cmd;
+    cmd.iCommand = RHybridHeap::EHeapMetaData;
+    TInt ret = aHeap.DebugFunction(RHeap::EHybridHeap, &cmd, 0);
+    test(ret == KErrNone);
+    
+    RHybridHeap* hybridHeap = (RHybridHeap*) cmd.iData;
+    
+    aMeta.iDLOnly              = hybridHeap->iDLOnly;
+    aMeta.iLock                = &hybridHeap->iLock;
+    aMeta.iChunkSize           = hybridHeap->iChunkSize;
+    aMeta.iSlabThreshold       = hybridHeap->iSlabThreshold;
+    aMeta.iSlabInitThreshold   = hybridHeap->iSlabInitThreshold;
+    aMeta.iSlabConfigBits      = hybridHeap->iSlabConfigBits;
+    aMeta.iPartialPage         = hybridHeap->iPartialPage;
+    aMeta.iFullSlab            = hybridHeap->iFullSlab;
+    aMeta.iSparePage           = hybridHeap->iSparePage;
+    aMeta.iMemBase             = hybridHeap->iMemBase;
+
+    TInt i;
+    TInt count;
+    count = sizeof(aMeta.iSizeMap)/sizeof(unsigned char);
+    for (i=0; i<count; ++i)
+        {
+        aMeta.iSizeMap[i] = hybridHeap->iSizeMap[i];
+        }
+    count = sizeof(aMeta.iSlabAlloc)/sizeof(slabset);
+    for (i=0; i<count; ++i)
+        {
+        aMeta.iSlabAlloc[i].iPartial = hybridHeap->iSlabAlloc[i].iPartial;
+        aMeta.iSlabAllocRealRootAddress[i] = &hybridHeap->iSlabAlloc[i].iPartial;
+        }
+}
+
+LOCAL_C void GetMeta(RHeap& aHeap, TMetaData& aMeta)
+{
+    TestHybridHeap::GetHeapMetaData(aHeap, aMeta);
+}
+
+/*LOCAL_C void PrintMeta(const char* aText, TMetaData& aMeta)
+{
+    RDebug::Printf("=========== HeapMetaData (local) - begin: %s", aText);
+
+    RDebug::Printf("iDLOnly: 0x%08x", aMeta.iDLOnly);
+    RDebug::Printf("iChunkSize: 0x%08x", aMeta.iChunkSize);
+    RDebug::Printf("iSlabThreshold: 0x%08x / %d", aMeta.iSlabThreshold, aMeta.iSlabThreshold);
+    RDebug::Printf("iSlabInitThreshold: 0x%08x / %d", aMeta.iSlabInitThreshold, aMeta.iSlabInitThreshold);
+    RDebug::Printf("iSlabConfigBits: 0x%08x", aMeta.iSlabConfigBits);
+    RDebug::Printf("iPartialPage: 0x%08x", aMeta.iPartialPage);
+    RDebug::Printf("iFullSlab: 0x%08x", aMeta.iFullSlab);
+    RDebug::Printf("iSparePage: 0x%08x", aMeta.iSparePage);
+    RDebug::Printf("iMemBase: 0x%08x", aMeta.iMemBase);
+
+    TInt i;
+    TInt count;
+    count = sizeof(aMeta.iSizeMap)/sizeof(unsigned char);
+    for (i=0; i<count; ++i)
+        {
+        RDebug::Printf("iSizeMap[%d]: %d", i, aMeta.iSizeMap[i]);
+        }
+    count = sizeof(aMeta.iSlabAlloc)/sizeof(slabset);
+    for (i=0; i<count; ++i)
+        {
+        RDebug::Printf("iSlabAlloc[%d].iPartial: 0x%08x", i, aMeta.iSlabAlloc[i].iPartial);
+        }
+    for (i=0; i<count; ++i)
+        {
+        RDebug::Printf("iSlabAllocRealRootAddress[%d]: 0x%08x", i, aMeta.iSlabAllocRealRootAddress[i]);
+        }
+    RDebug::Printf("=========== HeapMetaData (local) - end");
+}
+
+LOCAL_C void GetAndPrintMeta(RHeap& aHeap, const char* aText, TMetaData& aMeta)
+{
+    (void)aText;
+    GetMeta(aHeap, aMeta);
+    TSTSLAB_DBG(PrintMeta(aText, aMeta));
+}
+
+#ifndef __KERNEL_MODE__
+LOCAL_C void Lock(TMetaData& aMeta)
+   {((RFastLock&)*aMeta.iLock).Wait();}
+
+LOCAL_C void Unlock(TMetaData& aMeta)
+   {((RFastLock&)*aMeta.iLock).Signal();}
+#else
+LOCAL_C void Lock(TMetaData& aMeta)
+   {;}
+
+LOCAL_C void Unlock(TMetaData& aMeta)
+   {;}
+#endif
+*/
+
+LOCAL_C  page* PageFor(slab* s)
+    {
+        return reinterpret_cast<page*>(Floor(s, PAGESIZE));
+    }
+
+   
+LOCAL_C slab* SlabFor(const void* p)
+{
+    return (slab*)(Floor(p, SLABSIZE));
+}
+
+LOCAL_C TInt TreeWalk(slab** aRealRootAddress, slab* const* aRoot, TTestWalkFunc aFunc, TAny* aParm, TInt& aOutParm)
+{
+    TInt count = 0;
+    aOutParm = 0;
+    
+    slab* s = *aRoot;
+    if (!s)
+        return count;
+    
+    for (;;)
+        {
+        slab* c;
+        while ((c = s->iChild1) != 0)
+            s = c;      // walk down left side to end
+        for (;;)
+            {
+            count++;
+            TSTSLAB_DBG(RDebug::Printf("TreeWalk - slab: 0x%08x", s));
+            (void)aParm;
+            if (aFunc == ETestWalk)
+                {
+                ;
+                }
+            else if (aFunc == ETestFindSlab)
+                {
+                if ((slab*)aParm == s)
+                    {
+                    aOutParm = 1;
+                    return 0;
+                    }
+                }
+            
+            c = s->iChild2;
+            if (c)
+                {   // one step down right side, now try and walk down left
+                s = c;
+                break;
+                }
+            for (;;)
+                {   // loop to walk up right side
+                slab** pp = s->iParent;
+                if (pp == aRealRootAddress)
+                    return count;
+                s = SlabFor(pp);
+                if (pp == &s->iChild1)
+                    break;
+                }
+            }
+        }
+}
+
+LOCAL_C TInt WalkSlabSet(TInt aSlabsetIndex, TMetaData& aMeta, TTestWalkFunc aFunc, TAny* aParm, TInt& aOutParm)
+{
+    if (aSlabsetIndex >= (MAXSLABSIZE>>2))
+        {
+        return 0;
+        }
+    return TreeWalk(aMeta.iSlabAllocRealRootAddress[aSlabsetIndex], &aMeta.iSlabAlloc[aSlabsetIndex].iPartial, aFunc, aParm, aOutParm);
+}
+
+/*LOCAL_C void DebugPrintSlabs(TInt aSlabsetIndex, TMetaData& aMeta)
+    {
+    //RDebug::Printf("=========== DebugPrintSlabs: %s", aText);
+    RDebug::Printf("=========== DebugPrintSlabs");
+
+    RDebug::Printf("iSparePage: 0x%08x", aMeta.iSparePage);
+    
+    slab* fullSlab = aMeta.iFullSlab;
+    TInt fullSlabCount = 0;
+    while (fullSlab)
+        {
+        RDebug::Printf("fullSlab: 0x%08x", fullSlab);
+        fullSlabCount++;
+        fullSlab = fullSlab->iChild1;
+        }
+
+    TInt outParm;
+    TInt partialTreeSlabCount = 0;
+    partialTreeSlabCount += WalkSlabSet(aSlabsetIndex, aMeta, ETestWalk, 0, outParm);
+
+    slab* partialPageSlab = aMeta.iPartialPage;
+    TInt partialPageSlabCount = 0;
+    while (partialPageSlab)
+        {
+        RDebug::Printf("partialPageSlab (empty): 0x%08x", partialPageSlab);
+        partialPageSlabCount++;
+        partialPageSlab = partialPageSlab->iChild1;
+        }
+    }*/
+
+LOCAL_C void TestSlabFixedSizeManyThreads(TSlabTestThreadParm& aParm)
+    {
+    RHeap* heap = aParm.iHeap;
+    TInt allocSize = aParm.iAllocSize;
+    TInt initSlabCount = aParm.iInitSlabCount;
+    //TBool useRandomSize = aParm.iUseRandomSize;
+    //TInt threadCount = aParm.iThreadCount;
+    TInt threadIndex = aParm.iThreadIndex;
+
+    TInt slabsPerPage = PageSize/SLABSIZE;
+
+    test(initSlabCount % slabsPerPage == 0); // for this test
+    
+#ifdef _DEBUG
+    TInt allocRealCellSize = allocSize + RHeap::EDebugHdrSize;
+#else
+    TInt allocRealCellSize = allocSize;
+#endif
+
+    TMetaData metaData;
+    GetMeta(*heap, metaData);
+    
+    if (allocRealCellSize >= metaData.iSlabThreshold)
+        {
+        allocRealCellSize = metaData.iSlabThreshold - 1;
+#ifdef _DEBUG
+        allocSize = allocRealCellSize - RHeap::EDebugHdrSize;
+#else
+        allocSize = allocRealCellSize;
+#endif
+        }
+    
+    TAny** pBuf = &PtrBuf[threadIndex][0];
+    TInt i;
+    for (i=0; i<MAX_ALLOCS; ++i)
+        {
+        pBuf[i] = 0;
+        }
+
+    //Allocate enough cells of the same size to fill initSlabCount (128) slabs
+    TInt slabsetIndex = metaData.iSizeMap[(allocRealCellSize+3)>>2];
+    test(slabsetIndex != 0xff);
+    TInt slabCellSize = 4 + (slabsetIndex * 4);
+    
+    TInt slabPayloadSize = SLABSIZE - sizeof(slabhdr);
+    TInt cellCountPerSlab = slabPayloadSize / slabCellSize;
+    TInt initCellCount = initSlabCount * cellCountPerSlab;
+
+    TSTSLAB_DBG(RDebug::Printf("=========== Allocate enough cells of the same size to fill %d slabs", initSlabCount));
+    TSTSLAB_DBG(RDebug::Printf("=========== counts: %d %d %d", cellCountPerSlab, initCellCount, slabCellSize));
+
+    for (i=0; i<initCellCount; ++i)
+        {
+        pBuf[i] = heap->Alloc(allocSize);
+        test(pBuf[i] != 0);
+        }
+
+    heap->Check();
+
+    TInt maxI5 = initCellCount + (cellCountPerSlab*(slabsPerPage+1));
+    for (i=initCellCount; i<maxI5; ++i)
+        {
+        pBuf[i] = heap->Alloc(allocSize);
+        test(pBuf[i] != 0);
+        }
+
+    heap->Check();
+    
+    TAny* p2 = heap->Alloc(allocSize);
+    test(p2 != 0);
+
+    heap->Check();
+    heap->Free(p2);
+
+    heap->Check();
+    
+
+    TInt page2pBufIndexFirst = cellCountPerSlab * slabsPerPage;
+    //TInt page2pBufIndexLast = page2pBufIndexFirst + (cellCountPerSlab * slabsPerPage);
+    
+    slab* partialTreeSlabX1 = SlabFor(pBuf[page2pBufIndexFirst]);
+    page* partialTreeSlabPageX1 = PageFor(partialTreeSlabX1);
+    
+    heap->Free(pBuf[page2pBufIndexFirst]);
+    pBuf[page2pBufIndexFirst] = 0;
+
+    heap->Check();
+    
+    TAny* p3 = heap->Alloc(allocSize);
+    test(p3 != 0);
+    heap->Check();
+    heap->Free(p3);
+    heap->Check();
+    
+    TInt size2 = metaData.iChunkSize;
+    TSTSLAB_DBG(RDebug::Printf("---- size2: 0x%08x", size2));
+    if (metaData.iSparePage)
+        {
+        size2 -= PageSize;
+        }
+
+    for (i=0; i<MAX_ALLOCS; ++i)
+        {
+        if (pBuf[i]) {
+            page* page1 = PageFor(SlabFor(pBuf[i]));
+            if (partialTreeSlabPageX1 == page1)
+                {
+                heap->Free(pBuf[i]);
+                pBuf[i] = 0;
+                }
+            }
+        }
+
+    heap->Check();
+
+    TInt size3 = metaData.iChunkSize;
+    if (metaData.iSparePage)
+        {
+        size3 -= PageSize;
+        }
+
+    TInt bufIndexFirst = cellCountPerSlab;
+    TInt maxI = bufIndexFirst + cellCountPerSlab;
+    for (i=bufIndexFirst; i<=maxI; ++i)
+        {
+        if (pBuf[i])
+            {
+            heap->Free(pBuf[i]);
+            pBuf[i] = 0;
+            }
+        }
+
+    heap->Check();
+
+    TInt firstI = cellCountPerSlab * 3;
+    maxI = firstI + cellCountPerSlab;
+    for (i=firstI; i<=maxI; ++i)
+        {
+        if (i % 3 == 0)
+            {
+            if (pBuf[i])
+                {
+                heap->Free(pBuf[i]);
+                pBuf[i] = 0;
+                }
+            }
+        }
+    
+    heap->Check();
+    
+    //Reallocate cells.
+    for (i=0; i<(MAX_ALLOCS); ++i)
+        {
+        if (pBuf[i] != 0)
+            {
+            pBuf[i] = heap->ReAlloc(pBuf[i], allocSize);
+            test(pBuf[i] != 0);
+            }
+        }
+    
+    heap->Check();
+    
+    //Allocate cells.
+    for (i=0; i<(MAX_ALLOCS/4); ++i)
+        {
+        if (pBuf[i] == 0)
+            {
+            pBuf[i] = heap->Alloc(allocSize);
+            test(pBuf[i] != 0);
+            }
+        }
+
+    heap->Check();
+    
+    for (i=0; i<MAX_ALLOCS; ++i)
+        {
+        if (pBuf[i])
+            {
+            heap->Free(pBuf[i]);
+            pBuf[i] = 0;
+            }
+        }
+    heap->Check();
+
+    TSTSLAB_DBG(RDebug::Printf("=========== TestSlabFixedSizeManyThreads end"));
+    }
+    
+    
+LOCAL_C void TestSlabFixedSizeOneThread(TSlabTestThreadParm& aParm)
+    {
+    RHeap* heap = aParm.iHeap;
+    TInt allocSize = aParm.iAllocSize;
+    TInt initSlabCount = aParm.iInitSlabCount;
+    //TBool useRandomSize = aParm.iUseRandomSize;
+    //TInt threadCount = aParm.iThreadCount;
+    TInt threadIndex = aParm.iThreadIndex;
+
+    TInt slabsPerPage = PageSize/SLABSIZE;
+
+    test(initSlabCount % slabsPerPage == 0); // for this test
+    
+#ifdef _DEBUG
+    TInt allocRealCellSize = allocSize + RHeap::EDebugHdrSize;
+#else
+    TInt allocRealCellSize = allocSize;
+#endif
+
+    TMetaData metaData;
+    GetMeta(*heap, metaData);
+    
+    TSTSLAB_DBG(PrintMeta(" --- TestSlabFixedSizeOneThread start", metaData));
+
+    if (allocRealCellSize >= metaData.iSlabThreshold)
+        {
+        allocRealCellSize = metaData.iSlabThreshold - 1;
+#ifdef _DEBUG
+        allocSize = allocRealCellSize - RHeap::EDebugHdrSize;
+#else
+        allocSize = allocRealCellSize;
+#endif
+        }
+
+    TAny** pBuf = &PtrBuf[threadIndex][0];
+    TInt i;
+    for (i=0; i<MAX_ALLOCS; ++i)
+        {
+        pBuf[i] = 0;
+        }
+
+    //Allocate enough cells of the same size to fill initSlabCount (128) slabs
+    TInt slabsetIndex = metaData.iSizeMap[(allocRealCellSize+3)>>2];
+    test(slabsetIndex != 0xff);
+    TInt slabCellSize = 4 + (slabsetIndex * 4);
+    
+    TInt slabPayloadSize = SLABSIZE - sizeof(slabhdr);
+    TInt cellCountPerSlab = slabPayloadSize / slabCellSize;
+    TInt initCellCount = initSlabCount * cellCountPerSlab;
+
+    TSTSLAB_DBG(RDebug::Printf("=========== Allocate enough cells of the same size to fill %d slabs", initSlabCount));
+    TSTSLAB_DBG(RDebug::Printf("=========== counts: %d %d %d", cellCountPerSlab, initCellCount, slabCellSize));
+
+    for (i=0; i<initCellCount; ++i)
+        {
+        pBuf[i] = heap->Alloc(allocSize);
+        test(pBuf[i] != 0);
+        }
+
+    heap->Check();
+    GetMeta(*heap, metaData);
+    
+    TSTSLAB_DBG(PrintMeta("after init allocs", metaData));
+    TSTSLAB_DBG(DebugPrintSlabs(slabsetIndex, metaData));
+
+    //Check the number of pages used corresponds to the number of slabs.
+    TSTSLAB_DBG(RDebug::Printf("=========== Check the number of pages used corresponds to the number of slabs"));
+    
+    TInt pageCountForSlabs1 = (metaData.iChunkSize / PageSize) - 1;
+    TInt pageCountForSlabs2 = (initSlabCount+(slabsPerPage-1)) / slabsPerPage;
+    TSTSLAB_DBG(RDebug::Printf("=========== page counts: %d %d", pageCountForSlabs1, pageCountForSlabs2));
+    test(pageCountForSlabs1 == pageCountForSlabs2);
+
+    //-----------------------------------------
+    TSTSLAB_DBG(RDebug::Printf("=========== check slab counts in the lists"));
+    
+    slab* fullSlab = metaData.iFullSlab;
+    TInt fullSlabCount = 0;
+    while (fullSlab)
+        {
+        TSTSLAB_DBG(RDebug::Printf("fullSlab: 0x%08x", fullSlab));
+        fullSlabCount++;
+        fullSlab = fullSlab->iChild1;
+        }
+
+    TInt outParm;
+    TInt partialTreeSlabCount = 0;
+    partialTreeSlabCount = WalkSlabSet(slabsetIndex, metaData, ETestWalk, 0, outParm);
+
+    slab* partialPageSlab = metaData.iPartialPage;
+    TInt partialPageSlabCount = 0;
+    while (partialPageSlab)
+        {
+        TSTSLAB_DBG(RDebug::Printf("partialPageSlab (empty): 0x%08x", partialPageSlab));
+        partialPageSlabCount++;
+        partialPageSlab = partialPageSlab->iChild1;
+        }
+
+    test(fullSlabCount == (initSlabCount-1));
+    test(partialTreeSlabCount == 1);
+    if (initSlabCount % slabsPerPage == 0)
+        {
+        test(partialPageSlabCount == 0);
+        }
+    else
+        {
+        test(partialPageSlabCount == 1);
+        }
+    //-----------------------------------------
+    TSTSLAB_DBG(RDebug::Printf("=========== alloc one cell more -> one full slab more"));
+
+    TAny* p = heap->Alloc(allocSize);
+    test(p != 0);
+    
+    heap->Check();
+    GetMeta(*heap, metaData);
+    TSTSLAB_DBG(DebugPrintSlabs(slabsetIndex, metaData));
+    
+    fullSlab = metaData.iFullSlab;
+    fullSlabCount = 0;
+    while (fullSlab)
+        {
+        TSTSLAB_DBG(RDebug::Printf("fullSlab: 0x%08x", fullSlab));
+        fullSlabCount++;
+        fullSlab = fullSlab->iChild1;
+        }
+    test(fullSlabCount == initSlabCount);
+
+    heap->Free(p);
+
+    heap->Check();
+    GetMeta(*heap, metaData);
+    TSTSLAB_DBG(DebugPrintSlabs(slabsetIndex, metaData));
+    
+    //-----------------------------------------
+    //Check that a new slab is taken from a partially filled page if available.
+    TSTSLAB_DBG(RDebug::Printf("=========== Check that a new slab is taken from a partially filled page if available"));
+
+    // fill the first slab in the page (after iSparePage)
+    TInt maxI5 = initCellCount + (cellCountPerSlab*(slabsPerPage+1));
+    for (i=initCellCount; i<maxI5; ++i)
+        {
+        pBuf[i] = heap->Alloc(allocSize);
+        test(pBuf[i] != 0);
+        }
+
+    heap->Check();
+    GetMeta(*heap, metaData);
+    TSTSLAB_DBG(DebugPrintSlabs(slabsetIndex, metaData));
+    
+    partialPageSlab = metaData.iPartialPage;
+    partialPageSlabCount = 0;
+    while (partialPageSlab)
+        {
+        TSTSLAB_DBG(RDebug::Printf("partialPageSlab (empty): 0x%08x", partialPageSlab));
+        partialPageSlabCount++;
+        partialPageSlab = partialPageSlab->iChild1;
+        }
+    test(partialPageSlabCount == 1);
+
+    page* page1 = PageFor(metaData.iPartialPage);
+    unsigned header = page1->iSlabs[0].iHeader;
+    unsigned pagemap = SlabHeaderPagemap(header);
+    unsigned slabix = LOWBIT(pagemap);
+    slab* partialPageSlab2 = &page1->iSlabs[slabix];
+    
+    TAny* p2 = heap->Alloc(allocSize);
+    test(p2 != 0);
+
+    heap->Check();
+    TSTSLAB_DBG(RDebug::Printf("p2: 0x%08x; partialPageSlab2: 0x%08x", p2, partialPageSlab2));
+    test(partialPageSlab2 == SlabFor(p2));
+    heap->Free(p2);
+
+    heap->Check();
+    
+    //-----------------------------
+    // use the second page for the next test
+    TInt page2pBufIndexFirst = cellCountPerSlab * slabsPerPage;
+    //TInt page2pBufIndexLast = page2pBufIndexFirst + (cellCountPerSlab * slabsPerPage);
+    
+    //-----------------------------------------
+    //Check that a partially filled slab is used if available.
+    TSTSLAB_DBG(RDebug::Printf("=========== Check that a partially filled slab is used if available"));
+
+    slab* partialTreeSlabX1 = SlabFor(pBuf[page2pBufIndexFirst]);
+    page* partialTreeSlabPageX1 = PageFor(partialTreeSlabX1);
+    
+    heap->Free(pBuf[page2pBufIndexFirst]);
+    pBuf[page2pBufIndexFirst] = 0;
+
+    heap->Check();
+    
+    TAny* p3 = heap->Alloc(allocSize);
+    test(p3 != 0);
+    heap->Check();
+    test(partialTreeSlabX1 == SlabFor(p3));
+    heap->Free(p3);
+    heap->Check();
+    
+    //-----------------------------------------
+    //Check that if all four slabs in a page are free the page is freed.
+    TSTSLAB_DBG(RDebug::Printf("=========== Check that if all four slabs in a page are free, the page is freed"));
+
+    GetMeta(*heap, metaData);
+    TSTSLAB_DBG(DebugPrintSlabs(slabsetIndex, metaData));
+
+    TInt size2 = metaData.iChunkSize;
+    TSTSLAB_DBG(RDebug::Printf("---- size2: 0x%08x", size2));
+    if (metaData.iSparePage)
+        {
+        size2 -= PageSize;
+        }
+
+    for (i=0; i<MAX_ALLOCS; ++i)
+        {
+        if (pBuf[i]) {
+            page* page1 = PageFor(SlabFor(pBuf[i]));
+            if (partialTreeSlabPageX1 == page1)
+                {
+                heap->Free(pBuf[i]);
+                pBuf[i] = 0;
+                }
+            }
+        }
+
+    heap->Check();
+    GetMeta(*heap, metaData);
+    TSTSLAB_DBG(DebugPrintSlabs(slabsetIndex, metaData));
+
+    TInt size3 = metaData.iChunkSize;
+    if (metaData.iSparePage)
+        {
+        size3 -= PageSize;
+        }
+
+    test(size3 == (size2-PageSize));
+
+    //-----------------------------------------
+    //Free cells to give empty slab (The second slab in the first page)
+    TSTSLAB_DBG(RDebug::Printf("=========== Free cells to give empty slab (The second slab in the first page)"));
+    slab* emptySlabAddr = (slab*)(metaData.iMemBase + SLABSIZE);
+
+    //Check that emptySlabAddr is not already in iPartialPage list
+    partialPageSlab = metaData.iPartialPage;
+    while (partialPageSlab)
+        {
+        if (partialPageSlab == emptySlabAddr)
+            {
+            test(0);
+            }
+        partialPageSlab = partialPageSlab->iChild1;
+        }
+
+    // free cells to give empty slab  - emptySlabAddr
+    TInt bufIndexFirst = cellCountPerSlab;
+    TInt maxI = bufIndexFirst + cellCountPerSlab;
+    for (i=bufIndexFirst; i<=maxI; ++i)
+        {
+        if (pBuf[i])
+            {
+            heap->Free(pBuf[i]);
+            pBuf[i] = 0;
+            }
+        }
+
+    heap->Check();
+    GetMeta(*heap, metaData);
+    TSTSLAB_DBG(DebugPrintSlabs(slabsetIndex, metaData));
+
+    // Check that emptySlabAddr is not now in iPartialPage list
+    partialPageSlab = metaData.iPartialPage;
+    while (partialPageSlab)
+        {
+        if (partialPageSlab == emptySlabAddr)
+            {
+            break;
+            }
+        partialPageSlab = partialPageSlab->iChild1;
+        }
+    test(partialPageSlab != 0);
+
+    //Free cells to give partial slab (The third slab in the first page)
+    TSTSLAB_DBG(RDebug::Printf("=========== Free cells to give partial slab (The third slab in the first page)"));
+    slab* partialSlabAddr = (slab*)(metaData.iMemBase + (3*SLABSIZE));
+
+    // Check that partialSlabAddr is not now in iPartialSlab list
+    WalkSlabSet(slabsetIndex, metaData, ETestFindSlab, partialSlabAddr, outParm);
+    test(outParm == 0);
+
+    TInt firstI = cellCountPerSlab * 3;
+    maxI = firstI + cellCountPerSlab;
+    for (i=firstI; i<=maxI; ++i)
+        {
+        if (i % 3 == 0)
+            {
+            if (pBuf[i])
+                {
+                heap->Free(pBuf[i]);
+                pBuf[i] = 0;
+                }
+            }
+        }
+    
+    heap->Check();
+    GetMeta(*heap, metaData);
+    TSTSLAB_DBG(DebugPrintSlabs(slabsetIndex, metaData));
+    
+    // Check that partialSlabAddr is now in iPartialSlab list
+    WalkSlabSet(slabsetIndex, metaData, ETestFindSlab, partialSlabAddr, outParm);
+    test(outParm == 1);
+    
+    //Reallocate cells.
+    for (i=0; i<(MAX_ALLOCS); ++i)
+        {
+        if (pBuf[i] != 0)
+            {
+            pBuf[i] = heap->ReAlloc(pBuf[i], allocSize);
+            test(pBuf[i] != 0);
+            }
+        }
+
+    heap->Check();
+    
+    //Allocate cells.
+    for (i=0; i<(MAX_ALLOCS/4); ++i)
+        {
+        if (pBuf[i] == 0)
+            {
+            pBuf[i] = heap->Alloc(allocSize);
+            test(pBuf[i] != 0);
+            }
+        }
+
+    heap->Check();
+    
+    for (i=0; i<MAX_ALLOCS; ++i)
+        {
+        if (pBuf[i])
+            {
+            heap->Free(pBuf[i]);
+            pBuf[i] = 0;
+            }
+        }
+    heap->Check();
+
+    TSTSLAB_DBG(RDebug::Printf("=========== TestSlabFixedSizeOneThread end"));
+    }
+
+LOCAL_C RHeap* CreateSlabHeap(TInt aThreadCount)
+{
+    //TPtrC slabHeap=_L("SlabHeap");
+    //RHeap* heap = User::ChunkHeap(&slabHeap, 0x1000, 0x10000);
+    TInt maxLth = 0x60000 * aThreadCount;
+    RHeap* heap = User::ChunkHeap(0, 0x1000, maxLth);
+    test(heap!=NULL);
+
+    // Configure heap for slab
+    RHybridHeap::STestCommand cmd;
+    cmd.iCommand = RHybridHeap::ESetConfig;
+    cmd.iConfig.iSlabBits = 0xabe;
+    cmd.iConfig.iDelayedSlabThreshold = 0;
+    cmd.iConfig.iPagePower = 0;  // 16 // 0 -> no page allocator
+    TInt ret = heap->DebugFunction(RHeap::EHybridHeap, &cmd, 0);
+    test(ret == KErrNone);
+    
+    return heap;
+}
+
+LOCAL_C TInt SlabTestManyThreads(TAny* aThreadParm)
+    {
+    TSlabTestThreadParm* parm = (TSlabTestThreadParm*)aThreadParm;
+
+    TInt i;
+    TInt maxLoops = 30; //300;
+    for (i=0; i<maxLoops; ++i)
+        {
+        TestSlabFixedSizeManyThreads(*parm);
+        }
+                                
+    return KErrNone;
+    }
+    
+LOCAL_C TInt SlabTestOneThread(TAny* aThreadParm)
+    {
+    TSlabTestThreadParm* parm = (TSlabTestThreadParm*)aThreadParm;
+    TestSlabFixedSizeOneThread(*parm);
+    return KErrNone;
+    }
+    
+TInt StartThreads(TInt aThreadCount, TSlabTestThreadParm& aThreadParm)
+    {
+    const TInt KSlabTestThreadStackSize=0x4000; //0x10000; //0x2000;
+
+    TRequestStatus  theStatus[MAX_THREADS];
+    RThread         theThreads[MAX_THREADS];
+    TBool           threadInUse[MAX_THREADS];
+
+    TInt index;
+    TInt ret;
+
+    if (aThreadCount <= 0)
+        {
+        return KErrNone;
+        }
+
+    RHeap* heap = CreateSlabHeap(aThreadCount);
+    aThreadParm.iHeap = heap;
+    
+    for (index = 0; index < aThreadCount; index++)
+        {
+        ThreadParm[index].iHeap             = aThreadParm.iHeap;
+        ThreadParm[index].iAllocSize        = aThreadParm.iAllocSize;
+        ThreadParm[index].iInitSlabCount    = aThreadParm.iInitSlabCount;
+        ThreadParm[index].iUseRandomSize    = aThreadParm.iUseRandomSize;
+        ThreadParm[index].iThreadCount      = aThreadParm.iThreadCount;
+        
+        ThreadParm[index].iThreadIndex = index;
+        
+        TBuf<32> threadName;
+        threadName.Format(_L("SlabTest%d"), index);
+        if (aThreadCount == 1)
+            {
+            ret = theThreads[index].Create(threadName, SlabTestOneThread, KSlabTestThreadStackSize, NULL, (TAny*)&ThreadParm[index]);
+            }
+        else
+            {
+            ret = theThreads[index].Create(threadName, SlabTestManyThreads, KSlabTestThreadStackSize, NULL, (TAny*)&ThreadParm[index]);
+            }
+        test(ret == KErrNone);
+        theThreads[index].Logon(theStatus[index]);
+        test(theStatus[index] == KRequestPending);
+        threadInUse[index] = ETrue;
+        theThreads[index].Resume();
+        }
+
+    User::WaitForAnyRequest();
+    
+    TBool anyUsed = ETrue;
+    while (anyUsed)
+        {
+        User::After(1001000);
+        anyUsed = EFalse;
+        for (index = 0; index < aThreadCount; index++)
+            {
+            if (threadInUse[index])
+                {
+                if (theThreads[index].ExitType() != EExitPending)
+                    {
+                    threadInUse[index] = EFalse;
+                    }
+                else
+                    {
+                    anyUsed = ETrue;
+                    }
+                }
+            }
+        }
+    
+    for (index = 0; index < aThreadCount; index++)
+        {
+        theThreads[index].Close();
+        }
+    TSTSLAB_DBG(RDebug::Printf("=========== StartThreads end"));
+    heap->Close();
+
+    return KErrNone;
+    }
+
+GLDEF_C TInt E32Main(void)
+    {
+    TInt ret;
+    
+    test.Title();
+
+    __KHEAP_MARK;
+    
+    test.Start(_L("TestSlab"));
+    UserHal::PageSizeInBytes(PageSize);
+
+    RHeap* heap = CreateSlabHeap(1);
+    
+    TMetaData metaData;
+    GetMeta(*heap, metaData);
+
+    heap->Close();
+
+    if (metaData.iDLOnly)
+        {
+        test.Printf(_L("Slab allocator is not used, no tests to run\n"));
+        __KHEAP_MARKEND;
+        test.End();
+        return(0);
+        }
+    
+    TSlabTestThreadParm threadParm;
+    threadParm.iHeap = heap;
+    threadParm.iAllocSize = 17;
+    threadParm.iInitSlabCount = 128; // 12
+    threadParm.iUseRandomSize = EFalse;
+
+    test.Next(_L("TestSlab - one thread"));
+
+    TInt threadCount;
+    threadCount = 1;
+    if (threadCount > MAX_THREADS)
+        {
+        threadCount = MAX_THREADS;
+        }
+    threadParm.iThreadCount = threadCount;
+    
+#if 0
+    ret = StartThreads(threadCount, threadParm);
+    test(ret==KErrNone);
+    
+#else   
+    
+    TInt i;
+    for (i=1; i<metaData.iSlabThreshold; ++i)
+        {
+#ifdef _DEBUG
+        if ((i + RHeap::EDebugHdrSize) >= metaData.iSlabThreshold)
+            {
+            break;
+            }
+#endif // _DEBUG
+        TSTSLAB_DBG(RDebug::Printf("=========== StartThreads size: %d", i));
+        threadParm.iAllocSize = i;
+        test.Printf(_L("AllocSize: %d\n"), i);
+        ret = StartThreads(threadCount, threadParm);
+        test(ret==KErrNone);
+        }
+#endif
+
+    
+    test.Next(_L("TestSlab - many threads"));
+
+    threadParm.iAllocSize = 17;
+    
+    threadCount = 3;
+    if (threadCount > MAX_THREADS)
+        {
+        threadCount = MAX_THREADS;
+        }
+    threadParm.iThreadCount = threadCount;
+    
+#if 1
+    ret = StartThreads(threadCount, threadParm);
+    test(ret==KErrNone);
+    
+#else   
+    
+    TInt i;
+    for (i=1; i<metaData.iSlabThreshold; ++i)
+        {
+#ifdef _DEBUG
+        if ((i + RHeap::EDebugHdrSize) >= metaData.iSlabThreshold)
+            {
+            break;
+            }
+#endif // _DEBUG
+        TSTSLAB_DBG(RDebug::Printf("=========== StartThreads size: %d", i));
+        threadParm.iAllocSize = i;
+        test.Printf(_L("AllocSize: %d\n"), i);
+        ret = StartThreads(threadCount, threadParm);
+        test(ret==KErrNone);
+        }
+#endif
+
+    __KHEAP_MARKEND;
+    
+    test.End();
+    return(0);
+    }