kerneltest/e32test/heap/t_heapdb.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 0 a41df078684a
child 43 c1f20ce4abcf
permissions -rw-r--r--
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_heapdb.cpp
// Tests the _DEBUG build dependent aspects of RHeap
// Overview:
// Tests debug build dependent aspects of RHeap.  
// API Information:
// RHeap.
// Details:
// Test1:
// - Allocate a variety of user heap objects and verify the nesting level, allocation count, 
// allocation level and length are correct. Also check for heap corruption.
// Test 2:
// - Some assorted indirect calls to alloc, and verify the nesting level, allocation count, 
// allocation level and length are correct. Also check for heap corruption.
// Test3:
// - Allocate a variety of objects and verify that the UHEAP_CHECKALL count is correct.
// - Verify the nesting of UHEAP_MARK and UHEAP_MARKEND macros.
// - Check the validity of the current thread's default heap.
// Test4:
// - Allocate memory for different heaps, check the total number of allocated cells
// for different heaps and for the current nested level is as expected.
// Test5:
// - Simulate heap allocation failures, allocate the memory from user and 
// kernel heap and check results are as expected.
// Platforms/Drives/Compatibility:
// All
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#include <e32test.h>
#include <e32def.h>
#include <e32def_private.h>

LOCAL_D RTest test(_L("T_HEAPDB"));

#if defined(_DEBUG)

RHeap::SHeapCellInfo CellInfo[4];

class RTestHeap : public RHeap
	{
public:
	void AttachInfo(SHeapCellInfo* aInfo)
		{iTestData = aInfo;}
	};

void AttachToHeap(RHeap* aHeap, TInt aInfo)
	{
	if (!aHeap)
		aHeap = (RHeap*)&User::Allocator();
	((RTestHeap*)aHeap)->AttachInfo(CellInfo + aInfo);
	}

void TestCellInfo(TInt aInfo, TInt aNest, TInt aAllocCount, TInt aLevelAlloc, TInt aSize, TAny* aAddr)
	{
	RHeap::SHeapCellInfo& ci = CellInfo[aInfo];
	RHeap::SDebugCell& cell = *ci.iStranded;
	test(cell.nestingLevel == aNest);
	test(cell.allocCount == aAllocCount);
	test(ci.iLevelAlloc == aLevelAlloc);
	test(cell.len == aSize + RHeap::EAllocCellSize);
	test((&cell+1) == aAddr);
	}

const TInt KMaxFailureRate=100;
const TInt KThreadMemError=-50;
const TInt KCellSize=(sizeof(RHeap::SCell)); // Size of free cell header	
const TInt KHeadSize=(sizeof(RHeap::SDebugCell)); // Size of allocated cell header with space for heaven info

LOCAL_D TInt heapCount=1;
LOCAL_D RSemaphore threadSemaphore;
LOCAL_D TBool array1[KMaxFailureRate+1];
LOCAL_D	TBool array2[KMaxFailureRate+1];

LOCAL_C TInt ThreadEntryPoint(TAny*)
	{
	threadSemaphore.Wait();
	if (User::Alloc(4)==NULL)
		return(KThreadMemError);
	else
		return(KErrNone);
	} 

class TestRHeapDebug
	{
public:
	void Test1(void);
	void Test2(void);
	void Test3(void);
	void Test4(void);
	void Test5(void);
	};

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));
	}

void TestRHeapDebug::Test1(void)
	{

	TAny* p;

	///////////////////////
	// Test heaven cell is found for each method of allocating memory
	////////////////////////

	// new(TInt aSize)
	__UHEAP_MARK;
	__UHEAP_CHECKALL(0);
	__UHEAP_CHECK(0);
	p=new TUint; 
	__UHEAP_CHECKALL(1);
	__UHEAP_CHECK(1);
	__UHEAP_MARKEND;
	__UHEAP_CHECK(0);
	__UHEAP_CHECKALL(1);
	TestCellInfo(0, 1, 1, 1, User::AllocLen(p), p);
	User::Free(p);

	// new(TInt aSize,TInt anExtraSize)
	__UHEAP_MARK;
	p=new(4) TUint; 
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(p), p);
	User::Free(p);

	// 	new(TInt aSize,TLeave)
	__UHEAP_MARK;
	p=new(ELeave) TUint; 
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(p), p);
	User::Free(p);

	// Alloc
	__UHEAP_MARK;
	p=User::Alloc(32); 
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(p), p);
	User::Free(p);

	// AllocL
	__UHEAP_MARK;
	p=User::AllocL(32);
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(p), p);
	User::Free(p);

	// ReAlloc with Null parameter
	__UHEAP_MARK;
	p=User::ReAlloc(NULL, 32); 
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(p), p);
	User::Free(p);

	// ReAllocL with Null parameter
	__UHEAP_MARK;
	p=User::ReAllocL(NULL, 32); 
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(p), p);
	User::Free(p);

	// ReAlloc with non Null parameter
	__UHEAP_MARK;
	p=User::Alloc(128);	   
	p=User::ReAlloc(p, 4); 
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(p), p);
	User::Free(p);
					
	// ReAlloc with non Null parameter such that cell is moved in memory
	__UHEAP_MARK;
	p=User::Alloc(128);	   
	TAny* temp=User::Alloc(128);
	p=User::ReAlloc(p, 526);   
	User::Free(temp);
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 3, 1, User::AllocLen(p), p);
	User::Free(p);

	// ReAllocL with non Null parameter
	__UHEAP_MARK;
	p=User::Alloc(32);	   
	p=User::ReAllocL(p, 128); 
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(p), p);
	User::Free(p);
	}


