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


