// btrace.cpp
// 
// Copyright (c) 2007 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//

#include "btrace.h"

#if defined(FSHELL_MEMORY_ACCESS_SUPPORT)
#define TEXT_TRACE_MODE_SUPPORTED
#endif

const TInt KMaxCategories = 256;
const TUint KPrimingStopped = 0x102864BC;



CCommandBase* CCmdBtrace::NewLC()
	{
	CCmdBtrace* self = new(ELeave) CCmdBtrace();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

CCmdBtrace::~CCmdBtrace()
	{
	iPrimaryFilters.Close();
	iSecondaryFilters.Close();
	iBtrace.Close();
	}

CCmdBtrace::CCmdBtrace()
	: iThreshold(50)
	{
	}

void CCmdBtrace::ConstructL()
	{
	BaseConstructL();
	}

const TDesC& CCmdBtrace::Name() const
	{
#ifdef BUILDING_ATRACE
	_LIT(KName, "atrace");
#else
	_LIT(KName, "btrace");
#endif
	return KName;
	}

#define CASE_LIT(x) case BTrace:: x : { _LIT(KName, #x); return &KName; }
#define CASE_REALLY_LIT(x) case x : { _LIT(KName, #x); return &KName; }

const TDesC* BtraceCategoryName(TInt aCategory)
	{
	switch (aCategory)
		{
		CASE_LIT(ERDebugPrintf)
		CASE_LIT(EKernPrintf)
		CASE_LIT(EPlatsecPrintf)
		CASE_LIT(EThreadIdentification)
		CASE_LIT(ECpuUsage)
		CASE_LIT(EKernPerfLog)
		CASE_LIT(EClientServer)
		CASE_LIT(ERequests)
		CASE_LIT(EChunks)
		CASE_LIT(ECodeSegs)
		CASE_LIT(EPaging)
		CASE_LIT(EThreadPriority)
		CASE_LIT(EPagingMedia)
		CASE_LIT(EKernelMemory)
		CASE_LIT(EHeap)
		CASE_LIT(EMetaTrace)
		CASE_LIT(ERamAllocator)
		CASE_LIT(EFastMutex)
		CASE_LIT(EProfiling)
		CASE_REALLY_LIT(ExtraBTrace::EPubSub)
		CASE_REALLY_LIT(ExtraBTrace::EFfsTrace)
		CASE_LIT(EPlatformSpecificFirst)
		CASE_LIT(EPlatformSpecificLast)
		CASE_LIT(ESymbianExtentionsFirst)
		CASE_LIT(ESymbianExtentionsLast)
		CASE_LIT(ETest1)
		CASE_LIT(ETest2)
		default:
			{
			_LIT(KUnknown, "?");
			return &KUnknown;
			}
		}
	}

void CCmdBtrace::DoRunL()
	{
#ifdef TEXT_TRACE_MODE_SUPPORTED
	if (iOptions.IsPresent(&iTextTraceMode))
		{
		LoadMemoryAccessL();
		const TInt ESerialOutMask = 3; // As per kernel.h
		TInt err = iMemAccess.SetTextTraceMode(iTextTraceMode, ESerialOutMask);
		LeaveIfErr(err, _L("Couldn't set text trace mode"));
		}
#endif

	TInt i;
	LeaveIfErr(iBtrace.Open(), _L("Couldn't open RBTrace"));

	if (iOptions.IsPresent(&iReset))
		{
		ResetL();
		}

	if (iOptions.IsPresent(&iBufferSize))
		{
		iBtrace.ResizeBuffer(iBufferSize * 1024);
		}

	if (iOptions.IsPresent(&iMode))
		{
		SetMode(iMode);
		}

	iBtrace.SetTimestamp2Enabled(iEnableTimestamp2);

	const TInt numPrimaryFilters = iPrimaryFilters.Count();
	if (numPrimaryFilters > 0)
		{
		RemovePrimaryFilters();
		EmptyBuffer();

		// Enable the specified categories.
		for (i = 0; i < numPrimaryFilters; ++i)
			{
			iBtrace.SetFilter(iPrimaryFilters[i], 1);
			}
		}
	if (iTest)
		{
		TestL();
		return;
		}

	const TInt numSecondaryFilters = iSecondaryFilters.Count();
	if (numSecondaryFilters > 0)
		{
		TUint32* filterArray = new(ELeave) TUint32[iSecondaryFilters.Count()];
		for (i = 0; i < numSecondaryFilters; ++i)
			{
			filterArray[i] = iSecondaryFilters[i];
			}

		TInt err = iBtrace.SetFilter2(filterArray, numSecondaryFilters);
		if (err)
			{
			PrintError(err, _L("Couldn't set secondary filter"));
			}
		else
			{
			// Discard old data.
			EmptyBuffer();
			}

		delete[] filterArray;
		}

	if (numPrimaryFilters > 0 || numSecondaryFilters > 0) 
		{
		BTrace8(BTrace::EMetaTrace, BTrace::EMetaTraceMeasurementEnd, KPrimingStopped, KPrimingStopped);
		}

	if (iFileName.Length())
		{
		RFile file;
		User::LeaveIfError(file.Replace(FsL(), iFileName, EFileWrite));
		file.Close();
		}

	if (iFileName.Length() || iDumpToRdebug)
		{
		if (iFollow)
			{
			if (iThreshold > 100)
				{
				PrintError(KErrArgument, _L("Invalid threshold percentage"));
				User::Leave(KErrArgument);
				}
			Printf(_L("Hit any key to abort waiting and flush buffer...\r\n"));
			TInt bytesRequired = (iBtrace.BufferSize() * iThreshold) / 100;
			TBool finished(EFalse);
			while (!finished)
				{
				TRequestStatus btraceStatus;
				iBtrace.RequestData(btraceStatus, bytesRequired);
				TRequestStatus keyStatus;
				Stdin().WaitForKey(keyStatus);
				if (iVerbose)
					{
					Printf(_L("Waiting for %d bytes in buffer\r\n"), bytesRequired);
					}
				User::WaitForRequest(btraceStatus, keyStatus);
				if (keyStatus == KRequestPending)
					{
					Stdin().WaitForKeyCancel();
					User::WaitForRequest(keyStatus);
					User::LeaveIfError(btraceStatus.Int());
					}
				else
					{
					iBtrace.CancelRequestData();
					User::WaitForRequest(btraceStatus);
					finished = ETrue;
					}

				DumpL();
				}
			}
		else
			{
			DumpL();
			}
		}

	if (iVerbose || (iFileName.Length() == 0))
		{
		Printf(_L("Buffer size: %d\r\n"), iBtrace.BufferSize());
		Printf(_L("Mode: %S (%u)\r\n"), ModeText(iBtrace.Mode()), iBtrace.Mode());
#ifdef TEXT_TRACE_MODE_SUPPORTED
		LoadMemoryAccessL();
		TUint traceMode = 0;
		TInt err = iMemAccess.SetTextTraceMode(traceMode, 0);
		if (err)
			{
			PrintWarning(_L("Couldn't read text trace mode: err=%d"), err);
			}
		else
			{
			_LIT(KHuh, "<?>");
			const TDesC* name = &KHuh;
#define CASE_LIT2(x, y) case x: { _LIT(KName, #y); name = &KName; break; }
			switch (traceMode)
				{
				CASE_LIT2(0, ESerialOutDefault)
				CASE_LIT2(1, ESerialOutNever)
				CASE_LIT2(2, ESerialOutAlways)
				default:
					break;
				}
			Printf(_L("Text trace mode: %S (%d)\r\n"), name, traceMode);
			}
#endif
		Printf(_L("Primary filters:\r\n"));
		TBool foundOne(EFalse);
		for (TInt i = 0; i < KMaxCategories; ++i)
			{
			if (iBtrace.Filter(i) == 1)
				{
				Printf(_L("\t%d (%S)\r\n"), i, BtraceCategoryName(i));
				foundOne = ETrue;
				}
			}
		if (!foundOne)
			{
			Write(_L("\tnone\r\n"));
			}
		Printf(_L("Secondary filters: "));
		TInt globalFilter;
		TUint32* uids;
		TInt ret = iBtrace.Filter2(uids, globalFilter);
		if (ret >= 0)
			{
			if (globalFilter)
				{
				Printf(_L("[Secondary filters accept]\r\n"));
				}
			else
				{
				Printf(_L("[Secondary filters reject]\r\n"));
				}
			if (ret > 0)
				{
				for (TInt i = 0; i < ret; ++i)
					{
					Printf(_L("\t0x%08x\r\n"), uids[i]);
					}
				}
			else
				{
				Write(_L("\tnone\r\n"));
				}
			delete uids;
			}
		else
			{
			PrintError(ret, _L("Unable to read secondary filters"));
			}
		}
	}

void CCmdBtrace::DumpL()
	{
	RFile file;
	if (iFileName.Length())
		{
		if (iVerbose)
			{
			Printf(_L("Opening \"%S\"\r\n"), &iFileName);
			}
		User::LeaveIfError(file.Open(FsL(), iFileName, EFileWrite));
		CleanupClosePushL(file);
		TInt pos = 0;
		User::LeaveIfError(file.Seek(ESeekEnd, pos));
		}

	TUint8* data;
	TInt size;
	TInt err = KErrNone;
	while ((size = iBtrace.GetData(data)) != 0)
		{
		if (file.SubSessionHandle() && (err == KErrNone))
			{
			if (iVerbose)
				{
				Printf(_L("Writing %d bytes to \"%S\"\r\n"), size, &iFileName);
				}
			err = file.Write(TPtrC8(data,size));
			if (err)
				{
				PrintError(err, _L("Couldn't write to file"));
				}
			}

		if (iDumpToRdebug)
			{
			if (iVerbose)
				{
				Printf(_L("Writing %d bytes to LtkUtils::HexDumpToRDebug\r\n"), size);
				}
			LtkUtils::HexDumpToRDebug(TPtrC8(data, size));
			}

		iBtrace.DataUsed();
		}

	User::LeaveIfError(err);
	if (iFileName.Length())
		{
		CleanupStack::PopAndDestroy(&file);
		}
	}

void CCmdBtrace::ArgumentsL(RCommandArgumentList& aArguments)
	{
	_LIT(KArgFiles, "file_name");
	aArguments.AppendFileNameL(iFileName, KArgFiles);
	}

void CCmdBtrace::OptionsL(RCommandOptionList& aOptions)
	{
	_LIT(KOptPrimaryFilter, "filter");
	aOptions.AppendUintL(iPrimaryFilters, KOptPrimaryFilter);

	_LIT(KOptSecondaryFilter, "secondary");
	aOptions.AppendUintL(iSecondaryFilters, KOptSecondaryFilter);

	_LIT(KOptMode, "mode");
	aOptions.AppendUintL(iMode, KOptMode);

	_LIT(KOptBufferSize, "buffer");
	aOptions.AppendUintL(iBufferSize, KOptBufferSize);

	_LIT(KOptDump, "dump");
	aOptions.AppendBoolL(iDumpToRdebug, KOptDump);

	_LIT(KOptFollow, "follow");
	aOptions.AppendBoolL(iFollow, KOptFollow);

	_LIT(KOptThreshold, "threshold");
	aOptions.AppendUintL(iThreshold, KOptThreshold);

	_LIT(KOptTimestamp2, "timestamp2");
	aOptions.AppendBoolL(iEnableTimestamp2, KOptTimestamp2);

	_LIT(KOptVerbose, "verbose");
	aOptions.AppendBoolL(iVerbose, KOptVerbose);

	_LIT(KOptTest, "test");
	aOptions.AppendBoolL(iTest, KOptTest);

	_LIT(KOptReset, "reset");
	aOptions.AppendBoolL(iReset, KOptReset);

#ifdef TEXT_TRACE_MODE_SUPPORTED
	_LIT(KOptTextTraceMode, "text-trace-mode");
	aOptions.AppendUintL(iTextTraceMode, KOptTextTraceMode);
#endif
	}

#ifdef EXE_BUILD
EXE_BOILER_PLATE(CCmdBtrace)
#endif

void CCmdBtrace::TestL()
	{
	SetMode(RBTrace::EEnable);
	EmptyBuffer();
	iBtrace.SetFilter(BTrace::ESymbianExtentionsFirst, 1);
	_LIT8(KHello, "Hello world of btrace!!!");
	TInt res = BTraceBig(BTrace::ESymbianExtentionsFirst, 0, 0, KHello().Ptr(), KHello().Size());
	Printf(_L8("Writing text '%S' to btrace returned %d...\r\n"), &KHello, res);

	TUint8* data;
	TInt size;
	TInt err = KErrNotFound;
	while ((size = iBtrace.GetData(data)) != 0 && err == KErrNotFound)
		{
		Printf(_L("Read a buffer of size %d\r\n"), size);
		TPtrC8 ptr(data, size);
		if (ptr.Find(KHello) >= 0)
			{
			Printf(_L("Found text ok.\r\n"));
			err = KErrNone;
			}
		}
	if (err == KErrNotFound)
		{
		Printf(_L("Failed to find the test data\r\n"));
		}
	}

void CCmdBtrace::ResetL()
	{
	SetMode(0);
	EmptyBuffer();
	RemovePrimaryFilters();
	RemoveSecondaryFiltersL();
	}

void CCmdBtrace::SetMode(TUint aMode)
	{
	if (iVerbose)
		{
		Printf(_L("Setting mode to \"%S\" (%u)\r\n"), ModeText(aMode), aMode);
		}

	iBtrace.SetMode(aMode);
	}

void CCmdBtrace::EmptyBuffer()
	{
	if (iVerbose)
		{
		Write(_L("Discarding contents of trace buffer...\r\n"));
		}

	iBtrace.Empty();
	}

void CCmdBtrace::RemovePrimaryFilters()
	{
	if (iVerbose)
		{
		Write(_L("Removing all primary filters...\r\n"));
		}

	for (TInt i = 0; i < KMaxCategories; ++i)
		{
		iBtrace.SetFilter(i, 0);
		}
	}

void CCmdBtrace::RemoveSecondaryFiltersL()
	{
	if (iVerbose)
		{
		Write(_L("Removing all secondary filters...\r\n"));
		}

	LeaveIfErr(iBtrace.SetFilter2((const TUint32*)NULL, 0), _L("Couldn't remove all secondary filters"));
	}

const TDesC* CCmdBtrace::ModeText(TUint aMode) const
	{
	_LIT(KDisabled, "disabled");
	_LIT(KEnabled, "enabled");
	_LIT(KFreeRunning, "enabled and free running");
	_LIT(KUnknown, "unknown");

	switch (aMode)
		{
		case 0:
			return &KDisabled;
		case RBTrace::EEnable:
			return &KEnabled;
		case RBTrace::EEnable | RBTrace::EFreeRunning:
			return &KFreeRunning;
		default:
			return &KUnknown;
		}
	}

