kerneltest/f32test/demandpaging/t_wdpstress.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 07 Jan 2010 13:38:45 +0200
changeset 33 0173bcd7697c
parent 0 a41df078684a
child 62 4a8fed1c0ef6
permissions -rw-r--r--
Revision: 201001 Kit: 201001

// Copyright (c) 2008-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_wdpstress.cpp
// Data Paging Stress Tests
// Common command lines:
// t_wdpstress lowmem
// debug - switch on debugging information
// silent - no output to the screen or serial port
// single - run the tests in a single thread
// multiple <numThreads> - run the tests in multiple threads where <numThreads> (max 50 simultaneous threads)
// 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
// stack - perform autotest only with stack paging tests
// chunk - perform autotest only with chunk paging tests 			
// commit - perform autotest only with committing and decommitting paging tests 
// ipc - perform autotest only with ipc pinning tests 			
// all - perform autotest with all paging tests(ipc, stack, chunk and commit)
// badserver - perform ipc pinning tests with dead server
// iters <count> - the number of times to loop 
// 
//

//! @SYMTestCaseID			KBASE-T_WDPSTRESS-xxx
//! @SYMTestType			UT
//! @SYMPREQ				PREQ1954
//! @SYMTestCaseDesc		Writable Data Paging Stress Tests
//! @SYMTestActions			
//! @SYMTestExpectedResults All tests should pass.
//! @SYMTestPriority        High
//! @SYMTestStatus          Implemented
//----------------------------------------------------------------------------------------------
//
#define __E32TEST_EXTENSION__
#include <e32test.h>
#include <e32ver.h>
RTest test(_L("T_WDPSTRESS"));

#include <e32rom.h>
#include <u32hal.h>
#include <f32file.h>
#include <e32svr.h>
#include <e32hal.h>
#include <f32dbg.h>
#include <e32msgqueue.h>
#include <e32math.h>
#include <dptest.h>
#include <hal.h>
#include "testdefs.h"

#ifdef __X86__
#define TEST_ON_UNPAGED
#endif

#include "t_pagestress.h"

TBool   	TestDebug					= EFalse;
TBool		TestSilent					= EFalse;
TBool		TestExit					= EFalse;


TInt		gPerformTestLoop			= 10;					// Number of times to perform test on a thread
const TUint KMaxTestThreads				= 20;					// The maximum number of threads allowed to run simultaniously
TInt		gNumTestThreads				= KMaxTestThreads;		// The number of threads to run simultaneously

#define TEST_INTERLEAVE_PRIO			EPriorityMore

TBool		TestWeAreTheTestBase		= EFalse;

#define TEST_NONE		0x0
#define TEST_IPC		0x1
#define TEST_STACK		0x2
#define TEST_CHUNK		0x4
#define TEST_COMMIT		0x8
#define TEST_ALL		(TEST_COMMIT | TEST_CHUNK | TEST_STACK | TEST_IPC)

TUint32		gSetTests					= TEST_ALL;
TUint32		gTestWhichTests				= gSetTests;
TBuf<32>	gTestNameBuffer;
TBool		gTestPrioChange				= EFalse;				
TBool		gTestStopMedia				= EFalse;
TBool		gTestMediaAccess			= EFalse;
TBool		gTestInterleave				= EFalse;
TBool		gTestBadServer				= EFalse;

#define TEST_LM_NUM_FREE	0
#define TEST_LM_BLOCKSIZE	1
#define TEST_LM_BLOCKS_FREE	4

RPageStressTestLdd Ldd;
RSemaphore	TestMultiSem;
RMsgQueue<TBuf <64> >	TestMsgQueue;

TBool		gIsDemandPaged			= ETrue;
TBool		gTestRunning				= EFalse;				// To control when to stop flushing
TBool		gMaxChunksReached			= EFalse;				// On moving memory model, the number of chunks per process is capped

TInt		gPageSize;											// The number of bytes per page
TUint		gPageShift;
TUint		gChunksAllocd				= 0;					// The total number of chunks that have been allocated
TUint		gMaxChunks					= 0;					// The max amount of chunks after which KErrOverflow will be returned
RHeap*		gThreadHeap					= NULL;					
RHeap*		gStackHeap					= NULL;

TInt		gTestType					= -1;					// The type of test that is to be performed

#define TEST_NEXT(__args) \
	if (!TestSilent)\
		test.Next __args;

#define RDBGD_PRINT(__args)\
	if (TestDebug)\
	RDebug::Printf __args ;\

#define RDBGS_PRINT(__args)\
	if (!TestSilent)\
	RDebug::Printf __args ;\

#define DEBUG_PRINT(__args)\
if (!TestSilent)\
	{\
	if (aTestArguments.iMsgQueue && aTestArguments.iBuffer && aTestArguments.iTheSem)\
		{\
		aTestArguments.iBuffer->Zero();\
		aTestArguments.iBuffer->Format __args ;\
		aTestArguments.iTheSem->Wait();\
		aTestArguments.iMsgQueue->SendBlocking(*aTestArguments.iBuffer);\
		aTestArguments.iTheSem->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)\
	}

#define DOTEST(__operation, __condition)\
	if (aLowMem) \
		{\
		__operation;\
		while (!__condition)\
			{\
			Ldd.DoReleaseSomeRam(TEST_LM_BLOCKS_FREE);\
			__operation;\
			}\
		RUNTEST1(__condition);\
		}\
	else\
		{\
		__operation;\
		RUNTEST1(__condition);\
		}

#define DOTEST1(__operation, __condition)\
	if (aTestArguments.iLowMem) \
		{\
		__operation;\
		while (!__condition)\
			{\
			Ldd.DoReleaseSomeRam(TEST_LM_BLOCKS_FREE);\
			__operation;\
			}\
		RUNTEST1(__condition);\
		}\
	else\
		{\
		__operation;\
		RUNTEST1(__condition);\
		}

struct SThreadExitResults
	{
	TInt					iExitType;
	TInt					iExitReason;
	};
SThreadExitResults* gResultsArray;
const TInt KExitTypeReset = -1;

struct SPerformTestArgs
	{
	TInt					iThreadIndex;
	RMsgQueue<TBuf <64> >	*iMsgQueue; 
	TBuf<64>				*iBuffer;
	RSemaphore				*iTheSem;
	TBool					iLowMem;
	TInt					iTestType;
	};


TInt DoTest(TInt gTestType, TBool aLowMem = EFalse);
enum
	{
	ETestSingle, 
	ETestMultiple,
	ETestMedia,
	ETestLowMem,
	ETestInterleave,
	ETestCommit, 
	ETestTypes,
	// This is at the moment manual
	ETestBadServer, 
	ETestTypeEnd, 
	};

TInt FreeRam()
	{
	// wait for any async cleanup in the supervisor to finish first...
	UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0);

	TMemoryInfoV1Buf meminfo;
	TInt r = UserHal::MemoryInfo(meminfo);
	test_KErrNone(r);
	return meminfo().iFreeRamInBytes;
	}

const TUint KStackSize = 20 * 4096;
TUint stackLimit = 150;//*** NEED TO WORK OUT HOW MUCH STACK WE HAVE***

/**
Recursive function
*/
void CallRecFunc(TUint aNum, TInt aThreadIndex)
	{
	RDBGD_PRINT(("ThreadId %d CallRecFunc, aNum = %d\n", aThreadIndex, aNum));
	if (aNum >= stackLimit)
		{// To avoid a stack overflow
		return;
		}
	else
		{
		CallRecFunc(++aNum, aThreadIndex);
		User::After(0);
		}
	RDBGD_PRINT(("ThreadId %d CRF(%d)Returning...", aThreadIndex, aNum));
	return;
	}

/**
Thread that calls a recursive function
*/
TInt ThreadFunc(TAny* aThreadIndex)
	{
	for (TUint i=0; i<1; i++)
		{
		CallRecFunc(0, (TInt)aThreadIndex);
		}
	RDBGD_PRINT(("ThreadId %d ThreadFunc Returning...", (TInt)aThreadIndex));
	return KErrNone;
	}

/**
Thread continuously flushes the paging cache
*/
TInt FlushFunc(TAny* /*aPtr*/)
	{
	RThread().SetPriority(EPriorityMore);
	while(gTestRunning)
		{
		DPTest::FlushCache();	
		User::After((Math::Random()&0xfff)*10);
		}
	return KErrNone;
	}


//
// TestStackPaging
//
// Create a paged thread which calls a recursive function.
// Calls to function will be placed on the stack, which is data paged
//

