kerneltest/f32test/demandpaging/t_pagestress.cpp
changeset 43 96e5fb8b040d
child 33 0173bcd7697c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/f32test/demandpaging/t_pagestress.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,1231 @@
+// Copyright (c) 2006-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:
+// f32test\demandpaging\t_pagestress.cpp
+// Demand Paging Stress Tests
+// t_pagestress.exe basically attempts to cause large ammounts of paging by calling
+// functions (256) which are alligned on a page boundary from multiple threads.
+// There are mulitple versions of t_pagestress installed on a test system to test rom
+// and code paging from various media types.
+// Usage:
+// t_pagestress and t_pagestress_rom
+// Common command lines:
+// t_pagestress lowmem
+// debug - switch on debugging information
+// silent - no output to the screen or serial port
+// check - check the allignments
+// single - run the tests in a single thread
+// multiple <numThreads> - run the tests in multiple threads where <numThreads>
+// interleave - force thread interleaving
+// prio - each thread reschedules in between each function call, causes lots of context changes
+// media - perform media access during the tests, very stressful
+// lowmem - low memory tests
+// forward - patern in which to execute function calls 
+// backward - patern in which to execute function calls 			
+// random - patern in which to execute function calls 			
+// all - patern in which to execute function calls (forward, backward and random)
+// inst - for debugging a parameter passed to a spawned exe to give it an id.
+// iters <count> - the number of times to loop (a '-' means run forever)
+// t_pagestress causes a large ammount of paging by repeatedly calling 
+// 256 functions which have been aligned on page boundaries from 
+// multiple threads.
+// 1  - a single thread calling all functions
+// 2  - Multiple threads calling all functions
+// 3  - Multiple threads calling all functions with a priority change 
+// after each function call
+// 4  - Multiple threads calling all functions with background 
+// media activity
+// 5  - Multiple threads calling all functions with media activity and 
+// a priority change after each function call
+// 6  - Multiple threads calling all functions with process interleave
+// 7  - Multiple threads calling all functions with process interleave 
+// and a priority change after each function call
+// 8  - Multiple threads calling all functions with process interleave 
+// media acess and a priority change after each function call
+// 9  - Multiple threads calling all functions with low available memory
+// 10 - Multiple threads calling all functions with low available memory,
+// starting with initial free ram.
+// 
+//
+
+//! @SYMTestCaseID			KBASE-T_PAGESTRESS-0327
+//! @SYMTestType			UT
+//! @SYMPREQ				PREQ1110
+//! @SYMTestCaseDesc		Demand Paging Stress Tests
+//! @SYMTestActions			0  - Check the alignment of all functions
+//! @SYMTestExpectedResults All tests should pass.
+//! @SYMTestPriority        High
+//! @SYMTestStatus          Implemented
+
+#include <e32test.h>
+RTest test(_L("T_PAGESTRESS"));
+
+#include <e32rom.h>
+#include <u32hal.h>
+#include <f32file.h>
+#include <f32dbg.h>
+#include <e32msgqueue.h>
+#include <e32math.h>
+
+#include "testdefs.h"
+
+#ifdef __X86__
+#define TEST_ON_UNPAGED
+#endif
+
+
+/* The following line will cause t_pagestress.h to declare an array of function
+ * pointers to  page boundary aligned functions that we can use in this test...
+ */
+#define TPS_DECLARE_ARRAY
+#include "t_pagestress.h"
+
+TBool   	TestDebug					= EFalse;
+TBool		TestSilent					= EFalse;
+TBool		TestExit					= EFalse;
+
+TBool   	TestCheck					= EFalse;
+TBool   	TestSingle					= EFalse;
+TBool   	TestMultiple				= EFalse;
+TBool   	TestForever					= EFalse;
+TInt		TestMaxLoops				= 20;
+TInt		TestMultipleThreadCount		= 50;
+TInt		TestInstanceId				= 0;
+
+#define TEST_INTERLEAVE_PRIO		EPriorityMore//EPriorityRealTime //23 // KNandThreadPriority - 1
+TBool		TestInterleave				= EFalse;
+TBool		TestWeAreTheTestBase		= EFalse;
+TBool		TestBootedFromMmc			= EFalse;
+TInt		DriveNumber=-1;   // Parameter - Which drive?  -1 = autodetect.
+#define TEST_NONE		0x0
+#define TEST_FORWARD	0x2
+#define TEST_BACKWARD	0x4
+#define TEST_RANDOM		0x8
+#define TEST_ALL		(TEST_RANDOM | TEST_BACKWARD | TEST_FORWARD)
+TUint32		TestWhichTests				= TEST_ALL;
+TBuf<32>	TestNameBuffer;
+TBool		TestPrioChange				= ETrue;
+TBool		TestStopMedia				= EFalse;
+TBool		TestMediaAccess				= EFalse;
+#define TEST_LM_NUM_FREE	0
+#define TEST_LM_BLOCKSIZE	1
+#define TEST_LM_BLOCKS_FREE	4
+TBool		TestLowMem					= EFalse;
+RPageStressTestLdd Ldd;
+TInt		TestPageSize				= 4096;
+RSemaphore	TestMultiSem;
+RMsgQueue<TBuf <64> >	TestMsgQueue;
+TBool		TestIsDemandPaged = ETrue;
+
+
+#define TEST_NEXT(__args) \
+	if (!TestSilent)\
+		test.Next __args;
+
+#define DBGS_PRINT(__args)\
+	if (!TestSilent)\
+		test.Printf __args ;
+
+#define DBGD_PRINT(__args)\
+	if (TestDebug)\
+		test.Printf __args ;\
+
+#define DEBUG_PRINT(__args)\
+if (!TestSilent)\
+	{\
+	if (aMsgQueue && aBuffer && aTheSem)\
+		{\
+		aBuffer->Zero();\
+		aBuffer->Format __args ;\
+		aTheSem->Wait();\
+		aMsgQueue->SendBlocking(*aBuffer);\
+		aTheSem->Signal();\
+		}\
+	else\
+		{\
+		test.Printf __args ;\
+		}\
+	}
+
+#define RUNTEST(__test, __error)\
+	if (!TestSilent)\
+		test(__test == __error);\
+	else\
+		__test;
+
+#define RUNTEST1(__test)\
+	if (!TestSilent)\
+		test(__test);
+
+#define DEBUG_PRINT1(__args)\
+if (TestDebug)\
+	{\
+	DEBUG_PRINT(__args)\
+	}
+
+//
+// CheckAlignments
+//
+// The following functions wanders through the function pointer array
+// declared in t_pagestress.h and ensures that the alignment has worked, 
+// (a good start!) and then calls each of the functions in turn to 
+// ensure that they work, simple first sanity check test.
+//
+
+TInt CheckAlignments()
+	{
+	// now it's time to play with the array of function pointers...
+	TInt  seed = 1;
+	TUint32 index = 0;
+	TInt ret = KErrNone;
+
+	while (index < (PAGESTRESS_FUNC_COUNT - 1))
+		{
+		DBGD_PRINT((_L("\nAddress (%u) 0x%x difference to next %u\n"), index, (TUint32)PagestressFuncPtrs[index], (TUint32)PagestressFuncPtrs[index + 1] - (TUint32)PagestressFuncPtrs[index]));	
+		if ((((TUint32)PagestressFuncPtrs[index + 1] - (TUint32)PagestressFuncPtrs[0]) % 4096) != 0)
+			{
+			DBGS_PRINT((_L("\nError! Allignment for %u is not offset of 4096 from index 0 0x%x 0x%x\n"), index, (TUint32)PagestressFuncPtrs[index + 1], (TUint32)PagestressFuncPtrs[0]));	
+			ret = KErrGeneral;
+			}
+		//seed = PagestressFuncPtrs[index](seed, index);
+		seed = CallTestFunc(seed, index, index);
+		index ++;
+		}
+	return ret;
+	}
+
+//
+// RunThreadForward
+//
+// Walk through the function pointer array (forwards) calling each function
+//
+
+void RunThreadForward()
+	{
+	TInt	seed = 1;
+	TUint32 index = 0;
+	RThread thisThread;
+
+	while (index < PAGESTRESS_FUNC_COUNT)
+		{
+		if (TestPrioChange)
+			{
+			TThreadPriority originalThreadPriority = thisThread.Priority();
+			thisThread.SetPriority(EPriorityLess);
+			User::AfterHighRes(0);
+			thisThread.SetPriority(originalThreadPriority);
+			}
+		//seed = PagestressFuncPtrs[index](seed, index);
+		seed = CallTestFunc(seed, index, index);
+		index ++;
+		}
+	}
+
+//
+// RunThreadBackward
+//
+// Walk through the function pointer array (backwards) calling each function
+//
+
+void RunThreadBackward()
+	{
+	TInt	seed = 1;
+	TInt	index = PAGESTRESS_FUNC_COUNT;
+	RThread thisThread;
+
+	while (index > 0)
+		{
+		if (TestPrioChange)
+			{
+			TThreadPriority originalThreadPriority = thisThread.Priority();
+			thisThread.SetPriority(EPriorityLess);
+			User::AfterHighRes(0);
+			thisThread.SetPriority(originalThreadPriority);
+			}
+		index --;
+		//seed = PagestressFuncPtrs[index](seed, index);
+		seed = CallTestFunc(seed, index, index);
+		}
+	}
+
+//
+// RunThreadRandom
+//
+// Walk through the function pointer array in a random order a number of times calling each function
+//
+
+void RunThreadRandom()
+	{
+	TInt	seed = 1;
+	TInt	index = 0;
+	TInt	randNum;
+	RThread thisThread;
+
+	while (index < (TInt)PAGESTRESS_FUNC_COUNT)
+		{
+		if (TestPrioChange)
+			{
+			TThreadPriority originalThreadPriority = thisThread.Priority();
+			thisThread.SetPriority(EPriorityLess);
+			User::AfterHighRes(0);
+			thisThread.SetPriority(originalThreadPriority);
+			}
+		randNum = Math::Random();
+		randNum %= PAGESTRESS_FUNC_COUNT;
+		//seed = PagestressFuncPtrs[randNum](seed, randNum);
+		seed = CallTestFunc(seed, randNum, randNum);
+		index ++;
+		}
+	}
+
+
+//
+// PerformTestThread
+//
+// This is the function that actually does the work.
+// It is complicated a little because test.Printf can only be called from the first thread that calls it 
+// so if we are using multiple threads we need to use a message queue to pass the debug info from the
+// child threads back to the parent for the parent to then call printf.
+//
+//
+
+LOCAL_C void PerformTestThread(TInt					  aThreadIndex, 
+							   RMsgQueue<TBuf <64> > *aMsgQueue = NULL, 
+							   TBuf<64>				 *aBuffer = NULL,
+							   RSemaphore			 *aTheSem = NULL)
+	{
+	TUint start = User::TickCount();
+
+	DEBUG_PRINT1((_L("%S : thread Starting %d\n"), &TestNameBuffer, aThreadIndex));
+	
+	// now select how we do the test...
+	TInt	iterIndex;
+
+	if (TEST_ALL == (TestWhichTests & TEST_ALL))
+		{
+		#define LOCAL_ORDER_INDEX1	6
+		#define LOCAL_ORDER_INDEX2	3
+		TInt	order[LOCAL_ORDER_INDEX1][LOCAL_ORDER_INDEX2] = {	{TEST_FORWARD, TEST_BACKWARD,TEST_RANDOM},
+																	{TEST_FORWARD, TEST_RANDOM,  TEST_BACKWARD},
+																	{TEST_BACKWARD,TEST_FORWARD, TEST_RANDOM},
+																	{TEST_BACKWARD,TEST_RANDOM,  TEST_FORWARD},
+																	{TEST_RANDOM,  TEST_FORWARD, TEST_BACKWARD},
+																	{TEST_RANDOM,  TEST_BACKWARD,TEST_FORWARD}};
+		TInt	whichOrder = 0;
+
+		for (iterIndex = 0; iterIndex < TestMaxLoops; iterIndex ++)
+			{
+			TInt    selOrder = ((aThreadIndex + 1) * (iterIndex + 1)) % LOCAL_ORDER_INDEX1;
+			for (whichOrder = 0; whichOrder < LOCAL_ORDER_INDEX2; whichOrder ++)
+				{
+				switch (order[selOrder][whichOrder])
+					{
+						case TEST_FORWARD:
+						DEBUG_PRINT1((_L("%S : %d Iter %d Forward\n"), &TestNameBuffer, aThreadIndex, iterIndex));
+						RunThreadForward();
+						break;
+
+						case TEST_BACKWARD:
+						DEBUG_PRINT1((_L("%S : %d Iter %d Backward\n"), &TestNameBuffer, aThreadIndex, iterIndex));
+						RunThreadBackward();
+						break;
+
+						case TEST_RANDOM:
+						DEBUG_PRINT1((_L("%S : %d Iter %d Random\n"), &TestNameBuffer, aThreadIndex, iterIndex));
+						RunThreadRandom();
+						break;
+						
+						default: // this is really an error.
+						break;
+					}
+				}
+			}
+		}
+	else
+		{
+		if (TestWhichTests & TEST_FORWARD)
+			{
+			for (iterIndex = 0; iterIndex < TestMaxLoops; iterIndex ++)
+				{
+				DEBUG_PRINT1((_L("%S : %d Iter %d Forward\n"), &TestNameBuffer, aThreadIndex, iterIndex));
+				RunThreadForward();
+				}
+			}
+			
+		if (TestWhichTests & TEST_BACKWARD)
+			{
+			for (iterIndex = 0; iterIndex < TestMaxLoops; iterIndex ++)
+				{
+				DEBUG_PRINT1((_L("%S : %d Iter %d Backward\n"), &TestNameBuffer, aThreadIndex, iterIndex));
+				RunThreadBackward();
+				}
+			}
+
+		if (TestWhichTests & TEST_RANDOM)
+			{
+			for (iterIndex = 0; iterIndex < TestMaxLoops; iterIndex ++)
+				{
+				DEBUG_PRINT1((_L("%S : %d Iter %d Random\n"), &TestNameBuffer, aThreadIndex, iterIndex));
+				RunThreadRandom();
+				}
+			}
+		}
+	DEBUG_PRINT1((_L("%S : thread Exiting %d (tickcount %u)\n"), &TestNameBuffer, aThreadIndex, User::TickCount() - start));
+	}
+
+
+//
+// MultipleTestThread
+//
+// Thread function, one created for each thread in a multiple thread test.
+//
+
+LOCAL_C TInt MultipleTestThread(TAny* aUseTb)
+	{
+	TBuf<64>					localBuffer;
+
+	if (TestInterleave)	
+		{
+		RThread				thisThread;
+		thisThread.SetPriority((TThreadPriority) TEST_INTERLEAVE_PRIO);
+		}
+
+	PerformTestThread((TInt) aUseTb, &TestMsgQueue, &localBuffer, &TestMultiSem);
+	
+	return KErrNone;
+	}
+
+//
+// DoSingleTest
+// 
+// Perform the single thread test, spawning a number of threads.
+//
+
+LOCAL_C void DoSingleTest()
+	{
+	PerformTestThread((TInt) TestInstanceId);
+	}
+
+//
+// FindDriveNumber
+// 
+// Find the first read write drive.
+//
+
+TInt FindDriveNumber(RFs fs)
+	{
+	TDriveInfo driveInfo;
+	for (TInt drvNum=0; drvNum<KMaxDrives; ++drvNum)
+		{
+		TInt r = fs.Drive(driveInfo, drvNum);
+		if (r >= 0)
+			{
+			if (driveInfo.iType == EMediaHardDisk)
+				return (drvNum);
+			}
+		}
+	return -1;
+	}
+
+//
+// FindFsNANDDrive
+//
+// Find the NAND drive
+//
+
+static TInt FindFsNANDDrive(RFs& aFs)
+	{
+	TDriveList driveList;
+	TDriveInfo driveInfo;
+	TInt r=aFs.DriveList(driveList);
+    if (r == KErrNone)
+		{
+		for (TInt drvNum= (DriveNumber<0)?0:DriveNumber; drvNum<KMaxDrives; ++drvNum)
+			{
+			if(!driveList[drvNum])
+				continue;   //-- skip unexisting drive
+
+			if (aFs.Drive(driveInfo, drvNum) == KErrNone)
+				{
+				if(driveInfo.iMediaAtt&KMediaAttPageable)
+					{
+					TBool readOnly = driveInfo.iMediaAtt & KMediaAttWriteProtected;		// skip ROFS partitions
+					if(!readOnly)
+						{
+						if ((drvNum==DriveNumber) || (DriveNumber<0))		// only test if running on this drive
+							{
+							return (drvNum);
+							}
+						}
+					}
+				}
+			}
+		}
+	return -1;
+	}
+
+//
+// FindMMCDriveNumber
+// 
+// Find the first read write drive.
+//
+
+TInt FindMMCDriveNumber(RFs& aFs)
+	{
+	TDriveInfo driveInfo;
+	for (TInt drvNum=0; drvNum<KMaxDrives; ++drvNum)
+		{
+		TInt r = aFs.Drive(driveInfo, drvNum);
+		if (r >= 0)
+			{
+			if (driveInfo.iType == EMediaHardDisk)
+				return (drvNum);
+			}
+		}
+	return -1; 
+	}
+
+//
+// PerformRomAndFileSystemAccess
+// 
+// Access the rom and dump it out to one of the writeable partitions...
+// really just to make the media server a little busy during the test.
+//
+
+TInt PerformRomAndFileSystemAccessThread(RMsgQueue<TBuf <64> > *aMsgQueue = NULL, 
+										 TBuf<64>			   *aBuffer = NULL,
+										 RSemaphore			   *aTheSem = NULL)
+	{
+	TUint maxBytes = KMaxTUint;
+	TInt startTime = User::TickCount();
+
+	RFs fs;
+	RFile file;
+	if (KErrNone != fs.Connect())
+		{
+		DEBUG_PRINT(_L("PerformRomAndFileSystemAccessThread : Can't connect to the FS\n"));
+		return KErrGeneral;
+		}
+
+	// get info about the ROM...
+	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
+	TUint8* start;
+	TUint8* end;
+	if(romHeader->iPageableRomStart)
+		{
+		start = (TUint8*)romHeader + romHeader->iPageableRomStart;
+		end = start + romHeader->iPageableRomSize;
+		}
+	else
+		{
+		start = (TUint8*)romHeader;
+		end = start + romHeader->iUncompressedSize;
+		}
+	if (end <= start)
+		return KErrGeneral;
+
+	// read all ROM pages in a random order...and write out to file in ROFs, 
+	TUint size = end - start - TestPageSize;
+	if(size > maxBytes)
+		size = maxBytes;
+
+	TUint32 random=1;
+	TPtrC8 rom;
+	TUint8 *theAddr;
+
+	TInt		drvNum = TestBootedFromMmc ? FindMMCDriveNumber(fs) : FindFsNANDDrive(fs);
+	TBuf<32>	filename = _L("d:\\Pageldrtst.tmp");
+	if (drvNum >= 0)
+		{
+		filename[0] = 'a' + drvNum;
+		DEBUG_PRINT((_L("%S : Filename %S\n"), &TestNameBuffer, &filename));
+		}
+	else
+		DEBUG_PRINT((_L("PerformRomAndFileSystemAccessThread : error getting drive num\n")));
+
+	for(TInt i=size/(TestPageSize); i>0; --i)
+		{
+		DEBUG_PRINT1((_L("%S : Opening the file\n"), &TestNameBuffer));
+		if (KErrNone != file.Replace(fs, filename, EFileWrite))
+			{
+			DEBUG_PRINT((_L("%S : Opening the file Failed!\n"), &TestNameBuffer));
+			}
+
+		random = random*69069+1;
+		theAddr = (TUint8*)(start+((TInt64(random)*TInt64(size))>>32));
+		if (theAddr + TestPageSize > end)
+			{
+			DEBUG_PRINT((_L("%S : address is past the end 0x%x / 0x%x\n"), &TestNameBuffer, (TInt)theAddr, (TInt)end));
+			}
+		rom.Set(theAddr,TestPageSize);
+		DEBUG_PRINT1((_L("%S : Writing the file\n"), &TestNameBuffer));
+		TInt ret = file.Write(rom);
+		if (ret != KErrNone)
+			{
+			DEBUG_PRINT1((_L("%S : Write returned error %d\n"), &TestNameBuffer, ret));
+			}
+		DEBUG_PRINT1((_L("%S : Closing the file\n"), &TestNameBuffer));
+		file.Close();
+
+		DEBUG_PRINT1((_L("%S : Deleting the file\n"), &TestNameBuffer));
+		ret = fs.Delete(filename);
+		if (KErrNone != ret)
+			{
+			DEBUG_PRINT((_L("%S : Delete returned error %d\n"), &TestNameBuffer, ret));
+			}
+		if (TestStopMedia)
+			break;
+		}
+	fs.Close();
+	DEBUG_PRINT1((_L("Done in %d ticks\n"), User::TickCount() - startTime));
+	return KErrNone;
+	}
+
+
+//
+// PerformRomAndFileSystemAccess
+//
+// Thread function, kicks off the file system access.
+//
+
+LOCAL_C TInt PerformRomAndFileSystemAccess(TAny* )
+	{
+	TBuf<64>					localBuffer;
+
+	PerformRomAndFileSystemAccessThread(&TestMsgQueue, &localBuffer, &TestMultiSem);
+	
+	return KErrNone;
+	}
+
+//
+// DoMultipleTest
+// 
+// Perform the multiple thread test, spawning a number of threads.
+// It is complicated a little because test.Printf can only be called from the first thread that calls it 
+// so if we are using multiple threads we need to use a message queue to pass the debug info from the
+// child threads back to the parent for the parent to then call printf.
+//
+#define DOTEST(__operation, __condition)\
+	if (aLowMem) \
+		{\
+		__operation;\
+		while (!__condition)\
+			{\
+			Ldd.DoReleaseSomeRam(TEST_LM_BLOCKS_FREE);\
+			__operation;\
+			}\
+		RUNTEST1(__condition);\
+		}\
+	else\
+		{\
+		__operation;\
+		RUNTEST1(__condition);\
+		}
+
+void DoMultipleTest(TBool aLowMem = EFalse)
+	{
+	TInt			 index;
+
+	RThread			*pTheThreads  = (RThread *)User::AllocZ(sizeof(RThread) * TestMultipleThreadCount);
+	TInt			*pThreadInUse = (TInt *)User::AllocZ(sizeof(TInt) * TestMultipleThreadCount);
+
+	TRequestStatus	mediaStatus;
+	RThread			mediaThread;
+	
+	TInt ret;
+	DOTEST((ret = TestMsgQueue.CreateLocal(TestMultipleThreadCount * 10, EOwnerProcess)),
+	       (KErrNone == ret));
+
+	DOTEST((ret = TestMultiSem.CreateLocal(1)),
+	       (KErrNone == ret));
+
+	// make sure we have a priority higher than that of the threads we spawn...
+	RThread thisThread;
+	TThreadPriority savedThreadPriority = thisThread.Priority();
+	const TThreadPriority KMainThreadPriority = EPriorityMuchMore;
+	__ASSERT_COMPILE(KMainThreadPriority>TEST_INTERLEAVE_PRIO);
+	thisThread.SetPriority(KMainThreadPriority);
+
+	if (TestMediaAccess)
+		{
+		TestStopMedia = EFalse;
+		mediaThread.Create(_L(""),PerformRomAndFileSystemAccess,KDefaultStackSize,NULL,NULL);
+		mediaThread.Logon(mediaStatus);
+		RUNTEST1(mediaStatus == KRequestPending);
+		mediaThread.Resume();
+		}
+
+	// spawn some processes to call the functions....
+	for (index = 0; index < TestMultipleThreadCount; index++)
+		{
+		DBGD_PRINT((_L("%S : Starting thread.%d!\n"), &TestNameBuffer, index));
+		DOTEST((ret = pTheThreads[index].Create(_L(""),MultipleTestThread,KDefaultStackSize,NULL,(TAny*) index)),
+		       (ret == KErrNone));
+		pTheThreads[index].Resume();
+		pThreadInUse[index] = 1;
+		}
+	
+	// now process any messages sent from the child threads.
+	TBool		anyUsed = ETrue;
+	TBuf<64>	localBuffer;
+
+	while(anyUsed)
+		{
+		anyUsed = EFalse;
+		// check the message queue and call printf if we get a message.
+		while (KErrNone == TestMsgQueue.Receive(localBuffer))
+			{
+			DBGS_PRINT((localBuffer));
+			}
+
+		// walk through the thread list to check which are still alive.
+		for (index = 0; index < TestMultipleThreadCount; index++)
+			{
+			if (pThreadInUse[index])
+				{
+				if (pTheThreads[index].ExitType() != EExitPending)
+					{
+					if (pTheThreads[index].ExitType() == EExitPanic)
+						{
+						DBGS_PRINT((_L("%S : Thread Panic'd  %d...\n"), &TestNameBuffer, index));	
+						}
+					pThreadInUse[index] = EFalse;
+					pTheThreads[index].Close();
+					}
+				else
+					{
+					anyUsed = ETrue;
+					}
+				}
+			}
+		User::AfterHighRes(50000);
+		}
+
+	if (TestMediaAccess)
+		{
+		TestStopMedia = ETrue;
+		DBGD_PRINT((_L("%S : Waiting for media thread to exit...\n"), &TestNameBuffer));	
+		User::WaitForRequest(mediaStatus);
+		mediaThread.Close();
+		}
+
+	TestMsgQueue.Close();
+	TestMultiSem.Close();
+
+	// cleanup the resources and exit.
+	User::Free(pTheThreads);
+	User::Free(pThreadInUse);
+
+	thisThread.SetPriority(savedThreadPriority);
+	}
+
+
+//
+// ParseCommandLine 
+//
+// read the arguments passed from the command line and set global variables to 
+// control the tests.
+//
+
+TBool ParseCommandLine()
+	{
+	TBuf<256> args;
+	User::CommandLine(args);
+	TLex	lex(args);
+	TBool	retVal = ETrue;
+	
+	// initially test for arguments, the parse them, if not apply some sensible defaults.
+	TBool	foundArgs = EFalse;	
+	
+	FOREVER
+		{
+		TPtrC  token=lex.NextToken();
+		if(token.Length()!=0)
+			{
+			if ((token == _L("help")) || (token == _L("-h")) || (token == _L("-?")))
+				{
+				DBGS_PRINT((_L("\nUsage:  %S [debug] [check] [single | multiple <numThreads>]  [forward | backward | random | all] [iters <iters>] [media] [lowmem] [interleave]\n'-' indicated infinity.\n\n"), &TestNameBuffer));
+				test.Getch();
+				}
+			else if (token == _L("debug"))
+				{
+				if (!TestSilent)
+					{
+					TestDebug = ETrue;
+					TestPrioChange = ETrue;
+					}
+				}
+			else if (token == _L("silent"))
+				{
+				TestSilent = ETrue;
+				TestDebug = EFalse;
+				}
+			else if (token == _L("check"))
+				{
+				TestCheck = ETrue;
+				}
+			else if (token == _L("single"))
+				{
+				TestSingle = ETrue;
+				}
+			else if (token == _L("multiple"))
+				{
+				TPtrC val=lex.NextToken();
+				TLex lexv(val);
+				TInt value;
+
+				if (lexv.Val(value)==KErrNone)
+					{
+					if ((value <= 0) || (value > 100))
+						{
+						TestMultipleThreadCount = 10;
+						}
+					else
+						{
+						TestMultipleThreadCount = value;
+						}
+					}
+				else
+					{
+					DBGS_PRINT((_L("Bad value for thread count '%S' was ignored.\n"), &val));
+					retVal = EFalse;
+					break;
+					}
+				TestMultiple = ETrue;
+				}
+			else if (token == _L("prio"))
+				{
+				TestPrioChange = !TestPrioChange;
+				}
+			else if (token == _L("lowmem"))
+				{
+				TestLowMem = ETrue;
+				}
+			else if (token == _L("media"))
+				{
+				TestMediaAccess = ETrue;
+				}
+			else if (token == _L("forward"))
+				{
+				TestWhichTests = TEST_FORWARD;
+				}
+			else if (token == _L("backward"))
+				{
+				TestWhichTests = TEST_BACKWARD;
+				}
+			else if (token == _L("random"))
+				{
+				TestWhichTests = TEST_RANDOM;
+				}
+			else if (token == _L("all"))
+				{
+				TestWhichTests = TEST_ALL;
+				}
+			else  if (token == _L("iters"))
+				{
+				TPtrC val=lex.NextToken();
+				TLex lexv(val);
+				TInt value;
+
+				if (val==_L("-"))
+					{
+					TestForever = ETrue;
+					TestMaxLoops = KMaxTInt;
+					}
+				else
+					{
+					if (lexv.Val(value)==KErrNone)
+						{
+						TestMaxLoops = value;
+						}
+					else
+						{
+						DBGS_PRINT((_L("Bad value for thread count '%S' was ignored.\n"), &val));
+						retVal = EFalse;
+						break;
+						}
+					}
+				}
+			else  if (token == _L("interleave"))
+				{
+				TestInterleave = ETrue;
+				}
+			else  if (token == _L("inst"))
+				{
+				TPtrC val=lex.NextToken();
+				TLex lexv(val);
+				TInt value;
+
+				if (lexv.Val(value)==KErrNone)
+					{
+					TestInstanceId = value;
+					}
+				}
+			else
+				{
+				if ((foundArgs == EFalse) && (token.Length() == 1))
+					{
+					// Single letter argument...only run on MMC drive
+					if (token.CompareF(_L("d")) == 0)
+						{
+						break;
+						}
+					else
+						{
+						if (!TestSilent)
+							{
+							test.Title();
+							test.Start(_L("Skipping non drive 'd' - Test Exiting."));
+							test.End();
+							}
+						foundArgs = ETrue;
+						TestExit = ETrue;
+						break;
+						}
+					}
+				DBGS_PRINT((_L("Unknown argument '%S' was ignored.\n"), &token));
+				break;
+				}
+			foundArgs = ETrue;
+			}
+		else
+			{
+			break;
+			}
+		}
+	if (!foundArgs)
+		{
+		retVal = EFalse;
+		}
+	return retVal;
+	}
+
+//
+// AreWeTheTestBase
+//
+// Test whether we are the root of the tests.
+//
+
+void AreWeTheTestBase(void)
+	{
+	if (!TestSilent)
+		{
+		TFileName  filename(RProcess().FileName());
+
+		TParse	myParse;
+		myParse.Set(filename, NULL, NULL);
+		TestNameBuffer.Zero();
+		TestNameBuffer.Append(myParse.Name());
+		TestNameBuffer.Append(_L(".exe"));
+
+		TestWeAreTheTestBase = !TestNameBuffer.Compare(_L("t_pagestress.exe"));
+
+		RFs fs;
+		if (KErrNone != fs.Connect())
+			{
+			TEntry  anEntry;
+			TInt retVal = fs.Entry(_L("z:\\test\\mmcdemandpaginge32tests.bat"), anEntry);
+			if (retVal == KErrNone)
+				{
+				TestBootedFromMmc = ETrue;
+				}
+			else
+				{
+				TestBootedFromMmc = EFalse;
+				}
+			fs.Close();
+			}
+
+		}
+	else
+		{
+		TestNameBuffer.Zero();
+		TestNameBuffer.Append(_L("t_pagestress.exe"));
+		}
+	}
+
+//
+// PerformAutoTest
+//
+// Perform the autotest
+//
+void PerformAutoTest()
+	{
+	SVMCacheInfo  tempPages;
+
+	if (TestIsDemandPaged)
+		{
+		UserSvr::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0);
+		DBGS_PRINT((_L("PerformAutoTest : Start cache info: iMinSize %d iMaxSize %d iCurrentSize %d iMaxFreeSize %d\n"),
+					 tempPages.iMinSize, tempPages.iMaxSize, tempPages.iCurrentSize ,tempPages.iMaxFreeSize));
+		}
+	TestInterleave = EFalse;
+	TestPrioChange = EFalse;
+	TestMediaAccess = EFalse;
+
+#if defined __ARMCC__ || defined __X86__
+	// Currently we only build aligned DLLs on ARMV5 and X86 builds.
+	TEST_NEXT((_L("Alignment Check.")));
+	RUNTEST1(CheckAlignments() == KErrNone);
+#endif
+
+	TestMaxLoops = 2;
+	TestWhichTests = TEST_RANDOM;
+
+	TEST_NEXT((_L("Single thread all.")));
+	DoSingleTest();
+
+	TEST_NEXT((_L("Multiple threads all.")));
+	DoMultipleTest();
+	
+	TestPrioChange = ETrue;
+	TEST_NEXT((_L("Multiple threads all with prio.")));
+	DoMultipleTest();
+
+	TestPrioChange = EFalse;
+	TestMediaAccess = ETrue;
+	TEST_NEXT((_L("Multiple threads all with media activity.")));
+	DoMultipleTest();
+
+	TestPrioChange = ETrue;
+	TestMediaAccess = ETrue;
+	TEST_NEXT((_L("Multiple threads all with media activity and prio.")));
+	DoMultipleTest();
+
+	TestInterleave = ETrue;
+	TestPrioChange = EFalse;
+	TestMediaAccess = EFalse;
+	
+	TEST_NEXT((_L("Multiple threads random with interleave.")));
+	DoMultipleTest();
+
+	TestPrioChange = ETrue;
+	TEST_NEXT((_L("Multiple threads random with interleave and prio.")));
+	DoMultipleTest();
+
+	TestMediaAccess = ETrue;
+	TEST_NEXT((_L("Multiple threads random with media interleave and prio.")));
+	DoMultipleTest();
+
+	if (TestIsDemandPaged)
+		{
+		UserSvr::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0);
+		DBGS_PRINT((_L("PerformAutoTest : End cache info: iMinSize %d iMaxSize %d iCurrentSize %d iMaxFreeSize %d\n"),
+					 tempPages.iMinSize, tempPages.iMaxSize, tempPages.iCurrentSize ,tempPages.iMaxFreeSize));
+		}
+	TestInterleave = EFalse;
+	TestPrioChange = EFalse;
+	TestMediaAccess = EFalse;
+	}
+
+//
+// DoLowMemTest
+//
+// Low Memory Test
+//
+
+void DoLowMemTest()
+	{
+	TInt r = User::LoadLogicalDevice(KPageStressTestLddName);
+	RUNTEST1(r==KErrNone || r==KErrAlreadyExists);
+	RUNTEST(Ldd.Open(),KErrNone);
+	
+	SVMCacheInfo  tempPages;
+	memset(&tempPages, 0, sizeof(tempPages));
+
+	if (TestIsDemandPaged)
+		{
+		// get the old cache info
+		UserSvr::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0);
+		TInt	minSize = 8 * 4096;
+		TInt	maxSize = 256 * 4096;
+		UserSvr::HalFunction(EHalGroupVM,EVMHalSetCacheSize,(TAny*)minSize,(TAny*)maxSize);
+		}
+
+	// First load some pages onto the page cache 
+	TestMaxLoops = 1;
+	TestWhichTests = TEST_RANDOM;
+	DoSingleTest();
+
+	Ldd.DoConsumeRamSetup(TEST_LM_NUM_FREE, TEST_LM_BLOCKSIZE);
+	TEST_NEXT((_L("Single thread with Low memory.")));
+	TestMultipleThreadCount	= 25;
+	TestInterleave = EFalse;
+	TestMaxLoops = 20;
+	TestPrioChange = EFalse;
+	TestMediaAccess = EFalse;
+	TestWhichTests = TEST_ALL;
+	
+	DoSingleTest();
+	
+	Ldd.DoConsumeRamFinish();
+
+	TEST_NEXT((_L("Multiple thread with Low memory.")));
+	// First load some pages onto the page cache 
+	TestMaxLoops = 1;
+	TestWhichTests = TEST_RANDOM;
+	DoSingleTest();
+
+	Ldd.DoConsumeRamSetup(TEST_LM_NUM_FREE, TEST_LM_BLOCKSIZE);
+	
+	TestWhichTests = TEST_ALL;
+	TestMaxLoops = 10;
+	TestMultipleThreadCount	= 25;
+	DoMultipleTest(ETrue);
+
+	Ldd.DoConsumeRamFinish();
+
+	TEST_NEXT((_L("Multiple thread with Low memory, with starting free ram.")));
+	// First load some pages onto the page cache 
+	TestMaxLoops = 1;
+	TestWhichTests = TEST_RANDOM;
+	DoSingleTest();
+
+	Ldd.DoConsumeRamSetup(32, TEST_LM_BLOCKSIZE);
+	
+	TestWhichTests = TEST_ALL;
+	TestMaxLoops = 10;
+	TestMultipleThreadCount	= 25;
+	DoMultipleTest(ETrue);
+
+	Ldd.DoConsumeRamFinish();
+	
+	TEST_NEXT((_L("Close test driver")));
+	Ldd.Close();
+	RUNTEST(User::FreeLogicalDevice(KPageStressTestLddName), KErrNone);
+	if (TestIsDemandPaged)
+		{
+		TInt minSize = tempPages.iMinSize;
+		TInt maxSize = tempPages.iMaxSize;
+		UserSvr::HalFunction(EHalGroupVM,EVMHalSetCacheSize,(TAny*)minSize,(TAny*)maxSize);
+		}
+	}
+
+//
+// E32Main
+//
+// Main entry point.
+//
+
+TInt E32Main()
+	{
+#ifndef TEST_ON_UNPAGED
+	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
+	if(!romHeader->iPageableRomStart)
+		{
+		TestIsDemandPaged = EFalse;
+		}
+#endif
+	TUint start = User::TickCount();
+
+	TBool parseResult = ParseCommandLine();
+
+	if (TestExit)
+		{
+		return KErrNone;
+		}
+
+	AreWeTheTestBase();
+
+	TInt  minSize = 8 * 4096;
+	TInt  maxSize = 64 * 4096;
+	SVMCacheInfo  tempPages;
+	memset(&tempPages, 0, sizeof(tempPages));
+	if (TestIsDemandPaged)
+		{
+		// get the old cache info
+		UserSvr::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0);
+		// set the cache to our test value
+		UserSvr::HalFunction(EHalGroupVM,EVMHalSetCacheSize,(TAny*)minSize,(TAny*)maxSize);
+		}
+
+	// get the page size.
+	UserSvr::HalFunction(EHalGroupKernel,EKernelHalPageSizeInBytes,&TestPageSize,0);
+
+	if (!TestSilent)
+		{
+		test.Title();
+		test.Start(_L("Demand Paging stress tests..."));
+		test.Printf(_L("%S\n"), &TestNameBuffer);
+		}
+
+	if (parseResult)
+		{
+		if (!TestSilent)
+			{
+			extern TInt *CheckLdmiaInstr(void);
+			test.Printf(_L("%S : CheckLdmiaInstr\n"), &TestNameBuffer);
+			TInt   *theAddr = CheckLdmiaInstr();
+			test.Printf(_L("%S : CheckLdmiaInstr complete 0x%x...\n"), &TestNameBuffer, (TInt)theAddr);
+			}
+		if (TestCheck)
+			{
+			CheckAlignments();
+			}
+		if (TestLowMem)
+			{
+			DoLowMemTest();
+			}
+		if (TestSingle)
+			{
+			DoSingleTest();
+			}
+		if (TestMultiple)
+			{
+			DoMultipleTest();
+			}
+		}
+	else
+		{
+		PerformAutoTest();
+
+		DoLowMemTest();
+
+		if (TestWeAreTheTestBase)
+			{
+			RProcess		theProcess;
+			TRequestStatus	status;
+
+			TInt retVal = theProcess.Create(_L("t_pagestress_rom.exe"),_L(""));
+			if (retVal != KErrNotFound)
+				{
+				RUNTEST1(retVal == KErrNone);
+				theProcess.Logon(status);
+				RUNTEST1(status == KRequestPending);
+				theProcess.Resume();
+				User::WaitForRequest(status);
+				if (theProcess.ExitType() != EExitPending)
+					{
+					RUNTEST1(theProcess.ExitType() != EExitPanic);
+					}
+				}
+			}
+		}
+
+	if (TestIsDemandPaged)
+		{
+		minSize = tempPages.iMinSize;
+		maxSize = tempPages.iMaxSize;
+		// put the cache back to the the original values.
+		UserSvr::HalFunction(EHalGroupVM,EVMHalSetCacheSize,(TAny*)minSize,(TAny*)maxSize);
+		}
+
+	if (!TestSilent)
+		{
+		test.Printf(_L("%S : Complete (%u ticks)\n"), &TestNameBuffer, User::TickCount() - start);	
+		test.End();
+		}
+	return 0;
+	}
+
+