libraries/btrace_parser/src/btrace_cpuusage.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 00:49:35 +0100
changeset 37 534b01198c2d
parent 0 7f656887cf89
permissions -rw-r--r--
Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase. Commands can now get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.

// btrace_cpuusage.cpp
// 
// Copyright (c) 2008 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//

#include "btrace_parser.h"

MBtraceCpuUsageObserver::TCpuUsage::TCpuUsage(const TBtraceThreadId& aId)
	: iId(aId), iNumFastTicks(0)
	{
	}

MBtraceCpuUsageObserver::TCpuUsage::TCpuUsage(const TBtraceThreadId& aId, const TBtraceTickCount& aTickCount)
	: iId(aId), iNumFastTicks(0), iSwitchedInAt(aTickCount)
	{
	}

EXPORT_C CBtraceCpuUsage* CBtraceCpuUsage::NewL(CBtraceReader& aReader, CBtraceContext& aContext)
	{
	CBtraceCpuUsage* self = new(ELeave) CBtraceCpuUsage(aReader, aContext);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

EXPORT_C CBtraceCpuUsage::~CBtraceCpuUsage()
	{
	while (iCpuUsageNotifs.Count())
		{
		iCpuUsageNotifs[0].Close();
		iCpuUsageNotifs.Remove(0);
		}
	iCpuUsageNotifs.Close();
	iContextSwitchNotifs.Close();
	iThreadIdleNotifs.Close();
	iReader.RemoveObserver(BTrace::ECpuUsage, *this);
	iReader.RemoveObserver(KAmTraceCategory, *this);
	}

EXPORT_C void CBtraceCpuUsage::NotifyCpuUsageL(TUint32 aNanoTickCount, TTimeIntervalMicroSeconds32 aPeriod, MBtraceCpuUsageObserver& aObserver)
	{
	NotifyCpuUsageL(aNanoTickCount, aPeriod, aObserver, ENotificationOneShot);
	}
	
EXPORT_C void CBtraceCpuUsage::NotifyCpuUsageL(TUint32 aNanoTickCount, TTimeIntervalMicroSeconds32 aPeriod, MBtraceCpuUsageObserver& aObserver, TBtraceNotificationPersistence aPersistence)
	{
	RCpuUsageNotif notif(aNanoTickCount, TBtraceUtils::MicroSecondsToNanoTicks(aPeriod), aObserver, aPersistence);
	User::LeaveIfError(iCpuUsageNotifs.Append(notif));
	}
	
EXPORT_C void CBtraceCpuUsage::NotifyContextSwitchL(const TBtraceThreadId& aId, MBtraceCpuUsageObserver& aObserver)
	{
	TContextSwitchNotif notif(aId, aObserver);
	User::LeaveIfError(iContextSwitchNotifs.InsertInUnsignedKeyOrderAllowRepeats(notif));
	}

EXPORT_C void CBtraceCpuUsage::NotifyThreadIdleL(const TBtraceThreadId& aId, const TBtraceTickCount& aFromTickCount, TTimeIntervalMicroSeconds32 aPeriod, MBtraceCpuUsageObserver& aObserver)
	{
	NotifyThreadIdleL(aId, aFromTickCount, aPeriod, aObserver, ENotificationOneShot);
	}
	
EXPORT_C void CBtraceCpuUsage::NotifyThreadIdleL(const TBtraceThreadId& aId, const TBtraceTickCount& aFromTickCount, TTimeIntervalMicroSeconds32 aPeriod, MBtraceCpuUsageObserver& aObserver, TBtraceNotificationPersistence aPersistence)
	{
	TThreadIdleNotif notif(aId, aFromTickCount, TBtraceUtils::MicroSecondsToNanoTicks(aPeriod), aObserver, aPersistence);
	User::LeaveIfError(iThreadIdleNotifs.InsertInUnsignedKeyOrderAllowRepeats(notif));
	}
	
EXPORT_C void CBtraceCpuUsage::CancelNotifyThreadIdle(MBtraceCpuUsageObserver& aObserver)
	{
	for (TInt i = iThreadIdleNotifs.Count()-1; i>=0; --i)
		{
		if (iThreadIdleNotifs[i].iObserver == &aObserver)
			{
			iThreadIdleNotifs.Remove(i);
			}
		}
	}

CBtraceCpuUsage::CBtraceCpuUsage(CBtraceReader& aReader, CBtraceContext& aContext)
	: iReader(aReader), iContext(aContext)
	{
	}

void CBtraceCpuUsage::ConstructL()
	{
	iReader.AddObserverL(BTrace::ECpuUsage, *this);
	iReader.AddObserverL(KAmTraceCategory, *this); // To ensure we get heartbeat and sync frames, which are needed to flush through and force TestThreadIdlenessL to be called
	}

void CBtraceCpuUsage::HandleBtraceFrameL(const TBtraceFrame& aFrame)
	{
	if (aFrame.iCategory == BTrace::ECpuUsage)
		{
		switch (aFrame.iSubCategory)
			{
			case BTrace::ENewThreadContext:
				{
				++iNumProcessedFrames;
				const TBtraceThreadId* btraceThreadId = iContext.FindThread(aFrame.iThreadContext);
//				__ASSERT_ALWAYS(btraceThreadId, Panic(EBtpPanicUnknownCpuContext));
				if (btraceThreadId)
					{
					HandleContextSwitchL(aFrame.iTickCount, *btraceThreadId);
					}
				else
					{
					iReader.Log(_L("BTrace::ENewThreadContext: Unknown thread context 0x%08x\r\n"), aFrame.iThreadContext);
					}
				break;
				}
			default:
				{
				// Ignore anything we don't know about.
				++iNumIgnoredFrames;
				break;
				}
			}
		}

	TestCpuUsagePeriodL(aFrame.iTickCount);
	TestThreadIdlenessL(aFrame.iTickCount);
	}

CBtraceCpuUsage::RCpuUsageNotif::RCpuUsageNotif(TUint32 aStartTickCount, TUint32 aNumNanoTicks, MBtraceCpuUsageObserver& aObserver, TBtraceNotificationPersistence aPersistence)
	: iStartTickCount(aStartTickCount), iNumNanoTicks(aNumNanoTicks), iObserver(&aObserver), iPersistence(aPersistence)
	{
	}

void CBtraceCpuUsage::RCpuUsageNotif::RCpuUsageNotif::Close()
	{
	iUsage.Close();
	}

CBtraceCpuUsage::TContextSwitchNotif::TContextSwitchNotif(const TBtraceThreadId& aId)
	: iId(aId), iObserver(NULL)
	{
	}

CBtraceCpuUsage::TContextSwitchNotif::TContextSwitchNotif(const TBtraceThreadId& aId, MBtraceCpuUsageObserver& aObserver)
	: iId(aId), iObserver(&aObserver)
	{
	}

CBtraceCpuUsage::TThreadIdleNotif::TThreadIdleNotif(const TBtraceThreadId& aId)
	: iId(aId), iNumNanoTicks(0), iObserver(NULL), iEverScheduled(EFalse), iPersistence(ENotificationOneShot)
	{
	}

CBtraceCpuUsage::TThreadIdleNotif::TThreadIdleNotif(const TBtraceThreadId& aId, const TBtraceTickCount& aFromTickCount, TUint aNumNanoTicks, MBtraceCpuUsageObserver& aObserver, TBtraceNotificationPersistence aPersistence)
	: iId(aId), iNumNanoTicks(aNumNanoTicks), iObserver(&aObserver), iLastSwitchedOut(aFromTickCount), iEverScheduled(EFalse), iPersistence(aPersistence)
	{
	}

void CBtraceCpuUsage::HandleContextSwitchL(const TBtraceTickCount& aTickCount, const TBtraceThreadId& aNewBtraceThreadId)
	{
	TBool relevant(EFalse);

	if (iLastBtractThreadId.Value() != 0)
		{
		// Update CPU usage notifications for the thread that has been switched out.
		for (TInt i = (iCpuUsageNotifs.Count() - 1); i >= 0; --i)
			{
			RCpuUsageNotif& notif = iCpuUsageNotifs[i];
			TInt pos = notif.iUsage.FindInUnsignedKeyOrder(MBtraceCpuUsageObserver::TCpuUsage(iLastBtractThreadId));
			if (pos >= 0)
				{
				MBtraceCpuUsageObserver::TCpuUsage& usage = notif.iUsage[pos];
				usage.iNumFastTicks += aTickCount.IntervalInFastTicks(usage.iSwitchedInAt);
				relevant = ETrue;
				}
			}
		}
	
	// Update CPU usage notifications to store this tick count at which this thread was switched in.
	for (TInt i = (iCpuUsageNotifs.Count() - 1); i >= 0; --i)
		{
		relevant = ETrue;
		RCpuUsageNotif& notif = iCpuUsageNotifs[i];
		TInt pos = notif.iUsage.FindInUnsignedKeyOrder(aNewBtraceThreadId);
		if (pos == KErrNotFound)
			{
			MBtraceCpuUsageObserver::TCpuUsage cpuUsage(aNewBtraceThreadId, aTickCount);
			TInt err = notif.iUsage.InsertInUnsignedKeyOrder(cpuUsage);
			__ASSERT_ALWAYS(err == KErrNone, Panic(EBtpPanicFailedToInsertCpuUsageObject));
			}
		else
			{
			notif.iUsage[pos].iSwitchedInAt = aTickCount;
			}
		}

	// Inform context switch observers that are interested in switches from the previous thread.
	TInt pos = iContextSwitchNotifs.SpecificFindInUnsignedKeyOrder(TContextSwitchNotif(iLastBtractThreadId), EArrayFindMode_First);
	if (pos >= 0)
		{
		const TInt numNotifs = iContextSwitchNotifs.Count();
		for (TInt i = 0; i < numNotifs; ++i)
			{
			const TContextSwitchNotif& thisNotif = iContextSwitchNotifs[i];
			if (thisNotif.iId == iLastBtractThreadId)
				{
				relevant = ETrue;
				thisNotif.iObserver->HandleContextSwitchL(aTickCount, iLastBtractThreadId, MBtraceCpuUsageObserver::EFromThisThread);
				}
			else
				{
				break;
				}
			}
		}

	// Inform context switch observers that are interested in switches to this new thread.
	pos = iContextSwitchNotifs.SpecificFindInUnsignedKeyOrder(TContextSwitchNotif(aNewBtraceThreadId), EArrayFindMode_First);
	if (pos >= 0)
		{
		const TInt numNotifs = iContextSwitchNotifs.Count();
		for (TInt i = pos; i < numNotifs; ++i)
			{
			const TContextSwitchNotif& thisNotif = iContextSwitchNotifs[i];
			if (thisNotif.iId == aNewBtraceThreadId)
				{
				relevant = ETrue;
				thisNotif.iObserver->HandleContextSwitchL(aTickCount, aNewBtraceThreadId, MBtraceCpuUsageObserver::EToThisThread);
				}
			else
				{
				break;
				}
			}
		}

	// Update thread idle notifications' last switched out time.
	pos = iThreadIdleNotifs.SpecificFindInUnsignedKeyOrder(TThreadIdleNotif(iLastBtractThreadId), EArrayFindMode_First);
	if (pos >= 0)
		{
		const TInt numNotifs = iThreadIdleNotifs.Count();
		for (TInt i = pos; i < numNotifs; ++i)
			{
			TThreadIdleNotif& thisNotif = iThreadIdleNotifs[i];
			if (thisNotif.iId == iLastBtractThreadId)
				{
				relevant = ETrue;
				thisNotif.iLastSwitchedOut = aTickCount;
				}
			else
				{
				break;
				}
			}
		}

	// Update thread idle notifications' ever scheduled.
	pos = iThreadIdleNotifs.SpecificFindInUnsignedKeyOrder(TThreadIdleNotif(aNewBtraceThreadId), EArrayFindMode_First);
	if (pos >= 0)
		{
		const TInt numNotifs = iThreadIdleNotifs.Count();
		for (TInt i = pos; i < numNotifs; ++i)
			{
			TThreadIdleNotif& thisNotif = iThreadIdleNotifs[i];
			if (thisNotif.iId == aNewBtraceThreadId)
				{
				relevant = ETrue;
				thisNotif.iEverScheduled = ETrue;
				}
			else
				{
				break;
				}
			}
		}

	iLastBtractThreadId = aNewBtraceThreadId;

	if (relevant)
		{
		++iNumRelevantFrames;
		}
	}

TInt CompareUsage(const MBtraceCpuUsageObserver::TCpuUsage& aFirst,const MBtraceCpuUsageObserver::TCpuUsage& aSecond)
	{
	if (aFirst.iNumFastTicks > aSecond.iNumFastTicks)
		{
		return -1;
		}
	else if (aFirst.iNumFastTicks < aSecond.iNumFastTicks)
		{
		return 1;
		}
	else
		{
		return 0;
		}
	}

void CBtraceCpuUsage::TestCpuUsagePeriodL(const TBtraceTickCount& aTickCount)
	{
	for (TInt i = (iCpuUsageNotifs.Count() - 1); i >= 0; --i)
		{
		RCpuUsageNotif& thisNotif = iCpuUsageNotifs[i];
		if ((aTickCount.iNano - thisNotif.iStartTickCount) >= thisNotif.iNumNanoTicks)
			{
			thisNotif.iUsage.Sort(TLinearOrder<MBtraceCpuUsageObserver::TCpuUsage>(CompareUsage));
			TArray<MBtraceCpuUsageObserver::TCpuUsage> array(thisNotif.iUsage.Array());
			thisNotif.iObserver->HandleCpuUsageL(aTickCount, array);
			if (thisNotif.iPersistence == ENotificationOneShot)
				{
				thisNotif.Close();
				iCpuUsageNotifs.Remove(i);
				}
			}
		}
	}

void CBtraceCpuUsage::TestThreadIdlenessL(const TBtraceTickCount& aTickCount)
	{
	for (TInt i = (iThreadIdleNotifs.Count() - 1); i >= 0; --i)
		{
		const TThreadIdleNotif& thisNotif = iThreadIdleNotifs[i];
		//iReader.Log(_L("CBtraceCpuUsage::TestThreadIdlenessL: now %d, lastswitch %d, remaining %d\r\n"), aTickCount.iNano, thisNotif.iLastSwitchedOut.iNano, thisNotif.iNumNanoTicks - (aTickCount.iNano - thisNotif.iLastSwitchedOut.iNano));
		if ((aTickCount.iNano > thisNotif.iLastSwitchedOut.iNano) && ((aTickCount.iNano - thisNotif.iLastSwitchedOut.iNano) >= thisNotif.iNumNanoTicks))
			{
			MBtraceCpuUsageObserver* observer = thisNotif.iObserver;
			TBtraceTickCount lastSwitchedOut = thisNotif.iLastSwitchedOut;
			TBtraceThreadId threadId = thisNotif.iId;
			MBtraceCpuUsageObserver::TIdleType idleType = thisNotif.iEverScheduled ? MBtraceCpuUsageObserver::EScheduledAtLeastOnce : MBtraceCpuUsageObserver::ENeverScheduled;

			if (thisNotif.iPersistence == ENotificationOneShot)
				{
				iThreadIdleNotifs.Remove(i);
				}

			observer->HandleThreadIdleL(lastSwitchedOut, threadId, idleType);
			}
		}
	}