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