TInt TestStackPaging(SPerformTestArgs& aTestArguments)
	{
	RDBGD_PRINT(("Creating test thread"));
	TBuf<16> runThreadName;
	runThreadName = _L("");
	TThreadCreateInfo threadCreateInfo(runThreadName, ThreadFunc, KStackSize, (TAny*) aTestArguments.iThreadIndex);
	threadCreateInfo.SetCreateHeap(KMinHeapSize, KMinHeapSize);
	//threadCreateInfo.SetUseHeap(NULL);
	threadCreateInfo.SetPaging(TThreadCreateInfo::EPaged);

	RThread testThread;
	TInt r;
	for(;;)
		{
		r = testThread.Create(threadCreateInfo);
		if(r != KErrNoMemory)
			break;
		if(!aTestArguments.iLowMem)
			break;
		if(Ldd.DoReleaseSomeRam(TEST_LM_BLOCKS_FREE) != KErrNone)
			break;
		RDBGD_PRINT(("TestStackPaging released some RAM\n"));
		}

	RDBGD_PRINT(("TID(%d) TestStackPaging create r = %d freeRam = %d\n", aTestArguments.iThreadIndex, r, FreeRam()));
	if (r != KErrNone)
		return r;

	TRequestStatus threadStatus;
	testThread.Logon(threadStatus);
	
	RDBGD_PRINT(("resuming test thread"));
	testThread.Resume();
	
	RDBGD_PRINT(("waiting for threadstatus"));
	User::WaitForRequest(threadStatus);
	
	RDBGD_PRINT(("Killing threads\n"));
	testThread.Close();

	return KErrNone;
	}

//--------------------------Server Pinning stuff-----------------------------------------------------
_LIT(KTestServer,"CTestServer");
const TUint KSemServer = 0;

class CTestServer : public CServer2
	{
public:
	CTestServer(TInt aPriority);
protected:
	//override the pure virtual functions:
	virtual CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const;
	};


class CTestSession : public CSession2
	{
public:
	enum TTestMode
		{
		EStop,
		ERead,
		EWrite,
		EReadWrite,
		};
//Override pure virtual
	IMPORT_C virtual void ServiceL(const RMessage2& aMessage);
private:
	TInt ReadWrite(const RMessage2& aMessage, TBool aRead, TBool aWrite);
	TBool iClientDied;
	};


class CMyActiveScheduler : public CActiveScheduler
	{
public:
	virtual void Error(TInt anError) const; //override pure virtual error function
	};


class RSession : public RSessionBase
	{
public:
	TInt PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr)
		{
		return (SendReceive(aFunction, aPtr));
		}
	TInt PublicCreateSession(const TDesC& aServer,TInt aMessageSlots)
		{
		return (CreateSession(aServer,User::Version(),aMessageSlots));
		}
	};

struct SServerArgs
	{
	TBool iBadServer;
	RSemaphore iSemArray;
	};

SServerArgs gServerArgsArray[KMaxTestThreads];

CTestServer::CTestServer(TInt aPriority)
//
// Constructor - sets name
//
	: CServer2(aPriority)
	{}

CSession2* CTestServer::NewSessionL(const TVersion& aVersion,const RMessage2& /*aMessage*/) const
//
// Virtual fn - checks version supported and creates a CTestSession
//
	{
	TVersion version(KE32MajorVersionNumber,KE32MinorVersionNumber,KE32BuildVersionNumber);
	if (User::QueryVersionSupported(version,aVersion)==EFalse)
		User::Leave(KErrNotSupported);
	CTestSession* newCTestSession = new CTestSession;
	if (newCTestSession==NULL)
		User::Panic(_L("NewSessionL failure"), KErrNoMemory);
	return(newCTestSession);
	}

TInt CTestSession::ReadWrite(const RMessage2& aMessage, TBool aRead, TBool aWrite)
	{
	TInt r = KErrNone;
	for (TUint argIndex = 0; argIndex < 4; argIndex++)
		{
		// Get the length of the descriptor and verify it is as expected.
		TInt length = aMessage.GetDesLength(argIndex);
		if (length < KErrNone)
			{
			RDebug::Printf("  Error getting descriptor length %d", length);
			return length;
			}

		
		if (aRead)
			{
			// Now read the descriptor
			HBufC8* des = HBufC8::New(length);
			if (!des)
				return KErrNoMemory;
			TPtr8 desPtr = des->Des();
			r = aMessage.Read(argIndex, desPtr);
			if (r != KErrNone)
				{
				delete des;
				return r;
				}
			//TODO: Verify the descriptor
			delete des;
			}

		if (aWrite)
			{
			// Now write to the maximum length of the descriptor.
			TInt max = length;
			HBufC8* argTmp = HBufC8::New(max);
			if (!argTmp)
				return KErrNoMemory;

			TPtr8 argPtr = argTmp->Des();
			argPtr.SetLength(max);
			for (TInt i = 0; i < max; i++)
				argPtr[i] = (TUint8)argIndex;
			r = aMessage.Write(argIndex, argPtr);
			delete argTmp;
			if (r != KErrNone)
				return r;
			}
		}

	return KErrNone;
	}


void CTestSession::ServiceL(const RMessage2& aMessage)
//
// Virtual message-handler
//
	{
	TInt r = KErrNone;
	iClientDied = EFalse;
	switch (aMessage.Function())
		{
		case EStop:
			RDBGD_PRINT(("Stopping server"));
			CActiveScheduler::Stop();
			break;

		case ERead:
			r = ReadWrite(aMessage, ETrue, EFalse);
			break;
		case EWrite:
			r = ReadWrite(aMessage, EFalse, ETrue);
			break;
		case EReadWrite:
			r = ReadWrite(aMessage, ETrue, ETrue);
			break;
	
		default:
			r = KErrNotSupported;

		}
 	aMessage.Complete(r);

	// If descriptors aren't as expected then panic so the test will fail.
	if (r != KErrNone)
		User::Panic(_L("ServiceL failure"), r);
	}

// CTestSession funtions

void CMyActiveScheduler::Error(TInt anError) const
//
// Virtual error handler
//
	{
	User::Panic(_L("CMyActiveScheduer::Error"), anError);
	}


TInt ServerThread(TAny* aThreadIndex)
//
// Passed as the server thread in 2 tests - sets up and runs CTestServer
//
	{
	RDBGD_PRINT(("ServerThread"));
	TUint threadIndex = (TUint)aThreadIndex;

	TBuf<16> serverName;
	serverName = _L("ServerName_");
	serverName.AppendNum(threadIndex);


	CMyActiveScheduler* pScheduler = new CMyActiveScheduler;
	if (pScheduler == NULL)
		{
		gServerArgsArray[threadIndex].iBadServer = ETrue;
		gServerArgsArray[threadIndex].iSemArray.Signal();
		return KErrNoMemory;
		}

	CActiveScheduler::Install(pScheduler);

	CTestServer* pServer = new CTestServer(0);
	if (pServer == NULL)
		{
		gServerArgsArray[threadIndex].iBadServer = ETrue;
		gServerArgsArray[threadIndex].iSemArray.Signal();
		delete pScheduler;
		return KErrNoMemory;
		}

	//Starting a CServer2 also Adds it to the ActiveScheduler
	TInt r = pServer->Start(serverName);
	if (r != KErrNone)
		{
		gServerArgsArray[threadIndex].iBadServer = ETrue;
		gServerArgsArray[threadIndex].iSemArray.Signal();
		delete pScheduler;
		delete pServer;
		return r;
		}

	RDBGD_PRINT(("Start ActiveScheduler and signal to client"));
	RDBGD_PRINT(("There might be something going on beneath this window\n"));
	gServerArgsArray[threadIndex].iSemArray.Signal();
	CActiveScheduler::Start();

	delete pScheduler;
	delete pServer;

	return KErrNone;
	}

TInt BadServerThread(TAny* /*aThreadIndex*/)
//
// Passed as the server thread in 2 tests - sets up and runs CTestServer
//
	{
	RDBGD_PRINT(("BadServerThread"));
	CMyActiveScheduler* pScheduler = new CMyActiveScheduler;
	if (pScheduler == NULL)
		{
		RDBGD_PRINT(("BST:Fail1"));
		gServerArgsArray[KSemServer].iBadServer = ETrue;
		gServerArgsArray[KSemServer].iSemArray.Signal();
		return KErrNoMemory;
		}

	CActiveScheduler::Install(pScheduler);

	CTestServer* pServer = new CTestServer(0);
	if (pServer == NULL)
		{
		RDBGD_PRINT(("BST:Fail2"));
		gServerArgsArray[KSemServer].iBadServer = ETrue;
		gServerArgsArray[KSemServer].iSemArray.Signal();
		delete pScheduler;
		return KErrNoMemory;
		}

	//pServer->SetPinClientDescriptors(ETrue);


	//Starting a CServer2 also Adds it to the ActiveScheduler
	TInt r = pServer->Start(KTestServer);
	if (r != KErrNone)
		{
		RDBGD_PRINT(("BST:Fail3"));
		gServerArgsArray[KSemServer].iBadServer = ETrue;
		gServerArgsArray[KSemServer].iSemArray.Signal();
		delete pScheduler;
		delete pServer;
		return r;
		}

	RDBGD_PRINT(("Start ActiveScheduler and signal to client"));
	RDBGD_PRINT(("There might be something going on beneath this window\n"));
	gServerArgsArray[KSemServer].iSemArray.Signal();
	CActiveScheduler::Start();

	delete pScheduler;
	delete pServer;
	RDBGD_PRINT(("BST:Pass1"));
	return KErrNone;
	}

