kernel/eka/drivers/trace/btracex.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2005-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:
// e32\drivers\trace\btrace.cpp
// 
//

#include <kernel/kern_priv.h>
#include "platform.h"
#include "drivers/btrace.h"

#if defined(__EPOC32__) && defined(__CPU_X86)
#include <x86.h>
#endif

TBTraceBufferK Buffer;

TBool ChannelOpen = EFalse;

const TUint KCopyBufferMaxSize = 0x10000;

TInt TBTraceBufferK::Create(TInt aSize)
	{
	if(aSize<=0)
		return KErrArgument;
	TUint pageSize = Kern::RoundToPageSize(1);
	aSize = (aSize+pageSize-1)&-(TInt)pageSize;

	TUint recordOffsets = aSize+pageSize;
	TUint recordOffsetsSize = Kern::RoundToPageSize(aSize>>2);
	TUint copyBuffer = recordOffsets+recordOffsetsSize+pageSize;
	TUint copyBufferSize = Kern::RoundToPageSize(aSize>>2);
	if(copyBufferSize>KCopyBufferMaxSize)
		copyBufferSize = KCopyBufferMaxSize;
	TUint chunkSize = copyBuffer+copyBufferSize+pageSize;

	// Create chunk...
    TChunkCreateInfo info;
    info.iType         = TChunkCreateInfo::ESharedKernelSingle;
    info.iMaxSize      = chunkSize;
#ifdef __EPOC32__
	// we want full caching, no execute, default sharing
	new (&info.iMapAttr) TMappingAttributes2(EMemAttNormalCached, EFalse, ETrue);
#endif
    info.iOwnsMemory   = ETrue; // Use memory from system's free pool
    info.iDestroyedDfc = NULL;
	TUint32 mapAttr;
	TInt r = Kern::ChunkCreate(info, iBufferChunk, iAddress, mapAttr);
	if(r==KErrNone)
		r = Kern::ChunkCommit(iBufferChunk, 0, aSize);
	if(r==KErrNone)
		r = Kern::ChunkCommit(iBufferChunk, recordOffsets, recordOffsetsSize);
	if(r==KErrNone)
		r = Kern::ChunkCommit(iBufferChunk, copyBuffer, copyBufferSize);

	// Check errors...
	if(r!=KErrNone)
		{
		Close();
		return r;
		}

	// Initialise state...
	iStart = sizeof(TBTraceBuffer);
	iEnd = aSize;
	iRecordOffsets = (TUint8*)(iAddress+recordOffsets);

	TBTraceBuffer* userBuffer = (TBTraceBuffer*)iAddress;
	userBuffer->iRecordOffsets = recordOffsets;
	userBuffer->iCopyBuffer = copyBuffer;
	userBuffer->iCopyBufferSize = copyBufferSize;

	Reset(0);

#ifndef __SMP__
	TInt irq = NKern::DisableAllInterrupts();
#endif
	iTimestamp2Enabled = EFalse;
	BTrace::SetHandlers(TBTraceBufferK::Trace,TBTraceBufferK::ControlFunction,iOldBTraceHandler,iOldBTraceControl);
#ifndef __SMP__
	NKern::RestoreInterrupts(irq);
#endif

	return KErrNone;
	}


void TBTraceBufferK::Close()
	{
#ifdef __SMP__
	if(iOldBTraceHandler)
		{
		BTrace::THandler handler;
		BTrace::TControlFunction control;
		BTrace::SetHandlers(iOldBTraceHandler,iOldBTraceControl,handler,control);
		iOldBTraceHandler = NULL;
		iOldBTraceControl = NULL;
		}
	TSpinLock* sl = BTrace::LockPtr();
	TInt irq = sl->LockIrqSave();	// guarantees handler can't run at the same time
	DChunk* chunk = iBufferChunk;
	iBufferChunk = NULL;
	iAddress = NULL;
	sl->UnlockIrqRestore(irq);
#else
	TInt irq = NKern::DisableAllInterrupts();
	if(iOldBTraceHandler)
		{
		BTrace::THandler handler;
		BTrace::TControlFunction control;
		BTrace::SetHandlers(iOldBTraceHandler,iOldBTraceControl,handler,control);
		iOldBTraceHandler = NULL;
		iOldBTraceControl = NULL;
		}
	DChunk* chunk = iBufferChunk;
	iBufferChunk = NULL;
	iAddress = NULL;
	NKern::RestoreInterrupts(irq);
#endif
	if(chunk)
		Kern::ChunkClose(chunk);
	}