void TestRHeapDebug::Test2(void)
	{ 
	// Some assorted indirect calls to alloc

	__UHEAP_MARK;
	CBufFlat* pBuf=CBufFlat::NewL(10); 
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(pBuf), pBuf);
	delete pBuf;

	__UHEAP_MARK;
	HBufC8* pHBufC=HBufC8::New(10);	
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(pHBufC), pHBufC);
	delete pHBufC;

// can also create a HBufC8 from a descriptor by using TDesC::Alloc
	}


void TestRHeapDebug::Test3(void)
	{ 

	//  Check num of cells detected is correct and CHECKTOTALNUM is ok
	// NOTE: CHECKTOTALNUM counts the TOTAL number of allocations in the heap regardless of
	// any MARKSTARTs
	// NOTE: the alloc count commences from the FIRST occurrence of a MARKSTART, so if one is nested
	// in another the alloc count will only start from the second MARKSTART if it applies to a 
	// different heap.
	__UHEAP_MARK;
	__UHEAP_CHECKALL(0);
	TAny* p1= new TUint; 
	__UHEAP_CHECKALL(1);
	TAny* p2= new(20) TUint;
	__UHEAP_CHECKALL(2);
	TAny* p3= User::Alloc(15); 
	__UHEAP_CHECKALL(3);
	__UHEAP_MARK;
	__UHEAP_CHECK(0);
	TAny* p4=User::Alloc(1); 
	TAny* p5 =new TUint; 
	__UHEAP_CHECK(2);
	__UHEAP_CHECKALL(5);
	__UHEAP_MARKEND;
	TestCellInfo(0, 2, 4, 2, User::AllocLen(p4), p4);
	__UHEAP_CHECKALL(5);
	__UHEAP_CHECK(3);
	__UHEAP_MARKENDC(3);
	User::Free(p1);
	User::Free(p2);
	User::Free(p3);
	User::Free(p4);
	User::Free(p5);

	// Check some nesting out
	p1=new TUint;
	__UHEAP_MARK;
	p2=new TUint; 
	__UHEAP_MARK;
	p3=new TUint; 
	__UHEAP_MARK;
	p4=new TUint; 
	__UHEAP_MARKEND;
	TestCellInfo(0, 3, 3, 1, User::AllocLen(p4), p4);
	__UHEAP_MARKEND;
	TestCellInfo(0, 2, 2, 1, User::AllocLen(p3), p3);
	__UHEAP_MARKEND;
	TestCellInfo(0, 1, 1, 1, User::AllocLen(p2), p2);
	User::Free(p1);
	User::Free(p2);
	User::Free(p3);
	User::Free(p4);
	User::Check();
	}

