kerneltest/e32test/demandpaging/t_thrash.cpp
branchRCL_3
changeset 26 c734af59ce98
parent 6 0173bcd7697c
child 28 5b5d147c7838
equal deleted inserted replaced
24:41f0cfe18c80 26:c734af59ce98
    25 #include <e32panic.h>
    25 #include <e32panic.h>
    26 #include "u32std.h"
    26 #include "u32std.h"
    27 #include <e32msgqueue.h>
    27 #include <e32msgqueue.h>
    28 #include <e32atomics.h>
    28 #include <e32atomics.h>
    29 #include <e32math.h>
    29 #include <e32math.h>
       
    30 #include <hal.h>
    30 
    31 
    31 #include "t_dpcmn.h"
    32 #include "t_dpcmn.h"
    32 #include "../mmu/mmudetect.h"
    33 #include "../mmu/mmudetect.h"
    33 #include "../mmu/d_memorytest.h"
    34 #include "../mmu/d_memorytest.h"
    34 #include "../mmu/t_codepaging_dll.h"
    35 #include "../mmu/t_codepaging_dll.h"
    37 
    38 
    38 volatile TBool gRunThrashTest = EFalse;
    39 volatile TBool gRunThrashTest = EFalse;
    39 
    40 
    40 _LIT(KChunkName, "t_thrash chunk");
    41 _LIT(KChunkName, "t_thrash chunk");
    41 
    42 
    42 class TRandom
    43 class TPRNG 
    43 	{
    44 	{
    44 public:
    45 public:
    45 	TRandom();
    46 	TPRNG();
    46 	TUint32 Next();
    47 	TUint32 IntRand();
       
    48 	TReal FloatRand();
    47 
    49 
    48 private:
    50 private:
    49 	enum
    51 	enum
    50 		{
    52 		{
    51 		KA = 1664525,
    53 		KA = 1664525,
    52 		KB = 1013904223
    54 		KB = 1013904223
    53 		};
    55 		};
    54 	TUint32 iV;
    56 	TUint32 iV;
    55 	};
    57 	};
    56 
    58 
    57 TRandom::TRandom()
    59 TPRNG::TPRNG()
    58 	{
    60 	{
    59 	iV = (TUint32)this + RThread().Id() + User::FastCounter() + 23;
    61 	iV = (TUint32)this + RThread().Id() + User::FastCounter() + 23;
    60 	}
    62 	}
    61 
    63 
    62 TUint32 TRandom::Next()
    64 TUint32 TPRNG::IntRand()
    63 	{
    65 	{
    64 	iV = KA * iV + KB;
    66 	iV = KA * iV + KB;
    65 	return iV;
    67 	return iV;
       
    68 	}
       
    69 
       
    70 TReal TPRNG::FloatRand()
       
    71 	{
       
    72 	return (TReal)IntRand() / KMaxTUint32;
       
    73 	}
       
    74 
       
    75 class TRandom
       
    76 	{
       
    77 public:
       
    78 	virtual ~TRandom() { }
       
    79 	virtual TUint32 Next() = 0;
       
    80 	};
       
    81 
       
    82  class TUniformRandom : public TRandom
       
    83 	{
       
    84 public:
       
    85 	void SetParams(TUint aMax) { iMax = aMax; }
       
    86 	virtual TUint32 Next();
       
    87 
       
    88 private:
       
    89 	TPRNG iRand;
       
    90 	TUint iMax;
       
    91 	};
       
    92 
       
    93 TUint32 TUniformRandom::Next()
       
    94 	{
       
    95 	return iRand.IntRand() % iMax;
       
    96 	}
       
    97 
       
    98 class TNormalRandom : public TRandom
       
    99 	{
       
   100 public:
       
   101 	void SetParams(TInt aMax, TInt aSd);
       
   102 	virtual TUint32 Next();
       
   103 
       
   104 private:
       
   105 	TUint32 GetNext();
       
   106 
       
   107 private:
       
   108 	TPRNG iRand;
       
   109 	TInt iMax;
       
   110 	TInt iExpectation;
       
   111 	TInt iSd;
       
   112 	TUint32 iCached;
       
   113 	};
       
   114 
       
   115 void TNormalRandom::SetParams(TInt aMax, TInt aSd)
       
   116 	{
       
   117 	iMax = aMax;
       
   118 	iExpectation = aMax / 2;
       
   119 	iSd = aSd;
       
   120 	iCached = KMaxTUint32;
       
   121 	}
       
   122 
       
   123 TUint32 TNormalRandom::Next()
       
   124 	{
       
   125 	TUint32 r;
       
   126 	do
       
   127 		{
       
   128 		r = GetNext();
       
   129 		}
       
   130 	while (r > (TUint)iMax);
       
   131 	return r;
       
   132 	}
       
   133 
       
   134 TUint32 TNormalRandom::GetNext()
       
   135 	{
       
   136 	if (iCached != KMaxTUint32)
       
   137 		{
       
   138 		TUint32 r = iCached;
       
   139 		iCached = KMaxTUint32;
       
   140 		return r;
       
   141 		}
       
   142 	
       
   143 	// box-muller transform
       
   144 	// from http://www.taygeta.com/random/gaussian.html
       
   145 
       
   146 	TReal x1, x2, w, ln_w, y1, y2;
       
   147 	do
       
   148 		{
       
   149 		x1 = 2.0 * iRand.FloatRand() - 1.0;
       
   150 		x2 = 2.0 * iRand.FloatRand() - 1.0;
       
   151 		w = x1 * x1 + x2 * x2;
       
   152 		}
       
   153 	while ( w >= 1.0 );
       
   154 
       
   155 	TInt r = Math::Ln(ln_w, w);
       
   156 	__ASSERT_ALWAYS(r == KErrNone, User::Invariant());
       
   157 	w = (-2.0 * ln_w ) / w;
       
   158 	TReal w2;
       
   159 	r = Math::Sqrt(w2, w);
       
   160 	__ASSERT_ALWAYS(r == KErrNone, User::Invariant());
       
   161 	y1 = x1 * w2;
       
   162 	y2 = x2 * w2;
       
   163 
       
   164 	y1 = y1 * iSd + iExpectation;
       
   165 	y2 = y2 * iSd + iExpectation;
       
   166 
       
   167 	iCached = (TUint32)y2;
       
   168 
       
   169 	return (TUint32)y1;
       
   170 	}
       
   171 
       
   172 static TBool BenchmarksSupported = EFalse;
       
   173 static TReal BenchmarkMultiplier;
       
   174 
       
   175 static TInt InitBenchmarks()
       
   176 	{
       
   177 	BenchmarksSupported = UserSvr::HalFunction(EHalGroupVM, EVMHalResetPagingBenchmark, (TAny*)EPagingBmReadRomPage, NULL) == KErrNone;
       
   178 	if (!BenchmarksSupported)
       
   179 		return KErrNone;
       
   180 	
       
   181 	TInt freq = 0;
       
   182 	TInt r = HAL::Get(HAL::EFastCounterFrequency, freq);
       
   183 	if (r != KErrNone)
       
   184 		return r;
       
   185 	BenchmarkMultiplier = 1000000.0 / freq;
       
   186 	return KErrNone;
       
   187 	}
       
   188 
       
   189 static void ResetBenchmarks()
       
   190 	{
       
   191 	if (!BenchmarksSupported)
       
   192 		return;	
       
   193 	for (TInt i = 0 ; i < EMaxPagingBm ; ++i)
       
   194 		{
       
   195 		TInt r = UserSvr::HalFunction(EHalGroupVM, EVMHalResetPagingBenchmark, (TAny*)i, NULL);
       
   196 		if (r != KErrNone)
       
   197 			test.Printf(_L("Error resetting benchmark %d\n"), i);
       
   198 		test_KErrNone(r);
       
   199 		}
       
   200 	}
       
   201 
       
   202 static TInt GetBenchmark(TPagingBenchmark aBenchmark, TInt& aCountOut, TInt& aTotalTimeInMicrosOut)
       
   203 	{
       
   204 	
       
   205 	SPagingBenchmarkInfo info;
       
   206 	TInt r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetPagingBenchmark, (TAny*)aBenchmark, &info);
       
   207 	if (r!=KErrNone)
       
   208 		return r;
       
   209 	
       
   210 	aCountOut = info.iCount;
       
   211 	aTotalTimeInMicrosOut = (TInt)(info.iTotalTime * BenchmarkMultiplier);
       
   212 	return KErrNone;
       
   213 	}
       
   214 
       
   215 static TInt GetAllBenchmarks(TInt aTestLengthInSeconds, TInt aCountOut[EMaxPagingBm], TInt aTimeOut[EMaxPagingBm])
       
   216 	{
       
   217 	for (TInt i = 0 ; i < EMaxPagingBm ; ++i)
       
   218 		{
       
   219 		TInt count = 0;
       
   220 		TInt timeInMicros = 0;
       
   221 		TInt r = GetBenchmark((TPagingBenchmark)i, count, timeInMicros);
       
   222 		if (r != KErrNone)
       
   223 			return r;
       
   224 		
       
   225 		aCountOut[i] = count / aTestLengthInSeconds;
       
   226 		aTimeOut[i] = timeInMicros / aTestLengthInSeconds;
       
   227 		}
       
   228 	return KErrNone;	
    66 	}
   229 	}
    67 
   230 
    68 void CreatePagedChunk(TInt aSizeInPages)
   231 void CreatePagedChunk(TInt aSizeInPages)
    69 	{
   232 	{
    70 	test_Equal(0,gChunk.Handle());
   233 	test_Equal(0,gChunk.Handle());
    82 TUint32* PageBasePtr(TInt aPage)
   245 TUint32* PageBasePtr(TInt aPage)
    83 	{
   246 	{
    84 	return (TUint32*)(gChunk.Base() + (gPageSize * aPage));
   247 	return (TUint32*)(gChunk.Base() + (gPageSize * aPage));
    85 	}
   248 	}
    86 
   249 
       
   250 TInt EnsureSystemIdleThread(TAny*)
       
   251 	{
       
   252 	RThread::Rendezvous(KErrNone);
       
   253 	for (;;)
       
   254 		{
       
   255 		// Spin
       
   256 		}
       
   257 	}
       
   258 
       
   259 void EnsureSystemIdle()
       
   260 	{
       
   261 	const TInt KMaxWait = 60 * 1000000;
       
   262 	const TInt KSampleTime = 1 * 1000000;
       
   263 	const TInt KWaitTime = 5 * 1000000;
       
   264 	
       
   265 	test.Printf(_L("Waiting for system to become idle\n"));
       
   266 	TInt totalTime = 0;
       
   267 	TBool idle;
       
   268 	do
       
   269 		{	
       
   270 		RThread thread;
       
   271 		test_KErrNone(thread.Create(_L("EnsureSystemIdleThread"), EnsureSystemIdleThread, 1024, NULL, NULL));		
       
   272 		thread.SetPriority(EPriorityLess);
       
   273 		thread.Resume();
       
   274 
       
   275 		TRequestStatus status;
       
   276 		thread.Rendezvous(status);
       
   277 		User::WaitForRequest(status);
       
   278 		test_KErrNone(status.Int());
       
   279 
       
   280 		User::After(KSampleTime);
       
   281 		thread.Suspend();
       
   282 
       
   283 		TTimeIntervalMicroSeconds time;
       
   284 		test_KErrNone(thread.GetCpuTime(time));
       
   285 		TReal error = (100.0 * Abs(time.Int64() - KSampleTime)) / KSampleTime;
       
   286 		test.Printf(_L("    time == %ld, error == %f%%\n"), time.Int64(), error);
       
   287 
       
   288 		idle = error < 2.0;		
       
   289 		
       
   290 		thread.Kill(KErrNone);
       
   291 		thread.Logon(status);
       
   292 		User::WaitForRequest(status);
       
   293 		test_KErrNone(status.Int());
       
   294 		CLOSE_AND_WAIT(thread);
       
   295 		
       
   296 		if (!idle)
       
   297 			User::After(KWaitTime);		// Allow system to finish whatever it's doing
       
   298 
       
   299 		totalTime += KSampleTime + KWaitTime;
       
   300 		test(totalTime < KMaxWait);
       
   301 		}
       
   302 	while(!idle);
       
   303 	}
       
   304 
    87 enum TWorkload
   305 enum TWorkload
    88 	{
   306 	{
    89 	EWorkloadSequential,
   307 	EWorkloadSequential,
    90 	EWorkloadRandom,
   308 	EWorkloadUniformRandom,
    91 	EWorkloadShuffle
   309 	EWorkloadNormalRandom1,
       
   310 	EWorkloadNormalRandom2,
       
   311 	EWorkloadShuffle,
       
   312 
       
   313 	EMaxWorkloads
    92 	};
   314 	};
    93 
   315 
    94 struct SThrashTestArgs
   316 struct SThrashTestArgs
    95 	{
   317 	{
    96 	TInt iThreadGroup;
   318 	TInt iThreadGroup;
   103 
   325 
   104 TInt ThrashTestFunc(TAny* aArg)
   326 TInt ThrashTestFunc(TAny* aArg)
   105 	{
   327 	{
   106 	SThrashTestArgs* args = (SThrashTestArgs*)aArg;
   328 	SThrashTestArgs* args = (SThrashTestArgs*)aArg;
   107 
   329 
   108 	TRandom random;
   330 	TPRNG random;
       
   331 	TUniformRandom uniformRand;
       
   332 	TNormalRandom normalRand;
       
   333 
   109 	TInt startPage = args->iThreadGroup * args->iGroupSize;
   334 	TInt startPage = args->iThreadGroup * args->iGroupSize;
   110 	TInt* ptr = (TInt*)(args->iBasePtr + startPage * gPageSize);
   335 	TInt* ptr = (TInt*)(args->iBasePtr + startPage * gPageSize);
       
   336 
       
   337 	
   111 	switch (args->iWorkload)
   338 	switch (args->iWorkload)
   112 		{
   339 		{
   113 		case EWorkloadSequential:
   340 		case EWorkloadSequential:
   114 			while (gRunThrashTest)
   341 			while (gRunThrashTest)
   115 				{
   342 				{
   116 				TInt size = (args->iPageCount * gPageSize) / sizeof(TInt);
   343 				for (TUint i = 0 ;
   117 				for (TInt i = 0 ; i < size && gRunThrashTest ; ++i)
   344 					 gRunThrashTest && i < (args->iPageCount * gPageSize) / sizeof(TInt)  ;
       
   345 					 ++i)
   118 					{
   346 					{
   119 					ptr[i] = random.Next();
   347 					ptr[i] = 1;
   120 					__e32_atomic_add_ord64(&args->iAccesses, 1);
   348 					__e32_atomic_add_ord64(&args->iAccesses, 1);
   121 					}
   349 					}
   122 				}
   350 				}
   123 			break;
   351 			break;
   124 				
   352 				
   125 		case EWorkloadRandom:
   353 		case EWorkloadUniformRandom:
       
   354 		case EWorkloadNormalRandom1:
       
   355 		case EWorkloadNormalRandom2:
   126 			{
   356 			{
   127 			TInt acc = 0;
   357 			TInt acc = 0;
       
   358 			TInt oldSize = -1;
       
   359 			TUint32 writeMask = 0;
       
   360 			switch (args->iWorkload)
       
   361 				{
       
   362 				case EWorkloadUniformRandom:
       
   363 				case EWorkloadNormalRandom1:
       
   364 					writeMask = 0x80000000; break;
       
   365 				case EWorkloadNormalRandom2:
       
   366 					writeMask = 0xc0000000; break;
       
   367 				default: test(EFalse); break;
       
   368 				}
   128 			while (gRunThrashTest)
   369 			while (gRunThrashTest)
   129 				{
   370 				{
   130 				TInt size = (args->iPageCount * gPageSize) / sizeof(TInt);
   371 				TInt size = args->iPageCount;
   131 				for (TInt i = 0 ; i < size && gRunThrashTest ; ++i)
   372 				if (size != oldSize)
   132 					{
   373 					{
   133 					TUint32 rand = random.Next();
   374 					switch (args->iWorkload)
   134 					TInt action = rand >> 31;
   375 						{
   135 					TInt r = rand % size;
   376 						case EWorkloadUniformRandom:
   136 					if (action == 0)
   377 							uniformRand.SetParams(size); break;
   137 						acc += ptr[r];
   378 						case EWorkloadNormalRandom1:
   138 					else
   379 						case EWorkloadNormalRandom2:
   139 						ptr[r] = acc;
   380 							normalRand.SetParams(size, size / 8); break;
   140 					__e32_atomic_add_ord64(&args->iAccesses, 1);
   381 						default: test(EFalse); break;
       
   382 						}
       
   383 					oldSize = size;
   141 					}
   384 					}
       
   385 				
       
   386 				TInt page = args->iWorkload == EWorkloadUniformRandom ?
       
   387 					uniformRand.Next() : normalRand.Next();
       
   388 				TInt index = page * (gPageSize / sizeof(TInt));
       
   389 				TBool write = (random.IntRand() & writeMask) == 0;
       
   390 				if (write)
       
   391 					ptr[index] = acc;
       
   392 				else
       
   393 					acc += ptr[index];
       
   394 				__e32_atomic_add_ord64(&args->iAccesses, 1);
   142 				}
   395 				}
   143 			}
   396 			}
   144 			break;
   397 			break;
   145 			
   398 			
   146 		case EWorkloadShuffle:
   399 		case EWorkloadShuffle:
   147 			{
   400 			{
   148 			TInt i;
   401 			TInt i = 0;
   149 			while (gRunThrashTest)
   402 			while (gRunThrashTest)
   150 				{
   403 				{
   151 				TInt size = (args->iPageCount * gPageSize) / sizeof(TInt);
   404 				TInt size = (args->iPageCount * gPageSize) / sizeof(TInt);
   152 				for (i = 0 ; gRunThrashTest && i < (size - 1) ; ++i)
   405 				Mem::Swap(&ptr[i], &ptr[i + random.IntRand() % (size - i - 1) + 1], sizeof(TInt));
   153 					{
   406 				__e32_atomic_add_ord64(&args->iAccesses, 2);
   154 					Mem::Swap(&ptr[i], &ptr[i + random.Next() % (size - i - 1) + 1], sizeof(TInt));
   407 				++i;
   155 					__e32_atomic_add_ord64(&args->iAccesses, 2);
   408 				if (i >= size - 1)
   156 					}
   409 					i = 0;
   157 				}
   410 				}
   158 			}
   411 			}
   159 			break;
   412 			break;
   160 
   413 
   161 		default:
   414 		default:
   170 	RThread iThread;
   423 	RThread iThread;
   171 	TRequestStatus iStatus;
   424 	TRequestStatus iStatus;
   172 	SThrashTestArgs iArgs;
   425 	SThrashTestArgs iArgs;
   173 	};
   426 	};
   174 
   427 
   175 void ThrashTest(TInt aThreads,			// number of threads to run
   428 void ThrashTest(const TDesC& aTestName,	// name and description
       
   429 				TInt aThreads,			// number of threads to run
   176 				TBool aSharedData,		// whether all threads share the same data
   430 				TBool aSharedData,		// whether all threads share the same data
   177 				TWorkload aWorkload,
   431 				TWorkload aWorkload,
   178 				TInt aBeginPages,		// number of pages to start with for last/all threads
   432 				TInt aBeginPages,		// number of pages to start with for last/all threads
   179 				TInt aEndPages,			// number of pages to end with for last/all threads
   433 				TInt aEndPages,			// number of pages to end with for last/all threads
   180 				TInt aOtherPages)		// num of pages for other threads, or zero to use same value for all
   434 				TInt aOtherPages)		// num of pages for other threads, or zero to use same value for all
   181 	{
   435 	{
   182 	RDebug::Printf("\nPages Accesses     ThL");
   436 	const TInt KTestLengthInSeconds = 2;
   183 
   437 	
       
   438 	test.Next(_L("Thrash test"));
       
   439 	
   184 	DPTest::FlushCache();
   440 	DPTest::FlushCache();
   185 	User::After(1000000);
   441 	EnsureSystemIdle();
       
   442 
       
   443 	TInt i;
       
   444 	test.Printf(_L("Table: %S\n"), &aTestName);
       
   445 	test.Printf(_L("totalPages, totalAccesses, thrashLevel"));
       
   446 	if (BenchmarksSupported)
       
   447 		test.Printf(_L(", rejuveCount, rejuveTime, codePageInCount, codePageInTime, initCount, initTime, readCount, readTime, writePages, writeCount, writeTime"));
       
   448 	if (aThreads > 1)
       
   449 		{
       
   450 		for (TInt i = 0 ; i < aThreads ; ++i)
       
   451 			test.Printf(_L(", Thread%dPages, Thread%dAccesses"), i, i);
       
   452 		}
       
   453 	test.Printf(_L("\n"));
   186 
   454 
   187 	TInt pagesNeeded;
   455 	TInt pagesNeeded;
   188 	TInt maxPages = Max(aBeginPages, aEndPages);
   456 	TInt maxPages = Max(aBeginPages, aEndPages);
   189 	TInt groupSize = 0;
   457 	TInt groupSize = 0;
   190 	if (aSharedData)
   458 	if (aSharedData)
   206 	
   474 	
   207 	SThrashThreadData* threads = new SThrashThreadData[aThreads];
   475 	SThrashThreadData* threads = new SThrashThreadData[aThreads];
   208 	test_NotNull(threads);
   476 	test_NotNull(threads);
   209 	
   477 	
   210 	gRunThrashTest = ETrue;
   478 	gRunThrashTest = ETrue;
   211 	TInt pageCount = aBeginPages;
       
   212 	const TInt maxSteps = 30;
   479 	const TInt maxSteps = 30;
   213 	TInt step = aEndPages >= aBeginPages ? Max((aEndPages - aBeginPages) / maxSteps, 1) : Min((aEndPages - aBeginPages) / maxSteps, -1);
   480 	TInt step = aEndPages >= aBeginPages ? Max((aEndPages - aBeginPages) / maxSteps, 1) : Min((aEndPages - aBeginPages) / maxSteps, -1);
   214 	
   481 	TInt pageCount = aBeginPages - 5 * step; // first run ignored
   215 	TInt i;
   482 	
   216 	for (i = 0 ; i < aThreads ; ++i)
   483 	for (i = 0 ; i < aThreads ; ++i)
   217 		{
   484 		{
   218 		SThrashThreadData& thread = threads[i];
   485 		SThrashThreadData& thread = threads[i];
   219 		thread.iArgs.iThreadGroup = aSharedData ? 0 : i;
   486 		thread.iArgs.iThreadGroup = aSharedData ? 0 : i;
   220 		thread.iArgs.iGroupSize = groupSize;
   487 		thread.iArgs.iGroupSize = groupSize;
   240 				threads[i].iArgs.iPageCount = pageCount;
   507 				threads[i].iArgs.iPageCount = pageCount;
   241 			}
   508 			}
   242 		
   509 		
   243 		for (i = 0 ; i < aThreads ; ++i)
   510 		for (i = 0 ; i < aThreads ; ++i)
   244 			__e32_atomic_store_ord64(&threads[i].iArgs.iAccesses, 0);
   511 			__e32_atomic_store_ord64(&threads[i].iArgs.iAccesses, 0);
       
   512 		ResetBenchmarks();
   245 		
   513 		
   246 		User::After(2000000);		
   514 		User::After(KTestLengthInSeconds * 1000 * 1000);
       
   515 
   247 		TInt thrashLevel = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
   516 		TInt thrashLevel = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
   248 		test(thrashLevel >= 0 && thrashLevel <= 255);
   517 		test(thrashLevel >= 0 && thrashLevel <= 255);
   249 		
   518 		
   250 		TInt64 totalAccesses = 0;
   519 		TInt64 totalAccesses = 0;
   251 		TInt totalPages = 0;
   520 		TInt totalPages = 0;
   255 			if (aSharedData)
   524 			if (aSharedData)
   256 				totalPages = Max(totalPages, threads[i].iArgs.iPageCount);
   525 				totalPages = Max(totalPages, threads[i].iArgs.iPageCount);
   257 			else
   526 			else
   258 				totalPages += threads[i].iArgs.iPageCount;
   527 				totalPages += threads[i].iArgs.iPageCount;
   259 			}
   528 			}
   260 
   529 		TInt accessesPerSecond = (TInt)(totalAccesses / KTestLengthInSeconds);
   261 		test.Printf(_L("%5d %12ld %3d"), totalPages, totalAccesses, thrashLevel);
   530 
   262 		for (i = 0 ; i < aThreads ; ++i)
   531 		TBool warmingUp = (step > 0) ? pageCount < aBeginPages : pageCount > aBeginPages;
       
   532 		if (!warmingUp)
   263 			{
   533 			{
   264 			test.Printf(_L(" %5d %12ld"),
   534 			test.Printf(_L("%10d, %13d, %11.2f"), totalPages, accessesPerSecond, (TReal)thrashLevel / 255);
   265 						threads[i].iArgs.iPageCount,
   535 		
   266 						__e32_atomic_load_acq64(&threads[i].iArgs.iAccesses));
   536 			if (BenchmarksSupported)
   267 			test_Equal(KRequestPending, threads[i].iStatus.Int());
   537 				{
       
   538 				TInt benchmarkCount[EMaxPagingBm];
       
   539 				TInt benchmarkTime[EMaxPagingBm];
       
   540 				test_KErrNone(GetAllBenchmarks(KTestLengthInSeconds, benchmarkCount, benchmarkTime));
       
   541 
       
   542 				TInt otherPageInCount = benchmarkCount[EPagingBmReadRomPage] + benchmarkCount[EPagingBmReadCodePage];
       
   543 				TInt otherPageInTime = benchmarkTime[EPagingBmReadRomPage] + benchmarkTime[EPagingBmReadCodePage];
       
   544 		
       
   545 				TInt initCount = benchmarkCount[EPagingBmReadDataPage] - benchmarkCount[EPagingBmReadDataMedia];
       
   546 				TInt initTime = benchmarkTime[EPagingBmReadDataPage] - benchmarkTime[EPagingBmReadDataMedia];
       
   547 
       
   548 				test.Printf(_L(", %11d, %10d, %15d, %14d, %9d, %8d, %9d, %8d, %10d, %10d, %9d"),
       
   549 							benchmarkCount[EPagingBmRejuvenate], benchmarkTime[EPagingBmRejuvenate],
       
   550 							otherPageInCount, otherPageInTime,
       
   551 							initCount, initTime,
       
   552 							benchmarkCount[EPagingBmReadDataMedia], benchmarkTime[EPagingBmReadDataMedia],
       
   553 							benchmarkCount[EPagingBmWriteDataPage], 
       
   554 							benchmarkCount[EPagingBmWriteDataMedia], benchmarkTime[EPagingBmWriteDataMedia]);
       
   555 				}
       
   556 		
       
   557 			if (aThreads > 1)
       
   558 				{
       
   559 				for (i = 0 ; i < aThreads ; ++i)
       
   560 					{
       
   561 					test.Printf(_L(", %12d, %15ld"),
       
   562 								threads[i].iArgs.iPageCount,
       
   563 								__e32_atomic_load_acq64(&threads[i].iArgs.iAccesses));
       
   564 					test_Equal(KRequestPending, threads[i].iStatus.Int());
       
   565 					}
       
   566 				}
       
   567 			test.Printf(_L("\n"));
   268 			}
   568 			}
   269 		test.Printf(_L("\n"));
   569 
   270 
   570 		pageCount += step;
   271 		if (aEndPages >= aBeginPages ? pageCount >= aEndPages : pageCount < aEndPages)
   571 		if (aEndPages >= aBeginPages ? pageCount >= aEndPages : pageCount < aEndPages)
   272 			break;
   572 			break;
   273 		pageCount += step;
       
   274 		}
   573 		}
   275 	
   574 	
   276 	gRunThrashTest = EFalse;
   575 	gRunThrashTest = EFalse;
   277 	
   576 	
   278 	for (i = 0 ; i < aThreads ; ++i)
   577 	for (i = 0 ; i < aThreads ; ++i)
   283 		test_KErrNone(thread.iStatus.Int());
   582 		test_KErrNone(thread.iStatus.Int());
   284 		thread.iThread.Close();
   583 		thread.iThread.Close();
   285 		}
   584 		}
   286 
   585 
   287 	gChunk.Close();	
   586 	gChunk.Close();	
   288 	RDebug::Printf("\n");
   587 	test.Printf(_L("\n"));
   289 	}
   588 	}
   290 
   589 
   291 void TestThrashing()
   590 void TestThrashing()
   292 	{
   591 	{
   293 	TInt minPages = (3 * gMaxCacheSize) / 4 - 4;
   592 	TInt minPages = (3 * gMaxCacheSize) / 4 - 4;
   296 	TInt maxPages2 = (5 * gMaxCacheSize) / 8;
   595 	TInt maxPages2 = (5 * gMaxCacheSize) / 8;
   297 	TInt minPages4 = (3 * gMaxCacheSize) / 16 - 4;
   596 	TInt minPages4 = (3 * gMaxCacheSize) / 16 - 4;
   298 	TInt maxPages4 = (5 * gMaxCacheSize) / 16;
   597 	TInt maxPages4 = (5 * gMaxCacheSize) / 16;
   299 
   598 
   300 	// Single thread increasing in size
   599 	// Single thread increasing in size
   301 	test.Next(_L("Thrash test: single thread, sequential workload"));
   600 	ThrashTest(_L("single thread, sequential workload"),
   302 	ThrashTest(1, ETrue, EWorkloadSequential, minPages, maxPages, 0);
   601 			   1, ETrue, EWorkloadSequential, minPages, maxPages, 0);
   303 	
   602 	
   304 	test.Next(_L("Thrash test: single thread, random workload"));
   603 	ThrashTest(_L("single thread, random workload"),
   305 	ThrashTest(1, ETrue, EWorkloadRandom, minPages, maxPages, 0);
   604 			   1, ETrue, EWorkloadUniformRandom, minPages, maxPages, 0);
   306 	
   605 	
   307 	test.Next(_L("Thrash test: single thread, shuffle workload"));
   606 	ThrashTest(_L("single thread, shuffle workload"),
   308 	ThrashTest(1, ETrue, EWorkloadShuffle, minPages, maxPages, 0);
   607 			   1, ETrue, EWorkloadShuffle, minPages, maxPages, 0);
   309 
   608 
   310 	// Multiple threads with shared data, one thread incresing in size
   609 	// Multiple threads with shared data, one thread incresing in size
   311 	test.Next(_L("Thrash test: two threads with shared data, one thread increasing, random workload"));
   610 	ThrashTest(_L("two threads with shared data, one thread increasing, random workload"),
   312 	ThrashTest(2, ETrue, EWorkloadRandom, minPages, maxPages, minPages);
   611 			   2, ETrue, EWorkloadUniformRandom, minPages, maxPages, minPages);
   313 	
   612 	
   314 	test.Next(_L("Thrash test: four threads with shared data, one thread increasing, random workload"));
   613 	ThrashTest(_L("four threads with shared data, one thread increasing, random workload"),
   315 	ThrashTest(4, ETrue, EWorkloadRandom, minPages, maxPages, minPages);
   614 			   4, ETrue, EWorkloadUniformRandom, minPages, maxPages, minPages);
   316 
   615 
   317 	// Multiple threads with shared data, all threads incresing in size
   616 	// Multiple threads with shared data, all threads incresing in size
   318 	test.Next(_L("Thrash test: two threads with shared data, all threads increasing, random workload"));
   617 	ThrashTest(_L("two threads with shared data, all threads increasing, random workload"),
   319 	ThrashTest(2, ETrue, EWorkloadRandom, minPages, maxPages, 0);
   618 			   2, ETrue, EWorkloadUniformRandom, minPages, maxPages, 0);
   320 	
   619 	
   321 	test.Next(_L("Thrash test: four threads with shared data, all threads increasing, random workload"));
   620 	ThrashTest(_L("four threads with shared data, all threads increasing, random workload"),
   322 	ThrashTest(4, ETrue, EWorkloadRandom, minPages, maxPages, 0);
   621 			   4, ETrue, EWorkloadUniformRandom, minPages, maxPages, 0);
   323 	
   622 	
   324 	// Multiple threads with independent data, one thread incresing in size
   623 	// Multiple threads with independent data, one thread incresing in size
   325 	test.Next(_L("Thrash test: two threads with independent data, one thread increasing, random workload"));
   624 	ThrashTest(_L("two threads with independent data, one thread increasing, random workload"),
   326 	ThrashTest(2, EFalse, EWorkloadRandom, minPages2, maxPages2, gMaxCacheSize / 2);
   625 			   2, EFalse, EWorkloadUniformRandom, minPages2, maxPages2, gMaxCacheSize / 2);
   327 	
   626 	
   328 	test.Next(_L("Thrash test: four threads with independent data, one thread increasing, random workload"));
   627 	ThrashTest(_L("four threads with independent data, one thread increasing, random workload"),
   329 	ThrashTest(4, EFalse, EWorkloadRandom, minPages4, maxPages4, gMaxCacheSize / 4);
   628 			   4, EFalse, EWorkloadUniformRandom, minPages4, maxPages4, gMaxCacheSize / 4);
   330 	
   629 	
   331 	// Multiple threads with independant data, all threads incresing in size
   630 	// Multiple threads with independant data, all threads incresing in size
   332 	test.Next(_L("Thrash test: two threads with independent data, all threads increasing, random workload"));
   631 	ThrashTest(_L("two threads with independent data, all threads increasing, random workload"),
   333 	ThrashTest(2, EFalse, EWorkloadRandom, minPages2, maxPages2, 0);
   632 			   2, EFalse, EWorkloadUniformRandom, minPages2, maxPages2, 0);
   334 
   633 
   335 	test.Next(_L("Thrash test: four threads with independent data, all threads increasing, random workload"));
   634 	ThrashTest(_L("four threads with independent data, all threads increasing, random workload"),
   336 	ThrashTest(4, EFalse, EWorkloadRandom, minPages4, maxPages4, 0);
   635 			   4, EFalse, EWorkloadUniformRandom, minPages4, maxPages4, 0);
   337 
   636 
   338 	// Attempt to create thrash state where there is sufficient cache
   637 	// Attempt to create thrash state where there is sufficient cache
   339 	test.Next(_L("Thrash test: two threads with independent data, one threads decreasing, random workload"));
       
   340 	TInt halfCacheSize = gMaxCacheSize / 2;
   638 	TInt halfCacheSize = gMaxCacheSize / 2;
   341 	ThrashTest(2, EFalse, EWorkloadRandom, halfCacheSize + 10, halfCacheSize - 30, halfCacheSize);
   639 	ThrashTest(_L("two threads with independent data, one threads decreasing, random workload"),
       
   640 			   2, EFalse, EWorkloadUniformRandom, halfCacheSize + 10, halfCacheSize - 30, halfCacheSize);
       
   641 	}
       
   642 
       
   643 void TestDistribution(TRandom& aRandom, TInt aSamples)
       
   644 	{
       
   645 	TUint32* data = new TUint32[aSamples];
       
   646 	test_NotNull(data);
       
   647 
       
   648 	TInt i;
       
   649 	TReal mean = 0.0;
       
   650 	for (i = 0 ; i < aSamples ; ++i)
       
   651 		{
       
   652 		data[i] = aRandom.Next();
       
   653 		mean += (TReal)data[i] / aSamples;
       
   654 		}
       
   655 
       
   656 	TReal sum2 = 0.0;
       
   657 	for (i = 0 ; i < aSamples ; ++i)
       
   658 		{
       
   659 		TReal d = (TReal)data[i] - mean;
       
   660 		sum2 += d * d;
       
   661 		}
       
   662 	TReal variance = sum2 / (aSamples - 1);
       
   663 
       
   664 	test.Printf(_L("  mean == %f\n"), mean);
       
   665 	test.Printf(_L("  variance == %f\n"), variance);
       
   666 
       
   667 	delete [] data;
       
   668 	}
       
   669 
       
   670 void BenchmarkReplacement()
       
   671 	{
       
   672  	test.Next(_L("Test uniform distribution"));
       
   673 	TUniformRandom rand1;
       
   674 	rand1.SetParams(100);
       
   675 	TestDistribution(rand1, 10000);
       
   676 	
       
   677  	test.Next(_L("Test normal distribution"));	
       
   678 	TNormalRandom rand2;
       
   679 	rand2.SetParams(100, 25);
       
   680 	TestDistribution(rand2, 10000);
       
   681 
       
   682 	ThrashTest(_L("Thrash test: single thread, normal random workload 1"),
       
   683 			   1, ETrue, EWorkloadNormalRandom1, (2 * gMaxCacheSize) / 3, 2 * gMaxCacheSize, 0);
       
   684 	
       
   685 	ThrashTest(_L("Thrash test: single thread, normal random workload 2"),
       
   686 			   1, ETrue, EWorkloadNormalRandom2, (2 * gMaxCacheSize) / 3, 2 * gMaxCacheSize, 0);
       
   687 
       
   688 	ThrashTest(_L("Thrash test: single thread, uniform random workload"),
       
   689 			   1, ETrue, EWorkloadUniformRandom, (2 * gMinCacheSize) / 3, (3 * gMaxCacheSize) / 2, 0);
   342 	}
   690 	}
   343 
   691 
   344 void TestThrashHal()
   692 void TestThrashHal()
   345 	{			 
   693 	{			 
   346 	test.Next(_L("Test EVMHalSetThrashThresholds"));
   694 	test.Next(_L("Test EVMHalSetThrashThresholds"));
   367 	test_KErrNone(notifier.Logon(status));
   715 	test_KErrNone(notifier.Logon(status));
   368 	test_KErrNone(notifier.Logon(status));  // first logon completes immediately
   716 	test_KErrNone(notifier.Logon(status));  // first logon completes immediately
   369 	test_Equal(KRequestPending, status.Int());
   717 	test_Equal(KRequestPending, status.Int());
   370 	
   718 	
   371 	// stress system and check thrash level and notification
   719 	// stress system and check thrash level and notification
   372 	ThrashTest(1, ETrue, EWorkloadRandom, gMaxCacheSize * 2, gMaxCacheSize * 2 + 5, 0);
   720 	ThrashTest(_L("stress system"),
       
   721 			   1, ETrue, EWorkloadUniformRandom, gMaxCacheSize * 2, gMaxCacheSize * 2 + 5, 0);
   373 	r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
   722 	r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
   374 	test(r >= 0 && r <= 255);
   723 	test(r >= 0 && r <= 255);
   375 	test.Printf(_L("Thrash level == %d\n"), r);
   724 	test.Printf(_L("Thrash level == %d\n"), r);
   376 	test(r > 200);  // should indicate thrashing
   725 	test(r > 200);  // should indicate thrashing
   377 	test_Equal(EChangesThrashLevel, status.Int());
   726 
   378 	User::WaitForAnyRequest();
   727 	TBool gotThrashNotification = EFalse;
   379 
   728 	
   380 	// wait for system to calm down and check notification again
   729 	// wait for EChangesThrashLevel notification
   381 	test_KErrNone(notifier.Logon(status));
   730 	while(status.Int() != KRequestPending)
   382 	User::WaitForAnyRequest();
   731 		{
   383 	test_Equal(EChangesThreadDeath, status.Int());
   732 		gotThrashNotification = (status.Int() & EChangesThrashLevel) != 0;
   384 
   733 		User::WaitForAnyRequest();
   385 	test_KErrNone(notifier.Logon(status));
   734 		test_KErrNone(notifier.Logon(status));
       
   735 		User::After(1);		
       
   736 		}
       
   737 	test(gotThrashNotification);
       
   738 	
   386 	User::After(2000000);
   739 	User::After(2000000);
   387 	r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
   740 	r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
   388 	test(r >= 0 && r <= 255);
   741 	test(r >= 0 && r <= 255);
   389 	test.Printf(_L("Thrash level == %d\n"), r);
   742 	test.Printf(_L("Thrash level == %d\n"), r);
   390 	test(r <= 10);  // should indicate lightly loaded system
   743 	test(r <= 10);  // should indicate lightly loaded system
   391 	test_Equal(EChangesThrashLevel, status.Int());	
   744 
       
   745 	// wait for EChangesThrashLevel notification
       
   746 	gotThrashNotification = EFalse;
       
   747 	while(status.Int() != KRequestPending)
       
   748 		{
       
   749 		gotThrashNotification = (status.Int() & EChangesThrashLevel) != 0;
       
   750 		User::WaitForAnyRequest();
       
   751 		test_KErrNone(notifier.Logon(status));
       
   752 		User::After(1);		
       
   753 		}
       
   754 	test(gotThrashNotification);
       
   755 	test_KErrNone(notifier.LogonCancel());
   392 	User::WaitForAnyRequest();
   756 	User::WaitForAnyRequest();
       
   757 	notifier.Close();
   393 	}
   758 	}
   394 
   759 
   395 void TestThrashHalNotSupported()
   760 void TestThrashHalNotSupported()
   396 	{
   761 	{
   397 	test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0));
   762 	test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0));
   398 	test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, 0, 0));
   763 	test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, 0, 0));
   399 	}
   764 	}
   400 
   765 
       
   766 _LIT(KUsageMessage, "usage: t_thrash [ test ] [ thrashing ] [ benchmarks ]\n");
       
   767 
       
   768 enum TTestAction
       
   769 	{
       
   770 	EActionTest       = 1 << 0,
       
   771 	EActionThrashing  = 1 << 1,
       
   772 	EActionBenchmarks = 1 << 2
       
   773 	};
       
   774 
       
   775 void BadUsage()
       
   776 	{
       
   777 	test.Printf(KUsageMessage);
       
   778 	test(EFalse);
       
   779 	}
       
   780 
       
   781 TInt ParseCommandLine()
       
   782 	{
       
   783 	const TInt KMaxLineLength = 64;
       
   784 	
       
   785 	if (User::CommandLineLength() > KMaxLineLength)
       
   786 		BadUsage();
       
   787 	TBuf<KMaxLineLength> buffer;
       
   788 	User::CommandLine(buffer);
       
   789 
       
   790 	if (buffer == KNullDesC)
       
   791 		return EActionTest;
       
   792 	
       
   793 	TLex lex(buffer);
       
   794 	TInt result = 0;
       
   795 	while (!lex.Eos())
       
   796 		{
       
   797 		TPtrC word = lex.NextToken();
       
   798 		if (word == _L("test"))
       
   799 			result |= EActionTest;
       
   800 		else if (word == _L("thrashing"))
       
   801 			result |= EActionThrashing;
       
   802 		else if (word == _L("benchmarks"))
       
   803 			result |= EActionBenchmarks;
       
   804 		else
       
   805 			{
       
   806 			test.Printf(_L("bad token '%S'\n"), &word);
       
   807 			BadUsage();
       
   808 			}
       
   809 		}
       
   810 	
       
   811 	return result;
       
   812 	}
       
   813 
   401 TInt E32Main()
   814 TInt E32Main()
   402 	{
   815 	{
   403 	test.Title();
   816 	test.Title();
   404 	test.Start(_L("Test thrashing monitor"));
   817 	test.Start(_L("Test thrashing monitor"));
   405 
   818 	
       
   819 	test_KErrNone(InitBenchmarks());
       
   820 
       
   821 	TInt actions = ParseCommandLine();
       
   822 	
   406 	test_KErrNone(GetGlobalPolicies());
   823 	test_KErrNone(GetGlobalPolicies());
   407 
   824 
   408 	TUint cacheOriginalMin = 0;
   825 	TUint cacheOriginalMin = 0;
   409 	TUint cacheOriginalMax = 0;
   826 	TUint cacheOriginalMax = 0;
   410 	TUint cacheCurrentSize = 0;
       
   411 
   827 
   412 	if (gDataPagingSupported)
   828 	if (gDataPagingSupported)
   413 		{
   829 		{
   414 		test.Next(_L("Thrash test: change maximum cache size to minimal"));
   830 		test.Next(_L("Thrash test: change cache size to maximum 2Mb"));
   415 		//store original values
   831 		TUint cacheCurrentSize = 0;
   416 		DPTest::CacheSize(cacheOriginalMin, cacheOriginalMax, cacheCurrentSize);
   832 		DPTest::CacheSize(cacheOriginalMin, cacheOriginalMax, cacheCurrentSize);
   417 		gMaxCacheSize = 256;
   833 		gMinCacheSize = 512;
   418 		gMinCacheSize = 64;
   834 		gMaxCacheSize = 520;
   419 		test_KErrNone(DPTest::SetCacheSize(gMinCacheSize * gPageSize, gMaxCacheSize * gPageSize));
   835 		test_KErrNone(DPTest::SetCacheSize(gMinCacheSize * gPageSize, gMaxCacheSize * gPageSize));
   420 		}
   836 		}
   421 
   837 	
   422 	TBool flexibleMemoryModel = (MemModelAttributes() & EMemModelTypeMask) == EMemModelTypeFlexible;
   838 	if (actions & EActionTest)
   423 	if (flexibleMemoryModel)
   839 		{
   424 		TestThrashHal();
   840 		TBool flexibleMemoryModel = (MemModelAttributes() & EMemModelTypeMask) == EMemModelTypeFlexible;
   425 	else
   841 		if (flexibleMemoryModel)
   426 		TestThrashHalNotSupported();
   842 			TestThrashHal();
   427 	
   843 		else
   428 	if (gDataPagingSupported && User::CommandLineLength() > 0)
   844 			TestThrashHalNotSupported();
   429 		{		
   845 		}
       
   846 
       
   847 	if (actions & EActionThrashing)
       
   848 		{	
   430 		test.Next(_L("Extended thrashing tests"));
   849 		test.Next(_L("Extended thrashing tests"));
   431 		TestThrashing();
   850 		TestThrashing();
   432 		}
   851 		}
       
   852 	
       
   853 	if (actions & EActionBenchmarks)
       
   854 		{	
       
   855 		test.Next(_L("Benchmarking page replacement"));
       
   856 		BenchmarkReplacement();
       
   857 		}
       
   858 
   433 	if (gDataPagingSupported)
   859 	if (gDataPagingSupported)
   434 		{
   860 		{
   435 		//Reset the cache size to normal
       
   436 		test.Next(_L("Thrash test: Reset cache size to normal"));
   861 		test.Next(_L("Thrash test: Reset cache size to normal"));
   437 		test_KErrNone(DPTest::SetCacheSize(cacheOriginalMin, cacheOriginalMax));
   862 		test_KErrNone(DPTest::SetCacheSize(cacheOriginalMin, cacheOriginalMax));
   438 		}
   863 		}
   439 
   864 	
   440 	test.End();
   865 	test.End();
   441 	return 0;
   866 	return 0;
   442 	}
   867 	}