/*
* 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 "MemSpyDriverLogChanHeapWalk.h"
// System includes
#include <u32hal.h>
#include <e32rom.h>
#include <memspy/driver/memspydriverconstants.h>
#include <memspy/driver/memspydriverobjectsshared.h>
// Shared includes
#include "MemSpyDriverOpCodes.h"
#include "MemSpyDriverObjectsInternal.h"
// User includes
#include "MemSpyDriverHeap.h"
#include "MemSpyDriverUtils.h"
#include "MemSpyDriverDevice.h"
#include "MemSpyDriverOSAdaption.h"
#include "MemSpyDriverSuspensionManager.h"
DMemSpyDriverLogChanHeapWalk::DMemSpyDriverLogChanHeapWalk( DMemSpyDriverDevice& aDevice, DThread& aThread )
: DMemSpyDriverLogChanHeapBase( aDevice, aThread ), iWalkHeap( aDevice.OSAdaption() )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::DMemSpyDriverLogChanHeapWalk() - this: 0x%08x", this ));
}
DMemSpyDriverLogChanHeapWalk::~DMemSpyDriverLogChanHeapWalk()
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::~DMemSpyDriverLogChanHeapWalk() - START - this: 0x%08x", this ));
NKern::ThreadEnterCS();
WalkHeapClose();
NKern::ThreadLeaveCS();
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::~DMemSpyDriverLogChanHeapWalk() - END - this: 0x%08x", this ));
}
TInt DMemSpyDriverLogChanHeapWalk::Request( TInt aFunction, TAny* a1, TAny* a2 )
{
TInt r = DMemSpyDriverLogChanBase::Request( aFunction, a1, a2 );
if ( r == KErrNone )
{
switch( aFunction )
{
case EMemSpyDriverOpCodeWalkHeapInit:
r = WalkHeapInit( (TMemSpyDriverInternalWalkHeapParamsInit*) a1 );
break;
case EMemSpyDriverOpCodeWalkHeapGetCellInfo:
r = WalkHeapGetCellInfo( (TAny*) a1, (TMemSpyDriverInternalWalkHeapParamsCell*) a2 );
break;
case EMemSpyDriverOpCodeWalkHeapReadCellData:
r = WalkHeapReadCellData( (TMemSpyDriverInternalWalkHeapCellDataReadParams*) a1 );
break;
case EMemSpyDriverOpCodeWalkHeapNextCell:
r = WalkHeapNextCell( (TUint) a1, (TMemSpyDriverInternalWalkHeapParamsCell*) a2 );
break;
case EMemSpyDriverOpCodeWalkHeapClose:
r = WalkHeapClose();
break;
default:
r = KErrNotSupported;
break;
}
}
//
return r;
}
TBool DMemSpyDriverLogChanHeapWalk::IsHandler( TInt aFunction ) const
{
return ( aFunction > EMemSpyDriverOpCodeWalkHeapBase && aFunction < EMemSpyDriverOpCodeWalkHeapEnd );
}
TInt DMemSpyDriverLogChanHeapWalk::WalkHeapInit( TMemSpyDriverInternalWalkHeapParamsInit* aParams )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit() - START"));
__ASSERT_ALWAYS( !iHeapWalkInitialised && iWalkHeap.ChunkIsInitialised() == EFalse, MemSpyDriverUtils::PanicThread( ClientThread(), EPanicHeapWalkPending ) );
TInt r = Kern::ThreadRawRead( &ClientThread(), aParams, &iHeapWalkInitialParameters, sizeof(TMemSpyDriverInternalWalkHeapParamsInit) );
if ( r == KErrNone )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit - thread id: %d, vtable: 0x%08x, debugAllocator: %d", iHeapWalkInitialParameters.iTid, iHeapWalkInitialParameters.iRHeapVTable, iHeapWalkInitialParameters.iDebugAllocator));
r = OpenTempObject( iHeapWalkInitialParameters.iTid, EThread );
if ( r == KErrNone )
{
// Find the chunk with the correct handle
DThread* thread = (DThread*) TempObject();
if ( SuspensionManager().IsSuspended( *thread ) )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit - thread: %O", thread));
// Open client's heap
DChunk* userHeapChunk = NULL;
r = OpenUserHeap( *thread, iHeapWalkInitialParameters.iRHeapVTable, iWalkHeap, userHeapChunk );
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit - opening client heap returned: %d", r) );
if ( r == KErrNone )
{
// Indicates that we've initiated a walk - so we can tell whether to close
// the chunk later on.
iHeapWalkInitialised = ETrue;
iWalkHeapCellIndex = 0;
// Walk the client's heap
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit - calling heap walker constructor..."));
RMemSpyDriverHeapWalker heapWalker( iWalkHeap, iHeapWalkInitialParameters.iDebugAllocator );
TMemSpyDriverLogChanHeapWalkObserver observer( *this );
heapWalker.SetObserver( &observer );
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit - starting traversal..."));
r = heapWalker.Traverse();
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit - finished traversal - err: %d", r));
}
// If the initialise process didn't complete successfully, then we must be sure
// to release the associated heap chunk
if ( r < KErrNone )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit - error scenario - releasing kernel heap chunk copy" ));
iWalkHeap.DisassociateWithKernelChunk();
}
}
else
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit - parent process not suspended => KErrAccessDenied"));
r = KErrAccessDenied;
}
CloseTempObject();
}
else
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit - thread not found"));
}
}
else
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit - params read error: %d", r));
}
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapInit() - END - ret: %d", r));
return r;
}
TInt DMemSpyDriverLogChanHeapWalk::WalkHeapNextCell( TUint aTid, TMemSpyDriverInternalWalkHeapParamsCell* aParams )
{
const TInt walkedHeapCellCount = iWalkHeapCells.Count();
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapNextCell() - START - current cell count: %d", walkedHeapCellCount));
__ASSERT_ALWAYS( iHeapWalkInitialised && iWalkHeap.ChunkIsInitialised(), MemSpyDriverUtils::PanicThread( ClientThread(), EPanicHeapWalkNotInitialised ) );
// Open the original thread
TInt r = OpenTempObject( aTid, EThread );
if ( r == KErrNone )
{
// Get the thread handle and that we have suspended the process' threads
DThread* thread = (DThread*) TempObject();
if ( SuspensionManager().IsSuspended( *thread ) )
{
NKern::ThreadEnterCS();
if ( walkedHeapCellCount > 0 && iWalkHeapCellIndex >= 0 && iWalkHeapCellIndex < walkedHeapCellCount )
{
// Write back head cell to user-space
TMemSpyDriverInternalWalkHeapParamsCell cell( iWalkHeapCells[ iWalkHeapCellIndex++ ] );
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapNextCell - returning... cellType: %1d, addr: 0x%08x, len: %8d, nestingLev: %8d, allocNum: %8d", cell.iCellType, cell.iCellAddress, cell.iLength, cell.iNestingLevel, cell.iAllocNumber ));
r = Kern::ThreadRawWrite( &ClientThread(), aParams, &cell, sizeof(TMemSpyDriverInternalWalkHeapParamsCell) );
if ( r != KErrNone )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapNextCell - params read error: %d", r));
}
}
else
{
r = KErrEof;
}
NKern::ThreadLeaveCS();
}
else
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapNextCell - parent process not suspended => KErrAccessDenied"));
r = KErrAccessDenied;
}
CloseTempObject();
}
else
{
Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapNextCell - thread not found");
}
//
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapNextCell() - END - ret: %d", r));
return r;
}
TInt DMemSpyDriverLogChanHeapWalk::WalkHeapClose()
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapClose() - START"));
//
if ( iHeapWalkInitialised )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapClose - heap walk was still open..."));
NKern::ThreadEnterCS();
if ( iWalkHeap.ChunkIsInitialised() )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapClose - removing chunk (%O) from process", &iWalkHeap.Chunk() ) );
iWalkHeap.DisassociateWithKernelChunk();
iWalkHeap.Reset();
}
// Discard handled cells
iWalkHeapCells.Reset();
iHeapWalkInitialised = EFalse;
NKern::ThreadLeaveCS();
}
//
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapClose() - END"));
return KErrNone;
}
TInt DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData(TMemSpyDriverInternalWalkHeapCellDataReadParams* aParams)
{
__ASSERT_ALWAYS( iHeapWalkInitialised && iWalkHeap.ChunkIsInitialised(), MemSpyDriverUtils::PanicThread( ClientThread(), EPanicHeapWalkNotInitialised ) );
//
const TBool debugEUser = iHeapWalkInitialParameters.iDebugAllocator;
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData() - START - thread id: %d, vtable: 0x%08x, debugAllocator: %d", iHeapWalkInitialParameters.iTid, iHeapWalkInitialParameters.iRHeapVTable, debugEUser));
//
TMemSpyDriverInternalWalkHeapCellDataReadParams params;
TInt r = Kern::ThreadRawRead( &ClientThread(), aParams, ¶ms, sizeof(TMemSpyDriverInternalWalkHeapCellDataReadParams) );
if ( r != KErrNone )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData() - END - params read error: %d", r));
return r;
}
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - cell: 0x%08x, readLen: %8d, writeAddr: 0x%08x", params.iCellAddress, params.iReadLen, params.iDes));
// Open the original thread
r = OpenTempObject( iHeapWalkInitialParameters.iTid, EThread );
if ( r == KErrNone )
{
// Get the thread handle
DThread* thread = (DThread*) TempObject();
// Check the threads in the process are suspended
if ( SuspensionManager().IsSuspended( *thread ) )
{
// Check we can find the cell in the cell list...
const TMemSpyDriverInternalWalkHeapParamsCell* cell = CellInfoForSpecificAddress( params.iCellAddress );
if ( cell == NULL )
{
// Maybe the client tried the base address of the cell data.
// try to take the header into account and retry.
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - didnt find matching cell for address: 0x%08x... trying address minus allocatedCellHeaderSize", params.iCellAddress ));
const TUint32 cellHeaderSize = (TUint32) RMemSpyDriverRHeapBase::AllocatedCellHeaderSize( debugEUser );
TUint32 addr = (TUint32) params.iCellAddress;
addr -= cellHeaderSize;
params.iCellAddress = (TAny*) addr;
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - new address: 0x%08x", params.iCellAddress ));
// Last try
cell = CellInfoForSpecificAddress( params.iCellAddress );
}
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - cell: 0x%08x for address: 0x%08x", cell, params.iCellAddress ));
if ( cell )
{
const TBool isValidCell = iWalkHeap.CheckCell( cell->iCellAddress, cell->iLength );
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - isValidCell: %d", isValidCell ));
if ( isValidCell )
{
// Check the length request is valid
const TInt cellLen = cell->iLength;
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - cellLen: %d", cellLen ));
if ( params.iReadLen <= cellLen )
{
const TInt cellHeaderSize = RMemSpyDriverRHeapBase::CellHeaderSize( *cell, debugEUser );
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - cellHeaderSize: %8d", cellHeaderSize ));
// Get user side descriptor length info
TInt destLen = 0;
TInt destMax = 0;
TUint8* destPtr = NULL;
r = Kern::ThreadGetDesInfo( &ClientThread(), params.iDes, destLen, destMax, destPtr, ETrue );
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - user side descriptor: 0x%08x (0x%08x), len: %8d, maxLen: %8d, r: %d", params.iDes, destPtr, destLen, destMax, r ));
// Work out the start offset for the data...
if ( r == KErrNone && destMax >= params.iReadLen )
{
const TAny* srcPos = ((TUint8*) cell->iCellAddress) + cellHeaderSize;
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - srcPos: 0x%08x", srcPos ));
// Read some data
r = Kern::ThreadRawRead( thread, srcPos, destPtr, params.iReadLen );
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - read from thread returned: %d", r));
if ( r == KErrNone )
{
// Client will update descriptor length in this situation.
r = params.iReadLen;
}
else if ( r == KErrBadDescriptor )
{
MemSpyDriverUtils::PanicThread( ClientThread(), EPanicBadDescriptor );
}
}
else
{
if ( r != KErrBadDescriptor )
{
r = KErrArgument;
Kern::Printf( "DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - error - user-descriptor isnt big enough for requested data" );
}
else
{
Kern::Printf( "DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - error - bad or non-writable user-side descriptor" );
}
}
}
else
{
r = KErrArgument;
Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - error - read length is bigger than cell length");
}
}
else
{
r = KErrArgument;
Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - invalid cell address: 0x%08x", cell);
}
}
else
{
r = KErrArgument;
Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - no cell at user supplied address!");
}
}
else
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - parent process not suspended => KErrAccessDenied"));
r = KErrAccessDenied;
}
CloseTempObject();
}
else
{
Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData - thread not found");
}
//
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapReadCellData() - END"));
return r;
}
TInt DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo( TAny* aCellAddress, TMemSpyDriverInternalWalkHeapParamsCell* aParams )
{
__ASSERT_ALWAYS( iHeapWalkInitialised && iWalkHeap.ChunkIsInitialised(), MemSpyDriverUtils::PanicThread( ClientThread(), EPanicHeapWalkNotInitialised ) );
//
const TBool debugEUser = iHeapWalkInitialParameters.iDebugAllocator;
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo() - START - thread id: %d, vtable: 0x%08x, debugAllocator: %d", iHeapWalkInitialParameters.iTid, iHeapWalkInitialParameters.iRHeapVTable, debugEUser));
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo - cell: 0x%08x", aCellAddress));
// Open the original thread
TInt r = OpenTempObject( iHeapWalkInitialParameters.iTid, EThread );
if (r != KErrNone)
{
Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo() - END - thread not found");
return r;
}
// Get the thread handle
DThread* thread = (DThread*) TempObject();
// Check the threads in the process are suspended
if ( !SuspensionManager().IsSuspended( *thread ) )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo - END - parent process not suspended => KErrAccessDenied"));
CloseTempObject();
return KErrAccessDenied;
}
// Check we can find the cell in the cell list...
const TMemSpyDriverInternalWalkHeapParamsCell* cell = CellInfoForSpecificAddress( aCellAddress );
if ( cell == NULL )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo - no exact match for address: 0x%08x...", aCellAddress));
// Maybe the client tried the base address of the cell data.
// try to take the header into account and retry.
const TUint32 cellHeaderSize = (TUint32) RMemSpyDriverRHeapBase::AllocatedCellHeaderSize( debugEUser );
TUint32 addr = (TUint32) aCellAddress;
addr -= cellHeaderSize;
TAny* cellByRawStartingAddress = (TAny*) addr;
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo - trying to search by start of cell address: 0x%08x (cellHeaderSize: %d)", cellByRawStartingAddress, cellHeaderSize));
cell = CellInfoForSpecificAddress( cellByRawStartingAddress );
// If the cell still wasn't found, then let's look for any heap cell that contains
// the client-specified address (i.e. find the heap cell that contains the specified
// address).
if ( cell == NULL )
{
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo - still couldnt find cell by exact address. Searching for the cell that contains the specified address..."));
cell = CellInfoForAddressWithinCellRange( aCellAddress );
}
}
if ( cell )
{
const TBool isValidCell = iWalkHeap.CheckCell( cell->iCellAddress, cell->iLength );
if ( isValidCell )
{
// Have enough info to write back to client now
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo - returning... cellType: %1d, addr: 0x%08x, len: %8d, nestingLev: %8d, allocNum: %8d", cell->iCellType, cell->iCellAddress, cell->iLength, cell->iNestingLevel, cell->iAllocNumber ));
r = Kern::ThreadRawWrite( &ClientThread(), aParams, cell, sizeof(TMemSpyDriverInternalWalkHeapParamsCell) );
}
else
{
r = KErrArgument;
Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo - invalid cell address: 0x%08x", cell);
}
}
else
{
r = KErrArgument;
Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo - no cell at user supplied address!");
}
CloseTempObject();
//
TRACE( Kern::Printf("DMemSpyDriverLogChanHeapWalk::WalkHeapGetCellInfo() - END"));
return r;
}
const TMemSpyDriverInternalWalkHeapParamsCell* DMemSpyDriverLogChanHeapWalk::CellInfoForAddressWithinCellRange( TAny* aAddress ) const
{
const TMemSpyDriverInternalWalkHeapParamsCell* ret = NULL;
//
const TInt count = iWalkHeapCells.Count();
for(TInt i=0; i<count; i++)
{
const TMemSpyDriverInternalWalkHeapParamsCell& item = iWalkHeapCells[i];
const TAny* cellExtent = (TAny*) (TUint32( item.iCellAddress ) + item.iLength);
//
if ( aAddress >= item.iCellAddress && aAddress < cellExtent )
{
ret = &item;
}
}
//
return ret;
}
const TMemSpyDriverInternalWalkHeapParamsCell* DMemSpyDriverLogChanHeapWalk::CellInfoForSpecificAddress( TAny* aAddress ) const
{
const TMemSpyDriverInternalWalkHeapParamsCell* ret = NULL;
//
const TInt count = iWalkHeapCells.Count();
for(TInt i=0; i<count; i++)
{
const TMemSpyDriverInternalWalkHeapParamsCell& item = iWalkHeapCells[i];
if ( item.iCellAddress == aAddress )
{
ret = &item;
}
}
//
return ret;
}
TBool DMemSpyDriverLogChanHeapWalk::WalkerHandleHeapCell( TInt aCellType, TAny* aCellAddress, TInt aLength, TInt aNestingLevel, TInt aAllocNumber )
{
TInt error = KErrNone;
//
if ( iHeapWalkInitialised )
{
TMemSpyDriverInternalWalkHeapParamsCell cell;
cell.iCellType = aCellType;
cell.iCellAddress = aCellAddress;
cell.iLength = aLength;
cell.iNestingLevel = aNestingLevel;
cell.iAllocNumber = aAllocNumber;
//
NKern::ThreadEnterCS();
error = iWalkHeapCells.Append( cell );
NKern::ThreadLeaveCS();
}
//
return ( error == KErrNone );
}