/**
Helper functions for encoding pseudo- floating point values recoverable by:

	int exponent = (signed char)(encoded_val >> 24);
	int mantissa = encoded_val & 0xFFFFFF;
	double val = mantissa * pow(2, exponent);
*/
TUint EncodeFloatesque(TUint64 val64, TInt exponent)
	{
	// Lose precision until it fits in 24 bits
	TInt round_up = 0;
	while (val64>=0x1000000) 
		{
		round_up = (TInt)(val64&1);
		val64 >>= 1;
		exponent++;
		}
	if (round_up) 
		{
		val64++;
		if (val64>=0x1000000) 
			{
			val64 >>= 1;
			exponent++;
			}
		}

	// Return 8-bit exponent and 24-bit mantissa
	return (TUint)(val64 | (((unsigned char)exponent)<<24));
	}

TUint EncodeReciprocal(TUint val)
	{
	if (val==0) return val;

	// Get reciprocal * 2^64
	TUint64 val64 = val;
	TUint64 div = 0;
	div--;
	val64 = div / val64;
	
	return EncodeFloatesque(val64, -64);
	}

TUint EncodePostDiv(TUint val, TUint divisor)
	{
	TUint64 val64 = val;
	val64 <<= 32;
	val64 = val64 / divisor;
	return EncodeFloatesque(val64, -32);
	}

void BTracePrimeMetatrace()
	{
#ifdef __SMP__
	TUint period1 = EncodeReciprocal(NKern::TimestampFrequency());
	TUint period2 = period1 + (32u<<24);	// timestamp2 period is 2^32 * timestamp1 period
	BTrace12(BTrace::EMetaTrace, BTrace::EMetaTraceTimestampsInfo, period1, period2, 1);
#else
	TUint period1 = EncodeReciprocal(NKern::FastCounterFrequency());
	TUint period2 = EncodePostDiv(NKern::TickPeriod(), 1000000);
	BTrace12(BTrace::EMetaTrace, BTrace::EMetaTraceTimestampsInfo, period1, period2, 0);
#endif
	}

void TBTraceBufferK::Reset(TUint aMode)
	{
#ifdef __SMP__
	TSpinLock* sl = BTrace::LockPtr();
#endif
	TInt irq = __SPIN_LOCK_IRQSAVE(*sl);	// guarantees handler can't run at the same time
	iHead = iStart;
	TBTraceBuffer* userBuffer = (TBTraceBuffer*)iAddress;
	userBuffer->iStart = iStart;
	userBuffer->iEnd = iEnd;
	userBuffer->iHead = iHead;
	userBuffer->iTail = iHead;
	userBuffer->iGeneration = 0;
	userBuffer->iMode = aMode;
	__SPIN_UNLOCK_IRQRESTORE(*sl,irq);
	if(aMode)
		{
		if (BTrace::CheckFilter(BTrace::EMetaTrace))
			BTracePrimeMetatrace();
		BTrace::Prime();
		}
	}


TInt TBTraceBufferK::RequestData(TInt aSize, TDfc* aDfc)
	{
	if(aSize<=0)
		aSize = 1;
#ifdef __SMP__
	TSpinLock* sl = BTrace::LockPtr();
#endif
	TInt irq = __SPIN_LOCK_IRQSAVE(*sl);	// guarantees handler can't run
	TBTraceBuffer* userBuffer = (TBTraceBuffer*)iAddress;
	if(!userBuffer)
		{
		__SPIN_UNLOCK_IRQRESTORE(*sl,irq);
		return KErrNotReady;
		}
	TInt dif = userBuffer->iTail-iHead;
	if(dif>0)
		aSize = 0; // we need no more bytes because all bytes to end of buffer are available
	else
		aSize += dif; // number of bytes extra we need
	if(aSize>0)
		{
		iRequestDataSize = aSize;
		iWaitingDfc = aDfc;
		}
	__SPIN_UNLOCK_IRQRESTORE(*sl,irq);
	if(aSize<=0)
		return KErrCompletion;
	return KErrNone;
	}


