Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h)
Have multiple extension sections in the bld.inf, one for each version
of the compiler. The RVCT version building the tools will build the
runtime libraries for its version, but make sure we extract all the other
versions from zip archives. Also add the archive for RVCT4.
// 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 aTmpSize2 = Max(_ALIGN_UP(aSize2 + RHeap::EAllocCellSize, KAlign), KMinFreeSize);
test((TInt)pHeap->AllocLen(aCell)<=aTmpSize2+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);
}