Adjust path in .mk fshell files.
// 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;
}