kerneltest/f32test/demandpaging/t_wdpstress.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 6 0173bcd7697c
child 19 4a8fed1c0ef6
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 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;
	}