TInt SendMessages(TUint aIters, TUint aSize, TDesC& aServerName, TInt aIndex, TBool aLowMem = EFalse)
//
// Passed as the first client thread - signals the server to do several tests
//
	{
	HBufC8* argTmp1;
	HBufC8* argTmp2;
	HBufC8* argTmp3;
	HBufC8* argTmp4;

	DOTEST((argTmp1 = HBufC8::New(aSize)), (argTmp1 != NULL));
	*argTmp1 = (const TUint8*)"argTmp1";
	TPtr8 ptr1 = argTmp1->Des();

	DOTEST((argTmp2 = HBufC8::New(aSize)), (argTmp2 != NULL));
	*argTmp2 = (const TUint8*)"argTmp2";
	TPtr8 ptr2 = argTmp2->Des();

	DOTEST((argTmp3 = HBufC8::New(aSize)), (argTmp3 != NULL));
	*argTmp3 = (const TUint8*)"argTmp3";
	TPtr8 ptr3 = argTmp3->Des();

	DOTEST((argTmp4 = HBufC8::New(aSize)), (argTmp1 != NULL));
	*argTmp4 = (const TUint8*)"argTmp4";
	TPtr8 ptr4 = argTmp4->Des();
	
	RSession session;
	TInt r = KErrNone;
	if(gTestBadServer)
		{//Don't do bad server tests with lowmem
		r = session.PublicCreateSession(aServerName,5);
		}
	else
		{
		DOTEST((r = session.PublicCreateSession(aServerName,5)), (r != KErrNoMemory));
		}
	if (r != KErrNone)
		{
		RDBGD_PRINT(("SendMessages[%d] failed to create session r = %d", aIndex, r));
		return r;
		}
	
	if(gTestBadServer)
		{
		RThread::Rendezvous(KErrNone);
		RDBGD_PRINT(("Wait on sem %d", aIndex));
		//gServerArgsArray[KSemCliSessStarted].iSemArray.Wait();
		}
	
	RDBGD_PRINT(("ID (%d)ReadWrite" ,aIndex));
	for (TUint i = 0; i < aIters; i++)
		{
		TUint mode = (i&0x3) + CTestSession::ERead;
		switch(mode)
			{
			case CTestSession::ERead:
				DOTEST((r = session.PublicSendReceive(CTestSession::ERead, TIpcArgs(&ptr1, &ptr2, &ptr3, &ptr4).PinArgs())), 
						(r != KErrNoMemory));
				if (r != KErrNone)
					return r;
				break;

			case CTestSession::EWrite:
				DOTEST((r = session.PublicSendReceive(CTestSession::EWrite, TIpcArgs(&ptr1, &ptr2, &ptr3, &ptr4).PinArgs())), 
						(r != KErrNoMemory));
				if (r != KErrNone)
					return r;
				break;
			case CTestSession::EReadWrite:
				DOTEST((r = session.PublicSendReceive(CTestSession::EReadWrite, TIpcArgs(&ptr1, &ptr2, &ptr3, &ptr4).PinArgs())), 
						(r != KErrNoMemory));
				if (r != KErrNone)
					return r;
				break;

			}
		}
	RDBGD_PRINT(("ID(%d) Closing session", aIndex));
	session.Close();
	return r;
	}

TInt TestIPCPinning(SPerformTestArgs& aTestArguments)
	{
	TInt r = KErrNone;
	// Create the server thread it needs to have a unpaged stack and heap.
	TBuf<16> serverThreadName;
	serverThreadName = _L("ServerThread_");
	serverThreadName.AppendNum(aTestArguments.iThreadIndex);
	TThreadCreateInfo serverInfo(serverThreadName, ServerThread, KDefaultStackSize, (TAny *) aTestArguments.iThreadIndex);
	serverInfo.SetUseHeap(NULL);
	
	gServerArgsArray[aTestArguments.iThreadIndex].iBadServer = EFalse;

	// Create the semaphores for the IPC pinning tests
	DOTEST1((r = gServerArgsArray[aTestArguments.iThreadIndex].iSemArray.CreateLocal(0)), (r != KErrNoMemory));
	if (r != KErrNone)
		{
		RDBGD_PRINT(("Failed to create semaphonre[%d] r = %d", aTestArguments.iThreadIndex, r));
		return r;
		}

	RThread serverThread;
	TInt r1 = KErrNone;
	DOTEST1((r1 = serverThread.Create(serverInfo)), (r1 != KErrNoMemory));
	if (r1 != KErrNone)
		{
		RDBGD_PRINT(("Failed to create server thread[%d] r1 = %d", aTestArguments.iThreadIndex, r1));
		return r1;
		}
	TRequestStatus serverStat;
	serverThread.Logon(serverStat);
	serverThread.Resume();

	// Wait for the server to start and then create a session to it.
	TBuf<16> serverName;
	serverName = _L("ServerName_");
	serverName.AppendNum(aTestArguments.iThreadIndex);

	gServerArgsArray[aTestArguments.iThreadIndex].iSemArray.Wait();
	
	// First check that the server started successfully
	if (gServerArgsArray[aTestArguments.iThreadIndex].iBadServer)
		return KErrServerTerminated;

	RSession session;
	DOTEST1((r1 = session.PublicCreateSession(serverName,5)), (r1 != KErrNoMemory));
	if (r1 != KErrNone)
		{
		RDBGD_PRINT(("Failed to create session[%d] r1 = %d", aTestArguments.iThreadIndex, r1));
		return r1;
		}
	
	r1 = SendMessages(50, 10, serverName, aTestArguments.iThreadIndex, aTestArguments.iLowMem);
	if (r1 != KErrNone)
		{
		RDBGD_PRINT(("SendMessages[%d] r1 = %d", aTestArguments.iThreadIndex, r1));
		return r1;
		}
	TInt r2 = KErrNone;
	
	// Signal to stop ActiveScheduler and wait for server to stop.
	session.PublicSendReceive(CTestSession::EStop, TIpcArgs());
	session.Close();

	User::WaitForRequest(serverStat);
	if (serverThread.ExitType() == EExitKill &&
		serverThread.ExitReason() != KErrNone)	
		{
		r2 = serverThread.ExitReason();
		}
	if (serverThread.ExitType() != EExitKill)	
		{
		RDBGD_PRINT(("Server thread panic'd"));
		r2 = KErrGeneral;
		}

	serverThread.Close();
	gServerArgsArray[aTestArguments.iThreadIndex].iSemArray.Close();
		
	if (r1 != KErrNone)
		return r1;

	return r2;
	}

TInt ClientThread(TAny* aClientThread)
	{
	TInt r = KErrNone;
	
	TBuf<16> serverName;
	serverName = KTestServer;
	RDBGD_PRINT(("CT(%d):Sending Messages" ,aClientThread));
	r = SendMessages(500, 10, serverName, (TInt) aClientThread);
	if (r != KErrNone)
		{
		RDBGD_PRINT(("SendMessages[%d] r = %d", (TInt) aClientThread, r));
		return r;
		}
	return r;
	}

