libraries/btrace_parser/src/btrace_cpuusage.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libraries/btrace_parser/src/btrace_cpuusage.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,344 @@
+// 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);
+			}
+		}
+	}