--- /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++;
+ }
+