diff -r 000000000000 -r a41df078684a kerneltest/e32test/demandpaging/t_thrash.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/demandpaging/t_thrash.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,422 @@ +// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// e32test\demandpaging\t_thrash.cpp +// todo: test combinations of rom / code / data paging +// + +#define __E32TEST_EXTENSION__ +#include +#include +#include +#include +#include +#include +#include +#include "u32std.h" +#include +#include +#include + +#include "t_dpcmn.h" +#include "../mmu/mmudetect.h" +#include "../mmu/d_memorytest.h" +#include "../mmu/t_codepaging_dll.h" + +RTest test(_L("T_THRASH")); + +volatile TBool gRunThrashTest = EFalse; + +_LIT(KChunkName, "t_thrash chunk"); + +class TRandom + { +public: + TRandom(); + TUint32 Next(); + +private: + enum + { + KA = 1664525, + KB = 1013904223 + }; + TUint32 iV; + }; + +TRandom::TRandom() + { + iV = (TUint32)this + RThread().Id() + User::FastCounter() + 23; + } + +TUint32 TRandom::Next() + { + iV = KA * iV + KB; + return iV; + } + +void CreatePagedChunk(TInt aSizeInPages) + { + test_Equal(0,gChunk.Handle()); + + TChunkCreateInfo createInfo; + TInt size = aSizeInPages * gPageSize; + createInfo.SetNormal(size, size); + createInfo.SetPaging(TChunkCreateInfo::EPaged); + createInfo.SetOwner(EOwnerProcess); + createInfo.SetGlobal(KChunkName); + test_KErrNone(gChunk.Create(createInfo)); + test(gChunk.IsPaged()); // this is only ever called if data paging is supported + } + +TUint32* PageBasePtr(TInt aPage) + { + return (TUint32*)(gChunk.Base() + (gPageSize * aPage)); + } + +enum TWorkload + { + EWorkloadSequential, + EWorkloadRandom, + EWorkloadShuffle + }; + +struct SThrashTestArgs + { + TInt iThreadGroup; + TInt iGroupSize; + TWorkload iWorkload; + TUint8* iBasePtr; + volatile TInt iPageCount; + volatile TInt64 iAccesses; + }; + +TInt ThrashTestFunc(TAny* aArg) + { + SThrashTestArgs* args = (SThrashTestArgs*)aArg; + + TRandom random; + 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) + { + ptr[i] = random.Next(); + __e32_atomic_add_ord64(&args->iAccesses, 1); + } + } + break; + + case EWorkloadRandom: + { + TInt acc = 0; + while (gRunThrashTest) + { + TInt size = (args->iPageCount * gPageSize) / sizeof(TInt); + for (TInt i = 0 ; i < size && gRunThrashTest ; ++i) + { + 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); + } + } + } + break; + + case EWorkloadShuffle: + { + TInt i; + 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); + } + } + } + break; + + default: + test(EFalse); + } + + return KErrNone;; + } + +struct SThrashThreadData + { + RThread iThread; + TRequestStatus iStatus; + SThrashTestArgs iArgs; + }; + +void ThrashTest(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"); + + DPTest::FlushCache(); + User::After(1000000); + + TInt pagesNeeded; + TInt maxPages = Max(aBeginPages, aEndPages); + TInt groupSize = 0; + if (aSharedData) + pagesNeeded = Max(maxPages, aOtherPages); + else + { + if (aOtherPages) + { + groupSize = aOtherPages; + pagesNeeded = (aThreads - 1) * aOtherPages + maxPages; + } + else + { + groupSize = maxPages; + pagesNeeded = aThreads * maxPages; + } + } + CreatePagedChunk(pagesNeeded); + + SThrashThreadData* threads = new SThrashThreadData[aThreads]; + 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 i; + for (i = 0 ; i < aThreads ; ++i) + { + SThrashThreadData& thread = threads[i]; + thread.iArgs.iThreadGroup = aSharedData ? 0 : i; + thread.iArgs.iGroupSize = groupSize; + thread.iArgs.iWorkload = aWorkload; + thread.iArgs.iBasePtr = gChunk.Base(); + if (aOtherPages) + thread.iArgs.iPageCount = (i == aThreads - 1) ? pageCount : aOtherPages; + else + thread.iArgs.iPageCount = pageCount; + test_KErrNone(thread.iThread.Create(KNullDesC, ThrashTestFunc, gPageSize, NULL, &thread.iArgs)); + thread.iThread.Logon(thread.iStatus); + thread.iThread.SetPriority(EPriorityLess); + threads[i].iThread.Resume(); + } + + for (;;) + { + if (aOtherPages) + threads[aThreads - 1].iArgs.iPageCount = pageCount; + else + { + for (i = 0 ; i < aThreads ; ++i) + threads[i].iArgs.iPageCount = pageCount; + } + + for (i = 0 ; i < aThreads ; ++i) + __e32_atomic_store_ord64(&threads[i].iArgs.iAccesses, 0); + + User::After(2000000); + TInt thrashLevel = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0); + test(thrashLevel >= 0 && thrashLevel <= 255); + + TInt64 totalAccesses = 0; + TInt totalPages = 0; + for (i = 0 ; i < aThreads ; ++i) + { + totalAccesses += __e32_atomic_load_acq64(&threads[i].iArgs.iAccesses); + if (aSharedData) + totalPages = Max(totalPages, threads[i].iArgs.iPageCount); + else + totalPages += threads[i].iArgs.iPageCount; + } + + test.Printf(_L("%5d %12ld %3d"), totalPages, totalAccesses, thrashLevel); + for (i = 0 ; i < aThreads ; ++i) + { + 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("\n")); + + if (aEndPages >= aBeginPages ? pageCount >= aEndPages : pageCount < aEndPages) + break; + pageCount += step; + } + + gRunThrashTest = EFalse; + + for (i = 0 ; i < aThreads ; ++i) + { + SThrashThreadData& thread = threads[i]; + User::WaitForRequest(thread.iStatus); + test_Equal(EExitKill, thread.iThread.ExitType()); + test_KErrNone(thread.iStatus.Int()); + thread.iThread.Close(); + } + + gChunk.Close(); + RDebug::Printf("\n"); + } + +void TestThrashing() + { + TInt minPages = (3 * gMaxCacheSize) / 4 - 4; + TInt maxPages = (5 * gMaxCacheSize) / 4; + TInt minPages2 = (3 * gMaxCacheSize) / 8 - 4; + TInt maxPages2 = (5 * gMaxCacheSize) / 8; + TInt minPages4 = (3 * gMaxCacheSize) / 16 - 4; + 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); + + test.Next(_L("Thrash test: single thread, random workload")); + ThrashTest(1, ETrue, EWorkloadRandom, minPages, maxPages, 0); + + test.Next(_L("Thrash test: single thread, shuffle workload")); + ThrashTest(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); + + test.Next(_L("Thrash test: four threads with shared data, one thread increasing, random workload")); + ThrashTest(4, ETrue, EWorkloadRandom, 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); + + test.Next(_L("Thrash test: four threads with shared data, all threads increasing, random workload")); + ThrashTest(4, ETrue, EWorkloadRandom, 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); + + test.Next(_L("Thrash test: four threads with independent data, one thread increasing, random workload")); + ThrashTest(4, EFalse, EWorkloadRandom, 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); + + test.Next(_L("Thrash test: four threads with independent data, all threads increasing, random workload")); + ThrashTest(4, EFalse, EWorkloadRandom, 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); + } + +void TestThrashHal() + { + test.Next(_L("Test EVMHalSetThrashThresholds")); + test_Equal(KErrArgument, UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, (TAny*)256, 0)); + test_Equal(KErrArgument, UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, (TAny*)0, (TAny*)1)); + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, 0, 0)); + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, (TAny*)255, 0)); + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, (TAny*)200, (TAny*)150)); + + test.Next(_L("Test EVMHalGetThrashLevel")); + User::After(2000000); + TInt 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 + + if (!gDataPagingSupported) + return; // rest of this test relies on data paging + + // set up thrashing notification + RChangeNotifier notifier; + test_KErrNone(notifier.Create()); + TRequestStatus status; + test_KErrNone(notifier.Logon(status)); + test_KErrNone(notifier.Logon(status)); // first logon completes immediately + test_Equal(KRequestPending, status.Int()); + + // stress system and check thrash level and notification + ThrashTest(1, ETrue, EWorkloadRandom, 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)); + 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()); + User::WaitForAnyRequest(); + } + +void TestThrashHalNotSupported() + { + test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0)); + test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, 0, 0)); + } + +TInt E32Main() + { + test.Title(); + test.Start(_L("Test thrashing monitor")); + + test_KErrNone(GetGlobalPolicies()); + + TBool flexibleMemoryModel = (MemModelAttributes() & EMemModelTypeMask) == EMemModelTypeFlexible; + if (flexibleMemoryModel) + TestThrashHal(); + else + TestThrashHalNotSupported(); + + if (gDataPagingSupported && User::CommandLineLength() > 0) + { + test.Next(_L("Extended thrashing tests")); + TestThrashing(); + } + + test.End(); + return 0; + }