/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "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:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#include <memspy/engine/memspyenginehelperheap.h>
// System includes
#include <s32mem.h>
// Driver includes
#include <memspy/driver/memspydriverclient.h>
// User includes
#include <memspy/engine/memspyengine.h>
#include <memspy/engine/memspyengineutils.h>
#include <memspy/engine/memspyengineoutputsink.h>
#include <memspy/engine/memspyengineoutputlist.h>
#include <memspy/engine/memspyengineobjectthread.h>
#include <memspy/engine/memspyengineobjectprocess.h>
#include <memspy/engine/memspyenginehelperrom.h>
#include <memspy/engine/memspyengineobjectcontainer.h>
#include <memspy/engine/memspyenginehelpercodesegment.h>
#include <memspy/driver/memspydriverenumerationsshared.h>
// Constants
const TBool KMemSpyHeapDumpCreateOwnDataStream = ETrue;
// Literal constants
_LIT( KCellTypeGoodAllocatedCell, "[Allocated Cell] ");
_LIT( KCellTypeGoodFreeCell, "[Free Cell] ");
_LIT( KCellTypeBadAllocatedCellSize, "[Bad Allocated Cell Size] ");
_LIT( KCellTypeBadAllocatedCellAddress, "[Bad Allocated Cell Address]");
_LIT( KCellTypeBadFreeCellAddress, "[Bad Free Cell Address] ");
_LIT( KCellTypeBadFreeCellSize, "[Bad Free Cell Size] ");
_LIT( KCellTypeUnknown, "[Unknown!] ");
_LIT( KCellListLineFormat, "%S cell: 0x%08x, cellLen: %8d, allocNum: %8d, nestingLev: %8d, cellData: 0x%08x, cellDataAddr: 0x%08x, headerSize: %02d");
_LIT( KMemSpyMarkerHeapData, "<%SMEMSPY_HEAP_DATA_%03d>" );
_LIT( KMemSpyMarkerCSV, "<%SMEMSPY_HEAP_CSV>" );
_LIT( KMemSpyPrefixHeapData, "HeapData - %S - ");
_LIT( KMemSpyPrefixCellList, "CellList - %S - ");
_LIT( KMemSpyPrefixCSV, "CSV - " );
CMemSpyEngineHelperHeap::CMemSpyEngineHelperHeap( CMemSpyEngine& aEngine )
: iEngine( aEngine )
{
}
CMemSpyEngineHelperHeap::~CMemSpyEngineHelperHeap()
{
}
void CMemSpyEngineHelperHeap::ConstructL()
{
}
CMemSpyEngineHelperHeap* CMemSpyEngineHelperHeap::NewL( CMemSpyEngine& aEngine )
{
CMemSpyEngineHelperHeap* self = new(ELeave) CMemSpyEngineHelperHeap( aEngine );
CleanupStack::PushL( self );
self->ConstructL();
CleanupStack::Pop( self );
return self;
}
EXPORT_C void CMemSpyEngineHelperHeap::OutputCellListingUserL( const CMemSpyThread& aThread )
{
// Suspend the process
iEngine.ProcessSuspendLC( aThread.Process().Id() );
// Free cells
RArray<TMemSpyDriverFreeCell> freeCells;
CleanupClosePushL( freeCells );
// Info section
TMemSpyHeapInfo heapInfo;
const TInt error = iEngine.Driver().GetHeapInfoUser( heapInfo, aThread.Id(), freeCells );
if ( error == KErrNone )
{
UpdateSharedHeapInfoL( aThread.Process().Id(), aThread.Id(), heapInfo );
}
if ( error == KErrNone && heapInfo.Type() == TMemSpyHeapInfo::ETypeRHeap )
{
// Get thread name for context
const TFullName pName( aThread.FullName() );
// Begin a new data stream
_LIT( KMemSpyFolder, "Heap\\Cell List" );
_LIT( KMemSpyContext, "Cell List - %S" );
HBufC* context = HBufC::NewLC( KMaxFileName );
TPtr pContext( context->Des() );
pContext.Format( KMemSpyContext, &pName );
iEngine.Sink().DataStreamBeginL( pContext, KMemSpyFolder );
CleanupStack::PopAndDestroy( context );
// Set prefix for overall listing
iEngine.Sink().OutputPrefixSetFormattedLC( KMemSpyPrefixCellList, &pName );
// Start new section
_LIT(KHeader, "CELL LISTING");
iEngine.Sink().OutputSectionHeadingL( KHeader, '=' );
// Prepare temp buffers
TBuf<KMaxFullName + 100> printFormat;
HBufC* tempBuffer = HBufC::NewLC( 2048 );
TPtr pTempBuffer( tempBuffer->Des() );
// Print initial info
OutputHeapInfoL( heapInfo, pName, &freeCells );
// Code segments (needed for map file reading...)
_LIT(KCellListCodeSegInfoFormat, "CodeSegs - ");
iEngine.HelperCodeSegment().OutputCodeSegmentsL( aThread.Process().Id(), printFormat, KCellListCodeSegInfoFormat, '-', ETrue );
// Now walk the heap!
TInt r = iEngine.Driver().WalkHeapInit( aThread.Id() );
if ( r == KErrNone )
{
_LIT(KHeader2, "Cells");
iEngine.Sink().OutputSectionHeadingL( KHeader2, '-' );
TMemSpyDriverCellType cellType;
TAny* cellAddress;
TInt cellLength;
TInt cellNestingLevel;
TInt cellAllocationNumber;
TInt cellHeaderSize;
TAny* cellPayloadAddress;
TBuf8<4> cellData;
//
r = iEngine.Driver().WalkHeapNextCell( aThread.Id(), cellType, cellAddress, cellLength, cellNestingLevel, cellAllocationNumber, cellHeaderSize, cellPayloadAddress );
while( r == KErrNone )
{
TUint fourByteCellData = 0;
TPtrC pType(KNullDesC);
//
switch(cellType)
{
case EMemSpyDriverGoodAllocatedCell:
{
r = iEngine.Driver().WalkHeapReadCellData( cellAddress, cellData, 4 );
if ( r == KErrNone )
{
fourByteCellData = DescriptorAsDWORD( cellData );
}
pType.Set(KCellTypeGoodAllocatedCell);
break;
}
case EMemSpyDriverGoodFreeCell:
pType.Set(KCellTypeGoodFreeCell);
break;
case EMemSpyDriverBadAllocatedCellSize:
pType.Set(KCellTypeBadAllocatedCellSize);
break;
case EMemSpyDriverBadAllocatedCellAddress:
pType.Set(KCellTypeBadAllocatedCellAddress);
break;
case EMemSpyDriverBadFreeCellAddress:
pType.Set(KCellTypeBadFreeCellAddress);
break;
case EMemSpyDriverBadFreeCellSize:
pType.Set(KCellTypeBadFreeCellSize);
break;
default:
pType.Set(KCellTypeUnknown);
break;
}
if ( r == KErrNone )
{
pTempBuffer.Format( KCellListLineFormat, &pType, cellAddress, cellLength, cellAllocationNumber, cellNestingLevel, fourByteCellData, cellPayloadAddress, cellHeaderSize );
iEngine.Sink().OutputLineL( pTempBuffer );
//
r = iEngine.Driver().WalkHeapNextCell( aThread.Id(), cellType, cellAddress, cellLength, cellNestingLevel, cellAllocationNumber, cellHeaderSize, cellPayloadAddress );
}
}
//
iEngine.Driver().WalkHeapClose();
}
CleanupStack::PopAndDestroy( tempBuffer );
CleanupStack::PopAndDestroy(); // clear prefix
iEngine.Sink().DataStreamEndL();
}
CleanupStack::PopAndDestroy( &freeCells );
CleanupStack::PopAndDestroy(); // resume process
}
EXPORT_C void CMemSpyEngineHelperHeap::OutputHeapDataUserL( const CMemSpyThread& aThread )
{
OutputHeapDataUserL( aThread, KMemSpyHeapDumpCreateOwnDataStream );
}
void CMemSpyEngineHelperHeap::OutputHeapDataUserL( const CMemSpyThread& aThread, TBool aCreateDataStream )
{
// Make sure the process is suspended for the entire time we are manipulating it's heap
iEngine.ProcessSuspendLC( aThread.Process().Id() );
// Get the heap info, including free cell information
RArray<TMemSpyDriverFreeCell> freeCells;
CleanupClosePushL( freeCells );
TMemSpyHeapInfo heapInfo;
TRACE( RDebug::Printf( "CMemSpyEngineHelperHeap::OutputHeapDataUserL() - checksum1: 0x%08x", heapInfo.AsRHeap().Statistics().StatsFree().Checksum() ) );
GetHeapInfoUserL( aThread.Process().Id(), aThread.Id(), heapInfo, &freeCells );
TRACE( RDebug::Printf( "CMemSpyEngineHelperHeap::OutputHeapDataUserL() - checksum2: 0x%08x", heapInfo.AsRHeap().Statistics().StatsFree().Checksum() ) );
// Get the heap data
const TFullName pName( aThread.FullName() );
OutputHeapDataUserL( aThread.Process().Id(), aThread.Id(), pName, heapInfo, aCreateDataStream, &freeCells );
CleanupStack::PopAndDestroy( &freeCells );
// Resume process
CleanupStack::PopAndDestroy();
}
EXPORT_C void CMemSpyEngineHelperHeap::OutputHeapDataUserL( const TProcessId& aPid, const TThreadId& aTid, const TDesC& aThreadName, const TMemSpyHeapInfo& aInfo, const RArray<TMemSpyDriverFreeCell>* aFreeCells )
{
OutputHeapDataUserL( aPid, aTid, aThreadName, aInfo, ETrue, aFreeCells );
}
void CMemSpyEngineHelperHeap::OutputHeapDataUserL( const TProcessId& aPid, const TThreadId& aTid, const TDesC& aThreadName, const TMemSpyHeapInfo& aInfo, TBool aCreateDataStream, const RArray<TMemSpyDriverFreeCell>* aFreeCells )
{
TBuf<KMaxFullName + 100> printFormat;
// Begin a new data stream
if ( aCreateDataStream )
{
_LIT( KMemSpyFolder, "Heap\\Data" );
_LIT( KMemSpyContext, "Heap Data - %S" );
HBufC* context = HBufC::NewLC( KMaxFileName );
TPtr pContext( context->Des() );
pContext.Format( KMemSpyContext, &aThreadName );
iEngine.Sink().DataStreamBeginL( pContext, KMemSpyFolder );
CleanupStack::PopAndDestroy( context );
}
// Get the heap info first of all
iEngine.ProcessSuspendLC( aPid );
// Start marker
iEngine.Sink().OutputLineFormattedL( KMemSpyMarkerHeapData, &KNullDesC, (TUint) aTid );
// Set overall prefix
iEngine.Sink().OutputPrefixSetFormattedLC( KMemSpyPrefixHeapData, &aThreadName );
// Info section
OutputHeapInfoL( aInfo, aThreadName, aFreeCells );
// Code segments (needed for map file reading...)
_LIT(KCellListCodeSegInfoFormat, "CodeSegs - ");
iEngine.HelperCodeSegment().OutputCodeSegmentsL( aPid, printFormat, KCellListCodeSegInfoFormat, '-', ETrue );
// Dump section
_LIT(KHeaderDump, "Heap Data");
iEngine.Sink().OutputSectionHeadingL( KHeaderDump, '-' );
HBufC8* data = HBufC8::NewLC( 4096 * 12 );
TPtr8 pData(data->Des());
TUint remaining = 0;
TUint readAddress = 0;
// When we obtained the heap info, we also obtained a checksum of all the free cells
// within the specified heap. We validate that this hasn't changed at the time we
// request the heap data for paranoia purposes (There have been "Issues" with MemSpy
// not actually suspending a process between fetching heap info & heap data, causing
// a mismatch in free cell information).
const TUint32 checksum = aInfo.AsRHeap().Statistics().StatsFree().Checksum();
TRACE( RDebug::Printf( "CMemSpyEngineHelperHeap::OutputHeapDataUserL() - checksum: 0x%08x", checksum ) );
TInt r = iEngine.Driver().GetHeapData( aTid, checksum, pData, readAddress, remaining );
if ( r == KErrNone )
{
while ( r == KErrNone )
{
_LIT(KHeapDumpDataFormat, "%S");
iEngine.Sink().OutputBinaryDataL( KHeapDumpDataFormat, pData.Ptr(), (const TUint8*) readAddress, pData.Length() );
if ( remaining > 0 )
r = iEngine.Driver().GetHeapDataNext( aTid, pData, readAddress, remaining );
else
break;
}
}
else
{
_LIT( KHeapFetchError, "Heap error: %d");
iEngine.Sink().OutputLineFormattedL( KHeapFetchError, r );
}
CleanupStack::PopAndDestroy( data );
CleanupStack::PopAndDestroy(); // clear prefix
CleanupStack::PopAndDestroy(); // resume process
// End marker
iEngine.Sink().OutputLineFormattedL( KMemSpyMarkerHeapData, &KMemSpySinkTagClose, (TUint) aTid );
if ( aCreateDataStream )
{
iEngine.Sink().DataStreamEndL();
}
}
EXPORT_C void CMemSpyEngineHelperHeap::OutputHeapInfoL( const TMemSpyHeapInfo& aInfo, const TDesC& aThreadName, const RArray<TMemSpyDriverFreeCell>* aFreeCells )
{
CMemSpyEngineOutputList* list = NewHeapSummaryExtendedLC( aInfo, aFreeCells );
// Format the thread name according to upper/lower case request parameters
_LIT( KOverallCaption1, "HEAP INFO FOR THREAD '%S'");
list->InsertItemFormatUCL( 0, KOverallCaption1, &aThreadName );
list->InsertUnderlineForItemAtL( 0 );
// Print it
list->PrintL();
// Tidy up
CleanupStack::PopAndDestroy( list );
}
void CMemSpyEngineHelperHeap::OutputCSVEntryL( TInt aIndex, const TMemSpyHeapInfo& aInfo, const TDesC& aThreadName, const TDesC& aProcessName )
{
const TMemSpyHeapInfoRHeap& rHeapInfo = aInfo.AsRHeap();
const TMemSpyHeapMetaDataRHeap& rHeapMetaData = rHeapInfo.MetaData();
const TMemSpyHeapObjectDataRHeap& rHeapObjectData = rHeapInfo.ObjectData();
const TMemSpyHeapStatisticsRHeap& rHeapStats = rHeapInfo.Statistics();
// Example:
//
// <ENTRY_001>
// <THREAD_NAME_001>ESock_IP</THREAD_NAME_001>
// <PROCESS_NAME_001>c32exe.exe[101f7989]0001</PROCESS_NAME_001>
// <CHUNK_NAME_001>Local-c812ba58</CHUNK_NAME_001>
// <FIELDS_001>1,0x12400000,0x00c00074,36744,4092,524288,0x00c011a4,0,13,6872,1368,1680,2584,219,40,28996,0</FIELDS_001>
// </ENTRY_001>
_LIT( KFmtTagOpenAndClose, "<%S_%04d>%S</%S_%04d>" );
_LIT( KFmtEntryId, "<%SENTRY_%04d>");
_LIT( KFmtNameThread, "THREAD_NAME");
_LIT( KFmtNameProcess, "PROCESS_NAME");
_LIT( KFmtNameChunk, "CHUNK_NAME");
_LIT( KFmtFields, "FIELDS");
_LIT( KFmtFieldContent , "<%S_%04d>%06d,0x%08x,0x%08x,%d,%d,%d,0x%08x,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d</%S_%04d>");
// <ENTRY_001>
iEngine.Sink().OutputLineFormattedL( KFmtEntryId, &KNullDesC, aIndex );
// <THREAD_NAME_001>ESock_IP</THREAD_NAME_001>
iEngine.Sink().OutputLineFormattedL( KFmtTagOpenAndClose, &KFmtNameThread, aIndex, &aThreadName, &KFmtNameThread, aIndex );
// <PROCESS_NAME_001>c32exe.exe[101f7989]0001</PROCESS_NAME_001>
iEngine.Sink().OutputLineFormattedL( KFmtTagOpenAndClose, &KFmtNameProcess, aIndex, &aProcessName, &KFmtNameProcess, aIndex );
// <CHUNK_NAME_001>Local-c812ba58</CHUNK_NAME_001>
const TPtrC pChunkName( rHeapMetaData.ChunkName() );
iEngine.Sink().OutputLineFormattedL( KFmtTagOpenAndClose, &KFmtNameChunk, aIndex, &pChunkName, &KFmtNameChunk, aIndex );
// Fields
iEngine.Sink().OutputLineFormattedL( KFmtFieldContent,
&KFmtFields,
aIndex,
aInfo.Tid(),
rHeapMetaData.ChunkHandle(),
rHeapObjectData.Base(),
rHeapObjectData.Size(),
rHeapObjectData.iMinLength,
rHeapObjectData.iMaxLength,
rHeapObjectData.iFree.next,
rHeapObjectData.iFree.len,
rHeapStats.StatsFree().TypeCount(),
rHeapStats.StatsFree().TypeSize(),
rHeapStats.StatsFree().SlackSpaceCellSize(),
rHeapStats.StatsFree().LargestCellSize(),
rHeapStats.StatsAllocated().LargestCellSize(),
rHeapObjectData.iCellCount,
rHeapObjectData.iMinCell,
rHeapObjectData.iTotalAllocSize,
rHeapMetaData.IsSharedHeap(),
&KFmtFields,
aIndex
);
// </ENTRY_001>
iEngine.Sink().OutputLineFormattedL( KFmtEntryId, &KMemSpySinkTagClose, aIndex );
}
EXPORT_C void CMemSpyEngineHelperHeap::OutputHeapInfoForDeviceL( TBool aIncludeKernel )
{
// NB: The goal here is to minimise the line length. We already look like we
// could exceed the available RDebug::Print length...
const TInt count = iEngine.Container().Count();
TInt index = 0;
//
HBufC* buf = HBufC::NewLC( 1024 );
TPtr pBuf(buf->Des());
//
_LIT( KMemSpyFolder, "Heap\\Compact" );
_LIT( KMemSpyContext, "Heap Compact" );
_LIT( KMemSpyExtension, ".log" );
iEngine.Sink().DataStreamBeginL( KMemSpyContext, KMemSpyFolder, KMemSpyExtension );
// Start marker
iEngine.Sink().OutputLineFormattedL( KMemSpyMarkerCSV, &KNullDesC );
// Set overall prefix
iEngine.Sink().OutputPrefixSetLC( KMemSpyPrefixCSV );
// Output version info
_LIT( KVersionNumber, "<VERSION>1</VERSION>" );
iEngine.Sink().OutputLineL( KVersionNumber );
// Output time stamp
_LIT( KTimeStamp, "<TIMESTAMP>%u</TIMESTAMP>" );
iEngine.Sink().OutputLineFormattedL( KTimeStamp, User::FastCounter() );
// Heap info we'll populate by calling the driver
TMemSpyHeapInfo info;
TFullName processName;
if ( aIncludeKernel )
{
// Get kernel heap info
GetHeapInfoKernelL( info );
if ( info.Type() == TMemSpyHeapInfo::ETypeRHeap )
{
TName threadName;
MemSpyEngineUtils::GetKernelHeapThreadAndProcessNames( threadName, processName );
OutputCSVEntryL( index++, info, threadName, processName );
}
}
for(TInt ii=0; ii<count; ii++)
{
const CMemSpyProcess& process = iEngine.Container().At( ii );
process.FullName( processName );
//
if ( iEngine.ProcessSuspendAndGetErrorLC( process.Id() ) == KErrNone )
{
const TInt threadCount = process.Count();
//
for(TInt j=0; j<threadCount; j++)
{
const CMemSpyThread& thread = process.At( j );
const TPtrC threadName( thread.Name() );
//
const TInt error = iEngine.Driver().GetHeapInfoUser( info, thread.Id() );
if ( error == KErrNone )
{
UpdateSharedHeapInfoL( process.Id(), thread.Id(), info );
}
if ( error == KErrNone && info.Type() == TMemSpyHeapInfo::ETypeRHeap )
{
OutputCSVEntryL( index++, info, threadName, processName );
}
}
CleanupStack::PopAndDestroy(); // ProcessSuspendLC
}
}
CleanupStack::PopAndDestroy(); // clear prefix
CleanupStack::PopAndDestroy( buf );
// End marker
iEngine.Sink().OutputLineFormattedL( KMemSpyMarkerCSV, &KMemSpySinkTagClose );
iEngine.Sink().DataStreamEndL();
}
EXPORT_C void CMemSpyEngineHelperHeap::GetHeapInfoUserL( const TProcessId& aProcess, const TThreadId& aThread, TMemSpyHeapInfo& aInfo, RArray<TMemSpyDriverFreeCell>* aFreeCells )
{
iEngine.ProcessSuspendLC( aProcess );
TRACE( RDebug::Printf( "CMemSpyEngineHelperHeap::GetHeapInfoUserL() - checksum1: 0x%08x", aInfo.AsRHeap().Statistics().StatsFree().Checksum() ) );
TInt r = KErrNone;
//
if ( aFreeCells )
{
r = iEngine.Driver().GetHeapInfoUser( aInfo, aThread, *aFreeCells );
}
else
{
r = iEngine.Driver().GetHeapInfoUser( aInfo, aThread );
}
if ( !r )
{
UpdateSharedHeapInfoL( aProcess, aThread, aInfo );
}
//
TRACE( RDebug::Printf( "CMemSpyEngineHelperHeap::GetHeapInfoUserL() - checksum2: 0x%08x", aInfo.AsRHeap().Statistics().StatsFree().Checksum() ) );
CleanupStack::PopAndDestroy(); // ProcessSuspendLC
User::LeaveIfError( r );
}
EXPORT_C void CMemSpyEngineHelperHeap::GetHeapInfoUserL( const CMemSpyProcess& aProcess, RArray<TMemSpyHeapInfo >& aInfos )
{
aInfos.Reset();
iEngine.ProcessSuspendLC( aProcess.Id() );
//
TMemSpyHeapInfo info;
//
const TInt count = aProcess.Count();
for( TInt i=0; i<count; i++ )
{
const CMemSpyThread& thread = aProcess.At( i );
//
GetHeapInfoUserL( aProcess.Id(), thread.Id(), info );
aInfos.AppendL( info );
}
//
CleanupStack::PopAndDestroy(); // ProcessSuspendLC
}
EXPORT_C void CMemSpyEngineHelperHeap::OutputHeapInfoUserL( const CMemSpyThread& aThread )
{
HBufC* threadName = aThread.FullName().AllocLC();
//
RArray<TMemSpyDriverFreeCell> freeCells;
CleanupClosePushL( freeCells );
//
TMemSpyHeapInfo info;
GetHeapInfoUserL( aThread.Process().Id(), aThread.Id(), info, &freeCells );
OutputHeapInfoL( info, *threadName, &freeCells );
//
CleanupStack::PopAndDestroy( 2, threadName ); // freecells & thread name
}
EXPORT_C void CMemSpyEngineHelperHeap::GetHeapInfoKernelL( TMemSpyHeapInfo& aInfo, RArray<TMemSpyDriverFreeCell>* aFreeCells )
{
TInt error = KErrNone;
//
if ( aFreeCells )
{
error = iEngine.Driver().GetHeapInfoKernel( aInfo, *aFreeCells );
}
else
{
error = iEngine.Driver().GetHeapInfoKernel( aInfo );
}
//
User::LeaveIfError( error );
}
EXPORT_C void CMemSpyEngineHelperHeap::OutputHeapInfoKernelL()
{
// Get thread name
TFullName threadName;
MemSpyEngineUtils::GetKernelHeapThreadName( threadName );
// Free cells
RArray<TMemSpyDriverFreeCell> freeCells;
CleanupClosePushL( freeCells );
// Get info
TMemSpyHeapInfo info;
GetHeapInfoKernelL( info, &freeCells );
// Ouput
OutputHeapInfoL( info, threadName, &freeCells );
CleanupStack::PopAndDestroy( &freeCells );
}
EXPORT_C void CMemSpyEngineHelperHeap::OutputHeapDataKernelL()
{
OutputHeapDataKernelL( KMemSpyHeapDumpCreateOwnDataStream );
}
void CMemSpyEngineHelperHeap::OutputHeapDataKernelL( TBool aCreateDataStream )
{
// Get thread name
TFullName threadName;
MemSpyEngineUtils::GetKernelHeapThreadName( threadName, EFalse );
// Begin a new data stream
if ( aCreateDataStream )
{
_LIT( KMemSpyFolder, "Heap\\Data" );
_LIT( KMemSpyContext, "Heap Data - %S" );
HBufC* context = HBufC::NewLC( KMaxFileName );
TPtr pContext( context->Des() );
pContext.Format( KMemSpyContext, &threadName );
iEngine.Sink().DataStreamBeginL( pContext, KMemSpyFolder );
CleanupStack::PopAndDestroy( context );
}
RArray<TMemSpyDriverFreeCell> freeCells;
CleanupClosePushL( freeCells );
// Get kernel data and heap info before outputting anything...
TMemSpyHeapInfo info;
HBufC8* data = iEngine.Driver().GetHeapDataKernelLC( info, freeCells );
// Start marker
iEngine.Sink().OutputLineFormattedL( KMemSpyMarkerHeapData, &KNullDesC, info.Tid() );
// Set overall prefix
iEngine.Sink().OutputPrefixSetFormattedLC( KMemSpyPrefixHeapData, &threadName );
// Info section
OutputHeapInfoL( info, threadName, &freeCells );
// Dump section
_LIT(KHeaderDump, "Heap Data");
iEngine.Sink().OutputSectionHeadingL( KHeaderDump, '-' );
_LIT(KHeapDumpDataFormat, "%S");
const TUint8* heapBaseAddress = info.AsRHeap().ObjectData().Base();
iEngine.Sink().OutputBinaryDataL( KHeapDumpDataFormat, data->Ptr(), heapBaseAddress, data->Length() );
CleanupStack::PopAndDestroy(); // clear prefix
CleanupStack::PopAndDestroy( data );
CleanupStack::PopAndDestroy( &freeCells );
// End marker
iEngine.Sink().OutputLineFormattedL( KMemSpyMarkerHeapData, &KMemSpySinkTagClose, info.Tid() );
if ( aCreateDataStream )
{
iEngine.Sink().DataStreamEndL();
}
}
EXPORT_C CMemSpyEngineOutputList* CMemSpyEngineHelperHeap::NewHeapSummaryShortLC( const TMemSpyHeapInfo& aInfo )
{
CMemSpyEngineOutputList* list = CMemSpyEngineOutputList::NewLC( iEngine.Sink() );
// Heap type
_LIT( KItem0, "Heap type" );
if ( aInfo.Type() == TMemSpyHeapInfo::ETypeUnknown )
{
_LIT( KItem0_Type_Unknown, "Unknown" );
list->AddItemL( KItem0, KItem0_Type_Unknown );
}
else if ( aInfo.Type() == TMemSpyHeapInfo::ETypeRHeap )
{
const TMemSpyHeapInfoRHeap& rHeap = aInfo.AsRHeap();
const TMemSpyHeapMetaDataRHeap& metaData = rHeap.MetaData();
const TMemSpyHeapObjectDataRHeap& objectData = rHeap.ObjectData();
const TMemSpyHeapStatisticsRHeap& statistics = rHeap.Statistics();
_LIT( KItem0_Type_RHeap, "RHeap" );
list->AddItemL( KItem0, KItem0_Type_RHeap );
// Heap size is the size of the heap minus the size of the embedded (in-place) RHeap.
_LIT( KItem1, "Heap size" );
list->AddItemL( KItem1, objectData.Size() );
_LIT( KItem8b, "Heap base address" );
list->AddItemHexL( KItem8b, (TUint) objectData.Base() );
_LIT( KItem1b, "Shared" );
list->AddItemYesNoL( KItem1b, metaData.IsSharedHeap() );
// This is the size (rounded to the page) of memory associated with
// the underlying heap chunk
_LIT( KItem2, "Chunk size" );
list->AddItemL( KItem2, metaData.ChunkSize() );
_LIT( KItem3, "Alloc. count" );
list->AddItemL( KItem3, statistics.StatsAllocated().TypeCount() );
_LIT( KItem4, "Free. count" );
list->AddItemL( KItem4, statistics.StatsFree().TypeCount() );
_LIT( KItem5, "Biggest alloc." );
list->AddItemL( KItem5, statistics.StatsAllocated().LargestCellSize() );
_LIT( KItem6, "Biggest free" );
list->AddItemL( KItem6, statistics.StatsFree().LargestCellSize() );
_LIT( KItem6a, "Total alloc." );
list->AddItemL( KItem6a, statistics.StatsAllocated().TypeSize() );
_LIT( KItem6b, "Total free" );
list->AddItemL( KItem6b, statistics.StatsFree().TypeSize() );
// Slack is the free space at the end of the heap
_LIT( KItem7, "Slack free space" );
list->AddItemL( KItem7, statistics.StatsFree().SlackSpaceCellSize() );
// Fragmentation is a measurement of free space scattered throughout the heap, but ignoring
// any slack space at the end (which can often be recovered, to the granularity of one page of ram)
_LIT( KItem8a, "Fragmentation" );
list->AddItemPercentageL( KItem8a, objectData.Size(), ( statistics.StatsFree().TypeSize() - statistics.StatsFree().SlackSpaceCellSize() ) );
_LIT( KItem13, "Header size (A)" );
list->AddItemL( KItem13, metaData.HeaderSizeAllocated() );
_LIT( KItem14, "Header size (F)" );
list->AddItemL( KItem14, metaData.HeaderSizeFree() );
_LIT( KItem9a, "Overhead (alloc)" );
const TInt allocOverhead = metaData.HeaderSizeAllocated() * statistics.StatsAllocated().TypeCount();
list->AddItemL( KItem9a, allocOverhead );
_LIT( KItem9b, "Overhead (free)" );
const TInt freeOverhead = metaData.HeaderSizeFree() * statistics.StatsFree().TypeCount();
list->AddItemL( KItem9b, freeOverhead );
_LIT( KItem9c, "Overhead (total)" );
const TInt totalOverhead = freeOverhead + allocOverhead;
list->AddItemL( KItem9c, totalOverhead );
_LIT( KItem9d, "Overhead" );
list->AddItemPercentageL( KItem9d, objectData.Size(), totalOverhead );
_LIT( KItem10, "Min. length" );
list->AddItemL( KItem10, objectData.iMinLength );
_LIT( KItem11, "Max. length" );
list->AddItemL( KItem11, objectData.iMaxLength );
_LIT( KItem12, "Debug Allocator Library" );
list->AddItemYesNoL( KItem12, metaData.IsDebugAllocator() );
}
return list;
}
EXPORT_C CMemSpyEngineOutputList* CMemSpyEngineHelperHeap::NewHeapSummaryExtendedLC( const TMemSpyHeapInfo& aInfo, const RArray<TMemSpyDriverFreeCell>* aFreeCells )
{
CMemSpyEngineOutputList* list = CMemSpyEngineOutputList::NewLC( iEngine.Sink() );
//
AppendMetaDataL( aInfo, *list );
AppendObjectDataL( aInfo, *list );
AppendStatisticsL( aInfo, *list );
//
if ( aFreeCells )
{
AppendFreeCellsL( *aFreeCells, *list );
}
//
return list;
}
//cigasto: not formatted - raw heap info
EXPORT_C TMemSpyHeapData CMemSpyEngineHelperHeap::NewHeapRawInfo( const TMemSpyHeapInfo& aInfo )
{
TMemSpyHeapData list;
// Heap type
if ( aInfo.Type() == TMemSpyHeapInfo::ETypeUnknown )
{
_LIT( KItem0_Type_Unknown, "Unknown" );
list.iType.Append( KItem0_Type_Unknown );
}
else if ( aInfo.Type() == TMemSpyHeapInfo::ETypeRHeap )
{
const TMemSpyHeapInfoRHeap& rHeap = aInfo.AsRHeap();
const TMemSpyHeapMetaDataRHeap& metaData = rHeap.MetaData();
const TMemSpyHeapObjectDataRHeap& objectData = rHeap.ObjectData();
const TMemSpyHeapStatisticsRHeap& statistics = rHeap.Statistics();
_LIT( KItem0_Type_RHeap, "RHeap" );
list.iType.Append( KItem0_Type_RHeap );
// Heap size is the size of the heap minus the size of the embedded (in-place) RHeap.
list.iSize = objectData.Size();
list.iBaseAddress = (TUint) objectData.Base();
list.iShared = metaData.IsSharedHeap();
list.iChunkSize = metaData.ChunkSize();
list.iAllocationsCount = statistics.StatsAllocated().TypeCount();
list.iFreeCount = statistics.StatsFree().TypeCount();
list.iBiggestAllocation = statistics.StatsAllocated().LargestCellSize();
list.iBiggestFree = statistics.StatsFree().LargestCellSize();
list.iTotalAllocations = statistics.StatsAllocated().TypeSize();
list.iTotalFree = statistics.StatsFree().TypeSize();
list.iSlackFreeSpace = statistics.StatsFree().SlackSpaceCellSize();
list.iFragmentation = statistics.StatsFree().TypeSize() - statistics.StatsFree().SlackSpaceCellSize(); //to calculate percentage value use iSize as 100% value
list.iHeaderSizeA = metaData.HeaderSizeAllocated();
list.iHeaderSizeF = metaData.HeaderSizeFree();
TInt allocOverhead = metaData.HeaderSizeAllocated() * statistics.StatsAllocated().TypeCount();
list.iAllocationOverhead = allocOverhead;
TInt freeOverhead = metaData.HeaderSizeFree() * statistics.StatsFree().TypeCount();
list.iFreeOverhead = freeOverhead;
list.iTotalOverhead = freeOverhead + allocOverhead;
list.iOverhead = freeOverhead + allocOverhead; //to calculate percentage value use iSize as 100% value
list.iMinLength = objectData.iMinLength;
list.iMaxLength = objectData.iMaxLength;
list.iDebugAllocatorLibrary = metaData.IsDebugAllocator();
}
return list;
}
TUint CMemSpyEngineHelperHeap::DescriptorAsDWORD( const TDesC8& aItem)
{
__ASSERT_ALWAYS( aItem.Length() >= 4, User::Invariant() );
const TUint ret = aItem[0] +
(aItem[1] << 8) +
(aItem[2] << 16) +
(aItem[3] << 24);
return ret;
}
void CMemSpyEngineHelperHeap::AppendMetaDataL( const TMemSpyHeapInfo& aInfo, CMemSpyEngineOutputList& aList )
{
const TMemSpyHeapInfoRHeap& rHeap = aInfo.AsRHeap();
// Make caption
_LIT( KOverallCaption1, "Meta Data" );
aList.AddItemL( KOverallCaption1 );
aList.AddUnderlineForPreviousItemL( '=', 0 );
// Type
_LIT( KMetaData_Type, "Type:" );
if ( aInfo.Type() != TMemSpyHeapInfo::ETypeRHeap )
{
_LIT( KMetaData_Type_Unknown, "Unknown" );
aList.AddItemL( KMetaData_Type, KMetaData_Type_Unknown );
}
else
{
const TMemSpyHeapMetaDataRHeap& metaData = rHeap.MetaData();
// Type
_LIT( KMetaData_Type_RHeap, "Symbian OS RHeap" );
aList.AddItemL( KMetaData_Type, KMetaData_Type_RHeap );
// VTable
_LIT( KMetaData_VTable, "VTable:" );
aList.AddItemHexL( KMetaData_VTable, metaData.VTable() );
// Object size
_LIT( KMetaData_ObjectSize, "Object Size:" );
aList.AddItemL( KMetaData_ObjectSize, metaData.ClassSize() );
// Chunk name
_LIT( KMetaData_ChunkName, "Chunk Name:" );
TPtrC pChunkName( metaData.ChunkName() );
aList.AddItemL( KMetaData_ChunkName, pChunkName );
// Chunk size
_LIT( KMetaData_ChunkSize, "Chunk Size:" );
aList.AddItemL( KMetaData_ChunkSize, metaData.ChunkSize() );
// Chunk base address
_LIT( KMetaData_ChunkBaseAddress, "Chunk Base Address:" );
aList.AddItemL( KMetaData_ChunkBaseAddress, metaData.ChunkBaseAddress() );
// Debug allocator
_LIT( KMetaData_DebugAllocator, "Debug Allocator:" );
aList.AddItemYesNoL( KMetaData_DebugAllocator, metaData.IsDebugAllocator() );
// Cell header overhead (free cells)
_LIT( KMetaData_CellHeaderOverheadFree, "Overhead (Free):" );
aList.AddItemL( KMetaData_CellHeaderOverheadFree, metaData.HeaderSizeFree() );
// Cell header overhead (allocated cells)
_LIT( KMetaData_CellHeaderOverheadAlloc, "Overhead (Alloc):" );
aList.AddItemL( KMetaData_CellHeaderOverheadAlloc, metaData.HeaderSizeAllocated() );
// Shared Heap
_LIT( KMetaData_Shared, "Shared:" );
aList.AddItemYesNoL( KMetaData_Shared, metaData.IsSharedHeap() );
// Add ROM info
iEngine.HelperROM().AddInfoL( aList );
}
aList.AddBlankItemL( 1 );
}
void CMemSpyEngineHelperHeap::AppendObjectDataL( const TMemSpyHeapInfo& aInfo, CMemSpyEngineOutputList& aList )
{
if ( aInfo.Type() == TMemSpyHeapInfo::ETypeRHeap )
{
const TMemSpyHeapInfoRHeap& rHeap = aInfo.AsRHeap();
const TMemSpyHeapObjectDataRHeap& objectData = rHeap.ObjectData();
// Make caption
_LIT( KOverallCaption1, "RAllocator" );
aList.AddItemL( KOverallCaption1 );
aList.AddUnderlineForPreviousItemL( '=', 0 );
// RAllocator
_LIT( KObjectData_RAllocator_iAccessCount, "RAllocator::iAccessCount" );
aList.AddItemL( KObjectData_RAllocator_iAccessCount, objectData.iAccessCount );
_LIT( KObjectData_RAllocator_iHandleCount, "RAllocator::iHandleCount" );
aList.AddItemL( KObjectData_RAllocator_iHandleCount, objectData.iHandleCount );
_LIT( KObjectData_RAllocator_iHandles, "RAllocator::iHandles" );
aList.AddItemL( KObjectData_RAllocator_iHandles, objectData.iHandles );
_LIT( KObjectData_RAllocator_iFlags, "RAllocator::iFlags" );
aList.AddItemHexL( KObjectData_RAllocator_iFlags, objectData.iFlags );
_LIT( KObjectData_RAllocator_iCellCount, "RAllocator::iCellCount" );
aList.AddItemL( KObjectData_RAllocator_iCellCount, objectData.iCellCount );
_LIT( KObjectData_RAllocator_iTotalAllocSize, "RAllocator::iTotalAllocSize" );
aList.AddItemL( KObjectData_RAllocator_iTotalAllocSize, objectData.iTotalAllocSize );
aList.AddBlankItemL( 1 );
// Make caption
_LIT( KOverallCaption2, "RHeap" );
aList.AddItemL( KOverallCaption2 );
aList.AddUnderlineForPreviousItemL( '=', 0 );
// RHeap
_LIT( KObjectData_RHeap_iMinLength, "RHeap::iMinLength" );
aList.AddItemL( KObjectData_RHeap_iMinLength, objectData.iMinLength );
_LIT( KObjectData_RHeap_iMaxLength, "RHeap::iMaxLength" );
aList.AddItemL( KObjectData_RHeap_iMaxLength, objectData.iMaxLength );
_LIT( KObjectData_RHeap_iOffset, "RHeap::iOffset" );
aList.AddItemL( KObjectData_RHeap_iOffset, objectData.iOffset );
_LIT( KObjectData_RHeap_iGrowBy, "RHeap::iGrowBy" );
aList.AddItemL( KObjectData_RHeap_iGrowBy, objectData.iGrowBy );
_LIT( KObjectData_RHeap_iChunkHandle, "RHeap::iChunkHandle" );
aList.AddItemHexL( KObjectData_RHeap_iChunkHandle, objectData.iChunkHandle );
_LIT( KObjectData_RHeap_iBase, "RHeap::iBase" );
aList.AddItemL( KObjectData_RHeap_iBase, objectData.iBase );
_LIT( KObjectData_RHeap_iTop, "RHeap::iTop" );
aList.AddItemL( KObjectData_RHeap_iTop, objectData.iTop );
_LIT( KObjectData_RHeap_iAlign, "RHeap::iAlign" );
aList.AddItemL( KObjectData_RHeap_iAlign, objectData.iAlign );
_LIT( KObjectData_RHeap_iMinCell, "RHeap::iMinCell" );
aList.AddItemL( KObjectData_RHeap_iMinCell, objectData.iMinCell );
_LIT( KObjectData_RHeap_iPageSize, "RHeap::iPageSize" );
aList.AddItemL( KObjectData_RHeap_iPageSize, objectData.iPageSize );
_LIT( KObjectData_RHeap_iFree_next, "RHeap::iFree.next" );
aList.AddItemL( KObjectData_RHeap_iFree_next, objectData.iFree.next );
_LIT( KObjectData_RHeap_iFree_len, "RHeap::iFree.len" );
aList.AddItemL( KObjectData_RHeap_iFree_len, objectData.iFree.len );
_LIT( KObjectData_RHeap_iNestingLevel, "RHeap::iNestingLevel" );
aList.AddItemL( KObjectData_RHeap_iNestingLevel, objectData.iNestingLevel );
_LIT( KObjectData_RHeap_iAllocCount, "RHeap::iAllocCount" );
aList.AddItemL( KObjectData_RHeap_iAllocCount, objectData.iAllocCount );
_LIT( KObjectData_RHeap_iFailType, "RHeap::iFailType" );
aList.AddItemL( KObjectData_RHeap_iFailType, (TInt) objectData.iFailType );
_LIT( KObjectData_RHeap_iFailRate, "RHeap::iFailRate" );
aList.AddItemL( KObjectData_RHeap_iFailRate, objectData.iFailRate );
_LIT( KObjectData_RHeap_iFailed, "RHeap::iFailed" );
aList.AddItemTrueFalseL( KObjectData_RHeap_iFailed, objectData.iFailed );
_LIT( KObjectData_RHeap_iFailAllocCount, "RHeap::iFailAllocCount" );
aList.AddItemL( KObjectData_RHeap_iFailAllocCount, objectData.iFailAllocCount );
_LIT( KObjectData_RHeap_iRand, "RHeap::iRand" );
aList.AddItemL( KObjectData_RHeap_iRand, objectData.iRand );
_LIT( KObjectData_RHeap_iTestData, "RHeap::iTestData" );
aList.AddItemL( KObjectData_RHeap_iTestData, objectData.iTestData );
aList.AddBlankItemL( 1 );
}
}
void CMemSpyEngineHelperHeap::AppendStatisticsL( const TMemSpyHeapInfo& aInfo, CMemSpyEngineOutputList& aList )
{
if ( aInfo.Type() == TMemSpyHeapInfo::ETypeRHeap )
{
const TMemSpyHeapInfoRHeap& rHeap = aInfo.AsRHeap();
const TMemSpyHeapStatisticsRHeap& rHeapStats = rHeap.Statistics();
// Shared captions
_LIT( KStatsData_CellCount, "Number of cells:" );
_LIT( KStatsData_CellSize, "Size of cells:" );
_LIT( KStatsData_LargestCellAddress, "Largest cell:" );
_LIT( KStatsData_LargestCellSize, "Largest cell size:" );
// Free space
_LIT( KOverallCaption1, "Free Cell Statistics" );
aList.AddItemL( KOverallCaption1 );
aList.AddUnderlineForPreviousItemL( '=', 0 );
aList.AddItemL( KStatsData_CellCount, rHeapStats.StatsFree().TypeCount() );
aList.AddItemL( KStatsData_CellSize, rHeapStats.StatsFree().TypeSize() );
aList.AddItemL( KStatsData_LargestCellAddress, rHeapStats.StatsFree().LargestCellAddress() );
aList.AddItemL( KStatsData_LargestCellSize, rHeapStats.StatsFree().LargestCellSize() );
_LIT( KStatsData_Free_SlackCellAddress, "Slack:" );
aList.AddItemL( KStatsData_Free_SlackCellAddress, rHeapStats.StatsFree().SlackSpaceCellAddress() );
_LIT( KStatsData_Free_SlackCellSize, "Slack size:" );
aList.AddItemL( KStatsData_Free_SlackCellSize, rHeapStats.StatsFree().SlackSpaceCellSize() );
_LIT( KStatsData_Free_Checksum, "Checksum:" );
aList.AddItemHexL( KStatsData_Free_Checksum, rHeapStats.StatsFree().Checksum() );
aList.AddBlankItemL( 1 );
// Allocated space
_LIT( KOverallCaption2, "Allocated Cell Statistics" );
aList.AddItemL( KOverallCaption2 );
aList.AddUnderlineForPreviousItemL( '=', 0 );
aList.AddItemL( KStatsData_CellCount, rHeapStats.StatsAllocated().TypeCount() );
aList.AddItemL( KStatsData_CellSize, rHeapStats.StatsAllocated().TypeSize() );
aList.AddItemL( KStatsData_LargestCellAddress, rHeapStats.StatsAllocated().LargestCellAddress() );
aList.AddItemL( KStatsData_LargestCellSize, rHeapStats.StatsAllocated().LargestCellSize() );
aList.AddBlankItemL( 1 );
// Common
_LIT( KOverallCaption3, "Common Statistics" );
aList.AddItemL( KOverallCaption3 );
aList.AddUnderlineForPreviousItemL( '=', 0 );
_LIT( KStatsData_Common_TotalCellCount, "Total cell count:" );
aList.AddItemL( KStatsData_Common_TotalCellCount, rHeapStats.StatsCommon().TotalCellCount() );
_LIT( KStatsData_Common_TotalSize, "Total cell size:" );
aList.AddItemL( KStatsData_Common_TotalSize, rHeapStats.StatsAllocated().TypeSize() + rHeapStats.StatsFree().TypeSize() );
aList.AddBlankItemL( 1 );
}
}
void CMemSpyEngineHelperHeap::AppendFreeCellsL( const RArray<TMemSpyDriverFreeCell>& aFreeCells, CMemSpyEngineOutputList& aList )
{
// Free space
_LIT( KOverallCaption1, "Free Cell List" );
aList.AddItemL( KOverallCaption1 );
aList.AddUnderlineForPreviousItemL( '=', 0 );
TBuf<128> caption;
_LIT( KCaptionFormat, "FC %04d" );
_LIT( KValueFormat, "0x%08x %8d %1d" );
const TInt count = aFreeCells.Count();
for( TInt i=0; i<count; i++ )
{
const TMemSpyDriverFreeCell& cell = aFreeCells[ i ];
caption.Format( KCaptionFormat, i + 1 );
aList.AddItemFormatL( caption, KValueFormat, cell.iAddress, cell.iLength, cell.iType );
}
}
void CMemSpyEngineHelperHeap::UpdateSharedHeapInfoL( const TProcessId& aProcess, const TThreadId& aThread, TMemSpyHeapInfo& aInfo )
{
RArray<TThreadId> threads;
CleanupClosePushL( threads );
iEngine.Driver().GetThreadsL( aProcess, threads );
TMemSpyHeapInfo otherHeap;
TThreadId otherThreadId;
TInt r( KErrNone );
for ( TInt i = 0; i < threads.Count(); i++ )
{
otherThreadId = threads[i];
if ( aThread != otherThreadId ) // skip current thread
{
r = iEngine.Driver().GetHeapInfoUser( otherHeap, otherThreadId );
if ( !r && otherHeap.AsRHeap().MetaData().ChunkHandle() == aInfo.AsRHeap().MetaData().ChunkHandle() )
{
TRACE( RDebug::Printf( "CMemSpyEngineHelperHeap::UpdateSharedHeapInfoL - shared heap detected chunkhandle: 0x%08x", aInfo.AsRHeap().MetaData().ChunkHandle() ) );
aInfo.AsRHeap().MetaData().SetSharedHeap( ETrue );
break;
}
}
}
CleanupStack::PopAndDestroy( &threads );
}