diff -r 000000000000 -r a41df078684a kerneltest/e32test/mmu/t_chunk.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/mmu/t_chunk.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1234 @@ +// Copyright (c) 1995-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: +// e32test\mmu\t_chunk.cpp +// Overview: +// Test RChunk class +// API Information: +// RChunk, RChangeNotifier, TFindChunk +// Details: +// - Test adjusting chunks: create a global chunk, adjust it, do some size +// checks, verify Create() rounds up to chunk multiple, verify that adjust +// request for size greater than MaxSize returns an error. +// - Test creating chunks with small sizes (0 and 1), verify results are as expected. +// - Create multiple local and global chunks, verify that Size,MaxSize,Name & FullName methods +// are as expected. +// Check Create method of global chunk if the name is already in use. Check the name can be +// reused after the chunk is closed. +// - Perform adjust tests on a chunk, verify results are as expected. +// - Open and test global chunks with multiple references, verify results are as expected. +// - Open and test local chunks, check TFindChunk::Next method, verify results are as expected. +// - Check user thread is panicked if it creates or adjusts chunk with negative size or of an +// invalid type or with an invalid name. +// - Check the chunk is filled in with the appropriate clear value after creation, verify results +// are as expected. +// - Test sharing a chunk between threads, verify results are as expected. +// - Create a thread to watch for notification of changes in free memory and +// changes in out of memory status. Verify adjusting an RChunk generates +// the expected notifications. +// - Test finding a global chunk by name and verify results are as expected. +// Platforms/Drives/Compatibility: +// All. +// Assumptions/Requirement/Pre-requisites: +// Failures and causes: +// Base Port information: +// +// + +#define __E32TEST_EXTENSION__ + +#include +#include +#include +#include "mmudetect.h" +#include "d_gobble.h" +#include "freeram.h" + +const TInt KHeapSize=0x200; + +//const TInt KNumberOfChunks=10; can't have that many +const TInt KNumberOfChunks=3; + +const TInt KChunkNum=5; +const TInt KNormalReturn=194; + +#ifdef __WINS__ +const TInt KMinChunkSizeInBytesMinus1=0x0000ffff; +const TUint KMinChunkSizeInBytesMask=0xffff0000; +#elif defined (__X86__) +const TInt KMinChunkSizeInBytesMinus1=0x003fffff; +const TUint KMinChunkSizeInBytesMask=0xffc00000; +#else +const TInt KMinChunkSizeInBytesMinus1=0x000fffff; +const TUint KMinChunkSizeInBytesMask=0xfff00000; +#endif + +const TInt KMinPageSizeInBytesMinus1=0x00000fff; +const TUint KMinPageSizeInBytesMask=0xfffff000; +TInt gPageSize; + +LOCAL_D RTest test(_L("T_CHUNK")); +LOCAL_D RTest t(_L("ShareThread")); +LOCAL_D RChunk gChunk; + +LOCAL_D TPtr nullPtr(NULL,0); + +TUint32 MemModel; + +enum TDirective + { + ENormal, + ECreateNegative, + EAdjustNegative, + ECreateInvalidType, + ECreateInvalidName, + ECreateNoName, + }; + +const TUint8 KDfltClearByte = 0x3; +TBool CheckChunkCleared(RChunk& aRC, TInt aOffset=0, TUint8 aClearByte = KDfltClearByte) + { + TUint8* base = aRC.Base()+aOffset; + TInt size = aRC.Size(); + test.Printf(_L("Testing chunk for 0x%x - size: %d!\n"), aClearByte, size); + TBool ret=ETrue; + for(TInt i = 0; i name; + TFullName fullname; + for (i=0; i b; + +// Create KNumberOfChunks Global chunks + for (i=0; i b; + + b.Copy(_L("Chunk")); + + r=c1.CreateGlobal(b,aSize,aSize); + test(r==KErrNone); + + test((TBool)ETrue==CheckChunkCleared(c1)); + c1.Close(); + + r=c2.CreateLocal(aSize,aSize,EOwnerProcess); + test(r==KErrNone); + + test((TBool)ETrue==CheckChunkCleared(c2)); + c2.Close(); + + r=c3.CreateLocalCode(aSize,aSize,EOwnerProcess); + test(r==KErrNone); + + test((TBool)ETrue==CheckChunkCleared(c3)); + c3.Close(); + + r=c4.CreateDoubleEndedLocal(0x1000,0x1000+aSize,0x100000); + test(r==KErrNone); + + test((TBool)ETrue==CheckChunkCleared(c4,c4.Bottom())); + c4.Close(); + + r=c5.CreateDoubleEndedGlobal(b,0x1000,0x1000+aSize,0x100000,EOwnerProcess); + test(r==KErrNone); + + test((TBool)ETrue==CheckChunkCleared(c5,c5.Bottom())); + c5.Close(); + + r=c6.CreateDisconnectedLocal(0x1000,0x1000+aSize,0x100000); + test(r==KErrNone); + + test((TBool)ETrue==CheckChunkCleared(c6,0x1000)); + c6.Close(); + + r=c7.CreateDisconnectedGlobal(b,0x1000,0x1000+aSize,0x100000,EOwnerProcess); + test(r==KErrNone); + + test((TBool)ETrue==CheckChunkCleared(c7,0x1000)); + c7.Close(); + + test.Next(_L("Test setting the clear byte of RChunk::Create()")); + + TChunkCreateInfo createInfo; + createInfo.SetNormal(aSize, aSize); + test_KErrNone(c10.Create(createInfo)); + TestClearChunk(c10, aSize, KDfltClearByte); + + createInfo.SetClearByte(0x0); + test_KErrNone(c8.Create(createInfo)); + TestClearChunk(c8, aSize, 0x0); + + createInfo.SetClearByte(0xff); + test_KErrNone(c9.Create(createInfo)); + TestClearChunk(c9, aSize, 0xff); + + test.End(); + __KHEAP_MARKEND; + } + +void testShare() +// +// Test sharing a chunk between threads +// + { + test.Start(_L("Test chunk sharing between threads")); + + test.Next(_L("Create chunk Marmalade")); + TInt r=0; + RChunk chunk; + TInt size=0x1000; + TInt maxSize=0x5000; + r=0; + r=chunk.CreateGlobal(_L("Marmalade"),size,maxSize); + test(r==KErrNone); + test.Next(_L("Write 0-9 to it")); + TUint8* base=chunk.Base(); + for (TInt8 j=0;j<10;j++) + *base++=j; // write 0 - 9 to the chunk + + RThread t; + TRequestStatus stat; + test.Next(_L("Create reader thread")); + r=t.Create(_L("RChunkShareThread"), ThreadEntry2, KDefaultStackSize,KHeapSize,KHeapSize,NULL); + test(r==KErrNone); + t.Logon(stat); + test.Next(_L("Resume reader thread")); + t.Resume(); + User::WaitForRequest(stat); + CLOSE_AND_WAIT(t); + chunk.Close(); + + test.End(); + } + +void FindChunks() + { // taken from some code written by SteveG + test.Start(_L("Finding chunks...\n")); + + TFullName name=_L("*"); + TFindChunk find(name); + TInt i=0; + + + while (find.Next(name)==KErrNone) + { + RChunk chunk; + test.Printf(_L("Chunk name %S\n"),&name); + TInt err=chunk.Open(find); + if (err) + test.Printf(_L("Error %d opening chunk"),err); + else + { + TBuf<16> access; + if (chunk.IsWritable()) + access=_L("ReadWrite"); + else if (chunk.IsReadable()) + access=_L("ReadOnly"); + else + access=_L("No Access"); + test.Printf(_L("Chunk size %08x bytes, %S\n"),chunk.Size(),&access); + chunk.Close(); + i++; + } + User::After(1000000); + } + test.End(); + } + +void testAdjustChunk() + { + test.Start(_L("Test adjusting chunks")); + + RChunk hermione; + + test.Next(_L("Create global chunk")); + TInt r=hermione.CreateGlobal(_L("Hermione"),0x1000,0x100000); + test(r==KErrNone); + TUint32* base=(TUint32*)hermione.Base(); + TUint32* top=(TUint32*)(hermione.Base()+hermione.Size()); + TUint32* i; + + test.Printf(_L("Base = %08x, Top = %08x\n"),base,top); + test.Next(_L("Check I can write to all of it")); + for (i=base;i MaxSize returns error")); + r=wibble.Adjust(wibble.MaxSize()+gPageSize); + test(r==KErrArgument); + + wibble.Close(); + test.End(); + } + +TInt NotifierCount=0; +TInt OOMCount=0; +RChangeNotifier Notifier; +RThread NtfThrd; +_LIT(KNotifierThreadName,"NotifierThread"); + +TInt NotifierThread(TAny*) + { + TInt r=Notifier.Create(); + while (r==KErrNone) + { + TRequestStatus s; + r=Notifier.Logon(s); + if (r!=KErrNone) + break; + User::WaitForRequest(s); + if (s.Int()&EChangesFreeMemory) + ++NotifierCount; + if (s.Int()&EChangesOutOfMemory) + ++OOMCount; + } + Notifier.Close(); + return r; + } + + +void WaitForNotifier() + { + User::After(500000); // wait for notifier + } + + +void CheckNotifierCount(TInt aLevel, TInt aOom) + { + WaitForNotifier(); + if (NtfThrd.ExitType()!=EExitPending) + { + TExitCategoryName exitCat=NtfThrd.ExitCategory(); + test.Printf(_L("Thread exited: %d,%d,%S"),NtfThrd.ExitType(),NtfThrd.ExitReason(),&exitCat); + test(0); + } + TInt c1=NotifierCount; + TInt c2=OOMCount; + if (c1!=aLevel || c2!=aOom) + { + test.Printf(_L("Count %d,%d Expected %d,%d"),c1,c2,aLevel,aOom); + test(0); + } + } + +void testNotifiers() + { + RGobbler gobbler; + TInt r = gobbler.Open(); + test(r==KErrNone); + TUint32 taken = gobbler.GobbleRAM(128*1024*1024); + test.Printf(_L("Gobbled: %dK\n"), taken/1024); + test.Printf(_L("Free RAM 0x%08X bytes\n"),FreeRam()); + + test.Next(_L("Create thread")); + r=NtfThrd.Create(KNotifierThreadName,NotifierThread,KDefaultStackSize,NULL,NULL); + test(r==KErrNone); + NtfThrd.SetPriority(EPriorityMore); + NtfThrd.Resume(); + test.Next(_L("Check for initial notifier")); + CheckNotifierCount(1,1); + TInt free=FreeRam(); + test.Printf(_L("Free RAM: %dK\n"),free/1024); + test(free>=1048576); + test.Next(_L("Set thresholds")); + r=UserSvr::SetMemoryThresholds(65536,524288); // low=64K good=512K + test(r==KErrNone); + test.Next(_L("Create chunk")); + // Chunk must not be paged otherwise it will not effect the amount + // of free ram reported plus on h4 swap size is less than the total ram. + TChunkCreateInfo createInfo; + createInfo.SetNormal(0, free+2097152); + createInfo.SetPaging(TChunkCreateInfo::EUnpaged); + RChunk c; + test_KErrNone(c.Create(createInfo)); + const TInt KBufferSpace = 768*1024; // 768K buffer + CheckNotifierCount(1,1); + test.Next(_L("Leave 768K")); + r=c.Adjust(free-KBufferSpace); // leave 768K + test(r==KErrNone); + CheckNotifierCount(1,1); // shouldn't get notifier + TInt free2=FreeRam(); + test.Printf(_L("Free RAM: %dK\n"),free2/1024); + test(free2<=KBufferSpace); + TInt free3=free-(KBufferSpace-free2); // this accounts for space used by page tables + test.Next(_L("Leave 32K")); + r=c.Adjust(free3-32768); // leave 32K + test(r==KErrNone); + CheckNotifierCount(2,1); // should get notifier + test.Next(_L("Leave 28K")); + r=c.Adjust(free3-28672); // leave 28K + test(r==KErrNone); + CheckNotifierCount(2,1); // shouldn't get another notifier + test.Next(_L("Ask for too much")); + r=c.Adjust(free3+4096); // try to get more than available + test(r==KErrNoMemory); + CheckNotifierCount(2,2); // should get another notifier + test.Next(_L("Leave 128K")); + r=c.Adjust(free3-131072); // leave 128K + test(r==KErrNone); + CheckNotifierCount(2,2); // shouldn't get another notifier + test.Next(_L("Leave 640K")); + r=c.Adjust(free3-655360); // leave 640K + test(r==KErrNone); + CheckNotifierCount(3,2); // should get another notifier + test.Next(_L("Leave 1M")); + r=c.Adjust(free3-1048576); // leave 1M + test(r==KErrNone); + CheckNotifierCount(3,2); // shouldn't get another notifier + test.Next(_L("Ask for too much")); + r=c.Adjust(free3+4096); // try to get more than available + test(r==KErrNoMemory); + + TInt notifierCount = 3; + if(MemModel==EMemModelTypeFlexible) + { + // on flexible memory model, we get memory changed notifiers + // on failed memory allocation; this hack lets the test code + // pass this as acceptable behaviour... + WaitForNotifier(); + notifierCount = NotifierCount; // expect whatever we actually got + } + + CheckNotifierCount(notifierCount,3); // should get another notifier + test.Next(_L("Leave 1M")); + r=c.Adjust(free3-1048576); // leave 1M + test(r==KErrNone); + CheckNotifierCount(notifierCount,3); // shouldn't get another notifier + + c.Close(); + TRequestStatus s; + NtfThrd.Logon(s); + NtfThrd.Kill(0); + User::WaitForRequest(s); + CLOSE_AND_WAIT(NtfThrd); + Notifier.Close(); + gobbler.Close(); + } + + +// TestFullAddressSpace is used to stress the memory allocation mechanism(beyond the 1GB limit). +// However, the memory model can introduce limitations in the total amount of memory a single +// process is allowed to allocate. To make the test more generic before closing the reserved +// chunks for this test we trigger the creation of a new process. This process executes +// t_chunk again, passing argument "extended". The result is that more chunks will be created +// through another call to TestFullAddressSpace, with the parameter extendedFlag set to true. +// Eventually the total amount of allocated space will overcome the 1Gb limit in any case. + +void TestFullAddressSpace(TBool extendedFlag ) + { + test.Start(_L("Fill process address space with chunks\n")); + RChunk chunk[2][11]; + TInt total = 0; + TInt i; + TInt j; + TInt r; + + for(j=0; j<=1; j++) + { + if(!j) + test.Next(_L("Creating local chunks")); + else + test.Next(_L("Creating global chunks")); + for(i=10; i>=0; --i) + { + TInt size = 1<<(20+i); + + if(!j) + r = chunk[j][i].CreateDisconnectedLocal(0,0,size); + else + r = chunk[j][i].CreateDisconnectedGlobal(KNullDesC,0,0,size); + TBuf<128> text; + text.AppendFormat(_L("Create %dMB chunk returns %d"),1<=0; --i) + chunk[j][i].Close(); + test.End(); + } + + +#ifdef __WINS__ +void TestExecLocalCode() + { + RChunk c; + TInt size = 10 * 1024; + TInt rc = c.CreateLocalCode(size, size, EOwnerProcess); + test_KErrNone(rc); + TUint8 *p = c.Base(); + TUint32 (*func)() = (TUint32 (*)())p; + test.Printf(_L("Create small function in the new code chunk\n")); + *p++ = 0xB8; // mov eax, 0x12345678 + *p++ = 0x78; + *p++ = 0x56; + *p++ = 0x34; + *p++ = 0x12; + *p = 0xC3; // ret + test.Printf(_L("Going to call the new function\n")); + TUint32 res = (*func)(); + test_Equal(0x12345678, res); + c.Close(); + } +#endif // __WINS__ + + +_LIT(KChunkName, "CloseChunk"); + +struct TRequestData + { + RSemaphore requestSem; + RSemaphore completionSem; + RSemaphore nextItSem; + }; + +TInt CloseThread(TAny* data) + { + TRequestData* reqData = (TRequestData*)data; + ASSERT(reqData); + + for(;;) + { + // Wait for a request to open and close the chunk. + reqData->requestSem.Wait(); + + // Try to open the chunk (may have already been closed by another thread). + RChunk chunk; + TInt r = chunk.OpenGlobal(KChunkName, EFalse, EOwnerThread); + if (r != KErrNone) + { + // The chunk was already closed... + r = (r == KErrNotFound) ? KErrNone : r; // Ensure no debug output for expected failures. + + if(r != KErrNone) + { + test.Printf(_L("CloseThread RChunk::OpenGlobal Err: %d\n"), r); + test_KErrNone(r); + } + } + else + { + // Close the chunk. + chunk.Close(); + } + + // Tell our parent we have completed this iteration and wait for the next. + reqData->completionSem.Signal(); + reqData->nextItSem.Wait(); + } + } + +void TestClosure() + { + const TUint KCloseThreads = 50; + RThread thread[KCloseThreads]; + TRequestStatus dead[KCloseThreads]; + + // We need three semaphores or we risk signal stealing if one thread gets ahead of the + // others and starts a second iteration before the other threads have been signalled + // and have begun their first iteration. Such a situation results in deadlock so we + // force all threads to finish the iteration first using the nextItSem semaphore to + // ensure we can only move to the next iteration once every thread has completed the + // current iteration. + TRequestData reqData; + test_KErrNone(reqData.requestSem.CreateLocal(0)); + test_KErrNone(reqData.completionSem.CreateLocal(0)); + test_KErrNone(reqData.nextItSem.CreateLocal(0)); + + TUint i = 0; + + // Create thread pool. We do this rather than create 50 threads + // over and over again for 800 times - the kernel's garbage collection + // does not keep up and we run out of backing store. + for (; i < KCloseThreads; i++) + { + test_KErrNone(thread[i].Create(KNullDesC, CloseThread, KDefaultStackSize, NULL, (TAny*)&reqData)); + thread[i].Logon(dead[i]); + thread[i].SetPriority(EPriorityMuchLess); + thread[i].Resume(); + } + + for (TUint delay = 200; delay < 1000; delay++) + { + test.Printf(_L("Closure delay %dus\r"), delay); + + // Create a global chunk. + RChunk chunk; + test_KErrNone(chunk.CreateGlobal(KChunkName, gPageSize, gPageSize)); + + // Release the threads so they can try to close the handle. + reqData.requestSem.Signal(KCloseThreads); + + // Wait for the delay then close the handle ourselves. + User::AfterHighRes(delay); + chunk.Close(); + + // Wait for the threads to complete then release them for the next iteration. + for (i = 0; i < KCloseThreads; i++) + { + reqData.completionSem.Wait(); + } + reqData.nextItSem.Signal(KCloseThreads); + + // Ensure garbage collection is complete to prevent the kernel's + // garbage collection from being overwhelmed and causing the + // backing store to be exhausted. + UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0); + } + + // Kill thread pool. + for (i = 0; i < KCloseThreads; i++) + { + thread[i].Kill(KErrNone); + User::WaitForRequest(dead[i]); + test(KErrNone == thread[i].ExitReason()); + test_Equal(EExitKill, thread[i].ExitType()); + thread[i].Close(); + } + + reqData.requestSem.Close(); + reqData.completionSem.Close(); + reqData.nextItSem.Close(); + + // Ensure garbage collection is complete to prevent false positive + // kernel memory leaks. + UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0); + } + + +/**Returns true if 'extended' is found in the command line*/ +TBool GetExtended() + { + _LIT(KExtended,"extended"); + TBuf<64> c; + User::CommandLine(c); + if (c.FindF(KExtended) >= 0) + return ETrue; + return EFalse; + } + + +TInt E32Main() +// +// Test RChunk class +// + { + test.Title(); + if (!HaveVirtMem()) + { + test.Printf(_L("This test requires an MMU\n")); + return KErrNone; + } + testInitialise(); + + // Turn off lazy dll unloading so the kernel heap checking isn't affected. + RLoader l; + test(l.Connect()==KErrNone); + test(l.CancelLazyDllUnload()==KErrNone); + l.Close(); + + __KHEAP_MARK; + + if (GetExtended() ) + { + test.Printf(_L("t_chunk extended was called. Ready to call TestFullAddressSpace(Etrue) \n")); + TestFullAddressSpace(ETrue); + } + else + { + test.Start(_L("Testing..")); + testAdjustChunk(); + test.Next(_L("Test1")); + test1(); + test.Next(_L("Test2")); + test2(0x80); + test.Next(_L("Test3")); + test3(0x7000); + test.Next(_L("Test4")); + test4(0x7000); + test.Next(_L("Test5")); + test5(0x80); + test.Next(_L("Test7")); + test7(0x80); + test.Next(_L("Test chunk data clearing attributes")); + testClear(0x500); + testClear(07000); + test.Next(_L("Test9")); + testShare(); + test.Next(_L("Test memory notifiers")); + testNotifiers(); + test.Next(_L("FindChunks")); + FindChunks(); + + test.Next(_L("Test full address space")); + TestFullAddressSpace(EFalse); + +#ifdef __WINS__ + test.Next(_L("Test execution of code in a local code chunk on emulator")); + TestExecLocalCode(); +#endif // __WINS__ + + test.Next(_L("Test for race conditions in chunk closure")); + TestClosure(); + test.End(); + } + + test.Close(); + __KHEAP_MARKEND; + + + return(KErrNone); + }