commands/top/sampler.cpp
changeset 0 7f656887cf89
--- /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++;
+	}