#ifndef BTRACE_DRIVER_MACHINE_CODED

TBool TBTraceBufferK::Trace_Impl(TUint32 aHeader,TUint32 aHeader2,const TUint32 aContext,const TUint32 a1,const TUint32 a2,const TUint32 a3,const TUint32 aExtra, const TUint32 aPc, TBool aIncTimestamp2)
	{
#ifndef __SMP__
	TInt irq = NKern::DisableAllInterrupts();
#endif


#ifdef __SMP__
	// Header 2 always present and contains CPU number
	// If Header2 not originally there, add 4 to size
	if (!(aHeader&(BTrace::EHeader2Present<<BTrace::EFlagsIndex*8)))
		aHeader += (4<<BTrace::ESizeIndex*8) + (BTrace::EHeader2Present<<BTrace::EFlagsIndex*8), aHeader2=0;
	aHeader2 = (aHeader2 &~ BTrace::ECpuIdMask) | (NKern::CurrentCpu()<<20);
#endif
#ifdef BTRACE_INCLUDE_TIMESTAMPS
	// Add timestamp to trace...
#if defined(__SMP__)
	aHeader += 8<<BTrace::ESizeIndex*8;
	aHeader |= BTrace::ETimestampPresent<<BTrace::EFlagsIndex*8 | BTrace::ETimestamp2Present<<BTrace::EFlagsIndex*8;
	TUint64 timeStamp = NKern::Timestamp();
#elif defined(__EPOC32__) && defined(__CPU_X86)
	aHeader += 8<<BTrace::ESizeIndex*8;
	aHeader |= BTrace::ETimestampPresent<<BTrace::EFlagsIndex*8 | BTrace::ETimestamp2Present<<BTrace::EFlagsIndex*8;
	TUint64 timeStamp = X86::Timestamp();
#else
	TUint32 timeStamp = NKern::FastCounter();
	TUint32 timeStamp2 = 0;
	if (aIncTimestamp2)
		{
		timeStamp2 = NKern::TickCount();
		aHeader += 8<<BTrace::ESizeIndex*8;
		aHeader |= (BTrace::ETimestampPresent | BTrace::ETimestamp2Present) << BTrace::EFlagsIndex*8;
		}
	else
		{
		aHeader += 4<<BTrace::ESizeIndex*8;
		aHeader |= BTrace::ETimestampPresent<<BTrace::EFlagsIndex*8;
		}
#endif
#endif
	TUint size = (aHeader+3)&0xfc;

	TBTraceBufferK& buffer = Buffer;
	TLinAddr address = buffer.iAddress;
	TBTraceBuffer& user_buffer = *(TBTraceBuffer*)address;
	++user_buffer.iGeneration;	// atomic not required since only driver modifies iGeneration
#ifdef __SMP__
	__e32_memory_barrier();
#endif
	TUint start = buffer.iStart;
	TUint end = buffer.iEnd;
	TUint orig_head = buffer.iHead;
	TInt requestDataSize = buffer.iRequestDataSize;
	TUint8* recordOffsets = buffer.iRecordOffsets;
	TUint32 orig_tail = user_buffer.iTail;
	TUint32 newHead, head, tail;

	if(!(user_buffer.iMode&RBTrace::EEnable))
		goto trace_off;

retry:
	head = orig_head;
	tail = orig_tail &~ 1;
	newHead = head+size;
	if(newHead>end)
		{
		requestDataSize = 0; 
		newHead = start+size;
		if(head<tail || tail<newHead+1)
			{
			if(!(user_buffer.iMode&RBTrace::EFreeRunning))
				goto trace_dropped;
			user_buffer.iWrap = head;
			head = start;
			tail = newHead+(recordOffsets[newHead>>2]<<2);
			goto overwrite;
			}
		user_buffer.iWrap = head;
		head = start;
		}
	else if(head<tail && tail<=newHead)
		{
		{
		requestDataSize = 0; 
		TUint wrap = user_buffer.iWrap;
		if(!(user_buffer.iMode&RBTrace::EFreeRunning))
			goto trace_dropped;
		if(newHead<end && newHead<wrap)
			{
			tail = newHead+(recordOffsets[newHead>>2]<<2);
			if(tail>=end || tail>=wrap)
				tail = start;
			}
		else
			tail = start;
		}
overwrite:
		*(TUint32*)(address+tail) |= BTrace::EMissingRecord<<(BTrace::EFlagsIndex*8);
		if (!__e32_atomic_cas_ord32(&user_buffer.iTail, &orig_tail, tail|1))
			goto retry;	// go round again if user side has already updated the tail pointer
		}

	buffer.iRequestDataSize = requestDataSize-size;

	{
	recordOffsets += head>>2;
	TUint32* src;
	TUint32* dst = (TUint32*)((TUint)address+head);
	size >>= 2; // we are now counting words, not bytes

	// store first word of trace...
	TUint w = aHeader;
	if(buffer.iDropped)
		{
		buffer.iDropped = 0;
		w |= BTrace::EMissingRecord<<(BTrace::EFlagsIndex*8); 
		}
	*recordOffsets++ = (TUint8)size;
	--size;
	*dst++ = w;

#ifndef __SMP__
	if(aHeader&(BTrace::EHeader2Present<<(BTrace::EFlagsIndex*8)))
#endif
		{
		w = aHeader2;
		*recordOffsets++ = (TUint8)size;
		--size;
		*dst++ = w;
		}

#ifdef BTRACE_INCLUDE_TIMESTAMPS
	// store timestamp...
#if defined(__SMP__) || (defined(__EPOC32__) && defined(__CPU_X86))
	*recordOffsets++ = (TUint8)size;
	--size;
	*dst++ = TUint32(timeStamp);
	*recordOffsets++ = (TUint8)size;
	--size;
	*dst++ = TUint32(timeStamp>>32);
#else
	*recordOffsets++ = (TUint8)size;
	--size;
	*dst++ = timeStamp;
	if (aIncTimestamp2)
		{
		*recordOffsets++ = (TUint8)size;
		--size;
		*dst++ = timeStamp2;
		}
#endif
#endif

	if(aHeader&(BTrace::EContextIdPresent<<(BTrace::EFlagsIndex*8)))
		{
		w = aContext;
		*recordOffsets++ = (TUint8)size;
		--size;
		*dst++ = w;
		}

	if(aHeader&(BTrace::EPcPresent<<(BTrace::EFlagsIndex*8)))
		{
		w = aPc;
		*recordOffsets++ = (TUint8)size;
		--size;
		*dst++ = w;
		}

	if(aHeader&(BTrace::EExtraPresent<<(BTrace::EFlagsIndex*8)))
		{
		w = aExtra;
		*recordOffsets++ = (TUint8)size;
		--size;
		*dst++ = w;
		}

	// store remainding words of trace...
	if(size)
		{
		w = a1;
		*recordOffsets++ = (TUint8)size;
		--size;
		*dst++ = w;
		if(size)
			{
			w = a2;
			*recordOffsets++ = (TUint8)size;
			--size;
			*dst++ = w;
			if(size)
				{
				if(size==1)
					{
					w = a3;
					*recordOffsets++ = (TUint8)size;
					*dst++ = w;
					}
				else
					{
					src = (TUint32*)a3;
					do
						{
						w = *src++;
						*recordOffsets++ = (TUint8)size;
						--size;
						*dst++ = w;
						}
					while(size);
					}
				}
			}
		}
	}
	buffer.iHead = newHead;
#ifdef __SMP__
	__e32_memory_barrier();	// make sure written data is observed before head pointer update
#endif
	user_buffer.iHead = newHead;

	{
	TDfc* dfc = (TDfc*)buffer.iWaitingDfc;
	if(dfc && buffer.iRequestDataSize<=0)
		{
		buffer.iWaitingDfc = NULL;
		dfc->RawAdd();
		}
	}

#ifdef __SMP__
	__e32_memory_barrier();
#endif
	++user_buffer.iGeneration;	// atomic not required since only driver modifies iGeneration
#ifndef __SMP__
	NKern::RestoreInterrupts(irq);
#endif
	return ETrue;


trace_dropped:
	buffer.iRequestDataSize = 0; 
	buffer.iDropped = ETrue;
#ifdef __SMP__
	__e32_memory_barrier();
#endif
	++user_buffer.iGeneration;	// atomic not required since only driver modifies iGeneration
#ifndef __SMP__
	NKern::RestoreInterrupts(irq);
#endif
	return ETrue;

trace_off:
#ifdef __SMP__
	__e32_memory_barrier();
#endif
	++user_buffer.iGeneration;	// atomic not required since only driver modifies iGeneration
#ifndef __SMP__
	NKern::RestoreInterrupts(irq);
#endif
	return EFalse;
	}

