First public contribution.
1 // Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32utils\profiler\sampler.cpp
21 #include <kernel/kern_priv.h> //temporary
22 #include <memmodel/epoc/plat_priv.h> // needed for DEpocCodeSeg
23 _LIT(KLddName,"Sampler");
25 TKName KiDFCThread = _L("Running from iDFC");
26 TKName KiDFCProcess = _L("N/A");
27 TUint KiDFCId = (TUint)-1; //both process and thread assigned to iDFC will have 'fake' id=-1
29 const TInt KMajorVersionNumber=2;
30 const TInt KMinorVersionNumber=0;
31 const TInt KBuildVersionNumber=0;
33 const TInt KMinRate=10;
34 const TInt KMaxRate=1000;
35 const TInt KRawBufSize=256;
36 const TInt KCookedBufSize=0x2000;
37 const TInt KCodeBufSize=0x2000;
39 const TInt KMaxCreateCodeSegRecordSize = 300; //Max size of the encoded CodeSegCreate record.
40 const TInt KMaxErrorReportRecordSize = 18; //Max size of the encoded ErrorReport record. (3 zeros and 3 integers)
41 const TInt KRequiredFreeSpace=512;
44 const TInt KNonXIPModeActive=1;
45 const TInt KNoDebugSupport=2;
48 #define PUT(p,x,e,s) {*(p)++=(x); if ((p)==(e)) (p)-=(s);}
49 #define GET_A_BYTE(p,x,e,s) {*(x)++=*(p)++; if ((p)==(e)) (p)-=(s);}
51 #define TAG(obj) (*(TUint32*)&(obj->iAsyncDeleteNext))
53 #define CODESEGBUFEND (iCodeSegBuffer+KCodeBufSize)
54 #define COOKEDBUFEND (iCookedBuf+KCookedBufSize)
56 extern TUint IntStackPtr();
57 extern TUint32 SPSR();
58 extern TUint IDFCRunning();
61 TDynamicDfcQue* gDfcQ;
63 class DDeviceSampler : public DLogicalDevice
68 virtual TInt Install();
69 virtual void GetCaps(TDes8& aDes) const;
70 virtual TInt Create(DLogicalChannelBase*& aChannel);
76 TUint32 iSampleCounter;
80 class DProfile : public DLogicalChannel
86 virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
87 virtual void HandleMsg(TMessageBase* aMsg);
89 TInt GetSegments(TDes8* aDes);
90 TInt StartSampling(TInt aRate);
92 TInt Reset(TBool aXIPOnly);
94 TInt Drain(TDes8* aDes);
95 TInt GetErrors(TDes8* aDes);
96 TInt ProcessReadRequest();
99 void Complete(TInt aResult);
100 inline TBool Running()
101 {return iTimer.iState!=NTimer::EIdle;}
103 static void Sample(TAny*);
104 static void Dfc(TAny*);
105 static TUint KernelEventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aPrivateData);
106 void LogCodeSegEvent(TKernelEvent aEvent, DEpocCodeSeg *pCodeSeg);
109 static TUint8* EncodeTag(TUint8* p, TUint8* e);
110 static TUint8* EncodeInt(TUint8* p, TUint8* e, TInt aValue);
111 static TUint8* EncodeUint(TUint8* p, TUint8* e, TUint aValue);
112 static TUint8* EncodeText(TUint8* p, TUint8* e, const TDesC& aDes);
113 static TUint8* EncodeRepeat(TUint8* p, TUint8* e, DProfile* aProfile);
114 TUint8* EncodeThread(TUint8* p, TUint8* e, DThread* aThread);
115 TUint8* EncodeIDFC(TUint8* p, TUint8* e);
117 TUint8* PutStream(TUint8* p, TUint8* e, const TUint8* aSource, TInt aSize);
118 TUint8* GetStream(TUint8* p, TUint8* e, TInt8* aDest, TInt aSize);
119 TBool CookCodeSeg(TBool aPutAll, TInt aSampleCounter);
130 TRequestStatus* iReqStatus;
131 TInt iPos; // client des pos
132 TInt iRemain; // space left in client des
133 TDes8* iDes; // client des pointer
134 TUint8 iRPut; // raw buffer put index
135 TUint8 iRGet; // raw buffer get index
136 TUint8* iCPut; // cooked buffer put
137 TUint8* iCGet; // cooked buffer get
138 SRawSample iRawBuf[KRawBufSize];
139 TUint8 iCookedBuf[KCookedBufSize];
141 DKernelEventHandler* iKernelEvHandler;
143 TInt iNextSampleCounter;
145 TBool iMarkedOnlySegments; // True during GettingSegments phase in which event handler...
146 // ... collects only the events from marked segments.
147 TUint8 iCodeSegBuffer[KCodeBufSize];
148 TUint8* iCSPut; // CodeSeg buffer put
149 TUint8* iCSGet; // CodeSeg buffer get
150 TUint iIDFCSeenBefore;
153 TUint iRowBufferErrCounter;
154 TUint iCodeSegErrCounter;
159 DECLARE_STANDARD_LDD()
161 return new DDeviceSampler;
164 DDeviceSampler::DDeviceSampler()
171 iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
174 const TInt KDSamplerThreadPriority = 27;
175 _LIT(KDSamplerThread,"DSamplerThread");
177 TInt DDeviceSampler::Install()
179 // Install the device driver.
182 // Allocate a kernel thread to run the DFC
183 TInt r = Kern::DynamicDfcQCreate(gDfcQ, KDSamplerThreadPriority, KDSamplerThread);
188 r=SetName(&KLddName);
192 void DDeviceSampler::GetCaps(TDes8& aDes) const
194 // Return the capabilities.
202 DDeviceSampler::~DDeviceSampler()
208 TInt DDeviceSampler::Create(DLogicalChannelBase*& aChannel)
210 // Create a channel on the device.
213 aChannel=new DProfile;
214 return aChannel?KErrNone:KErrNoMemory;
218 : iTimer(Sample,this),
219 iDfc(Dfc,this,NULL,7)
226 DProfile::~DProfile()
231 Kern::SafeClose((DObject*&)iClient, NULL);
234 TInt DProfile::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer)
236 // Create the channel from the passed info.
239 if (!Kern::QueryVersionSupported(TVersion(1,0,1),aVer))
240 return KErrNotSupported;
241 iClient=&Kern::CurrentThread();
243 Kern::SetThreadPriority(24);
244 iIntStackTop=(TUint*)IntStackPtr();
251 void DProfile::Complete(TInt aResult)
252 //Completes user request
254 DEBUG_PROFILER(Kern::Printf("C");)
255 Kern::RequestComplete(iClient,iReqStatus,aResult);
258 TInt DProfile::StartSampling(TInt aRate)
260 DEBUG_PROFILER(Kern::Printf("START");)
262 aRate=Min(KMaxRate, Max(KMinRate, aRate));
265 iTimer.OneShot(iPeriod);
267 DEBUG_PROFILER(Kern::Printf("START end");)
271 TInt DProfile::GetSegments(TDes8* aDes)
273 // Collects and marks all non-XIP segments.
276 DEBUG_PROFILER(Kern::Printf("GS");)
277 TInt max=Kern::ThreadGetDesMaxLength(iClient,aDes);
278 Kern::ThreadDesWrite(iClient,aDes,KNullDesC8,0,0,iClient);//Set length to zero
283 // Take all records that are collected by event handler first. They may be only Delete CodeSeg
284 // events of tagged(marked) segments. On the first GetSegments call, cooked buffer also contains Profile Tag.
286 CookCodeSeg(ETrue, 0); // Transfer/encode from CodeSeg buffer into cooked buffer
287 current = iCPut-iCGet;
291 {//Copy data into user side descriptor
292 TPtrC8 aPtr(iCGet, current);
293 Kern::ThreadDesWrite(iClient,aDes,aPtr,0,KChunkShiftBy0,iClient);
297 //This is very unlikely as in this stage we collect only CodeSeg Delete events of the marked segments.
298 //It cannot happen on the first call, as there are no marked segments - which means that Profiler Tag is OK.
299 iReport.iCodeSegErrCounter++;
302 iCGet = iCPut = iCookedBuf; //Reset the cooked buffer
304 //Collect all non-XIP segments that are not already marked.
306 SDblQue* p = Kern::CodeSegList();
307 SDblQueLink* anchor=&p->iA;
308 SDblQueLink* a=anchor->iNext;
309 for (; a!=anchor; a=a->iNext)
311 DEpocCodeSeg* pSeg = (DEpocCodeSeg*) _LOFF(a, DCodeSeg, iLink);
312 if (pSeg->iXIP || pSeg->iMark&DCodeSeg::EMarkProfilerTAG)
314 if (current > (max-KMaxCreateCodeSegRecordSize))
315 break;//No more space. Finish now and wait for another GetSegments request.
317 pSeg->iMark |= DCodeSeg::EMarkProfilerTAG; //Mark this segment
318 LogCodeSegEvent(EEventAddCodeSeg, pSeg); //Place this record into CodeSeg buffer ...
319 CookCodeSeg(ETrue, 0); //...and encode it into cooked buffer
320 TPtrC8 aPtr(iCGet, iCPut-iCGet);
321 Kern::ThreadDesWrite(iClient,aDes,aPtr,current,KChunkShiftBy0,iClient);//Copy record into user desc.
322 current += iCPut-iCGet;
323 iCPut = iCGet = iCookedBuf; //Reset cooked buffer
326 if (!current)//This will be the last GetSegments call. From now on, all events have to be recorded.
327 iMarkedOnlySegments = EFalse;
329 Kern::EndAccessCode();
330 DEBUG_PROFILER(Kern::Printf("GS end %d",current);)
334 TInt DProfile::ResetSegments()
336 // Unmarks all non-XIP segments
337 // Sets device into GettingSegments mode in which only the events of the marked Code Segments will be recorder
340 DEBUG_PROFILER(Kern::Printf("RS");)
345 SDblQue* p = Kern::CodeSegList();
346 SDblQueLink* anchor=&p->iA;
347 SDblQueLink* a=anchor->iNext;
348 for (; a!=anchor; a=a->iNext)
350 DEpocCodeSeg* pSeg = (DEpocCodeSeg*) _LOFF(a, DCodeSeg, iLink);
352 pSeg->iMark &= ~DCodeSeg::EMarkProfilerTAG;
355 if (DKernelEventHandler::DebugSupportEnabled())
357 DEBUG_PROFILER(Kern::Printf("RS add handler");)
358 iKernelEvHandler->Add();
359 iReport.iReportMask|= KNonXIPModeActive;
362 iReport.iReportMask|= KNoDebugSupport;
364 iMarkedOnlySegments = ETrue;
365 Kern::EndAccessCode();
366 DEBUG_PROFILER(Kern::Printf("RS end");)
370 TInt DProfile::Reset(TBool aXIPOnly)
373 // Resets the device. It is the first message sent by profiler application.
378 DEBUG_PROFILER(Kern::Printf("RST %d", aXIPOnly);)
385 iLast.iSampleCounter=0;
390 iRPut=0; // raw buffer put index
391 iRGet=0; // raw buffer get index
392 iCPut=EncodeTag(iCookedBuf,COOKEDBUFEND); //cooked buffer put
393 iCGet=iCookedBuf; // cooked buffer get
394 iPos=0; // client des pos
395 iDes=NULL; // client des pointer
396 iStartTime=NKern::TickCount();
398 iReport.iRowBufferErrCounter = 0;
399 iReport.iCodeSegErrCounter = 0;
400 iReport.iReportMask = 0;
401 iNextSampleCounter = 0;
402 iCSPut=iCodeSegBuffer; // CodeSeg buffer put
403 iCSGet=iCodeSegBuffer; // CodeSeg buffer get
404 iMarkedOnlySegments = EFalse;
405 iIDFCSeenBefore = EFalse;
407 iKernelEvHandler = new DKernelEventHandler(KernelEventHandler, this);
409 DEBUG_PROFILER(Kern::Printf("RST end");)
413 TInt DProfile::StopSampling()
418 DEBUG_PROFILER(Kern::Printf("STOP");)
427 DEBUG_PROFILER(Kern::Printf("STOP end");)
431 TInt DProfile::GetErrors(TDes8* aDes)
433 // Returns error report and closes event handler
437 TBuf8<KMaxErrorReportRecordSize> localBuf; //Enough space to encode 3 zeros and 3 integers
438 DEBUG_PROFILER(Kern::Printf("GE");)
440 TInt max=Kern::ThreadGetDesMaxLength(iClient,aDes);
441 if (max<KMaxErrorReportRecordSize)
444 Kern::ThreadDesWrite(iClient,aDes,KNullDesC8,0,0,iClient);//set zero length
446 TUint8* p = (TUint8*)localBuf.Ptr();
447 TUint8* e = p+KMaxErrorReportRecordSize;
448 p = EncodeInt (p, e, 0);
449 p = EncodeUint(p, e, 0);
450 p = EncodeUint(p, e, 0);
452 p = EncodeUint(p, e, iReport.iRowBufferErrCounter);
453 p = EncodeUint(p, e, iReport.iCodeSegErrCounter);
454 p = EncodeUint(p, e, iReport.iReportMask);
456 localBuf.SetLength(p-localBuf.Ptr());
457 r=Kern::ThreadDesWrite(iClient,aDes,localBuf,0,KChunkShiftBy0,iClient);
459 if(iKernelEvHandler && iKernelEvHandler->IsQueued())
460 iKernelEvHandler->Close();
462 DEBUG_PROFILER(Kern::Printf("GE end %d %d %d", iReport.iRowBufferErrCounter, iReport.iCodeSegErrCounter, iReport.iReportMask);)
467 TInt DProfile::Drain(TDes8* aDes)
469 // Collects any remaining data
472 DEBUG_PROFILER(Kern::Printf("D");)
475 // we can assume read request is not pending
476 TInt max=Kern::ThreadGetDesMaxLength(iClient,aDes);
481 TInt r=Kern::ThreadDesWrite(iClient,aDes,KNullDesC8,0,0,iClient); // set client descriptor length to zero
491 r=DoDrainCooked(); // drain any cooked data if possible
492 if (r<0 && r!=KErrUnderflow)
493 return r; // error writing client buffer
494 n=Cook(); // cook the samples, return number cooked
497 // there might still be data left over
498 DEBUG_PROFILER(Kern::Printf("D end");)
502 TInt DProfile::ProcessReadRequest()
504 // If the profiler is stopped and there is available data, return it immediately and complete the request
505 // If the profiler is stopped and there is no data, wait.
506 // If the profiler is running, retrieve any data available now, if more is req'd set the trigger
507 DEBUG_PROFILER(Kern::Printf("READ");)
508 TInt max=Kern::ThreadGetDesMaxLength(iClient,iDes);
513 TInt r=Kern::ThreadDesWrite(iClient,iDes,KNullDesC8,0,0,iClient); // set client descriptor length to zero
522 r=DoDrainCooked(); // drain any cooked data if possible
523 if (r!=KErrUnderflow)
524 read=ETrue; // we've got something
526 return KErrNone; // request completed, so finish
527 if (r!=KErrNone && r!=KErrUnderflow)
528 return r; // error writing client buffer
529 n=Cook(); // cook the samples, return number cooked
531 if (!Running() && read)
532 return KErrCompletion; // if stopped and data read, return it
533 return KErrNone; // wait
536 TInt DProfile::DoDrainCooked()
538 // Copies encoded data from Cook buffer into user side descriptor (iDes).
540 // KErrUnderflow if all the data was already transfered or the desciptor was already full before the call.
541 // KErrNone if there is still remaining space available in the descriptor.
542 // 1 - descriptor is full and user request is completed.
543 // Error code other then KErrNone if writing to the user memory fails
546 TInt avail=iCPut-iCGet;
548 avail+=KCookedBufSize;
549 TInt len=Min(avail,iRemain);
552 TUint8* pE=iCookedBuf+KCookedBufSize;
553 TInt len1=Min(len,pE-iCGet);
554 TPtrC8 local(iCGet,len1);
555 TInt r=Kern::ThreadDesWrite(iClient, iDes, local, iPos, KChunkShiftBy0, iClient);
559 TUint8* pG=iCGet+len1;
565 if (len) // will be > 0 if there are remaining data at the beginning of Cooked buffer to be copied.
567 TPtrC8 local(iCGet,len);
568 r=Kern::ThreadDesWrite(iClient, iDes, local, iPos, KChunkShiftBy0, iClient);
575 if (iRemain==0 && iReqStatus)
582 return KErrUnderflow;
585 void DProfile::HandleMsg(TMessageBase* aMsg)
591 TThreadMessage& m=*(TThreadMessage*)aMsg;
593 // Allow the client thread to send a message or system critical thread
594 // to send a close message as this is probably the supervisor thread doing clean up
595 if (m.Client()!=iClient &&
596 !((m.Client()->iFlags&KThreadFlagSystemCritical) && id==(TInt)ECloseMsg))
598 m.PanicClient(_L("SAMPLER"),EAccessDenied);
601 if (id==(TInt)ECloseMsg)
603 DEBUG_PROFILER(Kern::Printf("CLOSE");)
606 m.Complete(KErrNone,EFalse);
607 iMsgQ.CompleteAll(KErrServerTerminated);
608 DEBUG_PROFILER(Kern::Printf("CLOSE end");)
613 if (id!=~RSampler::ERequestRead)
615 TRequestStatus* pS=(TRequestStatus*)m.Ptr0();
616 Kern::RequestComplete(iClient,pS,KErrNotSupported);
620 m.PanicClient(_L("SAMPLER"),ERequestAlreadyPending);
623 iReqStatus=(TRequestStatus*)m.Ptr0();
624 iDes=(TDes8*)m.Ptr1();
625 r=ProcessReadRequest();
628 if (r==KErrCompletion)
634 else if (id==KMaxTInt)
637 if (mask & (1<<RSampler::ERequestRead))
639 Complete(KErrCancel);
646 case RSampler::EControlGetSegments:
647 r=GetSegments((TDes8*)m.Ptr0());
649 case RSampler::EControlStartProfile:
650 r=StartSampling(m.Int0());
652 case RSampler::EControlStopProfile:
655 case RSampler::EControlResetProfile:
656 r=Reset((TBool)m.Ptr0());
658 case RSampler::EControlResetSegments:
661 case RSampler::EControlDrain:
662 r=Drain((TDes8*)m.Ptr0());
664 case RSampler::EControlGetErrors:
665 r=GetErrors((TDes8*)m.Ptr0());
675 TUint8* DProfile::EncodeTag(TUint8* p, TUint8* e)
677 // Encode a tag and version to the trace data. This allows the offline analyser to
678 // identify the sample data.
681 _LIT(KTraceTag,"profile");
682 p=EncodeText(p,e,KTraceTag);
683 p=EncodeUint(p,e,KMajorVersionNumber);
687 TUint8* DProfile::EncodeInt(TUint8* p, TUint8* e, TInt aValue)
689 // Encode a 32 bit signed integer into the data stream
690 // This has to deal with wrap around at the end of the buffer
696 byte = aValue & 0x7f;
697 if ((aValue >> 6) == (aValue >> 7))
700 PUT(p,(TUint8)byte,e,KCookedBufSize);
702 PUT(p,(TUint8)(byte|0x80),e,KCookedBufSize);
706 TUint8* DProfile::EncodeUint(TUint8* p, TUint8* e, TUint aValue)
708 // Encode a 32 bit unsigned integer into the data stream
709 // This has to deal with wrap around at the end of the buffer
715 byte = aValue & 0x7f;
719 PUT(p,(TUint8)byte,e,KCookedBufSize);
721 PUT(p,(TUint8)(byte|0x80),e,KCookedBufSize);
725 TUint8* DProfile::EncodeText(TUint8* p, TUint8* e, const TDesC& aDes)
727 // Encode a descriptor into the data stream
728 // This is currently limited to a descriptor that is up to 255 characters in length,
729 // and Unicode characters are truncated to 8 bits
732 TInt len=aDes.Length();
733 PUT(p,(TUint8)len,e,KCookedBufSize);
734 const TText* s = aDes.Ptr();
736 PUT(p,*s++,e,KCookedBufSize);
740 TUint8* DProfile::EncodeIDFC(TUint8* p, TUint8* e)
742 // iDFC samples do not really belong to any thread.
743 // However, the profiler protocol requires each sample to be associated to a particular thread.
744 // This method will encode 'fake' process ID & name and thread name for iDFC sample in the data stream.
745 // It will be embedded only for the very first sample from iDFCs.
746 // (For the rest of iDFCs samples, threadID is sufficient - as for the real threads.)
749 p=EncodeUint(p,e,KiDFCId); //processID for iDFC
750 p=EncodeText(p,e,KiDFCProcess);//process name for iDFC
751 p=EncodeText(p,e,KiDFCThread); //thread name for iDFC
755 TUint8* DProfile::EncodeThread(TUint8* p, TUint8* e, DThread* aThread)
757 // Encode a thread name in the data stream.
758 // The thread is identified by its name, and the identity of its owning process.
759 // If the process has not been identified in the data stream already, it's name is
763 DProcess* pP=aThread->iOwningProcess;
765 p=EncodeUint(p,e,pP->iId);
766 if (TAG(pP)!=iStartTime) // not seen this before
769 // Provide the name matching this process ID
778 TUint8* DProfile::EncodeRepeat(TUint8* p, TUint8* e, DProfile* aP)
780 // Encode a repeated sequence of samples
784 p=EncodeUint(p,e,aP->iRepeat);
789 TInt DProfile::CookCodeSeg(TBool aPutAll, TInt aSampleCounter)
791 // Transfers and encodes CodeSeg Create/Delete records from CodeSeg buffer into Cooked buffer.
792 // If aPutAll = Etrue, all records will be transferred.
793 // If aPutAll = EFalse, only records at the beginning of the queue that matches aSampleCounter will be transferred.
794 // It stopps if there is less then KRequiredFreeSpace bytes available in cooked buffer.
795 // Returns the number of the transferred records.
802 TInt codeSampleCounter;//Will hold the sample counter of the record
807 TUint8* localCSGet = iCSGet;
811 //Check if there is any Code Seg record left.
813 return n;//No records left
815 //Check if the next record is due to be encoded. Both Create & Delete CodeSeg records start with sample counter.
817 localCSGet = GetStream(localCSGet, CODESEGBUFEND, (TInt8*)(&codeSampleCounter), sizeof(TInt));
818 if (!aPutAll && codeSampleCounter!=aSampleCounter)
819 return n; //Still too early to insert the record into Cook Buffer
821 //Check for the space in cook buffer
822 TInt cookAvailable = (TInt)iCGet - (TInt)iCPut;
823 if (cookAvailable <= 0)
824 cookAvailable+=KCookedBufSize;
825 if (cookAvailable < KRequiredFreeSpace)
826 return n;//No space in Cook Buffer.
828 //At this point it is for sure that we have to transfer some record to cook buffer
832 //The next field for both Create & Delete CodeSeg records is run address:
833 iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(&runAddress), sizeof(TInt));
835 if (runAddress & 1)//LSB in run address idenifies the type of the record
836 {//CodeSegment Delete record. To be encoded as Int(0), UInt(0), UInt(RunAddress | 1)
837 iCPut = EncodeInt (iCPut, COOKEDBUFEND, 0);
838 iCPut = EncodeUint(iCPut, COOKEDBUFEND, 0);
839 iCPut = EncodeUint(iCPut, COOKEDBUFEND, runAddress);
842 {//CodeSegment Create record.
843 iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(&codeSize), sizeof(TInt));
844 iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(&textLen), sizeof(TInt8));
845 iCSGet = GetStream(iCSGet, CODESEGBUFEND, (TInt8*)(text.Ptr()), textLen);
846 text.SetLength(textLen);
847 //To be encoded as Int(0), UInt(0), UInt(RunAddress), UInt(SegmentSize), Text(FileNeme)
848 iCPut = EncodeInt(iCPut, COOKEDBUFEND, 0);
849 iCPut = EncodeUint(iCPut, COOKEDBUFEND, 0);
850 iCPut = EncodeUint(iCPut, COOKEDBUFEND, runAddress);
851 iCPut = EncodeUint(iCPut, COOKEDBUFEND, codeSize);
852 iCPut = EncodeText(iCPut, COOKEDBUFEND, text);
857 TInt DProfile::Cook()
859 // Transfers/encodes row data and code segments record into cooked buffer.
860 // Returns the number of records (incl. both samples and codeSeg records) cooked.
864 TUint8* e=iCookedBuf+KCookedBufSize;
869 iCPut=p; //update iCPut before calling CookCodeSeg
872 n+=CookCodeSeg(ETrue, 0); //Cook the remaining content of CodeSeg buffer.
876 SRawSample* s=iRawBuf+iRGet; // pointer to the next sample to be cooked
878 n+=CookCodeSeg(EFalse, s->iSampleCounter);//cook all codeSeg records than matches this sample counter
879 p=iCPut; //CookCodeSeg might have changed iCPut
883 space+=KCookedBufSize; // space remaining in cooked buffer
884 if (space<KRequiredFreeSpace)
885 break; // if insufficient, finish
887 //Cook the next sample record from Row buffer
890 TBool newthread=s->iPC & 1; // bit 0 of PC means so far unknown thread
891 TLinAddr pc=s->iPC &~ 1;
893 TInt diff=TInt(pc-iLast.iPC);
896 if (s->iThreadId!=iLast.iThreadId)
900 iRepeat = rp + 1; // Identical sample, bump up the repeat count
905 // Encode the repeat data
906 p = EncodeRepeat(p,e,this);
908 // Encode the PC difference
909 p = EncodeInt(p, e, diff);
912 // Encode the new thread ID
913 iLast.iThreadId = s->iThreadId;
914 p = EncodeUint(p, e, s->iThreadId);
921 // Encode the repeat data
922 p = EncodeRepeat(p,e,this);
924 // Encode the PC difference
925 p = EncodeInt(p, e, diff|1);
927 if (s->iThreadId == KiDFCId)
929 // This is the first sample from iDFC. Encode 'threadID'
930 iLast.iThreadId = KiDFCId;
931 p = EncodeUint(p, e, KiDFCId);
932 // and encode processID, processName & threadName for this sample
933 p = EncodeIDFC(p, e);
937 // Encode the new thread ID
938 DThread* pT=(DThread*)s->iThreadId;
939 iLast.iThreadId = pT->iId;
940 p = EncodeUint(p, e, pT->iId);
941 // The thread is 'unknown' to this sample, so encode the thread name
942 p = EncodeThread(p, e, pT);
950 void DProfile::Dfc(TAny* aPtr)
952 // Tranfers/encodes Row & CodeSeg buffers' content into Cook buffer (by Cook()),
953 // and copies encoded data into user side descriptor (by DoDrainCooked())
956 DProfile& d=*(DProfile*)aPtr;
960 TInt r=d.DoDrainCooked(); // drain any cooked data if possible
961 if (r<0 && r!=KErrUnderflow)
963 d.Complete(r); // error writing client buffer
966 n=d.Cook(); // cook the samples, return number cooked
970 TUint8* DProfile::PutStream(TUint8* p, TUint8* e, const TUint8* aSource, TInt aSize)
972 // Put data into CodeSeg stream
973 // This has to deal with wrap around at the end of the buffer
976 for (TInt i = 0 ; i< aSize ; i++)
978 PUT(p,(TUint8)(*aSource),e,KCodeBufSize);
984 TUint8* DProfile::GetStream(TUint8* p, TUint8* e, TInt8* aDest, TInt aSize)
986 // Get 32 bits from CodeSeg stream.
987 // This has to deal with wrap around at the end of the buffer
990 for (TInt i = 0 ; i< aSize ; i++)
991 GET_A_BYTE(p,aDest,e,KCodeBufSize);
995 void DProfile::LogCodeSegEvent(TKernelEvent aEvent, DEpocCodeSeg *pCodeSeg)
997 // Records the event in CodeSeg buffer.
1002 TUint8* localCSPut = iCSPut;
1003 TInt available = KCodeBufSize + (TInt)iCSGet - (TInt)iCSPut;
1004 if (available > KCodeBufSize)
1005 available -= KCodeBufSize;
1009 case EEventAddCodeSeg:
1011 TInt textOffset = 0;
1012 TInt textSize= pCodeSeg->iFileName->Length();
1013 //Restrict file name to max 255 sharacters. If bigger, record the last 255 bytes only
1016 textOffset = textSize-255;
1019 if ((available -= 13+textSize) < 0) //13 bytes needed for sample counter, run address, size and text size)
1021 iReport.iCodeSegErrCounter++;
1024 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&iNextSampleCounter), sizeof(TInt));
1025 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&pCodeSeg->iRunAddress), sizeof(TInt));
1026 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&pCodeSeg->iSize), sizeof(TInt));
1027 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&textSize), sizeof(TInt8));
1028 localCSPut = PutStream( localCSPut, CODESEGBUFEND, pCodeSeg->iFileName->Ptr()+textOffset, textSize);
1029 iCSPut = localCSPut;
1033 TBuf<256> buf(textSize+1);
1034 buf.Copy(pCodeSeg->iFileName->Ptr()+textOffset,textSize); buf.SetLength(textSize+1); buf[textSize]=0;
1035 Kern::Printf("CREATE CS:%s, %x, %x,", buf.Ptr(), pCodeSeg->iRunAddress, pCodeSeg->iSize);
1039 case EEventRemoveCodeSeg:
1041 if ((available-=8) < 0) //8 bytes needed for sample counter and run address
1043 iReport.iCodeSegErrCounter++;
1046 TInt runAddress = pCodeSeg->iRunAddress | 1;
1047 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&iNextSampleCounter), sizeof(TInt));
1048 localCSPut = PutStream(localCSPut, CODESEGBUFEND, (TUint8*)(&runAddress), sizeof(TInt));
1049 iCSPut = localCSPut;
1051 DEBUG_PROFILER(Kern::Printf("DELETE CS:%x", pCodeSeg->iRunAddress);)
1057 if (available < KCodeBufSize/2) //Start emptying CodeSeg (and Raw Buffer, as well) Buffer if more then a half full.
1061 TUint DProfile::KernelEventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aPrivateData)
1063 // Logs non-XIP CodeSeg Create/Delete events.
1064 // In GettingSegments mode, it logs only deletion of the marked segments.
1065 // Runs in the content of the Kernel scheduler
1068 if (aEvent==EEventAddCodeSeg || aEvent==EEventRemoveCodeSeg)
1070 DProfile *p = (DProfile*)aPrivateData;
1071 DEpocCodeSeg* pCodeSeg = (DEpocCodeSeg*)(DCodeSeg*)a1;
1074 if ((!pCodeSeg->iXIP) && (!p->iMarkedOnlySegments || pCodeSeg->iMark&DCodeSeg::EMarkProfilerTAG))
1075 p->LogCodeSegEvent(aEvent, pCodeSeg);
1076 Kern::EndAccessCode();
1079 return DKernelEventHandler::ERunNext;
1083 void DProfile::Sample(TAny* aPtr)
1085 DProfile& d=*(DProfile*)aPtr;
1086 d.iTimer.Again(d.iPeriod);
1087 TUint8 next_put=(TUint8)(d.iRPut+1);
1088 if (next_put!=d.iRGet) // space in raw buffer
1090 DThread* pT=Kern::NThreadToDThread(NKern::CurrentThread());
1093 SRawSample* p=d.iRawBuf+d.iRPut;
1095 p->iPC=((d.iIntStackTop)[-1]) & ~1; //clear LSB bit (in case of Jazelle code)
1096 p->iSampleCounter=d.iNextSampleCounter++;
1100 p->iThreadId=KiDFCId; //indicates iDFC running
1101 if (!d.iIDFCSeenBefore)
1103 d.iIDFCSeenBefore = ETrue;
1104 p->iPC|=1; // set bit 0 of PC to indicate new thread
1108 TUint8 used=(TUint8)(d.iRPut-d.iRGet);
1109 if (used<=KRawBufSize/2)
1116 if (TAG(pT)!=d.iStartTime) // not seen this before
1118 TAG(pT)=d.iStartTime;
1119 p->iThreadId=(TUint)pT;
1120 p->iPC|=1; // set bit 0 of PC to indicate new thread
1124 p->iThreadId=pT->iId;
1125 TUint8 used=(TUint8)(d.iRPut-d.iRGet);
1126 if (used<=KRawBufSize/2)
1130 d.iDfc.Add(); // queue DFC if new thread seen or buffer more than half full
1134 d.iReport.iRowBufferErrCounter++;