kerneltest/e32test/misc/t_cputime.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 8 538db54a451d
child 43 c1f20ce4abcf
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// 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 =    1000;  // 1 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 = 0;				// Later tests will exercise other CPUs

	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 - 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;
	}