TInt TestIPCBadServer(SPerformTestArgs& aTestArguments)
	{
	TInt cliRet = KErrNone;
	TInt serRet = KErrNone;

	// Create the server thread it needs to have a unpaged stack and heap.
	TBuf<16> serverThreadName;
	serverThreadName = _L("BadServerThread");
	TThreadCreateInfo serverInfo(serverThreadName, BadServerThread, KDefaultStackSize, NULL);
	serverInfo.SetUseHeap(NULL);
	
	// Create the semaphores for the IPC pinning tests
	DOTEST1((serRet = gServerArgsArray[KSemServer].iSemArray.CreateLocal(0)), (serRet != KErrNoMemory));
	if (serRet != KErrNone)
		{
		RDBGD_PRINT(("Failed to create semaphonre[%d] serRet = %d", KSemServer, serRet));
		return serRet;
		}

	RThread serverThread;
	DOTEST1((serRet = serverThread.Create(serverInfo)), (serRet != KErrNoMemory));
	if (serRet != KErrNone)
		{
		RDBGD_PRINT(("Failed to create server thread serRet = %d", serRet));
		return serRet;
		}
	TRequestStatus serverStat;
	serverThread.Logon(serverStat);
	serverThread.Resume();

	// Wait for the server to start and then create a session to it.
	gServerArgsArray[KSemServer].iSemArray.Wait();
	
	// First check that the server started successfully
	if (gServerArgsArray[KSemServer].iBadServer)
		return KErrServerTerminated;


	//create client threads
	const TUint KNumClientThreads = 50;	
	RThread clientThreads[KNumClientThreads];
	TRequestStatus clientStarted[KNumClientThreads];
	TRequestStatus clientStats[KNumClientThreads];

	// Create the client threads
	TBuf<16> clientThreadName;
	TUint i;
	for (i = 0; i < KNumClientThreads; i++)
		{
		clientThreadName = _L("clientThread_");
		clientThreadName.AppendNum(i);
		TThreadCreateInfo clientInfo(clientThreadName, ClientThread, KDefaultStackSize, (TAny*)i);
		clientInfo.SetPaging(TThreadCreateInfo::EPaged);
		clientInfo.SetCreateHeap(KMinHeapSize, KMinHeapSize);
		cliRet = clientThreads[i].Create(clientInfo);
		if (cliRet != KErrNone)
			{
			RDBGD_PRINT(("Failed to create client thread [%d] cliRet = %d", i, cliRet));
			return cliRet;
			}
		clientThreads[i].Rendezvous(clientStarted[i]);	
		clientThreads[i].Logon(clientStats[i]);
		clientThreads[i].Resume();
		}
	
	// Wait for creation of the client thread sessions
	for (i = 0; i < KNumClientThreads; i++)
		{
		User::WaitForRequest(clientStarted[i]);
		if (clientStarted[i].Int() != KErrNone)
			return clientStarted[i].Int();
		}
	

	// Once the messages are being sent, create a session to the
	// same server and signal to stop ActiveScheduler
	RSession session;
	serRet = session.PublicCreateSession(KTestServer,5);
	if (serRet != KErrNone)
		{
		RDBGD_PRINT(("Failed to create session serRet = %d", serRet));
		return serRet;
		}
	session.PublicSendReceive(CTestSession::EStop, TIpcArgs());
	session.Close();

	// Wait for the client thread to end.
	cliRet = KErrNone;
	for (i = 0; i < KNumClientThreads; i++)
		{
		User::WaitForRequest(clientStats[i]);
		RDBGD_PRINT(("Thread complete clientStats[%d] = %d", i, clientStats[i].Int()));
		if (clientStats[i].Int() != KErrNone && 
			clientStats[i].Int() != KErrServerTerminated)
			{
			cliRet = clientStats[i].Int();
			}
		}

	// Check that the server ended correctly
	serRet = KErrNone;
	User::WaitForRequest(serverStat);
	if (serverThread.ExitType() == EExitKill &&
		serverThread.ExitReason() != KErrNone)	
		{
		serRet = serverThread.ExitReason();
		}
	if (serverThread.ExitType() != EExitKill)	
		{
		RDBGD_PRINT(("Server thread panic'd"));
		serRet = KErrGeneral;
		}

	// Close all the server thread and client threads
	for (i = 0; i < KNumClientThreads; i++)
		{
		clientThreads[i].Close();
		}
	serverThread.Close();
		
	if (cliRet != KErrNone)
		return cliRet;

	return serRet;
	}


//
// RemoveChunkAlloc
//
// Remove ALL chunks allocated
//
// @param aChunkArray The array that stores a reference to the chunks created.
// @param aChunkArraySize The size of aChunkArray.
//
void RemoveChunkAlloc(RChunk*& aChunkArray, TUint aChunkArraySize)
	{
	if (aChunkArray == NULL)
		{// The chunk array has already been deleted.
		return;
		}

	for (TUint i = 0; i < aChunkArraySize; i++)
		{
		if (aChunkArray[i].Handle() != NULL)
			{
			aChunkArray[i].Close();
			gChunksAllocd --;
			if (gChunksAllocd < gMaxChunks)
				gMaxChunksReached = EFalse;
			}
		}
	delete[] aChunkArray;
	aChunkArray = NULL;
	}	

TInt WriteToChunk(RChunk* aChunkArray, TUint aChunkArraySize)
	{
	for (TUint j = 0; j < aChunkArraySize; j++) 
		{
		if (aChunkArray[j].Handle() != NULL)
			{
			TUint32* base = (TUint32*)aChunkArray[j].Base();
			TUint32* end = (TUint32*)(aChunkArray[j].Base() + aChunkArray[j].Size());
			for (TUint32 k = 0; base < end; k++)
				{
				*base++ = k; // write index to the chunk
				}
			}		
		}
	return KErrNone;
	}

TUint32 ReadByte(volatile TUint32* aPtr)
	{
	return *aPtr;
	}

TInt ReadChunk(RChunk* aChunkArray, TUint aChunkArraySize)
	{
	for (TUint j=0; j < aChunkArraySize; j++) //Read all open chunks
		{
		if (aChunkArray[j].Handle() != NULL)
			{
			TUint32* base = (TUint32*)aChunkArray[j].Base();
			TUint32* end = (TUint32*)(aChunkArray[j].Base() + aChunkArray[j].Size());
			for (TUint32 k = 0; base < end; k++)
				{
				TUint value = ReadByte((volatile TUint32*)base++);
				if (value != k)
					{
					RDBGS_PRINT(("Read value incorrect expected 0x%x got 0x%x", k, value));
					return KErrGeneral;
					}
				}
			}		
		}
	return KErrNone;
	}


TInt CreateChunks(SPerformTestArgs& aTestArguments, RChunk*& aChunkArray, TUint aChunkArraySize)
	{
	TInt r = KErrNone;

	TUint chunkSize = 1 << gPageShift;

	// Allocate as many chunks as is specified, either with the default chunk size or a specified chunk size
	if (aChunkArray == NULL)
		{
		DOTEST1((aChunkArray = new RChunk[aChunkArraySize]), (aChunkArray != NULL));
		if (aChunkArray == NULL)
			return KErrNoMemory;
		}
	
	TChunkCreateInfo createInfo;
	createInfo.SetNormal(chunkSize, chunkSize);
	createInfo.SetPaging(TChunkCreateInfo::EPaged);


	// Create chunks for each RChunk with a NULL handle.
	for (TUint i = 0; i < aChunkArraySize; i++)
		{
		DOTEST1((r = aChunkArray[i].Create(createInfo)), (r != KErrNoMemory));
		if (r != KErrNone)
			{
			if (r == KErrOverflow)
				{
				gMaxChunks = gChunksAllocd;
				RDBGD_PRINT(("Max Chunks Allowed = %d", gMaxChunks));
				gMaxChunksReached = ETrue;
				}
			return r;
			}
		gChunksAllocd++;
		RDBGD_PRINT(("TID(%d) aChunkArray[%d], r = %d", aTestArguments.iThreadIndex, i, r));
		}
	RDBGD_PRINT(("TID(%d) created chunks r = %d", aTestArguments.iThreadIndex, r));
	
	return KErrNone;
	}
//
// TestChunkPaging
//
// Create a number of chunks and write to them
// read the chunk back to ensure the values are correct
//

TInt TestChunkPaging(SPerformTestArgs& aTestArguments)
	{
	TInt r = KErrNone;
	const TUint KNumChunks = 10;
	
	
	if(gMaxChunksReached)
		{// We cant create any more chunks as the max number has been reached
		return KErrNone;
		}

	RChunk* chunkArray = NULL;	
	r = CreateChunks(aTestArguments, chunkArray, KNumChunks);
	if (r != KErrNone)
		{
		if (r == KErrOverflow)
			{
			RDBGD_PRINT(("Max number of chunks reached"));
			RemoveChunkAlloc(chunkArray, KNumChunks);
			return KErrNone;
			}
		RDBGD_PRINT(("TID(%d) CreateChunks r = %d", aTestArguments.iThreadIndex, r));
		return r;
		}

	r = WriteToChunk(chunkArray, KNumChunks);
	if (r != KErrNone)
		{
		RemoveChunkAlloc(chunkArray, KNumChunks);
		RDBGD_PRINT(("TID(%d) WriteToChunk r = %d", aTestArguments.iThreadIndex, r));
		return r;
		}

	r = ReadChunk(chunkArray, KNumChunks);
	if (r != KErrNone)
		{
		RemoveChunkAlloc(chunkArray, KNumChunks);
		RDBGD_PRINT(("TID(%d) ReadChunk r = %d", aTestArguments.iThreadIndex, r));
		return r;
		} 
	RemoveChunkAlloc(chunkArray, KNumChunks);
	return KErrNone;
	}


//
// TestChunkCommit
//
// Create a chunk
// commit a page at a time, write to that page and then decommit the page
//

TInt TestChunkCommit(SPerformTestArgs& aTestArguments)
	{
	TInt r = KErrNone;
	RChunk testChunk;

	TUint chunkSize = 70 << gPageShift;

	TChunkCreateInfo createInfo;
	createInfo.SetDisconnected(0, 0, chunkSize);
	createInfo.SetPaging(TChunkCreateInfo::EPaged);
	DOTEST1((r = testChunk.Create(createInfo)), (r != KErrNoMemory));
	if (r != KErrNone)
		{
		return r;
		}
	TUint offset = 0;
	while(offset < chunkSize)
		{
		// Commit a page
		DOTEST1((r = testChunk.Commit(offset,gPageSize)), (r != KErrNoMemory));
		if (r != KErrNone)
			{
			return r;
			}

		// Write to the page
		TUint8* pageStart = testChunk.Base() + offset;
		*pageStart = 0xed;


		// Decommit the page
		r = testChunk.Decommit(offset, gPageSize);
		if (r != KErrNone)
			{
			return r;
			}
		
		offset += gPageSize;
		}
	

	testChunk.Close();
	return r;
	}

