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