commands/memsampler/memsampler.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/memsampler/memsampler.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,1021 @@
+// memsampler.cpp
+// 
+// Copyright (c) 2008 - 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 <fshell/ioutils.h>
+#include <fshell/common.mmh>
+#include <e32cons.h>
+#include FSHELL_D32BTRACE_HEADER
+#include "memsamplerdd.h"
+
+_LIT(KLdd, "memsamplerdd");	
+const TInt KMaxCategories = 256;
+const TInt KBtraceBufferSize = 64 * 1024;
+
+using namespace IoUtils;
+
+
+class TChunkInfo
+	{
+public:
+	TChunkInfo(TUint32 aAddress);
+	TChunkInfo(const TDesC& aName, TUint32 aAddress, TInt aMaxSize);
+	static TBool Match(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+	static TInt SortByUpdateHistoryAscending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+	static TInt SortByUpdateHistoryDescending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+	static TInt SortByNameAscending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+	static TInt SortByNameDescending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+	static TInt SortByMaxSizeAscending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+	static TInt SortByMaxSizeDescending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+	static TInt SortByCurrentSizeAscending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+	static TInt SortByCurrentSizeDescending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+	static TInt SortByHighWaterMarkAscending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+	static TInt SortByHighWaterMarkDescending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB);
+public:
+	TInt iUpdateCount;
+	TFullName iName;
+	TUint32 iAddress;
+	TInt iMaxSize;
+	TInt iCurrentSize;
+	TInt iHighWaterMark;
+	};
+
+TChunkInfo::TChunkInfo(TUint32 aAddress)
+	: iUpdateCount(0), iAddress(aAddress), iMaxSize(0), iCurrentSize(0), iHighWaterMark(0)
+	{
+	}
+
+TChunkInfo::TChunkInfo(const TDesC& aName, TUint32 aAddress, TInt aMaxSize)
+	: iUpdateCount(0), iName(aName), iAddress(aAddress), iMaxSize(aMaxSize), iCurrentSize(0), iHighWaterMark(0)
+	{
+	}
+
+TBool TChunkInfo::Match(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoA.iAddress == aChunkInfoB.iAddress);
+	}
+
+TInt TChunkInfo::SortByUpdateHistoryAscending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoA.iUpdateCount > aChunkInfoB.iUpdateCount);
+	}
+
+TInt TChunkInfo::SortByUpdateHistoryDescending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoA.iUpdateCount < aChunkInfoB.iUpdateCount);
+	}
+
+TInt TChunkInfo::SortByNameAscending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoA.iName.CompareC(aChunkInfoB.iName));
+	}
+
+TInt TChunkInfo::SortByNameDescending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoB.iName.CompareC(aChunkInfoA.iName));
+	}
+
+TInt TChunkInfo::SortByMaxSizeAscending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoA.iMaxSize > aChunkInfoB.iMaxSize);
+	}
+
+TInt TChunkInfo::SortByMaxSizeDescending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoA.iMaxSize < aChunkInfoB.iMaxSize);
+	}
+
+TInt TChunkInfo::SortByCurrentSizeAscending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoA.iCurrentSize > aChunkInfoB.iCurrentSize);
+	}
+
+TInt TChunkInfo::SortByCurrentSizeDescending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoA.iCurrentSize < aChunkInfoB.iCurrentSize);
+	}
+
+TInt TChunkInfo::SortByHighWaterMarkAscending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoA.iHighWaterMark > aChunkInfoB.iHighWaterMark);
+	}
+
+TInt TChunkInfo::SortByHighWaterMarkDescending(const TChunkInfo& aChunkInfoA, const TChunkInfo& aChunkInfoB)
+	{
+	return (aChunkInfoA.iHighWaterMark < aChunkInfoB.iHighWaterMark);
+	}
+
+
+class CMemoryView : public CBase
+	{
+public:
+	enum TSortType
+		{
+		ESortUnspecified,
+		ESortByChunkName,
+		ESortByCurrentSize,
+		ESortByHighWaterMark,
+		ESortByMaxSize,
+		ESortByUpdateHistory
+		};
+	enum TSortOrder
+		{
+		EAscending,
+		EDescending
+		};
+public:
+	static CMemoryView* NewL(RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, TBool aHuman);
+	~CMemoryView();
+	void ChangeSortType(TSortType aSortType);
+	void ChangeSortOrder(TSortOrder aSortOrder);
+	void ScrollUp();
+	void ScrollDown();
+	void ScrollLeft();
+	void ScrollRight();
+	void HandleNewChunk(const TDesC& aName, TUint32 aAddress, TInt aMaxSize);
+	void HandleChangedChunk(TUint32 aAddress, TInt aSize, TInt aHighWaterMark);
+	void HandleDeletedChunk(TUint32 aAddress);
+	void UpdateL();
+private:
+	CMemoryView(RIoWriteHandle& aStderr, TBool aHuman);
+	void ConstructL(RIoWriteHandle& aStdout);
+	void Sort();
+	void PrintWarning(TRefByValue<const TDesC> aFmt, ...);
+private:
+	RIoWriteHandle& iStderr;
+	TBool iHuman;
+	RIoConsoleWriteHandle iStdout;
+	TInt iNumConsoleLines;
+	TInt iNumLinesInLastUpdate;
+	RArray<TChunkInfo> iChunkList;
+	CTextBuffer* iBuffer;
+	CTextFormatter* iFormatter;
+	TSortOrder iSortOrder;
+	TLinearOrder<TChunkInfo> iSortByUpdateHistoryAscending;
+	TLinearOrder<TChunkInfo> iSortByUpdateHistoryDescending;
+	TLinearOrder<TChunkInfo> iSortByChunkNameAscending;
+	TLinearOrder<TChunkInfo> iSortByChunkNameDescending;
+	TLinearOrder<TChunkInfo> iSortByCurrentSizeAscending;
+	TLinearOrder<TChunkInfo> iSortByCurrentSizeDescending;
+	TLinearOrder<TChunkInfo> iSortByHighWaterMarkAscending;
+	TLinearOrder<TChunkInfo> iSortByHighWaterMarkDescending;
+	TLinearOrder<TChunkInfo> iSortByMaxSizeAscending;
+	TLinearOrder<TChunkInfo> iSortByMaxSizeDescending;
+	TLinearOrder<TChunkInfo>* iCurrentSortType;
+	TInt iVerticalOffset;
+	TInt iHorizontalOffset;
+	};
+
+CMemoryView* CMemoryView::NewL(RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, TBool aHuman)
+	{
+	CMemoryView* self = new(ELeave) CMemoryView(aStderr, aHuman);
+	CleanupStack::PushL(self);
+	self->ConstructL(aStdout);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CMemoryView::~CMemoryView()
+	{
+	delete iBuffer;
+	delete iFormatter;
+	iChunkList.Close();
+	}
+
+void CMemoryView::ChangeSortType(TSortType aSortType)
+	{
+	switch (aSortType)
+		{
+		default:
+		case ESortByChunkName:
+			{
+			if (iCurrentSortType == &iSortByChunkNameAscending)
+				{
+				iCurrentSortType = &iSortByChunkNameDescending;
+				}
+			else if (iCurrentSortType == &iSortByChunkNameDescending)
+				{
+				iCurrentSortType = &iSortByChunkNameAscending;
+				}
+			else
+				{
+				iCurrentSortType = &iSortByChunkNameAscending;
+				}
+			break;
+			}
+		case ESortByCurrentSize:
+			{
+			if (iCurrentSortType == &iSortByCurrentSizeAscending)
+				{
+				iCurrentSortType = &iSortByCurrentSizeDescending;
+				}
+			else if (iCurrentSortType == &iSortByCurrentSizeDescending)
+				{
+				iCurrentSortType = &iSortByCurrentSizeAscending;
+				}
+			else
+				{
+				iCurrentSortType = &iSortByCurrentSizeDescending;
+				}
+			break;
+			}
+		case ESortByHighWaterMark:
+			{
+			if (iCurrentSortType == &iSortByHighWaterMarkAscending)
+				{
+				iCurrentSortType = &iSortByHighWaterMarkDescending;
+				}
+			else if (iCurrentSortType == &iSortByHighWaterMarkDescending)
+				{
+				iCurrentSortType = &iSortByHighWaterMarkAscending;
+				}
+			else
+				{
+				iCurrentSortType = &iSortByHighWaterMarkDescending;
+				}
+			break;
+			}
+		case ESortByMaxSize:
+			{
+			if (iCurrentSortType == &iSortByMaxSizeAscending)
+				{
+				iCurrentSortType = &iSortByMaxSizeDescending;
+				}
+			else if (iCurrentSortType == &iSortByMaxSizeDescending)
+				{
+				iCurrentSortType = &iSortByMaxSizeAscending;
+				}
+			else
+				{
+				iCurrentSortType = &iSortByMaxSizeDescending;
+				}
+			break;
+			}
+		case ESortByUpdateHistory:
+			{
+			if (iCurrentSortType == &iSortByUpdateHistoryAscending)
+				{
+				iCurrentSortType = &iSortByUpdateHistoryDescending;
+				}
+			else if (iCurrentSortType == &iSortByUpdateHistoryDescending)
+				{
+				iCurrentSortType = &iSortByUpdateHistoryAscending;
+				}
+			else
+				{
+				iCurrentSortType = &iSortByUpdateHistoryAscending;
+				}
+			break;
+			}
+		}
+	Sort();
+	}
+
+void CMemoryView::ChangeSortOrder(TSortOrder aSortOrder)
+	{
+	iSortOrder = aSortOrder;
+	}
+
+void CMemoryView::ScrollUp()
+	{
+	if (iChunkList.Count() > iNumConsoleLines)
+		{
+		if (iVerticalOffset > 0)
+			{
+			// Not yet reached the top.
+			--iVerticalOffset;
+			}
+		}
+	}
+
+void CMemoryView::ScrollDown()
+	{
+	const TInt numChunks = iChunkList.Count();
+	if (numChunks > iNumConsoleLines)
+		{
+		if ((numChunks - iVerticalOffset) > iNumConsoleLines)
+			{
+			// Not yet reached the bottom.
+			++iVerticalOffset;
+			}
+		}
+	}
+
+void CMemoryView::ScrollLeft()
+	{
+	if (iHorizontalOffset > 0)
+		{
+		--iHorizontalOffset;
+		}
+	}
+
+void CMemoryView::ScrollRight()
+	{
+	// Allow the horizontal offset to increase unboundedly here - it'll be limited to something sensible in UpdateL.
+	++iHorizontalOffset;
+	}
+
+void CMemoryView::HandleNewChunk(const TDesC& aName, TUint32 aAddress, TInt aMaxSize)
+	{
+	// Age all the existing chunk's update counts by one.
+	const TInt numChunks = iChunkList.Count();
+	for (TInt i = 0; i < numChunks; ++i)
+		{
+		++iChunkList[i].iUpdateCount;
+		}
+
+	// Insert the new chunk (claiming update count zero).
+	TChunkInfo newChunkInfo(aName, aAddress, aMaxSize);
+	TInt err = iChunkList.InsertInOrderAllowRepeats(newChunkInfo, *iCurrentSortType);
+	if (err)
+		{
+		PrintWarning(_L("Couldn't handle new chunk: %d"), err);
+		}
+	}
+
+void CMemoryView::HandleChangedChunk(TUint32 aAddress, TInt aSize, TInt aHighWaterMark)
+	{
+	TChunkInfo chunkInfo(aAddress);
+	TInt pos = iChunkList.Find(chunkInfo, TChunkInfo::Match);
+	if (pos < 0)
+		{
+		PrintWarning(_L("Couldn't handle updated to chunk 0x%08x: %d"), aAddress, pos);
+		}
+	else
+		{
+		TChunkInfo& c = iChunkList[pos];
+		// Age all the chunks that have been updated more recently than this one.
+		const TInt numChunks = iChunkList.Count();
+		for (TInt i = 0; i < numChunks; ++i)
+			{
+			TChunkInfo& d = iChunkList[i];
+			if (d.iUpdateCount < c.iUpdateCount)
+				{
+				++d.iUpdateCount;
+				}
+			}
+
+		c.iUpdateCount = 0;
+		c.iCurrentSize = aSize;
+		c.iHighWaterMark = aHighWaterMark;
+		Sort();
+		}
+	}
+
+void CMemoryView::HandleDeletedChunk(TUint32 aAddress)
+	{
+	TChunkInfo chunkInfo(aAddress);
+	TInt pos = iChunkList.Find(chunkInfo, TChunkInfo::Match);
+	if (pos < 0)
+		{
+		PrintWarning(_L("Couldn't handle deletion of chunk 0x%08x: %d"), aAddress, pos);
+		}
+	else
+		{
+		TChunkInfo& c = iChunkList[pos];
+
+		// Rejuvenate chunks that haven't been updated since this one last was.
+		const TInt numChunks = iChunkList.Count();
+		for (TInt i = 0; i < numChunks; ++i)
+			{
+			TChunkInfo& d = iChunkList[i];
+			if (d.iUpdateCount > c.iUpdateCount)
+				{
+				--d.iUpdateCount;
+				}
+			}
+		
+		iChunkList.Remove(pos);
+		if (iChunkList.Count() <= iNumConsoleLines)
+			{
+			iVerticalOffset = 0;
+			}
+		}
+	}
+
+CMemoryView::CMemoryView(RIoWriteHandle& aStderr, TBool aHuman)
+	: iStderr(aStderr),
+	iHuman(aHuman),
+	iSortByUpdateHistoryAscending(TChunkInfo::SortByUpdateHistoryAscending),
+	iSortByUpdateHistoryDescending(TChunkInfo::SortByUpdateHistoryDescending),
+	iSortByChunkNameAscending(TChunkInfo::SortByNameAscending),
+	iSortByChunkNameDescending(TChunkInfo::SortByNameDescending),
+	iSortByCurrentSizeAscending(TChunkInfo::SortByCurrentSizeAscending),
+	iSortByCurrentSizeDescending(TChunkInfo::SortByCurrentSizeDescending),
+	iSortByHighWaterMarkAscending(TChunkInfo::SortByHighWaterMarkAscending),
+	iSortByHighWaterMarkDescending(TChunkInfo::SortByHighWaterMarkDescending),
+	iSortByMaxSizeAscending(TChunkInfo::SortByMaxSizeAscending),
+	iSortByMaxSizeDescending(TChunkInfo::SortByMaxSizeDescending),
+	iCurrentSortType(&iSortByUpdateHistoryAscending)
+	{
+	}
+
+void CMemoryView::ConstructL(RIoWriteHandle& aStdout)
+	{
+	if (aStdout.AttachedToConsole())
+		{
+		iStdout = aStdout;
+		TSize size;
+		User::LeaveIfError(iStdout.GetScreenSize(size));
+		iNumConsoleLines = size.iHeight;
+		iBuffer = CTextBuffer::NewL(0x100);
+		iFormatter = CTextFormatter::NewL(size.iWidth);
+		iStdout.SetCursorHeight(0);
+		iStdout.ClearScreen();
+		}
+	else
+		{
+		PrintWarning(_L("memsampler can't run unless it is attached directly to a console, aborting..."));
+		User::Leave(KErrArgument);
+		}
+
+	}
+
+void CMemoryView::Sort()
+	{
+	iChunkList.Sort(*iCurrentSortType);
+	}
+
+void CMemoryView::UpdateL()
+	{
+	iBuffer->Zero();
+	iFormatter->Zero();
+	const TInt numChunks = iChunkList.Count();
+	iBuffer->AppendL(_L("Chunk name\t   Current\t       Max\t      Peak\r\n"));
+	TInt numLines = 1;
+	for (TInt i = iVerticalOffset; i < numChunks; ++i)
+		{
+		++numLines;
+		const TChunkInfo& chunkInfo = iChunkList[i];
+		if (iHuman)
+			{
+			if (iHorizontalOffset >= chunkInfo.iName.Length())
+				{
+				// The horizontal offset is larger than this chunk name - reduce it to stop the chunk name column disappearing altogether.
+				// Note, ideally it would be nice to limit horizontal scrolling when the right most part of all the chunk names is visible.
+				// However, that would involve calculating all the column widths twice which seems a bit clumsy.
+				iHorizontalOffset = chunkInfo.iName.Length() - 1;
+				}
+			TPtrC name(chunkInfo.iName.Mid(iHorizontalOffset));
+			iBuffer->AppendFormatL(_L("%S\t"), &name);
+			iBuffer->AppendHumanReadableSizeL(chunkInfo.iCurrentSize, EColumnAlignedRight);
+			iBuffer->AppendL(_L("\t"));
+			iBuffer->AppendHumanReadableSizeL(chunkInfo.iMaxSize, EColumnAlignedRight);
+			iBuffer->AppendL(_L("\t"));
+			iBuffer->AppendHumanReadableSizeL(chunkInfo.iHighWaterMark, EColumnAlignedRight);
+			iBuffer->AppendL(_L("\r\n"));
+			}
+		else
+			{
+			iBuffer->AppendFormatL(_L("%S\t%d\t%d\t%d\r\n"), &chunkInfo.iName, chunkInfo.iCurrentSize, chunkInfo.iMaxSize, chunkInfo.iHighWaterMark);
+			}
+		if (numLines >= (iNumConsoleLines - 1))
+			{
+			break;
+			}
+		}
+	iFormatter->TabulateL(0, 1, iBuffer->Descriptor(), ETruncateLongestColumn);
+	User::LeaveIfError(iStdout.SetCursorPosAbs(TPoint(0, 0)));
+	iStdout.Write(iFormatter->Descriptor());
+	TInt numOldLines = iNumLinesInLastUpdate - numLines;
+	while (numOldLines > 0)
+		{
+		iStdout.ClearToEndOfLine();
+		iStdout.SetCursorPosRel(TPoint(0, 1));
+		--numOldLines;
+		}
+	iNumLinesInLastUpdate = numLines;
+	}
+
+void CMemoryView::PrintWarning(TRefByValue<const TDesC> aFmt, ...)
+	{
+	TOverflowTruncate overflow;
+	VA_LIST list;
+	VA_START(list, aFmt);
+	TBuf<0x100> buf(_L("Warning: "));
+	buf.AppendFormatList(aFmt, list, &overflow);
+	buf.AppendFormat(_L("\r\n"), &overflow);
+	iStderr.Write(buf);
+	}
+
+
+class CStdinReader : public CActive
+	{
+public:
+	static CStdinReader* NewL(RIoReadHandle& aStdin, CCommandBase& aCommand, CMemoryView* aMemoryView);
+	~CStdinReader();
+private: // From CActive.
+	virtual void RunL();
+	virtual void DoCancel();
+	virtual TInt RunError(TInt aError);
+private:
+	CStdinReader(RIoReadHandle& aStdin, CCommandBase& aCommand, CMemoryView* aMemoryView);
+	void QueueRead();
+private:
+	RIoConsoleReadHandle iStdin;
+	CCommandBase& iCommand;
+	CMemoryView* iMemoryView;
+	};
+
+CStdinReader* CStdinReader::NewL(RIoReadHandle& aStdin, CCommandBase& aCommand, CMemoryView* aMemoryView)
+	{
+	CStdinReader* self = new(ELeave) CStdinReader(aStdin, aCommand, aMemoryView);
+	self->QueueRead();
+	return self;
+	}
+
+CStdinReader::~CStdinReader()
+	{
+	Cancel();
+	}
+
+void CStdinReader::RunL()
+	{
+	if (iStatus.Int())
+		{
+		iCommand.Complete(iStatus.Int());
+		}
+	else
+		{
+		TBool noUpdate(EFalse);
+		CMemoryView::TSortType newSortType = CMemoryView::ESortUnspecified;
+		switch (iStdin.KeyCode())
+			{
+			case 'n':
+			case 'N':
+				{
+				newSortType = CMemoryView::ESortByChunkName;
+				break;
+				}
+			case 'c':
+			case 'C':
+				{
+				newSortType = CMemoryView::ESortByCurrentSize;
+				break;
+				}
+			case 'p':
+			case 'P':
+				{
+				newSortType = CMemoryView::ESortByHighWaterMark;
+				break;
+				}
+			case 'm':
+			case 'M':
+				{
+				newSortType = CMemoryView::ESortByMaxSize;
+				break;
+				}
+			case 'u':
+			case 'U':
+				{
+				newSortType = CMemoryView::ESortByUpdateHistory;
+				break;
+				}
+			case 'q':
+			case 'Q':
+				{
+				iCommand.Complete(KErrNone);
+				break;
+				}
+			case EKeyUpArrow:
+				{
+				if (iMemoryView)
+					{
+					iMemoryView->ScrollUp();
+					}
+				break;
+				}
+			case EKeyDownArrow:
+				{
+				if (iMemoryView)
+					{
+					iMemoryView->ScrollDown();
+					}
+				break;
+				}
+			case EKeyLeftArrow:
+				{
+				if (iMemoryView)
+					{
+					iMemoryView->ScrollLeft();
+					}
+				break;
+				}
+			case EKeyRightArrow:
+				{
+				if (iMemoryView)
+					{
+					iMemoryView->ScrollRight();
+					}
+				break;
+				}
+			default:
+				{
+				noUpdate = ETrue;
+				}
+			}
+
+		if ((newSortType != CMemoryView::ESortUnspecified) && iMemoryView)
+			{
+			iMemoryView->ChangeSortType(newSortType);
+			}
+
+		if (iMemoryView && !noUpdate)
+			{
+			iMemoryView->UpdateL();
+			}
+
+		QueueRead();
+		}
+	}
+
+void CStdinReader::DoCancel()
+	{
+	iStdin.WaitForKeyCancel();
+	}
+
+TInt CStdinReader::RunError(TInt aError)
+	{
+	iCommand.Complete(aError);
+	return KErrNone;
+	}
+
+CStdinReader::CStdinReader(RIoReadHandle& aStdin, CCommandBase& aCommand, CMemoryView* aMemoryView)
+	: CActive(CActive::EPriorityStandard), iCommand(aCommand), iMemoryView(aMemoryView)
+	{
+	iStdin = aStdin;
+	CActiveScheduler::Add(this);
+	}
+
+void CStdinReader::QueueRead()
+	{
+	iStdin.WaitForKey(iStatus);
+	SetActive();
+	}
+
+
+
+class TBtraceHeader
+	{
+public:
+	TUint8 iSize;
+	TUint8 iFlags;
+	TUint8 iCategory;
+	TUint8 iSubCategory;
+	};
+
+class CBtraceReader : public CActive
+	{
+public:
+	enum TMode
+		{
+		EConfigBtrace = 0x0001,
+		EDebug        = 0x0002
+		};
+public:
+	static CBtraceReader* NewL(TUint aMode, const TDesC& aFileName, CCommandBase& aCommand, CMemoryView* aMemoryView);
+	~CBtraceReader();
+private: // From CActive.
+	virtual void RunL();
+	virtual void DoCancel();
+	virtual TInt RunError(TInt aError);
+private:
+	CBtraceReader(const TDesC& aFileName, CCommandBase& aCommand, CMemoryView* aMemoryView);
+	void ConstructL(TUint aMode);
+	void QueueRead();
+	void DecodeFrame(const TBtraceHeader& aHeader, const TDesC8& aFrame, TUint32 aTickCount);
+	void Printf(TRefByValue<const TDesC> aFmt, ...);
+private:
+	const TDesC& iFileName;
+	CCommandBase& iCommand;
+	CMemoryView* iMemoryView;
+	RBTrace iBtrace;
+	CConsoleBase* iDebugConsole;
+	RFile iFile;
+	};
+
+CBtraceReader* CBtraceReader::NewL(TUint aMode, const TDesC& aFileName, CCommandBase& aCommand, CMemoryView* aMemoryView)
+	{
+	CBtraceReader* self = new(ELeave) CBtraceReader(aFileName, aCommand, aMemoryView);
+	CleanupStack::PushL(self);
+	self->ConstructL(aMode);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CBtraceReader::~CBtraceReader()
+	{
+	Cancel();
+	iBtrace.Close();
+	iFile.Close();
+	delete iDebugConsole;
+	}
+
+void CBtraceReader::RunL()
+	{
+	QueueRead();
+	TUint8* data;
+	TInt size;
+	while ((size = iBtrace.GetData(data)) != 0)
+		{
+		if (iMemoryView || iDebugConsole)
+			{
+			// Only decode btrace frames if we're attached to a CMemoryView object or have a debug console.
+			TUint8* c = data;
+			TUint8* end = c + size;
+			do
+				{
+				TBtraceHeader* header = (TBtraceHeader*)c;
+				TUint8* d = c + sizeof(TBtraceHeader);
+				TUint32 tickCount = 0;
+				if (header->iFlags & BTrace::EMissingRecord)
+					{
+					User::Leave(KErrOverflow);
+					}
+				if (header->iFlags & BTrace::EHeader2Present)
+					{
+					d += 4;
+					}
+				if (header->iFlags & BTrace::ETimestampPresent)
+					{
+					tickCount = *((TUint32*)d);
+					d += 4;
+					}
+				if (header->iFlags & BTrace::ETimestamp2Present)
+					{
+					d += 4;
+					}
+				if (header->iFlags & BTrace::EContextIdPresent)
+					{
+					d += 4;
+					}
+				if (header->iFlags & BTrace::EPcPresent)
+					{
+					d += 4;
+					}
+				if (header->iFlags & BTrace::EExtraPresent)
+					{
+					d += 4;
+					}
+				TPtrC8 ptr(d, (c + header->iSize) - d);
+				DecodeFrame(*header, ptr, tickCount);
+				c += (header->iSize + 3) & ~3;
+				}
+				while (c < end);
+			}
+		if (iFileName.Length())
+			{
+			User::LeaveIfError(iFile.Write(TPtrC8(data, size)));
+			}
+		iBtrace.DataUsed();
+		}
+	if (iMemoryView)
+		{
+		iMemoryView->UpdateL();
+		}
+	}
+
+void CBtraceReader::DoCancel()
+	{
+	iBtrace.CancelRequestData();
+	}
+
+TInt CBtraceReader::RunError(TInt aError)
+	{
+	if (aError == KErrOverflow)
+		{
+		Printf(_L("Warning: BTrace buffer overflowed, aborting..."), aError);
+		iCommand.Complete(aError);
+		}
+	else if (aError)
+		{
+		Printf(_L("Warning: Could not update view (%d), aborting..."), aError);
+		iCommand.Complete(aError);
+		}
+	return KErrNone;
+	}
+
+CBtraceReader::CBtraceReader(const TDesC& aFileName, CCommandBase& aCommand, CMemoryView* aMemoryView)
+	: CActive(CActive::EPriorityStandard), iFileName(aFileName), iCommand(aCommand), iMemoryView(aMemoryView)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+void CBtraceReader::ConstructL(TUint aMode)
+	{
+	if (aMode & EDebug)
+		{
+		iDebugConsole = Console::NewL(_L("debug"), TSize(KConsFullScreen,KConsFullScreen));
+		}
+	User::LeaveIfError(iBtrace.Open());
+	if (aMode & EConfigBtrace)
+		{
+		User::LeaveIfError(iBtrace.ResizeBuffer(KBtraceBufferSize));
+		// Turn everything off.
+		for (TInt i = 0; i < KMaxCategories; ++i)
+			{
+			iBtrace.SetFilter(i, 0);
+			}
+		if (aMode & EDebug)
+			{
+			iBtrace.SetFilter(BTrace::EKernPrintf, 1);
+			}
+		iBtrace.SetFilter(RMemSampler::EBtraceCategory, 1);
+		User::LeaveIfError(iBtrace.SetFilter2((const TUint32*)NULL, 0));
+		iBtrace.SetMode(RBTrace::EEnable | RBTrace::EFreeRunning);
+		}
+	if (iFileName.Length())
+		{
+		User::LeaveIfError(iFile.Replace(iCommand.FsL(), iFileName, EFileWrite));
+		}
+	QueueRead();
+	}
+
+void CBtraceReader::QueueRead()
+	{
+	iBtrace.RequestData(iStatus, 0);
+	SetActive();
+	}
+
+void CBtraceReader::DecodeFrame(const TBtraceHeader& aHeader, const TDesC8& aFrame, TUint32)
+	{
+	if (aHeader.iCategory == BTrace::EKernPrintf)
+		{
+		TUint32 threadId = *(TUint32*)aFrame.Ptr();
+		TBuf<256> text;
+		text.Copy(aFrame.Mid(4));
+		if (iDebugConsole)
+			{
+			iDebugConsole->Printf(_L("Kern::Printf (0x%08x) \'%S\'\r\n"), &threadId, &text);
+			}
+		}
+	else if (aHeader.iCategory == RMemSampler::EBtraceCategory)
+		{
+		switch (aHeader.iSubCategory)
+			{
+			case RMemSampler::ENewChunk:
+				{
+				TUint32 address = *(TUint32*)aFrame.Ptr();
+				TInt maxSize = *((TUint32*)aFrame.Ptr() + 1);
+				TFullName fullName;
+				fullName.Copy(aFrame.Mid(8));
+				if (iDebugConsole)
+					{
+					iDebugConsole->Printf(_L("New chunk - %S\r\n\taddress: 0x%08x max size: %d\r\n"), &fullName, address, maxSize);
+					}
+				if (iMemoryView)
+					{
+					iMemoryView->HandleNewChunk(fullName, address, maxSize);
+					}
+				break;
+				}
+			case RMemSampler::EChangedChunk:
+				{
+				TUint32 address = *(TUint32*)aFrame.Ptr();
+				TUint32 size = *((TUint32*)aFrame.Ptr() + 1);
+				TUint32 highWaterMark = *((TUint32*)aFrame.Ptr() + 2);
+				if (iDebugConsole)
+					{
+					iDebugConsole->Printf(_L("Changed chunk - address: 0x%08x size: %d hwm: %d\r\n"), address, size, highWaterMark);
+					}
+				if (iMemoryView)
+					{
+					iMemoryView->HandleChangedChunk(address, size, highWaterMark);
+					}
+				break;
+				}
+			case RMemSampler::EDeletedChunk:
+				{
+				TUint32 address = *(TUint32*)aFrame.Ptr();
+				if (iDebugConsole)
+					{
+					iDebugConsole->Printf(_L("Deleted chunk - address: 0x%08x\r\n"), address);
+					}
+				if (iMemoryView)
+					{
+					iMemoryView->HandleDeletedChunk(address);
+					}
+				break;
+				}
+			}
+		}
+	}
+
+void CBtraceReader::Printf(TRefByValue<const TDesC> aFmt, ...)
+	{
+	TOverflowTruncate overflow;
+	VA_LIST list;
+	VA_START(list, aFmt);
+	TBuf<0x100> buf;
+	buf.AppendFormatList(aFmt, list, &overflow);
+	iCommand.Stdout().Write(buf);
+	}
+
+
+class CCmdMemsampler : public CCommandBase
+	{
+public:
+	static CCommandBase* NewLC();
+	~CCmdMemsampler();
+private:
+	CCmdMemsampler();
+private: // From CCommandBase.
+	virtual const TDesC& Name() const;
+	virtual void DoRunL();
+	virtual void OptionsL(RCommandOptionList& aOptions);
+private:
+	RMemSampler iMemSampler;
+	CMemoryView* iMemoryView;
+	CStdinReader* iStdinReader;
+	CBtraceReader* iBtraceReader;
+	TUint iRate;
+	TBool iNoBtraceConfig;
+	TBool iDebug;
+	TBool iHuman;
+	TFileName2 iFileName;
+	TBool iNoLiveView;
+	};
+
+
+CCommandBase* CCmdMemsampler::NewLC()
+	{
+	CCmdMemsampler* self = new(ELeave) CCmdMemsampler();
+	CleanupStack::PushL(self);
+	self->BaseConstructL();
+	return self;
+	}
+
+CCmdMemsampler::~CCmdMemsampler()
+	{
+	iMemSampler.Close();
+	User::FreeLogicalDevice(KLdd);
+	delete iStdinReader;
+	delete iBtraceReader;
+	delete iMemoryView;
+	}
+
+CCmdMemsampler::CCmdMemsampler() : CCommandBase(EManualComplete), iRate(1000)
+	{
+	}
+
+const TDesC& CCmdMemsampler::Name() const
+	{
+	_LIT(KName, "memsampler");	
+	return KName;
+	}
+
+void CCmdMemsampler::DoRunL()
+	{
+	TUint mode = 0;
+	if (!iNoBtraceConfig)
+		{
+		mode |= CBtraceReader::EConfigBtrace;
+		}
+	if (iDebug)
+		{
+		mode |= CBtraceReader::EDebug;
+		}
+
+	if (!iNoLiveView)
+		{
+		iMemoryView = CMemoryView::NewL(Stdout(), Stderr(), iHuman);
+		}
+	iStdinReader = CStdinReader::NewL(Stdin(), *this, iMemoryView);
+	iBtraceReader = CBtraceReader::NewL(mode, iFileName, *this, iMemoryView);
+	User::LeaveIfError(User::LoadLogicalDevice(KLdd));
+	User::LeaveIfError(iMemSampler.Open());
+	iMemSampler.Start(iRate);
+	}
+
+void CCmdMemsampler::OptionsL(RCommandOptionList& aOptions)
+	{
+	_LIT(KOptSampleRate, "rate");
+	aOptions.AppendUintL(iRate, KOptSampleRate);
+
+	_LIT(KOptNoBtraceConfig, "no-btrace-config");
+	aOptions.AppendBoolL(iNoBtraceConfig, KOptNoBtraceConfig);
+
+	_LIT(KOptDebug, "debug");
+	aOptions.AppendBoolL(iDebug, KOptDebug);
+
+	_LIT(KOptHuman, "human");
+	aOptions.AppendBoolL(iHuman, KOptHuman);
+
+	_LIT(KOptFile, "file");
+	aOptions.AppendFileNameL(iFileName, KOptFile);
+
+	_LIT(KOptNoLiveView, "no-live-view");
+	aOptions.AppendBoolL(iNoLiveView, KOptNoLiveView);
+	}
+
+
+EXE_BOILER_PLATE(CCmdMemsampler)
+