//
// 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 TInt PerformTestThread(SPerformTestArgs& aTestArguments)
	{
	TInt r = KErrNone;
	TUint start = User::TickCount();

	DEBUG_PRINT1((_L("%S : thread Starting %d\n"), &gTestNameBuffer, aTestArguments.iThreadIndex));
	// now select how we do the test...
	TInt	iterIndex = 0;


	if (TEST_ALL == (gTestWhichTests & TEST_ALL))
		{
		#define LOCAL_ORDER_INDEX1	6
		#define LOCAL_ORDER_INDEX2	4
		TInt	order[LOCAL_ORDER_INDEX1][LOCAL_ORDER_INDEX2] = {	{TEST_STACK, TEST_CHUNK,TEST_COMMIT, TEST_IPC},
																	{TEST_STACK, TEST_COMMIT,  TEST_CHUNK, TEST_IPC},
																	{TEST_CHUNK,TEST_STACK, TEST_COMMIT, TEST_IPC},
																	{TEST_CHUNK,TEST_COMMIT,  TEST_STACK, TEST_IPC},
																	{TEST_COMMIT,  TEST_STACK, TEST_CHUNK, TEST_IPC},
																	{TEST_COMMIT,  TEST_CHUNK,TEST_STACK, TEST_IPC}};
		TInt	whichOrder = 0;
		iterIndex = 0;
		for (iterIndex = 0; iterIndex < gPerformTestLoop; iterIndex ++)
			{
			DEBUG_PRINT1((_L("iterIndex = %d\n"), iterIndex));
			TInt    selOrder = ((aTestArguments.iThreadIndex + 1) * (iterIndex + 1)) % LOCAL_ORDER_INDEX1;
			for (whichOrder = 0; whichOrder < LOCAL_ORDER_INDEX2; whichOrder ++)
				{
				DEBUG_PRINT1((_L("whichOrder = %d\n"), whichOrder));
				switch (order[selOrder][whichOrder])
					{
					case TEST_STACK:
					DEBUG_PRINT1((_L("%S : %d Iter %d Stack\n"), &gTestNameBuffer, aTestArguments.iThreadIndex, iterIndex));
					r = TestStackPaging(aTestArguments);
					DEBUG_PRINT1((_L("ThreadId %d Finished TestStackPaging() r = %d\n"), aTestArguments.iThreadIndex, r));
					if (r != KErrNone)
						return r;
					break;

					case TEST_CHUNK:
					DEBUG_PRINT1((_L("%S : %d Iter %d Chunk\n"), &gTestNameBuffer, aTestArguments.iThreadIndex, iterIndex));
					r = TestChunkPaging(aTestArguments);
					DEBUG_PRINT1((_L("ThreadId %d Finished TestChunkPaging() r = %d\n"), aTestArguments.iThreadIndex, r));
					if (r != KErrNone)
						return r;
					break;

					case TEST_COMMIT:
					DEBUG_PRINT1((_L("%S : %d Iter %d Commit\n"), &gTestNameBuffer, aTestArguments.iThreadIndex, iterIndex));
					r = TestChunkCommit(aTestArguments);
					DEBUG_PRINT1((_L("ThreadId %d Finished TestChunkCommit() r = %d\n"), aTestArguments.iThreadIndex, r));
					if (r != KErrNone)
						return r;
					break;

					case TEST_IPC:
					
					if (gTestBadServer)
						{
						DEBUG_PRINT1((_L("%S : %d Iter %d IPC-BadServer\n"), &gTestNameBuffer, aTestArguments.iThreadIndex, iterIndex));
						r = TestIPCBadServer(aTestArguments);
						DEBUG_PRINT1((_L("ThreadId %d Finished TestIPCBadServer() r = %d\n"), aTestArguments.iThreadIndex, r));
						}
					else
						{
						DEBUG_PRINT1((_L("%S : %d Iter %d IPC\n"), &gTestNameBuffer, aTestArguments.iThreadIndex, iterIndex));
						// Limit the IPC pinning stuff to 2 loops else will take a long time to run
						if (gNumTestThreads > 1 && gPerformTestLoop > 2)
							break;
						r = TestIPCPinning(aTestArguments);
						DEBUG_PRINT1((_L("ThreadId %d Finished TestIPCPinning() r = %d\n"), aTestArguments.iThreadIndex, r));
						if (r != KErrNone)
							return r;
						}
					break;
					
					default: // this is really an error.
					break;
					}
				iterIndex++;
				}
			}
		}
	else
		{
		if (gTestWhichTests & TEST_STACK)
			{
			for (iterIndex = 0; iterIndex < gPerformTestLoop; iterIndex ++)
				{
				DEBUG_PRINT1((_L("%S : %d Iter %d Stack\n"), &gTestNameBuffer, aTestArguments.iThreadIndex, iterIndex));
				r = TestStackPaging(aTestArguments);
				DEBUG_PRINT1((_L("ThreadId %d Finished TestStackPaging() r = %d\n"), aTestArguments.iThreadIndex, r));
				if (r != KErrNone)
						return r;
				}
			}
			
		if (gTestWhichTests & TEST_CHUNK)
			{
			for (iterIndex = 0; iterIndex < gPerformTestLoop; iterIndex ++)
				{
				DEBUG_PRINT1((_L("%S : %d Iter %d Chunk\n"), &gTestNameBuffer, aTestArguments.iThreadIndex, iterIndex));
				r = TestChunkPaging(aTestArguments);
				DEBUG_PRINT1((_L("ThreadId %d Finished TestChunkPaging() r = %d\n"), aTestArguments.iThreadIndex, r));
				if (r != KErrNone)
						return r;
				}
			}

		if (gTestWhichTests & TEST_COMMIT)
			{
			for (iterIndex = 0; iterIndex < gPerformTestLoop; iterIndex ++)
				{
				DEBUG_PRINT1((_L("%S : %d Iter %d Commit\n"), &gTestNameBuffer, aTestArguments.iThreadIndex, iterIndex));
				r = TestChunkCommit(aTestArguments);
				DEBUG_PRINT1((_L("ThreadId %d Finished TestChunkCommit() r = %d\n"), aTestArguments.iThreadIndex, r));
				if (r != KErrNone)
					return r;
				}
			}

		if (gTestWhichTests & TEST_IPC)
			{
			// In multiple thread case limit IPC test to 2 loops else will take a long time
			TInt loops = (gPerformTestLoop <= 2 && gNumTestThreads) ? gPerformTestLoop : 2;
			for (iterIndex = 0; iterIndex < loops; iterIndex ++)
				{
				if (gTestBadServer)
					{
					r = TestIPCBadServer(aTestArguments);
					DEBUG_PRINT1((_L("ThreadId %d Finished TestIPCBadServer() r = %d\n"), aTestArguments.iThreadIndex, r));
					}
				else
					{
					DEBUG_PRINT1((_L("%S : %d Iter %d IPC\n"), &gTestNameBuffer, aTestArguments.iThreadIndex, iterIndex));
					r = TestIPCPinning(aTestArguments);
					DEBUG_PRINT1((_L("ThreadId %d Finished TestIPCPinning() r = %d\n"), aTestArguments.iThreadIndex, r));
					if (r != KErrNone)
						return r;
					}
				}
			}
		}
	
	DEBUG_PRINT1((_L("%S : thread Exiting %d (tickcount %u)\n"), &gTestNameBuffer, aTestArguments.iThreadIndex, (User::TickCount() - start)));
	return r;
	}


//
// MultipleTestThread
//
// Thread function, one created for each thread in a multiple thread test.
//

LOCAL_C TInt MultipleTestThread(TAny* aTestArgs)
	{
	TInt r = KErrNone;
	TBuf<64>					localBuffer;

	if (gTestInterleave)	
		{
		RThread				thisThread;
		thisThread.SetPriority((TThreadPriority) TEST_INTERLEAVE_PRIO);
		}
	
	SPerformTestArgs& testArgs = *(SPerformTestArgs*)aTestArgs;
	testArgs.iBuffer = &localBuffer;
	
	RDBGD_PRINT(("Performing test thread ThreadID(%d)\n", testArgs.iThreadIndex));
	r = PerformTestThread(testArgs);
	
	return r;
	}



