diff -r 000000000000 -r a41df078684a kerneltest/f32test/demandpaging/t_wdpstress.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/f32test/demandpaging/t_wdpstress.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,2377 @@ +// 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 - run the tests in multiple threads where (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 - 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 +RTest test(_L("T_WDPSTRESS")); + +#include +#include +#include +#include +#include +#include +#include +#include +#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 > 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 > *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= 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 ] : 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 ] : 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; + } + +