Convert Kernelhwsrv package from SFL to EPL
kernel\eka\compsupp is subject to the ARM EABI LICENSE
userlibandfileserver\fatfilenameconversionplugins\unicodeTables is subject to the Unicode license
kernel\eka\kernel\zlib is subject to the zlib license
// Copyright (c) 2007-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:
//
#if defined(__EPOC32__) || defined(__WINS__)
// compiling for Symbian OS...
#include <e32std.h>
#include <e32std_private.h>
#include <e32svr.h>
#include <e32def.h>
#include <e32def_private.h>
#include <d32btrace.h>
#include "e32btrace.h"
inline void* malloc(TInt aSize) { return User::Alloc(aSize); }
inline void* realloc(void* aPtr,TInt aSize) { return User::ReAlloc(aPtr,aSize); }
inline void free(void* aPtr) { User::Free(aPtr); }
TUint8 PrintfBuffer[256];
TUint PrintfBufferWidth = 0;
static void printf(const char* aFormat,...)
{
VA_LIST list;
VA_START(list,aFormat);
TPtrC8 format((const TUint8*)aFormat);
TPtr8 buf(PrintfBuffer+PrintfBufferWidth,sizeof(PrintfBuffer)-PrintfBufferWidth);
buf.AppendFormatList(format,list);
PrintfBufferWidth += buf.Size();
for(;;)
{
TUint width = 0;
for(;;)
{
if(width>=PrintfBufferWidth)
return;
if(PrintfBuffer[width]=='\n')
break;
++width;
}
TPtrC8 line(PrintfBuffer,width);
++width;
RDebug::RawPrint(line);
_LIT(KLineEnd,"\r\n");
RDebug::RawPrint(KLineEnd);
memcpy(PrintfBuffer,PrintfBuffer+width,PrintfBufferWidth-width);
PrintfBufferWidth -= width;
}
}
typedef TUint uintptr_t;
#ifndef ASSERT
#define ASSERT(c) (void)((c)||(AssertFailed(__LINE__)))
TInt AssertFailed(TInt aLine)
{
_LIT(KPanicCategory,"BTRACE-ANALYSE");
User::Panic(KPanicCategory,aLine);
return 0;
}
#endif // ASSERT
//
// Trace buffer helper functions - for use on target only
//
RBTrace Trace;
TUint8* AnalyseData;
TUint AnalyseDataRemain = 0;
void RePrime()
{
for(TInt i=0; i<256; ++i)
{
if(Trace.Filter(i))
{
// toggle filter to force a prime
Trace.SetFilter(i,0);
Trace.SetFilter(i,1);
}
}
}
TUint AnalyseStream(TAny* aBuffer, TUint aMaxSize)
{
TUint size = AnalyseDataRemain;
if(!size)
{
Trace.DataUsed();
AnalyseDataRemain = Trace.GetData(AnalyseData);
size = AnalyseDataRemain;
}
if(size>aMaxSize)
size = aMaxSize;
memcpy(aBuffer,AnalyseData,size);
AnalyseData += size;
AnalyseDataRemain -= size;
return size;
}
void ProcessAllTrace(TUint (*aInput)(TAny* aBuffer, TUint aMaxSize),TInt aReportLevel);
void DoAnalyse(TInt aAnalysisLevel)
{
AnalyseDataRemain = 0;
TUint oldMode = Trace.Mode();
Trace.SetMode(0); // turn off trace capture while we dump
ProcessAllTrace(AnalyseStream, aAnalysisLevel);
Trace.SetMode(oldMode);
RePrime();
}
TInt BTraceAnalyseSetup()
{
TInt r = Trace.Open();
if (r != KErrNone)
{
return r;
}
// Stop tracing
TUint oldMode = Trace.Mode();
Trace.SetMode(0);
// empty btrace buffer and reprime it
Trace.Empty();
Trace.SetMode(oldMode);
RePrime();
return KErrNone;
}
void BTraceAnalyseEnd()
{
Trace.Close();
}
void BTraceAnalyse(TInt aAnalysisLevel)
{
// Stop tracing
TUint oldMode = Trace.Mode();
Trace.SetMode(0);
AnalyseDataRemain = 0;
ProcessAllTrace(AnalyseStream, aAnalysisLevel);
// empty btrace buffer and reprime it
Trace.Empty();
Trace.SetMode(oldMode);
RePrime();
}
#else
// compiling for host...
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#if defined(_MSC_VER)
typedef __int64 longlong;
typedef unsigned __int64 ulonglong;
#define BREAKPOINT { _asm int 3 } /**< Invoke debugger */
#else
typedef long long longlong;
typedef long long ulonglong;
#define BREAKPOINT
#endif
typedef signed char TInt8;
typedef unsigned char TUint8;
typedef unsigned short TUint16;
typedef unsigned int TUint32;
typedef ulonglong TUint64;
typedef unsigned int uintptr_t;
typedef int TInt;
typedef unsigned TUint;
typedef int TBool;
typedef void TAny;
typedef float TReal;
#define IMPORT_C
#include "e32btrace.h"
#define ASSERT(c) (void)((c)||(AssertFailed(__LINE__)))
extern "C" void exit(int);
TInt AssertFailed(TInt aLine)
{
fprintf(stderr,"Panic: BTRACE-ANALYSE %d",aLine);
BREAKPOINT;
exit(2);
return 0;
}
#define __UHEAP_MARK
#define __UHEAP_MARKEND
TAny* operator new(size_t, TAny* p) {return p;}
#endif // SYMBIAN_OS
//
// Global data
//
TInt ReportLevel = 0;
TUint8 TraceBuffer[0x10000];
TUint TraceBufferSize = 0;
TUint64 Timestamp = 0;
TUint64 TimestampBase = 0;
TUint32 TraceRecordId = 0;
TUint32 TimestampPeriod = 0;
TUint32 Timestamp2Period = 0;
TBool Timestamp64Bit = 0;
TUint TraceFormatErrors = 0;
TBool TraceBufferFilled = false;
TBool SMP = true;
TBool ErrorOnThisTrace = false;
const TUint8 KJunkTraceSubcategory = 255; // Dummy sub-category for EMetaTrace
const TInt KMaxCpus = 8;
//
// Utilities
//
void ToHex(TUint8* aString,TUint32 aValue)
{
TUint i=32;
do
{
i -= 4;
TUint8 c = (TUint8)((aValue>>i)&0xf);
if(c>=10)
c += 'a'-10;
else
c += '0';
*aString++ = c;
}
while(i);
}
TUint MakeName(TUint8* aString,const char* aName,TUint32 aHexValue)
{
TUint8* start = aString;
for(;;)
{
TUint8 c = *aName++;
if(!c)
break;
*aString++ = c;
}
ToHex(aString,aHexValue);
aString[8] = 0;
return aString-start+8;
}
/**
Convert a timestamp into real time units (micro-seconds)
*/
TUint32 Time(TUint64 aTimestamp)
{
if(!TimestampPeriod)
return (TUint32)aTimestamp;
TInt exponent = (TInt8)(TimestampPeriod>>24);
TUint64 mantissa = TimestampPeriod&0xffffff;
aTimestamp *= mantissa;
exponent += 32;
if(exponent<0)
aTimestamp >>= -exponent;
else
aTimestamp <<= exponent;
TUint64 timeLo = (aTimestamp&0xffffffffu)*1000000;
TUint64 timeHi = (aTimestamp>>32)*1000000;
TUint64 us = timeHi+(timeLo>>32)+((timeLo>>31)&1);
return TUint32(us);
}
void ReportTimeUnits()
{
if(!TimestampPeriod)
printf("\nAll times are given in BTrace Timestamp1 units.\n\n");
else
{
TInt exponent = (TInt8)(TimestampPeriod>>24);
TUint64 mantissa = TimestampPeriod&0xffffff;
TUint64 period = 1000000;
period *= mantissa;
exponent += 32;
if(exponent<0)
period >>= -exponent;
else
period <<= exponent;
printf("\nAll times are given in microseconds, resolution %d.%03dus\n\n",(int)TUint32(period>>32),(int)TUint32((((period&0xffffffffu)*1000)+0x80000000u)>>32));
}
}
TUint SetBits(TUint8* aData, TUint aOffset, TUint aSize)
{
TUint mask = 1<<(aOffset&7);
TUint8* data = aData+(aOffset>>3);
TUint errors = 0;
while(aSize)
{
if(*data&mask)
++errors;
*data |= (TUint8)mask;
mask <<= 1;
if(mask>0xFF)
{
mask = 0x01;
++data;
}
--aSize;
}
return errors;
}
TUint ClearBits(TUint8* aData, TUint aOffset, TUint aSize)
{
TUint mask = 1<<(aOffset&7);
TUint8* data = aData+(aOffset>>3);
TUint errors = 0;
while(aSize)
{
if(!(*data&mask))
++errors;
*data &= (TUint8)~mask;
mask <<= 1;
if(mask>0xFF)
{
mask = 0x01;
++data;
}
--aSize;
}
return errors;
}
void WarnIfError(TUint aErrorCount)
{
if (TraceBufferFilled)
printf("WARNING - BTRACE BUFFER IS FULL SO TRACE DATA MAY BE INCOMPLETE\n");
if(aErrorCount==0 && TraceFormatErrors==0)
return;
printf("CONSISTENCY ERRORS FOUND DURING TRACE ANALYSIS, RESULTS ARE UNRELIABLE!\n");
}
#define CHECK_TRACE_DATA_WORDS(numWords) \
if(aTrace.iDataSize<numWords*4 && ++TraceFormatErrors) return
//
// Category naming
//
const char* const UnknownNames[256] =
{
"?00","?01","?02","?03","?04","?05","?06","?07","?08","?09",
"?10","?11","?12","?13","?14","?15","?16","?17","?18","?19",
"?20","?21","?22","?23","?24","?25","?26","?27","?28","?29",
"?30","?31","?32","?33","?34","?35","?36","?37","?38","?39",
"?40","?41","?42","?43","?44","?45","?46","?47","?48","?49",
"?50","?51","?52","?53","?54","?55","?56","?57","?58","?59",
"?60","?61","?62","?63","?64","?65","?66","?67","?68","?69",
"?70","?71","?72","?73","?74","?75","?76","?77","?78","?79",
"?80","?81","?82","?83","?84","?85","?86","?87","?88","?89",
"?90","?91","?92","?93","?94","?95","?96","?97","?98","?99",
"?100","?101","?102","?103","?104","?105","?106","?107","?108","?109",
"?110","?111","?112","?113","?114","?115","?116","?117","?118","?119",
"?120","?121","?122","?123","?124","?125","?126","?127","?128","?129",
"?130","?131","?132","?133","?134","?135","?136","?137","?138","?139",
"?140","?141","?142","?143","?144","?145","?146","?147","?148","?149",
"?150","?151","?152","?153","?154","?155","?156","?157","?158","?159",
"?160","?161","?162","?163","?164","?165","?166","?167","?168","?169",
"?170","?171","?172","?173","?174","?175","?176","?177","?178","?179",
"?180","?181","?182","?183","?184","?185","?186","?187","?188","?189",
"?190","?191","?192","?193","?194","?195","?196","?197","?198","?199",
"?200","?201","?202","?203","?204","?205","?206","?207","?208","?209",
"?210","?211","?212","?213","?214","?215","?216","?217","?218","?219",
"?220","?221","?222","?223","?224","?225","?226","?227","?228","?229",
"?230","?231","?232","?233","?234","?235","?236","?237","?238","?239",
"?240","?241","?242","?243","?244","?245","?246","?247","?248","?249",
"?250","?251","?252","?253","?254","?255"
};
#define STRINGIFY2(a) #a /**< Helper for #STRINGIFY */
#define STRINGIFY(a) STRINGIFY2(a) /**< Convert \a a into a quoted string */
#define CASE_CAT_NAME(name) case BTrace::name: return STRINGIFY(name)
const char* CategoryName(TUint8 aCategory)
{
switch((BTrace::TCategory)aCategory)
{
CASE_CAT_NAME(ERDebugPrintf);
CASE_CAT_NAME(EKernPrintf);
CASE_CAT_NAME(EPlatsecPrintf);
case BTrace::EThreadIdentification: return "EThreadId"; // CASE_CAT_NAME(EThreadIdentification)
CASE_CAT_NAME(ECpuUsage);
CASE_CAT_NAME(EKernPerfLog);
CASE_CAT_NAME(EClientServer);
CASE_CAT_NAME(ERequests);
CASE_CAT_NAME(EChunks);
CASE_CAT_NAME(ECodeSegs);
CASE_CAT_NAME(EPaging);
CASE_CAT_NAME(EThreadPriority);
CASE_CAT_NAME(EPagingMedia);
CASE_CAT_NAME(EKernelMemory);
CASE_CAT_NAME(EHeap);
CASE_CAT_NAME(EMetaTrace);
CASE_CAT_NAME(EFastMutex);
CASE_CAT_NAME(EProfiling);
CASE_CAT_NAME(ESymbianKernelSync);
CASE_CAT_NAME(EFlexibleMemModel);
CASE_CAT_NAME(ETest1);
CASE_CAT_NAME(ETest2);
default:
break;
}
return UnknownNames[aCategory];
}
const char* SubCategoryName(TUint8 aCategory, TUint8 aSubCategory)
{
switch(aCategory)
{
case BTrace::ERDebugPrintf:
case BTrace::EKernPrintf:
case BTrace::EPlatsecPrintf:
return ""; // no subcategories for these
case BTrace::EThreadIdentification:
switch((BTrace::TThreadIdentification)aSubCategory)
{
CASE_CAT_NAME(ENanoThreadCreate);
CASE_CAT_NAME(ENanoThreadDestroy);
CASE_CAT_NAME(EThreadCreate);
CASE_CAT_NAME(EThreadDestroy);
CASE_CAT_NAME(EThreadName);
CASE_CAT_NAME(EProcessName);
CASE_CAT_NAME(EThreadId);
CASE_CAT_NAME(EProcessCreate);
CASE_CAT_NAME(EProcessDestroy);
}
break;
case BTrace::ECpuUsage:
switch((BTrace::TCpuUsage)aSubCategory)
{
CASE_CAT_NAME(EIrqStart);
CASE_CAT_NAME(EIrqEnd);
CASE_CAT_NAME(EFiqStart);
CASE_CAT_NAME(EFiqEnd);
CASE_CAT_NAME(EIDFCStart);
CASE_CAT_NAME(EIDFCEnd);
CASE_CAT_NAME(ENewThreadContext);
}
break;
case BTrace::EChunks:
switch((BTrace::TChunks)aSubCategory)
{
CASE_CAT_NAME(EChunkCreated);
CASE_CAT_NAME(EChunkInfo);
CASE_CAT_NAME(EChunkDestroyed);
CASE_CAT_NAME(EChunkMemoryAllocated);
CASE_CAT_NAME(EChunkMemoryDeallocated);
CASE_CAT_NAME(EChunkMemoryAdded);
CASE_CAT_NAME(EChunkMemoryRemoved);
CASE_CAT_NAME(EChunkOwner);
}
break;
case BTrace::ECodeSegs:
switch((BTrace::TCodeSegs)aSubCategory)
{
CASE_CAT_NAME(ECodeSegCreated);
CASE_CAT_NAME(ECodeSegInfo);
CASE_CAT_NAME(ECodeSegDestroyed);
CASE_CAT_NAME(ECodeSegMapped);
CASE_CAT_NAME(ECodeSegUnmapped);
CASE_CAT_NAME(ECodeSegMemoryAllocated);
CASE_CAT_NAME(ECodeSegMemoryDeallocated);
}
break;
case BTrace::EPaging:
switch((BTrace::TPaging)aSubCategory)
{
CASE_CAT_NAME(EPagingPageInBegin);
CASE_CAT_NAME(EPagingPageInUnneeded);
CASE_CAT_NAME(EPagingPageInROM);
CASE_CAT_NAME(EPagingPageOutROM);
CASE_CAT_NAME(EPagingPageInFree);
CASE_CAT_NAME(EPagingPageOutFree);
CASE_CAT_NAME(EPagingRejuvenate);
CASE_CAT_NAME(EPagingPageNop);
CASE_CAT_NAME(EPagingPageLock);
CASE_CAT_NAME(EPagingPageUnlock);
CASE_CAT_NAME(EPagingPageOutCache);
CASE_CAT_NAME(EPagingPageInCode);
CASE_CAT_NAME(EPagingPageOutCode);
CASE_CAT_NAME(EPagingMapCode);
CASE_CAT_NAME(EPagingAged);
CASE_CAT_NAME(EPagingDecompressStart);
CASE_CAT_NAME(EPagingDecompressEnd);
CASE_CAT_NAME(EPagingMemoryModel);
CASE_CAT_NAME(EPagingChunkDonatePage);
CASE_CAT_NAME(EPagingChunkReclaimPage);
CASE_CAT_NAME(EPagingPageIn);
CASE_CAT_NAME(EPagingPageOut);
CASE_CAT_NAME(EPagingMapPage);
CASE_CAT_NAME(EPagingDonatePage);
CASE_CAT_NAME(EPagingReclaimPage);
CASE_CAT_NAME(EPagingAgedClean);
CASE_CAT_NAME(EPagingAgedDirty);
CASE_CAT_NAME(EPagingPageTableAlloc);
}
break;
case BTrace::EKernelMemory:
switch((BTrace::TKernelMemory)aSubCategory)
{
CASE_CAT_NAME(EKernelMemoryInitialFree);
CASE_CAT_NAME(EKernelMemoryCurrentFree);
CASE_CAT_NAME(EKernelMemoryMiscAlloc);
CASE_CAT_NAME(EKernelMemoryMiscFree);
CASE_CAT_NAME(EKernelMemoryDemandPagingCache);
CASE_CAT_NAME(EKernelMemoryDrvPhysAlloc);
CASE_CAT_NAME(EKernelMemoryDrvPhysFree);
}
break;
case BTrace::EMetaTrace:
{
if(aSubCategory==KJunkTraceSubcategory)
return "*JUNK*";
else
{
switch((BTrace::TMetaTrace)aSubCategory)
{
CASE_CAT_NAME(EMetaTraceTimestampsInfo);
CASE_CAT_NAME(EMetaTraceMeasurementStart);
CASE_CAT_NAME(EMetaTraceMeasurementEnd);
CASE_CAT_NAME(EMetaTraceFilterChange);
}
}
}
break;
case BTrace::EFastMutex:
switch((BTrace::TFastMutex)aSubCategory)
{
CASE_CAT_NAME(EFastMutexWait);
CASE_CAT_NAME(EFastMutexSignal);
CASE_CAT_NAME(EFastMutexFlash);
CASE_CAT_NAME(EFastMutexName);
CASE_CAT_NAME(EFastMutexBlock);
}
break;
case BTrace::EProfiling:
switch((BTrace::TProfiling)aSubCategory)
{
CASE_CAT_NAME(ECpuFullSample);
CASE_CAT_NAME(ECpuOptimisedSample);
CASE_CAT_NAME(ECpuIdfcSample);
CASE_CAT_NAME(ECpuNonSymbianThreadSample);
}
break;
case BTrace::ESymbianKernelSync:
switch((BTrace::TSymbianKernelSync)aSubCategory)
{
CASE_CAT_NAME(ESemaphoreCreate);
CASE_CAT_NAME(ESemaphoreDestroy);
CASE_CAT_NAME(ESemaphoreAcquire);
CASE_CAT_NAME(ESemaphoreRelease);
CASE_CAT_NAME(ESemaphoreBlock);
CASE_CAT_NAME(EMutexCreate);
CASE_CAT_NAME(EMutexDestroy);
CASE_CAT_NAME(EMutexAcquire);
CASE_CAT_NAME(EMutexRelease);
CASE_CAT_NAME(EMutexBlock);
CASE_CAT_NAME(ECondVarCreate);
CASE_CAT_NAME(ECondVarDestroy);
CASE_CAT_NAME(ECondVarBlock);
CASE_CAT_NAME(ECondVarWakeUp);
CASE_CAT_NAME(ECondVarSignal);
CASE_CAT_NAME(ECondVarBroadcast);
}
break;
case BTrace::EFlexibleMemModel:
switch((BTrace::TFlexibleMemModel)aSubCategory)
{
CASE_CAT_NAME(EMemoryObjectCreate);
CASE_CAT_NAME(EMemoryObjectDestroy);
CASE_CAT_NAME(EMemoryMappingCreate);
CASE_CAT_NAME(EMemoryMappingDestroy);
CASE_CAT_NAME(EMemoryObjectIsChunk);
CASE_CAT_NAME(EMemoryObjectIsCodeSeg);
CASE_CAT_NAME(EMemoryObjectIsProcessStaticData);
CASE_CAT_NAME(EMemoryObjectIsDllStaticData);
CASE_CAT_NAME(EMemoryObjectIsSupervisorStack);
CASE_CAT_NAME(EMemoryObjectIsUserStack);
CASE_CAT_NAME(EAddressSpaceId);
}
break;
}
return UnknownNames[aSubCategory];
}
//
// Data structures
//
enum TDataType
{
EDataTypeNone = 0,
EDataTypeText,
EDataTypeObject,
};
class Thread;
class Cpu;
struct TraceHeader
{
TUint8 iCpuNum;
TUint8 iCategory;
TUint8 iSubCategory;
TUint8 iFlags;
TUint32 iHeader2;
TUint64 iTimestamp;
TUint32 iTimestamp2;
Thread* iContextID;
TUint32 iPC;
TUint32 iExtra;
TUint8 iDataTypes[4];
TUint32 iCalculatedData[2];
TUint iDataSize;
Cpu* iCpu;
TUint32 iError;
};
struct TraceRecord : public TraceHeader
{
TUint32 iData[(8+KMaxBTraceDataArray)/4];
};
//
// ECpuUsage traces
//
enum TContext
{
EContextThread,
EContextFiq,
EContextIrq,
EContextIDFC,
EContextUnknown
};
class Cpu
{
public:
Cpu();
void ChangeContext(TContext aType, Thread* aThread=0);
void Reset();
TContext iCurrentContext;
TInt iFiqNest;
TInt iIrqNest;
TInt iIDFCNest;
TUint64 iFiqTime;
TUint64 iIrqTime;
TUint64 iIDFCTime;
Thread* iCurrentThread;
TUint64 iBaseTimestamp;
};
//
// Objects
//
const TUint KMaxTraceNameLength = 10;
class Object
{
public:
Object(TUint32 aTraceId, const char* aTraceNamePrefix)
: iTraceId(aTraceId), iIndex(~0u), iOwner(0), iOwnerTraceId(0), iAlive(1), iNameSet(false), iNameLength(0),
iTraceNamePrefix(aTraceNamePrefix)
{
iName[0] = 0;
}
void Destroy()
{
if(iAlive)
--iAlive;
if(iOwnerTraceId && !iOwner)
--UnknownOwners;
}
virtual ~Object()
{}
void SetName(void* aName, TUint aLength)
{
ASSERT(aLength<sizeof(iName));
iNameLength = (TUint8)aLength;
memcpy(iName,aName,aLength);
iName[aLength] = 0;
iNameSet = true;
}
void SetName(TraceRecord& aTrace, TUint aIndex)
{
SetName(aTrace.iData+aIndex,aTrace.iDataSize-aIndex*4);
aTrace.iDataTypes[aIndex] = EDataTypeText;
}
TBool IsName(void* aName, TUint aLength)
{
if(aLength!=iNameLength)
return false;
while(aLength--)
if(iName[aLength]!=((TUint8*)aName)[aLength])
return false;
return true;
}
typedef TUint8 TraceNameBuf[KMaxTraceNameLength+1];
typedef TUint8 FullNameBuf[KMaxBTraceDataArray+2+KMaxBTraceDataArray+2+KMaxBTraceDataArray+1]; // space for name1::name2::name3[tracename]
typedef TUint8 FullTraceNameBuf[KMaxBTraceDataArray+1+KMaxBTraceDataArray+2+KMaxBTraceDataArray+KMaxTraceNameLength+1+1]; // space for [tracename]'name1::name2::name3'
TUint FullName(FullNameBuf& aName)
{
TUint length = 0;
if(iOwner)
{
if(iOwner->iOwner)
{
memcpy(aName+length,iOwner->iOwner->iName,iOwner->iOwner->iNameLength);
length += iOwner->iOwner->iNameLength;
aName[length++] = ':';
aName[length++] = ':';
}
memcpy(aName+length,iOwner->iName,iOwner->iNameLength);
length += iOwner->iNameLength;
aName[length++] = ':';
aName[length++] = ':';
}
memcpy(aName+length,iName,iNameLength);
length += iNameLength;
aName[length] = 0;
return length;
}
TUint TraceName(TraceNameBuf& aName)
{
TInt i = 0;
const TUint KNumDigits = KMaxTraceNameLength-4;
aName[i++] = '<';
const char* prefix = iTraceNamePrefix;
if(prefix[0])
aName[i++] = *prefix++;
if(prefix[0])
aName[i++] = *prefix++;
TUint n = iIndex;
for(TUint d=KNumDigits; d>0; --d)
{
aName[i+d-1] = TUint8('0'+(n%10));
n /= 10;
}
i += KNumDigits;
aName[i++] = '>';
aName[i] = 0;
return i;
}
TUint FullTraceName(FullTraceNameBuf& aName)
{
TUint l1 = TraceName(*(TraceNameBuf*)aName);
aName[l1++] = '\'';
TUint l2 = FullName(*(FullNameBuf*)(aName+l1));
aName[l1+l2++] = '\'';
aName[l1+l2] = 0;
return l1+l2;
}
public:
TUint32 iTraceId; ///< ID for object as found in raw trace data.
TUint iIndex; ///< Index into container for this object.
Object* iOwner; ///< Object which 'owns' this one, e.g. process which owns a thread.
TUint32 iOwnerTraceId; ///< Trace ID for owner if owner object as yet unknown
TUint8 iAlive; ///< True if object destroyed trace not yet parsed.
TUint8 iNameSet; ///< True if name has been set.
TUint8 iNameLength;
TUint8 iName[KMaxBTraceDataArray+1];
const char* iTraceNamePrefix;
public:
static TUint32 UnknownOwners;
};
TUint32 Object::UnknownOwners = 0;
class ObjectContainer
{
public:
ObjectContainer()
: iNumEntries(0), iEntriesLength(0) , iEntries(0)
{
iLink = iAllContainers;
iAllContainers = this;
}
static void Reset()
{
ObjectContainer* container = iAllContainers;
while(container)
{
TUint i = container->iNumEntries;
while(i--)
free(container->iEntries[i].iItem);
free(container->iEntries);
container->iEntries = 0;
container->iNumEntries = 0;
container->iEntriesLength = 0;
container = container->iLink;
}
}
void Add(Object* aObject)
{
if(iNumEntries>=iEntriesLength)
{
iEntriesLength += 128;
iEntries = (Entry*)realloc(iEntries,iEntriesLength*sizeof(Entry));
ASSERT(iEntries);
}
aObject->iIndex = iNumEntries;
Entry& entry = iEntries[iNumEntries++];
entry.iTraceId = aObject->iTraceId;
entry.iItem = aObject;
}
TUint Count()
{
return iNumEntries;
}
Object* operator[](TInt aIndex)
{
if(TUint(aIndex)<iNumEntries)
return iEntries[aIndex].iItem;
ASSERT(0);
return 0;
}
Object* Find(TUint32 aTraceId)
{
Entry* ptr = iEntries+iNumEntries;
Entry* end = iEntries;
while(ptr>end)
{
--ptr;
if(ptr->iTraceId==aTraceId)
{
if(ptr->iItem->iAlive)
return ptr->iItem;
else
break;
}
}
return 0;
}
private:
struct Entry
{
TUint32 iTraceId;
Object* iItem;
};
TUint iNumEntries;
TUint iEntriesLength;
Entry* iEntries;
ObjectContainer* iLink;
static ObjectContainer* iAllContainers;
};
ObjectContainer* ObjectContainer::iAllContainers = 0;
#define GENERIC_OBJECT_DEFINITIONS(C) \
\
static C* Find(TUint32 aTraceId) \
{ \
return (C*)iContainer.Find(aTraceId); \
} \
\
static C* Create(TraceRecord& aTrace, TUint aIndex) \
{ \
TUint32 traceId = aTrace.iData[aIndex]; \
C* object = new C(traceId); \
aTrace.iDataTypes[aIndex] = EDataTypeObject; \
aTrace.iData[aIndex] = (uintptr_t)object; \
return object; \
} \
\
static C* Find(TraceRecord& aTrace, TUint aIndex) \
{ \
TUint32 traceId = aTrace.iData[aIndex]; \
C* object = Find(traceId); \
if(!object) \
return 0; \
aTrace.iDataTypes[aIndex] = EDataTypeObject; \
aTrace.iData[aIndex] = (uintptr_t)object; \
return object; \
} \
\
static C* FindOrCreate(TraceRecord& aTrace, TUint aIndex) \
{ \
C* object = Find(aTrace,aIndex); \
if(!object) \
object = Create(aTrace,aIndex); \
return object; \
}
//
// Process
//
class Process : public Object
{
public:
Process(TUint32 aTraceId)
: Object(aTraceId,"P"), iThreadCount(0), iMaxThreadCount(0)
{
iContainer.Add(this);
}
GENERIC_OBJECT_DEFINITIONS(Process);
public:
TUint iThreadCount;
TUint iMaxThreadCount;
static ObjectContainer iContainer;
};
ObjectContainer Process::iContainer;
//
// Thread
//
class FastMutex;
class Thread : public Object
{
public:
Thread(TUint32 aTraceId)
: Object(aTraceId,"T"), iId(~0u), iCpuTime(0), iSamples(0)
{
iContainer.Add(this);
iNameLength = (TUint8)MakeName(iName,"NThread-",aTraceId);
}
TUint64 CpuTime()
{
if(!iLastCpu || !iLastCpu->iBaseTimestamp)
return 0;
if(iLastCpu->iCurrentThread==this)
return iCpuTime + Timestamp - iLastCpu->iBaseTimestamp;
return iCpuTime;
}
void Sampled()
{
if( iSamples+1 != 0xFFFFFFFF)
{
iSamples++;
}
}
GENERIC_OBJECT_DEFINITIONS(Thread);
static Object* FindThreadOrProcess(TraceRecord& aTrace, TUint aIndex)
{
if (!aTrace.iData[aIndex])
return 0;
Object* p = Find(aTrace, aIndex);
if (!p)
p = Process::Find(aTrace, aIndex);
return p;
}
public:
TUint32 iId;
Cpu* iLastCpu;
TUint64 iCpuTime;
TUint64 iFMBlockStartTime;
FastMutex* iWaitFastMutex;
// Number of profiling samples
TUint32 iSamples;
static ObjectContainer iContainer;
};
ObjectContainer Thread::iContainer;
//
// Chunk
//
TUint ChunkErrors = 0;
const TUint KPageShift = 12; // chunk memory is allocated in 4kB pages
class Chunk : public Object
{
public:
Chunk(TUint32 aTraceId)
: Object(aTraceId,"C"), iCurrentSize(0), iPeakSize(0), iMaxSize(0), iPageMap(0), iTraceErrors(0)
{
iContainer.Add(this);
}
~Chunk()
{
free(iPageMap);
}
void SetMaxSize(TUint32 aMaxSize)
{
ASSERT(!iMaxSize);
iMaxSize = aMaxSize;
TUint mapSize = ((aMaxSize>>KPageShift)+7)>>3;
iPageMap = (TUint8*)malloc(mapSize);
ASSERT(iPageMap);
memset(iPageMap,0,mapSize);
iCurrentSize = 0;
}
void Commit(TUint32 aStart,TUint32 aSize)
{
if(!iPageMap) // we havent been intialised yet
return;
if(aStart+aSize<aStart || aStart+aSize>iMaxSize)
{
++iTraceErrors;
++ChunkErrors;
return;
}
if(SetBits(iPageMap,aStart>>KPageShift,aSize>>KPageShift))
{
++iTraceErrors;
++ChunkErrors;
ErrorOnThisTrace = true;
}
}
void Decommit(TUint32 aStart,TUint32 aSize)
{
if(!iPageMap) // we havent been intialised yet
return;
// Decommit is complicated by the fact that aSize is the number of pages
// actually decommited, which may be less than the region of the original
// chunk decommit operation. E.g. if pages 1 and 3 were originally decommited
// and the decommit operation was for pages 0-3, then the trace has a size of
// 2 pages, even though the operation was on 4 pages.
// We handle this, repeatedly decommiting from our iPageMap, until we have
// freed aSize bytes worth of pages...
while(aSize)
{
if(aStart+aSize<aStart || aStart+aSize>iMaxSize)
{
// we haven't found enough memory to decommit
++iTraceErrors;
++ChunkErrors;
ErrorOnThisTrace = true;
return;
}
TUint notDecommitted = ClearBits(iPageMap,aStart>>KPageShift,aSize>>KPageShift);
aStart += aSize;
aSize = notDecommitted<<KPageShift;
}
}
void ResetMemory()
{
}
GENERIC_OBJECT_DEFINITIONS(Chunk);
public:
TUint32 iCurrentSize;
TUint32 iPeakSize;
TUint32 iMaxSize;
TUint8* iPageMap;
TUint iTraceErrors;
static ObjectContainer iContainer;
};
ObjectContainer Chunk::iContainer;
//
// Semaphore, Mutex, CondVar
//
class Semaphore : public Object
{
public:
Semaphore(TUint32 aTraceId)
: Object(aTraceId,"S")
{
iContainer.Add(this);
}
~Semaphore()
{
}
GENERIC_OBJECT_DEFINITIONS(Semaphore);
public:
static ObjectContainer iContainer;
};
ObjectContainer Semaphore::iContainer;
class Mutex : public Object
{
public:
Mutex(TUint32 aTraceId)
: Object(aTraceId,"M")
{
iContainer.Add(this);
}
~Mutex()
{
}
GENERIC_OBJECT_DEFINITIONS(Mutex);
public:
static ObjectContainer iContainer;
};
ObjectContainer Mutex::iContainer;
class CondVar : public Object
{
public:
CondVar(TUint32 aTraceId)
: Object(aTraceId,"V")
{
iContainer.Add(this);
}
~CondVar()
{
}
GENERIC_OBJECT_DEFINITIONS(CondVar);
public:
static ObjectContainer iContainer;
};
ObjectContainer CondVar::iContainer;
//
// CodeSeg
//
TUint CodeSegErrors = 0;
class CodeSeg : public Object
{
public:
CodeSeg(TUint32 aTraceId)
: Object(aTraceId,"CS"), iAllocatedMemory(0)
{
iContainer.Add(this);
}
GENERIC_OBJECT_DEFINITIONS(CodeSeg);
TUint iAllocatedMemory;
public:
static ObjectContainer iContainer;
};
ObjectContainer CodeSeg::iContainer;
//
// FastMutex
//
TUint FastMutexNestErrors = 0;
class FastMutex : public Object
{
public:
FastMutex(TUint32 aTraceId)
: Object(aTraceId,"FM"), iHoldingThread(0), iHeldCount(0), iTotalHeldTime(0),
iMaxHeldTime(0), iMaxHeldTimestamp(0), iMaxHeldPc(0), iBlockCount(0)
{
iContainer.Add(this);
iNameLength = (TUint8)MakeName(iName,"NFastMutex-",aTraceId);
iNameLength = 19;
}
TUint32 Wait(Thread* aThread)
{
TUint32 time = 0;
if(iHoldingThread)
{
++FastMutexNestErrors;
ErrorOnThisTrace = true;
}
iHoldingThread = aThread;
iWaitCpuTimeBase = aThread->CpuTime();
if (aThread->iWaitFastMutex == this)
{
time = (TUint32)(Timestamp - aThread->iFMBlockStartTime);
}
aThread->iWaitFastMutex = 0;
return time;
}
void Block(Thread* aThread)
{
if (aThread->iWaitFastMutex != this)
{
aThread->iFMBlockStartTime = Timestamp;
aThread->iWaitFastMutex = this;
++iBlockCount;
}
};
TUint32 Signal(Thread* aThread, TUint32 aPc)
{
if (!iHoldingThread) // First record for this mutex so ignore it as don't
return 0; // have waiting thread details
if(iHoldingThread!=aThread)
{
++FastMutexNestErrors;
ErrorOnThisTrace = true;
iHoldingThread = 0;
return 0;
}
iHoldingThread = 0;
TUint64 time = aThread->CpuTime()-iWaitCpuTimeBase;
++iHeldCount;
iTotalHeldTime += time;
if(time>iMaxHeldTime)
{
iMaxHeldTime = time;
iMaxHeldPc = aPc;
iMaxHeldTimestamp = Timestamp;
}
return (TUint32)time;
}
GENERIC_OBJECT_DEFINITIONS(FastMutex);
public:
Thread* iHoldingThread;
TUint32 iHeldCount; // number of times mutex acquired
TUint64 iTotalHeldTime;
TUint64 iWaitCpuTimeBase;
TUint64 iMaxHeldTime;
TUint64 iMaxHeldTimestamp;
TUint32 iMaxHeldPc;
TUint32 iBlockCount; // number of times mutex caused a thread to wait
static ObjectContainer iContainer;
};
ObjectContainer FastMutex::iContainer;
//
// ProfilingSample
//
TUint ProfilingSampleErrors = 0;
class ProfilingSample : public Object
{
public:
ProfilingSample(TUint32 aTraceId)
: Object(aTraceId,"PS")
{
iContainer.Add(this);
iNameLength = (TUint8)MakeName(iName,"ProfilingSample-",aTraceId);
}
void SetPC( TUint32 aPC )
{
iPC = aPC;
}
void SetThread( TUint32 aThread )
{
if( 0 != aThread )
iThread = aThread;
}
void SetType(const BTrace::TProfiling aType)
{
iType = aType;
}
GENERIC_OBJECT_DEFINITIONS(ProfilingSample);
public:
static ObjectContainer iContainer;
static Thread* iTopTen[10];
static TUint32 iSamples;
static TUint32 iLastThread;
TUint32 iPC;
TUint32 iThread;
BTrace::TProfiling iType;
};
TUint32 ProfilingSample::iSamples = 0;
TUint32 ProfilingSample::iLastThread = 0;
ObjectContainer ProfilingSample::iContainer;
//
// EThreadIdentification traces
//
void PreProcessThreadIdentification(TraceRecord& aTrace)
{
Thread* thread;
Process* process;
switch((BTrace::TThreadIdentification)aTrace.iSubCategory)
{
case BTrace::ENanoThreadCreate:
CHECK_TRACE_DATA_WORDS(1);
thread = Thread::FindOrCreate(aTrace,0);
break;
case BTrace::ENanoThreadDestroy:
CHECK_TRACE_DATA_WORDS(1);
thread = Thread::Find(aTrace,0);
if(thread)
thread->Destroy();
break;
case BTrace::EThreadCreate:
case BTrace::EThreadName:
CHECK_TRACE_DATA_WORDS(2);
thread = Thread::FindOrCreate(aTrace,0);
if(aTrace.iSubCategory==BTrace::EThreadCreate)
++thread->iAlive; // thread needs destroying twice (ENanoThreadDestroy+EThreadDestroy)
process = Process::FindOrCreate(aTrace,1);
thread->iOwner = process;
++process->iThreadCount;
if(process->iThreadCount>process->iMaxThreadCount)
process->iMaxThreadCount = process->iThreadCount;
thread->SetName(aTrace,2);
break;
case BTrace::EThreadDestroy:
CHECK_TRACE_DATA_WORDS(1);
thread = Thread::Find(aTrace,0);
if(thread)
{
thread->Destroy();
process = (Process*)thread->iOwner;
if(process && process->iThreadCount)
--process->iThreadCount;
}
break;
case BTrace::EProcessName:
CHECK_TRACE_DATA_WORDS(2);
if(aTrace.iData[0])
{
thread = Thread::FindOrCreate(aTrace,0);
process = Process::Find(aTrace.iData[1]);
if(!process || (process->iNameLength && !process->IsName(aTrace.iData+2,aTrace.iDataSize-2*4)))
{
if(process)
process->Destroy();
process = Process::Create(aTrace,1); // no existing process, or name different
}
else
process = Process::Find(aTrace,1); // find again (this will update trace data[1])
}
else
process = Process::Find(aTrace,1);
if(process)
process->SetName(aTrace,2);
break;
case BTrace::EThreadId:
CHECK_TRACE_DATA_WORDS(2);
thread = Thread::FindOrCreate(aTrace,0);
process = Process::FindOrCreate(aTrace,1);
thread->iId = aTrace.iData[2];
break;
case BTrace::EProcessCreate:
CHECK_TRACE_DATA_WORDS(1);
process = Process::FindOrCreate(aTrace,0);
break;
case BTrace::EProcessDestroy:
CHECK_TRACE_DATA_WORDS(1);
process = Process::FindOrCreate(aTrace,0);
if(process)
process->Destroy();
break;
default:
break;
}
}
//
// ECpuUsage traces
//
Cpu TheCpus[KMaxCpus];
TUint InterruptNestErrors = 0;
TUint CpuUsagePresent = 0;
Cpu::Cpu()
: iCurrentContext(EContextUnknown),
iFiqNest(0),
iIrqNest(0),
iIDFCNest(0),
iFiqTime(0),
iIrqTime(0),
iIDFCTime(0),
iCurrentThread(0),
iBaseTimestamp(0)
{
}
void Cpu::Reset()
{
iCurrentContext = EContextUnknown;
iFiqNest = 0;
iIrqNest = 0;
iIDFCNest = 0;
iCurrentThread = 0;
iBaseTimestamp = 0;
}
void ResetCpuUsage()
{
TInt i;
for (i=0; i<KMaxCpus; ++i)
TheCpus[i].Reset();
}
void StartCpuUsage()
{
TInt i;
for (i=0; i<KMaxCpus; ++i)
new (&TheCpus[i]) Cpu;
InterruptNestErrors = 0;
}
void Cpu::ChangeContext(TContext aType, Thread* aThread)
{
TUint64 delta = Timestamp-iBaseTimestamp;
switch(iCurrentContext)
{
case EContextThread:
iCurrentThread->iCpuTime += delta;
break;
case EContextFiq:
iFiqTime += delta;
break;
case EContextIrq:
iIrqTime += delta;
break;
case EContextIDFC:
iIDFCTime += delta;
break;
default:
break;
}
if(aType==EContextUnknown)
{
if(iFiqNest)
aType = EContextFiq;
else if(iIrqNest)
aType = EContextIrq;
else if(iIDFCNest)
aType = EContextIDFC;
else
{
aType = EContextThread;
aThread = iCurrentThread;
}
}
if(aType==EContextThread && !aThread)
{
iCurrentContext = EContextUnknown;
iBaseTimestamp = 0;
return;
}
iCurrentContext = aType;
if(aType==EContextThread)
{
iCurrentThread = aThread;
aThread->iLastCpu = this;
}
iBaseTimestamp = Timestamp;
}
void PreProcessCpuUsage(TraceRecord& aTrace)
{
CpuUsagePresent = true;
Cpu& cpu = *aTrace.iCpu;
switch((BTrace::TCpuUsage)aTrace.iSubCategory)
{
case BTrace::EIrqStart:
++cpu.iIrqNest;
cpu.ChangeContext(EContextIrq);
break;
case BTrace::EFiqStart:
++cpu.iFiqNest;
cpu.ChangeContext(EContextFiq);
break;
case BTrace::EIDFCStart:
if(cpu.iIrqNest+cpu.iFiqNest>1 || cpu.iIDFCNest!=0)
{
++InterruptNestErrors;
ErrorOnThisTrace = true;
}
cpu.iIrqNest = 0;
cpu.iFiqNest = 0;
cpu.iIDFCNest = 1;
cpu.ChangeContext(EContextIDFC);
break;
case BTrace::EIrqEnd:
if(cpu.iIrqNest)
--cpu.iIrqNest;
else
{
++InterruptNestErrors;
ErrorOnThisTrace = true;
}
cpu.ChangeContext(EContextUnknown);
break;
case BTrace::EFiqEnd:
if(cpu.iFiqNest)
--cpu.iFiqNest;
else
{
++InterruptNestErrors;
ErrorOnThisTrace = true;
}
cpu.ChangeContext(EContextUnknown);
break;
case BTrace::EIDFCEnd:
if(cpu.iIDFCNest!=1)
{
++InterruptNestErrors;
ErrorOnThisTrace = true;
}
cpu.iIDFCNest = 0;
cpu.ChangeContext(EContextUnknown);
break;
case BTrace::ENewThreadContext:
if(cpu.iIrqNest+cpu.iFiqNest>1 || cpu.iIDFCNest!=0)
{
++InterruptNestErrors;
ErrorOnThisTrace = true;
}
cpu.iIrqNest = 0;
cpu.iFiqNest = 0;
cpu.iIDFCNest = 0;
cpu.ChangeContext(EContextThread,aTrace.iContextID);
break;
}
}
void ReportThreads()
{
TUint numThreads = Thread::iContainer.Count();
if(!numThreads)
return;
TUint64 totalTime = 0;
printf("\nREPORT: Threads\n\n");
WarnIfError(0);
printf("%-10s %5s %10s %8s %s\n","","State","CPUTime","TraceId","Name");
TUint i;
for(i=0; i<numThreads; ++i)
{
Thread* thread = (Thread*)Thread::iContainer[i];
Object::FullNameBuf fullName;
thread->FullName(fullName);
Object::TraceNameBuf name;
thread->TraceName(name);
printf("%-10s %5s %10d %08x '%s'\n",name,
thread->iAlive?(const char*)"Alive":(const char*)"Dead",
(int)Time(thread->iCpuTime),(int)thread->iTraceId,fullName);
totalTime += thread->iCpuTime;
}
for (i=0; i<(TUint)KMaxCpus; ++i)
{
printf("CPU %1d\n", i);
Cpu& cpu = TheCpus[i];
printf("%-10s %5s %10d %s\n","FIQ","",(int)Time(cpu.iFiqTime),"");
printf("%-10s %5s %10d %s\n","IRQ","",(int)Time(cpu.iIrqTime),"");
printf("%-10s %5s %10d %s\n","IDFC","",(int)Time(cpu.iIDFCTime),"");
totalTime += cpu.iFiqTime + cpu.iIrqTime + cpu.iIDFCTime;
}
printf("%-10s %5s ----------\n","","");
printf("%-10s %5s %10d\n","","",(int)Time(totalTime));
printf("\n");
}
void ReportProcesses()
{
TUint numProcesses = Process::iContainer.Count();
if(!numProcesses)
return;
printf("\nREPORT: Processes\n\n");
WarnIfError(0);
printf("%-10s %5s %7s %8s %s\n","","State","Threads","TraceId","Name");
TUint i;
for(i=0; i<numProcesses; ++i)
{
Process* process = (Process*)Process::iContainer[i];
Object::FullNameBuf fullName;
process->FullName(fullName);
Object::TraceNameBuf name;
process->TraceName(name);
printf("%-10s %5s %3u/%-3u %08x '%s'\n",name,
process->iAlive?(const char*)"Alive":(const char*)"Dead",
(unsigned int)process->iThreadCount,(unsigned int)process->iMaxThreadCount,
(unsigned int)process->iTraceId,fullName);
}
printf("\n");
}
void EndCpuUsage()
{
TInt i;
for (i=0; i<KMaxCpus; ++i)
TheCpus[i].ChangeContext(EContextUnknown);
}
//
// EChunks traces
//
void StartChunks()
{
ChunkErrors = 0;
}
void PreProcessChunks(TraceRecord& aTrace)
{
CHECK_TRACE_DATA_WORDS(1);
Chunk* chunk = Chunk::FindOrCreate(aTrace,0);
switch((BTrace::TChunks)aTrace.iSubCategory)
{
case BTrace::EChunkCreated:
CHECK_TRACE_DATA_WORDS(2);
chunk->SetName(aTrace,2);
chunk->SetMaxSize(aTrace.iData[1]);
// start by assuming thread is 'owned' by the thread which created it...
chunk->iOwner = aTrace.iContextID;
break;
case BTrace::EChunkInfo:
CHECK_TRACE_DATA_WORDS(3);
break; // ignore
case BTrace::EChunkDestroyed:
chunk->Destroy();
break;
case BTrace::EChunkMemoryAllocated:
{
CHECK_TRACE_DATA_WORDS(3);
chunk->Commit(aTrace.iData[1],aTrace.iData[2]);
TUint32 size = chunk->iCurrentSize+aTrace.iData[2];
if(size<chunk->iCurrentSize || size>chunk->iMaxSize)
size = chunk->iMaxSize;
chunk->iCurrentSize = size;
if(size>chunk->iPeakSize)
chunk->iPeakSize = size;
aTrace.iCalculatedData[0] = size/1024;
}
break;
case BTrace::EChunkMemoryDeallocated:
{
CHECK_TRACE_DATA_WORDS(3);
chunk->Decommit(aTrace.iData[1],aTrace.iData[2]);
TUint32 size = chunk->iCurrentSize-aTrace.iData[2];
if(size>chunk->iCurrentSize)
size = 0;
chunk->iCurrentSize = size;
aTrace.iCalculatedData[0] = size/1024;
}
break;
case BTrace::EChunkMemoryAdded:
CHECK_TRACE_DATA_WORDS(3);
chunk->Commit(aTrace.iData[1],aTrace.iData[2]);
break;
case BTrace::EChunkMemoryRemoved:
CHECK_TRACE_DATA_WORDS(3);
chunk->Decommit(aTrace.iData[1],aTrace.iData[2]);
break;
case BTrace::EChunkOwner:
{
CHECK_TRACE_DATA_WORDS(2);
Process* process = Process::FindOrCreate(aTrace,1);
// set owner, unless current owner is owned by the same process
// (this preserves creating thread names in ownership list which is more useful)
if(!chunk->iOwner || chunk->iOwner->iOwner!=process)
chunk->iOwner = process;
}
break;
}
}
void ReportChunks()
{
TUint numChunks = Chunk::iContainer.Count();
if(!numChunks)
return;
if(!ReportLevel)
printf("\nREPORT: Chunks (Named objects only)\n\n");
else
printf("\nREPORT: Chunks\n\n");
WarnIfError(ChunkErrors);
printf("%-10s %5s %8s %8s %8s %8s %s\n",
"","State","Size","Peak","Max","TraceId","Name");
TUint totalSize = 0;
TUint i;
for(i=0; i<numChunks; ++i)
{
Chunk* chunk = (Chunk*)Chunk::iContainer[i];
if(ReportLevel==0 && !chunk->iNameSet)
continue; // only report explicitly named mutexes at report level 0
Object::FullNameBuf fullName;
chunk->FullName(fullName);
Object::TraceNameBuf name;
chunk->TraceName(name);
printf("%-10s %5s %7uk %7uk %7uk %08x '%s'\n",
name,chunk->iAlive?(const char*)"Alive":(const char*)"Dead",
(unsigned int)chunk->iCurrentSize/1024,(unsigned int)chunk->iPeakSize/1024,(unsigned int)chunk->iMaxSize/1024,
(unsigned int)chunk->iTraceId,fullName);
totalSize += chunk->iCurrentSize/1024;
}
printf("%-10s %5s --------\n","","");
printf("%-10s %5s %7uk\n","","",totalSize);
printf("\n");
}
//
// CodeSeg
//
void StartCodeSegs()
{
CodeSegErrors = 0;
}
void PreProcessCodeSegs(TraceRecord& aTrace)
{
CHECK_TRACE_DATA_WORDS(1);
CodeSeg* codeseg;
switch((BTrace::TCodeSegs)aTrace.iSubCategory)
{
case BTrace::ECodeSegCreated:
codeseg = CodeSeg::FindOrCreate(aTrace,0);
codeseg->SetName(aTrace,1);
break;
case BTrace::ECodeSegInfo:
CHECK_TRACE_DATA_WORDS(10);
CodeSeg::FindOrCreate(aTrace,0);
/* - 4 bytes containing the attributes.
- 4 bytes containing the code base address (.text).
- 4 bytes containing the size of the code section (.text).
- 4 bytes containing the base address of the constant data section (.radata).
- 4 bytes containing the size of the constant data section (.radata).
- 4 bytes containing the base address of the initialised data section (.data).
- 4 bytes containing the size of the initialised data section (.data).
- 4 bytes containing the base address of the uninitialised data section (.bss).
- 4 bytes containing the size of the uninitialised data section (.bss).
*/ break;
case BTrace::ECodeSegDestroyed:
codeseg = CodeSeg::FindOrCreate(aTrace,0);
codeseg->Destroy();
codeseg->iAllocatedMemory = 0; // clear this now because ECodeSegMemoryDeallocated comes after codeseg destroy
break;
case BTrace::ECodeSegMapped:
CHECK_TRACE_DATA_WORDS(2);
codeseg = CodeSeg::FindOrCreate(aTrace,0);
Process::FindOrCreate(aTrace,1);
break;
case BTrace::ECodeSegUnmapped:
CHECK_TRACE_DATA_WORDS(2);
CodeSeg::FindOrCreate(aTrace,0);
Process::FindOrCreate(aTrace,1);
break;
case BTrace::ECodeSegMemoryAllocated:
CHECK_TRACE_DATA_WORDS(2);
codeseg = CodeSeg::FindOrCreate(aTrace,0);
codeseg->iAllocatedMemory += aTrace.iData[1];
if(codeseg->iAllocatedMemory<aTrace.iData[1])
{
codeseg->iAllocatedMemory = ~0u; // overflowed!
++CodeSegErrors;
ErrorOnThisTrace = true;
}
break;
case BTrace::ECodeSegMemoryDeallocated:
{
CHECK_TRACE_DATA_WORDS(2);
codeseg = CodeSeg::Find(aTrace,0);
if(codeseg)
{
TUint32 memory = codeseg->iAllocatedMemory-aTrace.iData[1];
if(memory>codeseg->iAllocatedMemory)
{
memory = 0; // underflowed
++CodeSegErrors;
ErrorOnThisTrace = true;
}
codeseg->iAllocatedMemory = memory;
}
}
break;
}
}
void ReportCodeSegs()
{
TUint numCodeSegs = CodeSeg::iContainer.Count();
if(!numCodeSegs)
return;
if(!ReportLevel)
printf("\nREPORT: CodeSegs (Named objects only)\n\n");
else
printf("\nREPORT: CodeSegs\n\n");
WarnIfError(CodeSegErrors);
printf("%-10s %5s %8s %8s %s\n",
"","State","Memory","TraceId","Name");
TUint totalSize = 0;
TUint i;
for(i=0; i<numCodeSegs; ++i)
{
CodeSeg* codeseg = (CodeSeg*)CodeSeg::iContainer[i];
if(ReportLevel==0 && !codeseg->iNameSet)
continue; // only report explicitly named mutexes at report level 0
Object::FullNameBuf fullName;
codeseg->FullName(fullName);
Object::TraceNameBuf name;
codeseg->TraceName(name);
printf("%-10s %5s %7uk %08x '%s'\n",
name,codeseg->iAlive?(const char*)"Alive":(const char*)"Dead",
(unsigned int)codeseg->iAllocatedMemory/1024,(unsigned int)codeseg->iTraceId,fullName);
totalSize += codeseg->iAllocatedMemory/1024;
}
printf("%-10s %5s --------\n","","");
printf("%-10s %5s %7uk\n","","",totalSize);
printf("\n");
}
//
// MetaTrace
//
TUint KernelMemoryInitialFree = 0;
TUint KernelMemoryCurrentFree = 0;
TUint KernelMemoryMisc = 0;
TUint KernelMemoryDrvPhys = 0;
TUint KernelMemoryDemandPagingCache = 0;
TUint KernelMemoryErrors = 0;
TBool KernelMemoryTracesPresent = false;
void StartKernelMemory()
{
KernelMemoryInitialFree = 0;
KernelMemoryCurrentFree = 0;
KernelMemoryMisc = 0;
KernelMemoryDrvPhys = 0;
KernelMemoryErrors = 0;
KernelMemoryTracesPresent = false;
}
void PreProcessKernelMemory(TraceRecord& aTrace)
{
CHECK_TRACE_DATA_WORDS(1);
KernelMemoryTracesPresent = true;
switch((BTrace::TKernelMemory)aTrace.iSubCategory)
{
case BTrace::EKernelMemoryInitialFree:
KernelMemoryInitialFree = aTrace.iData[0];
aTrace.iCalculatedData[0] = KernelMemoryInitialFree/1024;
break;
case BTrace::EKernelMemoryCurrentFree:
KernelMemoryCurrentFree = aTrace.iData[0];
aTrace.iCalculatedData[0] = KernelMemoryCurrentFree/1024;
break;
case BTrace::EKernelMemoryMiscAlloc:
KernelMemoryMisc += aTrace.iData[0];
if(KernelMemoryMisc < aTrace.iData[0])
{
KernelMemoryMisc = 0xffffffffu;
++KernelMemoryErrors;
ErrorOnThisTrace = true;
}
aTrace.iCalculatedData[0] = KernelMemoryMisc/1024;
break;
case BTrace::EKernelMemoryMiscFree:
if(KernelMemoryMisc >= aTrace.iData[0])
KernelMemoryMisc -= aTrace.iData[0];
else
{
KernelMemoryMisc = 0;
++KernelMemoryErrors;
ErrorOnThisTrace = true;
}
aTrace.iCalculatedData[0] = KernelMemoryMisc/1024;
break;
case BTrace::EKernelMemoryDemandPagingCache:
KernelMemoryDemandPagingCache = aTrace.iData[0];
break;
case BTrace::EKernelMemoryDrvPhysAlloc:
KernelMemoryDrvPhys += aTrace.iData[0];
if(KernelMemoryDrvPhys < aTrace.iData[0])
{
KernelMemoryDrvPhys = 0xffffffffu;
++KernelMemoryErrors;
ErrorOnThisTrace = true;
}
aTrace.iCalculatedData[0] = KernelMemoryDrvPhys/1024;
break;
case BTrace::EKernelMemoryDrvPhysFree:
if(KernelMemoryDrvPhys >= aTrace.iData[0])
KernelMemoryDrvPhys -= aTrace.iData[0];
else
{
KernelMemoryDrvPhys = 0;
++KernelMemoryErrors;
ErrorOnThisTrace = true;
}
aTrace.iCalculatedData[0] = KernelMemoryDrvPhys/1024;
break;
}
}
void ReportKernelMemory()
{
if(!KernelMemoryTracesPresent)
return;
printf("\nREPORT: Kernel Memory\n\n");
WarnIfError(KernelMemoryErrors);
printf("Total RAM size............................. %dk\n",KernelMemoryInitialFree/1024);
printf("Miscelaneous RAM used by kernel............ %dk\n",KernelMemoryMisc/1024);
printf("Physical RAM allocated by device drivers... %dk\n",KernelMemoryDrvPhys/1024);
printf("Demand paging cache reserve................ %dk\n",KernelMemoryDemandPagingCache/1024);
if(ReportLevel>1)
printf("Last 'current free RAM' value seen......... %dk\n",KernelMemoryCurrentFree/1024);
printf("\n");
}
//
// MetaTrace
//
void StartMetaTrace()
{
TimestampPeriod = 0;
Timestamp2Period = 0;
}
void PreProcessMetaTrace(TraceRecord& aTrace)
{
switch((BTrace::TMetaTrace)aTrace.iSubCategory)
{
case BTrace::EMetaTraceTimestampsInfo:
CHECK_TRACE_DATA_WORDS(3);
TimestampPeriod = aTrace.iData[0];
Timestamp2Period = aTrace.iData[1];
Timestamp64Bit = aTrace.iData[2]&1;
break;
case BTrace::EMetaTraceMeasurementStart:
case BTrace::EMetaTraceMeasurementEnd:
CHECK_TRACE_DATA_WORDS(2);
aTrace.iDataTypes[2] = EDataTypeText;
break;
case BTrace::EMetaTraceFilterChange:
CHECK_TRACE_DATA_WORDS(1);
break;
}
}
//
// EFastMutex traces
//
void StartFastMutex()
{
FastMutexNestErrors = 0;
}
void PreProcessFastMutex(TraceRecord& aTrace)
{
CHECK_TRACE_DATA_WORDS(1);
FastMutex* mutex = FastMutex::FindOrCreate(aTrace,0);
Thread* thread = aTrace.iContextID;
switch((BTrace::TFastMutex)aTrace.iSubCategory)
{
case BTrace::EFastMutexWait:
aTrace.iCalculatedData[0] = Time(mutex->Wait(thread));
break;
case BTrace::EFastMutexSignal:
aTrace.iCalculatedData[0] = Time(mutex->Signal(thread,aTrace.iPC));
break;
case BTrace::EFastMutexFlash:
aTrace.iCalculatedData[0] = Time(mutex->Signal(thread,aTrace.iPC));
mutex->Wait(thread);
break;
case BTrace::EFastMutexName:
CHECK_TRACE_DATA_WORDS(2);
mutex->SetName(aTrace,2);
break;
case BTrace::EFastMutexBlock:
mutex->Block(thread);
break;
}
}
void PreProcessSymbianKernelSync(TraceRecord& aTrace)
{
switch((BTrace::TSymbianKernelSync)aTrace.iSubCategory)
{
case BTrace::ESemaphoreCreate:
{
CHECK_TRACE_DATA_WORDS(2);
TUint32 ownerid = aTrace.iData[1];
Semaphore* sem = Semaphore::FindOrCreate(aTrace,0);
Object* owner = Thread::FindThreadOrProcess(aTrace,1);
sem->iOwner = owner;
if (!owner && ownerid)
sem->iOwnerTraceId = ownerid, ++Object::UnknownOwners;
sem->SetName(aTrace,2);
break;
}
case BTrace::ESemaphoreDestroy:
{
CHECK_TRACE_DATA_WORDS(1);
Semaphore* sem = Semaphore::Find(aTrace,0);
if (sem)
sem->Destroy();
break;
}
case BTrace::ESemaphoreAcquire:
case BTrace::ESemaphoreRelease:
case BTrace::ESemaphoreBlock:
{
CHECK_TRACE_DATA_WORDS(1);
Semaphore::FindOrCreate(aTrace,0);
break;
}
case BTrace::EMutexCreate:
{
CHECK_TRACE_DATA_WORDS(2);
TUint32 ownerid = aTrace.iData[1];
Mutex* m = Mutex::FindOrCreate(aTrace,0);
Object* owner = Thread::FindThreadOrProcess(aTrace,1);
m->iOwner = owner;
if (!owner && ownerid)
m->iOwnerTraceId = ownerid, ++Object::UnknownOwners;
m->SetName(aTrace,2);
break;
}
case BTrace::EMutexDestroy:
{
CHECK_TRACE_DATA_WORDS(1);
Mutex* m = Mutex::Find(aTrace,0);
if (m)
m->Destroy();
break;
}
case BTrace::EMutexAcquire:
case BTrace::EMutexRelease:
case BTrace::EMutexBlock:
{
CHECK_TRACE_DATA_WORDS(1);
Mutex::FindOrCreate(aTrace,0);
break;
}
case BTrace::ECondVarCreate:
{
CHECK_TRACE_DATA_WORDS(2);
TUint32 ownerid = aTrace.iData[1];
CondVar* cv = CondVar::FindOrCreate(aTrace,0);
Object* owner = Thread::FindThreadOrProcess(aTrace,1);
cv->iOwner = owner;
if (!owner && ownerid)
cv->iOwnerTraceId = ownerid, ++Object::UnknownOwners;
cv->SetName(aTrace,2);
break;
}
case BTrace::ECondVarDestroy:
{
CHECK_TRACE_DATA_WORDS(1);
CondVar* cv = CondVar::Find(aTrace,0);
if (cv)
cv->Destroy();
break;
}
case BTrace::ECondVarBlock:
case BTrace::ECondVarWakeUp:
case BTrace::ECondVarSignal:
case BTrace::ECondVarBroadcast:
{
CHECK_TRACE_DATA_WORDS(1);
CondVar::FindOrCreate(aTrace,0);
break;
}
default:
break;
}
}
void ReportFastMutex()
{
TUint numMutexes = FastMutex::iContainer.Count();
if(!numMutexes)
return;
if(!ReportLevel)
printf("\nREPORT: FastMutexes (Named objects only)\n\n");
else
printf("\nREPORT: FastMutexes\n\n");
WarnIfError(0);
printf("%-10s %8s %8s %10s %10s %-8s %12s %8s %s\n",
"","MaxTime","AveTime","HeldCount","BlockCount","MaxPC","MaxTimestamp","TraceId","Name");
TUint i;
for(i=0; i<numMutexes; ++i)
{
FastMutex* mutex = (FastMutex*)FastMutex::iContainer[i];
if(ReportLevel==0 && !mutex->iNameSet)
continue; // only report explicitly named mutexes at report level 0
Object::FullNameBuf fullName;
mutex->FullName(fullName);
Object::TraceNameBuf name;
mutex->TraceName(name);
TUint32 averageHeldTime = mutex->iHeldCount ? Time(mutex->iTotalHeldTime/mutex->iHeldCount) : 0;
printf("%-10s %8u %8u %10u %10u %08x %12u %08x '%s'\n",
name,(unsigned int)Time(mutex->iMaxHeldTime),(unsigned int)averageHeldTime,(unsigned int)mutex->iHeldCount,(unsigned int)mutex->iBlockCount,
(unsigned int)mutex->iMaxHeldPc,(unsigned int)Time(mutex->iMaxHeldTimestamp-TimestampBase),(unsigned int)mutex->iTraceId,fullName);
}
printf("\n");
}
//
// ProfilingSample
//
void StartProfilingSample()
{
ProfilingSampleErrors = 0;
}
/**
Index 0 of TraceRecord is the program counter
The only one not having it are ECpuNonSymbianThreadSample samples.
Index 1 of TraceRecord is the NThread pointer.
The only one that has it is ECpuFullSample.
The samples are identified by their index, which is maintained by
ProfilingSample::iSamples. Thus to create a ProfilingSample object we
need to put this value in the data at index 0 after copying the PC
and Thread id (if present).
The reasoning is that all samples need to be represented and thus we
need to create a ProfilingSample object, even when they are on the same
PC and or thread. Each sample important and should not be discarded.
*/
void PreProcessProfiling(TraceRecord& aTrace)
{
ProfilingSample* sample;
Thread* thread;
switch((BTrace::TProfiling)aTrace.iSubCategory)
{
case BTrace::ECpuFullSample:
{
CHECK_TRACE_DATA_WORDS(2);
TUint32 aThread = aTrace.iData[1];
// The thread id is aTrace.iData[1], so find or create it
// This action can modify aTrace.iData[1], that is why we took a copy above
thread = Thread::FindOrCreate(aTrace,1);
if( thread )
thread->Sampled();
TUint32 aPC = aTrace.iData[0];
// Always create a sample identified by the running counter ProfilingSample::iSamples
aTrace.iData[0] = ProfilingSample::iSamples;
sample = ProfilingSample::Create(aTrace,0);
if( sample )
{
sample->SetPC( aPC );
sample->SetThread( aThread );
sample->SetType(BTrace::ECpuFullSample);
}
ProfilingSample::iLastThread = aThread;
}
break;
case BTrace::ECpuOptimisedSample:
{
CHECK_TRACE_DATA_WORDS(1);
TUint32 aPC = aTrace.iData[0];
aTrace.iData[0] = ProfilingSample::iSamples;
sample = ProfilingSample::Create(aTrace,0);
if( sample )
{
sample->SetPC( aPC );
sample->SetType( BTrace::ECpuOptimisedSample );
sample->SetThread(ProfilingSample::iLastThread);
}
if( 0 != ProfilingSample::iLastThread )
{
thread = Thread::Find(ProfilingSample::iLastThread);
if( thread )
{
thread->Sampled();
}
}
}
break;
case BTrace::ECpuIdfcSample:
{
CHECK_TRACE_DATA_WORDS(1);
TUint32 aPC = aTrace.iData[0];
aTrace.iData[0] = ProfilingSample::iSamples;
sample = ProfilingSample::Create(aTrace,0);
sample->SetPC( aPC );
sample->SetType(BTrace::ECpuIdfcSample);
}
break;
case BTrace::ECpuNonSymbianThreadSample:
{
// No data
aTrace.iData[0] = ProfilingSample::iSamples;
sample = ProfilingSample::Create(aTrace,0);
sample->SetType(BTrace::ECpuNonSymbianThreadSample);
}
break;
default:
ProfilingSampleErrors++;
ErrorOnThisTrace = true;
}
ProfilingSample::iSamples++;
}
void ReportSampleProfiling()
{
printf("\nREPORT: Profiling\n\n");
TUint numSamples = ProfilingSample::iContainer.Count();
if(!numSamples)
{
printf("\n No Samples\n\n");
return;
}
WarnIfError(0);
// Print thread samples
TUint numThreads = Thread::iContainer.Count();
if(numThreads)
{
printf(" Samples by Thread\n\n");
printf("%-11s %-8s %-8s\t%-12s\t%s\n\n", "", "TraceId", "Samples", "%", "Name");
TUint i;
TReal threadPercentage;
for(i=0; i<numThreads; ++i)
{
Thread* thread = (Thread*)Thread::iContainer[i];
if( thread && thread->iSamples )
{
Object::FullNameBuf fullName;
thread->FullName(fullName);
Object::TraceNameBuf name;
thread->TraceName(name);
threadPercentage = thread->iSamples*100.0/numSamples;
printf("%-10s %08x %8d\t%02.2f\t'%s'\n",
name,
(unsigned int)thread->iTraceId,
(unsigned int)(thread->iSamples),
threadPercentage,
fullName );
}//if samples
}//for numThreads
}//if threads
if(ReportLevel>0)
{
printf("\nAll samples\n\n%-21s %-8s %-8s\n\n", "Type", "ThreadId", "PC");
TUint i;
TUint fullSamples = 0;
TUint optSamples = 0;
TUint dfcSamples = 0;
TUint nonSymbSamples = 0;
for(i=0; i<numSamples; ++i)
{
ProfilingSample* sample = (ProfilingSample*)ProfilingSample::iContainer[i];
switch((BTrace::TProfiling)sample->iType)
{
case BTrace::ECpuFullSample:
{
if( ReportLevel>1)
printf("ECpuFull %08x %08x\n",
(unsigned int)(sample->iThread), (unsigned int)(sample->iPC) );
fullSamples++;
}
break;
case BTrace::ECpuOptimisedSample:
{
if( ReportLevel>1)
printf("ECpuOptimised %08x %08x\n",
(unsigned int)(sample->iThread), (unsigned int)(sample->iPC) );
optSamples++;
}
break;
case BTrace::ECpuIdfcSample:
{
if( ReportLevel>1)
printf("ECpuIdfc %08x\n", (unsigned int)(sample->iPC) );
dfcSamples++;
}
break;
case BTrace::ECpuNonSymbianThreadSample:
{
if( ReportLevel>1)
printf("ECpuNonSymbianThread\n");
nonSymbSamples++;
}
break;
}//switch
}//for
TReal typePercentage;
printf("\nSamples by type\n");
typePercentage = fullSamples * 100.0 / numSamples;
printf(" Samples of type ECpuFullSample :\t\t%-10d\t%02.2f %%\n", fullSamples, typePercentage );
typePercentage = optSamples * 100.0 / numSamples;
printf(" Samples of type ECpuOptimisedSample :\t\t%-10d\t%02.2f %%\n", optSamples, typePercentage );
typePercentage = dfcSamples * 100.0 / numSamples;
printf(" Samples of type ECpuIdfcSample :\t\t%-10d\t%02.2f %%\n", dfcSamples, typePercentage );
typePercentage = nonSymbSamples * 100.0 / numSamples;
printf(" Samples of type ECpuNonSymbianThreadSample :\t%-10d\t%02.2f %%\n", nonSymbSamples, typePercentage );
printf(" Total Samples : \t\t\t\t%d\n", numSamples );
}//report level
printf("\n");
}
//
// Trace processing
//
TraceRecord** TraceIndex = 0;
TUint TraceIndexSize = 0;
TUint32 NextTraceId = 0;
TraceRecord* LastTrace = 0;
TBool Timestamp2Present = 0;
TBool TraceDumpStarted = false;
void StartTrace()
{
TraceDumpStarted = false;
TraceFormatErrors = 0;
TraceBufferFilled = false;
Timestamp2Present = false;
}
TUint32 ReadTraceWord(const TUint8*& header)
{
TUint32 word;
memcpy(&word, header, sizeof(TUint32));
header += sizeof(TUint32);
return word;
}
TBool PreProcessTrace(TraceRecord& aTrace, const TUint8* aData)
{
ErrorOnThisTrace = false;
aTrace.iError = 0;
aTrace.iDataSize = 0; // initialise to safe value
// process aTrace header...
TUint traceSize = aData[BTrace::ESizeIndex];
if(traceSize<4u || traceSize>(TUint)KMaxBTraceRecordSize)
{
aTrace.iError = 1;
return false; // bad size
}
aTrace.iCpuNum = 0;
TUint8 flags = aData[BTrace::EFlagsIndex];
if(!TraceRecordId) // first trace record...?
flags &= ~BTrace::EMissingRecord; // ignore missing traces before log start
aTrace.iFlags = flags;
TUint8 category = aData[BTrace::ECategoryIndex];
aTrace.iCategory = category;
TUint8 subCategory = aData[BTrace::ESubCategoryIndex];
aTrace.iSubCategory = subCategory;
const TUint8* header = aData+4;
TUint32 header2 = 0;
if(flags&BTrace::EHeader2Present)
{
header2 = ReadTraceWord(header);
aTrace.iCpuNum = (TUint8)(header2>>20);
}
aTrace.iHeader2 = header2;
aTrace.iCpu = TheCpus + aTrace.iCpuNum;
// process timestamp and timestamp2...
TUint32 ts1 = 0;
TUint32 ts2 = 0;
TUint64 timestamp = 0;
if(flags&BTrace::ETimestampPresent)
ts1 = ReadTraceWord(header);
if(flags&BTrace::ETimestamp2Present)
{
Timestamp2Present = true;
ts2 = ReadTraceWord(header);
}
aTrace.iTimestamp2 = ts2;
if(flags&BTrace::ETimestampPresent)
{
if (Timestamp64Bit)
{
timestamp = ts2;
timestamp <<= 32;
timestamp |= ts1;
Timestamp = timestamp;
}
else
{
timestamp = ts1;
if(timestamp<(Timestamp&0xffffffffu))
Timestamp += TUint64(1)<<32;
Timestamp &= TUint64(0xffffffff)<<32;
Timestamp |= timestamp;
timestamp = Timestamp;
}
if(!TraceRecordId)
TimestampBase = timestamp; // record timestamp of first trace
}
aTrace.iTimestamp = timestamp;
// process context...
// coverity[assign_zero]
aTrace.iContextID = 0;
if(flags&BTrace::EContextIdPresent)
{
TUint32 contextId = ReadTraceWord(header);
Thread* thread = Thread::Find(contextId);
if(!thread)
thread = new Thread(contextId);
aTrace.iContextID = thread;
}
// process pc...
TUint32 pc = 0;
if(flags&BTrace::EPcPresent)
pc = ReadTraceWord(header);
aTrace.iPC = pc;
// process extra...
TUint32 extra = 0;
if(flags&BTrace::EExtraPresent)
extra = ReadTraceWord(header);
aTrace.iExtra = extra;
// process payload data...
TUint headerSize = header-aData;
aData = (TUint8*)header;
if(headerSize>traceSize)
{
aTrace.iError = 1;
return false; // bad trace record
}
TUint dataSize = traceSize-headerSize;
if(dataSize>sizeof(aTrace.iData))
{
aTrace.iError = 1;
return false; // bad trace record
}
aTrace.iDataSize = dataSize;
memcpy(&aTrace.iData,aData,dataSize);
// clear pre-processor specific data...
aTrace.iDataTypes[0] = 0;
aTrace.iDataTypes[1] = 0;
aTrace.iDataTypes[2] = 0;
aTrace.iDataTypes[3] = 0;
aTrace.iCalculatedData[0] = 0;
aTrace.iCalculatedData[1] = 0;
// check for missing.
if(flags & BTrace::EMissingRecord)
{// Some trace was missing as the btrace buffer must have been filled.
TraceBufferFilled = true;
aTrace.iError = 1;
return false;
}
// category specific processing...
switch(aTrace.iCategory)
{
case BTrace::ERDebugPrintf:
case BTrace::EKernPrintf:
case BTrace::EPlatsecPrintf:
if((flags&BTrace::EHeader2Present) && (header2&BTrace::EMultipartFlagMask))
aTrace.iDataTypes[2] = EDataTypeText;
else
aTrace.iDataTypes[1] = EDataTypeText;
break;
case BTrace::EThreadIdentification:
PreProcessThreadIdentification(aTrace); break;
case BTrace::ECpuUsage:
PreProcessCpuUsage(aTrace); break;
case BTrace::EChunks:
PreProcessChunks(aTrace); break;
case BTrace::ECodeSegs:
PreProcessCodeSegs(aTrace); break;
case BTrace::EKernelMemory:
PreProcessKernelMemory(aTrace); break;
case BTrace::EMetaTrace:
PreProcessMetaTrace(aTrace); break;
case BTrace::EFastMutex:
PreProcessFastMutex(aTrace); break;
case BTrace::EProfiling:
PreProcessProfiling(aTrace); break;
case BTrace::ESymbianKernelSync:
PreProcessSymbianKernelSync(aTrace); break;
default:
break;
}
// update trace ID...
++TraceRecordId;
if (ErrorOnThisTrace)
aTrace.iError = 1;
return true;
}
void DumpTrace(TraceRecord& aTrace)
{
if(!TraceDumpStarted)
{
// print heading...
if(SMP)
printf("C ");
if(Timestamp2Present)
printf("%10s ","TimeStamp2");
printf("%10s ","Time");
printf("%-8s ","PC");
if(ReportLevel>2)
{
printf("%-60s ","Context");
printf("%18s ","Category");
printf("%24s ","SubCategory");
}
else
{
printf("%-10s ","Context");
printf("%24s ","SubCategory");
}
printf("Data...\n");
TraceDumpStarted = true;
}
if(aTrace.iFlags&BTrace::EMissingRecord)
printf("MISSING TRACE RECORD(S)\n");
// print CPU number
if (SMP)
{
printf("%1d ", aTrace.iCpuNum);
}
// print timestamp...
if(Timestamp2Present)
{
if(aTrace.iFlags&BTrace::ETimestamp2Present)
printf("%10u ",(unsigned int)aTrace.iTimestamp2);
else
printf(" ");
}
if(aTrace.iFlags&BTrace::ETimestampPresent)
printf("%10u ",(unsigned int)Time(aTrace.iTimestamp-TimestampBase));
else
printf(" ");
// print PC...
if(aTrace.iFlags&BTrace::EPcPresent)
printf("%08x ",(unsigned int)aTrace.iPC);
else
printf(" ");
// print context...
if(ReportLevel>2)
{
Object::FullTraceNameBuf fullName;
fullName[0] = 0;
if(aTrace.iFlags&BTrace::EContextIdPresent)
aTrace.iContextID->FullTraceName(fullName);
printf("%-60s ",fullName);
}
else
{
Object::TraceNameBuf traceName;
traceName[0] = 0;
if(aTrace.iFlags&BTrace::EContextIdPresent)
aTrace.iContextID->TraceName(traceName);
printf("%-10s ",traceName);
}
// print trace categories...
const char* catName = CategoryName(aTrace.iCategory);
const char* subCatName = SubCategoryName(aTrace.iCategory,aTrace.iSubCategory);
if(ReportLevel>2)
printf("%18s %-24s ",catName,subCatName);
else
{
if(subCatName[0])
printf("%24s ",subCatName);
else
printf("%24s ",catName);
};
// print trace data contents...
TUint i;
for(i=0; i<aTrace.iDataSize; i+=4)
{
TUint32 data = aTrace.iData[i/sizeof(TUint32)];
if(i<16)
{
// first 4 words of data may have 'type' info set during pre-processing...
switch(aTrace.iDataTypes[i/sizeof(TUint32)])
{
case EDataTypeObject:
{
// data is an object, print "full name"[traceID]...
Object* object = (Object*)data;
if(ReportLevel>2)
{
Object::FullTraceNameBuf name;
object->FullTraceName(name);
printf("%s ",name);
}
else
{
Object::TraceNameBuf name;
object->TraceName(name);
printf("%s ",name);
}
}
continue;
case EDataTypeText:
{
// rest of trace is text...
TUint8* text = (TUint8*)aTrace.iData+i;
TUint8* textEnd = text+(aTrace.iDataSize-i);
TUint8 buffer[256];
TUint x=0;
while(text<textEnd && x<sizeof(buffer)-2)
{
TUint8 c = *text++;
TUint8 escape = 0;
switch(c)
{
case 9: escape = 't'; break;
case 10: escape = 'n'; break;
case 13: escape = 'r'; break;
default:
if(c<' ') c = '?';
break;
}
if(!escape)
buffer[x++] = c;
else
{
buffer[x++] = '\\';
buffer[x++] = escape;
}
}
buffer[x] = 0;
printf("\"%s\" ",buffer);
i = aTrace.iDataSize; // skip to end of data
}
continue;
default:
break;
}
}
// default to print data as hex value...
printf("%08x ",(unsigned int)data);
}
// print any extra data added by pre-processing...
for(i=0; i<2; ++i)
{
if(aTrace.iCalculatedData[i])
printf("{%u} ",(unsigned int)aTrace.iCalculatedData[i]);
}
if (aTrace.iError)
printf(" ***ERROR***");
// end-of-line finally!
printf("\n");
}
void DumpAllTraces()
{
printf("\nREPORT: Trace Dump\n\n");
for(TUint i=0; i<NextTraceId; i++)
DumpTrace(*TraceIndex[i]);
printf("\n");
}
void ReportErrors()
{
TBool errors = TraceFormatErrors || InterruptNestErrors || TraceFormatErrors
|| ChunkErrors || CodeSegErrors || FastMutexNestErrors || KernelMemoryErrors
|| ProfilingSampleErrors;
if(!errors)
return;
printf("\nREPORT: Trace Analysis Errors\n\n");
if(TraceFormatErrors)
printf("\tTrace Format Errors = %d\n",TraceFormatErrors);
if(InterruptNestErrors)
printf("\tInterrupt Nest Errors = %d\n",InterruptNestErrors);
if(FastMutexNestErrors)
printf("\tFast Mutex Nest Errors = %d\n",FastMutexNestErrors);
if(KernelMemoryErrors)
printf("\tKernel Memory Errors = %d\n",KernelMemoryErrors);
if(ChunkErrors)
printf("\tChunk Errors = %d\n",ChunkErrors);
if(CodeSegErrors)
printf("\tCodeSeg Errors = %d\n",CodeSegErrors);
if(ProfilingSampleErrors)
printf("\tProfiling Errors = %d\n",ProfilingSampleErrors);
printf("\n");
}
/**
The last trace record created has been preporcessed.
*/
void DoneTrace()
{
if(LastTrace)
TraceIndex[NextTraceId++] = (TraceRecord*)realloc(LastTrace,sizeof(TraceHeader)+LastTrace->iDataSize);
LastTrace = 0;
}
/**
Create a new trace record.
*/
TraceRecord* NewTrace()
{
if(NextTraceId>=TraceIndexSize)
{
TraceIndexSize += 1024;
TraceIndex = (TraceRecord**)realloc(TraceIndex,TraceIndexSize*sizeof(TraceIndex[0]));
ASSERT(TraceIndex);
}
DoneTrace();
LastTrace = (TraceRecord*)malloc(sizeof(TraceRecord));
return LastTrace;
}
/**
Delete all processed traces records.
*/
void ResetTrace()
{
DoneTrace();
TUint i;
for(i=0; i<NextTraceId; ++i)
free(TraceIndex[i]);
free(TraceIndex);
TraceIndex = 0;
TraceIndexSize = 0;
LastTrace = 0;
NextTraceId = 0;
TraceRecordId = 0;
}
void EndTrace()
{
DoneTrace();
}
/**
Process an entire BTrace log capture.
@param aInput Pointer to function which will supply raw trace data.
Function should place up to aMaxSize bytes of data at aBuffer and return
the number of bytes stored. Return zero to indicate end of data.
@param aReportLevel Level of detail required of trace alaysis.
0 = brief summary, 1 = full summary, 2 = condensed trace dump, 3 = full trace dump.
*/
void ProcessAllTrace(TUint (*aInput)(TAny* aBuffer, TUint aMaxSize),TInt aReportLevel)
{
ReportLevel = aReportLevel;
// __UHEAP_MARK;
printf("Btrace Analysis:\n");
printf("\nTHIS TOOL IS UNOFFICIAL, UNSUPPORTED AND SUBJECT TO CHANGE WITHOUT NOTICE!\n");
StartTrace();
StartCpuUsage();
StartChunks();
StartCodeSegs();
StartFastMutex();
StartKernelMemory();
StartMetaTrace();
StartProfilingSample();
for(; !TraceBufferFilled ;)
{
// read more data...
TUint size = (*aInput)(TraceBuffer+TraceBufferSize, sizeof(TraceBuffer)-TraceBufferSize);
if(!size)
break;
TraceBufferSize += size;
// process all the complete traces in buffer...
const TUint8* data = TraceBuffer;
TUint sizeRemaining = TraceBufferSize;
while(sizeRemaining>BTrace::ESizeIndex)
{
TUint traceSize = (data[BTrace::ESizeIndex]+3)&~3;
if(traceSize>sizeRemaining)
break;
TraceRecord* trace = NewTrace();
ASSERT(trace);
if(!PreProcessTrace(*trace,data))
{
if (!TraceBufferFilled)
{
// bad trace, create dummy 1 byte trace record...
memset(trace,0,sizeof(*trace));
trace->iCategory = BTrace::EMetaTrace;
trace->iSubCategory = KJunkTraceSubcategory;
trace->iDataSize = 4;
trace->iData[0] = *data;
++TraceFormatErrors;
ErrorOnThisTrace = true;
traceSize = 1;
}
else // The buffer was filled so ignore the rest of the data
break;
}
data += traceSize;
sizeRemaining -= traceSize;
}
if (!TraceBufferFilled)
{
memcpy(TraceBuffer,data,sizeRemaining);
TraceBufferSize = sizeRemaining;
}
else
{
// The trace buffer was filled so ignore the rest of the data
// and just read whatever is left to flush it from the btrace buffer.
while ((*aInput)(TraceBuffer, sizeof(TraceBuffer))){};
TraceBufferSize = 0; // reset here so a format error isn't reported
}
if(aReportLevel<2)
ResetTrace(); // free up memory as we go along
}
EndTrace();
EndCpuUsage();
if(TraceBufferSize)
{
++TraceFormatErrors;
ErrorOnThisTrace = true;
}
ReportTimeUnits();
ReportErrors();
if(aReportLevel>=2)
DumpAllTraces();
if(ReportLevel>=1 || CpuUsagePresent)
{
ReportProcesses();
ReportThreads();
}
ReportChunks();
ReportKernelMemory();
ReportCodeSegs();
ReportFastMutex();
ReportSampleProfiling();
ResetTrace();
ObjectContainer::Reset();
// __UHEAP_MARKEND;
}