Tidied iocli exports, build macro tweaks.
Removed 4 overloads of CCommandBase::RunCommand[L] that are no longer used at all, and changed one more to not be exported as it's only used internally to iocli.dll.
fixed builds on platforms that don't support btrace or any form of tracing.
// 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;
}