commands/top/topfshell.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/top/topfshell.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,245 @@
+// topfshell.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 <e32hashtab.h>
+#include <fshell/ioutils.h>
+#include <fshell/memoryaccesscmd.h>
+#include "sampler.h"
+
+using namespace IoUtils;
+
+class CCmdTop : public CMemoryAccessCommandBase
+	{
+public:
+	static CCommandBase* NewLC();
+	~CCmdTop();
+private:
+	CCmdTop();
+	static TInt TimerCallback(TAny* aSelf);
+	void TimerCallback();
+	void UpdateL();
+	struct SThreadData;
+	static TInt SortReverse(const CCmdTop::SThreadData& aLeft, const CCmdTop::SThreadData& aRight);
+
+private: // From CCommandBase.
+	virtual const TDesC& Name() const;
+	virtual void DoRunL();
+	virtual void ArgumentsL(RCommandArgumentList& aArguments);
+	virtual void OptionsL(RCommandOptionList& aOptions);
+private:
+	RBuf8 iReadBuf;
+	struct SThreadData
+		{
+		TUint iId;
+		TInt iNumSamples;
+		TFullName iName; // Saves recalculating every time
+		};
+	RHashMap<TUint, SThreadData*> iSeenThreads; // Saves linear searching through iThreads for each sample (Maps thread id to SThreadData
+	RPointerArray<SThreadData> iThreads;
+	RSampler iSampler;
+	CPeriodic* iTimer;
+	TInt iRate;
+
+	CTextBuffer* iBuffer;
+	CTextFormatter* iFormatter;
+	TInt iNumConsoleLines;
+	TInt iNumLinesInLastUpdate;
+	};
+
+CCommandBase* CCmdTop::NewLC()
+	{
+	CCmdTop* self = new(ELeave) CCmdTop();
+	CleanupStack::PushL(self);
+	self->BaseConstructL();
+	return self;
+	}
+
+CCmdTop::~CCmdTop()
+	{
+	if (iTimer)
+		{
+		iTimer->Cancel();
+		}
+	delete iTimer;
+	if (iSampler.Handle())
+		{
+		iSampler.Stop();
+		iSampler.Close();
+		User::FreeLogicalDevice(KSamplerName);
+		}
+	delete iBuffer;
+	delete iFormatter;
+	iReadBuf.Close();
+	iThreads.ResetAndDestroy();
+	iSeenThreads.Close();
+	}
+
+CCmdTop::CCmdTop()
+	: CMemoryAccessCommandBase(EManualComplete), iRate(1000)
+	{
+	}
+
+const TDesC& CCmdTop::Name() const
+	{
+	_LIT(KName, "top");	
+	return KName;
+	}
+
+void CCmdTop::ArgumentsL(RCommandArgumentList& /*aArguments*/)
+	{
+	}
+
+void CCmdTop::OptionsL(RCommandOptionList& aOptions)
+	{
+	aOptions.AppendIntL(iRate, _L("rate"));
+	}
+
+EXE_BOILER_PLATE(CCmdTop)
+
+void CCmdTop::DoRunL()
+	{
+#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
+	LoadMemoryAccessL();
+#endif
+
+	TInt err = User::LoadLogicalDevice(KSamplerName);
+	if (err != KErrNone && err != KErrAlreadyExists)
+		{
+		LeaveIfErr(err, _L("Couldn't load sampler ldd %S"), &KSamplerName);
+		}
+	iReadBuf.CreateL(iRate * 2 * sizeof(TUint32)); // go twice as big as needed just to be safe, allows for AO not getting to run on time
+	iTimer = CPeriodic::NewL(CActive::EPriorityStandard);
+	iTimer->Start(iRate*1000, iRate*1000, TCallBack(&CCmdTop::TimerCallback, this));
+
+	LeaveIfErr(iSampler.Open(), _L("Couldn't open sampler"));
+	iSampler.Reset(ETrue); // Parameter is ignored
+	iSampler.Start(1000); // Always tell sampler to sample every millisecond, regardless of the frequency we update the UI
+
+	TSize size;
+	User::LeaveIfError(Stdout().GetScreenSize(size));
+	iNumConsoleLines = size.iHeight;
+	iBuffer = CTextBuffer::NewL(512);
+	iFormatter = CTextFormatter::NewL(size.iWidth);
+	Stdout().ClearScreen();
+	UpdateL(); // Just so we display something on screen before the first update
+	}
+
+TInt CCmdTop::TimerCallback(TAny* aSelf)
+	{
+	static_cast<CCmdTop*>(aSelf)->TimerCallback();
+	return 0;
+	}
+
+void CCmdTop::TimerCallback()
+	{
+	iReadBuf.Zero();
+	TRequestStatus stat;
+	iSampler.Read(iReadBuf, stat);
+	User::WaitForRequest(stat); // API used to be async, it's now sync but I haven't changed the interface
+	
+	// First off clear all the iNumSamples (as that was for the previous sampling period)
+	for (TInt i = 0; i < iThreads.Count(); i++)
+		{
+		iThreads[i]->iNumSamples = 0;
+		}
+
+	TInt numSamples = iReadBuf.Length() / sizeof(TUint32);
+	TUint32* buf = (TUint32*)iReadBuf.Ptr();
+	for (TInt i = 0; i < numSamples; i++)
+		{
+		TUint32 threadId = buf[i];
+		SThreadData** thread = iSeenThreads.Find(threadId);
+		if (thread)
+			{
+			(*thread)->iNumSamples++;
+			}
+		else
+			{
+			// New thread id
+			SThreadData* thread = new SThreadData;
+			if (!thread) continue;
+			TInt err = iThreads.Append(thread);
+			if (err)
+				{
+				delete thread;
+				continue;
+				}
+			err = iSeenThreads.Insert(threadId, thread);
+			if (err)
+				{
+				iThreads.Remove(iThreads.Count()-1);
+				delete thread;
+				continue;
+				}
+			thread->iNumSamples = 1;
+			thread->iId = threadId;
+			RThread rthread;
+#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
+			err = iMemAccess.RThreadForceOpen(rthread, threadId);
+#else
+			err = rthread.Open(TThreadId(threadId));
+#endif
+			if (err == KErrNone)
+				{
+				thread->iName = rthread.FullName();
+				//PrettyName(EListThread, thread->iName);
+				rthread.Close();
+				}
+			else
+				{
+				thread->iName = _L("Tid: ");
+				thread->iName.AppendNum(threadId);
+				}
+			}
+		}
+	TRAP_IGNORE(UpdateL());
+	}
+
+TInt CCmdTop::SortReverse(const CCmdTop::SThreadData& aLeft, const CCmdTop::SThreadData& aRight)
+	{
+	// This sorts largest number of samples first 
+	TInt res = - (aLeft.iNumSamples - aRight.iNumSamples);
+	if (res == 0) return aLeft.iId - aRight.iId; // make sure there is a total ordering in event of tie break
+	return res;
+	}
+
+void CCmdTop::UpdateL()
+	{
+	// First, sort iThreads
+	iThreads.Sort(TLinearOrder<SThreadData>(&SortReverse));
+
+	TInt numSamples = iReadBuf.Size() / sizeof(TUint32);
+	
+	iBuffer->Zero();
+	iFormatter->Zero();
+	iBuffer->AppendL(_L("Tid\tThread name\tCPU usage\r\n"));
+
+	for (TInt i = 0; i < iThreads.Count() && i < iNumConsoleLines-2; i++) // minus one for title, one for last line of screen which I can't seem to figure out how to make use of
+		{
+		SThreadData& thread = *iThreads[i];
+		TReal percent = 100 * thread.iNumSamples / (TReal)numSamples;
+		iBuffer->AppendFormatL(_L("%d\t%S\t%00.2f%%\r\n"), thread.iId, &thread.iName, percent);
+		}
+	Stdout().SetCursorHeight(0);
+	iFormatter->TabulateL(0, 1, iBuffer->Descriptor(), ETruncateLongestColumn);
+	User::LeaveIfError(Stdout().SetCursorPosAbs(TPoint(0, 0)));
+	Stdout().Write(iFormatter->Descriptor());
+	TInt numLines = 1 + iThreads.Count(); // plus 1 for the title line
+	TInt numOldLines = iNumLinesInLastUpdate - numLines;
+	while (numOldLines > 0)
+		{
+		Stdout().ClearToEndOfLine();
+		Stdout().SetCursorPosRel(TPoint(0, 1));
+		--numOldLines;
+		}
+	iNumLinesInLastUpdate = numLines;
+	}