TBool TBTraceBufferK::TraceWithTimestamp2(TUint32 aHeader,TUint32 aHeader2,const TUint32 aContext,const TUint32 a1,const TUint32 a2,const TUint32 a3,const TUint32 aExtra,const TUint32 aPc)
	{
	return Trace_Impl(aHeader, aHeader2, aContext, a1, a2, a3, aExtra, aPc, ETrue);
	}

TBool TBTraceBufferK::Trace(TUint32 aHeader,TUint32 aHeader2,const TUint32 aContext,const TUint32 a1,const TUint32 a2,const TUint32 a3,const TUint32 aExtra,const TUint32 aPc)
	{
	return Trace_Impl(aHeader, aHeader2, aContext, a1, a2, a3, aExtra, aPc, EFalse);
	}

#endif // BTRACE_DRIVER_MACHINE_CODED


TInt TBTraceBufferK::ControlFunction(BTrace::TControl aFunction, TAny* aArg1, TAny* aArg2)
	{
	switch(aFunction)
		{
	case BTrace::ECtrlSystemCrashed:
		if(Buffer.iAddress)
			((TBTraceBuffer*)Buffer.iAddress)->iMode = 0; // turn off trace
		return KErrNone;
		
	case BTrace::ECtrlCrashReadFirst:
		Buffer.iCrashReadPart = 0;
		// fall through...
	case BTrace::ECtrlCrashReadNext:
		Buffer.CrashRead(*(TUint8**)aArg1,*(TUint*)aArg2);
		++Buffer.iCrashReadPart;
		return KErrNone;
				
	default:
		return KErrNotSupported;
		}
	}