void TestRHeapDebug::Test4(void)
	{
	// Test with different heaps
	TAny* p1=new TUint;
	__UHEAP_MARK;	// Default start
	__UHEAP_CHECKALL(1);
	__UHEAP_CHECK(0);
	TAny* p2=new TUint;	  
	RHeap* pHeap1=allocHeap(1000); 
	AttachToHeap(pHeap1,1);
	__RHEAP_MARK(pHeap1); // Heap1 start
	__RHEAP_CHECKALL(pHeap1,0);
	__RHEAP_CHECK(pHeap1,0);
	TAny* p3=pHeap1->Alloc(4); 
	__RHEAP_CHECKALL(pHeap1,1);
	__RHEAP_CHECK(pHeap1,1);
	__RHEAP_CHECKALL(pHeap1,1);
	__UHEAP_CHECKALL(3);
	RHeap* pHeap2=allocHeap(1000);
	AttachToHeap(pHeap2,2);
	RHeap* pHeap3=allocHeap(1000);
	AttachToHeap(pHeap3,3);
	__UHEAP_CHECKALL(5);
	__RHEAP_MARK(pHeap2); // Heap2 start
	__RHEAP_MARK(pHeap3); // Heap3 start
	TAny* p4=pHeap2->Alloc(8); 
	TAny* p5=pHeap2->Alloc(37);
	TAny* p6=pHeap3->Alloc(32);	
	TAny* p7=pHeap1->Alloc(43);	
	__UHEAP_CHECKALL(5);
	__RHEAP_CHECKALL(pHeap1,2);
	__RHEAP_CHECKALL(pHeap2,2);
	__RHEAP_CHECKALL(pHeap3,1);
	__RHEAP_MARKEND(pHeap3); // Heap3 end
	TestCellInfo(3, 1, 1, 1, pHeap3->AllocLen(p6), p6);
	__RHEAP_MARKEND(pHeap2); // Heap2 end
	TestCellInfo(2, 1, 1, 2, pHeap2->AllocLen(p4), p4);
	pHeap1->Free(p3);
	__RHEAP_MARKEND(pHeap1); // Heap1 end
	TestCellInfo(1, 1, 2, 1, pHeap1->AllocLen(p7), p7);
	User::Free(p1);
	User::Free(p2);
	pHeap2->Free(p4);
	pHeap2->Free(p5);
	pHeap3->Free(p6);
	pHeap1->Free(p7);
	__UHEAP_CHECKALL(3);   
	pHeap2->Close();
	pHeap3->Close();
	__UHEAP_MARKEND;
	pHeap1->Close();
	__UHEAP_CHECKALL(0);
	}

