diff -r 000000000000 -r 7f656887cf89 commands/memsampler/memsampler.cpp --- /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 +#include +#include +#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 aFmt, ...); +private: + RIoWriteHandle& iStderr; + TBool iHuman; + RIoConsoleWriteHandle iStdout; + TInt iNumConsoleLines; + TInt iNumLinesInLastUpdate; + RArray iChunkList; + CTextBuffer* iBuffer; + CTextFormatter* iFormatter; + TSortOrder iSortOrder; + TLinearOrder iSortByUpdateHistoryAscending; + TLinearOrder iSortByUpdateHistoryDescending; + TLinearOrder iSortByChunkNameAscending; + TLinearOrder iSortByChunkNameDescending; + TLinearOrder iSortByCurrentSizeAscending; + TLinearOrder iSortByCurrentSizeDescending; + TLinearOrder iSortByHighWaterMarkAscending; + TLinearOrder iSortByHighWaterMarkDescending; + TLinearOrder iSortByMaxSizeAscending; + TLinearOrder iSortByMaxSizeDescending; + TLinearOrder* 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 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 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 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) +