kerneltest/e32test/mmu/t_cachechunk.cpp
changeset 0 a41df078684a
child 24 41f0cfe18c80
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/mmu/t_cachechunk.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,578 @@
+// 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\mmu\t_chunk.cpp
+// Tests on RChunk objects in connection with demand paging.
+// Tests exercise the locking, unlocking, commiting and decommiting of 
+// pages to chunk objects.
+// 1  Check Unlocked page gets counted as free memory
+// 2  Check Unlock/Lock preserves page contents
+// 3  Tests at chunk offset '0'
+// 4    Check Lock is idempotent
+// 5    Check non page aligned Unlock/Lock
+// 6    Check unlocked pages get reclaimed for new memory allocation
+// 7    Check reclaimed memory is unmapped from original location
+// 8    Check Lock fails when memory is reclaimed
+// 9    Check Lock failure Decommits memory
+// 10   Recommit memory to chunk
+// 11   Check Commit on unlocked pages
+// 12   Check Commit on unlocked and reclaimed pages
+// 13   Restore chunk
+// 14 Tests at chunk offset 'PageSize'
+// 15   Check Lock is idempotent
+// 16   Check non page aligned Unlock/Lock
+// 17   Check unlocked pages get reclaimed for new memory allocation
+// 18   Check reclaimed memory is unmapped from original location
+// 19   Check Lock fails when memory is reclaimed
+// 20   Check Lock failure Decommits memory
+// 21   Recommit memory to chunk
+// 22   Check Commit on unlocked pages
+// 23   Check Commit on unlocked and reclaimed pages
+// 24   Restore chunk
+// 25 Tests at chunk offset '0x100000-PageSize'
+// 26   Check Lock is idempotent
+// 27   Check non page aligned Unlock/Lock
+// 28   Check unlocked pages get reclaimed for new memory allocation
+// 29   Check reclaimed memory is unmapped from original location
+// 30   Check Lock fails when memory is reclaimed
+// 31   Check Lock failure Decommits memory
+// 32   Recommit memory to chunk
+// 33   Check Commit on unlocked pages
+// 34   Check Commit on unlocked and reclaimed pages
+// 35   Restore chunk
+// 36 Tests at chunk offset '0x400000-PageSize'
+// 37   Check Lock is idempotent
+// 38   Check non page aligned Unlock/Lock
+// 39   Check unlocked pages get reclaimed for new memory allocation
+// 40   Check reclaimed memory is unmapped from original location
+// 41   Check Lock fails when memory is reclaimed
+// 42   Check Lock failure Decommits memory
+// 43   Recommit memory to chunk
+// 44 Check Commit on unlocked pages
+// 45 Check Commit on unlocked and reclaimed pages
+// 46 Restore chunk
+// 47 Big Unlock/Lock
+// 48 Benchmarks...
+// 49 Close chunk with unlocked pages which have been flushed
+// 
+//
+
+//! @SYMTestCaseID			KBASE-T_CACHECHUNK-0336
+//! @SYMTestType			UT
+//! @SYMPREQ				PREQ1110
+//! @SYMTestCaseDesc		Demand Paging Loader Stress Tests
+//! @SYMTestActions			0  Commit all of memory
+//! @SYMTestExpectedResults All tests should pass.
+//! @SYMTestPriority        High
+//! @SYMTestStatus          Implemented
+
+#define __E32TEST_EXTENSION__
+
+#include <e32test.h>
+#include <e32panic.h>
+#include <e32svr.h>
+#include <hal.h>
+#include "mmudetect.h"
+#include "d_memorytest.h"
+#include "d_gobble.h"
+#include <dptest.h>
+#include "freeram.h"
+
+LOCAL_D RTest test(_L("T_CACHECHUNK"));
+
+RMemoryTestLdd MemoryTest;
+
+RChunk TestChunk;
+TUint8* TestChunkBase;
+TInt CommitEnd;
+TInt PageSize;
+TInt NoFreeRam;
+RTimer Timer;
+
+
+
+void FillPage(TUint aOffset)
+	{
+	TUint8* ptr = TestChunkBase+aOffset;
+	TUint8* ptrEnd = ptr+PageSize;
+	do *((TUint32*&)ptr)++ = aOffset+=4;
+	while(ptr<ptrEnd);
+	}
+
+
+TBool CheckPage(TUint aOffset)
+	{
+	TUint8* ptr = TestChunkBase+aOffset;
+	TUint8* ptrEnd = ptr+PageSize;
+	do if(*((TUint32*&)ptr)++ != (aOffset+=4)) break;
+	while(ptr<ptrEnd);
+	return ptr==ptrEnd;
+	}
+
+
+TBool CheckPages(TUint aOffset, TInt aNumPages)
+	{
+	while(aNumPages--)
+		if(!CheckPage(aOffset+=PageSize))
+			return EFalse;
+	return ETrue;
+	}
+
+
+TBool IsPageMapped(TUint aOffset)
+	{
+	TUint32 value;
+	TInt r=MemoryTest.ReadMemory(TestChunkBase+aOffset,value);
+	return r==KErrNone;
+	}
+
+
+void Tests(TInt aOffset)
+	{
+	if(aOffset+5*PageSize>=CommitEnd)
+		{
+		test.Start(_L("TEST NOT RUN - Not enough system RAM"));
+		test.End();
+		return;
+		}
+	TInt r;
+	TInt freeRam;
+
+	TUint origChunkSize = TestChunk.Size();
+
+	test.Start(_L("Check Unlock is idempotent"));
+	r = TestChunk.Unlock(aOffset+PageSize,PageSize);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam+PageSize);
+	r = TestChunk.Unlock(aOffset+PageSize,PageSize);
+	test_KErrNone(r);
+	test_Equal(FreeRam(), freeRam);
+	// Ensure unlock on reclaimed pages is idempotent
+	TInt flushSupported = UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+	r = TestChunk.Unlock(aOffset+PageSize,PageSize);
+	test_KErrNone(r);
+	test_Equal(FreeRam(), freeRam);
+	test_Equal(origChunkSize, TestChunk.Size());
+	
+	if (flushSupported == KErrNotSupported)
+		{// Flush cache not supported so lock won't fail so no need to recommit the pages.
+		test_KErrNone(TestChunk.Lock(aOffset+PageSize,PageSize));
+		}
+	else
+		{// Recommit the reclaimed pages.
+		test_KErrNone(flushSupported);
+		test_Equal(KErrNotFound, TestChunk.Lock(aOffset+PageSize,PageSize));
+		test_KErrNone(TestChunk.Commit(aOffset+PageSize,PageSize));
+		}
+
+	test.Next(_L("Check Lock is idempotent"));
+	r = TestChunk.Lock(aOffset,3*PageSize);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam);
+	CheckPages(aOffset,3);
+	r = TestChunk.Lock(aOffset,3*PageSize);
+	test_KErrNone(r);
+	CheckPages(aOffset,3);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam);
+	test_Equal(origChunkSize, TestChunk.Size());
+
+	test.Next(_L("Check non page aligned Unlock/Lock"));
+	r = TestChunk.Unlock(aOffset+PageSize-1,1);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam+PageSize);
+	r = TestChunk.Lock(aOffset+PageSize-1,1);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam);
+	r = TestChunk.Unlock(aOffset+PageSize-1,2);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam+PageSize*2);
+	r = TestChunk.Lock(aOffset+PageSize-1,2);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam);
+	test_Equal(origChunkSize, TestChunk.Size());
+
+	test.Next(_L("Check unlocked pages get reclaimed for new memory allocation"));
+	r=TestChunk.Commit(CommitEnd,PageSize);
+	test(r==KErrNoMemory);
+	r = TestChunk.Unlock(aOffset,4*PageSize);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam+PageSize*4);
+	r=TestChunk.Commit(CommitEnd,PageSize);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam<NoFreeRam+PageSize*4);
+	r=TestChunk.Decommit(CommitEnd,PageSize);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam+PageSize*4);
+	UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0); // make sure unlocked page is gone
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam+PageSize*4);
+
+#ifndef __WINS__ // wins fakery doesn't actually do this
+	test.Next(_L("Check reclaimed memory is unmapped and decommitted from original location"));
+	TInt mappedPages = IsPageMapped(aOffset+PageSize*0);
+	mappedPages += IsPageMapped(aOffset+PageSize*1);
+	mappedPages += IsPageMapped(aOffset+PageSize*2);
+	mappedPages += IsPageMapped(aOffset+PageSize*3);
+	test(mappedPages<4);
+#endif
+	if(aOffset>PageSize)
+		{
+		test(CheckPage(aOffset+PageSize*-1)); // should be left mapped
+		}
+	test(CheckPage(aOffset+PageSize*4)); // should be left mapped
+
+	test.Next(_L("Check Lock fails when memory is reclaimed"));
+	r = TestChunk.Lock(aOffset,4*PageSize);
+	test(r==KErrNotFound);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam+PageSize*4);
+
+	test.Next(_L("Check Lock failure Decommits memory"));
+	test(!IsPageMapped(aOffset+PageSize*0));
+	test(!IsPageMapped(aOffset+PageSize*1));
+	test(!IsPageMapped(aOffset+PageSize*2));
+	test(!IsPageMapped(aOffset+PageSize*3));
+	test_Equal(origChunkSize-PageSize*4, TestChunk.Size());
+
+	test.Next(_L("Recommit memory to chunk"));
+	TInt offset;
+	for(offset=aOffset; offset<aOffset+PageSize*4; offset+=PageSize)
+		{
+		r=TestChunk.Commit(offset,PageSize);
+		test_KErrNone(r);
+		FillPage(offset);
+		}
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam);
+	test_Equal(origChunkSize, TestChunk.Size());
+
+	test.Next(_L("Check Commit on unlocked pages"));
+	r = TestChunk.Unlock(aOffset,4*PageSize);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam>=NoFreeRam+PageSize*4);
+	r=TestChunk.Commit(aOffset,4*PageSize);
+	test(r==KErrAlreadyExists);
+	freeRam = FreeRam();
+	test(freeRam>=NoFreeRam+PageSize*4);
+	test_Equal(origChunkSize, TestChunk.Size());
+
+	test.Next(_L("Check Commit on unlocked and reclaimed pages"));
+	// unlock and force a page to be reclaimed...
+	r=TestChunk.Commit(CommitEnd,PageSize);
+	test_KErrNone(r);
+	r=TestChunk.Decommit(CommitEnd,PageSize);
+	test_KErrNone(r);
+	UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0); // make sure unlocked page is gone
+	freeRam = FreeRam();
+	test(freeRam>=NoFreeRam+PageSize*4);
+	// check can't commit any pages (because they are unlocked, not decommitted)...
+	r=TestChunk.Commit(aOffset+PageSize*0,PageSize);
+	test(r==KErrAlreadyExists);
+	r=TestChunk.Commit(aOffset+PageSize*1,PageSize);
+	test(r==KErrAlreadyExists);
+	r=TestChunk.Commit(aOffset+PageSize*2,PageSize);
+	test(r==KErrAlreadyExists);
+	r=TestChunk.Commit(aOffset+PageSize*3,PageSize);
+	test(r==KErrAlreadyExists);
+	freeRam = FreeRam();
+	test(freeRam>=NoFreeRam+PageSize*4);
+	test_Equal(origChunkSize, TestChunk.Size());
+	// Restore the chunk to original size.
+	r = TestChunk.Lock(aOffset,4*PageSize);
+	test_Equal(r, KErrNotFound);
+	freeRam = FreeRam();
+	test_Compare(freeRam, >=, NoFreeRam+PageSize*4);
+	test_Equal(origChunkSize - PageSize*4, TestChunk.Size());
+	r = TestChunk.Commit(aOffset, PageSize*4);
+	test_KErrNone(r);
+
+	test.Next(_L("Check Decommit on unlocked pages"));
+	r = TestChunk.Unlock(aOffset,PageSize*4);
+	test_KErrNone(r);
+	test(FreeRam() >= NoFreeRam+PageSize*4);
+	r=TestChunk.Decommit(aOffset, PageSize*4);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test_Compare(freeRam, >=, NoFreeRam+PageSize*4);
+	test_Equal(origChunkSize - PageSize*4, TestChunk.Size());
+	// Restore chunk back to original state
+	r = TestChunk.Commit(aOffset, PageSize*4);
+	test_KErrNone(r);
+	test(FreeRam() == NoFreeRam);
+
+	test.Next(_L("Check Decommit on unlocked and reclaimed pages"));
+	r = TestChunk.Unlock(aOffset,PageSize*4);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test_Compare(freeRam, >=, NoFreeRam+PageSize*4);
+	r=TestChunk.Commit(CommitEnd,PageSize);
+	test_KErrNone(r);
+	r=TestChunk.Decommit(CommitEnd,PageSize);
+	test_KErrNone(r);
+	UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0); // make sure unlocked page is gone
+	freeRam = FreeRam();
+	test_Compare(freeRam, >=, NoFreeRam+PageSize*4);
+	r=TestChunk.Decommit(aOffset, PageSize*4);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test_Compare(freeRam, >=, NoFreeRam+PageSize*4);
+	test_Equal(origChunkSize - PageSize*4, TestChunk.Size());
+
+	test.Next(_L("Restore chunk"));
+	test_Equal(origChunkSize-PageSize*4, TestChunk.Size());
+	for(offset=aOffset; offset<aOffset+PageSize*4; offset+=PageSize)
+		{
+		r=TestChunk.Commit(offset,PageSize);
+		test_KErrNone(r);
+		FillPage(offset);
+		}
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam);
+	test_Equal(origChunkSize, TestChunk.Size());
+
+	test.End();
+	}
+
+
+
+void TestBenchmarks()
+	{
+	TInt r = TestChunk.Unlock(0,CommitEnd); // start with everthing unlocked
+	test_KErrNone(r);
+	TInt testSizes[] = { PageSize,1<<16,1<<20,0 };
+	TInt* sizes = testSizes;
+	TInt size;
+	while((size=*sizes++)!=0)
+		{
+		TRequestStatus status;
+		Timer.After(status,1);
+		User::WaitForRequest(status);
+		TInt KRunTime = 1*1000*1000;
+		Timer.After(status,KRunTime);
+		TInt count = 0;
+		while(status==KRequestPending)
+			{
+			r = TestChunk.Lock(0,size);
+			test_KErrNone(r);
+			r = TestChunk.Unlock(0,size);
+			test_KErrNone(r);
+			++count;
+			}
+		User::WaitForRequest(status);
+		test.Printf(_L("Unlock/Lock of %d kB takes %d us\n"),size>>10,KRunTime/count);
+		}
+	}
+
+
+
+void TestUnlockOld()
+	{
+	// we start with TestChunk being locked and no or little free RAM
+	// (hence live list should be close to minimum size.)
+
+	// get sizes...
+	TUint min = 0;
+	TUint max = 0;
+	TUint cur = 0;
+	TInt r = DPTest::CacheSize(min,max,cur);
+
+	// manipulate demand paging live list so we end up with zero old pages...
+
+	r = TestChunk.Unlock(0,min*2); // dump 2*min bytes at start of live list
+	test_KErrNone(r);
+	// live list now cur+2*min bytes
+
+	r = TestChunk.Commit(CommitEnd,cur); // use up 'cur' bytes of RAM from end of live list
+	test_KErrNone(r);
+	// live list now 2*min bytes of pages which were unlocked from our test chunk
+
+	// lock pages until free RAM is <= 2 pages.
+	// this should remove all of the 'old' pages
+	TUint i = 0;
+	while(FreeRam()>2*PageSize)
+		{
+		TestChunk.Lock(i,PageSize);
+		i += PageSize;
+		test(i<=min);
+		}
+	// live list now min+2*PageSize bytes, with no old pages
+
+	// now commit memory, which forces allocation from the demand paging live list
+	// which doesn't have any old pages (the purpose of this test)...
+	TUint extra = 0;
+	for(;;)
+		{
+		r = TestChunk.Commit(CommitEnd+min+extra,PageSize);
+		if(r==KErrNoMemory)
+			break;
+		extra += PageSize;
+		}
+	test(extra>0);
+
+	// restore commit state...
+	r = TestChunk.Decommit(CommitEnd,min+extra);
+	test_KErrNone(r);
+	r = TestChunk.Decommit(0,min*2);
+	test_KErrNone(r);
+	r = TestChunk.Commit(0,min*2);
+	test_KErrNone(r);
+	}
+
+
+
+TInt E32Main()
+	{
+	test.Title();
+
+	if (!HaveVirtMem())
+		{
+		test.Printf(_L("This test requires an MMU\n"));
+		return KErrNone;
+		}
+	test.Start(_L("Initialise test"));
+	test.Next(_L("Load gobbler LDD"));
+	TInt r = User::LoadLogicalDevice(KGobblerLddFileName);
+	test(r==KErrNone || r==KErrAlreadyExists);
+	RGobbler gobbler;
+	r = gobbler.Open();
+	test(r==KErrNone);
+	TUint32 taken = gobbler.GobbleRAM(496*1024*1024);
+	test.Printf(_L("Gobbled: %dK\n"), taken/1024);
+	test.Printf(_L("Free RAM 0x%08X bytes\n"),FreeRam());
+
+	test_KErrNone(HAL::Get(HAL::EMemoryPageSize,PageSize));
+	TInt totalRAM;
+	test_KErrNone(HAL::Get(HAL::EMemoryRAM, totalRAM));
+	totalRAM -= taken;
+	test.Printf(_L("totalRAM=%dK\n"), totalRAM/1024);
+
+	test(KErrNone==MemoryTest.Open());
+	// Create the test chunk.  It must not be paged otherwise
+	// unlocking its pages will have no effect.
+	TChunkCreateInfo createInfo;
+	createInfo.SetCache(totalRAM);
+	test_KErrNone(TestChunk.Create(createInfo));
+	TestChunkBase = TestChunk.Base();
+
+	test(KErrNone==Timer.CreateLocal());
+	UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+
+	test.Next(_L("Commit all of memory"));
+	CommitEnd = 0;
+	while(KErrNone==(r=TestChunk.Commit(CommitEnd,PageSize)))
+		{
+		FillPage(CommitEnd);
+		CommitEnd += PageSize;
+		}
+	test(r==KErrNoMemory);
+	NoFreeRam = FreeRam();
+	test(NoFreeRam<=PageSize);
+
+	test.Next(_L("Check Unlocked page gets counted as free memory"));
+	r = TestChunk.Unlock(0,PageSize);
+	test_KErrNone(r);
+	TInt freeRam = FreeRam();
+	test(freeRam==NoFreeRam+PageSize);
+	r = TestChunk.Lock(0,PageSize);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam==NoFreeRam);
+
+	test.Next(_L("Check Unlock/Lock preserves page contents"));
+	TInt offset;
+	for(offset=0; offset<CommitEnd; offset+=PageSize)
+		{
+		test(CheckPage(offset));
+		r = TestChunk.Unlock(offset,PageSize);
+		test_KErrNone(r);
+		r = TestChunk.Lock(offset,PageSize);
+		test_KErrNone(r);
+		test(CheckPage(offset));
+		freeRam = FreeRam();
+		test(freeRam==NoFreeRam);
+		}
+
+	test.Next(_L("Tests at chunk offset '0'"));
+	Tests(0);
+	test.Next(_L("Tests at chunk offset 'PageSize'"));
+	Tests(PageSize);
+	test.Next(_L("Tests at chunk offset '0x100000-PageSize'"));
+	Tests(0x100000-PageSize);
+	test.Next(_L("Tests at chunk offset '0x400000-PageSize'"));
+	Tests(0x400000-PageSize);
+
+	// Remove limit on max size of live list
+	TUint originalMin = 0;
+	TUint originalMax = 0;
+	TUint currentSize = 0;
+	r = DPTest::CacheSize(originalMin, originalMax, currentSize);
+	test(r == KErrNone || r == KErrNotSupported);
+	TBool resizeCache = r == KErrNone;
+	if (resizeCache)
+		test_KErrNone(DPTest::SetCacheSize(originalMin, KMaxTUint));
+
+	test.Next(_L("Big Unlock/Lock"));
+	r = TestChunk.Unlock(0,CommitEnd);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test(freeRam>=NoFreeRam+CommitEnd);
+	r = TestChunk.Lock(0,CommitEnd);
+	test_KErrNone(r);
+	freeRam = FreeRam();
+	test_Equal(NoFreeRam, freeRam);
+
+	if (resizeCache)
+		{
+		test.Next(_L("Check Unlock of old pages doesn't cause problems"));
+		TestUnlockOld();
+		}
+
+	test.Next(_L("Benchmarks..."));
+	TestBenchmarks();
+
+	test.Next(_L("Close chunk with unlocked pages which have been flushed"));
+	r = TestChunk.Unlock(0,CommitEnd);
+	test_KErrNone(r);
+	UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+	TestChunk.Close();
+
+	test.Next(_L("Check can't lock/unlock non-cache chunks"));
+	RChunk chunk;
+	test_KErrNone(chunk.CreateDisconnectedLocal(0,PageSize,2*PageSize));
+	test_Equal(KErrGeneral,chunk.Lock(PageSize,PageSize));
+	test_Equal(KErrGeneral,chunk.Unlock(0,PageSize));
+	chunk.Close();
+
+	// Restore original settings for live list size
+	if (resizeCache)
+		test_KErrNone(DPTest::SetCacheSize(originalMin, originalMax));
+
+	// end...
+	test.End();
+	MemoryTest.Close();
+	gobbler.Close();
+	test.Close();
+
+	return KErrNone;
+	}