void TBTraceBufferK::CrashRead(TUint8*& aData, TUint& aSize)
	{
	// start by assuming no data...
	aData = 0;
	aSize = 0;
	
	TBTraceBuffer* userBuffer = (TBTraceBuffer*)iAddress;
	if(!userBuffer)
		return; // no trace buffer, so end...

	TUint head = iHead;
	TUint tail = userBuffer->iTail;
	TUint8* data = (TUint8*)userBuffer;
	
	if(head>tail)
		{
		// data is in one part...
		if(iCrashReadPart==0)
			{
			aData = data+tail;
			aSize = head-tail;
			}
		// else no more parts
		}
	else if(head<tail)
		{
		// data is in two parts...
		if(iCrashReadPart==0)
			{
			// first part...
			aData = data+tail;
			aSize = userBuffer->iWrap-tail;
			}
		else if(iCrashReadPart==1)
			{
			// second part...
			aData = data+iStart;
			aSize = head-iStart;
			}
		// else no more parts
		}
	}


//
// LDD
//

class DBTraceFactory : public DLogicalDevice
	{
public:
	virtual TInt Install();
	virtual void GetCaps(TDes8& aDes) const;
	virtual TInt Create(DLogicalChannelBase*& aChannel);
	};


class DBTraceChannel : public DLogicalChannelBase
	{
public:
	DBTraceChannel();
	virtual ~DBTraceChannel();
	//	Inherited from DObject
	virtual TInt RequestUserHandle(DThread* aThread, TOwnerType aType);
	// Inherited from DLogicalChannelBase
	virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
	virtual TInt Request(TInt aReqNo, TAny* a1, TAny* a2);
	//
	static void WaitCallback(TAny* aSelf);
private:
	DThread* iClient;
	TClientRequest*	iWaitRequest;
	TDfc iWaitDfc;
	TBool iOpened;
	TInt iFilter2Count;
	TUint32* iFilter2;
	TUint32* iFilter2Set;
	TBool iTimestamp2Enabled;
	};


