--- a/kerneltest/e32test/demandpaging/t_thrash.cpp Tue Feb 02 01:24:03 2010 +0200
+++ b/kerneltest/e32test/demandpaging/t_thrash.cpp Fri Apr 16 16:24:37 2010 +0300
@@ -27,6 +27,7 @@
#include <e32msgqueue.h>
#include <e32atomics.h>
#include <e32math.h>
+#include <hal.h>
#include "t_dpcmn.h"
#include "../mmu/mmudetect.h"
@@ -39,11 +40,12 @@
_LIT(KChunkName, "t_thrash chunk");
-class TRandom
+class TPRNG
{
public:
- TRandom();
- TUint32 Next();
+ TPRNG();
+ TUint32 IntRand();
+ TReal FloatRand();
private:
enum
@@ -54,17 +56,178 @@
TUint32 iV;
};
-TRandom::TRandom()
+TPRNG::TPRNG()
{
iV = (TUint32)this + RThread().Id() + User::FastCounter() + 23;
}
-TUint32 TRandom::Next()
+TUint32 TPRNG::IntRand()
{
iV = KA * iV + KB;
return iV;
}
+TReal TPRNG::FloatRand()
+ {
+ return (TReal)IntRand() / KMaxTUint32;
+ }
+
+class TRandom
+ {
+public:
+ virtual ~TRandom() { }
+ virtual TUint32 Next() = 0;
+ };
+
+ class TUniformRandom : public TRandom
+ {
+public:
+ void SetParams(TUint aMax) { iMax = aMax; }
+ virtual TUint32 Next();
+
+private:
+ TPRNG iRand;
+ TUint iMax;
+ };
+
+TUint32 TUniformRandom::Next()
+ {
+ return iRand.IntRand() % iMax;
+ }
+
+class TNormalRandom : public TRandom
+ {
+public:
+ void SetParams(TInt aMax, TInt aSd);
+ virtual TUint32 Next();
+
+private:
+ TUint32 GetNext();
+
+private:
+ TPRNG iRand;
+ TInt iMax;
+ TInt iExpectation;
+ TInt iSd;
+ TUint32 iCached;
+ };
+
+void TNormalRandom::SetParams(TInt aMax, TInt aSd)
+ {
+ iMax = aMax;
+ iExpectation = aMax / 2;
+ iSd = aSd;
+ iCached = KMaxTUint32;
+ }
+
+TUint32 TNormalRandom::Next()
+ {
+ TUint32 r;
+ do
+ {
+ r = GetNext();
+ }
+ while (r > (TUint)iMax);
+ return r;
+ }
+
+TUint32 TNormalRandom::GetNext()
+ {
+ if (iCached != KMaxTUint32)
+ {
+ TUint32 r = iCached;
+ iCached = KMaxTUint32;
+ return r;
+ }
+
+ // box-muller transform
+ // from http://www.taygeta.com/random/gaussian.html
+
+ TReal x1, x2, w, ln_w, y1, y2;
+ do
+ {
+ x1 = 2.0 * iRand.FloatRand() - 1.0;
+ x2 = 2.0 * iRand.FloatRand() - 1.0;
+ w = x1 * x1 + x2 * x2;
+ }
+ while ( w >= 1.0 );
+
+ TInt r = Math::Ln(ln_w, w);
+ __ASSERT_ALWAYS(r == KErrNone, User::Invariant());
+ w = (-2.0 * ln_w ) / w;
+ TReal w2;
+ r = Math::Sqrt(w2, w);
+ __ASSERT_ALWAYS(r == KErrNone, User::Invariant());
+ y1 = x1 * w2;
+ y2 = x2 * w2;
+
+ y1 = y1 * iSd + iExpectation;
+ y2 = y2 * iSd + iExpectation;
+
+ iCached = (TUint32)y2;
+
+ return (TUint32)y1;
+ }
+
+static TBool BenchmarksSupported = EFalse;
+static TReal BenchmarkMultiplier;
+
+static TInt InitBenchmarks()
+ {
+ BenchmarksSupported = UserSvr::HalFunction(EHalGroupVM, EVMHalResetPagingBenchmark, (TAny*)EPagingBmReadRomPage, NULL) == KErrNone;
+ if (!BenchmarksSupported)
+ return KErrNone;
+
+ TInt freq = 0;
+ TInt r = HAL::Get(HAL::EFastCounterFrequency, freq);
+ if (r != KErrNone)
+ return r;
+ BenchmarkMultiplier = 1000000.0 / freq;
+ return KErrNone;
+ }
+
+static void ResetBenchmarks()
+ {
+ if (!BenchmarksSupported)
+ return;
+ for (TInt i = 0 ; i < EMaxPagingBm ; ++i)
+ {
+ TInt r = UserSvr::HalFunction(EHalGroupVM, EVMHalResetPagingBenchmark, (TAny*)i, NULL);
+ if (r != KErrNone)
+ test.Printf(_L("Error resetting benchmark %d\n"), i);
+ test_KErrNone(r);
+ }
+ }
+
+static TInt GetBenchmark(TPagingBenchmark aBenchmark, TInt& aCountOut, TInt& aTotalTimeInMicrosOut)
+ {
+
+ SPagingBenchmarkInfo info;
+ TInt r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetPagingBenchmark, (TAny*)aBenchmark, &info);
+ if (r!=KErrNone)
+ return r;
+
+ aCountOut = info.iCount;
+ aTotalTimeInMicrosOut = (TInt)(info.iTotalTime * BenchmarkMultiplier);
+ return KErrNone;
+ }
+
+static TInt GetAllBenchmarks(TInt aTestLengthInSeconds, TInt aCountOut[EMaxPagingBm], TInt aTimeOut[EMaxPagingBm])
+ {
+ for (TInt i = 0 ; i < EMaxPagingBm ; ++i)
+ {
+ TInt count = 0;
+ TInt timeInMicros = 0;
+ TInt r = GetBenchmark((TPagingBenchmark)i, count, timeInMicros);
+ if (r != KErrNone)
+ return r;
+
+ aCountOut[i] = count / aTestLengthInSeconds;
+ aTimeOut[i] = timeInMicros / aTestLengthInSeconds;
+ }
+ return KErrNone;
+ }
+
void CreatePagedChunk(TInt aSizeInPages)
{
test_Equal(0,gChunk.Handle());
@@ -84,11 +247,70 @@
return (TUint32*)(gChunk.Base() + (gPageSize * aPage));
}
+TInt EnsureSystemIdleThread(TAny*)
+ {
+ RThread::Rendezvous(KErrNone);
+ for (;;)
+ {
+ // Spin
+ }
+ }
+
+void EnsureSystemIdle()
+ {
+ const TInt KMaxWait = 60 * 1000000;
+ const TInt KSampleTime = 1 * 1000000;
+ const TInt KWaitTime = 5 * 1000000;
+
+ test.Printf(_L("Waiting for system to become idle\n"));
+ TInt totalTime = 0;
+ TBool idle;
+ do
+ {
+ RThread thread;
+ test_KErrNone(thread.Create(_L("EnsureSystemIdleThread"), EnsureSystemIdleThread, 1024, NULL, NULL));
+ thread.SetPriority(EPriorityLess);
+ thread.Resume();
+
+ TRequestStatus status;
+ thread.Rendezvous(status);
+ User::WaitForRequest(status);
+ test_KErrNone(status.Int());
+
+ User::After(KSampleTime);
+ thread.Suspend();
+
+ TTimeIntervalMicroSeconds time;
+ test_KErrNone(thread.GetCpuTime(time));
+ TReal error = (100.0 * Abs(time.Int64() - KSampleTime)) / KSampleTime;
+ test.Printf(_L(" time == %ld, error == %f%%\n"), time.Int64(), error);
+
+ idle = error < 2.0;
+
+ thread.Kill(KErrNone);
+ thread.Logon(status);
+ User::WaitForRequest(status);
+ test_KErrNone(status.Int());
+ CLOSE_AND_WAIT(thread);
+
+ if (!idle)
+ User::After(KWaitTime); // Allow system to finish whatever it's doing
+
+ totalTime += KSampleTime + KWaitTime;
+ test(totalTime < KMaxWait);
+ }
+ while(!idle);
+ }
+
enum TWorkload
{
EWorkloadSequential,
- EWorkloadRandom,
- EWorkloadShuffle
+ EWorkloadUniformRandom,
+ EWorkloadNormalRandom1,
+ EWorkloadNormalRandom2,
+ EWorkloadShuffle,
+
+ EMaxWorkloads
};
struct SThrashTestArgs
@@ -105,55 +327,86 @@
{
SThrashTestArgs* args = (SThrashTestArgs*)aArg;
- TRandom random;
+ TPRNG random;
+ TUniformRandom uniformRand;
+ TNormalRandom normalRand;
+
TInt startPage = args->iThreadGroup * args->iGroupSize;
TInt* ptr = (TInt*)(args->iBasePtr + startPage * gPageSize);
+
+
switch (args->iWorkload)
{
case EWorkloadSequential:
while (gRunThrashTest)
{
- TInt size = (args->iPageCount * gPageSize) / sizeof(TInt);
- for (TInt i = 0 ; i < size && gRunThrashTest ; ++i)
+ for (TUint i = 0 ;
+ gRunThrashTest && i < (args->iPageCount * gPageSize) / sizeof(TInt) ;
+ ++i)
{
- ptr[i] = random.Next();
+ ptr[i] = 1;
__e32_atomic_add_ord64(&args->iAccesses, 1);
}
}
break;
- case EWorkloadRandom:
+ case EWorkloadUniformRandom:
+ case EWorkloadNormalRandom1:
+ case EWorkloadNormalRandom2:
{
TInt acc = 0;
+ TInt oldSize = -1;
+ TUint32 writeMask = 0;
+ switch (args->iWorkload)
+ {
+ case EWorkloadUniformRandom:
+ case EWorkloadNormalRandom1:
+ writeMask = 0x80000000; break;
+ case EWorkloadNormalRandom2:
+ writeMask = 0xc0000000; break;
+ default: test(EFalse); break;
+ }
while (gRunThrashTest)
{
- TInt size = (args->iPageCount * gPageSize) / sizeof(TInt);
- for (TInt i = 0 ; i < size && gRunThrashTest ; ++i)
+ TInt size = args->iPageCount;
+ if (size != oldSize)
{
- TUint32 rand = random.Next();
- TInt action = rand >> 31;
- TInt r = rand % size;
- if (action == 0)
- acc += ptr[r];
- else
- ptr[r] = acc;
- __e32_atomic_add_ord64(&args->iAccesses, 1);
+ switch (args->iWorkload)
+ {
+ case EWorkloadUniformRandom:
+ uniformRand.SetParams(size); break;
+ case EWorkloadNormalRandom1:
+ case EWorkloadNormalRandom2:
+ normalRand.SetParams(size, size / 8); break;
+ default: test(EFalse); break;
+ }
+ oldSize = size;
}
+
+ TInt page = args->iWorkload == EWorkloadUniformRandom ?
+ uniformRand.Next() : normalRand.Next();
+ TInt index = page * (gPageSize / sizeof(TInt));
+ TBool write = (random.IntRand() & writeMask) == 0;
+ if (write)
+ ptr[index] = acc;
+ else
+ acc += ptr[index];
+ __e32_atomic_add_ord64(&args->iAccesses, 1);
}
}
break;
case EWorkloadShuffle:
{
- TInt i;
+ TInt i = 0;
while (gRunThrashTest)
{
TInt size = (args->iPageCount * gPageSize) / sizeof(TInt);
- for (i = 0 ; gRunThrashTest && i < (size - 1) ; ++i)
- {
- Mem::Swap(&ptr[i], &ptr[i + random.Next() % (size - i - 1) + 1], sizeof(TInt));
- __e32_atomic_add_ord64(&args->iAccesses, 2);
- }
+ Mem::Swap(&ptr[i], &ptr[i + random.IntRand() % (size - i - 1) + 1], sizeof(TInt));
+ __e32_atomic_add_ord64(&args->iAccesses, 2);
+ ++i;
+ if (i >= size - 1)
+ i = 0;
}
}
break;
@@ -172,17 +425,32 @@
SThrashTestArgs iArgs;
};
-void ThrashTest(TInt aThreads, // number of threads to run
+void ThrashTest(const TDesC& aTestName, // name and description
+ TInt aThreads, // number of threads to run
TBool aSharedData, // whether all threads share the same data
TWorkload aWorkload,
TInt aBeginPages, // number of pages to start with for last/all threads
TInt aEndPages, // number of pages to end with for last/all threads
TInt aOtherPages) // num of pages for other threads, or zero to use same value for all
{
- RDebug::Printf("\nPages Accesses ThL");
+ const TInt KTestLengthInSeconds = 2;
+
+ test.Next(_L("Thrash test"));
+
+ DPTest::FlushCache();
+ EnsureSystemIdle();
- DPTest::FlushCache();
- User::After(1000000);
+ TInt i;
+ test.Printf(_L("Table: %S\n"), &aTestName);
+ test.Printf(_L("totalPages, totalAccesses, thrashLevel"));
+ if (BenchmarksSupported)
+ test.Printf(_L(", rejuveCount, rejuveTime, codePageInCount, codePageInTime, initCount, initTime, readCount, readTime, writePages, writeCount, writeTime"));
+ if (aThreads > 1)
+ {
+ for (TInt i = 0 ; i < aThreads ; ++i)
+ test.Printf(_L(", Thread%dPages, Thread%dAccesses"), i, i);
+ }
+ test.Printf(_L("\n"));
TInt pagesNeeded;
TInt maxPages = Max(aBeginPages, aEndPages);
@@ -208,11 +476,10 @@
test_NotNull(threads);
gRunThrashTest = ETrue;
- TInt pageCount = aBeginPages;
const TInt maxSteps = 30;
TInt step = aEndPages >= aBeginPages ? Max((aEndPages - aBeginPages) / maxSteps, 1) : Min((aEndPages - aBeginPages) / maxSteps, -1);
+ TInt pageCount = aBeginPages - 5 * step; // first run ignored
- TInt i;
for (i = 0 ; i < aThreads ; ++i)
{
SThrashThreadData& thread = threads[i];
@@ -242,8 +509,10 @@
for (i = 0 ; i < aThreads ; ++i)
__e32_atomic_store_ord64(&threads[i].iArgs.iAccesses, 0);
+ ResetBenchmarks();
- User::After(2000000);
+ User::After(KTestLengthInSeconds * 1000 * 1000);
+
TInt thrashLevel = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
test(thrashLevel >= 0 && thrashLevel <= 255);
@@ -257,20 +526,50 @@
else
totalPages += threads[i].iArgs.iPageCount;
}
+ TInt accessesPerSecond = (TInt)(totalAccesses / KTestLengthInSeconds);
- test.Printf(_L("%5d %12ld %3d"), totalPages, totalAccesses, thrashLevel);
- for (i = 0 ; i < aThreads ; ++i)
+ TBool warmingUp = (step > 0) ? pageCount < aBeginPages : pageCount > aBeginPages;
+ if (!warmingUp)
{
- test.Printf(_L(" %5d %12ld"),
- threads[i].iArgs.iPageCount,
- __e32_atomic_load_acq64(&threads[i].iArgs.iAccesses));
- test_Equal(KRequestPending, threads[i].iStatus.Int());
+ test.Printf(_L("%10d, %13d, %11.2f"), totalPages, accessesPerSecond, (TReal)thrashLevel / 255);
+
+ if (BenchmarksSupported)
+ {
+ TInt benchmarkCount[EMaxPagingBm];
+ TInt benchmarkTime[EMaxPagingBm];
+ test_KErrNone(GetAllBenchmarks(KTestLengthInSeconds, benchmarkCount, benchmarkTime));
+
+ TInt otherPageInCount = benchmarkCount[EPagingBmReadRomPage] + benchmarkCount[EPagingBmReadCodePage];
+ TInt otherPageInTime = benchmarkTime[EPagingBmReadRomPage] + benchmarkTime[EPagingBmReadCodePage];
+
+ TInt initCount = benchmarkCount[EPagingBmReadDataPage] - benchmarkCount[EPagingBmReadDataMedia];
+ TInt initTime = benchmarkTime[EPagingBmReadDataPage] - benchmarkTime[EPagingBmReadDataMedia];
+
+ test.Printf(_L(", %11d, %10d, %15d, %14d, %9d, %8d, %9d, %8d, %10d, %10d, %9d"),
+ benchmarkCount[EPagingBmRejuvenate], benchmarkTime[EPagingBmRejuvenate],
+ otherPageInCount, otherPageInTime,
+ initCount, initTime,
+ benchmarkCount[EPagingBmReadDataMedia], benchmarkTime[EPagingBmReadDataMedia],
+ benchmarkCount[EPagingBmWriteDataPage],
+ benchmarkCount[EPagingBmWriteDataMedia], benchmarkTime[EPagingBmWriteDataMedia]);
+ }
+
+ if (aThreads > 1)
+ {
+ for (i = 0 ; i < aThreads ; ++i)
+ {
+ test.Printf(_L(", %12d, %15ld"),
+ threads[i].iArgs.iPageCount,
+ __e32_atomic_load_acq64(&threads[i].iArgs.iAccesses));
+ test_Equal(KRequestPending, threads[i].iStatus.Int());
+ }
+ }
+ test.Printf(_L("\n"));
}
- test.Printf(_L("\n"));
+ pageCount += step;
if (aEndPages >= aBeginPages ? pageCount >= aEndPages : pageCount < aEndPages)
break;
- pageCount += step;
}
gRunThrashTest = EFalse;
@@ -285,7 +584,7 @@
}
gChunk.Close();
- RDebug::Printf("\n");
+ test.Printf(_L("\n"));
}
void TestThrashing()
@@ -298,47 +597,96 @@
TInt maxPages4 = (5 * gMaxCacheSize) / 16;
// Single thread increasing in size
- test.Next(_L("Thrash test: single thread, sequential workload"));
- ThrashTest(1, ETrue, EWorkloadSequential, minPages, maxPages, 0);
+ ThrashTest(_L("single thread, sequential workload"),
+ 1, ETrue, EWorkloadSequential, minPages, maxPages, 0);
- test.Next(_L("Thrash test: single thread, random workload"));
- ThrashTest(1, ETrue, EWorkloadRandom, minPages, maxPages, 0);
+ ThrashTest(_L("single thread, random workload"),
+ 1, ETrue, EWorkloadUniformRandom, minPages, maxPages, 0);
- test.Next(_L("Thrash test: single thread, shuffle workload"));
- ThrashTest(1, ETrue, EWorkloadShuffle, minPages, maxPages, 0);
+ ThrashTest(_L("single thread, shuffle workload"),
+ 1, ETrue, EWorkloadShuffle, minPages, maxPages, 0);
// Multiple threads with shared data, one thread incresing in size
- test.Next(_L("Thrash test: two threads with shared data, one thread increasing, random workload"));
- ThrashTest(2, ETrue, EWorkloadRandom, minPages, maxPages, minPages);
+ ThrashTest(_L("two threads with shared data, one thread increasing, random workload"),
+ 2, ETrue, EWorkloadUniformRandom, minPages, maxPages, minPages);
- test.Next(_L("Thrash test: four threads with shared data, one thread increasing, random workload"));
- ThrashTest(4, ETrue, EWorkloadRandom, minPages, maxPages, minPages);
+ ThrashTest(_L("four threads with shared data, one thread increasing, random workload"),
+ 4, ETrue, EWorkloadUniformRandom, minPages, maxPages, minPages);
// Multiple threads with shared data, all threads incresing in size
- test.Next(_L("Thrash test: two threads with shared data, all threads increasing, random workload"));
- ThrashTest(2, ETrue, EWorkloadRandom, minPages, maxPages, 0);
+ ThrashTest(_L("two threads with shared data, all threads increasing, random workload"),
+ 2, ETrue, EWorkloadUniformRandom, minPages, maxPages, 0);
- test.Next(_L("Thrash test: four threads with shared data, all threads increasing, random workload"));
- ThrashTest(4, ETrue, EWorkloadRandom, minPages, maxPages, 0);
+ ThrashTest(_L("four threads with shared data, all threads increasing, random workload"),
+ 4, ETrue, EWorkloadUniformRandom, minPages, maxPages, 0);
// Multiple threads with independent data, one thread incresing in size
- test.Next(_L("Thrash test: two threads with independent data, one thread increasing, random workload"));
- ThrashTest(2, EFalse, EWorkloadRandom, minPages2, maxPages2, gMaxCacheSize / 2);
+ ThrashTest(_L("two threads with independent data, one thread increasing, random workload"),
+ 2, EFalse, EWorkloadUniformRandom, minPages2, maxPages2, gMaxCacheSize / 2);
- test.Next(_L("Thrash test: four threads with independent data, one thread increasing, random workload"));
- ThrashTest(4, EFalse, EWorkloadRandom, minPages4, maxPages4, gMaxCacheSize / 4);
+ ThrashTest(_L("four threads with independent data, one thread increasing, random workload"),
+ 4, EFalse, EWorkloadUniformRandom, minPages4, maxPages4, gMaxCacheSize / 4);
// Multiple threads with independant data, all threads incresing in size
- test.Next(_L("Thrash test: two threads with independent data, all threads increasing, random workload"));
- ThrashTest(2, EFalse, EWorkloadRandom, minPages2, maxPages2, 0);
+ ThrashTest(_L("two threads with independent data, all threads increasing, random workload"),
+ 2, EFalse, EWorkloadUniformRandom, minPages2, maxPages2, 0);
- test.Next(_L("Thrash test: four threads with independent data, all threads increasing, random workload"));
- ThrashTest(4, EFalse, EWorkloadRandom, minPages4, maxPages4, 0);
+ ThrashTest(_L("four threads with independent data, all threads increasing, random workload"),
+ 4, EFalse, EWorkloadUniformRandom, minPages4, maxPages4, 0);
// Attempt to create thrash state where there is sufficient cache
- test.Next(_L("Thrash test: two threads with independent data, one threads decreasing, random workload"));
TInt halfCacheSize = gMaxCacheSize / 2;
- ThrashTest(2, EFalse, EWorkloadRandom, halfCacheSize + 10, halfCacheSize - 30, halfCacheSize);
+ ThrashTest(_L("two threads with independent data, one threads decreasing, random workload"),
+ 2, EFalse, EWorkloadUniformRandom, halfCacheSize + 10, halfCacheSize - 30, halfCacheSize);
+ }
+
+void TestDistribution(TRandom& aRandom, TInt aSamples)
+ {
+ TUint32* data = new TUint32[aSamples];
+ test_NotNull(data);
+
+ TInt i;
+ TReal mean = 0.0;
+ for (i = 0 ; i < aSamples ; ++i)
+ {
+ data[i] = aRandom.Next();
+ mean += (TReal)data[i] / aSamples;
+ }
+
+ TReal sum2 = 0.0;
+ for (i = 0 ; i < aSamples ; ++i)
+ {
+ TReal d = (TReal)data[i] - mean;
+ sum2 += d * d;
+ }
+ TReal variance = sum2 / (aSamples - 1);
+
+ test.Printf(_L(" mean == %f\n"), mean);
+ test.Printf(_L(" variance == %f\n"), variance);
+
+ delete [] data;
+ }
+
+void BenchmarkReplacement()
+ {
+ test.Next(_L("Test uniform distribution"));
+ TUniformRandom rand1;
+ rand1.SetParams(100);
+ TestDistribution(rand1, 10000);
+
+ test.Next(_L("Test normal distribution"));
+ TNormalRandom rand2;
+ rand2.SetParams(100, 25);
+ TestDistribution(rand2, 10000);
+
+ ThrashTest(_L("Thrash test: single thread, normal random workload 1"),
+ 1, ETrue, EWorkloadNormalRandom1, (2 * gMaxCacheSize) / 3, 2 * gMaxCacheSize, 0);
+
+ ThrashTest(_L("Thrash test: single thread, normal random workload 2"),
+ 1, ETrue, EWorkloadNormalRandom2, (2 * gMaxCacheSize) / 3, 2 * gMaxCacheSize, 0);
+
+ ThrashTest(_L("Thrash test: single thread, uniform random workload"),
+ 1, ETrue, EWorkloadUniformRandom, (2 * gMinCacheSize) / 3, (3 * gMaxCacheSize) / 2, 0);
}
void TestThrashHal()
@@ -369,27 +717,44 @@
test_Equal(KRequestPending, status.Int());
// stress system and check thrash level and notification
- ThrashTest(1, ETrue, EWorkloadRandom, gMaxCacheSize * 2, gMaxCacheSize * 2 + 5, 0);
+ ThrashTest(_L("stress system"),
+ 1, ETrue, EWorkloadUniformRandom, gMaxCacheSize * 2, gMaxCacheSize * 2 + 5, 0);
r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
test(r >= 0 && r <= 255);
test.Printf(_L("Thrash level == %d\n"), r);
test(r > 200); // should indicate thrashing
- test_Equal(EChangesThrashLevel, status.Int());
- User::WaitForAnyRequest();
- // wait for system to calm down and check notification again
- test_KErrNone(notifier.Logon(status));
- User::WaitForAnyRequest();
- test_Equal(EChangesThreadDeath, status.Int());
-
- test_KErrNone(notifier.Logon(status));
+ TBool gotThrashNotification = EFalse;
+
+ // wait for EChangesThrashLevel notification
+ while(status.Int() != KRequestPending)
+ {
+ gotThrashNotification = (status.Int() & EChangesThrashLevel) != 0;
+ User::WaitForAnyRequest();
+ test_KErrNone(notifier.Logon(status));
+ User::After(1);
+ }
+ test(gotThrashNotification);
+
User::After(2000000);
r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
test(r >= 0 && r <= 255);
test.Printf(_L("Thrash level == %d\n"), r);
test(r <= 10); // should indicate lightly loaded system
- test_Equal(EChangesThrashLevel, status.Int());
+
+ // wait for EChangesThrashLevel notification
+ gotThrashNotification = EFalse;
+ while(status.Int() != KRequestPending)
+ {
+ gotThrashNotification = (status.Int() & EChangesThrashLevel) != 0;
+ User::WaitForAnyRequest();
+ test_KErrNone(notifier.Logon(status));
+ User::After(1);
+ }
+ test(gotThrashNotification);
+ test_KErrNone(notifier.LogonCancel());
User::WaitForAnyRequest();
+ notifier.Close();
}
void TestThrashHalNotSupported()
@@ -398,45 +763,105 @@
test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, 0, 0));
}
+_LIT(KUsageMessage, "usage: t_thrash [ test ] [ thrashing ] [ benchmarks ]\n");
+
+enum TTestAction
+ {
+ EActionTest = 1 << 0,
+ EActionThrashing = 1 << 1,
+ EActionBenchmarks = 1 << 2
+ };
+
+void BadUsage()
+ {
+ test.Printf(KUsageMessage);
+ test(EFalse);
+ }
+
+TInt ParseCommandLine()
+ {
+ const TInt KMaxLineLength = 64;
+
+ if (User::CommandLineLength() > KMaxLineLength)
+ BadUsage();
+ TBuf<KMaxLineLength> buffer;
+ User::CommandLine(buffer);
+
+ if (buffer == KNullDesC)
+ return EActionTest;
+
+ TLex lex(buffer);
+ TInt result = 0;
+ while (!lex.Eos())
+ {
+ TPtrC word = lex.NextToken();
+ if (word == _L("test"))
+ result |= EActionTest;
+ else if (word == _L("thrashing"))
+ result |= EActionThrashing;
+ else if (word == _L("benchmarks"))
+ result |= EActionBenchmarks;
+ else
+ {
+ test.Printf(_L("bad token '%S'\n"), &word);
+ BadUsage();
+ }
+ }
+
+ return result;
+ }
+
TInt E32Main()
{
test.Title();
test.Start(_L("Test thrashing monitor"));
+
+ test_KErrNone(InitBenchmarks());
+ TInt actions = ParseCommandLine();
+
test_KErrNone(GetGlobalPolicies());
TUint cacheOriginalMin = 0;
TUint cacheOriginalMax = 0;
- TUint cacheCurrentSize = 0;
if (gDataPagingSupported)
{
- test.Next(_L("Thrash test: change maximum cache size to minimal"));
- //store original values
+ test.Next(_L("Thrash test: change cache size to maximum 2Mb"));
+ TUint cacheCurrentSize = 0;
DPTest::CacheSize(cacheOriginalMin, cacheOriginalMax, cacheCurrentSize);
- gMaxCacheSize = 256;
- gMinCacheSize = 64;
+ gMinCacheSize = 512;
+ gMaxCacheSize = 520;
test_KErrNone(DPTest::SetCacheSize(gMinCacheSize * gPageSize, gMaxCacheSize * gPageSize));
}
+
+ if (actions & EActionTest)
+ {
+ TBool flexibleMemoryModel = (MemModelAttributes() & EMemModelTypeMask) == EMemModelTypeFlexible;
+ if (flexibleMemoryModel)
+ TestThrashHal();
+ else
+ TestThrashHalNotSupported();
+ }
- TBool flexibleMemoryModel = (MemModelAttributes() & EMemModelTypeMask) == EMemModelTypeFlexible;
- if (flexibleMemoryModel)
- TestThrashHal();
- else
- TestThrashHalNotSupported();
-
- if (gDataPagingSupported && User::CommandLineLength() > 0)
- {
+ if (actions & EActionThrashing)
+ {
test.Next(_L("Extended thrashing tests"));
TestThrashing();
}
+
+ if (actions & EActionBenchmarks)
+ {
+ test.Next(_L("Benchmarking page replacement"));
+ BenchmarkReplacement();
+ }
+
if (gDataPagingSupported)
{
- //Reset the cache size to normal
test.Next(_L("Thrash test: Reset cache size to normal"));
test_KErrNone(DPTest::SetCacheSize(cacheOriginalMin, cacheOriginalMax));
}
-
+
test.End();
return 0;
}