void TestRHeapDebug::Test5()
// Check the alloc failure macros
	{
	TAny *p, *p1;
	RHeap* pHeap=allocHeap(1000);

	// DETERMINISTIC FAILURE
	__UHEAP_RESET;
	__UHEAP_FAILNEXT(1);
	test(User::Alloc(1)==NULL);
	p=User::Alloc(1);
	test(p!=NULL);
	User::FreeZ(p);
	__UHEAP_RESET;

	__RHEAP_RESET(pHeap);
	__RHEAP_FAILNEXT(pHeap,1);
	test(pHeap->Alloc(1)==NULL);
	p=pHeap->Alloc(1);
	test(p!=NULL);
	pHeap->FreeZ(p);
   	__RHEAP_RESET(pHeap);

	__KHEAP_RESET;
	__KHEAP_FAILNEXT(1);
	RSemaphore semaphore;
	test(semaphore.CreateLocal(1)==KErrNoMemory); // allocated from the kernel heap
	test(semaphore.CreateLocal(1)==KErrNone);
	semaphore.Close();
	__KHEAP_RESET;

	__UHEAP_SETFAIL(RHeap::EDeterministic,0);
	test(User::Alloc(1)==NULL);
	__UHEAP_RESET;

	__RHEAP_SETFAIL(pHeap,RHeap::EDeterministic,0);
	test(pHeap->Alloc(1)==NULL);
	__RHEAP_RESET(pHeap);

	__KHEAP_SETFAIL(RHeap::EDeterministic,0);
	test(semaphore.CreateLocal(1)==KErrNoMemory);
	__KHEAP_RESET;

	TInt determinism;
	for(determinism=1; determinism<=KMaxFailureRate; determinism++)
		{
		__UHEAP_SETFAIL(RHeap::EDeterministic,determinism);
		__RHEAP_SETFAIL(pHeap,RHeap::EDeterministic,determinism);
		for(TInt ii=1; ii<=determinism; ii++)
			{
			p=User::Alloc(1);
			p1=pHeap->Alloc(1);
			if(ii%determinism==0)
				{
				test(p==NULL);
				test(p1==NULL);
				}
			else
				{
				test(p!=NULL);
				test(p1!=NULL);
				pHeap->Free(p1); 
				User::Free(p);
				}
			}
		}
	__UHEAP_RESET;
	__RHEAP_RESET(pHeap);

	// Test SetKernelAllocFail
	// its not possible to test SetKernelAllocFail as above as it is not possible to control the
	// number of calls to Alloc for the dernel heap - but the following will definitely fail:
	__KHEAP_SETFAIL(RHeap::EDeterministic,1);
	RSemaphore r; 
	test(r.CreateLocal(1)==KErrNoMemory); // allocated from the kernel heap
	__KHEAP_SETFAIL(RHeap::EDeterministic,50);
	test(r.CreateLocal(1)==KErrNone);
	r.Close();
	__KHEAP_RESET;

	// RANDOM TESTS
	TInt numOccurences1, numOccurences2;

	__UHEAP_SETFAIL(RHeap::ERandom,1);
	test(User::Alloc(1)==NULL);
	__UHEAP_RESET;

	__RHEAP_SETFAIL(pHeap,RHeap::ERandom,1);
	test(pHeap->Alloc(1)==NULL);
	__RHEAP_RESET(pHeap);

//	__KHEAP_SETFAIL(RHeap::ERandom,1);
//	test(semaphore.CreateLocal(1)==KErrNoMemory);
//	__KHEAP_RESET;

	__UHEAP_SETFAIL(RHeap::ETrueRandom,1);
	test(User::Alloc(1)==NULL);
	__UHEAP_RESET;

	__RHEAP_SETFAIL(pHeap,RHeap::ETrueRandom,1);
	test(pHeap->Alloc(1)==NULL);
	__RHEAP_RESET(pHeap);

//	__KHEAP_SETFAIL(RHeap::ETrueRandom,1);
//	test(semaphore.CreateLocal(1)==KErrNoMemory);
//	__KHEAP_RESET;

	for(determinism=1; determinism<=KMaxFailureRate; determinism++)
		{
		__UHEAP_SETFAIL(RHeap::ERandom,determinism);
		__RHEAP_SETFAIL(pHeap,RHeap::ERandom,determinism);
        TInt ii;
		for(ii=1; ii<=determinism; ii++)
			{
			p=User::Alloc(1);
			p1=pHeap->Alloc(1);
			array1[ii]=(p==NULL);
			array2[ii]=(p==NULL);
			if(p)
				User::Free(p);
			if(p1)
				pHeap->Free(p1);
			}
		numOccurences1=0;
		numOccurences2=0;
		for(ii=1; ii<=determinism; ii++)
			{
			if(array1[ii])
				numOccurences1++;					
			if(array2[ii])
				numOccurences2++;
			}
		test(numOccurences1==1);
		test(numOccurences2==1);
		}
	__UHEAP_RESET;
	__RHEAP_RESET(pHeap);		

	__UHEAP_SETFAIL(RHeap::ERandom,5);
	TInt ii;
	for(ii=1; ii<=50; ii++)
		{
		p=User::Alloc(1);
		array1[ii]=(p==NULL);
		if(p)	
			User::Free(p);
		}
	numOccurences1=0;
	numOccurences2=0;
	for(ii=1; ii<=50; ii++)
		{
		if(array1[ii])
			{
			numOccurences1++;
			numOccurences2++;
			}
		if(ii%5==0)
			{
			test(numOccurences1==1);
			numOccurences1=0;
			}
		}
	test(numOccurences2==50/5);	
	
	// Cannot really test random failure of the kernel heap accurately

	pHeap->Close();
	//client.Disconnect();

	// Test failing the heap of a child thread
	// 1st test that it allocates normally
	TRequestStatus stat;
	RThread thread;
	test(threadSemaphore.CreateLocal(0)==KErrNone);
	test(thread.Create(_L("Thread"),ThreadEntryPoint,KDefaultStackSize,0x200,0x200,NULL)==KErrNone);
	thread.Logon(stat);
	thread.Resume();
	threadSemaphore.Signal();
	User::WaitForRequest(stat);
	test(thread.ExitReason()==KErrNone);
	thread.Close();
#if defined(CAN_TEST_THREADS)
	// Now make the thread's heap fail
	test(thread.Create(_L("Thread"),ThreadEntryPoint,KDefaultStackSize,0x200,0x200,NULL)==KErrNone);
	thread.Logon(stat);
	thread.Resume();
	TH_FAILNEXT(thread.Handle());
	threadSemaphore.Signal();
	User::WaitForRequest(stat);
	test(thread.ExitReason()==KThreadMemError);
	thread.Close();
	threadSemaphore.Close();
#endif
	}	

GLDEF_C TInt E32Main(void)
    {

	test.Title();
	AttachToHeap(NULL,0);
	test.Start(_L("Test1"));
	TestRHeapDebug T;
	T.Test1();	 
	test.Next(_L("Test2"));
	T.Test2();
	test.Next(_L("Test3"));
	T.Test3();
	test.Next(_L("Test4"));
	T.Test4();
	test.Next(_L("Test5"));
	T.Test5();
	test.End();
	return(0);
    }
#else
GLDEF_C TInt E32Main()
//
// Test unavailable in release build.
//
    {

	test.Title();	
	test.Start(_L("No tests for release builds"));
	test.End();
	return(0);
    }
#endif