//
// DBTraceFactory
//

TInt DBTraceFactory::Install()
	{
	return SetName(&RBTrace::Name());
	}

void DBTraceFactory::GetCaps(TDes8& aDes) const
	{
	Kern::InfoCopy(aDes,0,0);
	}

TInt DBTraceFactory::Create(DLogicalChannelBase*& aChannel)
	{
	aChannel=new DBTraceChannel();
	if(!aChannel)
		return KErrNoMemory;
	return KErrNone;
	}

void syncDfcFn(TAny* aPtr)
	{
	NKern::FSSignal((NFastSemaphore*)aPtr);
	}

void Sync(TDfcQue* aDfcQ)
	{
	NFastSemaphore s(0);
	TDfc dfc(&syncDfcFn, &s, aDfcQ, 0);
	dfc.Enque();
	NKern::FSWait(&s);
	}

//
// DBTraceChannel
//

DBTraceChannel::DBTraceChannel()
	: iWaitDfc(WaitCallback,this,Kern::DfcQue1(),7)
	{
	}

DBTraceChannel::~DBTraceChannel()
	{
	delete iFilter2Set;
	Buffer.iWaitingDfc = NULL;
	iWaitDfc.Cancel();
	Sync(Kern::DfcQue1());
	if (iWaitRequest)
		{
		Kern::QueueRequestComplete(iClient, iWaitRequest, KErrCancel);	// does nothing if request not pending
		Kern::DestroyClientRequest(iWaitRequest);
		}
	if (iOpened)
		__e32_atomic_swp_ord32(&ChannelOpen, 0);
	}

TInt DBTraceChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/)
	{
//	_LIT_SECURITY_POLICY_C2(KSecurityPolicy,ECapabilityReadDeviceData,ECapabilityWriteDeviceData);
//	if(!KSecurityPolicy().CheckPolicy(&Kern::CurrentThread(),__PLATSEC_DIAGNOSTIC_STRING("Checked by BTRACE")))
//		return KErrPermissionDenied;
	iClient = &Kern::CurrentThread();
	TInt r = Kern::CreateClientRequest(iWaitRequest);
	if (r!=KErrNone)
		return r;
	if (__e32_atomic_swp_ord32(&ChannelOpen, 1))
		return KErrInUse;
	iOpened = ETrue;
	return KErrNone;
	}


TInt DBTraceChannel::RequestUserHandle(DThread* aThread, TOwnerType aType)
	{
	if (aType!=EOwnerThread || aThread!=iClient)
		return KErrAccessDenied;
	return KErrNone;
	}

void DBTraceChannel::WaitCallback(TAny* aSelf)
	{
	DBTraceChannel& c = *(DBTraceChannel*)aSelf;
	Kern::QueueRequestComplete(c.iClient, c.iWaitRequest, KErrNone);
	}

