os/kernelhwsrv/kerneltest/e32test/demandpaging/t_datapaging.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
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".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // e32test\demandpaging\t_datapaging.cpp
    15 // Functional tests for data paging.
    16 // 002 Test UserHeap::ChunkHeap data paging attributes
    17 // 003 Test RThread::Create data paging attributes
    18 // 
    19 //
    20 
    21 //! @SYMTestCaseID			KBASE-T_DATAPAGING
    22 //! @SYMTestType			UT
    23 //! @SYMPREQ				PREQ1954
    24 //! @SYMTestCaseDesc		Data Paging functional tests.
    25 //! @SYMTestActions			001 Test RChunk data paging attributes
    26 //! @SYMTestExpectedResults All tests should pass.
    27 //! @SYMTestPriority        High
    28 //! @SYMTestStatus          Implemented
    29 
    30 #define __E32TEST_EXTENSION__
    31 #include <e32test.h>
    32 #include <dptest.h>
    33 #include <e32hal.h>
    34 #include <u32exec.h>
    35 #include <e32svr.h>
    36 #include <e32panic.h>
    37 #include "u32std.h"
    38 #include <e32msgqueue.h>
    39 #include <e32atomics.h>
    40 #include <e32math.h>
    41 
    42 #include "t_dpcmn.h"
    43 #include "../mmu/mmudetect.h"
    44 #include "../mmu/d_memorytest.h"
    45 #include "../mmu/paging_info.h"
    46 
    47 RTest test(_L("T_DATAPAGING"));
    48 
    49 _LIT(KChunkName, "t_datapaging chunk");
    50 
    51 class TRandom
    52 	{
    53 public:
    54 	TRandom();
    55 	TUint32 Next();
    56 
    57 private:
    58 	enum
    59 		{
    60 		KA = 1664525,
    61 		KB = 1013904223
    62 		};
    63 	TUint32 iV;
    64 	};
    65 
    66 TRandom::TRandom()
    67 	{
    68 	iV = RThread().Id() + User::NTickCount() + 23;
    69 	}
    70 
    71 TUint32 TRandom::Next()
    72 	{
    73 	iV = KA * iV + KB;
    74 	return iV;
    75 	}
    76 
    77 void CreatePagedChunk(TInt aSizeInPages, TInt aWipeByte = -1)
    78 	{
    79 	test_Equal(0,gChunk.Handle());
    80 	
    81 	TChunkCreateInfo createInfo;
    82 	TInt size = aSizeInPages * gPageSize;
    83 	createInfo.SetNormal(size, size);
    84 	createInfo.SetPaging(TChunkCreateInfo::EPaged);
    85 	createInfo.SetOwner(EOwnerProcess);
    86 	createInfo.SetGlobal(KChunkName);
    87 	if (aWipeByte != -1)
    88 		createInfo.SetClearByte(aWipeByte);
    89 	test_KErrNone(gChunk.Create(createInfo));
    90 	test(gChunk.IsPaged()); // this is only ever called if data paging is supported
    91 	}
    92 
    93 // The contents of a page is represented as type from enum below ORed with a byte value
    94 enum TPageContent
    95 	{
    96 	ETypeUniform    = 0 << 8,
    97 	ETypeIncreasing = 1 << 8,
    98 
    99 	EContentValueMask = 255,
   100 	EContentTypeMask  = 255 << 8
   101 	};
   102 
   103 // Write to a page to page it in and verify its previous contents
   104 void WritePage(TInt aIndex, TUint aExpectedContents, TUint aNewContents)
   105 	{
   106 	test.Printf(_L("  %3d Write %x\n"), aIndex, aNewContents);
   107 	
   108 	TUint oldType = aExpectedContents & EContentTypeMask;
   109 	TUint oldValue = aExpectedContents & EContentValueMask;
   110 	
   111 	TUint type = aNewContents & EContentTypeMask;
   112 	TUint value = aNewContents & EContentValueMask;
   113 	
   114 	TUint8* page = gChunk.Base() + (gPageSize * aIndex);
   115 
   116 	// write first byte first so page is paged in or rejuvenated with write permissions
   117 	page[0] = 0;
   118 	
   119 	for (TInt i = 0 ; i < gPageSize ; ++i)
   120 		{
   121 		if (i != 0)
   122 			test_Equal(oldValue, page[i]);
   123 		if (oldType == ETypeIncreasing)
   124 			oldValue = (oldValue + 1) & 255;
   125 		
   126 		page[i] = value;
   127 		if (type == ETypeIncreasing)
   128 			value = (value + 1) & 255;
   129 		}
   130 	}
   131 
   132 // Read a page and verify its contents
   133 void ReadPage(TInt aIndex, TUint aExpectedContents)
   134 	{
   135 	test.Printf(_L("  %3d Read  %x\n"), aIndex, aExpectedContents);
   136 	TUint type = aExpectedContents & EContentTypeMask;
   137 	TUint value = aExpectedContents & EContentValueMask;
   138 	TUint8* page = gChunk.Base() + (gPageSize * aIndex);
   139 	for (TInt i = 0 ; i < gPageSize ; ++i)
   140 		{
   141 		test_Equal(value, page[i]);
   142 		if (type == ETypeIncreasing)
   143 			value = (value + 1) & 255;
   144 		}
   145 	}
   146 
   147 void PageOut()
   148 	{
   149 	test.Printf(_L("      PageOut\n"));
   150 	DPTest::FlushCache();
   151 	}
   152 
   153 void TestOnePage()
   154 	{
   155 	CreatePagedChunk(1, 0xed);
   156 
   157 	// Test initial contents (read)
   158 	ReadPage(0, ETypeUniform | 0xed);
   159 
   160 	// Test read initial contents after flush (may or may not actually been paged out)
   161 	PageOut();
   162 	ReadPage(0, ETypeUniform | 0xed);
   163 
   164 	// Test page out / page in (read) of dirty contents
   165 	WritePage(0, ETypeUniform | 0xed, ETypeIncreasing | 0x1a);
   166 	PageOut();
   167 	ReadPage(0, ETypeIncreasing | 0x1a);
   168 
   169 	// Test page out / page in (read) of clean contents
   170 	PageOut();
   171 	ReadPage(0, ETypeIncreasing | 0x1a);
   172  
   173 	// Test page out / page in (write) of dirty contents
   174 	WritePage(0, ETypeIncreasing | 0x1a, ETypeIncreasing | 0x23);
   175 	PageOut();
   176 	WritePage(0, ETypeIncreasing | 0x23, ETypeIncreasing | 0x45);
   177 
   178 	CLOSE_AND_WAIT(gChunk);
   179 	CreatePagedChunk(1, 0x0d);
   180 
   181 	// Test initial contents (write)
   182 	WritePage(0, ETypeUniform | 0x0d, ETypeIncreasing | 0x1a);
   183 
   184 	// Test page out / page in (read) of dirty contents
   185 	PageOut();
   186 	ReadPage(0, ETypeIncreasing | 0x1a);
   187 	
   188 	CLOSE_AND_WAIT(gChunk);
   189 	}
   190 
   191 TInt PageInThreadFunc(TAny* aArg)
   192 	{
   193 	TUint8* page = (TUint8*)aArg;
   194 	for (;;)
   195 		{
   196 		DPTest::FlushCache();
   197 		RDebug::Printf("Start page in...");
   198 		volatile TInt i = page[0];
   199 		(void)i;
   200 		RDebug::Printf("  done.");
   201 		}
   202 	}
   203 
   204 TInt PageOutThreadFunc(TAny* aArg)
   205 	{
   206 	TUint8* page = (TUint8*)aArg;
   207 	for (;;)
   208 		{
   209 		page[0] = 1;  // make page dirty
   210 		RDebug::Printf("Start page out...");
   211 		DPTest::FlushCache();
   212 		RDebug::Printf("  done.");
   213 		}
   214 	}
   215 
   216 void TestKillThread(TThreadFunction aFunc, TInt aIterations)
   217 	{
   218 	__KHEAP_MARK;
   219 	TRandom random;
   220 	CreatePagedChunk(1);
   221 	TUint8* page = gChunk.Base();
   222 	page[0] = 0;  // make page dirty
   223 	DPTest::FlushCache();
   224 	for (TInt i = 0 ; i < aIterations ; ++i)
   225 		{
   226 		RThread thread;
   227 		test_KErrNone(thread.Create(KNullDesC, aFunc, gPageSize, NULL, page));
   228 		TRequestStatus status;
   229 		thread.Logon(status);
   230 		thread.Resume();
   231 		User::AfterHighRes((random.Next() % 50 + 1) * 1000);
   232 		thread.Kill(123);
   233 		User::WaitForRequest(status);
   234 		test_Equal(123, status.Int());
   235 		CLOSE_AND_WAIT(thread);
   236 		}
   237 	CLOSE_AND_WAIT(gChunk);
   238 	User::After(1000000);
   239 	__KHEAP_MARKEND;
   240 	}
   241 
   242 struct SSoakTestArgs
   243 	{
   244 	TInt iThreadIndex;
   245 	TInt iPages;
   246 	};
   247 
   248 TUint32* PageBasePtr(TInt aPage)
   249 	{
   250 	return (TUint32*)(gChunk.Base() + (gPageSize * aPage));
   251 	}
   252 
   253 TUint32* PageDataPtr(TInt aPage, TInt aThreadIndex)
   254 	{
   255 	return (TUint32*)((TUint8*)PageBasePtr(aPage) + ((aThreadIndex * 2 + 1) * sizeof(TUint32)));
   256 	}
   257 
   258 TUint32 PageTag(TInt aPage)
   259 	{
   260 	return 0x80000000 | aPage;
   261 	}	
   262 
   263 void StopSoakTest(RMsgQueue<TInt> aMsgQueue)
   264 	{
   265 	while(aMsgQueue.Send(0) != KErrOverflow)
   266 		;
   267 	}
   268 
   269 TBool ContinueSoakTest(RMsgQueue<TInt> aMsgQueue)
   270 	{
   271 	TInt msg;
   272 	return aMsgQueue.Receive(msg) == KErrUnderflow;
   273 	}
   274 
   275 _LIT(KMsgQueueName, "t_datapaging_queue");
   276 
   277 TInt PinPagesFunc(TAny* aArg)
   278 	{
   279 	SSoakTestArgs* args = (SSoakTestArgs*)aArg;
   280 
   281 	RMemoryTestLdd ldd;
   282 	TInt r = ldd.Open();
   283 	if (r != KErrNone)
   284 		return r;
   285 	r = ldd.CreateVirtualPinObject();
   286 	if (r != KErrNone)
   287 		return r;
   288 
   289 	RMsgQueue<TInt> msgQueue;
   290 	r = msgQueue.OpenGlobal(KMsgQueueName, EOwnerThread);
   291 	if (r != KErrNone)
   292 		return r;
   293 
   294 	TInt i = 0;
   295 	TRandom random;
   296 	while (ContinueSoakTest(msgQueue))
   297 		{
   298 		TInt count = 1 + random.Next() % (args->iPages / 4);
   299 		TInt start = random.Next() % (args->iPages - count);
   300 		TInt sleepInMs = 1 + random.Next() % 20;
   301 		TUint32* ptr = PageBasePtr(start);
   302 
   303 		r = ldd.PinVirtualMemory((TLinAddr)ptr, count * gPageSize);
   304 		if (r != KErrNone)
   305 			return r;
   306 
   307 		User::AfterHighRes(sleepInMs * 1000);
   308 
   309 		r = ldd.UnpinVirtualMemory();
   310 		if (r != KErrNone)
   311 			return r;
   312 	
   313 		++i;
   314 		}
   315 
   316 	msgQueue.Close();
   317 
   318 	r = ldd.DestroyVirtualPinObject();
   319 	if (r != KErrNone)
   320 		return r;
   321 	ldd.Close();
   322 					
   323 	RDebug::Printf("  thread %d performed %d iterations (pinning)", args->iThreadIndex, i);
   324 	return KErrNone;
   325 	}
   326 
   327 TBool TestReadWord(TUint32* aPtr, TUint32 aExpected, TInt aThread, TInt aPage, TInt aIteration, TInt aLine, RMsgQueue<TInt> aMsgQueue)
   328 	{
   329 	TUint32 aActual = *aPtr;
   330 	if (aActual != aExpected)
   331 		{
   332 		StopSoakTest(aMsgQueue);
   333 		RDebug::Printf("  thread %d failure reading page %d at iteration %d address %08x: expected %08x but got %08x",
   334 					   aThread, aPage, aIteration, aPtr, aExpected, aActual);
   335 		return EFalse;
   336 		}
   337 	return ETrue;
   338 	}
   339 
   340 TInt SoakTestFunc(TAny* aArg)
   341 	{
   342 	SSoakTestArgs* args = (SSoakTestArgs*)aArg;
   343 
   344 	
   345 	RMsgQueue<TInt> msgQueue;
   346 	TInt r = msgQueue.OpenGlobal(KMsgQueueName, EOwnerThread);
   347 	if (r != KErrNone)
   348 		return r;
   349 
   350 	TUint32* contents = new TUint32[args->iPages];
   351 	if (contents == NULL)
   352 		return KErrNoMemory;
   353 	Mem::Fill(contents, args->iPages * sizeof(TUint32), 0);
   354 
   355 	TInt i = 0;
   356 	TRandom random;
   357 	while (ContinueSoakTest(msgQueue))
   358 		{
   359 		TUint32 rand = random.Next();
   360 		TInt page = rand % args->iPages;
   361 		TUint32* ptr = PageDataPtr(page, args->iThreadIndex);
   362 		TInt action = rand >> 31;
   363 		if (action == 0)
   364 			{
   365 			if (!TestReadWord(PageBasePtr(page), PageTag(page), args->iThreadIndex, page, i, __LINE__, msgQueue))
   366 				return KErrGeneral;
   367 			if (!TestReadWord(&ptr[0], contents[page], args->iThreadIndex, page, i, __LINE__, msgQueue))
   368 				return KErrGeneral;
   369 			if (!TestReadWord(&ptr[1], contents[page], args->iThreadIndex, page, i, __LINE__, msgQueue))
   370 				return KErrGeneral;
   371 			}
   372 		else
   373 			{
   374 			TUint newContents = args->iThreadIndex+0x100+(contents[page]&~0xff);
   375 			ptr[0] = newContents;
   376 			if (!TestReadWord(PageBasePtr(page), PageTag(page), args->iThreadIndex, page, i, __LINE__, msgQueue))
   377 				return KErrGeneral;
   378 			if (!TestReadWord(&ptr[1], contents[page], args->iThreadIndex, page, i, __LINE__, msgQueue))
   379 				return KErrGeneral;
   380 			ptr[1] = newContents;
   381 			contents[page] = newContents;
   382 			}
   383 		++i;
   384 		}
   385 	
   386 	for (TInt j = 0 ; j < args->iPages ; ++j)
   387 		{
   388 		TUint32* ptr = PageDataPtr(j, args->iThreadIndex);
   389 		if (!TestReadWord(PageBasePtr(j), PageTag(j), args->iThreadIndex, j, i, __LINE__, msgQueue))
   390 			return KErrGeneral;
   391 		if (!TestReadWord(&ptr[0], contents[j], args->iThreadIndex, j, i, __LINE__, msgQueue))
   392 			return KErrGeneral;
   393 		if (!TestReadWord(&ptr[1], contents[j], args->iThreadIndex, j, i, __LINE__, msgQueue))
   394 			return KErrGeneral;
   395 		}
   396 
   397 	delete [] contents;
   398 	msgQueue.Close();
   399 
   400 	RDebug::Printf("  thread %d performed %d iterations", args->iThreadIndex, i);
   401 	return KErrNone;
   402 	}
   403 
   404 TInt SoakProcess(TInt aProcessIndex, TInt aThreads, TInt aPages, TBool aPinPages)
   405 	{
   406 	TInt pinThreadIndex = aPinPages ? aThreads++ : -1;
   407 
   408 	test_KErrNone(gChunk.OpenGlobal(KChunkName, EFalse));
   409 	
   410 	SSoakTestArgs* testArgs = new SSoakTestArgs[aThreads];
   411 	test_NotNull(testArgs);
   412 			
   413 	RThread* threads = new RThread[aThreads];
   414 	test_NotNull(threads);
   415 	
   416 	TRequestStatus* statuses = new TRequestStatus[aThreads];
   417 	test_NotNull(statuses);
   418 	
   419 	TInt i;
   420 	for (i = 0 ; i < aThreads ; ++i)
   421 		{
   422 		testArgs[i].iThreadIndex = aProcessIndex * aThreads + i;
   423 		testArgs[i].iPages = aPages;
   424 		TThreadFunction func = i == pinThreadIndex ? PinPagesFunc : SoakTestFunc;
   425 		test_KErrNone(threads[i].Create(KNullDesC, func, gPageSize, NULL, &testArgs[i]));
   426 		threads[i].Logon(statuses[i]);
   427 		}
   428 
   429 	// todo: rendezvous here?
   430 	
   431 	for (i = 0 ; i < aThreads ; ++i)
   432 		threads[i].Resume();
   433 	
   434 	TBool ok = ETrue;		
   435 	for (i = 0 ; i < aThreads ; ++i)
   436 		{
   437 		User::WaitForRequest(statuses[i]);
   438 		if (threads[i].ExitType() != EExitKill || statuses[i].Int() != KErrNone)
   439 			ok = EFalse;
   440 		threads[i].Close();
   441 		}
   442 	
   443 	delete [] testArgs;
   444 	delete [] threads;
   445 	delete [] statuses;
   446 	gChunk.Close();
   447 	
   448 	return ok ? KErrNone : KErrGeneral;
   449 	}
   450 
   451 TInt RunSoakProcess()
   452 	{
   453 	TBuf<80> buf;
   454 	if (User::CommandLineLength() > buf.MaxLength())
   455 		return KErrArgument;
   456 	User::CommandLine(buf);
   457 	TLex lex(buf);
   458 
   459 	TInt index;
   460 	TInt r = lex.Val(index);
   461 	if (r != KErrNone)
   462 		return r;
   463 	lex.SkipSpace();
   464 
   465 	TInt threads;
   466 	r = lex.Val(threads);
   467 	if (r != KErrNone)
   468 		return r;
   469 	lex.SkipSpace();
   470 	
   471 	TInt pages;
   472 	r = lex.Val(pages);
   473 	if (r != KErrNone)
   474 		return r;
   475 	lex.SkipSpace();
   476 	
   477 	TBool pinPages;
   478 	r = lex.Val(pinPages);
   479 	if (r != KErrNone)
   480 		return r;
   481 	
   482 	return SoakProcess(index, threads, pages, pinPages);
   483 	}
   484 
   485 void SoakTest(TInt aProcesses, TInt aThreads, TInt aPages, TBool aPinPages, TInt aDurationInSeconds)
   486 	{
   487 	RDebug::Printf("Soak test: %d processes, %d threads, %d pages, %s pinning for %d seconds",
   488 				   aProcesses, aThreads, aPages, (aPinPages ? "with" : "without"), aDurationInSeconds);
   489 	DPTest::FlushCache();
   490 	
   491 	TInt totalThreads = (aThreads + (aPinPages ? 1 : 0)) * aProcesses;
   492 	test(totalThreads < 512); // each thread uses two words in a page
   493 
   494 	TMediaPagingStats dummy=EMediaPagingStatsRomAndCode;
   495 	PagingInfo::ResetBenchmarks(-1, dummy);	// Don't worry about locmedia stats.
   496 
   497 	RMsgQueue<TInt> msgQueue;
   498 	test_KErrNone(msgQueue.CreateGlobal(KMsgQueueName, totalThreads, EOwnerThread));
   499 
   500 	CreatePagedChunk(aPages, 0);
   501 	TInt i;
   502 	for (i = 0 ; i < aPages ; ++i)
   503 		*PageBasePtr(i) = PageTag(i);
   504 			
   505 	RProcess* processes = new RProcess[aProcesses];
   506 	TRequestStatus* statuses = new TRequestStatus[aProcesses];
   507 	for (i = 0 ; i < aProcesses ; ++i)
   508 		{
   509 		TBuf<80> args;
   510 		args.AppendFormat(_L("%d %d %d %d"), i, aThreads, aPages, aPinPages);
   511 		test_KErrNone(processes[i].Create(_L("t_datapaging"), args));
   512 		processes[i].Logon(statuses[i]);
   513 		}
   514 
   515 	RThread().SetPriority(EPriorityMore); // so we don't get starved of CPU by worker threads
   516 
   517 	for (i = 0 ; i < aProcesses ; ++i)
   518 		processes[i].Resume();
   519 
   520 	User::After(aDurationInSeconds * 1000000);
   521 	StopSoakTest(msgQueue);
   522 	
   523 	TBool ok = ETrue;		
   524 	for (i = 0 ; i < aProcesses ; ++i)
   525 		{
   526 		User::WaitForRequest(statuses[i]);
   527 		if (processes[i].ExitType() != EExitKill || statuses[i].Int() != KErrNone)
   528 			{
   529 			ok = EFalse;
   530 			RDebug::Printf("  process %i died with %d,%d", i, processes[i].ExitType(), statuses[i].Int());
   531 			}
   532 		processes[i].Close();
   533 		}
   534 
   535 	RThread().SetPriority(EPriorityNormal);
   536 
   537 	if (!ok)
   538 		{
   539 		for (i = 0 ; i < aPages ; ++i)
   540 			{
   541 			test.Printf(_L("%3d %08x"), i, *PageBasePtr(i));
   542 			for (TInt j = 0 ; j < totalThreads ; ++j)
   543 				{
   544 				TUint32* ptr = PageDataPtr(i, j);
   545 				test.Printf(_L(" %08x,%08x"), ptr[0], ptr[1]);
   546 				}
   547 			test.Printf(_L("\n"), i);
   548 			}
   549 		}
   550 	test(ok);	
   551 
   552 	gChunk.Close();
   553 	
   554 	User::After(1000000);
   555 	RDebug::Printf("  done");
   556 	RDebug::Printf("\n");
   557 	
   558 	msgQueue.Close();
   559 	delete [] processes;
   560 	delete [] statuses;
   561 
   562 	PagingInfo::PrintBenchmarks(-1, dummy);	// Don't worry about locmedia stats.
   563 	}
   564 
   565 void CommitPage(RChunk chunk, TInt aPageIndex)
   566 	{
   567 	test_KErrNone(chunk.Commit(aPageIndex * gPageSize, gPageSize));
   568 	}
   569 
   570 void DecommitPage(RChunk chunk, TInt aPageIndex)
   571 	{
   572 	test_KErrNone(chunk.Decommit(aPageIndex * gPageSize, gPageSize));
   573 	}
   574 
   575 void WaitForNotifiers()
   576 	{
   577 	// wait until notifiers have had chance to signal us...
   578 	UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0);
   579 	}
   580 
   581 void TestSwapHal()
   582 	{
   583 	test.Next(_L("Test EVMHalGetSwapInfo"));
   584 
   585 	TChunkCreateInfo createInfo;
   586 	createInfo.SetDisconnected(0, 0, 256 * gPageSize);
   587 	createInfo.SetPaging(TChunkCreateInfo::EPaged);
   588 	RChunk chunk;
   589 	test_KErrNone(chunk.Create(createInfo));
   590 	if (gDataPagingSupported)
   591 		test(chunk.IsPaged());
   592 	
   593 	SVMSwapInfo swapInfo;
   594 	test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo, 0));
   595 	test(swapInfo.iSwapFree <= swapInfo.iSwapSize);
   596 	test.Printf(_L("  Swap size == 0x%x bytes\n"), swapInfo.iSwapSize);
   597 	test.Printf(_L("  Swap free == 0x%x bytes\n"), swapInfo.iSwapFree);
   598 	if (!gDataPagingSupported)
   599 		{
   600 		test_Equal(0, swapInfo.iSwapSize);
   601 		}
   602 	else
   603 		{
   604 		test(swapInfo.iSwapSize != 0);
   605 		
   606 		CommitPage(chunk, 0);
   607 		SVMSwapInfo swapInfo2;
   608 		test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0));
   609 		test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize);
   610 		test_Equal(swapInfo.iSwapFree - gPageSize, swapInfo2.iSwapFree);
   611 		
   612 		DecommitPage(chunk, 0);
   613 		test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0));
   614 		test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize);
   615 		test_Equal(swapInfo.iSwapFree, swapInfo2.iSwapFree);
   616 
   617 		// Test that closing the chunk releases the swap page.
   618 		CommitPage(chunk, 0);
   619 		test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0));
   620 		test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize);
   621 		test_Equal(swapInfo.iSwapFree - gPageSize, swapInfo2.iSwapFree);
   622 		
   623 		chunk.Close();
   624 		test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0));
   625 		test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize);
   626 		test_Equal(swapInfo.iSwapFree, swapInfo2.iSwapFree);
   627 
   628 		// Chunk must be created for rest of testing.
   629 		test_KErrNone(chunk.Create(createInfo));
   630 		if (gDataPagingSupported)
   631 			test(chunk.IsPaged());
   632 		}
   633 	
   634 	//	EVMHalSetSwapThresholds,
   635 	test.Next(_L("Test EVMHalSetSwapThresholds"));
   636 	SVMSwapThresholds thresholds;
   637 	thresholds.iLowThreshold = 1;
   638 	thresholds.iGoodThreshold = 0;
   639 	test_Equal(KErrArgument, UserSvr::HalFunction(EHalGroupVM, EVMHalSetSwapThresholds, &thresholds, 0));
   640 	thresholds.iLowThreshold = swapInfo.iSwapSize + 1;
   641 	thresholds.iGoodThreshold = swapInfo.iSwapSize + 1;
   642 	test_Equal(KErrArgument, UserSvr::HalFunction(EHalGroupVM, EVMHalSetSwapThresholds, &thresholds, 0));
   643 	thresholds.iLowThreshold = 0;
   644 	thresholds.iGoodThreshold = 0;
   645 	test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalSetSwapThresholds, &thresholds, 0));
   646 	thresholds.iLowThreshold = swapInfo.iSwapSize;
   647 	thresholds.iGoodThreshold = swapInfo.iSwapSize;
   648 	test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalSetSwapThresholds, &thresholds, 0));
   649 
   650 	// test thresholds trigger ok
   651 	
   652 	RChangeNotifier changes;
   653 	test_KErrNone(changes.Create());
   654 	TRequestStatus status;
   655 	test_KErrNone(changes.Logon(status));
   656 	User::WaitForRequest(status);
   657 	test_KErrNone(changes.Logon(status));
   658 	test_Equal(KRequestPending, status.Int());
   659 	
   660 	thresholds.iLowThreshold = swapInfo.iSwapFree - 2 * gPageSize;
   661 	thresholds.iGoodThreshold = swapInfo.iSwapFree - gPageSize;
   662 	test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalSetSwapThresholds, &thresholds, 0));
   663 
   664 	CommitPage(chunk, 0);
   665 	CommitPage(chunk, 1);
   666 	WaitForNotifiers();
   667 	test_Equal(KRequestPending, status.Int());
   668 	CommitPage(chunk, 2);
   669 	WaitForNotifiers();
   670 	test_Equal(EChangesFreeMemory | EChangesLowMemory, status.Int());
   671 	User::WaitForRequest(status);
   672 	
   673 	test_KErrNone(changes.Logon(status));
   674 	DecommitPage(chunk, 2);
   675 	WaitForNotifiers();
   676 	test_Equal(KRequestPending, status.Int());
   677 	DecommitPage(chunk, 1);
   678 	WaitForNotifiers();
   679 	test_Equal(EChangesFreeMemory, status.Int());
   680 	User::WaitForRequest(status);
   681 	DecommitPage(chunk, 0);
   682 	
   683 	CLOSE_AND_WAIT(changes);
   684 
   685 	// leave some sensible thresholds set
   686 	thresholds.iLowThreshold = (10 * swapInfo.iSwapSize) / 100;
   687 	thresholds.iGoodThreshold = (20 * swapInfo.iSwapSize) / 100;
   688 	test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalSetSwapThresholds, &thresholds, 0));
   689 
   690 	CLOSE_AND_WAIT(chunk);
   691 	}
   692 
   693 void TestSwapHalNotSupported()
   694 	{
   695 	test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, 0, 0));
   696 	test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalSetSwapThresholds, 0, 0));
   697 	}
   698 
   699 void TestHal()
   700 	{
   701 	if (gDataPagingSupported)
   702 		TestSwapHal();
   703 	else
   704 		TestSwapHalNotSupported();
   705 	}
   706 
   707 
   708 TBool gStealEnable = false;
   709 
   710 TInt DecommitThread(TAny*)
   711 	{
   712 	RThread().SetPriority(EPriorityLess); // so this thread gets pre-empted by StealThread
   713 	TUint8* base = gChunk.Base();
   714 	TInt size = gChunk.MaxSize();
   715 	for(;;)
   716 		{
   717 		// dirty all pages
   718 		for(TInt i=0; i<size; i+=gPageSize)
   719 			base[i] = 0;
   720 		// free pages...
   721 		gStealEnable = true;
   722 		gChunk.Adjust(0);
   723 		gStealEnable = false;
   724 		// recommit pages...
   725 		TInt r = gChunk.Adjust(size);
   726 		if(r!=KErrNone)
   727 			return r; // error
   728 		}
   729 	}
   730 
   731 
   732 TInt StealThread(TAny*)
   733 	{
   734 	for(;;)
   735 		{
   736 		while(!gStealEnable)
   737 			User::AfterHighRes(0);
   738 		DPTest::FlushCache();
   739 		}
   740 	}
   741 
   742 
   743 void TestDecommitAndStealInteraction(TInt aSeconds)
   744 	{
   745 	__KHEAP_MARK;
   746 
   747 	CreatePagedChunk(256);
   748 
   749 	RThread thread1;
   750 	test_KErrNone(thread1.Create(_L("DecommitThread"), DecommitThread, gPageSize, NULL, 0));
   751 	TRequestStatus status1;
   752 	thread1.Logon(status1);
   753 
   754 	RThread thread2;
   755 	test_KErrNone(thread2.Create(_L("StealThread"), StealThread, gPageSize, NULL, 0));
   756 	TRequestStatus status2;
   757 	thread1.Logon(status2);
   758 
   759 	RTimer timer;
   760 	test_KErrNone(timer.CreateLocal());
   761 	TRequestStatus timeoutStatus;
   762 	timer.After(timeoutStatus,aSeconds*1000000);
   763 
   764 	thread1.Resume();
   765 	thread2.Resume();
   766 	User::WaitForAnyRequest();
   767 
   768 	thread1.Kill(123);
   769 	User::WaitForRequest(status1);
   770 	test_Equal(123, status1.Int());
   771 	CLOSE_AND_WAIT(thread1);
   772 
   773 	thread2.Kill(123);
   774 	User::WaitForRequest(status2);
   775 	test_Equal(123, status2.Int());
   776 	CLOSE_AND_WAIT(thread2);
   777 
   778 	CLOSE_AND_WAIT(timer);
   779 	test_KErrNone(timeoutStatus.Int());
   780 	
   781 	CLOSE_AND_WAIT(gChunk);
   782 	__KHEAP_MARKEND;
   783 	}
   784 
   785 TInt ThreadAtomic64Flush(TAny*)
   786 	{
   787 	TInt64 seed = 0x33333333;
   788 	FOREVER
   789 		{
   790 		DPTest::FlushCache();
   791 		User::After(Math::Rand(seed) & 0x48);
   792 		}
   793 	}
   794 
   795 enum TAtomic64Test
   796 	{
   797 	EAtomic64Add,
   798 	EAtomic64Logic,
   799 	EAtomic64Cas,
   800 	EAtomic64Steps,
   801 	};
   802 
   803 struct SAtomic64Args
   804 	{
   805 	TUint iIters;
   806 	TUint64* iData;
   807 	TInt iIncs;
   808 	TUint iClears[64];
   809 	TUint iSets[64];
   810 	};
   811 
   812 
   813 TInt ThreadAtomic64Cas(TAny* aArgs)
   814 	{
   815 	SAtomic64Args& args = *(SAtomic64Args*)aArgs;
   816 	for (TUint i = 0; i < args.iIters; i++)
   817 		{
   818 		TUint64 setMask = UI64LIT(0xffffffffffffffff);
   819 		TUint64 clrMask = 0;
   820 		if (__e32_atomic_cas_ord64(args.iData, &setMask, clrMask))
   821 			args.iClears[0]++;
   822 		// Undo any clearing of setMask which will happen if iData is 0.
   823 		setMask = UI64LIT(0xffffffffffffffff);
   824 		if (__e32_atomic_cas_ord64(args.iData, &clrMask, setMask))
   825 			args.iSets[0]++;
   826 		}
   827 	return KErrNone;
   828 	}
   829 
   830 
   831 TInt ThreadAtomic64Logic(TAny* aArgs)
   832 	{
   833 	TInt r = KErrNone;
   834 	SAtomic64Args& args = *(SAtomic64Args*)aArgs;
   835 	for(TUint i = 0; i < args.iIters; i++)
   836 		{
   837 		TUint bitNo = (i & 0x3f);
   838 		TUint64 bitMask = ((TUint64)1) << bitNo;
   839 		TUint64 andMask = ~bitMask;
   840 
   841 		TUint64 old = __e32_atomic_and_ord64(args.iData, andMask);
   842 		if (old & bitMask)
   843 			args.iClears[bitNo]++;
   844 
   845 		old = __e32_atomic_ior_ord64(args.iData, bitMask);
   846 		if (!(old & bitMask))
   847 			args.iSets[bitNo]++;
   848 
   849 		old = __e32_atomic_xor_ord64(args.iData, bitMask);
   850 		if (old & bitMask)
   851 			args.iClears[bitNo]++;
   852 		else
   853 			args.iSets[bitNo]++;
   854 
   855 		old = __e32_atomic_axo_ord64(args.iData, UI64LIT(0xffffffffffffffff), bitMask);
   856 		if (old & bitMask)
   857 			args.iClears[bitNo]++;
   858 		else
   859 			args.iSets[bitNo]++;
   860 		
   861 		}
   862 	return r;
   863 	}
   864 
   865 
   866 TInt ThreadAtomic64Add(TAny* aArgs)
   867 	{
   868 	TInt r = KErrNone;
   869 	SAtomic64Args& args = *(SAtomic64Args*)aArgs;
   870 	for(TUint i = 0; i < args.iIters; i++)
   871 		{
   872 		TUint64 old = __e32_atomic_add_ord64(args.iData, 1);
   873 		args.iIncs += 1;
   874 		old = __e32_atomic_tau_ord64(args.iData, 1000, 1, 2);
   875 		args.iIncs += (old >= 1000)? 1 : 2;
   876 		old = __e32_atomic_tas_ord64(args.iData, 1000, 1, -1);
   877 		args.iIncs += (old >= 1000)? 1 : -1;
   878 		}
   879 	return r;
   880 	}
   881 
   882 
   883 void TestAtomic64()
   884 	{
   885 	CreatePagedChunk(sizeof(TUint64));
   886 	TUint64* data = (TUint64*)gChunk.Base();
   887 
   888 	const TUint KThreads = 25;
   889 	RThread threads[KThreads];
   890 	TRequestStatus stats[KThreads];
   891 	SAtomic64Args* args = new SAtomic64Args[KThreads];
   892 	test_NotNull(args);
   893 
   894 	for (TInt testStep = EAtomic64Add; testStep < EAtomic64Steps; testStep++)
   895 		{
   896 		switch (testStep)
   897 			{
   898 			case EAtomic64Add:
   899 				test.Next(_L("Test 64-bit atomic addition operations"));
   900 					break;
   901 			case EAtomic64Logic:
   902 				test.Next(_L("Test 64-bit atomic logic operations"));
   903 				break;
   904 			case EAtomic64Cas:
   905 				test.Next(_L("Test 64-bit atomic cas operations"));
   906 				break;
   907 			}
   908 		*data = 0;
   909 		RThread threadFlush;
   910 		test_KErrNone(threadFlush.Create(_L("ThreadAtomicFlush"), ThreadAtomic64Flush, gPageSize, NULL, NULL));
   911 		TRequestStatus status1;
   912 		threadFlush.Logon(status1);
   913 		threadFlush.SetPriority(EPriorityAbsoluteHigh);
   914 
   915 		memclr(args, sizeof(SAtomic64Args)*KThreads);
   916 		TUint i = 0;
   917 		for (; i < KThreads; i++)
   918 			{
   919 			args[i].iIters = 10000;
   920 			args[i].iData = data;
   921 			switch (testStep)
   922 				{
   923 				case EAtomic64Add:
   924 					test_KErrNone(threads[i].Create(KNullDesC, ThreadAtomic64Add, gPageSize, NULL, (TAny*)&args[i]));
   925 					break;
   926 				case EAtomic64Logic:
   927 					test_KErrNone(threads[i].Create(KNullDesC, ThreadAtomic64Logic, gPageSize, NULL, (TAny*)&args[i]));
   928 					break;
   929 				case EAtomic64Cas:
   930 					test_KErrNone(threads[i].Create(KNullDesC, ThreadAtomic64Cas, gPageSize, NULL, (TAny*)&args[i]));
   931 					break;
   932 				}
   933 			threads[i].Logon(stats[i]);
   934 			}
   935 		threadFlush.Resume();
   936 		for (i = 0; i < KThreads; i++)
   937 			{
   938 			threads[i].Resume();
   939 			}
   940 
   941 		// Wait for add threads to complete and kill flushing thread.
   942 		for (i = 0; i < KThreads; i++)
   943 			{
   944 			User::WaitForRequest(stats[i]);
   945 			test_KErrNone(stats[i].Int());
   946 			}
   947 		threadFlush.Kill(KErrNone);
   948 		User::WaitForRequest(status1);
   949 		test_KErrNone(status1.Int());
   950 		TInt64 expected = 0;
   951 		switch (testStep)
   952 			{
   953 			case EAtomic64Add:
   954 				{
   955 				for (TUint i = 0; i < KThreads; i++)
   956 					{
   957 					threads[i].Close();
   958 					expected += args[i].iIncs;
   959 					}
   960 				break;
   961 				}
   962 			case EAtomic64Logic:
   963 				{
   964 				TUint totalSets[64];
   965 				TUint totalClears[64];
   966 				memclr(totalSets, sizeof(TUint)*64);
   967 				memclr(totalClears, sizeof(TUint)*64);
   968 				for (TUint i = 0; i < KThreads; i++)
   969 					{
   970 					threads[i].Close();
   971 					for (TUint j = 0; j < 64; j++)
   972 						{
   973 						totalSets[j] += args[i].iSets[j];
   974 						totalClears[j] += args[i].iClears[j];
   975 						}
   976 					}
   977 				for (TUint j = 0; j < 64; j++)
   978 					{
   979 					TUint64 bitMask = 1 << j;
   980 					if (totalSets[j] > totalClears[j])
   981 						{
   982 						test_Equal(totalSets[j] - 1, totalClears[j]);
   983 						expected |= bitMask;
   984 						}
   985 					else
   986 						{// Can only clear a bit if it was previously set.
   987 						test_Equal(totalClears[j], totalSets[j]);
   988 						}
   989 					}
   990 				break;
   991 				}
   992 			case EAtomic64Cas:
   993 				{
   994 				TUint totalSets = 0;
   995 				TUint totalClears = 0;
   996 				for (TUint i = 0; i < KThreads; i++)
   997 					{
   998 					threads[i].Close();
   999 					totalSets += args[i].iSets[0];
  1000 					totalClears += args[i].iClears[0];
  1001 					}
  1002 				if (totalSets > totalClears)
  1003 					{
  1004 					test_Equal(totalSets - 1, totalClears);
  1005 					expected = UI64LIT(0xffffffffffffffff);
  1006 					}
  1007 				else
  1008 					{// Can only clear a word if it was previously set.
  1009 					test_Equal(totalClears, totalSets);
  1010 					}
  1011 				break;
  1012 				}
  1013 			}
  1014 		test_Equal(expected, *data);
  1015 		CLOSE_AND_WAIT(threadFlush);
  1016 		}
  1017 	delete[] args;
  1018 	CLOSE_AND_WAIT(gChunk);
  1019 	}
  1020 
  1021 
  1022 //
  1023 // soak test for writeable paged code...
  1024 //
  1025 
  1026 const TUint KCodeStride = 20; // spacing between generated code
  1027 
  1028 void CodeStart(TUint8* aCode, TUint8* aTarget, TUint32 aInit)
  1029 	{
  1030 #if defined(__CPU_X86)
  1031 	aCode[0] = 0xb8; *(TUint32*)&(aCode[1]) = aInit;				// mov eax,aInit
  1032 	aCode[5] = 0xe9; *(TUint32*)&(aCode[6]) = aTarget-(aCode+10);	// jmp aTarget
  1033 	__ASSERT_COMPILE(KCodeStride>=10);
  1034 
  1035 #elif defined(__CPU_ARM)
  1036 	*(TUint32*)&(aCode[0]) = 0xe59f0000;			// ldr r0, [pc, #0]
  1037 	TInt32 offset = (aTarget-aCode-4-8)/4;
  1038 	if(offset&0xff000000u)
  1039 		{
  1040 		offset ^= 0xff000000u;
  1041 		test_Equal(0,offset&0xff000000u);
  1042 		}
  1043 	*(TUint32*)&(aCode[4]) = 0xea000000|offset;		// b aTarget
  1044 	*(TUint32*)&(aCode[8]) = aInit;					// dcd aInit
  1045 	__ASSERT_COMPILE(KCodeStride>=12);
  1046 
  1047 #else
  1048 #error Unknown CPU
  1049 #endif
  1050 	}
  1051 
  1052 
  1053 void CodeStep(TUint8* aCode, TUint8* aTarget, TUint32 aAdd)
  1054 	{
  1055 #if defined(__CPU_X86)
  1056 	aCode[0] = 0xd1; aCode[1] = 0xc0;								// rol eax, 1
  1057 	aCode[2] = 0x05; *(TUint32*)&(aCode[3]) = aAdd;					// add eax, aAdd
  1058 	aCode[7] = 0xe9; *(TUint32*)&(aCode[8]) = aTarget-(aCode+12);	// jmp aTarget
  1059 	__ASSERT_COMPILE(KCodeStride>=12);
  1060 
  1061 #elif defined(__CPU_ARM)
  1062 	*(TUint32*)&(aCode[0]) = 0xe1a00fe0;			// ror r0, r0, #31
  1063 	*(TUint32*)&(aCode[4]) = 0xe59f1004;			// ldr r1, [pc, #4]
  1064 	*(TUint32*)&(aCode[8]) = 0xe0800001;			// add r0, r0, r1
  1065 	TInt32 offset = (aTarget-aCode-12-8)/4;
  1066 	if(offset&0xff000000u)
  1067 		{
  1068 		offset ^= 0xff000000u;
  1069 		test_Equal(0,offset&0xff000000u);
  1070 		}
  1071 	*(TUint32*)&(aCode[12]) = 0xea000000|offset;	// b aTarget
  1072 	*(TUint32*)&(aCode[16]) = aAdd;					// dcd aAdd
  1073 	__ASSERT_COMPILE(KCodeStride>=20);
  1074 
  1075 #else
  1076 #error Unknown CPU
  1077 #endif
  1078 	}
  1079 
  1080 
  1081 void CodeEnd(TUint8* aCode)
  1082 	{
  1083 #if defined(__CPU_X86)
  1084 	aCode[0] = 0xc3;						// ret
  1085 	__ASSERT_COMPILE(KCodeStride>=1);
  1086 
  1087 #elif defined(__CPU_ARM)
  1088 	*(TUint32*)&(aCode[0]) = 0xe12fff1e;	// bx lr
  1089 	__ASSERT_COMPILE(KCodeStride>=4);
  1090 
  1091 #else
  1092 #error Unknown CPU
  1093 #endif
  1094 	}
  1095 
  1096 
  1097 void TestExecutableMemory()
  1098 	{
  1099 	__KHEAP_MARK;
  1100 
  1101 #if defined(__CPU_ARM)
  1102 	const TUint KMaxChunkSize = 31*1024*1024; // ARM branch instruction limit
  1103 #else
  1104 	const TUint KMaxChunkSize = 1024*1024*1024; // 1GB
  1105 #endif
  1106 	const TUint KMaxPages = KMaxChunkSize/gPageSize;
  1107 	TUint sizeInPages = gMaxCacheSize*2;
  1108 	if(sizeInPages>KMaxPages)
  1109 		sizeInPages = KMaxPages;
  1110 
  1111 	// create code chunk...
  1112 	test.Start(_L("Create code chunk"));
  1113 	TChunkCreateInfo createInfo;
  1114 	TInt size = sizeInPages * gPageSize;
  1115 	createInfo.SetCode(size, size);
  1116 	createInfo.SetPaging(TChunkCreateInfo::EPaged);
  1117 	createInfo.SetClearByte(0);
  1118 	RChunk chunk;
  1119 	test_KErrNone(chunk.Create(createInfo));
  1120 	test(chunk.IsPaged()); // this is only ever called if data paging is supported
  1121 	TUint8* base = chunk.Base();
  1122 
  1123 	// create code path through the pages in the chunk with quadratic distribution...
  1124 	test.Next(_L("Weave path"));
  1125 	TInt pathLength = 0;
  1126 	const TUint maxStepsPerPage = gPageSize/KCodeStride;
  1127 	const TInt maxPathLength = sizeInPages*maxStepsPerPage;
  1128 	TUint8** path = (TUint8**)User::Alloc(maxPathLength*sizeof(TUint8*));
  1129 	test(path!=0);
  1130 	for(TUint page=0; page<sizeInPages; ++page)
  1131 		{
  1132 		TUint step = (maxStepsPerPage-1)*(page*page)/(sizeInPages*sizeInPages)+1;
  1133 		do path[pathLength++] = base+page*gPageSize+step*KCodeStride;
  1134 		while(--step);
  1135 		}
  1136 	TUint32 rand = 0x12345678;
  1137 	for(TUint scramble=pathLength*4; scramble>0; --scramble)
  1138 		{
  1139 		// swap random pair of entries on path...
  1140 		TUint i = (TUint)(TUint64(TUint64(rand)*TUint64(pathLength))>>32);
  1141 		rand = rand*69069+1;
  1142 		TUint j = (TUint)(TUint64(TUint64(rand)*TUint64(pathLength))>>32);
  1143 		rand = rand*69069+1;
  1144 		TUint8* t = path[i];
  1145 		path[i] = path[j];
  1146 		path[j] = t;
  1147 		}
  1148 
  1149 	// write code to generated path...
  1150 	test.Next(_L("Write code"));
  1151 	TUint32 a = 0;
  1152 	TUint32 (*code)() = (TUint32 (*)())path[pathLength-1];
  1153 	CodeStart(path[pathLength-1],path[pathLength-2],a);
  1154 	while(--pathLength>1)
  1155 		{
  1156 		rand = rand*69069+1;
  1157 		CodeStep(path[pathLength-1],path[pathLength-2],rand);
  1158 		a = (a<<1)+(a>>31);
  1159 		a += rand;
  1160 		}
  1161 	CodeEnd(path[0]);
  1162 	--pathLength;
  1163 	test_Equal(0,pathLength);
  1164 	test.Next(_L("IMB"));
  1165 	User::IMB_Range(base,base+chunk.Size());
  1166 
  1167 	// run code...
  1168 	TMediaPagingStats dummy=EMediaPagingStatsRomAndCode;
  1169 	PagingInfo::ResetBenchmarks(-1, dummy);	// Don't worry about locmedia stats.
  1170 	test.Next(_L("Execute code"));
  1171 	TUint32 result = code();
  1172 	test_Equal(a,result);
  1173 	PagingInfo::PrintBenchmarks(-1, dummy);	// Don't worry about locmedia stats.
  1174 
  1175 	// cleanup...
  1176 	test.Next(_L("Cleanup"));
  1177 	User::Free(path);
  1178 	CLOSE_AND_WAIT(chunk);
  1179 
  1180 	test.End();
  1181 
  1182 	UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0);
  1183 	__KHEAP_MARKEND;
  1184 	}
  1185 
  1186 
  1187 
  1188 TInt E32Main()
  1189 	{
  1190 	test_KErrNone(UserHal::PageSizeInBytes(gPageSize));
  1191 	
  1192 	if (User::CommandLineLength() != 0)
  1193 		return RunSoakProcess();
  1194 	
  1195 	test.Title();
  1196 	test_KErrNone(GetGlobalPolicies());
  1197 
  1198 	test.Start(_L("Test HAL APIs"));
  1199 	TestHal();
  1200 
  1201 	if (gDataPagingSupported)
  1202 		{
  1203 		test.Next(_L("Test reading and writing to a single page"));
  1204 		TestOnePage();
  1205 
  1206 		test.Next(_L("Test 64-bit atomic operations are atomic with paged out data"));
  1207 		TestAtomic64();
  1208 
  1209 		test.Next(_L("Test interaction between decommit and steal"));
  1210 		TestDecommitAndStealInteraction(10);
  1211 
  1212 		test.Next(_L("Test killing a thread while it's paging in"));
  1213 		TestKillThread(PageInThreadFunc, 200);
  1214 				
  1215 		test.Next(_L("Test killing a thread while it's paging out"));
  1216 		TestKillThread(PageOutThreadFunc, 200);
  1217 		
  1218 		test.Next(_L("Test executable memory"));
  1219 		TestExecutableMemory();
  1220 
  1221 		test.Next(_L("Soak tests"));
  1222 		DPTest::FlushCache();
  1223 
  1224 		test.Next(_L("Soak test: change maximum cache size to minimal"));
  1225 		TUint cacheOriginalMin = 0;
  1226 		TUint cacheOriginalMax = 0;
  1227 		TUint cacheCurrentSize = 0;
  1228 		//store original values
  1229 		DPTest::CacheSize(cacheOriginalMin, cacheOriginalMax, cacheCurrentSize);
  1230 		gMaxCacheSize = 256;
  1231 		gMinCacheSize = 64;
  1232 		test_KErrNone(DPTest::SetCacheSize(gMinCacheSize * gPageSize, gMaxCacheSize * gPageSize));
  1233 
  1234 		for (TUint totalThreads = 1 ; totalThreads <= 64 ; totalThreads *= 4)
  1235 			{
  1236 			for (TUint processes = 1 ; processes <= 16 && processes <= totalThreads ; processes *= 4)
  1237 				{
  1238 				TUint threads = totalThreads / processes;
  1239 				for (TUint pages = gMaxCacheSize / 2 ; pages <= gMaxCacheSize * 2 ; pages *= 2)
  1240 					{
  1241 					for (TUint pin = 0 ; pin <= 1 ; ++pin)
  1242 						{
  1243 						test.Printf(_L("processes=%d threads=%d pages=%d maxcachesize=%d pin=%d\r\n"),processes, threads, pages, gMaxCacheSize,pin);
  1244 						SoakTest(processes, threads, pages, pin, 3);
  1245 						}
  1246 					}
  1247 				}
  1248 			}
  1249 
  1250 			//Reset the cache size to normal
  1251 			test.Next(_L("Soak test: Reset cache size to normal"));
  1252 			test_KErrNone(DPTest::SetCacheSize(cacheOriginalMin, cacheOriginalMax)); 
  1253 		}
  1254 
  1255 	test.End();
  1256 	return 0;
  1257 	}