diff -r 67c6ff54ec25 -r a009639409f5 piprofiler/plugins/GeneralsPlugin/src/GppSamplerImpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/piprofiler/plugins/GeneralsPlugin/src/GppSamplerImpl.cpp Thu May 27 14:26:55 2010 +0300 @@ -0,0 +1,572 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "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: +* +*/ + + +#include +#include +#include +#include + +#include "GppSamplerImpl.h" + +extern TUint* IntStackPtr(); +#define TAG(obj) (*(TUint32*)&(obj.iAsyncDeleteNext)) + +// properties for ISA task parsing +const TUid KIsaPropertyCat={0x2001E5AD}; +enum TIsaPropertyKeys + { + EIsaPropertyIsaTaskParserStatus = 1, + EIsaPropertyIsaTaskAddressStart, + EIsaPropertyIsaTaskAddressEnd, + EIsaPropertyIsaTaskAddress, + EIsaPropertyIsaOsTaskRunningAddress, + EIsaPropertyIsaTaskParsedName + }; + + +DGppSamplerImpl::DGppSamplerImpl() + { + LOGTEXT("GppSamplerImpl::GppSamplerImpl"); + iInterruptStack = (TUint*)IntStackPtr(); + + LOGTEXT("GppSamplerImpl::GppSamplerImpl - attaching to properties"); + + TInt err = iIsaStartAddr.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskAddressStart); + if(err != KErrNone) + LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskAddressStart not available"); + err = iIsaEndAddr.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskAddressEnd); + if(err != KErrNone) + LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskAddressEnd not available"); + err = iIsaPluginStatus.Attach(KIsaPropertyCat, EIsaPropertyIsaTaskParserStatus); + if(err != KErrNone) + LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaTaskParserStatus not available"); + err = iIsaOsTaskRunning.Attach(KIsaPropertyCat, EIsaPropertyIsaOsTaskRunningAddress); + if(err != KErrNone) + LOGTEXT("GppSamplerImpl::GppSamplerImpl() - Property EIsaPropertyIsaOsTaskRunningAddress not available"); + + PROFILER_ISA_TASK_NAMES + + Reset(); + } + +DGppSamplerImpl::~DGppSamplerImpl() + { + iIsaStartAddr.Close(); + iIsaEndAddr.Close(); + iIsaPluginStatus.Close(); + iIsaOsTaskRunning.Close(); + } + +void DGppSamplerImpl::Reset() + { + LOGTEXT("GppSamplerImpl::Reset"); + iLastPc = 0; + iLastThread = 0; + iRepeat = 0; + iIsaStatus = 0; + iIsaStart = 0; + iIsaEnd = 0; +// isaOsTaskRunningAddr = 0; + + // in SMP start time common with all CPUs, provided by DGeneralsDriver class +#ifndef __SMP__ + iStartTime = ( NKern::TickCount() & 0xfffffffc ); +#endif + + TPropertyStatus status; + TInt osAddr = 0; + + LOGTEXT("GppSamplerImpl::Reset - getting status"); + + // get status of ISA plug-in + if(iIsaPluginStatus.GetStatus(status)) + { + iIsaPluginStatus.Get(iIsaStatus); + LOGSTRING2("GppSamplerImpl::Reset - ISA plug-in status %d", iIsaStatus); + } + + if(iIsaStatus > 0) + { + LOGTEXT("GppSamplerImpl::Reset - get isa start address"); + iIsaStartAddr.Get(iIsaStart); + LOGTEXT("GppSamplerImpl::Reset - get isa end address"); + iIsaEndAddr.Get(iIsaEnd); + LOGTEXT("GppSamplerImpl::Reset - get isa os_task_running address"); + iIsaOsTaskRunning.Get(osAddr); + isaOsTaskRunningAddr = reinterpret_cast(osAddr); + LOGSTRING2("GppSamplerImpl::Reset - got isa os_task_running address 0x%X", osAddr); + } + + LOGTEXT("GppSamplerImpl::Reset - initializing isa task list"); + + iIsaSample = false; + + for(TInt i=0;i<256;i++) + knownIsaTasks[i] = -1; + + knownIsaTaskCount = 0; + + iCpuSelector = 0x3; +#ifndef __SMP__ + iMask = 0xfffffffc; +#else + iMask = 0xfffffff0; + switch(iCpuNumber) + { + case 0: + iCpuSelector = 0x1; + break; + case 1: + iCpuSelector = 0x2; + break; + case 2: + iCpuSelector = 0x4; + break; + case 3: + iCpuSelector = 0x8; + break; + } +#endif + } + +TUint8* DGppSamplerImpl::EncodeTag(TUint8* aPtr) +// +// Encode a tag and version to the trace data. This allows the offline analyser to +// identify the sample data. +// +{ + _LIT(KGppSamplerVersion,"Bappea_GPP_V"); + _LIT(KProfilerVersion,"#Prof#"); + _LIT(KSamplerVersion,"#Samp#"); +#ifdef __SMP__ + _LIT(KCPUNumberText,"#CPU#"); +#endif + + TBuf<64> buf; + buf.Zero(); + buf.Append(KGppSamplerVersion); + buf.Append(PROFILER_GPP_SAMPLER_VERSION); + buf.Append(KProfilerVersion); + buf.Append(PROFILER_VERSION_SHORT); + buf.Append(KSamplerVersion); + buf.Append(PROFILER_SAMPLER_VERSION); +#ifdef __SMP__ + buf.Append(KCPUNumberText); + buf.AppendNum(iCpuNumber); +#endif + aPtr = EncodeText(aPtr, buf); + return aPtr; +} + +TUint8* DGppSamplerImpl::EncodeInt(TUint8* aPtr,TInt aValue) +{ + LOGSTRING2("Encoding int 0x%x",aPtr); + + LOGSTRING2("TIint = 0x%x",aValue); + + TUint byte; + for (;;) + { + byte = aValue & 0x7f; + if ((aValue >> 6) == (aValue >> 7)) + break; + aValue >>= 7; + *aPtr++ = byte; + } + *aPtr++ = byte | 0x80; + + LOGSTRING2("Encoded int 0x%x",aPtr); + + return aPtr; +} + +TUint8* DGppSamplerImpl::EncodeUint(TUint8* aPtr,TUint aValue) +{ + LOGSTRING2("Encoding Uint 0x%x",aPtr); + + LOGSTRING2("TUint = 0x%x",aValue); + + + TUint byte; + for (;;) + { + byte = aValue & 0x7f; + aValue >>= 7; + if (aValue == 0) + break; + *aPtr++ = byte; + } + *aPtr++ = byte | 0x80; + + LOGSTRING2("Encoded Uint 0x%x",aPtr); + + return aPtr; +} + +TUint8* DGppSamplerImpl::EncodeText(TUint8* aPtr, 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 +// +{ + LOGSTRING2("Encoding text 0x%x",aPtr); + TInt len=aDes.Length(); + *aPtr++ = TUint8(len); + const TText* p = aDes.Ptr(); + while (--len >= 0) + { + *aPtr++ = TUint8(*p++); + } + + LOGSTRING2("Encoded text 0x%x",aPtr); + return aPtr; +} + + +TUint8* DGppSamplerImpl::EncodeName(TUint8* aPtr, DObject& aObject,TUint32 id) +// +// Encode the name of a kernel object +// +{ + LOGSTRING2("Encoding name 0x%x",aPtr); + TBuf8<0x5f> name; + aObject.TraceAppendName(name,false); + + if(id != 0xffffffff) + { + name.Append('['); + name.AppendNum(id,EHex); + name.Append(']'); + } + else + { + name.Append('['); + name.AppendNum((TUint32)((void*)&(((DThread*)&aObject)->iNThread)),EHex); + name.Append(']'); + } + + aPtr = EncodeText(aPtr,name); + LOGSTRING2("Encoded name 0x%x",aPtr); + return aPtr; +} + +TUint8* DGppSamplerImpl::EncodeThread(TUint8* aPtr, 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. +// +{ + LOGSTRING2("Encoding thread 0x%x",aPtr); + + DProcess& p = *aThread.iOwningProcess; + + aPtr = EncodeUint(aPtr, p.iId); + +#ifdef __SMP__ + // check if first time founding + if ((TAG(p) & iMask) != iStartTime) + { + // mark tagged for this CPU + TAG(p) = (iStartTime | iCpuSelector); + + // The thread is 'unknown' to this sample, so encode the thread name + aPtr = EncodeName(aPtr, p, p.iId); + } + // check if thread appeared already on this CPU + else if((TAG(p) & iCpuSelector) != iCpuSelector) + { + TAG(p) = (TAG(p) | iCpuSelector); + // The thread is 'unknown' to this sample, so encode the thread name + aPtr = EncodeName(aPtr, p, p.iId); + } +#else + if (TAG(p) != iStartTime) + { + TAG(p) = iStartTime; + // Provide the name matching this process ID + aPtr = EncodeName(aPtr, p, p.iId); + } +#endif + aPtr = EncodeName(aPtr, aThread,0xffffffff); + + LOGSTRING2("Encoded thread 0x%x",aPtr); + + return aPtr; + } + +TUint8* DGppSamplerImpl::EncodeRepeat(TUint8* aPtr) +// +// Encode a repeated sequence of samples +// +{ + LOGSTRING2("Encoding repeat, 0x%x",iRepeat); + + aPtr = EncodeInt(aPtr, 0); + aPtr = EncodeUint(aPtr, iRepeat); + iRepeat = 0; + + LOGSTRING2("Encoded repeat, 0x%x",iRepeat); + + return aPtr; +} + +TInt DGppSamplerImpl::CreateFirstSample() +{ + LOGTEXT("GppSamplerImpl::CreateFirstSample"); + Reset(); + + TUint8* w = this->tempBuf; + w = EncodeTag(w); + + TInt length = w-tempBuf; + + LOGSTRING2("TAG encoded, length %d",length); + return length; +} + +TBool DGppSamplerImpl::IsaTaskKnown(TUint8 task) +{ + for(TInt i=0;i<256;i++) + { + if(knownIsaTasks[i] == -1) + { + knownIsaTasks[i] = task; + knownIsaTaskCount++; + return false; + } + else if(knownIsaTasks[i] == task) + { + return true; + } + } + + return false; +} + +TUint8* DGppSamplerImpl::EncodeIsaTask(TUint8* aPtr, TUint task) + +{ + LOGSTRING2("Encoding ISA task 0x%x",aPtr); + + aPtr = EncodeUint(aPtr,task); + // use the task name as the process name + aPtr = EncodeIsaName(aPtr,task,true); + // then encode the task name + aPtr = EncodeIsaName(aPtr,task,false); + + LOGSTRING2("Encoded ISA task 0x%x",aPtr); + + return aPtr; +} + +TUint8* DGppSamplerImpl::EncodeIsaName(TUint8* aPtr, TUint task,TBool process) +// +// 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 +// +{ + TBuf8<256> aDes; + +// #ifdef NCP_COMMON_PROFILER_ISA_TASKS + if(iIsaStatus > 0) + { + // resolve the isa task name from the task name array + if((task-100000) < PROFILER_ISA_OS_TASK_AMOUNT && process == false) + { + aDes.Append(isaTaskNames[(task-100000)]); + } + else + { + aDes.Append(_L8("NativeOS_Task")); + } + } + else + { + aDes.Append(_L8("NativeOS_Task")); + } + + aDes.Append('['); + aDes.AppendNum((task-100000),EHex); + aDes.Append(']'); + + LOGSTRING2("Encoding ISA name 0x%x",aPtr); + TInt len=aDes.Length(); + *aPtr++ = TUint8(len); + const TText* p = aDes.Ptr(); + while (--len >= 0) + { + *aPtr++ = TUint8(*p++); + } + + LOGSTRING2("Encoded ISA name 0x%x",aPtr); + return aPtr; +} + + +TInt DGppSamplerImpl::SampleImpl() +// +// ISR for the profile timer +// This extracts the thread and PC that was current when the interrupt went off and +// encodes it into the sample data buffer. If enough data has been generated, the +// DFC is triggered to complete a read request +// + { + TUint8* w(this->tempBuf); + +// Kern::Printf(("Got thread 0x%08x"), &t); +#ifdef __SMP__ + // get the program counter of irq mode + TUint32 pc = (TUint32)Arm::IrqReturnAddress(); +#else + // get program counter of irq mode + TUint32 pc = iInterruptStack[-1]; +#endif + //LOGSTRING3("pc value 0x%x sp 0x%x",pc,iInterruptStack); + + // ignore the low bit being set for THUMB mode - we use for something else + pc &= ~1; + TInt diff = pc - iLastPc; + iLastPc = pc; + + if(iIsaStatus > 0) + { + if((TUint32)pc > (TUint32)iIsaStart && (TUint32)pc < (TUint32)iIsaEnd) + { + LOGSTRING2("Identified ISA execution at 0x%x",pc); + iIsaSample = true; + } + else + { + LOGSTRING2("Normal sample at 0x%x",pc); + iIsaSample = false; + } + } + + // request for current thread from kernel + DThread& t = ((DThread&)*Kern::NThreadToDThread(NKern::CurrentThread())); + + TUint tid; + TUint8 isaTask = 0; + if(iIsaSample) + { + LOGSTRING2("Reading ISA task number from 0x%x",isaOsTaskRunningAddr); + + // if we don't get reasonable ISA address to read, skip ISA task handling + if(isaOsTaskRunningAddr == 0) + { + tid = 100000; // to tell the difference from SOS threads + iIsaSample = false; + } + else // normal ISA task parsing process + { + isaTask = *isaOsTaskRunningAddr; + LOGSTRING2("ISA task = %d",isaTask); + tid = isaTask; + // this will make sure we don't mix ISA tasks and normal tasks + tid += 100000; + } + + } + else + { + tid = t.iId; + } + + if (tid != iLastThread) + { + // Change of thread is marked in the low bit of the PC difference + diff |= 1; + } + TUint rp = iRepeat; + if (diff == 0) + { + // Identical sample, bump up the repeat count + iRepeat = rp + 1; + } + else + { + if (rp) + { + // Encode the repeat data + w = EncodeRepeat(w); + } + // Encode the PC difference + w = EncodeInt(w, diff); + if (diff & 1) + { + // Encode the new thread ID + if(iIsaSample) + { + iLastThread = tid; + w = EncodeUint(w,tid); + + if(!this->IsaTaskKnown(isaTask)) + { + w = EncodeIsaTask(w,iLastThread); + } + //LOGSTRING2("Sample total length: %d",w-tempBuf); + TInt length = w-tempBuf; + // encoded isa task, return here + return length; + } + + iLastThread = tid; + w = EncodeUint(w, tid); + +#ifdef __SMP__ + // iStartTime format: 0xXXXXXXX0, the last byte set to zero + // iMask = 0xfffffff0(0b111....1110000) + // iCpuSelector = 0x1(0b0001), 0x2(0b0010), 0x4(0b0100) or 0x8(0b1000) + + // check first time founding + if ((TAG(t) & iMask) != iStartTime) + { + // mark tagged for this CPU + TAG(t) = (iStartTime | iCpuSelector); + + // The thread is 'unknown' to this sample, so encode the thread name + w = EncodeThread(w, t); + } + // check if thread appeared on this CPU + else if((TAG(t) & iCpuSelector) != iCpuSelector) + { + TAG(t) = (TAG(t) | iCpuSelector); + // The thread is 'unknown' to this sample, so encode the thread name + w = EncodeThread(w, t); + } +#else + // check if tag has not been set, neither original nor + if ((TAG(t) & 0xfffffffc) != iStartTime) + { + TAG(t) = ((TAG(t) & 0x3) | iStartTime); + // The thread is 'unknown' to this sample, so encode the thread name + w = EncodeThread(w, t); + } +#endif + } + } + LOGSTRING2("Sample total length: %d",w-tempBuf); + TInt length = w-tempBuf; + + return length; +} +