//
// 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(SPerformTestArgs& aTestArguments)
	{
	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 - gPageSize;
	if(size > maxBytes)
		size = maxBytes;

	TUint32 random = 1;
	TPtrC8 rom;
	TUint8 *theAddr;

	//TInt		drvNum = TestBootedFromMmc ? FindMMCDriveNumber(fs) : FindFsNANDDrive(fs);
	TInt drvNum = FindMMCDriveNumber(fs);
	TBuf<32>	filename = _L("d:\\Pageldrtst.tmp");
	if (drvNum >= 0)
		{
		filename[0] = (TUint16)('a' + drvNum);
		DEBUG_PRINT1((_L("%S : Filename %S\n"), &gTestNameBuffer, &filename));
		}
	else
		DEBUG_PRINT((_L("PerformRomAndFileSystemAccessThread : error getting drive num\n")));

	for(TInt i = (size >> gPageShift); i > 0; --i)
		{
		DEBUG_PRINT1((_L("%S : Opening the file\n"), &gTestNameBuffer));
		if (KErrNone != file.Replace(fs, filename, EFileWrite))
			{
			DEBUG_PRINT1((_L("%S : Opening the file Failed!\n"), &gTestNameBuffer));
			}

		random = random * 69069 + 1;
		theAddr = (TUint8*)(start+((TInt64(random)*TInt64(size))>>32));
		if (theAddr + gPageSize > end)
			{
			DEBUG_PRINT1((_L("%S : address is past the end 0x%x / 0x%x\n"), &gTestNameBuffer, (TInt)theAddr, (TInt)end));
			}
		rom.Set(theAddr,gPageSize);
		DEBUG_PRINT1((_L("%S : Writing the file\n"), &gTestNameBuffer));
		TInt ret = file.Write(rom);
		if (ret != KErrNone)
			{
			DEBUG_PRINT1((_L("%S : Write returned error %d\n"), &gTestNameBuffer, ret));
			}
		DEBUG_PRINT1((_L("%S : Closing the file\n"), &gTestNameBuffer));
		file.Close();

		DEBUG_PRINT1((_L("%S : Deleting the file\n"), &gTestNameBuffer));
		ret = fs.Delete(filename);
		if (KErrNone != ret)
			{
			DEBUG_PRINT1((_L("%S : Delete returned error %d\n"), &gTestNameBuffer, ret));
			}
		if (gTestStopMedia)
			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* aTestArgs)
	{
	TBuf<64>					localBuffer;
	
	SPerformTestArgs& testArgs = *(SPerformTestArgs*)aTestArgs;
	testArgs.iBuffer = &localBuffer;
	
	PerformRomAndFileSystemAccessThread(testArgs);
	
	return KErrNone;
	}




//
// StartFlushing
//
// Create a thread that will continuously flush the paging cache
//
void StartFlushing(TRequestStatus &aStatus, RThread &aFlushThread, TBool aLowMem = EFalse)
	{
	TInt ret;
	gTestRunning = ETrue;

	TThreadCreateInfo flushThreadInfo(_L("FlushThread"), FlushFunc, KDefaultStackSize,NULL);
	flushThreadInfo.SetCreateHeap(KMinHeapSize, KMinHeapSize);
	
	if (!aLowMem)
		{
		test_KErrNone(aFlushThread.Create(flushThreadInfo));
		}
	else
		{
		DOTEST((ret = aFlushThread.Create(flushThreadInfo)), (ret != KErrNoMemory));
		test_KErrNone(ret);
		}
	
	
	aFlushThread.Logon(aStatus);
	
	aFlushThread.Resume();
	}

//
// FinishFlushing
//
// Close the thread flushing the paging cache
//
void FinishFlushing(TRequestStatus &aStatus, RThread &aFlushThread)
	{
	gTestRunning = EFalse;
	User::WaitForRequest(aStatus);
	// TO DO: Check Exit tyoe
	CLOSE_AND_WAIT(aFlushThread);
	}


//
// ResetResults
// 
// Clear the previous results from the results array
//
TInt ResetResults()
	{
	for (TUint i = 0; i < KMaxTestThreads; i++)
		{
		gResultsArray[i].iExitType = KExitTypeReset;
		gResultsArray[i].iExitReason = KErrNone;
		}
	return KErrNone;
	}


//
// CheckResults
//
// Check that the results are as expected
//
TInt CheckResults()
	{
	TUint i;
	for (i = 0; i < KMaxTestThreads; i++)
		{
		if (gResultsArray[i].iExitType == KExitTypeReset)
			continue;
		RDBGD_PRINT(("%S : Thread %d ExitType(%d) ExitReason(%d)...\n", 
					&gTestNameBuffer, i, gResultsArray[i].iExitType, gResultsArray[i].iExitReason));
		}
	
	for (i = 0; i < KMaxTestThreads; i++)
		{
		if (gResultsArray[i].iExitType == KExitTypeReset)
			continue;
		
		if (gResultsArray[i].iExitType != EExitKill)
			{
			RDBGS_PRINT(("Thread %d ExitType(%d) Expected(%d)\n", i, gResultsArray[i].iExitType, EExitKill));
			return KErrGeneral;
			}
		
		// Allow for No Memory as we can run out of memory due to high number of threads and
		// Overflow as the number of chunks that can be created on moving memory model is capped
		if (gResultsArray[i].iExitReason != KErrNone &&
			gResultsArray[i].iExitReason != KErrNoMemory &&
			gResultsArray[i].iExitReason != KErrOverflow)
			{
			RDBGS_PRINT(("Thread %d ExitReason(%d) Expected either %d, %d or %d\n", 
							i, gResultsArray[i].iExitReason, KErrNone, KErrNoMemory, KErrOverflow));
			return KErrGeneral;
			}
		}
	return KErrNone;
	}


//
// PrintOptions
//
// Print out the options of the test
//
void PrintOptions()
	{
	SVMCacheInfo  tempPages;
	if (gIsDemandPaged)
		{
		UserSvr::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0);
		test.Printf(_L("PerformAutoTest : Start cache info: iMinSize 0x%x iMaxSize 0x%x iCurrentSize 0x%x iMaxFreeSize 0x%x\n"),
					 tempPages.iMinSize, tempPages.iMaxSize, tempPages.iCurrentSize ,tempPages.iMaxFreeSize);
		}
	
	test.Printf(_L("Loops (%d), Threads (%d), Tests: "), gPerformTestLoop, gNumTestThreads);
	if (TEST_ALL == (gTestWhichTests & TEST_ALL))
		{
		test.Printf(_L("All, "));
		}
	else if (gTestWhichTests & TEST_STACK)
		{
		test.Printf(_L("Stack, "));
		}
	else if (gTestWhichTests & TEST_CHUNK)
		{
		test.Printf(_L("Chunk, "));
		}
	else if (gTestWhichTests & TEST_COMMIT)
		{
		test.Printf(_L("Commit, "));
		}
	else if (gTestWhichTests & TEST_IPC)
		{
		test.Printf(_L("IPC Pinning, "));
		}
	else
		{
		test.Printf(_L("?, "));
		}
	test.Printf(_L("\nOptions: "));

	if(gTestInterleave)
		test.Printf(_L("Interleave "));
	if(gTestPrioChange)
		test.Printf(_L("Priority "));
	if(gTestMediaAccess)
		test.Printf(_L("Media"));
	if(gTestBadServer)
		test.Printf(_L("BadServer"));
	test.Printf(_L("\n"));
	}

