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