--- /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)
+