kerneltest/e32utils/profiler/sampler.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32utils/profiler/sampler.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1136 @@
+// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "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:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32utils\profiler\sampler.cpp
+// 
+//
+
+#include "platform.h"
+
+#include "sampler.h"
+#include <kernel/kern_priv.h>		//temporary
+#include <memmodel/epoc/plat_priv.h> // needed for DEpocCodeSeg
+_LIT(KLddName,"Sampler");
+
+TKName KiDFCThread = _L("Running from iDFC");
+TKName KiDFCProcess = _L("N/A");
+TUint  KiDFCId = (TUint)-1; //both process and thread assigned to iDFC will have 'fake' id=-1
+
+const TInt KMajorVersionNumber=2;
+const TInt KMinorVersionNumber=0;
+const TInt KBuildVersionNumber=0;
+
+const TInt KMinRate=10;
+const TInt KMaxRate=1000;
+const TInt KRawBufSize=256;
+const TInt KCookedBufSize=0x2000;
+const TInt KCodeBufSize=0x2000;
+
+const TInt KMaxCreateCodeSegRecordSize = 300; //Max size of the encoded CodeSegCreate record.
+const TInt KMaxErrorReportRecordSize = 18;    //Max size of the encoded ErrorReport record. (3 zeros and 3 integers)
+const TInt KRequiredFreeSpace=512;
+
+//Bit mask in report
+const TInt KNonXIPModeActive=1; 
+const TInt KNoDebugSupport=2;
+
+
+#define PUT(p,x,e,s)	{*(p)++=(x); if ((p)==(e)) (p)-=(s);}
+#define GET_A_BYTE(p,x,e,s)	{*(x)++=*(p)++; if ((p)==(e)) (p)-=(s);}
+
+#define	TAG(obj)		(*(TUint32*)&(obj->iAsyncDeleteNext))
+
+#define CODESEGBUFEND (iCodeSegBuffer+KCodeBufSize)
+#define COOKEDBUFEND (iCookedBuf+KCookedBufSize)
+
+extern TUint IntStackPtr();
+extern TUint32 SPSR();
+extern TUint IDFCRunning();
+
+// global Dfc Que
+TDynamicDfcQue* gDfcQ;
+
+class DDeviceSampler : public DLogicalDevice
+	{
+public:
+	DDeviceSampler();
+	~DDeviceSampler();
+	virtual TInt Install();
+	virtual void GetCaps(TDes8& aDes) const;
+	virtual TInt Create(DLogicalChannelBase*& aChannel);
+	};
+
+struct SRawSample
+	{
+	TLinAddr iPC;
+	TUint32 iSampleCounter;
+	TUint32 iThreadId;
+	};
+
+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 GetSegments(TDes8* aDes);
+	TInt StartSampling(TInt aRate);
+	TInt StopSampling();
+	TInt Reset(TBool aXIPOnly);
+	TInt ResetSegments();
+	TInt Drain(TDes8* aDes);
+	TInt GetErrors(TDes8* aDes);
+	TInt ProcessReadRequest();
+	TInt DoDrainCooked();
+	TInt Cook();
+	void Complete(TInt aResult);
+	inline TBool Running()
+		{return iTimer.iState!=NTimer::EIdle;}
+private:
+	static void Sample(TAny*);
+	static void Dfc(TAny*);
+	static TUint KernelEventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aPrivateData);
+	void LogCodeSegEvent(TKernelEvent aEvent, DEpocCodeSeg *pCodeSeg);
+
+private:
+	static TUint8* EncodeTag(TUint8* p, TUint8* e);
+	static TUint8* EncodeInt(TUint8* p, TUint8* e, TInt aValue);
+	static TUint8* EncodeUint(TUint8* p, TUint8* e, TUint aValue);
+	static TUint8* EncodeText(TUint8* p, TUint8* e, const TDesC& aDes);
+	static TUint8* EncodeRepeat(TUint8* p, TUint8* e, DProfile* aProfile);
+	TUint8* EncodeThread(TUint8* p, TUint8* e, DThread* aThread);
+	TUint8* EncodeIDFC(TUint8* p, TUint8* e);
+	
+	TUint8* PutStream(TUint8* p, TUint8* e, const TUint8* aSource, TInt aSize);
+	TUint8* GetStream(TUint8* p, TUint8* e, TInt8* aDest, TInt aSize);	
+	TBool CookCodeSeg(TBool aPutAll, TInt aSampleCounter);
+
+private:
+	TUint32 iStartTime;
+	TInt iRepeat;
+	SRawSample iLast;
+	TInt iPeriod;
+	NTimer iTimer;
+	TDfc iDfc;
+	TUint* iIntStackTop;
+	DThread* iClient;
+	TRequestStatus* iReqStatus;
+	TInt iPos;			// client des pos
+	TInt iRemain;		// space left in client des
+	TDes8* iDes;		// client des pointer
+	TUint8 iRPut;		// raw buffer put index
+	TUint8 iRGet;		// raw buffer get index
+	TUint8* iCPut;		// cooked buffer put
+	TUint8* iCGet;		// cooked buffer get
+	SRawSample iRawBuf[KRawBufSize];
+	TUint8 iCookedBuf[KCookedBufSize];
+	
+	DKernelEventHandler* iKernelEvHandler;
+
+	TInt iNextSampleCounter;
+	TBool iXIPOnly;
+	TBool iMarkedOnlySegments;	// True during GettingSegments phase in which event handler...
+								// ... collects only the events from marked segments.
+	TUint8 iCodeSegBuffer[KCodeBufSize];
+	TUint8* iCSPut;		// CodeSeg buffer put
+	TUint8* iCSGet;		// CodeSeg buffer get
+	TUint iIDFCSeenBefore;
+	struct TReport
+		{
+		TUint iRowBufferErrCounter;
+		TUint iCodeSegErrCounter;
+		TInt  iReportMask; 
+		} iReport;
+	};
+
+DECLARE_STANDARD_LDD()
+	{
+	return new DDeviceSampler;
+	}
+
+DDeviceSampler::DDeviceSampler()
+//
+// Constructor
+//
+	{
+	//iParseMask=0;
+	//iUnitsMask=0;
+	iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
+	}
+
+const TInt KDSamplerThreadPriority = 27;
+_LIT(KDSamplerThread,"DSamplerThread");
+
+TInt DDeviceSampler::Install()
+//
+// Install the device driver.
+//
+	{
+	// Allocate a kernel thread to run the DFC 
+	TInt r = Kern::DynamicDfcQCreate(gDfcQ, KDSamplerThreadPriority, KDSamplerThread);
+
+	if (r != KErrNone)
+		return r; 	
+
+	r=SetName(&KLddName);
+	return r;
+	}
+
+void DDeviceSampler::GetCaps(TDes8& aDes) const
+//
+// Return the capabilities.
+//
+	{
+	}
+
+/**
+  Destructor
+*/
+DDeviceSampler::~DDeviceSampler()
+	{
+	if (gDfcQ)
+		gDfcQ->Destroy();
+	}
+
+TInt DDeviceSampler::Create(DLogicalChannelBase*& aChannel)
+//
+// Create a channel on the device.
+//
+	{
+	aChannel=new DProfile;
+	return aChannel?KErrNone:KErrNoMemory;
+	}
+
+DProfile::DProfile()
+	:	iTimer(Sample,this),
+		iDfc(Dfc,this,NULL,7)
+//
+// Constructor
+//
+	{
+	}
+
+DProfile::~DProfile()
+//
+// Destructor
+//
+	{
+	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);
+	iIntStackTop=(TUint*)IntStackPtr();
+	SetDfcQ(gDfcQ);
+	iDfc.SetDfcQ(iDfcQ);
+	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())
+		iTimer.OneShot(iPeriod);
+	
+	DEBUG_PROFILER(Kern::Printf("START end");)
+	return KErrNone;
+	}
+
+TInt DProfile::GetSegments(TDes8* aDes)
+//
+// Collects and marks all non-XIP segments.
+//
+	{
+	DEBUG_PROFILER(Kern::Printf("GS");)
+	TInt max=Kern::ThreadGetDesMaxLength(iClient,aDes);
+	Kern::ThreadDesWrite(iClient,aDes,KNullDesC8,0,0,iClient);//Set length to zero
+	TInt current = 0;
+
+	Kern::AccessCode();
+	
+	// Take all records that are collected by event handler first. They may be only Delete CodeSeg 
+	// events of tagged(marked) segments. On the first GetSegments call, cooked buffer also contains Profile Tag.
+
+	CookCodeSeg(ETrue, 0); // Transfer/encode from CodeSeg buffer into cooked buffer
+	current = iCPut-iCGet;
+	if (current)
+		{
+		if (current < max)
+			{//Copy data into user side descriptor
+			TPtrC8 aPtr(iCGet, current);
+			Kern::ThreadDesWrite(iClient,aDes,aPtr,0,KChunkShiftBy0,iClient);
+			}
+		else
+			{	
+			//This is very unlikely as in this stage we collect only CodeSeg Delete events of the marked segments.
+			//It cannot happen on the first call, as there are no marked segments - which means that Profiler Tag is OK.
+			iReport.iCodeSegErrCounter++;
+			}
+		}
+	iCGet = iCPut = iCookedBuf; //Reset the cooked buffer
+	
+	//Collect all non-XIP segments that are not already marked.
+
+	SDblQue* p = Kern::CodeSegList();
+	SDblQueLink* anchor=&p->iA;
+	SDblQueLink* a=anchor->iNext;
+	for (; a!=anchor; a=a->iNext) 
+		{
+		DEpocCodeSeg* pSeg = (DEpocCodeSeg*) _LOFF(a, DCodeSeg, iLink);
+		if (pSeg->iXIP || pSeg->iMark&DCodeSeg::EMarkProfilerTAG)
+			continue;
+		if (current > (max-KMaxCreateCodeSegRecordSize))
+			break;//No more space. Finish now and wait for another GetSegments request.
+			
+		pSeg->iMark |= DCodeSeg::EMarkProfilerTAG;	//Mark this segment
+		LogCodeSegEvent(EEventAddCodeSeg, pSeg); 	//Place this record into CodeSeg buffer ...
+		CookCodeSeg(ETrue, 0);						//...and encode it into cooked buffer
+		TPtrC8 aPtr(iCGet, iCPut-iCGet);
+		Kern::ThreadDesWrite(iClient,aDes,aPtr,current,KChunkShiftBy0,iClient);//Copy record into user desc.
+		current += iCPut-iCGet;
+		iCPut = iCGet = iCookedBuf; //Reset cooked buffer
+		}
+
+	if (!current)//This will be the last GetSegments call. From now on, all events have to be recorded.
+		iMarkedOnlySegments = EFalse;
+	
+	Kern::EndAccessCode();
+	DEBUG_PROFILER(Kern::Printf("GS end %d",current);)
+	return KErrNone;
+	}
+
+TInt DProfile::ResetSegments()
+//
+// Unmarks all non-XIP segments 
+// Sets device into GettingSegments mode in which only the events of the marked Code Segments will be recorder
+// 
+	{
+	DEBUG_PROFILER(Kern::Printf("RS");)
+	if (iXIPOnly)
+		return KErrGeneral;
+	
+	Kern::AccessCode();
+	SDblQue* p = Kern::CodeSegList();
+	SDblQueLink* anchor=&p->iA;
+	SDblQueLink* a=anchor->iNext;
+	for (; a!=anchor; a=a->iNext) 
+		{
+		DEpocCodeSeg* pSeg = (DEpocCodeSeg*) _LOFF(a, DCodeSeg, iLink);
+		if (!pSeg->iXIP)
+			pSeg->iMark &= ~DCodeSeg::EMarkProfilerTAG;
+		}
+
+	if (DKernelEventHandler::DebugSupportEnabled())
+		{
+		DEBUG_PROFILER(Kern::Printf("RS add handler");)
+		iKernelEvHandler->Add();
+		iReport.iReportMask|= KNonXIPModeActive;
+		}
+	else
+		iReport.iReportMask|= KNoDebugSupport;	
+	
+	iMarkedOnlySegments = ETrue;	
+	Kern::EndAccessCode();
+	DEBUG_PROFILER(Kern::Printf("RS end");)
+	return KErrNone;
+	}
+
+TInt DProfile::Reset(TBool aXIPOnly)
+	{
+//
+// Resets the device. It is the first message sent by profiler application.
+//	
+	if (Running())
+		return KErrGeneral;
+	
+	DEBUG_PROFILER(Kern::Printf("RST %d", aXIPOnly);)
+
+	iXIPOnly = aXIPOnly;
+
+	iTimer.Cancel();
+	iDfc.Cancel();
+	iLast.iPC=0;
+	iLast.iSampleCounter=0;
+	iLast.iThreadId=0;
+	iRepeat=0;
+	iPeriod=1;
+	iReqStatus=NULL;
+	iRPut=0;				// raw buffer put index
+	iRGet=0;				// raw buffer get index
+	iCPut=EncodeTag(iCookedBuf,COOKEDBUFEND); //cooked buffer put
+	iCGet=iCookedBuf;		// cooked buffer get
+	iPos=0;					// client des pos
+	iDes=NULL;				// client des pointer
+	iStartTime=NKern::TickCount();
+
+	iReport.iRowBufferErrCounter = 0;
+	iReport.iCodeSegErrCounter = 0;
+	iReport.iReportMask = 0;
+	iNextSampleCounter = 0;
+	iCSPut=iCodeSegBuffer;	// CodeSeg buffer put
+	iCSGet=iCodeSegBuffer;	// CodeSeg buffer get
+	iMarkedOnlySegments = EFalse;
+	iIDFCSeenBefore = EFalse;
+	if (!iXIPOnly)
+		iKernelEvHandler = new DKernelEventHandler(KernelEventHandler, this);
+	
+	DEBUG_PROFILER(Kern::Printf("RST end");)
+	return KErrNone;
+	}
+
+TInt DProfile::StopSampling()
+//
+// Stops sampling
+//
+	{
+	DEBUG_PROFILER(Kern::Printf("STOP");)
+	if (Running())
+		{
+		iTimer.Cancel();
+		Dfc(this);
+		}
+	if (iReqStatus)
+		Complete(KErrNone);
+	
+	DEBUG_PROFILER(Kern::Printf("STOP end");)
+	return KErrNone;
+	}
+
+TInt DProfile::GetErrors(TDes8* aDes)
+//
+// Returns error report and closes event handler
+//
+	{
+	TInt r = KErrNone;
+	TBuf8<KMaxErrorReportRecordSize> localBuf; //Enough space to encode 3 zeros and 3 integers
+	DEBUG_PROFILER(Kern::Printf("GE");)
+
+	TInt max=Kern::ThreadGetDesMaxLength(iClient,aDes);
+	if (max<KMaxErrorReportRecordSize)
+		return KErrArgument;
+	
+	Kern::ThreadDesWrite(iClient,aDes,KNullDesC8,0,0,iClient);//set zero length
+	
+	TUint8* p = (TUint8*)localBuf.Ptr();
+	TUint8* e = p+KMaxErrorReportRecordSize;
+	p = EncodeInt (p, e, 0);
+	p = EncodeUint(p, e, 0);
+	p = EncodeUint(p, e, 0);
+
+	p = EncodeUint(p, e, iReport.iRowBufferErrCounter);
+	p = EncodeUint(p, e, iReport.iCodeSegErrCounter);
+	p = EncodeUint(p, e, iReport.iReportMask);
+
+	localBuf.SetLength(p-localBuf.Ptr());
+	r=Kern::ThreadDesWrite(iClient,aDes,localBuf,0,KChunkShiftBy0,iClient);
+	
+	if(iKernelEvHandler && iKernelEvHandler->IsQueued())
+		iKernelEvHandler->Close();
+
+	DEBUG_PROFILER(Kern::Printf("GE end %d %d %d", iReport.iRowBufferErrCounter, iReport.iCodeSegErrCounter, iReport.iReportMask);)
+	return r;
+	}
+
+
+TInt DProfile::Drain(TDes8* aDes)
+//
+// Collects any remaining data
+//
+	{
+	DEBUG_PROFILER(Kern::Printf("D");)		
+	if (Running())
+		return KErrGeneral;
+	// we can assume read request is not pending
+	TInt max=Kern::ThreadGetDesMaxLength(iClient,aDes);
+	if (max<0)
+		return max;
+	if (max==0)
+		return KErrArgument;
+	TInt r=Kern::ThreadDesWrite(iClient,aDes,KNullDesC8,0,0,iClient);		// set client descriptor length to zero
+	if (r!=KErrNone)
+		return r;
+	iDes=aDes;
+	iRemain=max;
+	iPos=0;
+	iReqStatus=NULL;
+	TInt n=-1;
+	while (n)
+		{
+		r=DoDrainCooked();				// drain any cooked data if possible
+		if (r<0 && r!=KErrUnderflow)
+			return r;					// error writing client buffer
+		n=Cook();						// cook the samples, return number cooked
+		}
+
+	// there might still be data left over
+	DEBUG_PROFILER(Kern::Printf("D end");)
+	return KErrNone;
+	}
+
+TInt DProfile::ProcessReadRequest()
+	{
+// If the profiler is stopped and there is available data, return it immediately and complete the request
+// If the profiler is stopped and there is no data, wait.
+// If the profiler is running, retrieve any data available now, if more is req'd set the trigger
+	DEBUG_PROFILER(Kern::Printf("READ");)
+	TInt max=Kern::ThreadGetDesMaxLength(iClient,iDes);
+	if (max<0)
+		return max;
+	if (max==0)
+		return KErrArgument;
+	TInt r=Kern::ThreadDesWrite(iClient,iDes,KNullDesC8,0,0,iClient);		// set client descriptor length to zero
+	if (r!=KErrNone)
+		return r;
+	iRemain=max;
+	iPos=0;
+	TInt n=-1;
+	TBool read=EFalse;
+	while (n)
+		{
+		r=DoDrainCooked();				// drain any cooked data if possible
+		if (r!=KErrUnderflow)
+			read=ETrue;					// we've got something
+		if (r>0)
+			return KErrNone;			// request completed, so finish
+		if (r!=KErrNone && r!=KErrUnderflow)
+			return r;					// error writing client buffer
+		n=Cook();						// cook the samples, return number cooked
+		}
+	if (!Running() && read)
+		return KErrCompletion;			// if stopped and data read, return it
+	return KErrNone;					// wait
+	}
+
+TInt DProfile::DoDrainCooked()
+//
+// Copies encoded data from Cook buffer into user side descriptor (iDes).
+// Returns:
+// KErrUnderflow if all the data was already transfered or the desciptor was already full before the call.
+// KErrNone if there is still remaining space available in the descriptor.
+// 1  - descriptor is full and user request is completed.
+// Error code other then KErrNone if writing to the user memory fails
+//
+	{
+	TInt avail=iCPut-iCGet;
+	if (avail<0)
+		avail+=KCookedBufSize;
+	TInt len=Min(avail,iRemain);
+	if (len)
+		{
+		TUint8* pE=iCookedBuf+KCookedBufSize;
+		TInt len1=Min(len,pE-iCGet);
+		TPtrC8 local(iCGet,len1);
+		TInt r=Kern::ThreadDesWrite(iClient, iDes, local, iPos, KChunkShiftBy0, iClient);
+		if (r!=KErrNone)
+			return r;
+		len-=len1;
+		TUint8* pG=iCGet+len1;
+		if (pG==pE)
+			pG=iCookedBuf;
+		iCGet=pG;
+		iRemain-=len1;
+		iPos+=len1;
+		if (len) // will be > 0 if there are remaining data at the beginning of Cooked buffer to be copied.
+			{
+			TPtrC8 local(iCGet,len);
+			r=Kern::ThreadDesWrite(iClient, iDes, local, iPos, KChunkShiftBy0, iClient);
+			if (r!=KErrNone)
+				return r;
+			iCGet+=len;
+			iRemain-=len;
+			iPos+=len;
+			}
+		if (iRemain==0 && iReqStatus)
+			{
+			Complete(KErrNone);
+			return 1;
+			}
+		return KErrNone;
+		}
+	return KErrUnderflow;
+	}
+
+void DProfile::HandleMsg(TMessageBase* aMsg)
+//
+// Client requests
+//
+	{
+	TInt r=KErrNone;
+	TThreadMessage& m=*(TThreadMessage*)aMsg;
+	TInt id=m.iValue;
+	// Allow the client thread to send a message or system critical thread
+	// to send a close message as this is probably the supervisor thread doing clean up
+	if (m.Client()!=iClient && 
+		!((m.Client()->iFlags&KThreadFlagSystemCritical) && id==(TInt)ECloseMsg))
+		{
+		m.PanicClient(_L("SAMPLER"),EAccessDenied);
+		return;
+		}
+	if (id==(TInt)ECloseMsg)
+		{
+		DEBUG_PROFILER(Kern::Printf("CLOSE");)
+		iTimer.Cancel();
+		iDfc.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();
+		iDes=(TDes8*)m.Ptr1();
+		r=ProcessReadRequest();
+		if (r!=KErrNone)
+			{
+			if (r==KErrCompletion)
+				r=KErrNone;
+			Complete(r);
+			}
+		r=KErrNone;
+		}
+	else if (id==KMaxTInt)
+		{
+		TInt mask=m.Int0();
+		if (mask & (1<<RSampler::ERequestRead))
+			{
+			Complete(KErrCancel);
+			}
+		}
+	else
+		{
+		switch(id)
+			{
+			case RSampler::EControlGetSegments:
+				r=GetSegments((TDes8*)m.Ptr0());
+				break;
+			case RSampler::EControlStartProfile:
+				r=StartSampling(m.Int0());
+				break;
+			case RSampler::EControlStopProfile:
+				r=StopSampling();
+				break;
+			case RSampler::EControlResetProfile:
+				r=Reset((TBool)m.Ptr0());
+				break;
+			case RSampler::EControlResetSegments:
+				r=ResetSegments();
+				break;
+			case RSampler::EControlDrain:
+				r=Drain((TDes8*)m.Ptr0());
+				break;
+			case RSampler::EControlGetErrors:
+				r=GetErrors((TDes8*)m.Ptr0());
+				break;
+			default:
+				r=KErrNotSupported;
+				break;
+			}
+		}
+	m.Complete(r,ETrue);
+	}
+
+TUint8* DProfile::EncodeTag(TUint8* p, TUint8* e)
+//
+// Encode a tag and version to the trace data. This allows the offline analyser to 
+// identify the sample data.
+//
+	{
+	_LIT(KTraceTag,"profile");
+	p=EncodeText(p,e,KTraceTag);
+	p=EncodeUint(p,e,KMajorVersionNumber);
+	return p;
+	}
+
+TUint8* DProfile::EncodeInt(TUint8* p, TUint8* e, TInt aValue)
+//
+// Encode a 32 bit signed integer into the data stream
+// This has to deal with wrap around at the end of the buffer
+//
+	{
+	TUint byte;
+	for (;;)
+		{
+		byte = aValue & 0x7f;
+		if ((aValue >> 6) == (aValue >> 7))
+			break;
+		aValue >>= 7;
+		PUT(p,(TUint8)byte,e,KCookedBufSize);
+		}
+	PUT(p,(TUint8)(byte|0x80),e,KCookedBufSize);
+	return p;
+	}
+
+TUint8* DProfile::EncodeUint(TUint8* p, TUint8* e, TUint aValue)
+//
+// Encode a 32 bit unsigned integer into the data stream
+// This has to deal with wrap around at the end of the buffer
+//
+	{
+	TUint byte;
+	for (;;)
+		{
+		byte = aValue & 0x7f;
+		aValue >>= 7;
+		if (aValue == 0)
+			break;
+		PUT(p,(TUint8)byte,e,KCookedBufSize);
+		}
+	PUT(p,(TUint8)(byte|0x80),e,KCookedBufSize);
+	return p;
+	}
+
+TUint8* DProfile::EncodeText(TUint8* p, TUint8* e, const TDesC& aDes)
+//
+// Encode a descriptor into the data stream
+// This is currently limited to a descriptor that is up to 255 characters in length,
+// and Unicode characters are truncated to 8 bits
+//
+	{
+	TInt len=aDes.Length();
+	PUT(p,(TUint8)len,e,KCookedBufSize);
+	const TText* s = aDes.Ptr();
+	while (--len >= 0)
+		PUT(p,*s++,e,KCookedBufSize);
+	return p;
+	}
+
+TUint8* DProfile::EncodeIDFC(TUint8* p, TUint8* e)
+//
+// iDFC samples do not really belong to any thread.
+// However, the profiler protocol requires each sample to be associated to a particular thread.
+// This method will encode 'fake' process ID & name and thread name for iDFC sample in the data stream.
+// It will be embedded only for the very first sample from iDFCs.
+// (For the rest of iDFCs samples, threadID is sufficient - as for the real threads.)
+//
+	{
+	p=EncodeUint(p,e,KiDFCId);     //processID for iDFC
+	p=EncodeText(p,e,KiDFCProcess);//process name for iDFC
+	p=EncodeText(p,e,KiDFCThread); //thread name for iDFC
+	return p;
+	}
+
+TUint8* DProfile::EncodeThread(TUint8* p, TUint8* e, DThread* aThread)
+//
+// Encode a thread name in the data stream.
+// The thread is identified by its name, and the identity of its owning process.
+// If the process has not been identified in the data stream already, it's name is
+// also encoded.
+//
+	{
+	DProcess* pP=aThread->iOwningProcess;
+	TKName n;
+	p=EncodeUint(p,e,pP->iId);
+	if (TAG(pP)!=iStartTime)	// not seen this before
+		{
+		TAG(pP)=iStartTime;
+		// Provide the name matching this process ID
+		pP->Name(n);
+		p=EncodeText(p,e,n);
+		}
+	aThread->Name(n);
+	p=EncodeText(p,e,n);
+	return p;
+	}
+
+TUint8* DProfile::EncodeRepeat(TUint8* p, TUint8* e, DProfile* aP)
+//
+// Encode a repeated sequence of samples
+//
+	{
+	p=EncodeInt(p,e,0);
+	p=EncodeUint(p,e,aP->iRepeat);
+	aP->iRepeat = 0;
+	return p;
+	}
+
+TInt DProfile::CookCodeSeg(TBool aPutAll, TInt aSampleCounter)
+//
+// Transfers and encodes CodeSeg Create/Delete records from CodeSeg buffer into Cooked buffer.
+// If aPutAll = Etrue, all records will be transferred.
+// If aPutAll = EFalse, only records at the beginning of the queue that matches aSampleCounter will be transferred.
+// It stopps if there is less then KRequiredFreeSpace bytes available in cooked buffer.
+// Returns the number of the transferred records.
+//
+	{
+	if (iXIPOnly)
+		return 0;
+	
+	TInt n = 0;
+	TInt codeSampleCounter;//Will hold the sample counter of the record
+	TInt runAddress;
+	TInt codeSize;
+	TInt8 textLen;
+	TBuf<255> text;
+	TUint8* localCSGet = iCSGet;
+
+	FOREVER
+		{
+		//Check if there is any Code Seg record left.
+		if (iCSGet==iCSPut)
+			return n;//No records left
+		
+		//Check if the next record is due to be encoded. Both Create & Delete CodeSeg records start with sample counter.
+		localCSGet = iCSGet;
+		localCSGet = GetStream(localCSGet, CODESEGBUFEND, (TInt8*)(&codeSampleCounter), sizeof(TInt));
+		if (!aPutAll && codeSampleCounter!=aSampleCounter)
+			return n; //Still too early to insert the record into Cook Buffer
+			
+		//Check for the space in cook buffer
+		TInt cookAvailable = (TInt)iCGet - (TInt)iCPut;
+		if (cookAvailable <= 0)	
+			cookAvailable+=KCookedBufSize;
+		if (cookAvailable < KRequiredFreeSpace)
+			return n;//No space in Cook Buffer.
+		
+		//At this point it is for sure that we have to transfer some record to cook buffer
+		
+		n++;
+		iCSGet = localCSGet;
+		//The next field for both Create & Delete CodeSeg records is run address:
+		iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(&runAddress), sizeof(TInt));
+		
+		if (runAddress & 1)//LSB in run address idenifies the type of the record
+			{//CodeSegment Delete record. To be encoded as Int(0), UInt(0), UInt(RunAddress | 1)
+			iCPut = EncodeInt (iCPut, COOKEDBUFEND, 0);
+			iCPut = EncodeUint(iCPut, COOKEDBUFEND, 0);
+			iCPut = EncodeUint(iCPut, COOKEDBUFEND, runAddress);
+			}
+		else
+			{//CodeSegment Create record.
+			iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(&codeSize), sizeof(TInt));
+			iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(&textLen), sizeof(TInt8));
+			iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(text.Ptr()), textLen);
+			text.SetLength(textLen);
+			//To be encoded as Int(0), UInt(0), UInt(RunAddress), UInt(SegmentSize), Text(FileNeme)
+			iCPut = EncodeInt(iCPut, COOKEDBUFEND, 0);
+			iCPut = EncodeUint(iCPut, COOKEDBUFEND, 0);
+			iCPut = EncodeUint(iCPut, COOKEDBUFEND, runAddress);
+			iCPut = EncodeUint(iCPut, COOKEDBUFEND, codeSize);
+			iCPut = EncodeText(iCPut, COOKEDBUFEND, text);
+			}
+		}
+	}
+
+TInt DProfile::Cook()
+//
+// Transfers/encodes row data and code segments record into cooked buffer.
+// Returns the number of records (incl. both samples and codeSeg records) cooked.
+//
+	{
+	TUint8* p=iCPut;
+	TUint8* e=iCookedBuf+KCookedBufSize;
+	TInt n=0;
+	
+	FOREVER
+		{
+		iCPut=p; //update iCPut before calling CookCodeSeg
+		if ((iRGet==iRPut))
+			{//No more samples.
+			n+=CookCodeSeg(ETrue, 0); //Cook the remaining content of CodeSeg buffer.
+			break;
+			}
+
+		SRawSample* s=iRawBuf+iRGet;	// pointer to the next sample to be cooked
+
+		n+=CookCodeSeg(EFalse, s->iSampleCounter);//cook all codeSeg records than matches this sample counter
+		p=iCPut; //CookCodeSeg might have changed iCPut
+
+		TInt space=iCGet-p;
+		if (space<=0)
+			space+=KCookedBufSize;		// space remaining in cooked buffer
+		if (space<KRequiredFreeSpace)
+			break;						// if insufficient, finish
+
+		//Cook the next sample record from Row buffer		
+		++iRGet;
+		++n;
+		TBool newthread=s->iPC & 1;		// bit 0 of PC means so far unknown thread
+		TLinAddr pc=s->iPC &~ 1;
+		TUint rp = iRepeat;
+		TInt diff=TInt(pc-iLast.iPC);
+		if (!newthread)
+			{
+			if (s->iThreadId!=iLast.iThreadId)
+				diff|=1;
+			if (diff == 0)
+				{
+				iRepeat = rp + 1;	// Identical sample, bump up the repeat count
+				continue;
+				}
+			if (rp)
+				{
+				// Encode the repeat data
+				p = EncodeRepeat(p,e,this);
+				}
+			// Encode the PC difference
+			p = EncodeInt(p, e, diff);
+			if (diff & 1)
+				{
+				// Encode the new thread ID
+				iLast.iThreadId = s->iThreadId;
+				p = EncodeUint(p, e, s->iThreadId);
+				}
+			}
+		else
+			{
+			if (rp)
+				{
+				// Encode the repeat data
+				p = EncodeRepeat(p,e,this);
+				}
+			// Encode the PC difference
+			p = EncodeInt(p, e, diff|1);
+
+			if (s->iThreadId == KiDFCId)
+				{
+				// This is the first sample from iDFC. Encode 'threadID'
+				iLast.iThreadId = KiDFCId;
+				p = EncodeUint(p, e, KiDFCId);
+				// and encode processID, processName & threadName for this sample
+				p = EncodeIDFC(p, e);
+				}
+			else
+				{
+				// Encode the new thread ID
+				DThread* pT=(DThread*)s->iThreadId;
+				iLast.iThreadId = pT->iId;
+				p = EncodeUint(p, e, pT->iId);
+				// The thread is 'unknown' to this sample, so encode the thread name
+				p = EncodeThread(p, e, pT);
+				}
+			}
+		iLast.iPC=pc;
+		}
+	return n;
+	}
+
+void DProfile::Dfc(TAny* aPtr)
+//
+// Tranfers/encodes Row & CodeSeg buffers' content into Cook buffer (by Cook()), 
+// and copies encoded data into user side descriptor (by DoDrainCooked())
+//
+	{
+	DProfile& d=*(DProfile*)aPtr;
+	TInt n=-1;
+	while (n)
+		{
+		TInt r=d.DoDrainCooked();		// drain any cooked data if possible
+		if (r<0 && r!=KErrUnderflow)
+			{
+			d.Complete(r);				// error writing client buffer
+			break;
+			}
+		n=d.Cook();						// cook the samples, return number cooked
+		}
+	}
+
+TUint8* DProfile::PutStream(TUint8* p, TUint8* e, const TUint8* aSource, TInt aSize)
+//
+// Put data into CodeSeg stream 
+// This has to deal with wrap around at the end of the buffer
+//
+	{
+	for (TInt i = 0 ; i< aSize ; i++)
+		{
+		PUT(p,(TUint8)(*aSource),e,KCodeBufSize);
+		aSource++;
+		}
+	return p;
+	}
+
+TUint8* DProfile::GetStream(TUint8* p, TUint8* e, TInt8* aDest, TInt aSize)
+//
+// Get 32 bits from CodeSeg stream.
+// This has to deal with wrap around at the end of the buffer
+//
+	{
+	for (TInt i = 0 ; i< aSize ; i++)
+		GET_A_BYTE(p,aDest,e,KCodeBufSize);
+	return p;
+	}
+
+void DProfile::LogCodeSegEvent(TKernelEvent aEvent, DEpocCodeSeg *pCodeSeg)
+//
+// Records the event in CodeSeg buffer.
+//
+	{
+///
+///	
+	TUint8* localCSPut = iCSPut;
+	TInt available = KCodeBufSize + (TInt)iCSGet - (TInt)iCSPut;
+	if (available > KCodeBufSize)
+		available -= KCodeBufSize;
+
+	switch (aEvent)
+		{
+	case EEventAddCodeSeg:
+		{
+		TInt textOffset = 0;
+		TInt textSize= pCodeSeg->iFileName->Length();
+		//Restrict file name to max 255 sharacters. If bigger, record the last 255 bytes only
+		if (textSize > 255)
+			{
+			textOffset = textSize-255;
+			textSize = 255;
+			}
+		if ((available -= 13+textSize) < 0) //13 bytes needed for sample counter, run address, size and text size) 
+			{
+			iReport.iCodeSegErrCounter++;
+			return;
+			}
+		localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&iNextSampleCounter), sizeof(TInt));
+		localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&pCodeSeg->iRunAddress), sizeof(TInt));
+		localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&pCodeSeg->iSize), sizeof(TInt));
+		localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&textSize), sizeof(TInt8));
+		localCSPut = PutStream(	localCSPut, CODESEGBUFEND, pCodeSeg->iFileName->Ptr()+textOffset, textSize);
+		iCSPut = localCSPut;
+
+		DEBUG_PROFILER
+			(
+			TBuf<256> buf(textSize+1);
+			buf.Copy(pCodeSeg->iFileName->Ptr()+textOffset,textSize); buf.SetLength(textSize+1); buf[textSize]=0;
+			Kern::Printf("CREATE CS:%s, %x, %x,", buf.Ptr(), pCodeSeg->iRunAddress, pCodeSeg->iSize);
+			)
+		break;
+		}
+	case EEventRemoveCodeSeg:
+		{
+		if ((available-=8) < 0) //8 bytes needed for sample counter and run address
+			{
+			iReport.iCodeSegErrCounter++;
+			return;
+			}
+		TInt runAddress = pCodeSeg->iRunAddress | 1; 
+		localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&iNextSampleCounter), sizeof(TInt));
+		localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&runAddress), sizeof(TInt));
+		iCSPut = localCSPut;
+
+		DEBUG_PROFILER(Kern::Printf("DELETE CS:%x", pCodeSeg->iRunAddress);)
+		break;
+		}
+	default:
+		return;
+	}
+	if (available < KCodeBufSize/2) //Start emptying CodeSeg (and Raw Buffer, as well) Buffer if more then a half full.
+		iDfc.Enque();
+}
+
+TUint DProfile::KernelEventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aPrivateData)
+//
+// Logs non-XIP CodeSeg Create/Delete events.
+// In GettingSegments mode, it logs only deletion of the marked segments.
+// Runs in the content of the Kernel scheduler
+//
+{
+	if (aEvent==EEventAddCodeSeg || aEvent==EEventRemoveCodeSeg) 
+	{
+		DProfile *p = (DProfile*)aPrivateData;
+		DEpocCodeSeg* pCodeSeg = (DEpocCodeSeg*)(DCodeSeg*)a1;
+
+		Kern::AccessCode();
+		if ((!pCodeSeg->iXIP) && (!p->iMarkedOnlySegments || pCodeSeg->iMark&DCodeSeg::EMarkProfilerTAG))
+			p->LogCodeSegEvent(aEvent, pCodeSeg);
+		Kern::EndAccessCode();
+
+	}
+	return DKernelEventHandler::ERunNext;
+}
+
+
+void DProfile::Sample(TAny* aPtr)
+	{
+	DProfile& d=*(DProfile*)aPtr;
+	d.iTimer.Again(d.iPeriod);
+	TUint8 next_put=(TUint8)(d.iRPut+1);
+	if (next_put!=d.iRGet)		// space in raw buffer
+		{
+		DThread* pT=Kern::NThreadToDThread(NKern::CurrentThread());
+		if (pT!=NULL)
+			{
+			SRawSample* p=d.iRawBuf+d.iRPut;
+			d.iRPut=next_put;
+			p->iPC=((d.iIntStackTop)[-1]) & ~1; //clear LSB bit (in case of Jazelle code)
+			p->iSampleCounter=d.iNextSampleCounter++;
+			
+			if (IDFCRunning())
+				{
+				p->iThreadId=KiDFCId; //indicates iDFC running
+				if (!d.iIDFCSeenBefore)
+					{
+					d.iIDFCSeenBefore = ETrue;
+					p->iPC|=1;				// set bit 0 of PC to indicate new thread
+					}
+				else
+					{
+					TUint8 used=(TUint8)(d.iRPut-d.iRGet);
+					if (used<=KRawBufSize/2)
+						return;
+					}
+				}
+			else
+				{
+				
+				if (TAG(pT)!=d.iStartTime)	// not seen this before
+					{
+					TAG(pT)=d.iStartTime;
+					p->iThreadId=(TUint)pT;
+					p->iPC|=1;				// set bit 0 of PC to indicate new thread
+					}
+				else
+					{
+					p->iThreadId=pT->iId;
+					TUint8 used=(TUint8)(d.iRPut-d.iRGet);
+					if (used<=KRawBufSize/2)
+						return;
+					}
+				}
+			d.iDfc.Add();	// queue DFC if new thread seen or buffer more than half full
+			}
+		}
+	else
+		d.iReport.iRowBufferErrCounter++;
+	}
+