// 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.
//
TInt DoMultipleTest(TBool aLowMem = EFalse)
	{
	SVMCacheInfo  tempPages;
	memset(&tempPages, 0, sizeof(tempPages));

	if (gIsDemandPaged)
		{
		// get the old cache info
		UserSvr::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0);
		// set the cache to our test value
		UserSvr::HalFunction(EHalGroupVM,EVMHalSetCacheSize,(TAny*)tempPages.iMinSize,(TAny*)(tempPages.iMaxSize * gNumTestThreads));
		}
	
	if (!TestSilent)
		PrintOptions();

	TUint startTime = User::TickCount();
	TInt			 index;
	TInt ret = KErrNone;
	TBuf<16> multiThreadName;
	TBuf<16> rerunThreadName;
	
	ResetResults();
	
	TRequestStatus flushStatus;
	RThread flushThread;
	StartFlushing(flushStatus, flushThread, aLowMem);

	DOTEST((gThreadHeap = User::ChunkHeap(NULL, 0x1000, 0x1000)), (gThreadHeap != NULL));
	test_NotNull(gThreadHeap);
	
	DOTEST((gStackHeap = User::ChunkHeap(NULL, 0x1000, 0x1000)), (gStackHeap != NULL));
	test_NotNull(gStackHeap);

	TThreadCreateInfo	*pThreadCreateInfo = (TThreadCreateInfo *)User::AllocZ(sizeof(TThreadCreateInfo) * gNumTestThreads);
	RThread				*pTheThreads  = (RThread *)User::AllocZ(sizeof(RThread) * gNumTestThreads);
	TInt				*pThreadInUse = (TInt *)User::AllocZ(sizeof(TInt) * gNumTestThreads);

	TRequestStatus	mediaStatus;
	RThread			mediaThread;
	
	
	DOTEST((ret = TestMsgQueue.CreateLocal(gNumTestThreads * 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);

	SPerformTestArgs mediaArgs;
	mediaArgs.iMsgQueue = &TestMsgQueue; 
	mediaArgs.iTheSem = &TestMultiSem;
	mediaArgs.iLowMem = aLowMem;
	
	if (gTestMediaAccess)
		{
		TThreadCreateInfo mediaInfo(_L(""),PerformRomAndFileSystemAccess,KDefaultStackSize,(TAny*)&mediaArgs);
		mediaInfo.SetUseHeap(NULL);
		mediaInfo.SetPaging(TThreadCreateInfo::EPaged);
		gTestStopMedia = EFalse;
		ret = mediaThread.Create(mediaInfo);
		if (ret != KErrNone)
			return ret;
		mediaThread.Logon(mediaStatus);
		RUNTEST1(mediaStatus == KRequestPending);
		mediaThread.Resume();
		}

	TThreadCreateInfo** infoPtrs = new TThreadCreateInfo*[gNumTestThreads]; 
	if (infoPtrs == NULL)
		return KErrNoMemory;
	
	SPerformTestArgs *testArgs = new SPerformTestArgs[gNumTestThreads];
	if (testArgs == NULL)
		return KErrNoMemory;

	Mem::FillZ(testArgs, gNumTestThreads * sizeof(SPerformTestArgs));

	for (index = 0; index < gNumTestThreads; index++)
		{
		RDBGD_PRINT(("%S : Starting thread.%d!\n", &gTestNameBuffer, index));
		multiThreadName = _L("TestThread_");
		multiThreadName.AppendNum(index);

		testArgs[index].iThreadIndex = index;
		testArgs[index].iMsgQueue = &TestMsgQueue; 
		testArgs[index].iTheSem = &TestMultiSem;
		testArgs[index].iLowMem = aLowMem;

		RDBGD_PRINT(("Creating thread.%d!\n", index));
		infoPtrs[index] = new TThreadCreateInfo(multiThreadName, MultipleTestThread, KDefaultStackSize, (TAny*)&testArgs[index]);
		if (infoPtrs[index] == NULL)
			continue;
		infoPtrs[index]->SetCreateHeap(KMinHeapSize, KMinHeapSize);
		infoPtrs[index]->SetPaging(TThreadCreateInfo::EPaged);
		//infoPtrs[index]->SetUseHeap(gThreadHeap);
		DOTEST((ret = pTheThreads[index].Create(*infoPtrs[index])), (ret != KErrNoMemory));
		if (ret != KErrNone)
			continue;
		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))
			{
			if (!TestSilent)
				test.Printf(localBuffer);
			}

		// walk through the thread list to check which are still alive.
		for (index = 0; index < gNumTestThreads; index++)
			{
			if (pThreadInUse[index])
				{
				if (pTheThreads[index].ExitType() != EExitPending)
					{
					if (aLowMem &&
						pTheThreads[index].ExitType() == EExitKill &&
						pTheThreads[index].ExitReason() == KErrNoMemory &&
						Ldd.DoReleaseSomeRam(TEST_LM_BLOCKS_FREE) == KErrNone)
						{// If thread was killed with no memory in a low mem scenario
						// then release some RAM and restart the thread again
						anyUsed = ETrue;
						RDBGD_PRINT(("Thread index %d EExitKill KErrNoMemory\n", index));
						CLOSE_AND_WAIT(pTheThreads[index]);

						RDBGD_PRINT(("Re-running Thread index %d\n", index));
						rerunThreadName = _L("RRTestThread_");
						rerunThreadName.AppendNum(index);
						
						delete infoPtrs[index];				
						infoPtrs[index] = new TThreadCreateInfo(rerunThreadName, MultipleTestThread, KDefaultStackSize, (TAny*)&testArgs[index]);
						if (infoPtrs[index] == NULL)
							continue;
						infoPtrs[index]->SetCreateHeap(KMinHeapSize, KMinHeapSize);
						infoPtrs[index]->SetPaging(TThreadCreateInfo::EPaged);
						//infoPtrs[index]->SetUseHeap(gThreadHeap);
						ret = pTheThreads[index].Create(*infoPtrs[index]);
						if (ret != KErrNone)
							continue;
						pTheThreads[index].Resume();
						pThreadInUse[index] = 1;
						continue;
						}
					if (pTheThreads[index].ExitType() == EExitPanic)
						{
						RDBGD_PRINT(("%S : Thread Panic'd  %d...\n", &gTestNameBuffer, index));	
						}
					
					//Store the results but let all the threads finish
					gResultsArray[index].iExitType = pTheThreads[index].ExitType();
					gResultsArray[index].iExitReason = pTheThreads[index].ExitReason();
					
					pThreadInUse[index] = EFalse;
					pTheThreads[index].Close();
					}
				else
					{
					anyUsed = ETrue;
					}
				}
			}

		User::AfterHighRes(50000);
		}

	if (gTestMediaAccess)
		{
		gTestStopMedia = ETrue;
		RDBGD_PRINT(("%S : Waiting for media thread to exit...\n", &gTestNameBuffer));	
		User::WaitForRequest(mediaStatus);
		mediaThread.Close();
		}

	TestMsgQueue.Close();
	TestMultiSem.Close();

	// cleanup the resources and exit.
	User::Free(pTheThreads);
	User::Free(pThreadInUse);
	User::Free(pThreadCreateInfo);
	delete infoPtrs;
	delete testArgs;


	FinishFlushing(flushStatus, flushThread);
	gThreadHeap->Close();
	gStackHeap->Close();
	thisThread.SetPriority(savedThreadPriority);
	ret = CheckResults();
	RDBGS_PRINT(("Test Complete (%u ticks)\n", User::TickCount() - startTime));
	
	if (gIsDemandPaged)
		{
		// put the cache back to the the original values.
		UserSvr::HalFunction(EHalGroupVM,EVMHalSetCacheSize,(TAny*)tempPages.iMinSize,(TAny*)tempPages.iMaxSize);
		}
	return ret;
	}


//
// DoSingleTest
// 
// Perform the single thread test,.
//

LOCAL_C TInt DoSingleTest(TBool aLowMem = EFalse)
	{
	TUint origThreadCount = gNumTestThreads;
	gNumTestThreads = 1;
	TInt r = DoMultipleTest(aLowMem);
	gNumTestThreads = origThreadCount;
	return r;
	}



//
// 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("-?")))
				{
				RDBGS_PRINT(("\nUsage:  %S n", &gTestNameBuffer));
				RDBGS_PRINT(("\ndebug: Prints out tracing in the test"));
				RDBGS_PRINT(("\n[single | multiple <numThreads>] : Specify to run in a single thread or multiple threads and how many"));
				RDBGS_PRINT(("\n[ipc | stack | chunk| commit| all | badserver] : which type of test to run "));
				RDBGS_PRINT(("\n-> ipc: IPC Pinning tests"));
				RDBGS_PRINT(("\n-> stack: Stack paging tests"));
				RDBGS_PRINT(("\n-> chunk: Chunk paging tests"));
				RDBGS_PRINT(("\n-> commit: Chunk committing tests"));
				RDBGS_PRINT(("\n-> all: All the above tests"));
				RDBGS_PRINT(("\n-> badserver: IPC Pinning tests with a dead server"));
				RDBGS_PRINT(("\n[iters <iters>] : Number of loops each test should perform"));
				RDBGS_PRINT(("\n[media] : Perform multiple test with media activity in the background"));
				RDBGS_PRINT(("\n[lowmem] : Perform testing in low memory situations "));
				RDBGS_PRINT(("\n[interleave]: Perform test with thread interleaving\n\n"));
				test.Getch();
				TestExit = ETrue;
				break;
				}
			else if (token == _L("debug"))
				{
				if (!TestSilent)
					{
					TestDebug = ETrue;
					gTestPrioChange = ETrue;
					}
				}
			else if (token == _L("silent"))
				{
				TestSilent = ETrue;
				TestDebug = EFalse;
				}
			else if (token == _L("single"))
				{
				gTestType = ETestSingle;
				}
			else if (token == _L("multiple"))
				{
				TPtrC val=lex.NextToken();
				TLex lexv(val);
				TInt value;

				if (lexv.Val(value) == KErrNone)
					{
					if ((value <= 0) || (value > (TInt)KMaxTestThreads))
						{
						gNumTestThreads = KMaxTestThreads;
						}
					else
						{
						gNumTestThreads = value;
						}
					}
				else
					{
					RDBGS_PRINT(("Bad value for thread count '%S' was ignored.\n", &val));
					}
				gTestType = ETestMultiple;
				}
			else if (token == _L("prio"))
				{
				gTestPrioChange = !gTestPrioChange;
				}
			else if (token == _L("lowmem"))
				{
				gTestType = ETestLowMem;
				}
			else if (token == _L("media"))
				{
				gTestType = ETestMedia;
				}
			else if (token == _L("stack"))
				{
				gSetTests = TEST_STACK;
				}
			else if (token == _L("chunk"))
				{
				gSetTests = TEST_CHUNK;
				}
			else if (token == _L("commit"))
				{
				gTestType = ETestCommit;
				gSetTests = TEST_COMMIT;
				}
			else if (token == _L("ipc"))
				{
				gSetTests = TEST_IPC;
				}
			else if (token == _L("badserver"))
				{
				gTestType = ETestBadServer;
				}
			else if (token == _L("all"))
				{
				gSetTests = TEST_ALL;
				}
			else  if (token == _L("iters"))
				{
				TPtrC val=lex.NextToken();
				TLex lexv(val);
				TInt value;

				if (lexv.Val(value) == KErrNone)
					{
					gPerformTestLoop = value;
					}
				else
					{
					RDBGS_PRINT(("Bad value for loop count '%S' was ignored.\n", &val));
					retVal = EFalse;
					break;
					}
				}
			else  if (token == _L("interleave"))
				{
				gTestType = ETestInterleave;
				}
			else
				{
				if ((foundArgs == EFalse) && (token.Length() == 1))
					{
					// Single letter argument...only run on 'd'
					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;
						}
					}
				RDBGS_PRINT(("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);
		gTestNameBuffer.Zero();
		gTestNameBuffer.Append(myParse.Name());
		gTestNameBuffer.Append(_L(".exe"));

		TestWeAreTheTestBase = !gTestNameBuffer.Compare(_L("t_wdpstress.exe"));

		}
	else
		{
		gTestNameBuffer.Zero();
		gTestNameBuffer.Append(_L("t_wdpstress.exe"));
		}
	}

