commands/top/topfshell.cpp
changeset 0 7f656887cf89
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 // topfshell.cpp
       
     2 // 
       
     3 // Copyright (c) 2008 - 2010 Accenture. All rights reserved.
       
     4 // This component and the accompanying materials are made available
       
     5 // under the terms of the "Eclipse Public License v1.0"
       
     6 // which accompanies this distribution, and is available
       
     7 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 // 
       
     9 // Initial Contributors:
       
    10 // Accenture - Initial contribution
       
    11 //
       
    12 
       
    13 #include <e32hashtab.h>
       
    14 #include <fshell/ioutils.h>
       
    15 #include <fshell/memoryaccesscmd.h>
       
    16 #include "sampler.h"
       
    17 
       
    18 using namespace IoUtils;
       
    19 
       
    20 class CCmdTop : public CMemoryAccessCommandBase
       
    21 	{
       
    22 public:
       
    23 	static CCommandBase* NewLC();
       
    24 	~CCmdTop();
       
    25 private:
       
    26 	CCmdTop();
       
    27 	static TInt TimerCallback(TAny* aSelf);
       
    28 	void TimerCallback();
       
    29 	void UpdateL();
       
    30 	struct SThreadData;
       
    31 	static TInt SortReverse(const CCmdTop::SThreadData& aLeft, const CCmdTop::SThreadData& aRight);
       
    32 
       
    33 private: // From CCommandBase.
       
    34 	virtual const TDesC& Name() const;
       
    35 	virtual void DoRunL();
       
    36 	virtual void ArgumentsL(RCommandArgumentList& aArguments);
       
    37 	virtual void OptionsL(RCommandOptionList& aOptions);
       
    38 private:
       
    39 	RBuf8 iReadBuf;
       
    40 	struct SThreadData
       
    41 		{
       
    42 		TUint iId;
       
    43 		TInt iNumSamples;
       
    44 		TFullName iName; // Saves recalculating every time
       
    45 		};
       
    46 	RHashMap<TUint, SThreadData*> iSeenThreads; // Saves linear searching through iThreads for each sample (Maps thread id to SThreadData
       
    47 	RPointerArray<SThreadData> iThreads;
       
    48 	RSampler iSampler;
       
    49 	CPeriodic* iTimer;
       
    50 	TInt iRate;
       
    51 
       
    52 	CTextBuffer* iBuffer;
       
    53 	CTextFormatter* iFormatter;
       
    54 	TInt iNumConsoleLines;
       
    55 	TInt iNumLinesInLastUpdate;
       
    56 	};
       
    57 
       
    58 CCommandBase* CCmdTop::NewLC()
       
    59 	{
       
    60 	CCmdTop* self = new(ELeave) CCmdTop();
       
    61 	CleanupStack::PushL(self);
       
    62 	self->BaseConstructL();
       
    63 	return self;
       
    64 	}
       
    65 
       
    66 CCmdTop::~CCmdTop()
       
    67 	{
       
    68 	if (iTimer)
       
    69 		{
       
    70 		iTimer->Cancel();
       
    71 		}
       
    72 	delete iTimer;
       
    73 	if (iSampler.Handle())
       
    74 		{
       
    75 		iSampler.Stop();
       
    76 		iSampler.Close();
       
    77 		User::FreeLogicalDevice(KSamplerName);
       
    78 		}
       
    79 	delete iBuffer;
       
    80 	delete iFormatter;
       
    81 	iReadBuf.Close();
       
    82 	iThreads.ResetAndDestroy();
       
    83 	iSeenThreads.Close();
       
    84 	}
       
    85 
       
    86 CCmdTop::CCmdTop()
       
    87 	: CMemoryAccessCommandBase(EManualComplete), iRate(1000)
       
    88 	{
       
    89 	}
       
    90 
       
    91 const TDesC& CCmdTop::Name() const
       
    92 	{
       
    93 	_LIT(KName, "top");	
       
    94 	return KName;
       
    95 	}
       
    96 
       
    97 void CCmdTop::ArgumentsL(RCommandArgumentList& /*aArguments*/)
       
    98 	{
       
    99 	}
       
   100 
       
   101 void CCmdTop::OptionsL(RCommandOptionList& aOptions)
       
   102 	{
       
   103 	aOptions.AppendIntL(iRate, _L("rate"));
       
   104 	}
       
   105 
       
   106 EXE_BOILER_PLATE(CCmdTop)
       
   107 
       
   108 void CCmdTop::DoRunL()
       
   109 	{
       
   110 #ifdef FSHELL_MEMORY_ACCESS_SUPPORT
       
   111 	LoadMemoryAccessL();
       
   112 #endif
       
   113 
       
   114 	TInt err = User::LoadLogicalDevice(KSamplerName);
       
   115 	if (err != KErrNone && err != KErrAlreadyExists)
       
   116 		{
       
   117 		LeaveIfErr(err, _L("Couldn't load sampler ldd %S"), &KSamplerName);
       
   118 		}
       
   119 	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
       
   120 	iTimer = CPeriodic::NewL(CActive::EPriorityStandard);
       
   121 	iTimer->Start(iRate*1000, iRate*1000, TCallBack(&CCmdTop::TimerCallback, this));
       
   122 
       
   123 	LeaveIfErr(iSampler.Open(), _L("Couldn't open sampler"));
       
   124 	iSampler.Reset(ETrue); // Parameter is ignored
       
   125 	iSampler.Start(1000); // Always tell sampler to sample every millisecond, regardless of the frequency we update the UI
       
   126 
       
   127 	TSize size;
       
   128 	User::LeaveIfError(Stdout().GetScreenSize(size));
       
   129 	iNumConsoleLines = size.iHeight;
       
   130 	iBuffer = CTextBuffer::NewL(512);
       
   131 	iFormatter = CTextFormatter::NewL(size.iWidth);
       
   132 	Stdout().ClearScreen();
       
   133 	UpdateL(); // Just so we display something on screen before the first update
       
   134 	}
       
   135 
       
   136 TInt CCmdTop::TimerCallback(TAny* aSelf)
       
   137 	{
       
   138 	static_cast<CCmdTop*>(aSelf)->TimerCallback();
       
   139 	return 0;
       
   140 	}
       
   141 
       
   142 void CCmdTop::TimerCallback()
       
   143 	{
       
   144 	iReadBuf.Zero();
       
   145 	TRequestStatus stat;
       
   146 	iSampler.Read(iReadBuf, stat);
       
   147 	User::WaitForRequest(stat); // API used to be async, it's now sync but I haven't changed the interface
       
   148 	
       
   149 	// First off clear all the iNumSamples (as that was for the previous sampling period)
       
   150 	for (TInt i = 0; i < iThreads.Count(); i++)
       
   151 		{
       
   152 		iThreads[i]->iNumSamples = 0;
       
   153 		}
       
   154 
       
   155 	TInt numSamples = iReadBuf.Length() / sizeof(TUint32);
       
   156 	TUint32* buf = (TUint32*)iReadBuf.Ptr();
       
   157 	for (TInt i = 0; i < numSamples; i++)
       
   158 		{
       
   159 		TUint32 threadId = buf[i];
       
   160 		SThreadData** thread = iSeenThreads.Find(threadId);
       
   161 		if (thread)
       
   162 			{
       
   163 			(*thread)->iNumSamples++;
       
   164 			}
       
   165 		else
       
   166 			{
       
   167 			// New thread id
       
   168 			SThreadData* thread = new SThreadData;
       
   169 			if (!thread) continue;
       
   170 			TInt err = iThreads.Append(thread);
       
   171 			if (err)
       
   172 				{
       
   173 				delete thread;
       
   174 				continue;
       
   175 				}
       
   176 			err = iSeenThreads.Insert(threadId, thread);
       
   177 			if (err)
       
   178 				{
       
   179 				iThreads.Remove(iThreads.Count()-1);
       
   180 				delete thread;
       
   181 				continue;
       
   182 				}
       
   183 			thread->iNumSamples = 1;
       
   184 			thread->iId = threadId;
       
   185 			RThread rthread;
       
   186 #ifdef FSHELL_MEMORY_ACCESS_SUPPORT
       
   187 			err = iMemAccess.RThreadForceOpen(rthread, threadId);
       
   188 #else
       
   189 			err = rthread.Open(TThreadId(threadId));
       
   190 #endif
       
   191 			if (err == KErrNone)
       
   192 				{
       
   193 				thread->iName = rthread.FullName();
       
   194 				//PrettyName(EListThread, thread->iName);
       
   195 				rthread.Close();
       
   196 				}
       
   197 			else
       
   198 				{
       
   199 				thread->iName = _L("Tid: ");
       
   200 				thread->iName.AppendNum(threadId);
       
   201 				}
       
   202 			}
       
   203 		}
       
   204 	TRAP_IGNORE(UpdateL());
       
   205 	}
       
   206 
       
   207 TInt CCmdTop::SortReverse(const CCmdTop::SThreadData& aLeft, const CCmdTop::SThreadData& aRight)
       
   208 	{
       
   209 	// This sorts largest number of samples first 
       
   210 	TInt res = - (aLeft.iNumSamples - aRight.iNumSamples);
       
   211 	if (res == 0) return aLeft.iId - aRight.iId; // make sure there is a total ordering in event of tie break
       
   212 	return res;
       
   213 	}
       
   214 
       
   215 void CCmdTop::UpdateL()
       
   216 	{
       
   217 	// First, sort iThreads
       
   218 	iThreads.Sort(TLinearOrder<SThreadData>(&SortReverse));
       
   219 
       
   220 	TInt numSamples = iReadBuf.Size() / sizeof(TUint32);
       
   221 	
       
   222 	iBuffer->Zero();
       
   223 	iFormatter->Zero();
       
   224 	iBuffer->AppendL(_L("Tid\tThread name\tCPU usage\r\n"));
       
   225 
       
   226 	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
       
   227 		{
       
   228 		SThreadData& thread = *iThreads[i];
       
   229 		TReal percent = 100 * thread.iNumSamples / (TReal)numSamples;
       
   230 		iBuffer->AppendFormatL(_L("%d\t%S\t%00.2f%%\r\n"), thread.iId, &thread.iName, percent);
       
   231 		}
       
   232 	Stdout().SetCursorHeight(0);
       
   233 	iFormatter->TabulateL(0, 1, iBuffer->Descriptor(), ETruncateLongestColumn);
       
   234 	User::LeaveIfError(Stdout().SetCursorPosAbs(TPoint(0, 0)));
       
   235 	Stdout().Write(iFormatter->Descriptor());
       
   236 	TInt numLines = 1 + iThreads.Count(); // plus 1 for the title line
       
   237 	TInt numOldLines = iNumLinesInLastUpdate - numLines;
       
   238 	while (numOldLines > 0)
       
   239 		{
       
   240 		Stdout().ClearToEndOfLine();
       
   241 		Stdout().SetCursorPosRel(TPoint(0, 1));
       
   242 		--numOldLines;
       
   243 		}
       
   244 	iNumLinesInLastUpdate = numLines;
       
   245 	}