kerneltest/e32utils/trace/btrace_analyse.cpp
changeset 0 a41df078684a
child 90 947f0dc9f7a8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32utils/trace/btrace_analyse.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,3110 @@
+// 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;
+	}
+
+