//
// PerformAutoTest
//
// Perform the autotest
//
TInt PerformAutoTest()
	{
	TInt r = KErrNone;

	// Run all the different types of test
	for (TUint testType = 0; testType < ETestTypes; testType++)
		{
		r = DoTest(testType);
		if (r != KErrNone)
			return r;
		}

	return r;
	}

//
// DoLowMemTest
//
// Low Memory Test
//

TInt DoLowMemTest()
	{
	TInt r = User::LoadLogicalDevice(KPageStressTestLddName);
	RUNTEST1(r==KErrNone || r==KErrAlreadyExists);
	RUNTEST(Ldd.Open(),KErrNone);
	
	SVMCacheInfo  tempPages;
	memset(&tempPages, 0, sizeof(tempPages));

	if (gIsDemandPaged)
		{
		// get the old cache info
		UserSvr::HalFunction(EHalGroupVM,EVMHalGetCacheSize,&tempPages,0);
		TInt	minSize = 8 << gPageShift;
		TInt	maxSize = 256 << gPageShift;
		UserSvr::HalFunction(EHalGroupVM,EVMHalSetCacheSize,(TAny*)minSize,(TAny*)maxSize);
		}


	// First load some pages onto the page cache 
	gPerformTestLoop = 1;
	r = DoTest(ETestSingle);
	test_KErrNone(r);


	Ldd.DoConsumeRamSetup(TEST_LM_NUM_FREE, TEST_LM_BLOCKSIZE);
	TEST_NEXT((_L("Single thread with Low memory.")));
	gNumTestThreads	= KMaxTestThreads / 2;
	gPerformTestLoop = 20;

	r = DoTest(ETestSingle, ETrue);
	Ldd.DoConsumeRamFinish();
	test_KErrNone(r);

	TEST_NEXT((_L("Multiple thread with Low memory.")));
	// First load some pages onto the page cache 
	gPerformTestLoop = 1;
	r = DoTest(ETestSingle);
	test_KErrNone(r);

	Ldd.DoConsumeRamSetup(TEST_LM_NUM_FREE, TEST_LM_BLOCKSIZE);
	
	gPerformTestLoop = 10;
	gNumTestThreads	= KMaxTestThreads / 2;
	r = DoTest(ETestMultiple, ETrue);
	Ldd.DoConsumeRamFinish();
	test_KErrNone(r);

	TEST_NEXT((_L("Multiple thread with Low memory, with starting free ram.")));
	// First load some pages onto the page cache 
	gPerformTestLoop = 1;
	r = DoTest(ETestSingle);
	test_KErrNone(r);

	Ldd.DoConsumeRamSetup(32, TEST_LM_BLOCKSIZE);
	
	gPerformTestLoop = 10;
	gNumTestThreads	= KMaxTestThreads / 2;
	r = DoTest(ETestMultiple, ETrue);
	Ldd.DoConsumeRamFinish();
	test_KErrNone(r);

	TEST_NEXT((_L("Close test driver")));
	Ldd.Close();
	RUNTEST(User::FreeLogicalDevice(KPageStressTestLddName), KErrNone);
	if (gIsDemandPaged)
		{
		TInt minSize = tempPages.iMinSize;
		TInt maxSize = tempPages.iMaxSize;
		UserSvr::HalFunction(EHalGroupVM,EVMHalSetCacheSize,(TAny*)minSize,(TAny*)maxSize);
		}

	return r;
	}

void RestoreDefaults()
	{
	gPerformTestLoop			= 10;					
	gNumTestThreads				= KMaxTestThreads;	

	gTestInterleave				= EFalse;
	
	gTestWhichTests				= gSetTests;
	gTestPrioChange				= EFalse;
	gTestStopMedia				= EFalse;
	gTestMediaAccess			= EFalse;
	}



TInt DoTest(TInt gTestType, TBool aLowMem)
	{
	TInt r = KErrNone;
	
	switch(gTestType)
		{
		case ETestSingle:
			TEST_NEXT((_L("Single thread")));
			r = DoSingleTest(aLowMem);
			break;

		case ETestMultiple:
			TEST_NEXT((_L("Multiple thread")));
			
			r = DoMultipleTest(aLowMem);
			break;

		case ETestLowMem:
			TEST_NEXT((_L("Low Memory Tests")));
			r = DoLowMemTest();
			break;

		case ETestMedia:
			TEST_NEXT((_L("Background Media Activity Tests")));
			gTestMediaAccess = ETrue;
			gPerformTestLoop = 2;					
			gNumTestThreads	= KMaxTestThreads / 2;	
			r = DoMultipleTest(aLowMem);
			break;

		case ETestCommit:
			TEST_NEXT((_L("Committing and Decommitting Tests")));	
			gTestWhichTests = TEST_COMMIT;
			r = DoSingleTest(aLowMem);
			break;

		case ETestInterleave:
			TEST_NEXT((_L("Testing multiple with thread interleaving")));
			gTestInterleave = ETrue;
			r = DoMultipleTest(aLowMem);
			break;

		case ETestBadServer:
			TEST_NEXT((_L("Testing multiple with thread interleaving")));
			gTestBadServer = ETrue;
			gTestWhichTests = TEST_IPC;
			r = DoSingleTest(aLowMem);
			break;


		}
	RestoreDefaults();
	return r;
	}
//
// E32Main
//
// Main entry point.
//

TInt E32Main()
	{
#ifndef TEST_ON_UNPAGED
	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
	if(!romHeader->iPageableRomStart)
		{
		gIsDemandPaged = EFalse;
		}
#endif
	TUint start = User::TickCount();
	
	gResultsArray = (SThreadExitResults *)User::AllocZ(sizeof(SThreadExitResults) * KMaxTestThreads);
	if (gResultsArray == NULL)
		return KErrNoMemory;

	AreWeTheTestBase();
	RestoreDefaults();

	TBool parseResult = ParseCommandLine();

	if (TestExit)
		{
		return KErrNone;
		}

	// Retrieve the page size and use it to detemine the page shift (assumes 32-bit system).
	TInt r = HAL::Get(HAL::EMemoryPageSize, gPageSize);
	if (r != KErrNone)
		{
		RDBGS_PRINT(("Cannot obtain the page size\n"));
		return r;
		}
	else
		{
		RDBGS_PRINT(("page size = %d\n", gPageSize));
		}


	TUint32 pageMask = gPageSize;
	TUint i = 0;
	for (; i < 32; i++)
		{
		if (pageMask & 1)
			{
			if (pageMask & ~1u)
				{
				test.Printf(_L("ERROR - page size not a power of 2"));
				return KErrNotSupported;
				}
			gPageShift = i;
			break;
			}
		pageMask >>= 1;
		}

	TInt  minSize = 8 << gPageShift;
	TInt  maxSize = 64 << gPageShift; 
	SVMCacheInfo  tempPages;
	memset(&tempPages, 0, sizeof(tempPages));
	if (gIsDemandPaged)
		{
		// 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);
		}


	if (!TestSilent)
		{
		test.Title();
		test.Start(_L("Writable Data Paging stress tests..."));
		test.Printf(_L("%S\n"), &gTestNameBuffer);
		}

	if (parseResult)
		{
		if (!TestSilent)
			{
			extern TInt *CheckLdmiaInstr(void);
			test.Printf(_L("%S : CheckLdmiaInstr\n"), &gTestNameBuffer);
			TInt   *theAddr = CheckLdmiaInstr();
			test.Printf(_L("%S : CheckLdmiaInstr complete 0x%x...\n"), &gTestNameBuffer, (TInt)theAddr);
			}
		
		if (gTestType < 0 || gTestType >= ETestTypeEnd)
			{
			r = PerformAutoTest();
			test_KErrNone(r);
			}
		else
			{
			r = DoTest(gTestType);
			test_KErrNone(r);
			}
		}
	else
		{
		r = PerformAutoTest();
		test_KErrNone(r);
		}

	if (gIsDemandPaged)
		{
		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"), &gTestNameBuffer, User::TickCount() - start);	
		test.End();
		}

	User::Free(gResultsArray);
	gResultsArray = NULL;
	
	return 0;
	}