--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/top/sampler.cpp Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,334 @@
+// sampler.cpp
+//
+// Copyright (c) 1999 - 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 <platform.h>
+
+#include "sampler.h"
+//#include <nkern.h>
+//#include <kernel.h>
+#include <kern_priv.h>
+_LIT(KLddName,"topsampler");
+
+const TInt KMinRate=10;
+const TInt KMaxRate=1000;
+const TInt KRawBufSize = 20000; // Enough for 20 seconds of samples - since max expected refresh rate in UI is 10s we can do everything nice and simply with one buffer
+
+class DDeviceSampler : public DLogicalDevice
+ {
+public:
+ DDeviceSampler();
+ virtual TInt Install();
+ virtual void GetCaps(TDes8& aDes) const;
+ virtual TInt Create(DLogicalChannelBase*& aChannel);
+ };
+
+class DProfile : public DLogicalChannel
+ {
+public:
+ DProfile();
+ ~DProfile();
+protected:
+ virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
+ virtual void HandleMsg(TMessageBase* aMsg);
+private:
+ TInt StartSampling(TInt aRate);
+ TInt StopSampling();
+ TInt Reset();
+ //TInt GetErrors(TDes8* aDes);
+ TInt ProcessReadRequest();
+ void Complete(TInt aResult);
+ inline TBool Running()
+ {return iTimer.iState!=NTimer::EIdle;}
+private:
+ static void Sample(TAny*);
+ void DoSample();
+
+
+private:
+ TUint32 iStartTime;
+ TInt iRepeat;
+ TInt iPeriod;
+ DThread* iClient;
+ TRequestStatus* iReqStatus;
+ TDes8* iClientDes; // client des pointer
+ TUint32 iBuf1[KRawBufSize]; // First word is the current position in the buffer
+ TUint32 iBuf2[KRawBufSize]; // Have two buffers that we can switch between, this avoids us having to stop the sampler timer, or disable interrupts or whatever, when we want to drain the buffer
+ TUint32* iCurrentBuf; // Pointer to either iBuf1 or iBuf2
+
+ struct TReport
+ {
+ TUint iRawBufferErrCounter;
+ TUint iCodeSegErrCounter;
+ TInt iReportMask;
+ } iReport;
+
+ NTimer iTimer;
+ };
+
+DECLARE_STANDARD_LDD()
+ {
+ return new DDeviceSampler;
+ }
+
+DDeviceSampler::DDeviceSampler()
+//
+// Constructor
+//
+ {
+ //iParseMask=0;
+ //iUnitsMask=0;
+ iVersion=TVersion(1,0,0);
+ }
+
+TInt DDeviceSampler::Install()
+//
+// Install the device driver.
+//
+ {
+ TInt r=SetName(&KLddName);
+ return r;
+ }
+
+void DDeviceSampler::GetCaps(TDes8& /*aDes*/) const
+//
+// Return the capabilities.
+//
+ {
+ }
+
+TInt DDeviceSampler::Create(DLogicalChannelBase*& aChannel)
+//
+// Create a channel on the device.
+//
+ {
+ aChannel=new DProfile;
+ return aChannel?KErrNone:KErrNoMemory;
+ }
+
+DProfile::DProfile()
+ : iTimer(Sample,this)
+//
+// Constructor
+//
+ {
+ iCurrentBuf = iBuf1;
+ iCurrentBuf[0] = 1;
+ }
+
+DProfile::~DProfile()
+//
+// Destructor
+//
+ {
+ iTimer.Cancel();
+ Kern::SafeClose((DObject*&)iClient, NULL);
+ }
+
+TInt DProfile::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer)
+//
+// Create the channel from the passed info.
+//
+ {
+ if (!Kern::QueryVersionSupported(TVersion(1,0,1),aVer))
+ return KErrNotSupported;
+
+ iClient=&Kern::CurrentThread();
+ iClient->Open();
+ //Kern::SetThreadPriority(24);
+ SetDfcQ(Kern::DfcQue0());
+ iMsgQ.Receive();
+ return KErrNone;
+ }
+
+void DProfile::Complete(TInt aResult)
+//Completes user request
+ {
+ DEBUG_PROFILER(Kern::Printf("C");)
+ Kern::RequestComplete(iClient,iReqStatus,aResult);
+ }
+
+TInt DProfile::StartSampling(TInt aRate)
+ {
+ DEBUG_PROFILER(Kern::Printf("START");)
+ //Activate timer
+ aRate=Min(KMaxRate, Max(KMinRate, aRate));
+ iPeriod=1000/aRate;
+
+ if (!Running())
+ {
+ iCurrentBuf = iBuf1;
+ iCurrentBuf[0] = 1;
+ iTimer.OneShot(iPeriod);
+ }
+
+ DEBUG_PROFILER(Kern::Printf("START end");)
+ return KErrNone;
+ }
+
+TInt DProfile::Reset()
+ {
+//
+// Resets the device. It is the first message sent by profiler application.
+//
+ if (Running())
+ return KErrGeneral;
+
+ DEBUG_PROFILER(Kern::Printf("RST %d", aXIPOnly);)
+
+ iTimer.Cancel();
+ iPeriod=1;
+ iReqStatus=NULL;
+ iClientDes=NULL;
+
+ iReport.iRawBufferErrCounter = 0;
+ iReport.iCodeSegErrCounter = 0;
+ iReport.iReportMask = 0;
+ DEBUG_PROFILER(Kern::Printf("RST end");)
+ return KErrNone;
+ }
+
+TInt DProfile::StopSampling()
+//
+// Stops sampling
+//
+ {
+ DEBUG_PROFILER(Kern::Printf("STOP");)
+ if (Running())
+ {
+ iTimer.Cancel();
+ }
+ if (iReqStatus)
+ Complete(KErrNone);
+
+ DEBUG_PROFILER(Kern::Printf("STOP end");)
+ return KErrNone;
+ }
+
+TInt DProfile::ProcessReadRequest()
+ {
+ // Whatever the status of the profiler, return the contents of the current sampling buffer
+ DEBUG_PROFILER(Kern::Printf("READ");)
+ TInt max=Kern::ThreadGetDesMaxLength(iClient, iClientDes);
+ if (max<0)
+ return max;
+ if (max==0)
+ return KErrArgument;
+
+ TUint32* otherBuf = (iCurrentBuf == iBuf1 ? iBuf2 : iBuf1);
+ otherBuf[0] = 1; // Reset other buf
+ TUint32* bufToWrite = iCurrentBuf;
+ iCurrentBuf = otherBuf; // And switch do it, so we can safely mess with bufToWrite without the sampling ISR adding more data under our feet
+ TInt numSamples = bufToWrite[0] - 1; // Because bufToWrite[0] is an index and the data actually starts at index 1
+ TPtrC8 desToWrite((TUint8*)&bufToWrite[1], (numSamples)*sizeof(TUint32));
+
+ TInt r = Kern::ThreadDesWrite(iClient, iClientDes, desToWrite, 0, 0, iClient);
+ return r;
+ }
+
+void DProfile::HandleMsg(TMessageBase* aMsg)
+//
+// Client requests
+//
+ {
+ TInt r=KErrNone;
+ TThreadMessage& m=*(TThreadMessage*)aMsg;
+ //BEGIN TOMSCI commented out this check as it is too strict
+ /*
+ if (m.Client()!=iClient)
+ {
+ m.PanicClient(_L("SAMPLER"),EAccessDenied);
+ return;
+ }
+ */
+ //END TOMSCI
+ TInt id=m.iValue;
+ if (id==(TInt)ECloseMsg)
+ {
+ DEBUG_PROFILER(Kern::Printf("CLOSE");)
+ iTimer.Cancel();
+ m.Complete(KErrNone,EFalse);
+ iMsgQ.CompleteAll(KErrServerTerminated);
+ DEBUG_PROFILER(Kern::Printf("CLOSE end");)
+ return;
+ }
+ else if (id<0)
+ {
+ if (id!=~RSampler::ERequestRead)
+ {
+ TRequestStatus* pS=(TRequestStatus*)m.Ptr0();
+ Kern::RequestComplete(iClient,pS,KErrNotSupported);
+ }
+ if (iReqStatus)
+ {
+ m.PanicClient(_L("SAMPLER"),ERequestAlreadyPending);
+ return;
+ }
+ iReqStatus=(TRequestStatus*)m.Ptr0();
+ iClientDes=(TDes8*)m.Ptr1();
+ TInt err = ProcessReadRequest();
+ Complete(err);
+ }
+ else if (id==KMaxTInt)
+ {
+ TInt mask=m.Int0();
+ if (mask & (1<<RSampler::ERequestRead))
+ {
+ Complete(KErrCancel);
+ }
+ }
+ else
+ {
+ switch(id)
+ {
+ case RSampler::EControlStartProfile:
+ r=StartSampling(m.Int0());
+ break;
+ case RSampler::EControlStopProfile:
+ r=StopSampling();
+ break;
+ case RSampler::EControlResetProfile:
+ r=Reset();
+ break;
+ //case RSampler::EControlGetErrors:
+ // r=GetErrors((TDes8*)m.Ptr0());
+ // break;
+ default:
+ r=KErrNotSupported;
+ break;
+ }
+ }
+ m.Complete(r,ETrue);
+ }
+
+void DProfile::Sample(TAny* aPtr)
+ {
+ DProfile& d=*(DProfile*)aPtr;
+ d.DoSample();
+ }
+
+void DProfile::DoSample()
+ {
+ iTimer.Again(iPeriod);
+
+ TUint32& currentPos = iCurrentBuf[0];
+ if (currentPos < (TUint)KRawBufSize) // space in buffer
+ {
+ DThread* pT=Kern::NThreadToDThread(NKern::CurrentThread());
+ if (pT!=NULL)
+ {
+ iCurrentBuf[currentPos] = pT->iId;
+ currentPos++;
+ }
+ }
+ else
+ iReport.iRawBufferErrCounter++;
+ }