diff -r 000000000000 -r bde4ae8d615e os/kernelhwsrv/kerneltest/e32utils/profiler/sampler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/kernelhwsrv/kerneltest/e32utils/profiler/sampler.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -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 //temporary +#include // 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 localBuf; //Enough space to encode 3 zeros and 3 integers + DEBUG_PROFILER(Kern::Printf("GE");) + + TInt max=Kern::ThreadGetDesMaxLength(iClient,aDes); + if (maxIsQueued()) + 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<> 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 (spaceiPC & 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++; + } +