sl@0: // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32test\mmu\t_mmustress.cpp sl@0: // Stress test for memory management services performed by the kernel's memory model. sl@0: // sl@0: // sl@0: sl@0: /** sl@0: @file sl@0: */ sl@0: sl@0: #define __E32TEST_EXTENSION__ sl@0: #include sl@0: #include "u32std.h" sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include "d_memorytest.h" sl@0: #include "..\defrag\d_pagemove.h" sl@0: sl@0: TBool TRACE = 0; sl@0: sl@0: LOCAL_D RTest test(_L("T_MMUSTRESS")); sl@0: sl@0: TUint32 MemModelAttributes; sl@0: TUint32 MemModel; sl@0: TInt PageSize; sl@0: TInt PageMask; sl@0: sl@0: #if !defined(__WINS__) && !defined(__X86__) sl@0: const TPtrC KMoveLddFileName=_L("D_PAGEMOVE.LDD"); sl@0: RPageMove MoveLdd; sl@0: #endif sl@0: sl@0: RMemoryTestLdd Ldd; sl@0: sl@0: const TUint KNumTestChunks = 6; sl@0: RChunk Chunks[KNumTestChunks]; sl@0: TInt Committed[KNumTestChunks] = {0}; // for each chunk, is the 'owned' region uncommited(0), commited(1) or mixed(-1) sl@0: class TNicePtr8 : public TPtr8 { public: TNicePtr8() : TPtr8(0,0) {} } ChunkPtr[KNumTestChunks]; sl@0: sl@0: const TUint KNumSlaveProcesses = 4; sl@0: RProcess Slaves[KNumSlaveProcesses]; sl@0: TRequestStatus SlaveLogons[KNumSlaveProcesses]; sl@0: TRequestStatus SlaveRendezvous[KNumSlaveProcesses]; sl@0: sl@0: TInt SlaveNumber = -1; // master process is slave -1 sl@0: sl@0: const TInt KLocalIpcBufferSize = 0x10000; sl@0: TUint8* LocalIpcBuffer = 0; sl@0: sl@0: RSemaphore StartSemaphore; sl@0: sl@0: // sl@0: // Random number generation sl@0: // sl@0: sl@0: TUint32 RandomSeed; sl@0: sl@0: TUint32 Random() sl@0: { sl@0: RandomSeed = RandomSeed*69069+1; sl@0: return RandomSeed; sl@0: } sl@0: sl@0: TUint32 Random(TUint32 aRange) sl@0: { sl@0: return (TUint32)((TUint64(Random())*TUint64(aRange))>>32); sl@0: } sl@0: sl@0: void RandomInit(TUint32 aSeed) sl@0: { sl@0: RandomSeed = aSeed+(aSeed<<8)+(aSeed<<16)+(aSeed<<24); sl@0: Random(); sl@0: Random(); sl@0: } sl@0: sl@0: sl@0: sl@0: // sl@0: // Chunk utils sl@0: // sl@0: sl@0: TBuf ChunkName(TInt aChunkNumber) sl@0: { sl@0: TBuf name; sl@0: name.Format(_L("T_MMUSTRESS-Chunk%d"),aChunkNumber); sl@0: return name; sl@0: } sl@0: sl@0: #ifdef __WINS__ sl@0: TInt KChunkShift = 16; sl@0: #elif defined(__X86__) sl@0: TInt KChunkShift = 22; sl@0: #else sl@0: TInt KChunkShift = 20; sl@0: #endif sl@0: sl@0: TInt ChunkSize(TInt aChunkNumber) sl@0: { sl@0: // biggest chunk (number 0) is big enough for each slave to own a region which is sl@0: // 2 page tables ('chunks') in size... sl@0: return (2*KNumSlaveProcesses)<<(KChunkShift-aChunkNumber); sl@0: } sl@0: sl@0: // check smallest chunk is less than 'chunk' size... sl@0: __ASSERT_COMPILE((2*KNumSlaveProcesses>>(KNumTestChunks-1))==0); sl@0: sl@0: sl@0: /* Memory region 'owned' by this slave process */ sl@0: void ChunkOwnedRegion(TInt aChunkNumber,TInt& aOffset,TInt& aSize) sl@0: { sl@0: TInt size = ChunkSize(aChunkNumber)/KNumSlaveProcesses; sl@0: aSize = size; sl@0: aOffset = SlaveNumber*size; sl@0: test_Equal(0,size&PageMask); sl@0: } sl@0: sl@0: void ChunkMarkRegion(TInt aChunkNumber,TInt aOffset,TInt aSize) sl@0: { sl@0: TInt pageSize = PageSize; sl@0: TUint32 mark = aOffset|aChunkNumber|(SlaveNumber<<4); sl@0: TUint8* ptr = Chunks[aChunkNumber].Base()+aOffset; sl@0: TUint8* ptrEnd = ptr+aSize; sl@0: while(ptr ServerName(TInt aSlaveNumber) sl@0: { sl@0: TBuf name; sl@0: name.Format(_L("T_MMUSTRESS-Server%d"),aSlaveNumber); sl@0: return name; sl@0: } sl@0: sl@0: RServer2 Server; sl@0: RMessage2 ServerMessage; sl@0: TRequestStatus ServerStatus; sl@0: sl@0: class RTestSession : public RSessionBase sl@0: { sl@0: public: sl@0: TInt Connect(TInt aServerNumber) sl@0: { sl@0: return CreateSession(ServerName(aServerNumber),TVersion(),1,EIpcSession_Unsharable,0,&iStatus); sl@0: } sl@0: TInt Send(TInt aChunkNumber) sl@0: { sl@0: return RSessionBase::Send(0,TIpcArgs(SlaveNumber,aChunkNumber,&ChunkPtr[aChunkNumber])); sl@0: } sl@0: TRequestStatus iStatus; sl@0: }; sl@0: RTestSession Sessions[KNumSlaveProcesses]; sl@0: sl@0: sl@0: // sl@0: // sl@0: // sl@0: sl@0: void SlaveInit() sl@0: { sl@0: RDebug::Printf("Slave %d initialising",SlaveNumber); sl@0: sl@0: TBuf name; sl@0: name.Format(_L("T_MMUSTRESS-Slave%d"),SlaveNumber); sl@0: User::RenameThread(name); sl@0: sl@0: test_KErrNone(StartSemaphore.Open(2)); sl@0: TInt r; sl@0: #if !defined(__WINS__) && !defined(__X86__) sl@0: // Move ldd may not be in the ROM so needs to be loaded. sl@0: r=User::LoadLogicalDevice(KMoveLddFileName); sl@0: test_Value(r, r==KErrNone || r==KErrAlreadyExists); sl@0: test_KErrNone(MoveLdd.Open()); sl@0: #endif sl@0: sl@0: test_KErrNone(Ldd.Open()); sl@0: test_KErrNone(Ldd.CreateVirtualPinObject()); sl@0: sl@0: LocalIpcBuffer = (TUint8*)User::Alloc(KLocalIpcBufferSize); sl@0: test(LocalIpcBuffer!=0); sl@0: sl@0: test_KErrNone(Server.CreateGlobal(ServerName(SlaveNumber))); sl@0: sl@0: TUint i; sl@0: sl@0: // create sessions with other slaves... sl@0: for(i=0; i%d",SlaveNumber,chunkNumber,randomSlave); sl@0: // ChunkPtr[chunkNumber].Set(chunk.Base(),ChunkSize(chunkNumber),ChunkSize(chunkNumber)); sl@0: ChunkPtr[chunkNumber].Set(chunk.Base()+offset,size,size); sl@0: Sessions[randomSlave].Send(chunkNumber); sl@0: break; sl@0: sl@0: case 9: sl@0: // process IPC messages... sl@0: if(ServerStatus.Int()==KRequestPending) sl@0: continue; sl@0: User::WaitForRequest(ServerStatus); sl@0: sl@0: { sl@0: TInt sourceSlave = ServerMessage.Int0(); sl@0: chunkNumber = ServerMessage.Int1(); sl@0: if(TRACE) RDebug::Printf("%d %d IPC Receive<-%d",SlaveNumber,chunkNumber,sourceSlave); sl@0: test_Equal(0,ServerMessage.Function()); sl@0: sl@0: // get local descriptor for owned region in chunk... sl@0: size = ServerMessage.GetDesMaxLength(2); sl@0: test_NotNegative(size); sl@0: if(size>KLocalIpcBufferSize) sl@0: size = KLocalIpcBufferSize; sl@0: TPtr8 local(LocalIpcBuffer,size,size); sl@0: sl@0: // if(Random(2)) sl@0: { sl@0: // IPC read from other slave... sl@0: if(TRACE) RDebug::Printf("%d %d IPC Read<-%d",SlaveNumber,chunkNumber,sourceSlave); sl@0: TInt panicTrace = Ldd.SetPanicTrace(EFalse); sl@0: r = ServerMessage.Read(2,local); sl@0: Ldd.SetPanicTrace(panicTrace); sl@0: if(r!=KErrBadDescriptor) sl@0: test_KErrNone(r); sl@0: } sl@0: // else sl@0: // { sl@0: // // IPC write to other slave... sl@0: // if(TRACE) RDebug::Printf("%d %d IPC Write->%d",SlaveNumber,chunkNumber,sourceSlave); sl@0: // r = ServerMessage.Write(2,local,offset); sl@0: // if(r!=KErrBadDescriptor) sl@0: // test_KErrNone(r); sl@0: // if(Committed[chunkNumber]==1) sl@0: // ChunkMarkRegion(chunkNumber,offset,size); sl@0: // } sl@0: } sl@0: sl@0: ServerMessage.Complete(KErrNone); sl@0: Server.Receive(ServerMessage,ServerStatus); sl@0: break; sl@0: sl@0: case 10: sl@0: case 11: sl@0: // pin memory... sl@0: { sl@0: test_KErrNone(Ldd.UnpinVirtualMemory()); sl@0: for(TInt tries=10; tries>0; --tries) sl@0: { sl@0: TInt chunkSize = ChunkSize(chunkNumber); sl@0: offset = Random(chunkSize); sl@0: TInt maxSize = chunkSize-offset; sl@0: if(maxSize>0x1000) sl@0: maxSize = 0x1000; sl@0: size = Random(maxSize); sl@0: r = Ldd.PinVirtualMemory((TLinAddr)chunk.Base()+offset, size); sl@0: if(r!=KErrNotFound && r!=KErrNoMemory) sl@0: { sl@0: test_KErrNone(r); sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: break; sl@0: case 12: sl@0: case 13: sl@0: // Move any page in the chunk, not just the owned region. sl@0: { sl@0: #if !defined(__WINS__) && !defined(__X86__) sl@0: for(TInt tries=10; tries>0; --tries) sl@0: { sl@0: TInt chunkSize = ChunkSize(chunkNumber); sl@0: offset = Random(chunkSize); sl@0: MoveLdd.TryMovingUserPage((TAny*)(chunk.Base()+offset), ETrue); sl@0: // Allow the move to fail for any reason as the page of the chunk sl@0: // may or may not be currently committed, pinned, or accessed. sl@0: } sl@0: #endif sl@0: } sl@0: break; sl@0: default: sl@0: test(false); // can't happen sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: sl@0: TInt E32Main() sl@0: { sl@0: // get system info... sl@0: MemModelAttributes = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL); sl@0: MemModel = MemModelAttributes&EMemModelTypeMask; sl@0: UserHal::PageSizeInBytes(PageSize); sl@0: PageMask = PageSize-1; sl@0: sl@0: // see if we are a slave process... sl@0: if(User::GetTIntParameter(1,SlaveNumber)==KErrNone) sl@0: { sl@0: // do testing... sl@0: SlaveInit(); sl@0: DoTest(); sl@0: return KErrGeneral; // shouldn't have returned from testing sl@0: } sl@0: sl@0: // master process... sl@0: TBool pass = true; // final test result sl@0: test.Title(); sl@0: if((MemModelAttributes&EMemModelAttrVA)==false) sl@0: { sl@0: test.Start(_L("TESTS NOT RUN - Not relevent for the memory model")); sl@0: test.End(); sl@0: return KErrNone; sl@0: } sl@0: sl@0: // get time to run tests for... sl@0: TInt timeout = 10; // time in seconds sl@0: TInt cmdLineLen = User::CommandLineLength(); sl@0: if(cmdLineLen) sl@0: { sl@0: // get timeout value from command line sl@0: RBuf cmdLine; sl@0: test_KErrNone(cmdLine.Create(cmdLineLen)); sl@0: User::CommandLine(cmdLine); sl@0: test_KErrNone(TLex(cmdLine).Val(timeout)); sl@0: if(timeout==0) sl@0: timeout = KMaxTInt; sl@0: } sl@0: TTimeIntervalMicroSeconds32 tickTime; sl@0: test_KErrNone(UserHal::TickPeriod(tickTime)); sl@0: TInt ticksPerSecond = 1000000/tickTime.Int(); sl@0: TInt timeoutTicks; sl@0: if(timeout>10); sl@0: test_KErrNone(Chunks[i].CreateDisconnectedGlobal(ChunkName(i),0,0,ChunkSize(i))); sl@0: } sl@0: sl@0: test.Next(_L("Spawning slave processes")); sl@0: test_KErrNone(StartSemaphore.CreateGlobal(KNullDesC,0)); sl@0: TFileName processFile(RProcess().FileName()); sl@0: for(i=0; i text; sl@0: text.Format(_L("Stressing for %d seconds..."),timeout); sl@0: test.Next(text); sl@0: timer.AfterTicks(timeoutStatus,timeoutTicks); sl@0: StartSemaphore.Signal(KNumSlaveProcesses); // release slaves to start testing sl@0: User::WaitForAnyRequest(); // wait for timeout or slave death via logon completion sl@0: sl@0: pass = timeoutStatus.Int()==KErrNone; // timeout means slaves are still running OK sl@0: sl@0: test.Next(_L("Check slaves still running")); sl@0: for(i=0; i