kerneltest/e32test/misc/t_cputime.cpp
changeset 0 a41df078684a
child 36 538db54a451d
--- /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 <e32test.h>
+#include <e32svr.h>
+#include <u32hal.h>
+#include <hal.h>
+#ifdef __WINS__
+#include <e32wins.h>
+#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<TThreadParam*>(aParam))->iCpu;
+		FailIfError(SetCpuAffinity(core));
+		}
+
+	RSemaphore& semaphore = (static_cast<TThreadParam*>(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;
+	}