--- /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 <e32test.h>
+#include <e32panic.h>
+#include <e32svr.h>
+#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<size; i++)
+ if(base[i] != aClearByte)
+ ret=EFalse;
+ memset((TAny*)base, 0x05, size);
+ return ret;
+ }
+
+TInt roundToPageSize(TInt aSize)
+ {
+
+ return(((aSize+KMinPageSizeInBytesMinus1)&KMinPageSizeInBytesMask));
+ }
+
+TInt roundToChunkSize(TInt aSize)
+ {
+ if(MemModel==EMemModelTypeFlexible)
+ return roundToPageSize(aSize);
+ return(((aSize+KMinChunkSizeInBytesMinus1)&KMinChunkSizeInBytesMask));
+ }
+
+TInt ThreadEntry2(TAny* /*aParam*/)
+//
+// Thread to read from shared chunk
+//
+ {
+ RChunk c;
+ TInt r;
+ r=c.OpenGlobal(_L("Marmalade"),ETrue);
+ t(r==KErrNone);
+ TUint8* base=c.Base();
+ for (TInt8 i=0;i<10;i++)
+ t(*base++==i); // check the chunk has 0-9
+ c.Close();
+ return(KErrNone);
+ }
+
+TInt ThreadEntry(TAny* aDirective)
+//
+// Thread to create a Panic in a variety of ways
+//
+ {
+
+ switch((TUint)aDirective)
+ {
+ case ENormal:
+ {
+ return KNormalReturn;
+ }
+ case ECreateNegative:
+ {
+ gChunk.CreateLocal(0x10,-0x10);
+ test(EFalse);
+ }
+ case EAdjustNegative:
+ {
+ gChunk.Adjust(-0x10);
+ test(EFalse);
+ }
+ case ECreateInvalidType :
+ {
+ TChunkCreateInfo createInfo;
+ createInfo.SetCode(gPageSize, gPageSize);
+ _LIT(KChunkName, "Chunky");
+ createInfo.SetGlobal(KChunkName);
+ RChunk chunk;
+ chunk.Create(createInfo);
+ test(EFalse);
+ }
+ default:
+ test(EFalse);
+ }
+ return(KErrNone);
+ }
+
+//
+// Test the clear flags for the specified chunk.
+// Assumes chunk has one commited region from base to aBytes.
+//
+void TestClearChunk(RChunk& aChunk, TUint aBytes, TUint8 aClearByte)
+ {
+ test_Equal(ETrue, CheckChunkCleared(aChunk, 0, aClearByte));
+ test_KErrNone(aChunk.Adjust(0));
+ test_KErrNone(aChunk.Adjust(aBytes));
+ test_Equal(ETrue, CheckChunkCleared(aChunk, 0, aClearByte));
+ aChunk.Close();
+ }
+
+
+//
+// Create the specified thread and verify the exit reason.
+//
+void TestThreadExit(TExitType aExitType, TInt aExitReason, TThreadFunction aFunc, TAny* aThreadParam)
+ {
+ RThread thread;
+ test_KErrNone(thread.Create(_L("RChunkPanicThread"), aFunc, KDefaultStackSize,
+ KHeapSize, KHeapSize, aThreadParam));
+ // Disable JIT debugging.
+ TBool justInTime=User::JustInTime();
+ User::SetJustInTime(EFalse);
+
+ TRequestStatus status;
+ thread.Logon(status);
+ thread.Resume();
+ User::WaitForRequest(status);
+ test_Equal(aExitType, thread.ExitType());
+ test_Equal(aExitReason, status.Int());
+ test_Equal(aExitReason, thread.ExitReason());
+ if (aExitType == EExitPanic)
+ test(thread.ExitCategory()==_L("USER"));
+ CLOSE_AND_WAIT(thread);
+
+ // Put JIT debugging back to previous status.
+ User::SetJustInTime(justInTime);
+ }
+
+
+//
+// Thread function to create a chunk with invalid attributes
+//
+TInt ChunkPanicThread(TAny* aCreateInfo)
+ {
+ // This should panic.
+ RChunk chunk;
+ chunk.Create((*(TChunkCreateInfo*) aCreateInfo));
+ test(EFalse);
+
+ return KErrGeneral; // Shouldn't reach here.
+ }
+
+void testInitialise()
+ {
+ test.Next(_L("Load gobbler LDD"));
+ TInt r = User::LoadLogicalDevice(KGobblerLddFileName);
+ test(r==KErrNone || r==KErrAlreadyExists);
+
+ // get system info...
+ MemModel = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL) & EMemModelTypeMask;
+
+ test_KErrNone(UserHal::PageSizeInBytes(gPageSize));
+ }
+
+
+void test1()
+//
+// Test creating chunks with silly sizes
+//
+ {
+
+ __KHEAP_MARK;
+ RChunk chunk;
+ TInt r;
+ test.Start(_L("Create global chunks"));
+ r=chunk.CreateGlobal(_L("Fopp"),1,1);
+ test(r==KErrNone);
+ chunk.Close();
+ r=chunk.CreateGlobal(_L("Fopp"),0,0);
+ test(r==KErrArgument);
+
+ __KHEAP_CHECK(0);
+
+ test.Next(_L("Create local chunks"));
+ r=chunk.CreateLocal(1,1);
+ test(r==KErrNone);
+ chunk.Close();
+ r=chunk.CreateLocal(0,0);
+ test(r==KErrArgument);
+ test.End();
+ __KHEAP_MARKEND;
+ }
+
+
+void test2(TInt aSize)
+//
+// Test local/global chunk creation
+//
+ {
+
+ RChunk lchunk[KNumberOfChunks];
+ RChunk gchunk[KNumberOfChunks];
+ RChunk chunk;
+
+ __KHEAP_MARK;
+ test.Start(_L("Create multiple local and global chunks"));
+
+ TInt i;
+ TBuf<0x40> name;
+ TFullName fullname;
+ for (i=0; i<KNumberOfChunks; i++)
+ {
+ TInt r;
+ // create a local chunk
+ r=lchunk[i].CreateLocal(aSize,aSize);
+ test(r==KErrNone);
+ r=lchunk[i].MaxSize();
+ test(r==roundToChunkSize(aSize));
+ test(lchunk[i].Size()==roundToPageSize(aSize)); // was 0
+ test(lchunk[i].Name().Left(6)==_L("Local-"));
+
+ fullname=RProcess().Name();
+ fullname+=_L("::");
+ fullname+=lchunk[i].Name();
+ test(lchunk[i].FullName().CompareF(fullname)==0);
+
+ // create a global chunk
+ name.Format(_L("Chunk%d"),i);
+ r=gchunk[i].CreateGlobal(name,aSize,aSize);
+ test(r==KErrNone);
+ test(gchunk[i].MaxSize()==roundToChunkSize(aSize));
+ test(gchunk[i].Size()==roundToPageSize(aSize)); // was 0
+ test(gchunk[i].Name()==name);
+ }
+
+ // make room for another chunk
+ lchunk[KNumberOfChunks-1].Close();
+ gchunk[KNumberOfChunks-1].Close();
+
+// Try to create a global chunk with a duplicate name
+ test.Next(_L("Create a chunk with a duplicate name"));
+ TInt r=chunk.CreateGlobal(_L("Chunk0"),aSize,aSize);
+ test(r==KErrAlreadyExists);
+
+ test.Next(_L("Close all chunks"));
+ for (i=0; i<KNumberOfChunks-1; i++)
+ {
+ lchunk[i].Close();
+ gchunk[i].Close();
+ }
+
+ test.Next(_L("Reuse a name"));
+ r=chunk.CreateGlobal(_L("Chunk1"),aSize,aSize);
+ test(r==KErrNone);
+ test(chunk.MaxSize()==roundToChunkSize(aSize));
+ test(chunk.Size()==roundToPageSize(aSize));
+ test(chunk.Name()==_L("Chunk1"));
+ chunk.Close();
+
+ test.End();
+ __KHEAP_MARKEND;
+ }
+
+void test3_2(RChunk& aChunk, TInt aSize)
+//
+// Perform Adjust tests on aChunk
+//
+ {
+
+ TInt r;
+ test.Start(_L("Adjust to full size"));
+ r=aChunk.Adjust(aSize);
+ test(r==KErrNone);
+ test(aChunk.Size()==roundToPageSize(aSize));
+ test(aChunk.MaxSize()==roundToChunkSize(aSize));
+
+ test.Next(_L("Adjust a chunk to half size"));
+ r=aChunk.Adjust(aSize/2);
+ test(r==KErrNone);
+ test(aChunk.Size()==roundToPageSize(aSize/2));
+ test(aChunk.MaxSize()==roundToChunkSize(aSize));
+
+ test.Next(_L("Adjust to same size"));
+ r=aChunk.Adjust(aSize/2);
+ test(r==KErrNone);
+ test(aChunk.Size()==roundToPageSize(aSize/2));
+ test(aChunk.MaxSize()==roundToChunkSize(aSize));
+
+ test.Next(_L("Adjusting to size=0"));
+ r=aChunk.Adjust(0);
+ test(r==KErrNone);
+ test(aChunk.Size()==0);
+ test(aChunk.MaxSize()==roundToChunkSize(aSize));
+
+ test.Next(_L("Adjust back to half size"));
+ r=aChunk.Adjust(aSize/2);
+ test(r==KErrNone);
+ test(aChunk.Size()==roundToPageSize(aSize/2));
+ test(aChunk.MaxSize()==roundToChunkSize(aSize));
+
+ test.End();
+ }
+
+
+void test3(TInt aSize)
+//
+// Test Adjust size of chunk
+//
+ {
+
+ RChunk chunk;
+ __KHEAP_MARK;
+ TInt r;
+// Run Adjust tests with a local chunk
+ test.Start(_L("Local Chunk.Adjust()"));
+ r=chunk.CreateLocal(aSize,aSize);
+ test3_2(chunk, aSize);
+ chunk.Close();
+// Run Adjust tests with a global chunk
+ test.Next(_L("Global Chunk.Adjust()"));
+ r=chunk.CreateGlobal(_L("Fopp"),aSize,aSize);
+ test(KErrNone==r);
+ test3_2(chunk,aSize);
+ chunk.Close();
+ test.End();
+ __KHEAP_MARKEND;
+ }
+
+void test4(TInt aSize)
+//
+// Test OpenGlobal
+//
+ {
+
+ RChunk chunk[KChunkNum];
+ RChunk c1, c2, c3;
+ TName name;
+
+ __KHEAP_MARK;
+ TInt i,r;
+ for (i=0; i<KChunkNum; i++)
+ {
+ name.Format(_L("Chunk%d"), i);
+ r=chunk[i].CreateGlobal(name,aSize,aSize);
+ }
+
+ test.Start(_L("Open Global chunks"));
+ r=c1.OpenGlobal(_L("Chunk1"),EFalse); // 2nd ref to this chunk
+ test(r==KErrNone);
+ r=c2.OpenGlobal(_L("Chunk3"),EFalse); // 2nd ref to this chunk
+ test(r==KErrNone);
+ r=c3.OpenGlobal(_L("Chunk4"),EFalse); // 2nd ref to this chunk
+ test(r==KErrNone);
+ c3.Close();
+
+ test.Next(_L("Attempt Open non-existant chunk"));
+ r=c3.OpenGlobal(_L("Non Existant Chunk"),EFalse);
+ test(r==KErrNotFound);
+
+ for (i=0; i<KChunkNum; i++)
+ {
+ test.Printf(_L("Closing chunk %d\n"),i);
+ chunk[i].Close();
+ }
+
+ test.Next(_L("Test chunks with multiple references are still valid"));
+ r=c1.Adjust(aSize/2);
+ test(r==KErrNone);
+ test(c1.MaxSize()==roundToChunkSize(aSize));
+ test(c1.Size()==roundToPageSize(aSize/2));
+ test(c1.Name()==_L("Chunk1"));
+
+ r=c2.Adjust(aSize/2);
+ test(r==KErrNone);
+ test(c2.MaxSize()==roundToChunkSize(aSize));
+ test(c2.Size()==roundToPageSize(aSize/2));
+ test(c2.Name()==_L("Chunk3"));
+
+// Open another reference to a chunk
+ r=c3.OpenGlobal(_L("Chunk3"),EFalse);
+ test(r==KErrNone);
+ test(c3.Base()==c2.Base());
+ test(c3.Size()==c2.Size());
+ test(c2.Size()!=aSize);
+// Adjust with one reference
+ r=c3.Adjust(aSize);
+ test(r==KErrNone);
+// Test sizes from the other
+ test(c2.Size()==roundToPageSize(aSize));
+ test(c2.MaxSize()==roundToChunkSize(aSize));
+
+ c1.Close();
+ c2.Close();
+ c3.Close();
+
+ test.Next(_L("And check the heap..."));
+ test.End();
+ __KHEAP_MARKEND;
+ }
+
+void test5(TInt aSize)
+//
+// Test Open
+//
+ {
+
+ RChunk chunk[2*KNumberOfChunks];
+ RChunk c1;
+
+ test.Start(_L("Creating Local and Global Chunks"));
+ __KHEAP_MARK;
+ TInt i,r;
+ TBuf<0x40> b;
+
+// Create KNumberOfChunks Global chunks
+ for (i=0; i<KNumberOfChunks; i++)
+ {
+ b.Format(_L("Chunk%d"), i);
+ r=chunk[i].CreateGlobal(b,aSize,aSize);
+ test(chunk[i].Name()==b);
+ test(r==KErrNone);
+
+ b.Format(_L("This is chunk %d"), i);
+ b.Append(TChar(0));
+ chunk[i].Adjust(aSize);
+ Mem::Copy(chunk[i].Base(), b.Ptr(), b.Size());
+ test(chunk[i].MaxSize()==roundToChunkSize(aSize));
+ test(chunk[i].Size()==roundToPageSize(aSize));
+ }
+
+ test.Next(_L("Find and Open the Chunks"));
+ TFindChunk find;
+ TFullName name;
+ for (i=0; i<KNumberOfChunks; i++)
+ {
+ test.Printf(_L("Opening chunk %d\n"),i);
+ find.Find(chunk[i].FullName());
+ r = find.Next(name);
+ test(r==KErrNone);
+ c1.Open(find);
+ b=TPtrC((TText*)c1.Base());
+ name.Format(_L("This is chunk %d"), i);
+ test(b==name);
+ c1.Close();
+ }
+
+ test.Next(_L("Close chunks"));
+ for (i=0; i<KNumberOfChunks; i++)
+ chunk[i].Close();
+
+ test.End();
+ __KHEAP_MARKEND;
+ }
+
+
+void test7(TInt aSize)
+//
+// Deliberately cause RChunk panics
+//
+ {
+ __KHEAP_MARK;
+
+// ENormal
+ test.Start(_L("Test panic thread"));
+ TestThreadExit(EExitKill, KNormalReturn, ThreadEntry, (TAny*)ENormal);
+
+// ECreateNegative
+ test.Next(_L("Create Chunk with a negative size"));
+ TestThreadExit(EExitPanic, EChkCreateMaxSizeNegative, ThreadEntry, (TAny*) ECreateNegative);
+
+// EAdjustNegative
+ test.Next(_L("Adjust a Chunk to Size = -0x10"));
+ gChunk.CreateLocal(aSize,aSize);
+ TestThreadExit(EExitPanic, EChkAdjustNewSizeNegative, ThreadEntry, (TAny*) EAdjustNegative);
+ gChunk.Close();
+
+// ECreateInvalidType
+ test.Next(_L("Create chunk of invalid type"));
+ TestThreadExit(EExitPanic, EChkCreateInvalidType, ThreadEntry, (TAny*) ECreateInvalidType);
+
+ test.End();
+
+ __KHEAP_MARKEND;
+ }
+
+
+void testClear(TInt aSize)
+ {
+ __KHEAP_MARK;
+ test.Start(_L("Test clearing memory (Platform Security)"));
+
+ RChunk c1,c2,c3,c4,c5,c6,c7,c8,c9,c10;
+ TInt r;
+
+ TBuf<0x40> 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<top;i++)
+ *i=0xdeaddead;
+
+ test.Next(_L("Adjust the chunk"));
+ r=hermione.Adjust(0x1400);
+ test(r==KErrNone);
+
+ base=(TUint32*)hermione.Base();
+ top=(TUint32*)(hermione.Base()+hermione.Size());
+ test.Printf(_L("Base = %08x, Top = %08x\n"),base,top);
+ test.Next(_L("Check I can write to all of the bigger chunk"));
+ for (i=base;i<top;i++)
+ *i=0xdeaddead;
+
+ hermione.Close();
+
+ test.Next(_L("Do some size checks"));
+ RChunk wibble;
+ r=wibble.CreateGlobal(_L("Wibble"),0x1,gPageSize*8);
+ test(r==KErrNone);
+ test.Next(_L("Check create rounds up to page multiple"));
+ test(wibble.Size()==(TInt)gPageSize);
+ test.Next(_L("Check create rounds up to chunk multiple"));
+ test(wibble.MaxSize()==roundToChunkSize(gPageSize*8));
+
+ test.Next(_L("Check adjust rounds up to page multiple"));
+ r=wibble.Adjust((gPageSize*6)-12);
+ test(r==KErrNone);
+ test(wibble.Size()==gPageSize*6);
+
+ test.Next(_L("Different number, same size"));
+ r=wibble.Adjust((gPageSize*6)-18);
+ test(r==KErrNone);
+ test(wibble.Size()==gPageSize*6);
+
+ test.Next(_L("Check adjust > 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<<i,r);
+ test.Next(text);
+ if(r!=KErrNoMemory)
+ {
+ test(r==KErrNone);
+ // commit memory to each 1MB region,
+ // this excercises page table allocation
+ volatile TUint8* base = (TUint8*)chunk[j][i].Base();
+ for(TInt o=0; o<size; o+=1<<20)
+ {
+ r = chunk[j][i].Commit(o,1);
+ test(r==KErrNone);
+ // access the commited memory...
+ base[o] = (TUint8)(o&0xff);
+ test(base[o]==(TUint8)(o&0xff));
+ base[o] = (TUint8)~(o&0xff);
+ test(base[o]==(TUint8)~(o&0xff));
+ }
+ total += 1<<i;
+ }
+ }
+ }
+
+ if (extendedFlag == EFalse)
+ {
+
+ test.Printf(_L("Total chunk size created was %d MB\n\n"),total);
+
+ if(total<1024)
+ {
+ _LIT(KOtherProcessName,"t_chunk");
+ _LIT(KProcessArgs,"extended");
+
+ RProcess process;
+ r=process.Create(KOtherProcessName,KProcessArgs );
+ test.Printf(_L("Creating new process( t_chunk extended) returns %d\n"),r );
+ test( r == KErrNone);
+
+ TRequestStatus status;
+ process.Logon(status);
+ process.Resume();
+
+ User::WaitForRequest(status);
+
+ test(process.ExitType() == EExitKill);
+ test(process.ExitReason() == 0);
+ process.Close();
+ }
+
+ }
+ else
+ test.Printf(_L("Total chunk size created by the new process was %d MB\n"),total);
+
+ for(j=0; j<=1; j++)
+ for(i=10; i>=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);
+ }