Update contrib.
1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32test\mmu\t_mmustress.cpp
15 // Stress test for memory management services performed by the kernel's memory model.
23 #define __E32TEST_EXTENSION__
30 #include <e32def_private.h>
31 #include "d_memorytest.h"
32 #include "..\defrag\d_pagemove.h"
36 LOCAL_D RTest test(_L("T_MMUSTRESS"));
38 TUint32 MemModelAttributes;
43 #if !defined(__WINS__) && !defined(__X86__)
44 const TPtrC KMoveLddFileName=_L("D_PAGEMOVE.LDD");
50 const TUint KNumTestChunks = 6;
51 RChunk Chunks[KNumTestChunks];
52 TInt Committed[KNumTestChunks] = {0}; // for each chunk, is the 'owned' region uncommited(0), commited(1) or mixed(-1)
53 class TNicePtr8 : public TPtr8 { public: TNicePtr8() : TPtr8(0,0) {} } ChunkPtr[KNumTestChunks];
55 const TUint KNumSlaveProcesses = 4;
56 RProcess Slaves[KNumSlaveProcesses];
57 TRequestStatus SlaveLogons[KNumSlaveProcesses];
58 TRequestStatus SlaveRendezvous[KNumSlaveProcesses];
60 TInt SlaveNumber = -1; // master process is slave -1
62 const TInt KLocalIpcBufferSize = 0x10000;
63 TUint8* LocalIpcBuffer = 0;
65 RSemaphore StartSemaphore;
68 // Random number generation
75 RandomSeed = RandomSeed*69069+1;
79 TUint32 Random(TUint32 aRange)
81 return (TUint32)((TUint64(Random())*TUint64(aRange))>>32);
84 void RandomInit(TUint32 aSeed)
86 RandomSeed = aSeed+(aSeed<<8)+(aSeed<<16)+(aSeed<<24);
97 TBuf<KMaxKernelName> ChunkName(TInt aChunkNumber)
99 TBuf<KMaxKernelName> name;
100 name.Format(_L("T_MMUSTRESS-Chunk%d"),aChunkNumber);
105 TInt KChunkShift = 16;
106 #elif defined(__X86__)
107 TInt KChunkShift = 22;
109 TInt KChunkShift = 20;
112 TInt ChunkSize(TInt aChunkNumber)
114 // biggest chunk (number 0) is big enough for each slave to own a region which is
115 // 2 page tables ('chunks') in size...
116 return (2*KNumSlaveProcesses)<<(KChunkShift-aChunkNumber);
119 // check smallest chunk is less than 'chunk' size...
120 __ASSERT_COMPILE((2*KNumSlaveProcesses>>(KNumTestChunks-1))==0);
123 /* Memory region 'owned' by this slave process */
124 void ChunkOwnedRegion(TInt aChunkNumber,TInt& aOffset,TInt& aSize)
126 TInt size = ChunkSize(aChunkNumber)/KNumSlaveProcesses;
128 aOffset = SlaveNumber*size;
129 test_Equal(0,size&PageMask);
132 void ChunkMarkRegion(TInt aChunkNumber,TInt aOffset,TInt aSize)
134 TInt pageSize = PageSize;
135 TUint32 mark = aOffset|aChunkNumber|(SlaveNumber<<4);
136 TUint8* ptr = Chunks[aChunkNumber].Base()+aOffset;
137 TUint8* ptrEnd = ptr+aSize;
140 ((TUint32*)ptr)[0] = mark;
141 ((TUint32*)ptr)[1] = ~mark;
147 void ChunkCheckRegion(TInt aChunkNumber,TInt aOffset,TInt aSize)
149 TInt pageSize = PageSize;
150 TUint32 mark = aOffset|aChunkNumber|(SlaveNumber<<4);
151 TUint8* ptr = Chunks[aChunkNumber].Base()+aOffset;
152 TUint8* ptrEnd = ptr+aSize;
155 test_Equal(mark,((TUint32*)ptr)[0]);
156 test_Equal(~mark,((TUint32*)ptr)[1]);
162 TInt ChunkOpen(TInt aChunkNumber)
164 RChunk& chunk = Chunks[aChunkNumber];
165 if(chunk.Handle()!=0)
168 if(TRACE) RDebug::Printf("%d %d Open",SlaveNumber,aChunkNumber);
169 TInt r = chunk.OpenGlobal(ChunkName(aChunkNumber),false);
179 TBuf<KMaxKernelName> ServerName(TInt aSlaveNumber)
181 TBuf<KMaxKernelName> name;
182 name.Format(_L("T_MMUSTRESS-Server%d"),aSlaveNumber);
187 RMessage2 ServerMessage;
188 TRequestStatus ServerStatus;
190 class RTestSession : public RSessionBase
193 TInt Connect(TInt aServerNumber)
195 return CreateSession(ServerName(aServerNumber),TVersion(),1,EIpcSession_Unsharable,0,&iStatus);
197 TInt Send(TInt aChunkNumber)
199 return RSessionBase::Send(0,TIpcArgs(SlaveNumber,aChunkNumber,&ChunkPtr[aChunkNumber]));
201 TRequestStatus iStatus;
203 RTestSession Sessions[KNumSlaveProcesses];
212 RDebug::Printf("Slave %d initialising",SlaveNumber);
214 TBuf<KMaxKernelName> name;
215 name.Format(_L("T_MMUSTRESS-Slave%d"),SlaveNumber);
216 User::RenameThread(name);
218 test_KErrNone(StartSemaphore.Open(2));
220 #if !defined(__WINS__) && !defined(__X86__)
221 // Move ldd may not be in the ROM so needs to be loaded.
222 r=User::LoadLogicalDevice(KMoveLddFileName);
223 test_Value(r, r==KErrNone || r==KErrAlreadyExists);
224 test_KErrNone(MoveLdd.Open());
227 test_KErrNone(Ldd.Open());
228 test_KErrNone(Ldd.CreateVirtualPinObject());
230 LocalIpcBuffer = (TUint8*)User::Alloc(KLocalIpcBufferSize);
231 test(LocalIpcBuffer!=0);
233 test_KErrNone(Server.CreateGlobal(ServerName(SlaveNumber)));
237 // create sessions with other slaves...
238 for(i=0; i<KNumSlaveProcesses; i++)
242 r = Sessions[i].Connect(i);
243 // RDebug::Printf("%d Session %d = %d,%d",SlaveNumber,i,r,Sessions[i].iStatus.Int());
246 // give other slaves time to create their servers...
255 // process session connect messages...
256 for(i=0; i<KNumSlaveProcesses; i++)
259 // RDebug::Printf("%d Server waiting for connect message",SlaveNumber);
261 test_Equal(RMessage2::EConnect,m.Function())
262 m.Complete(KErrNone);
265 // wait for our session connections...
266 for(i=0; i<KNumSlaveProcesses; i++)
268 // RDebug::Printf("%d Session wait %d",SlaveNumber,i);
269 User::WaitForRequest(Sessions[i].iStatus);
272 // prime server for receiving mesages...
273 Server.Receive(ServerMessage,ServerStatus);
275 // synchronise with other processes...
276 RDebug::Printf("Slave %d waiting for trigger",SlaveNumber);
277 RProcess::Rendezvous(KErrNone);
278 StartSemaphore.Wait();
279 RDebug::Printf("Slave %d started",SlaveNumber);
285 // Test by random operations...
290 RandomInit(SlaveNumber);
294 // select random chunk...
295 TInt chunkNumber = Random(KNumTestChunks);
296 RChunk& chunk = Chunks[chunkNumber];
298 // get the region of this chunk which this process 'owns'...
301 ChunkOwnedRegion(chunkNumber,offset,size);
303 // calculate a random region in the owned part...
304 TInt randomOffset = offset+(Random(size)&~PageMask);
305 TInt randomSize = (Random(size-(randomOffset-offset))+PageMask)&~PageMask;
307 continue; // try again
309 // pick a random slave...
310 TInt randomSlave = Random(KNumSlaveProcesses);
312 // open chunk if it isn't already...
313 r = ChunkOpen(chunkNumber);
315 continue; // can't do anything with chunk if we can't open it
317 // check our contents of chunk...
318 if(Committed[chunkNumber]==1)
320 if(TRACE) RDebug::Printf("%d %d Check %08x+%08x",SlaveNumber,chunkNumber,offset,size);
321 ChunkCheckRegion(chunkNumber,offset,size);
324 // perform random operation...
330 if(TRACE) RDebug::Printf("%d %d Close",SlaveNumber,chunkNumber);
336 if(TRACE) RDebug::Printf("%d %d Commit all %08x+%08x",SlaveNumber,chunkNumber,offset,size);
337 if(Committed[chunkNumber]!=0)
339 r = chunk.Decommit(offset,size);
341 Committed[chunkNumber] = 0;
343 r = chunk.Commit(offset,size);
347 Committed[chunkNumber] = 1;
348 ChunkMarkRegion(chunkNumber,offset,size);
354 if(TRACE) RDebug::Printf("%d %d Decommit all %08x+%08x",SlaveNumber,chunkNumber,offset,size);
355 r = chunk.Decommit(offset,size);
357 Committed[chunkNumber] = 0;
363 if(TRACE) RDebug::Printf("%d %d Commit %08x+%08x",SlaveNumber,chunkNumber,randomOffset,randomSize);
364 r = chunk.Commit(randomOffset,randomSize);
367 if(Committed[chunkNumber]==0)
370 Committed[chunkNumber] = -1;
372 else if(Committed[chunkNumber]==1)
374 test_Equal(KErrAlreadyExists,r);
378 if(r!=KErrAlreadyExists)
386 // decommit random...
387 if(TRACE) RDebug::Printf("%d %d Decommit %08x+%08x",SlaveNumber,chunkNumber,randomOffset,randomSize);
388 r = chunk.Decommit(randomOffset,randomSize);
390 if(Committed[chunkNumber]==1)
391 Committed[chunkNumber] = -1;
395 if(TRACE) RDebug::Printf("%d %d IPC Send->%d",SlaveNumber,chunkNumber,randomSlave);
396 // ChunkPtr[chunkNumber].Set(chunk.Base(),ChunkSize(chunkNumber),ChunkSize(chunkNumber));
397 ChunkPtr[chunkNumber].Set(chunk.Base()+offset,size,size);
398 Sessions[randomSlave].Send(chunkNumber);
402 // process IPC messages...
403 if(ServerStatus.Int()==KRequestPending)
405 User::WaitForRequest(ServerStatus);
408 TInt sourceSlave = ServerMessage.Int0();
409 chunkNumber = ServerMessage.Int1();
410 if(TRACE) RDebug::Printf("%d %d IPC Receive<-%d",SlaveNumber,chunkNumber,sourceSlave);
411 test_Equal(0,ServerMessage.Function());
413 // get local descriptor for owned region in chunk...
414 size = ServerMessage.GetDesMaxLength(2);
415 test_NotNegative(size);
416 if(size>KLocalIpcBufferSize)
417 size = KLocalIpcBufferSize;
418 TPtr8 local(LocalIpcBuffer,size,size);
422 // IPC read from other slave...
423 if(TRACE) RDebug::Printf("%d %d IPC Read<-%d",SlaveNumber,chunkNumber,sourceSlave);
424 TInt panicTrace = Ldd.SetPanicTrace(EFalse);
425 r = ServerMessage.Read(2,local);
426 Ldd.SetPanicTrace(panicTrace);
427 if(r!=KErrBadDescriptor)
432 // // IPC write to other slave...
433 // if(TRACE) RDebug::Printf("%d %d IPC Write->%d",SlaveNumber,chunkNumber,sourceSlave);
434 // r = ServerMessage.Write(2,local,offset);
435 // if(r!=KErrBadDescriptor)
437 // if(Committed[chunkNumber]==1)
438 // ChunkMarkRegion(chunkNumber,offset,size);
442 ServerMessage.Complete(KErrNone);
443 Server.Receive(ServerMessage,ServerStatus);
450 test_KErrNone(Ldd.UnpinVirtualMemory());
451 for(TInt tries=10; tries>0; --tries)
453 TInt chunkSize = ChunkSize(chunkNumber);
454 offset = Random(chunkSize);
455 TInt maxSize = chunkSize-offset;
458 size = Random(maxSize);
459 r = Ldd.PinVirtualMemory((TLinAddr)chunk.Base()+offset, size);
460 if(r!=KErrNotFound && r!=KErrNoMemory)
470 // Move any page in the chunk, not just the owned region.
472 #if !defined(__WINS__) && !defined(__X86__)
473 for(TInt tries=10; tries>0; --tries)
475 TInt chunkSize = ChunkSize(chunkNumber);
476 offset = Random(chunkSize);
477 MoveLdd.TryMovingUserPage((TAny*)(chunk.Base()+offset), ETrue);
478 // Allow the move to fail for any reason as the page of the chunk
479 // may or may not be currently committed, pinned, or accessed.
485 test(false); // can't happen
495 // get system info...
496 MemModelAttributes = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL);
497 MemModel = MemModelAttributes&EMemModelTypeMask;
498 UserHal::PageSizeInBytes(PageSize);
499 PageMask = PageSize-1;
501 // see if we are a slave process...
502 if(User::GetTIntParameter(1,SlaveNumber)==KErrNone)
507 return KErrGeneral; // shouldn't have returned from testing
511 TBool pass = true; // final test result
513 if((MemModelAttributes&EMemModelAttrVA)==false)
515 test.Start(_L("TESTS NOT RUN - Not relevent for the memory model"));
520 // get time to run tests for...
521 TInt timeout = 10; // time in seconds
522 TInt cmdLineLen = User::CommandLineLength();
525 // get timeout value from command line
527 test_KErrNone(cmdLine.Create(cmdLineLen));
528 User::CommandLine(cmdLine);
529 test_KErrNone(TLex(cmdLine).Val(timeout));
533 TTimeIntervalMicroSeconds32 tickTime;
534 test_KErrNone(UserHal::TickPeriod(tickTime));
535 TInt ticksPerSecond = 1000000/tickTime.Int();
537 if(timeout<KMaxTInt/ticksPerSecond)
538 timeoutTicks = timeout*ticksPerSecond;
541 timeoutTicks = KMaxTInt;
542 timeout = timeoutTicks/ticksPerSecond;
545 // master process runs at higher priority than slaves so it can timeout and kill them...
546 RThread().SetPriority(EPriorityMore);
548 test.Start(_L("Creating test chunks"));
550 for(i=0; i<KNumTestChunks; i++)
552 test.Printf(_L("Size %dkB\r\n"),ChunkSize(i)>>10);
553 test_KErrNone(Chunks[i].CreateDisconnectedGlobal(ChunkName(i),0,0,ChunkSize(i)));
556 test.Next(_L("Spawning slave processes"));
557 test_KErrNone(StartSemaphore.CreateGlobal(KNullDesC,0));
558 TFileName processFile(RProcess().FileName());
559 for(i=0; i<KNumSlaveProcesses; i++)
561 test.Printf(_L("Slave %d\r\n"),i);
562 RProcess& slave = Slaves[i];
563 test_KErrNone(slave.Create(processFile,KNullDesC));
564 test_KErrNone(slave.SetParameter(1,i));
565 test_KErrNone(slave.SetParameter(2,StartSemaphore));
566 slave.Logon(SlaveLogons[i]);
567 test_Equal(KRequestPending,SlaveLogons[i].Int());
568 slave.Rendezvous(SlaveRendezvous[i]);
569 test_Equal(KRequestPending,SlaveRendezvous[i].Int());
572 test.Next(_L("Create timer"));
574 test_KErrNone(timer.CreateLocal());
576 test.Next(_L("Resuming slave processes"));
577 for(i=0; i<KNumSlaveProcesses; i++)
580 // this test must now take care not to die (e.g. panic due to assert fail)
581 // until it has killed the slave processes
583 test.Next(_L("Change paging cache size"));
584 TUint cacheOriginalMin = 0;
585 TUint cacheOriginalMax = 0;
586 TUint cacheCurrentSize = 0;
587 DPTest::CacheSize(cacheOriginalMin, cacheOriginalMax, cacheCurrentSize);
588 DPTest::SetCacheSize(1, 2*ChunkSize(0)); // big enough for all the test chunks
590 test.Next(_L("Wait for slaves to initialise"));
591 TRequestStatus timeoutStatus;
592 timer.After(timeoutStatus,10*1000000); // allow short time for slaves to initialise
593 for(i=0; i<KNumSlaveProcesses; i++)
595 User::WaitForAnyRequest(); // wait for a rendexvous
596 if(timeoutStatus.Int()!=KRequestPending)
598 test.Printf(_L("Timeout waiting for slaves to initialise\r\n"));
604 test.Next(_L("Restore paging cache size"));
605 DPTest::SetCacheSize(cacheOriginalMin, cacheOriginalMax);
610 User::WaitForAnyRequest(); // swallow timer signal
612 test.Next(_L("Check slaves are ready"));
613 for(i=0; i<KNumSlaveProcesses; i++)
615 if(SlaveRendezvous[i].Int()!=KErrNone || Slaves[i].ExitType()!=EExitPending)
617 test.Printf(_L("Slaves not ready or died!\r\n"));
626 test.Next(_L("Setup simulated kernel heap failure"));
627 __KHEAP_SETFAIL(RAllocator::EDeterministic,100);
630 text.Format(_L("Stressing for %d seconds..."),timeout);
632 timer.AfterTicks(timeoutStatus,timeoutTicks);
633 StartSemaphore.Signal(KNumSlaveProcesses); // release slaves to start testing
634 User::WaitForAnyRequest(); // wait for timeout or slave death via logon completion
636 pass = timeoutStatus.Int()==KErrNone; // timeout means slaves are still running OK
638 test.Next(_L("Check slaves still running"));
639 for(i=0; i<KNumSlaveProcesses; i++)
640 if(Slaves[i].ExitType()!=EExitPending)
643 test.Next(_L("Clear kernel heap failure"));
644 TUint kheapFails = __KHEAP_CHECKFAILURE;
646 test.Printf(_L("Number of simulated memory failures = %d\r\n"),kheapFails);
649 test.Next(_L("Killing slave processes"));
650 for(i=0; i<KNumSlaveProcesses; i++)
653 test.Next(_L("Assert test passed"));
658 for(i=0; i<KNumSlaveProcesses; i++)
660 for(i=0; i<KNumTestChunks; i++)
663 for(i=0; i<KNumSlaveProcesses; i++)
664 User::WaitForRequest(SlaveLogons[i]);
666 UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, (TAny*)5000, 0);