diff -r 000000000000 -r a41df078684a kerneltest/e32test/misc/t_cputime.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/misc/t_cputime.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,439 @@ +// Copyright (c) 2005-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\misc\t_cputime.cpp +// Tests User::FastCounter() and RThread::GetCpuTime() +// Note: This test only works on the emulator when run in textshell mode. The +// reason for this is that is assumes that it will be able to use 100% of CPU +// time, but when techview is starting up there are many other threads consuming +// CPU time. +// +// + +#include +#include +#include +#include +#ifdef __WINS__ +#include +#endif + +RTest test(_L("T_CPUTIME")); + +_LIT(KUp, "up"); +_LIT(KDown, "down"); + +const TInt KLongWait = 3000000; // 3 seconds +const TInt KShortWait = 100000; // 0.1 seconds +const TInt KTolerance = 500; // 0.5 ms +const TInt numCpus = UserSvr::HalFunction(EHalGroupKernel, EKernelHalNumLogicalCpus, 0, 0); + +#define FailIfError(EXPR) \ + { \ + TInt aErr = (EXPR); \ + if (aErr != KErrNone) \ + { \ + test.Printf(_L("Return code == %d\n"), aErr); \ + test(EFalse); \ + } \ + } + +class TThreadParam + { +public: + TInt iCpu; + RSemaphore iSem; + }; + +TBool GetCpuTimeIsSupported() + { + RThread thread; + TTimeIntervalMicroSeconds time; + TInt err = thread.GetCpuTime(time); + test(err == KErrNone || err == KErrNotSupported); + return err == KErrNone; + } + +TInt SetCpuAffinity(TInt aCore) + { + TInt r = UserSvr::HalFunction(EHalGroupKernel, EKernelHalLockThreadToCpu, (TAny *)aCore, 0); + test(r==KErrNone); + return r; + } + + +//! @SYMTestCaseID t_cputime_0 +//! @SYMTestType CT +//! @SYMTestCaseDesc Fast counter tests +//! @SYMREQ CR RFID-66JJKX +//! @SYMTestActions Compares the high res timer against the nanokernel microsecond tick +//! @SYMTestExpectedResults The differnce measured should be < 1% +//! @SYMTestPriority High +//! @SYMTestStatus Defined +void TestFastCounter() + { + test.Start(_L("Comparing NTickCount with FastCounter")); + + TInt tickPeriod = 0; + FailIfError(HAL::Get(HAL::ENanoTickPeriod, tickPeriod)); + test.Printf(_L(" tick period == %d\n"), tickPeriod); + + TInt countFreq = 0; + FailIfError(HAL::Get(HAL::EFastCounterFrequency, countFreq)); + test.Printf(_L(" count freq == %d\n"), countFreq); + + TBool fcCountsUp = 0; + FailIfError(HAL::Get(HAL::EFastCounterCountsUp, fcCountsUp)); + test.Printf(_L(" count dir == %S\n"), fcCountsUp ? &KUp : &KDown); + + TUint startTick = User::NTickCount(); + TUint startCount = User::FastCounter(); + + User::After(KLongWait); + + TUint endTick = User::NTickCount(); + TUint endCount = User::FastCounter(); + + TInt tickDiff = endTick - startTick; + TInt countDiff = fcCountsUp ? (endCount - startCount) : (startCount - endCount); + + test.Printf(_L(" tick difference == %d\n"), tickDiff); + test.Printf(_L(" fast count difference == %d\n"), countDiff); + + TInt elapsedTickUs = tickDiff * tickPeriod; + TInt elapsedCountUs = (TInt)(((TInt64)1000000 * countDiff) / countFreq); + + test.Printf(_L(" tick time == %d\n"), elapsedTickUs); + test.Printf(_L(" count time == %d\n"), elapsedCountUs); + + TReal diff = (100.0 * Abs(elapsedCountUs - elapsedTickUs)) / elapsedTickUs; + + test.Printf(_L(" %% difference == %f\n"), diff); + test(diff < 1.0); + test.End(); + } + +TInt ThreadFunction(TAny* aParam) + { + if (numCpus > 1) + { + TInt& core = (static_cast(aParam))->iCpu; + FailIfError(SetCpuAffinity(core)); + } + + RSemaphore& semaphore = (static_cast(aParam))->iSem; + semaphore.Wait(); + for (;;) + { + // Spin + } + } + +void EnsureSystemIdle() + { + // This test assumes 100% cpu resource is available, so it can fail on + // windows builds if something else is running in the background. This + // function attempts to wait for the system to become idle. + +#ifdef __WINS__ + + const TInt KMaxWait = 60 * 1000000; + const TInt KSampleTime = 1 * 1000000; + const TInt KWaitTime = 5 * 1000000; + + test.Start(_L("Waiting for system to become idle")); + TInt totalTime = 0; + TBool idle; + do + { + test(totalTime < KMaxWait); + + TThreadParam threadParam; + FailIfError((threadParam.iSem).CreateLocal(0)); + threadParam.iCpu = 1; + + RThread thread; + FailIfError(thread.Create(_L("Thread"), ThreadFunction, 1024, NULL, &threadParam)); + thread.SetPriority(EPriorityLess); + thread.Resume(); + + User::After(KShortWait); // Pause to allow thread setup + + (threadParam.iSem).Signal(); + User::After(KSampleTime); + thread.Suspend(); + + TTimeIntervalMicroSeconds time; + FailIfError(thread.GetCpuTime(time)); + TReal error = (100.0 * Abs(time.Int64() - KSampleTime)) / KSampleTime; + test.Printf(_L(" time == %ld, error == %f%%\n"), time, error); + + idle = error < 2.0; + + thread.Kill(KErrNone); + TRequestStatus status; + thread.Logon(status); + User::WaitForRequest(status); + test(status == KErrNone); + CLOSE_AND_WAIT(thread); + + (threadParam.iSem).Close(); + + if (!idle) + User::After(KWaitTime); // Allow system to finish whatever it's doing + + totalTime += KShortWait + KSampleTime + KWaitTime; + } + while(!idle); + + test.End(); + +#endif + } + +//! @SYMTestCaseID t_cputime_1 +//! @SYMTestType CT +//! @SYMTestCaseDesc Thread CPU time tests +//! @SYMREQ CR RFID-66JJKX +//! @SYMTestActions Tests cpu time when a thread is put through the various states +//! @SYMTestExpectedResults Reported cpu time increses only when the thread is running +//! @SYMTestPriority High +//! @SYMTestStatus Defined +void TestThreadCpuTime() + { + test.Start(_L("CPU thread time unit tests")); + + TThreadParam threadParam; + FailIfError((threadParam.iSem).CreateLocal(0)); + threadParam.iCpu = 1; + + RThread thread; + RUndertaker u; + TInt h; + TRequestStatus s; + FailIfError(thread.Create(_L("Thread"), ThreadFunction, 1024, NULL, &threadParam)); + thread.SetPriority(EPriorityLess); + FailIfError(u.Create()); + FailIfError(u.Logon(s,h)); + test(s==KRequestPending); + + TTimeIntervalMicroSeconds time, time2; + + // Test time is initially zero + FailIfError(thread.GetCpuTime(time)); + test(time == 0); + + // Test not increased while waiting on semaphore + thread.Resume(); + User::After(KShortWait); + FailIfError(thread.GetCpuTime(time)); + test(time < KTolerance); // wait happens in less than 0.5ms + + // Test increases when thread allowed to run + (threadParam.iSem).Signal(); + User::After(KShortWait); + FailIfError(thread.GetCpuTime(time)); + test(time > (KShortWait - 2 * KTolerance)); + + // Test not increased while suspended + thread.Suspend(); + FailIfError(thread.GetCpuTime(time)); + User::After(KShortWait); + FailIfError(thread.GetCpuTime(time2)); + test(time == time2); + thread.Resume(); + + // Test not increased while dead + thread.Kill(KErrNone); + User::WaitForRequest(s); // wait on undertaker since that completes in supervisor thread + FailIfError(thread.GetCpuTime(time)); + User::After(KShortWait); + FailIfError(thread.GetCpuTime(time2)); + test(time == time2); + + RThread t; + t.SetHandle(h); + test(t.Id()==thread.Id()); + t.Close(); + u.Close(); + thread.Close(); + (threadParam.iSem).Close(); + test.End(); + } + +//! @SYMTestCaseID t_cputime_2 +//! @SYMTestType CT +//! @SYMTestCaseDesc Thread CPU time tests +//! @SYMREQ CR RFID-66JJKX +//! @SYMTestActions Tests cpu time when multiple threads are running +//! @SYMTestExpectedResults Total time is divided evenly among running threads +//! @SYMTestPriority High +//! @SYMTestStatus Defined + +TBool DoTestThreadCpuTime2() // Returns ETrue if test passed + { + test.Start(_L("Testing time shared between threads")); + + if (numCpus > 1) + { + test.Printf(_L("** SMP system detected - not testing time shared between threads until load balancing optimized **\n")); + return ETrue; + } + + const TInt KMaxThreads = 4; + + TThreadParam threadParam; + + RThread* threads = NULL; + threads = new(ELeave) RThread[numCpus*KMaxThreads]; + FailIfError((threadParam.iSem).CreateLocal(0)); + + TBool pass = ETrue; + for (TInt numThreads = 1 ; pass && numThreads <= KMaxThreads ; ++numThreads) + { + test.Printf(_L(" testing with %d threads on each of %d CPUs:\n"), numThreads, numCpus); + + TInt i, j, k; + for (i = 0 ; i < numThreads ; ++i) + { + for (j = 0 ; j < numCpus ; ++j) + { + TBuf<16> name; + name.AppendFormat(_L("Thread%d%d"), i, j); + threadParam.iCpu = j; + k = i+j*KMaxThreads; + FailIfError(threads[k].Create(name, ThreadFunction, 1024, NULL, &threadParam)); + threads[k].SetPriority(EPriorityLess); + threads[k].Resume(); + } + } + + User::After(KShortWait); // Pause to allow thread setup + + (threadParam.iSem).Signal(numThreads*numCpus); + User::After(KLongWait); + for (i = 0 ; i < numThreads ; ++i) + for (j = 0 ; j < numCpus ; ++j) + threads[i+j*KMaxThreads].Suspend(); + + TInt expected = KLongWait / numThreads; + for (i = 0 ; i < numThreads ; ++i) + { + for (j = 0 ; j < numCpus ; ++j) + { + k = i+j*KMaxThreads; + TTimeIntervalMicroSeconds time; + FailIfError(threads[k].GetCpuTime(time)); + + TReal error = (100.0 * Abs(time.Int64() - expected)) / expected; + + test.Printf(_L(" %d%d: time == %ld, error == %d%%\n"), i, j, time.Int64(), TInt(error)); + + if (error >= 5.0) + pass = EFalse; + + threads[k].Kill(KErrNone); + TRequestStatus status; + threads[k].Logon(status); + User::WaitForRequest(status); + test(status == KErrNone); + CLOSE_AND_WAIT(threads[k]); + } + } + } + + (threadParam.iSem).Close(); + test.End(); + + return pass; + } + +void TestThreadCpuTime2() + { +#ifdef __WINS__ + TBool pass = EFalse; + for (TInt retry = 0 ; !pass && retry < 5 ; ++retry) + { + if (retry > 0) + { + test.Printf(_L("Test failed, retrying...\n")); + EnsureSystemIdle(); + } + pass = DoTestThreadCpuTime2(); + } + test(pass); +#else + test(DoTestThreadCpuTime2()); +#endif + } + +TInt ThreadFunction2(TAny* aParam) + { + TTimeIntervalMicroSeconds& time = *(TTimeIntervalMicroSeconds*)aParam; + RThread thread; + return thread.GetCpuTime(time); + } + +#ifdef __MARM__ + +void DoTestThreadCpuTime3(TAny* aParam, TExitType aExpectedExitType, TInt aExpectedExitReason) + { + RThread thread; + FailIfError(thread.Create(_L("TestThread"), ThreadFunction2, 1024, NULL, aParam)); + thread.Resume(); + TRequestStatus status; + thread.Logon(status); + User::WaitForRequest(status); + + TExitCategoryName exitCat = thread.ExitCategory(); + test.Printf(_L("Thread exit with type == %d, reason == %d, cat == %S\n"), + thread.ExitType(), thread.ExitReason(), &exitCat); + + test(thread.ExitType() == aExpectedExitType); + test(thread.ExitReason() == aExpectedExitReason); + CLOSE_AND_WAIT(thread); + } + +void TestThreadCpuTime3() + { + // Test kernel writes the return value back to user-space with the correct permissions + TTimeIntervalMicroSeconds time; + DoTestThreadCpuTime3(&time, EExitKill, 0); // ok + DoTestThreadCpuTime3((TAny*)0, EExitPanic, 3); // null pointer + DoTestThreadCpuTime3((TAny*)0x64000000, EExitPanic, 3); // start of kernel data on moving memory model + DoTestThreadCpuTime3((TAny*)0xc8000000, EExitPanic, 3); // start of kernel data on moving multiple model + } + +#endif + +GLDEF_C TInt E32Main() + { + test.Title(); + test.Start(_L("T_CPUTIME")); + + if (numCpus > 1) + FailIfError(SetCpuAffinity(0)); + + TestFastCounter(); + if (GetCpuTimeIsSupported()) + { + EnsureSystemIdle(); + TestThreadCpuTime(); + TestThreadCpuTime2(); +#ifdef __MARM__ + TestThreadCpuTime3(); +#endif + } + test.End(); + return 0; + }