commands/btrace/btrace.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 00:49:35 +0100
changeset 45 534b01198c2d
parent 0 7f656887cf89
permissions -rw-r--r--
Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase. Commands can now get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.

// 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;
		}
	}