TInt DBTraceChannel::Request(TInt aReqNo, TAny* a1, TAny* a2)
	{
	TInt r;
	TBTraceBufferK& buffer = Buffer;

	switch(aReqNo)
		{
	case RBTrace::EOpenBuffer:
		NKern::ThreadEnterCS();
		if(!Buffer.iBufferChunk)
			r = buffer.Create(0x100000);
		else
			r = KErrNone;
		if(r==KErrNone)
			r = Kern::MakeHandleAndOpen(NULL, buffer.iBufferChunk);
		NKern::ThreadLeaveCS();
		return r;

	case RBTrace::EResizeBuffer:
		NKern::ThreadEnterCS();
		buffer.Close();
		r = buffer.Create((TInt)a1);
		NKern::ThreadLeaveCS();
		return r;

	case RBTrace::ESetFilter:
		{
		TInt old = BTrace::SetFilter((BTrace::TCategory)(TInt)a1,(TInt)a2);
		if((TInt)a2==1 && old==0) // filter turned on?
			{
			if ((TInt)a1==BTrace::EMetaTrace) 
				BTracePrimeMetatrace();
			BTrace::Prime((TInt)a1); // prime this trace category
			}
		return old;
		}

	case RBTrace::ESetFilter2:
		return BTrace::SetFilter2((TUint32)a1,(TBool)a2);

	case RBTrace::ESetFilter2Array:
		{
		NKern::ThreadEnterCS();
		delete iFilter2Set;
		TInt size = (TInt)a2*sizeof(TUint32);
		TUint32* buffer = (TUint32*)Kern::Alloc(size);
		iFilter2Set = buffer;
		NKern::ThreadLeaveCS();
		if(!buffer)
			return KErrNoMemory;
		kumemget32(buffer,a1,size);
		r = BTrace::SetFilter2(buffer,(TInt)a2);
		NKern::ThreadEnterCS();
		delete iFilter2Set;
		iFilter2Set = 0;
		NKern::ThreadLeaveCS();
		return r;
		}

	case RBTrace::ESetFilter2Global:
		BTrace::SetFilter2((TBool)a1);
		return KErrNone;

	case RBTrace::EGetFilter2Part1:
		{
		NKern::ThreadEnterCS();
		delete iFilter2;
		iFilter2 = 0;
		iFilter2Count = 0;
		TInt globalFilter = 0;
		iFilter2Count = BTrace::Filter2(iFilter2,globalFilter);
		NKern::ThreadLeaveCS();
		kumemput32(a2,&globalFilter,sizeof(TBool));
		return iFilter2Count;
		}

	case RBTrace::EGetFilter2Part2:
		if((TInt)a2!=iFilter2Count)
			return KErrArgument;
		if(iFilter2Count>0)
			kumemput32(a1,iFilter2,iFilter2Count*sizeof(TUint32));
		NKern::ThreadEnterCS();
		delete iFilter2;
		iFilter2 = 0;
		iFilter2Count = 0;
		NKern::ThreadLeaveCS();
		return KErrNone;

	case RBTrace::ERequestData:
		if (iWaitRequest->SetStatus((TRequestStatus*)a1) != KErrNone)
			Kern::PanicCurrentThread(RBTrace::Name(),RBTrace::ERequestAlreadyPending);
		r = buffer.RequestData((TInt)a2,&iWaitDfc);
		if (r!=KErrNone)
			{
			iWaitRequest->Reset();
			TRequestStatus* s = (TRequestStatus*)a1;
			if (r==KErrCompletion)
				r = KErrNone;
			Kern::RequestComplete(s, r);
			}
		return r;

	case RBTrace::ECancelRequestData:
		buffer.iWaitingDfc = NULL;
		iWaitDfc.Cancel();
		Kern::QueueRequestComplete(iClient, iWaitRequest, KErrCancel);
		return KErrNone;

	case RBTrace::ESetSerialPortOutput:
		{
		TUint mode = Kern::ESerialOutNever+(TUint)a1;
		mode = Kern::SetTextTraceMode(mode,Kern::ESerialOutMask);
		mode &= Kern::ESerialOutMask;
		return mode-Kern::ESerialOutNever;
		}

	case RBTrace::ESetTimestamp2Enabled:
		{
		TBool old = iTimestamp2Enabled;
		iTimestamp2Enabled = (TBool)a1;
		BTrace::TControlFunction oldControl;
		BTrace::THandler oldHandler;
		BTrace::THandler handler = iTimestamp2Enabled ? TBTraceBufferK::TraceWithTimestamp2 : TBTraceBufferK::Trace;
		BTrace::SetHandlers(handler,TBTraceBufferK::ControlFunction,oldHandler,oldControl);
		return old;
		}

	default:
		break;
		}
	return KErrNotSupported;
	}


DECLARE_EXTENSION_LDD()
	{
	return new DBTraceFactory;
	}

#ifdef __WINS__
DECLARE_STANDARD_EXTENSION()
#else
DECLARE_EXTENSION_WITH_PRIORITY(KExtensionMaximumPriority)
#endif
	{
	TSuperPage& superPage = Kern::SuperPage();
	TInt bufferSize = superPage.iInitialBTraceBuffer;
	if(!bufferSize)
		bufferSize = 0x10000;
	TInt r=Buffer.Create(bufferSize);
	if(r==KErrNone)
		Buffer.Reset(superPage.iInitialBTraceMode);
	return r;
	}