// 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_heap.cpp
// Overview:
// Tests RHeap class.
// API Information:
// RHeap
// Details:
// - Test that the expected methods are in the DLL by calling each one.
// - Test heap auto expansion and compression by calling Alloc and Compress
// and verifying the results are as expected.
// - Verify the heap dump Base, Size, MinLength, Top and len values.
// - Test the RHeap AllocSize, Alloc, AllocLen, Count and Free methods. Verify
// results are as expected. Check heap object and confirm Invariant status.
// - For an RHeap object, test and verify the results of: allocate some cells,
// free them with Reset, allocate some cells again, free them with Free,
// allocate some cells again, free them backwards, allocate again, free the
// odd cells then the even cells, allocate again, free one half then the other.
// Check heap object and confirm Invariant status.
// - For an RHeap object, test and verify the results of: attempt to resize a
// block above the space available, resize the block to 0, resize positively,
// allocate a block, fill with data, allocate another block or two then resize
// the original block such that it has to be moved in memory, then check the
// blocks' contents, test data was copied on reallocation, resize blocks and
// verify data integrity, expand and shrink, verify data.
// Check heap object and confirm Invariant status.
// - For an RHeap object, test and verify the results of: Alloc some cells,
// verify the Count, Check the object, Free some cells, verify the Count,
// Check and Reset the object, corrupt the heap data and reset the object.
// - Test the leaving methods: AllocL and ReAllocL. Verify the results are as
// expected.
// - Test the RHeap methods: Alloc, Count, Size, Free and Close. Verify results
// are as expected.
// - Test sharing a chunk heap between two separate threads. Each thread
// accesses the shared heap in a timed loop, to ensure that some true
// concurrency.
// - Test sharing a chunk heap between two separate threads. Run each thread in
// a timed loop, to ensure that some true concurrency. Each thread accesses
// the shared heap and results are verified. The heap size is used to verify
// no leaks and that the largest available space is still available. The heap
// is checked to verify that no cells remain allocated after the tests are
// complete.
// - Test sharing a heap between two threads. The thread whose heap it was is
// killed first. Each thread accesses the shared heap and results are
// verified.
// 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>
// Sets data for Test6
#define SetData(size) pHeap->Reset();\
Cell1=pHeap->Alloc(size);\
Cell2=pHeap->Alloc(size);\
Cell3=pHeap->Alloc(size);\
for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+pHeap->AllocLen(Cell1); *pC++='x');\
for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+pHeap->AllocLen(Cell2); *pC++='y');\
for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); *pC++='z');\
OrigLen=pHeap->AllocLen(Cell2);
// Tests cell contents for Test6
#define TestCells(Cell2Len) for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+pHeap->AllocLen(Cell1); test(*pC++=='x'));\
for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+Cell2Len; test(*pC++=='y'));\
for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); test(*pC++=='z'));\
pHeap->Check();
#ifdef __EABI__
IMPORT_D extern const TInt KHeapMinCellSize;
#else
const TInt KHeapMinCellSize = 0;
#endif
const TInt KHeadSize = (TInt)RHeap::EAllocCellSize;
const TInt KAlign = _FOFF(RHeap::_s_align, d);
const TInt KMinCellLength = _ALIGN_UP((KHeapMinCellSize + Max(TInt(RHeap::EFreeCellSize),TInt(RHeap::EAllocCellSize))),KAlign) - RHeap::EAllocCellSize;
const TInt KMinFreeSize = _ALIGN_UP((KHeapMinCellSize + Max(TInt(RHeap::EFreeCellSize),TInt(RHeap::EAllocCellSize))),KAlign);
TInt PageSize;
class RTestHeap : public RHeap
{
public:
void __DbgTest(void* pRHeapDump) const;
};
struct RHeapDump
{
TUint iMinLength;
RChunk iChunk;
TUint8 *iBase;
TUint8 *iTop;
RHeap::SCell iFree;
};
#pragma warning ( disable :4705 ) // statement has no effect
RHeapDump OrigDump;
#pragma warning ( default :4705 )
#if defined(_DEBUG)
void RTestHeap::__DbgTest(void* aPtr) const
{
RHeapDump& d = *(RHeapDump*)aPtr;
d.iMinLength=iMinLength;
d.iChunk.SetHandle(iChunkHandle);
d.iBase=iBase;
d.iTop=iTop;
d.iFree=iFree;
}
#endif
#if defined(_DEBUG)
TBool Invariant(RHeap* aHeap)
{
RHeapDump dump;
((RTestHeap*)aHeap)->__DbgTest(&dump);
if(dump.iMinLength!=OrigDump.iMinLength) return(EFalse);
// Note: iChunk is a class
if(dump.iBase!=OrigDump.iBase) return(EFalse);
if(*dump.iBase!=*OrigDump.iBase) return(EFalse);
if(dump.iTop!=OrigDump.iTop) return(EFalse);
if(dump.iTop[-1]!=OrigDump.iTop[-1]) return(EFalse);
if(dump.iFree.len!=OrigDump.iFree.len) return(EFalse);
// iFree.Next changes during allocation/freeing etc.
return(ETrue);
}
#define INV(x) x;
#else
#define INV(x)
#endif
LOCAL_D RTest test(_L("T_HEAP"));
LOCAL_D TInt heapCount=1;
LOCAL_D RHeap *gHeapPtr;
LOCAL_D RHeap *gHeapPtr2;
class TestRHeap
{
public:
void Test1(void);
void Test2(void);
void Test3(void);
void Test4(void);
void Test5(void);
void Test7(void);
void Test8(void);
void TestCompressAll(void);
void TestOffset(void);
private:
TInt RHeapCalcReduce(TInt aCellSize, TInt aGrowBy);
};
LOCAL_C RHeap* allocHeap(TInt aSize)
//
// Allocate a chunk heap with max size aSize
//
{
TName n;
n.Format(_L("TESTHEAP%d"),heapCount++);
return(User::ChunkHeap(&n,aSize,aSize));
}
////////////////////////////////////////////////////////////////////////////////////////
// Test that methods are in the DLL
////////////////////////////////////////////////////////////////////////////////////////
void TestRHeap::Test1(void)
{
TAny* aCell;
TInt aVar;
RHeap* pHeap=allocHeap(3000); // tests first constructor indirectly
// constructor with Chunk not tested
pHeap->Base();
pHeap->Size();
pHeap->Available(aVar);
pHeap->Check();
pHeap->Count();
pHeap->Count(aVar);
aCell=pHeap->Alloc(50);
pHeap->Free(aCell);
aCell=pHeap->AllocL(50);
pHeap->AllocLen(aCell);
pHeap->ReAlloc(aCell, 100);
pHeap->ReAllocL(aCell, 150);
pHeap->Reset();
pHeap->Close();
}
///////////////////////////////////////////////////////////////////////////////
// Test Assorted Methods 1
//////////////////////////////////////////////////////////////////////////////
void TestRHeap::Test2(void)
{
#if defined(_DEBUG)
RHeapDump dump;
RHeap* pHeap=allocHeap(3000);
((RTestHeap*)pHeap)->__DbgTest(&OrigDump);
((RTestHeap*)pHeap)->__DbgTest(&dump);
test(dump.iBase==pHeap->Base());
test((dump.iTop-dump.iBase)==pHeap->Size());
pHeap->Check();
test(Invariant(pHeap));
pHeap->Close();
#endif
}
///////////////////////////////////////////////////////////////////////////////
// Test Assorted Methods 2
//////////////////////////////////////////////////////////////////////////////
void TestRHeap::Test3(void)
{
TInt CellLen;
TInt OrigBiggestBlock, BiggestBlock;
TAny* aCell;
TInt FreeCount, AllocCount, AllocSize;
RHeap* pHeap=allocHeap(5000);
#if defined(_DEBUG)
((RTestHeap*)pHeap)->__DbgTest(&OrigDump);
#endif
// test AllocSize
AllocCount=pHeap->Count(FreeCount);
test(pHeap->AllocSize(AllocSize)==pHeap->Count());
test(AllocSize==0);
test(AllocCount==pHeap->Count());
test(AllocCount==0);
test(FreeCount==1);
TAny* p1=pHeap->Alloc(1);
test(pHeap->AllocSize(AllocSize)==1);
test(AllocSize==pHeap->AllocLen(p1));
TAny* p2=pHeap->Alloc(8);
test(pHeap->AllocSize(AllocSize)==2);
test(AllocSize==pHeap->AllocLen(p1)+pHeap->AllocLen(p2));
TAny* p3=pHeap->Alloc(127);
test(pHeap->AllocSize(AllocSize)==3);
test(AllocSize==pHeap->AllocLen(p1)+pHeap->AllocLen(p2)+pHeap->AllocLen(p3));
pHeap->Free(p2);
test(pHeap->AllocSize(AllocSize)==2);
test(AllocSize==pHeap->AllocLen(p1)+pHeap->AllocLen(p3));
pHeap->Free(p1);
test(pHeap->AllocSize(AllocSize)==1);
test(AllocSize==pHeap->AllocLen(p3));
pHeap->Free(p3);
test(pHeap->AllocSize(AllocSize)==0);
test(AllocSize==0);
pHeap->Available(OrigBiggestBlock);
// Request too large a block
test((aCell=pHeap->Alloc(OrigBiggestBlock+1))==NULL);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==0);
test(FreeCount==1);
// Request block same size as that available
test((aCell=pHeap->Alloc(OrigBiggestBlock))!=NULL);
test(pHeap->Available(BiggestBlock)==0);
test(BiggestBlock==0);
test(pHeap->AllocLen(aCell)==OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==pHeap->Count());
test(AllocCount==1);
test(FreeCount==0);
pHeap->Check();
// Free the block
pHeap->FreeZ(aCell);
test(aCell==NULL);
pHeap->Available(BiggestBlock);
test(BiggestBlock==OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==0);
test(FreeCount==1);
// Request a block much smaller than that available
test((aCell=pHeap->Alloc(1))!=NULL);
CellLen=pHeap->AllocLen(aCell);
pHeap->Available(BiggestBlock);
test(pHeap->Available(BiggestBlock)==BiggestBlock);
test((BiggestBlock+CellLen+KHeadSize)==OrigBiggestBlock);
// NOTE: if a block of 1000 was initially available, getting a cell of length 100 DOES NOT
// leave 900 available as some of the 1000(KHeadSize) is used up storing the length of the
// allocated block
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==1);
test(FreeCount==1);
pHeap->Check();
// Free the block
pHeap->Free(aCell);
test(aCell!=NULL);
pHeap->Available(BiggestBlock);
test(BiggestBlock==OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==0);
test(FreeCount==1);
// Request a block only just smaller than that available
test((aCell=pHeap->Alloc(OrigBiggestBlock-1))!=NULL);
CellLen=pHeap->AllocLen(aCell);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==1);
test(FreeCount==0);
pHeap->Check();
// Free the block
pHeap->Free(aCell);
pHeap->Available(BiggestBlock);
test(BiggestBlock==OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==0);
test(FreeCount==1);
//Request a block of 0 size Note: 0 may not necessarily be allocated (probably will be 4)
test((aCell=pHeap->Alloc(0))!=NULL);
pHeap->Available(BiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==1);
test(FreeCount==1);
pHeap->Check();
//Free the block
pHeap->Free(aCell);
pHeap->Available(BiggestBlock);
test(BiggestBlock==OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==0);
test(FreeCount==1);
pHeap->Check();
INV(test(Invariant(pHeap)));
// close heap so we don't exceed chunk limit
pHeap->Close();
}
///////////////////////////////////////////////////////////////////////////////
// Test Assorted Methods 3 - Here we go loopy loo, here we go loopy li
//////////////////////////////////////////////////////////////////////////////
void TestRHeap::Test4(void)
{
TInt OrigBiggestBlock, BiggestBlock, FreeCount, AllocCount;
RHeap* pHeap=allocHeap(5000);
pHeap->Available(OrigBiggestBlock);
#if defined(_DEBUG)
((RTestHeap*)pHeap)->__DbgTest(&OrigDump);
#endif
for(TInt ArraySize=1; ArraySize<=100; ArraySize++)
{
TAny** ArrayOfCells;
ArrayOfCells= new TAny*[ArraySize];
TInt ArrayIndex;
// Allocate some cells
for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
ArrayOfCells[ArrayIndex]=pHeap->Alloc(OrigBiggestBlock/(ArraySize*3));
pHeap->Available(BiggestBlock);
test(BiggestBlock!=OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test((TInt)AllocCount==ArraySize);
test(FreeCount==1);
pHeap->Check();
// Now free them with Reset
pHeap->Reset();
pHeap->Available(BiggestBlock);
test(BiggestBlock==OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==0);
test(FreeCount==1);
// Allocate some cells again
for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
ArrayOfCells[ArrayIndex]=pHeap->Alloc(OrigBiggestBlock/(ArraySize*3));
pHeap->Available(BiggestBlock);
test(BiggestBlock!=OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test((TInt)AllocCount==ArraySize);
test(FreeCount==1);
pHeap->Check();
// Free them with Free
for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
pHeap->Free(ArrayOfCells[ArrayIndex]);
pHeap->Available(BiggestBlock);
test(BiggestBlock==OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==0);
test(FreeCount==1);
// Allocate some cells again
for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
ArrayOfCells[ArrayIndex]=pHeap->Alloc(OrigBiggestBlock/(ArraySize*3));
pHeap->Available(BiggestBlock);
test(BiggestBlock!=OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test((TInt)AllocCount==ArraySize);
test(FreeCount==1);
pHeap->Check();
// Free them backwards
for(ArrayIndex=ArraySize-1; ArrayIndex>=0; ArrayIndex--)
pHeap->Free(ArrayOfCells[ArrayIndex]);
pHeap->Available(BiggestBlock);
test(BiggestBlock==OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==0);
test(FreeCount==1);
// Allocate some cells again
for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
ArrayOfCells[ArrayIndex]=pHeap->Alloc(OrigBiggestBlock/(ArraySize*3));
pHeap->Available(BiggestBlock);
test(BiggestBlock!=OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test((TInt)AllocCount==ArraySize);
test(FreeCount==1);
pHeap->Check();
// Free the odd cells then the even cells
for(ArrayIndex=0; ArrayIndex<ArraySize; ArrayIndex+=2)
pHeap->Free(ArrayOfCells[ArrayIndex]);
pHeap->Check();
for(ArrayIndex=1; ArrayIndex<ArraySize; ArrayIndex+=2)
pHeap->Free(ArrayOfCells[ArrayIndex]);
pHeap->Check();
pHeap->Available(BiggestBlock);
test(BiggestBlock==OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==0);
test(FreeCount==1);
// Allocate some cells again
for(ArrayIndex=0; ArrayIndex<ArraySize;ArrayIndex++)
ArrayOfCells[ArrayIndex]=pHeap->Alloc(OrigBiggestBlock/(ArraySize*3));
pHeap->Available(BiggestBlock);
test(BiggestBlock!=OrigBiggestBlock);
AllocCount=pHeap->Count(FreeCount);
test((TInt)AllocCount==ArraySize);
test(FreeCount==1);
pHeap->Check();
// Free one half then the other
for(ArrayIndex=ArraySize-1; ArrayIndex>=ArraySize/2; ArrayIndex--)
pHeap->Free(ArrayOfCells[ArrayIndex]);
for(ArrayIndex=0; ArrayIndex<ArraySize/2; ArrayIndex++)
pHeap->Free(ArrayOfCells[ArrayIndex]);
AllocCount=pHeap->Count(FreeCount);
test(AllocCount==0);
test(FreeCount==1);
delete [] ArrayOfCells;
pHeap->Check();
INV(test(Invariant(pHeap)))
}
// close heap so we don't exceed chunk limit
pHeap->Close();
}
///////////////////////////////////////////////////////////////////////////////
// Test ReAlloc
//////////////////////////////////////////////////////////////////////////////
void TestRHeap::Test5(void)
{
TInt BiggestBlock, CellSize;
RHeap* pHeap=allocHeap(5000);
#if defined(_DEBUG)
((RTestHeap*)pHeap)->__DbgTest(&OrigDump);
#endif
pHeap->Available(BiggestBlock);
TAny* aCell=pHeap->Alloc(BiggestBlock);
// Attempt to resize the block above the space available
test(pHeap->ReAlloc(aCell, BiggestBlock*2)==NULL);
// Resize the block to 0
aCell=pHeap->ReAlloc(aCell, 0);
CellSize=pHeap->AllocLen(aCell); // test?
// Resize positively
for(TInt aSize=0; aSize<=BiggestBlock; aSize++, pHeap->Available(BiggestBlock))
{
test(pHeap->ReAlloc(aCell, aSize)!=NULL);
CellSize=pHeap->AllocLen(aCell);
test(CellSize>=aSize);
if (aSize<KMinCellLength)
test(CellSize==KMinCellLength);
else
test(CellSize<aSize+KAlign);
}
// Note: when increasing a cell size the size is rounded up to the nearest 4 but when
// decreasing a cell the size is rounded down to the nearest 8 - this is due to the fact
// that when memory is released its size must be big enough to hold a free cell header which
// is greater(8) than an allocated header(4)
// i.e. size = 16, resize to 17 => result is 20. But resize to 15 stays as 16, resize to 9
// stays as 16 but resize as 8 will resize to 8
for(TInt aSize2=(TInt)pHeap->AllocLen(aCell); aSize2>=0; aSize2--)
{
test(pHeap->ReAlloc(aCell, aSize2)!=NULL);
test(((TInt)pHeap->AllocLen(aCell)>=aSize2)&&((TInt)pHeap->AllocLen(aCell)<=aSize2+KMinFreeSize));
}
pHeap->Check();
pHeap->Reset();
// Allocate a block, fill with data, allocate another block or two then resize the original
// block such that it has to be moved in memory, then check the blocks' contents
TAny* Cell1=pHeap->Alloc(16);
TText8* pC;
TInt Cell1Size=pHeap->AllocLen(Cell1);
for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; *pC++='x')
;
TAny* Cell2=pHeap->Alloc(16);
TInt Cell2Size=pHeap->AllocLen(Cell2);
for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+pHeap->AllocLen(Cell2); *pC++='y')
;
Cell1=pHeap->ReAlloc(Cell1, 128);
// Test data was copied on reallocation
for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; test(*pC++=='x'))
;
// Test other data wasn't corrupted
for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+pHeap->AllocLen(Cell2); test(*pC++=='y'))
;
// Allocate another block
TAny* Cell3=pHeap->Alloc(8);
for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); *pC++='z')
;
// test existing blocks to be safe
for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; test(*pC++=='x'))
;
for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+Cell2Size; test(*pC++=='y'))
;
// Resize previous blocks
Cell1=pHeap->ReAlloc(Cell1, 16); // Shrink previously expanded block
Cell2=pHeap->ReAlloc(Cell2, 64);
// Now test data
for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; test(*pC++=='x'))
;
for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+Cell2Size; test(*pC++=='y'))
;
for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); test(*pC++=='z'))
;
// Re-expand Cell1
Cell1=pHeap->ReAlloc(Cell1, 1028);
for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; test(*pC++=='x'))
;
for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+Cell2Size; test(*pC++=='y'))
;
for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); test(*pC++=='z'))
;
// Shrink cells back to original size
Cell1=pHeap->ReAlloc(Cell1, Cell1Size);
Cell2=pHeap->ReAlloc(Cell2, Cell2Size);
for(pC=(TText8*)Cell1; pC<(TText8*)Cell1+Cell1Size; test(*pC++=='x'))
;
for(pC=(TText8*)Cell2; pC<(TText8*)Cell2+Cell2Size; test(*pC++=='y'))
;
for(pC=(TText8*)Cell3; pC<(TText8*)Cell3+pHeap->AllocLen(Cell3); test(*pC++=='z'))
;
pHeap->Check();
INV(test(Invariant(pHeap)));
// close heap so we don't exceed chunk limit
pHeap->Close();
}
///////////////////////////////////////////////////////////////////////////////
// Test walking methods (more thoroughly than previously)
//////////////////////////////////////////////////////////////////////////////
void TestRHeap::Test7(void)
{
TInt NumAllocated=0, NumFree=1, i;
RHeap* pHeap=allocHeap(5000);
TAny** ArrayOfCells;
ArrayOfCells= new TAny*[100];
for(i=0; i<100; i++)
{
ArrayOfCells[i]=pHeap->Alloc(8);
NumAllocated++;
test(NumAllocated==pHeap->Count(NumFree));
test(NumFree==1);
}
pHeap->Check();
for(i=0; i<100; i+=2)
{
TInt temp;
pHeap->Free(ArrayOfCells[i]);
NumAllocated--;
NumFree++;
test(NumAllocated==pHeap->Count(temp));
test(NumFree==temp);
}
pHeap->Check();
pHeap->Reset();
///////////////////////////////////////////
// Corrupt data and see what happens
///////////////////////////////////////////
// Corrupt allocated cell header
ArrayOfCells[0]=pHeap->Alloc(32);
TUint32* pC=(TUint32*)ArrayOfCells[0]-KHeadSize;
*pC=0xa5a5a5a5u;
// pHeap->Check();
// Corrupt free cell header
pHeap->Reset();
ArrayOfCells[0]=pHeap->Alloc(32);
pC=(TUint32*)ArrayOfCells[0]+(pHeap->AllocLen(ArrayOfCells[0])>>2);
*pC=0xa1a1a1a1u;
//pHeap->Check(); // Check doesn't pick it up but an access violation is generated
// Write past end of heap
pHeap->Reset();
TInt Avail;
ArrayOfCells[0]=pHeap->Alloc(pHeap->Available(Avail));
pC=(TUint32*)ArrayOfCells[0]+(pHeap->AllocLen(ArrayOfCells[0])>>2);
//*pC=0xa1a1a1a1u; // This line isn't picked up by Check (wouldn't expect it to) but the call
//pHeap->Check(); // to delete below consequently crashes
delete [] ArrayOfCells;
// close heap so we don't exceed chunk limit
pHeap->Close();
}
//////////////////////////////////////
// Test the leave methods
//////////////////////////////////////
void TestRHeap::Test8(void)
{
TAny* aCell=NULL;
RHeap* pHeap=allocHeap(1000);
TRAPD(ret,aCell=pHeap->AllocL(100))
test(ret==KErrNone);
TRAP(ret,aCell=pHeap->AllocL(PageSize))
test(ret==KErrNoMemory);
TRAP(ret,aCell=pHeap->ReAllocL(aCell,32))
test(ret==KErrNone);
TRAP(ret,aCell=pHeap->ReAllocL(NULL,10000))
test(ret==KErrNoMemory);
// close heap so we don't exceed chunk limit
pHeap->Close();
}
class RMyHeap : public RHeap
{
public:
void MyCompressAll(){}
private:
RMyHeap();
};
#include "TestRHeapShrink.h"
/**
Calculates whether or not the heap with iGrowBy=aGrowBy will be reduced if a
cell of size aCellSize bytes is the top free cell.
It must be calculated as both the page size and min cell size could vary
between different platforms/builds. Also, KHeapMinCellSize is 'patchdata' and can be
different for particular ROM builds
ASSUMPTIONS:-
1 - The cell of aCellSize starts past the RHeap's iMinLength (i.e. all of it can be
removed without the RHeap becoming smaller than iMinLength
2 - The default value of aAlign was passed to RHeap contructor
These should be safe as this is onl used by t_heap TestRHeap::CompressAll()
@return The number of bytes the heap will be reduced by
*/
TInt TestRHeap::RHeapCalcReduce(TInt aCellSize, TInt aGrowBy)
{
TInt ret = 0;
TInt pageSize = 0;
test(UserHal::PageSizeInBytes(pageSize)==KErrNone);
// adjust aGrowBy to match what RHeap would have aligned its iGrowBy to
// see RHeap::RHeap()
aGrowBy = _ALIGN_UP(aGrowBy, pageSize);
if (aCellSize >= KHeapShrinkHysRatio*(aGrowBy>>8))
{
//calc for amount to reduce heap from RHeap::Reduce()
// assumes that cell of aCellSize starts past the RHeap's iMinLength
ret=_ALIGN_DOWN(aCellSize, pageSize);
}
return ret;
}
void TestRHeap::TestCompressAll()
{
TPtrC myHeapName=_L("MyHeap");
// myHeap will have default GrowBy of KMinHeapGrowBy
RMyHeap* myHeap=(RMyHeap*)User::ChunkHeap(&myHeapName,0x100,0x2000);
const TInt KnormHeapGrowBy = 0x2000;
RHeap* normHeap=User::ChunkHeap(NULL,0x100,0x20000,KnormHeapGrowBy);
TAny* ptrMy1=myHeap->Alloc(0x102);
test(ptrMy1!=NULL);
TAny* ptrMy2=myHeap->Alloc(0x1001);
test(ptrMy2!=NULL);
TInt r=myHeap->Count();
test(r==2);
TAny* ptrNorm1=normHeap->Alloc(0x8002);
test(ptrNorm1!=NULL);
TAny* ptrNorm2=normHeap->Alloc(0x12fff);
test(ptrNorm2!=NULL);
TAny* ptrNorm3=normHeap->Alloc(0x334f);
test(ptrNorm3!=NULL);
r=normHeap->Count();
test(r==3);
TInt oldMyHeapSize=myHeap->Size();
TInt oldNormHeapSize=normHeap->Size();
myHeap->MyCompressAll();
r=myHeap->Count();
test(r==2);
r=myHeap->Size();
test(r==oldMyHeapSize);
r=normHeap->Count();
test(r==3);
r=normHeap->Size();
test(r==oldNormHeapSize);
// Remove the cell on the top of the normHeap
normHeap->Free(ptrNorm3);
// check myHeap unaffected
r=myHeap->Count();
test(r==2);
r=myHeap->Size();
test(r==oldMyHeapSize);
//check normHeap updated after free of top cell
r=normHeap->Count();
test(r==2);
r=normHeap->Size();
// Calc the amount, if any, the overall size of normHeap will have been shrunk by
// will depend on value of KHeapShrinkHysRatio.
// 1st calc current total size of the allocated cells
TInt normAllocdSize = normHeap->AllocLen(ptrNorm1)+RHeap::EAllocCellSize +
normHeap->AllocLen(ptrNorm2)+RHeap::EAllocCellSize;
TInt normReduce = RHeapCalcReduce(oldNormHeapSize-normAllocdSize,KnormHeapGrowBy);
oldNormHeapSize -= normReduce;
test(r==oldNormHeapSize);
normHeap->Free(ptrNorm2);
myHeap->Free(ptrMy2);
r=myHeap->Count();
test(r==1);
r=myHeap->Size();
// Calc the current total size of the allocated cells
TInt myAllocdSize = myHeap->AllocLen(ptrMy1)+RHeap::EAllocCellSize;
TInt myReduce=RHeapCalcReduce(oldMyHeapSize-myAllocdSize,1);
oldMyHeapSize -= myReduce;
test(r==oldMyHeapSize);
r=normHeap->Count();
test(r==1);
r=normHeap->Size();
// cell represented by ptrNorm3 may have already caused the heap
// size to be reduced so ensure normReduce is factored into calcs
test(r==oldNormHeapSize-(0x16000-normReduce));
myHeap->Close();
normHeap->Close();
}
void TestRHeap::TestOffset()
{
TInt size = 0x100000;
const TInt offset = 0x8;
const TUint8 magic = 0x74; // arbitrary magic value
RChunk chunk;
RHeap* heap;
chunk.CreateLocal(0, size);
size = chunk.MaxSize(); // X86 has 4MB chunk size
// try and create a heap with a large offset - no room to make RHeap, should fail
heap = UserHeap::OffsetChunkHeap(chunk, 0, size);
test(heap==NULL);
// write some magic numbers into the offset-reserved area
chunk.Adjust(offset);
TUint8* reserved = chunk.Base();
TUint8* limit = reserved + offset;
for (; reserved<limit; reserved++)
*reserved = magic;
// make a heap with an offset
heap = UserHeap::OffsetChunkHeap(chunk, 0, offset);
test(heap!=NULL);
test(chunk.Base() + offset == (TUint8*)heap);
TInt origsize = heap->Size();
// force the heap to grow to the maximum size by allocating 1kb blocks
// and then allocating whatever is left. Check this really is the end
// of the chunk.
TUint8* temp = NULL;
TUint8* last = NULL;
do
{
last = temp;
temp = (TUint8*)heap->Alloc(1024);
}
while (temp != NULL);
TInt biggestblock, space;
space = heap->Available(biggestblock);
if (space>0)
{
last = (TUint8*)heap->Alloc(space);
test(last!=NULL);
// Check that the last allocation doesn't pass the end of the chunk
test(last+space <= chunk.Base()+size);
// but that it is within the alignment requirement, as less than this
// would be short of the end
test(last+space > chunk.Base()+size-RHeap::ECellAlignment);
}
else
{
test(last+1024 == chunk.Base()+size);
}
// try writing at the top end of it to make sure it's backed
*(chunk.Base()+size-1) = 1;
// test resetting the heap
heap->Reset();
test(origsize == heap->Size());
// check reducing the heap works
last = (TUint8*)heap->Alloc(size>>2);
TInt midsize = heap->Size();
temp = (TUint8*)heap->Alloc(size>>2);
heap->Free(temp);
heap->Compress();
test(midsize == heap->Size());
heap->Free(last);
heap->Compress();
test(origsize == heap->Size());
// check the magic numbers are still there
for (reserved = chunk.Base(); reserved<limit; reserved++)
test(*reserved==magic);
heap->Close();
}
RSemaphore sem;
LOCAL_C void syncThreads(TAny* anArg)
//
// get the threads both running at the same time
//
{
if ((TInt)anArg==1)
sem.Wait();
else
sem.Signal();
}
TInt comeInNumber=0;
LOCAL_C TInt sharedHeapTest1(TAny* anArg)
//
// Shared heap test thread.
//
{
RHeap* pH = (RHeap*)&User::Allocator();
if (gHeapPtr && pH!=gHeapPtr)
return(KErrGeneral);
gHeapPtr2 = pH;
syncThreads(anArg);
TAny* a[0x100];
TInt mod=((TInt)anArg)*3;
// Run in a timed loop, to ensure that we get some true concurrency
RTimer timer;
TTime now;
TRequestStatus done;
test(timer.CreateLocal()==KErrNone);
now.HomeTime();
timer.At(done,now+TTimeIntervalSeconds(20));
while (done==KRequestPending && comeInNumber!=(TInt)anArg)
{
TInt i=0;
for (;i<0x100;i++)
{
a[i]=User::Alloc(0x10);
test(a[i]!=NULL);
Mem::Fill(a[i],0x10,(((TInt)anArg)<<4)|(i&0x0F)); // marker
if ((i%mod)==0)
pH->Check();
}
for (i=0;i<0x100;i++)
{
User::Free(a[i]);
if ((i%mod)==0)
pH->Check();
}
}
timer.Cancel();
return((TInt)anArg);
}
LOCAL_C void bumpKernelGranularity()
//
// Push up the kernels granularities
//
{
RThread t[4];
TInt r;
TUint i=0;
for (;i<4;i++)
{
TName n;
n.Format(_L("Temp%d"),i);
r=t[i].Create(n,sharedHeapTest1,KDefaultStackSize,NULL,NULL);
test(r==KErrNone);
}
for (i=0;i<4;i++)
{
t[i].Kill(KErrNone);
t[i].Close();
}
}
LOCAL_C void createTestThreads(TThreadFunction aFunction,RHeap* aHeap)
//
// Create two test threads using the supplied entry point and heap
//
{
test.Next(_L("Create t1"));
RThread t1;
TInt r=t1.Create(_L("Shared1"),aFunction,KDefaultStackSize,aHeap,(TAny*)1);
test(r==KErrNone);
TRequestStatus tStat1;
t1.Logon(tStat1);
test(tStat1==KRequestPending);
test.Next(_L("Create t2"));
RThread t2;
r=t2.Create(_L("Shared2"),aFunction,KDefaultStackSize,aHeap,(TAny*)2);
test(r==KErrNone);
TRequestStatus tStat2;
t2.Logon(tStat2);
test(tStat2==KRequestPending);
test.Next(_L("Wait for t1 or t2 - approx 20 seconds"));
t1.Resume();
t2.Resume();
User::WaitForRequest(tStat1,tStat2);
User::WaitForRequest(tStat1==KRequestPending ? tStat1 : tStat2);
test(tStat1==1);
test(tStat2==2);
CLOSE_AND_WAIT(t1);
CLOSE_AND_WAIT(t2);
}
LOCAL_C void SharedHeapTest1()
//
// Shared heap test using normal chunk heap
//
{
sem.CreateLocal(0); // create synchronisation semaphore
test.Start(_L("Create chunk to share"));
TPtrC sharedHeap=_L("SharedHeap");
TInt minsize = ((RHeap&)User::Allocator()).Size();
gHeapPtr=User::ChunkHeap(&sharedHeap,minsize/*0x20000*/,0x40000);
test(gHeapPtr!=NULL);
TInt count=gHeapPtr->Count();
createTestThreads(sharedHeapTest1,gHeapPtr);
test(count==gHeapPtr->Count());
gHeapPtr->Close();
test.End();
}
LOCAL_C void SharedHeapTest2()
//
// Shared heap test using the current threads heap. Can test kernel
// cleanup since granularity will have been handled by running
// SharedHeapTest2().
//
{
test.Start(_L("Current chunk to share"));
test.Next(_L("Bump up granularities"));
//
// First create a number of threads to push up the kernels granularities
//
bumpKernelGranularity();
//
__KHEAP_MARK;
gHeapPtr = (RHeap*)&User::Allocator();
TInt biggest1;
TInt avail1=gHeapPtr->Available(biggest1);
TInt size1=gHeapPtr->Size();
createTestThreads(sharedHeapTest1,NULL);
TInt biggest2;
TInt avail2=gHeapPtr->Available(biggest2);
TInt size2=gHeapPtr->Size();
test.Printf(_L("Before: size %d, %d available (biggest %d)\r\n"),size1,avail1,biggest1);
test.Printf(_L("After: size %d, %d available (biggest %d)\r\n"),size2,avail2,biggest2);
test((size1-avail1)==(size2-avail2)); // no leaks
if (avail1==biggest1) // if it was a single block of free space before
test(avail2==biggest2); // then it should still be a single block
__KHEAP_MARKEND;
test.End();
}
LOCAL_C void SharedHeapTest3()
//
// Shared heap test borrowing a thread's default heap and
// killing threads in different orders.
//
{
test.Start(_L("Create t1 whose heap will be shared"));
gHeapPtr = NULL;
RThread t1;
TInt r=t1.Create(_L("Owner_T1"),sharedHeapTest1,KDefaultStackSize,0x20000,0x40000,(TAny*)1);
test(r==KErrNone);
TRequestStatus tStat1;
t1.Logon(tStat1);
test(tStat1==KRequestPending);
t1.SetPriority(EPriorityMore); //t1 gets to wait on semaphore sem, before we start t2
t1.Resume();
test.Next(_L("Create t2 sharing t1's heap"));
RThread t2;
r=t2.Create(_L("Sharer_T2"),sharedHeapTest1,KDefaultStackSize,gHeapPtr2,(TAny*)2);
test(r==KErrNone);
TRequestStatus tStat2;
t2.Logon(tStat2);
test(tStat2==KRequestPending);
test.Next(_L("Get t1 to exit while t2 continues running"));
test(tStat1==KRequestPending);
test(tStat2==KRequestPending);
t1.SetPriority(EPriorityNormal); //back to the same priority as t2
t2.Resume();
test(tStat1==KRequestPending);
test(tStat2==KRequestPending);
comeInNumber=1;
test.Next(_L("Wait for t1"));
User::WaitForRequest(tStat1);
test(tStat1==1);
test(t1.ExitType()==EExitKill);
test(t1.ExitReason()==1);
test(tStat2==KRequestPending);
test(t2.ExitType()==EExitPending);
test.Next(_L("Wait for t2"));
User::WaitForRequest(tStat2);
test(tStat2==2);
test(t2.ExitType()==EExitKill);
test(t2.ExitReason()==2);
CLOSE_AND_WAIT(t2);
CLOSE_AND_WAIT(t1);
test.End();
}
LOCAL_C void TestAuto()
//
// Test heap auto expansion and compression
//
{
test.Start(_L("Create chunk to"));
TPtrC autoHeap=_L("AutoHeap");
gHeapPtr=User::ChunkHeap(&autoHeap,0x1800,0x6000);
test(gHeapPtr!=NULL);
TInt biggest;
TInt avail=gHeapPtr->Available(biggest);
test(avail==biggest);
TAny *p1=gHeapPtr->Alloc(biggest);
test(p1!=NULL);
TAny *p2=gHeapPtr->Alloc(biggest);
test(p2!=NULL);
TAny *p3=gHeapPtr->Alloc(biggest);
test(p3!=NULL);
TAny *p4=gHeapPtr->Alloc(biggest);
test(p4==NULL);
TInt comp=gHeapPtr->Compress();
test(comp==0);
gHeapPtr->Free(p2);
comp=gHeapPtr->Compress();
test(comp==0);
gHeapPtr->Free(p3);
comp=gHeapPtr->Compress();
// stop wins compiler warning of constant expression as KHeapShrinkHysRatio
// isn't constant for non-emulator builds but ROM 'patchdata'
#pragma warning(disable : 4127)
// When hysteresis value > 4.0*GrowBy then Free() calls
// won't shrink heap but normally will shrink heap
if (KHeapShrinkHysRatio <= 1024)
test(comp==0);
else
test(comp==0x4000);
#pragma warning(default : 4127)
gHeapPtr->Free(p1);
comp=gHeapPtr->Compress();
test(comp==0);
TInt biggest1;
TInt avail1=gHeapPtr->Available(biggest1);
test(avail1==avail1);
test(biggest==biggest1);
test(gHeapPtr->Count()==0);
gHeapPtr->Close();
test.End();
}
GLDEF_C TInt E32Main(void)
{
test.Title();
__KHEAP_MARK;
test.Start(_L("Test 1"));
UserHal::PageSizeInBytes(PageSize);
TestRHeap T;
T.Test1();
test.Next(_L("Test auto expand and compress"));
TestAuto();
test.Next(_L("Test 2"));
T.Test2();
test.Next(_L("Test 3"));
T.Test3();
test.Next(_L("Test 4"));
T.Test4();
test.Next(_L("Test 5"));
T.Test5();
test.Next(_L("Test 7"));
T.Test7();
test.Next(_L("Test 8"));
T.Test8();
test.Next(_L("Test CompressAll()"));
T.TestCompressAll();
test.Next(_L("Test offset heap"));
T.TestOffset();
test.Next(_L("Shared heap test 1"));
SharedHeapTest1();
test.Next(_L("Shared heap test 2"));
SharedHeapTest2();
test.Next(_L("Shared heap test 3"));
SharedHeapTest3();
sem.Close();
__KHEAP_CHECK(0);
__KHEAP_MARKEND;
//
test.End();
return(0);
}