1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/kernelhwsrv/kerneltest/e32test/demandpaging/t_thrash.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,442 @@
1.4 +// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of the License "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// e32test\demandpaging\t_thrash.cpp
1.18 +// todo: test combinations of rom / code / data paging
1.19 +//
1.20 +
1.21 +#define __E32TEST_EXTENSION__
1.22 +#include <e32test.h>
1.23 +#include <dptest.h>
1.24 +#include <e32hal.h>
1.25 +#include <u32hal.h>
1.26 +#include <u32exec.h>
1.27 +#include <e32svr.h>
1.28 +#include <e32panic.h>
1.29 +#include "u32std.h"
1.30 +#include <e32msgqueue.h>
1.31 +#include <e32atomics.h>
1.32 +#include <e32math.h>
1.33 +
1.34 +#include "t_dpcmn.h"
1.35 +#include "../mmu/mmudetect.h"
1.36 +#include "../mmu/d_memorytest.h"
1.37 +#include "../mmu/t_codepaging_dll.h"
1.38 +
1.39 +RTest test(_L("T_THRASH"));
1.40 +
1.41 +volatile TBool gRunThrashTest = EFalse;
1.42 +
1.43 +_LIT(KChunkName, "t_thrash chunk");
1.44 +
1.45 +class TRandom
1.46 + {
1.47 +public:
1.48 + TRandom();
1.49 + TUint32 Next();
1.50 +
1.51 +private:
1.52 + enum
1.53 + {
1.54 + KA = 1664525,
1.55 + KB = 1013904223
1.56 + };
1.57 + TUint32 iV;
1.58 + };
1.59 +
1.60 +TRandom::TRandom()
1.61 + {
1.62 + iV = (TUint32)this + RThread().Id() + User::FastCounter() + 23;
1.63 + }
1.64 +
1.65 +TUint32 TRandom::Next()
1.66 + {
1.67 + iV = KA * iV + KB;
1.68 + return iV;
1.69 + }
1.70 +
1.71 +void CreatePagedChunk(TInt aSizeInPages)
1.72 + {
1.73 + test_Equal(0,gChunk.Handle());
1.74 +
1.75 + TChunkCreateInfo createInfo;
1.76 + TInt size = aSizeInPages * gPageSize;
1.77 + createInfo.SetNormal(size, size);
1.78 + createInfo.SetPaging(TChunkCreateInfo::EPaged);
1.79 + createInfo.SetOwner(EOwnerProcess);
1.80 + createInfo.SetGlobal(KChunkName);
1.81 + test_KErrNone(gChunk.Create(createInfo));
1.82 + test(gChunk.IsPaged()); // this is only ever called if data paging is supported
1.83 + }
1.84 +
1.85 +TUint32* PageBasePtr(TInt aPage)
1.86 + {
1.87 + return (TUint32*)(gChunk.Base() + (gPageSize * aPage));
1.88 + }
1.89 +
1.90 +enum TWorkload
1.91 + {
1.92 + EWorkloadSequential,
1.93 + EWorkloadRandom,
1.94 + EWorkloadShuffle
1.95 + };
1.96 +
1.97 +struct SThrashTestArgs
1.98 + {
1.99 + TInt iThreadGroup;
1.100 + TInt iGroupSize;
1.101 + TWorkload iWorkload;
1.102 + TUint8* iBasePtr;
1.103 + volatile TInt iPageCount;
1.104 + volatile TInt64 iAccesses;
1.105 + };
1.106 +
1.107 +TInt ThrashTestFunc(TAny* aArg)
1.108 + {
1.109 + SThrashTestArgs* args = (SThrashTestArgs*)aArg;
1.110 +
1.111 + TRandom random;
1.112 + TInt startPage = args->iThreadGroup * args->iGroupSize;
1.113 + TInt* ptr = (TInt*)(args->iBasePtr + startPage * gPageSize);
1.114 + switch (args->iWorkload)
1.115 + {
1.116 + case EWorkloadSequential:
1.117 + while (gRunThrashTest)
1.118 + {
1.119 + TInt size = (args->iPageCount * gPageSize) / sizeof(TInt);
1.120 + for (TInt i = 0 ; i < size && gRunThrashTest ; ++i)
1.121 + {
1.122 + ptr[i] = random.Next();
1.123 + __e32_atomic_add_ord64(&args->iAccesses, 1);
1.124 + }
1.125 + }
1.126 + break;
1.127 +
1.128 + case EWorkloadRandom:
1.129 + {
1.130 + TInt acc = 0;
1.131 + while (gRunThrashTest)
1.132 + {
1.133 + TInt size = (args->iPageCount * gPageSize) / sizeof(TInt);
1.134 + for (TInt i = 0 ; i < size && gRunThrashTest ; ++i)
1.135 + {
1.136 + TUint32 rand = random.Next();
1.137 + TInt action = rand >> 31;
1.138 + TInt r = rand % size;
1.139 + if (action == 0)
1.140 + acc += ptr[r];
1.141 + else
1.142 + ptr[r] = acc;
1.143 + __e32_atomic_add_ord64(&args->iAccesses, 1);
1.144 + }
1.145 + }
1.146 + }
1.147 + break;
1.148 +
1.149 + case EWorkloadShuffle:
1.150 + {
1.151 + TInt i;
1.152 + while (gRunThrashTest)
1.153 + {
1.154 + TInt size = (args->iPageCount * gPageSize) / sizeof(TInt);
1.155 + for (i = 0 ; gRunThrashTest && i < (size - 1) ; ++i)
1.156 + {
1.157 + Mem::Swap(&ptr[i], &ptr[i + random.Next() % (size - i - 1) + 1], sizeof(TInt));
1.158 + __e32_atomic_add_ord64(&args->iAccesses, 2);
1.159 + }
1.160 + }
1.161 + }
1.162 + break;
1.163 +
1.164 + default:
1.165 + test(EFalse);
1.166 + }
1.167 +
1.168 + return KErrNone;;
1.169 + }
1.170 +
1.171 +struct SThrashThreadData
1.172 + {
1.173 + RThread iThread;
1.174 + TRequestStatus iStatus;
1.175 + SThrashTestArgs iArgs;
1.176 + };
1.177 +
1.178 +void ThrashTest(TInt aThreads, // number of threads to run
1.179 + TBool aSharedData, // whether all threads share the same data
1.180 + TWorkload aWorkload,
1.181 + TInt aBeginPages, // number of pages to start with for last/all threads
1.182 + TInt aEndPages, // number of pages to end with for last/all threads
1.183 + TInt aOtherPages) // num of pages for other threads, or zero to use same value for all
1.184 + {
1.185 + RDebug::Printf("\nPages Accesses ThL");
1.186 +
1.187 + DPTest::FlushCache();
1.188 + User::After(1000000);
1.189 +
1.190 + TInt pagesNeeded;
1.191 + TInt maxPages = Max(aBeginPages, aEndPages);
1.192 + TInt groupSize = 0;
1.193 + if (aSharedData)
1.194 + pagesNeeded = Max(maxPages, aOtherPages);
1.195 + else
1.196 + {
1.197 + if (aOtherPages)
1.198 + {
1.199 + groupSize = aOtherPages;
1.200 + pagesNeeded = (aThreads - 1) * aOtherPages + maxPages;
1.201 + }
1.202 + else
1.203 + {
1.204 + groupSize = maxPages;
1.205 + pagesNeeded = aThreads * maxPages;
1.206 + }
1.207 + }
1.208 + CreatePagedChunk(pagesNeeded);
1.209 +
1.210 + SThrashThreadData* threads = new SThrashThreadData[aThreads];
1.211 + test_NotNull(threads);
1.212 +
1.213 + gRunThrashTest = ETrue;
1.214 + TInt pageCount = aBeginPages;
1.215 + const TInt maxSteps = 30;
1.216 + TInt step = aEndPages >= aBeginPages ? Max((aEndPages - aBeginPages) / maxSteps, 1) : Min((aEndPages - aBeginPages) / maxSteps, -1);
1.217 +
1.218 + TInt i;
1.219 + for (i = 0 ; i < aThreads ; ++i)
1.220 + {
1.221 + SThrashThreadData& thread = threads[i];
1.222 + thread.iArgs.iThreadGroup = aSharedData ? 0 : i;
1.223 + thread.iArgs.iGroupSize = groupSize;
1.224 + thread.iArgs.iWorkload = aWorkload;
1.225 + thread.iArgs.iBasePtr = gChunk.Base();
1.226 + if (aOtherPages)
1.227 + thread.iArgs.iPageCount = (i == aThreads - 1) ? pageCount : aOtherPages;
1.228 + else
1.229 + thread.iArgs.iPageCount = pageCount;
1.230 + test_KErrNone(thread.iThread.Create(KNullDesC, ThrashTestFunc, gPageSize, NULL, &thread.iArgs));
1.231 + thread.iThread.Logon(thread.iStatus);
1.232 + thread.iThread.SetPriority(EPriorityLess);
1.233 + threads[i].iThread.Resume();
1.234 + }
1.235 +
1.236 + for (;;)
1.237 + {
1.238 + if (aOtherPages)
1.239 + threads[aThreads - 1].iArgs.iPageCount = pageCount;
1.240 + else
1.241 + {
1.242 + for (i = 0 ; i < aThreads ; ++i)
1.243 + threads[i].iArgs.iPageCount = pageCount;
1.244 + }
1.245 +
1.246 + for (i = 0 ; i < aThreads ; ++i)
1.247 + __e32_atomic_store_ord64(&threads[i].iArgs.iAccesses, 0);
1.248 +
1.249 + User::After(2000000);
1.250 + TInt thrashLevel = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
1.251 + test(thrashLevel >= 0 && thrashLevel <= 255);
1.252 +
1.253 + TInt64 totalAccesses = 0;
1.254 + TInt totalPages = 0;
1.255 + for (i = 0 ; i < aThreads ; ++i)
1.256 + {
1.257 + totalAccesses += __e32_atomic_load_acq64(&threads[i].iArgs.iAccesses);
1.258 + if (aSharedData)
1.259 + totalPages = Max(totalPages, threads[i].iArgs.iPageCount);
1.260 + else
1.261 + totalPages += threads[i].iArgs.iPageCount;
1.262 + }
1.263 +
1.264 + test.Printf(_L("%5d %12ld %3d"), totalPages, totalAccesses, thrashLevel);
1.265 + for (i = 0 ; i < aThreads ; ++i)
1.266 + {
1.267 + test.Printf(_L(" %5d %12ld"),
1.268 + threads[i].iArgs.iPageCount,
1.269 + __e32_atomic_load_acq64(&threads[i].iArgs.iAccesses));
1.270 + test_Equal(KRequestPending, threads[i].iStatus.Int());
1.271 + }
1.272 + test.Printf(_L("\n"));
1.273 +
1.274 + if (aEndPages >= aBeginPages ? pageCount >= aEndPages : pageCount < aEndPages)
1.275 + break;
1.276 + pageCount += step;
1.277 + }
1.278 +
1.279 + gRunThrashTest = EFalse;
1.280 +
1.281 + for (i = 0 ; i < aThreads ; ++i)
1.282 + {
1.283 + SThrashThreadData& thread = threads[i];
1.284 + User::WaitForRequest(thread.iStatus);
1.285 + test_Equal(EExitKill, thread.iThread.ExitType());
1.286 + test_KErrNone(thread.iStatus.Int());
1.287 + thread.iThread.Close();
1.288 + }
1.289 +
1.290 + gChunk.Close();
1.291 + RDebug::Printf("\n");
1.292 + }
1.293 +
1.294 +void TestThrashing()
1.295 + {
1.296 + TInt minPages = (3 * gMaxCacheSize) / 4 - 4;
1.297 + TInt maxPages = (5 * gMaxCacheSize) / 4;
1.298 + TInt minPages2 = (3 * gMaxCacheSize) / 8 - 4;
1.299 + TInt maxPages2 = (5 * gMaxCacheSize) / 8;
1.300 + TInt minPages4 = (3 * gMaxCacheSize) / 16 - 4;
1.301 + TInt maxPages4 = (5 * gMaxCacheSize) / 16;
1.302 +
1.303 + // Single thread increasing in size
1.304 + test.Next(_L("Thrash test: single thread, sequential workload"));
1.305 + ThrashTest(1, ETrue, EWorkloadSequential, minPages, maxPages, 0);
1.306 +
1.307 + test.Next(_L("Thrash test: single thread, random workload"));
1.308 + ThrashTest(1, ETrue, EWorkloadRandom, minPages, maxPages, 0);
1.309 +
1.310 + test.Next(_L("Thrash test: single thread, shuffle workload"));
1.311 + ThrashTest(1, ETrue, EWorkloadShuffle, minPages, maxPages, 0);
1.312 +
1.313 + // Multiple threads with shared data, one thread incresing in size
1.314 + test.Next(_L("Thrash test: two threads with shared data, one thread increasing, random workload"));
1.315 + ThrashTest(2, ETrue, EWorkloadRandom, minPages, maxPages, minPages);
1.316 +
1.317 + test.Next(_L("Thrash test: four threads with shared data, one thread increasing, random workload"));
1.318 + ThrashTest(4, ETrue, EWorkloadRandom, minPages, maxPages, minPages);
1.319 +
1.320 + // Multiple threads with shared data, all threads incresing in size
1.321 + test.Next(_L("Thrash test: two threads with shared data, all threads increasing, random workload"));
1.322 + ThrashTest(2, ETrue, EWorkloadRandom, minPages, maxPages, 0);
1.323 +
1.324 + test.Next(_L("Thrash test: four threads with shared data, all threads increasing, random workload"));
1.325 + ThrashTest(4, ETrue, EWorkloadRandom, minPages, maxPages, 0);
1.326 +
1.327 + // Multiple threads with independent data, one thread incresing in size
1.328 + test.Next(_L("Thrash test: two threads with independent data, one thread increasing, random workload"));
1.329 + ThrashTest(2, EFalse, EWorkloadRandom, minPages2, maxPages2, gMaxCacheSize / 2);
1.330 +
1.331 + test.Next(_L("Thrash test: four threads with independent data, one thread increasing, random workload"));
1.332 + ThrashTest(4, EFalse, EWorkloadRandom, minPages4, maxPages4, gMaxCacheSize / 4);
1.333 +
1.334 + // Multiple threads with independant data, all threads incresing in size
1.335 + test.Next(_L("Thrash test: two threads with independent data, all threads increasing, random workload"));
1.336 + ThrashTest(2, EFalse, EWorkloadRandom, minPages2, maxPages2, 0);
1.337 +
1.338 + test.Next(_L("Thrash test: four threads with independent data, all threads increasing, random workload"));
1.339 + ThrashTest(4, EFalse, EWorkloadRandom, minPages4, maxPages4, 0);
1.340 +
1.341 + // Attempt to create thrash state where there is sufficient cache
1.342 + test.Next(_L("Thrash test: two threads with independent data, one threads decreasing, random workload"));
1.343 + TInt halfCacheSize = gMaxCacheSize / 2;
1.344 + ThrashTest(2, EFalse, EWorkloadRandom, halfCacheSize + 10, halfCacheSize - 30, halfCacheSize);
1.345 + }
1.346 +
1.347 +void TestThrashHal()
1.348 + {
1.349 + test.Next(_L("Test EVMHalSetThrashThresholds"));
1.350 + test_Equal(KErrArgument, UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, (TAny*)256, 0));
1.351 + test_Equal(KErrArgument, UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, (TAny*)0, (TAny*)1));
1.352 + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, 0, 0));
1.353 + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, (TAny*)255, 0));
1.354 + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, (TAny*)200, (TAny*)150));
1.355 +
1.356 + test.Next(_L("Test EVMHalGetThrashLevel"));
1.357 + User::After(2000000);
1.358 + TInt r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
1.359 + test(r >= 0 && r <= 255);
1.360 + test.Printf(_L("Thrash level == %d\n"), r);
1.361 + test(r <= 10); // should indicate lightly loaded system
1.362 +
1.363 + if (!gDataPagingSupported)
1.364 + return; // rest of this test relies on data paging
1.365 +
1.366 + // set up thrashing notification
1.367 + RChangeNotifier notifier;
1.368 + test_KErrNone(notifier.Create());
1.369 + TRequestStatus status;
1.370 + test_KErrNone(notifier.Logon(status));
1.371 + test_KErrNone(notifier.Logon(status)); // first logon completes immediately
1.372 + test_Equal(KRequestPending, status.Int());
1.373 +
1.374 + // stress system and check thrash level and notification
1.375 + ThrashTest(1, ETrue, EWorkloadRandom, gMaxCacheSize * 2, gMaxCacheSize * 2 + 5, 0);
1.376 + r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
1.377 + test(r >= 0 && r <= 255);
1.378 + test.Printf(_L("Thrash level == %d\n"), r);
1.379 + test(r > 200); // should indicate thrashing
1.380 + test_Equal(EChangesThrashLevel, status.Int());
1.381 + User::WaitForAnyRequest();
1.382 +
1.383 + // wait for system to calm down and check notification again
1.384 + test_KErrNone(notifier.Logon(status));
1.385 + User::WaitForAnyRequest();
1.386 + test_Equal(EChangesThreadDeath, status.Int());
1.387 +
1.388 + test_KErrNone(notifier.Logon(status));
1.389 + User::After(2000000);
1.390 + r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0);
1.391 + test(r >= 0 && r <= 255);
1.392 + test.Printf(_L("Thrash level == %d\n"), r);
1.393 + test(r <= 10); // should indicate lightly loaded system
1.394 + test_Equal(EChangesThrashLevel, status.Int());
1.395 + User::WaitForAnyRequest();
1.396 + }
1.397 +
1.398 +void TestThrashHalNotSupported()
1.399 + {
1.400 + test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0));
1.401 + test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, 0, 0));
1.402 + }
1.403 +
1.404 +TInt E32Main()
1.405 + {
1.406 + test.Title();
1.407 + test.Start(_L("Test thrashing monitor"));
1.408 +
1.409 + test_KErrNone(GetGlobalPolicies());
1.410 +
1.411 + TUint cacheOriginalMin = 0;
1.412 + TUint cacheOriginalMax = 0;
1.413 + TUint cacheCurrentSize = 0;
1.414 +
1.415 + if (gDataPagingSupported)
1.416 + {
1.417 + test.Next(_L("Thrash test: change maximum cache size to minimal"));
1.418 + //store original values
1.419 + DPTest::CacheSize(cacheOriginalMin, cacheOriginalMax, cacheCurrentSize);
1.420 + gMaxCacheSize = 256;
1.421 + gMinCacheSize = 64;
1.422 + test_KErrNone(DPTest::SetCacheSize(gMinCacheSize * gPageSize, gMaxCacheSize * gPageSize));
1.423 + }
1.424 +
1.425 + TBool flexibleMemoryModel = (MemModelAttributes() & EMemModelTypeMask) == EMemModelTypeFlexible;
1.426 + if (flexibleMemoryModel)
1.427 + TestThrashHal();
1.428 + else
1.429 + TestThrashHalNotSupported();
1.430 +
1.431 + if (gDataPagingSupported && User::CommandLineLength() > 0)
1.432 + {
1.433 + test.Next(_L("Extended thrashing tests"));
1.434 + TestThrashing();
1.435 + }
1.436 + if (gDataPagingSupported)
1.437 + {
1.438 + //Reset the cache size to normal
1.439 + test.Next(_L("Thrash test: Reset cache size to normal"));
1.440 + test_KErrNone(DPTest::SetCacheSize(cacheOriginalMin, cacheOriginalMax));
1.441 + }
1.442 +
1.443 + test.End();
1.444 + return 0;
1.445 + }