memspy/Engine/Source/ThreadAndProcess/MemSpyEngineObjectContainer.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 13 Oct 2010 16:17:58 +0300
branchRCL_3
changeset 59 8ad140f3dd41
parent 49 7fdc9a71d314
permissions -rw-r--r--
Revision: 201039 Kit: 201041

/*
* 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/memspyengineobjectcontainer.h>

// System includes
#include <e32svr.h>

// Driver includes
#include <memspy/driver/memspydriverclient.h>

// User includes
#include <memspy/engine/memspyengine.h>
#include <memspy/engine/memspyengineutils.h>
#include <memspy/engine/memspyengineobjectprocess.h>
#include <memspy/engine/memspyengineobjectthread.h>
#include <memspy/engine/memspyenginehelperheap.h>
#include <memspy/engine/memspyenginehelperstack.h>
#include <memspy/engine/memspyenginehelpercodesegment.h>

// Literal constants
_LIT(KProcessFilter, "*");
_LIT(KEKernProcessName, "ekern");


CMemSpyEngineObjectContainer::CMemSpyEngineObjectContainer( CMemSpyEngine& aEngine )
:   iEngine( aEngine )
    {
	/*
    TFindProcess procFinder( KProcessFilter );
    TFullName nextMatch;
    //
    while( procFinder.Next( nextMatch ) == KErrNone )
        {
        _LIT(KProcListing, "%S");
        RDebug::Print(KProcListing, &nextMatch);


        _LIT(KFindMask, "*");
        nextMatch += KFindMask;
        //
        TFindThread threadFinder( nextMatch );
        //
        while( threadFinder.Next( nextMatch ) == KErrNone )
            {
            _LIT(KThreadListing, "+--- %S");
            RDebug::Print(KThreadListing, &nextMatch);
            }
        }
	*/
    }


CMemSpyEngineObjectContainer::~CMemSpyEngineObjectContainer()
    {
    DestroyProcesses( iGarbage );
    DestroyProcesses( iProcesses );
    delete iUndertaker;
    delete iMidwife;
    delete iIdleNotifyContainerChanged;
    }


void CMemSpyEngineObjectContainer::ConstructL( const TDesC& aFilter )
    {
    iUndertaker = CMemSpyEngineUndertaker::NewL( iEngine.Driver() );
    iUndertaker->AddObserverL( *this );
    //
    iMidwife = CMemSpyEngineMidwife::NewL( iEngine.Driver() );
    iMidwife->AddObserverL( *this );
    //
    iIdleNotifyContainerChanged = CIdle::NewL( CActive::EPriorityIdle );
    //
    RefreshL( aFilter );
    }


CMemSpyEngineObjectContainer* CMemSpyEngineObjectContainer::NewL( CMemSpyEngine& aEngine )
    {
    return NewL( KProcessFilter, aEngine );
    }


CMemSpyEngineObjectContainer* CMemSpyEngineObjectContainer::NewL( const TDesC& aFilter, CMemSpyEngine& aEngine )
    {
    CMemSpyEngineObjectContainer* self = new(ELeave) CMemSpyEngineObjectContainer( aEngine );
    CleanupStack::PushL( self );
    self->ConstructL( aFilter );
    CleanupStack::Pop( self );
    return self;
    }


EXPORT_C TInt CMemSpyEngineObjectContainer::Count() const
    {
    return iProcesses.Count();
    }


EXPORT_C TInt CMemSpyEngineObjectContainer::CountAll() const
    {
    TInt ret = 0;
    //
    const TInt count = iProcesses.Count();
    for( TInt i=0; i<count; i++ )
        {
        CMemSpyProcess* process = iProcesses[ i ];
        ret += process->Count();
        }
    //
    return ret;
    }


EXPORT_C void CMemSpyEngineObjectContainer::RefreshL()
    {
    RefreshL( KProcessFilter );
    }


EXPORT_C void CMemSpyEngineObjectContainer::RefreshL( const TDesC& aFilter )
    {
    LocateProcessesL( aFilter );
    }


EXPORT_C CMemSpyProcess& CMemSpyEngineObjectContainer::At( TInt aIndex ) const
    {
    CMemSpyProcess* ret = iProcesses[ aIndex ];
    return *ret;
    }


EXPORT_C CMemSpyProcess& CMemSpyEngineObjectContainer::ProcessByIdL( TProcessId aId ) const
    {
    CMemSpyProcess* ret = NULL;
    //
    const TInt count = iProcesses.Count();
    for( TInt i=0; i<count; i++ )
        {
        CMemSpyProcess* process = iProcesses[ i ];
        if  ( process->Id() == aId )
            {
            ret = process;
            break;
            }
        }
    //
    if  ( ret == NULL )
        {
        User::Leave( KErrNotFound );
        }
    //
    return *ret;
    }


EXPORT_C TInt CMemSpyEngineObjectContainer::ProcessIndexById( TProcessId aId ) const
    {
    TInt index = KErrNotFound;
    //
    const TInt count = iProcesses.Count();
    for( TInt i=0; i<count; i++ )
        {
        const CMemSpyProcess* process = iProcesses[ i ];
        if  ( process->Id() == aId )
            {
            index = i;
            break;
            }
        }
    //
    return index;
    }


EXPORT_C TInt CMemSpyEngineObjectContainer::ProcessAndThreadByThreadId( TThreadId aTid, CMemSpyProcess*& aProcess, CMemSpyThread*& aThread ) const
    {
    aProcess = NULL;
    aThread = NULL;
    TInt error = KErrNotFound;
    //
    const TInt count = iProcesses.Count();
    for( TInt i=0; i<count; i++ )
        {
        CMemSpyProcess* process = iProcesses[ i ];

        // Check whether this process contains the thread we are looking for...
        const TInt index = process->ThreadIndexById( aTid );
        if  ( index >= 0 )
            {
            // Found it
            aProcess = process;
            aThread = &process->At( index );
            error = KErrNone;
            break;
            }
        }
    //
    if  ( error == KErrNotFound )
        {
        CMemSpyEngineObjectContainer* self = const_cast< CMemSpyEngineObjectContainer* >( this );
        TRAP( error, self->TryToCreateProcessAndThreadL( aTid, aProcess, aThread ) );
        }
    //
    return error;
    }


EXPORT_C TInt CMemSpyEngineObjectContainer::ProcessAndThreadByFullName( const TDesC& aFullName, CMemSpyProcess*& aProcess, CMemSpyThread*& aThread ) const
    {
    aProcess = NULL;
    aThread = NULL;
    TInt error = KErrNotFound;
    //
    const TInt count = iProcesses.Count();
    for( TInt i=0; i<count && aThread == NULL; i++ )
        {
        CMemSpyProcess& process = *iProcesses[ i ];
        const TInt threadCount = process.Count();
        //
        for(TInt j=0; j<threadCount; j++)
            {
            CMemSpyThread& thread = process.At( j );
            const TFullName threadName( thread.FullName() );
            //
            if  ( threadName.CompareF( aFullName ) == 0 )
                {
                // Found it
                aProcess = &process;
                aThread = &thread;
                error = KErrNone;
                break;
                }
            }

        }
    //
    if  ( error == KErrNotFound )
        {
        // NB: cannot use driver API as we must open thread by name, and that only supports opening
        // by id.
        RThread thread;
        error = thread.Open( aFullName );
        if ( error == KErrNone )
            {
            const TThreadId threadId = thread.Id();
            thread.Close();

            CMemSpyEngineObjectContainer* self = const_cast< CMemSpyEngineObjectContainer* >( this );
            TRAP( error, self->TryToCreateProcessAndThreadL( threadId, aProcess, aThread ) );
            }
        }
    //
    return error;
    }


EXPORT_C TInt CMemSpyEngineObjectContainer::ProcessAndThreadByPartialName( const TDesC& aPartialName, CMemSpyProcess*& aProcess, CMemSpyThread*& aThread ) const
    {
    aProcess = NULL;
    aThread = NULL;
    TInt error = KErrNotFound;
    //
    const TInt count = iProcesses.Count();
    for( TInt i=0; i<count && aThread == NULL; i++ )
        {
        CMemSpyProcess& process = *iProcesses[ i ];
        const TInt threadCount = process.Count();
        //
        for(TInt j=0; j<threadCount; j++)
            {
            CMemSpyThread& thread = process.At( j );
            const TFullName threadName( thread.FullName() );
            //
            if  ( threadName.FindF( aPartialName ) >= 0 )
                {
                // Found it
                aProcess = &process;
                aThread = &thread;
                error = KErrNone;
                break;
                }
            }

        }
    //
    return error;
    }


EXPORT_C TBool CMemSpyEngineObjectContainer::IsAlive( TProcessId aPid ) const
    {
    const TInt processIndex = ProcessIndexById( aPid );
    return ( processIndex >= 0 );
    }


EXPORT_C TBool CMemSpyEngineObjectContainer::IsAlive( TProcessId aPid, TThreadId aTid ) const
    {
    TBool isAlive = IsAlive( aPid );
    if  ( isAlive )
        {
        const TInt processIndex = ProcessIndexById( aPid );
        const CMemSpyProcess& process = At( processIndex );

        // Check whether the thread is alive
        isAlive = ( process.ThreadIndexById( aTid ) >= 0 );
        }
    //
    return isAlive;
    }


EXPORT_C TInt CMemSpyEngineObjectContainer::MdcaCount() const
    {
    return iProcesses.Count();
    }


EXPORT_C TPtrC CMemSpyEngineObjectContainer::MdcaPoint(TInt aIndex) const
    {
    const CMemSpyProcess* process = iProcesses[ aIndex ];
    return TPtrC( process->NameForListBox() );
    }


void CMemSpyEngineObjectContainer::LocateProcessesL( const TDesC& aFilter )
    {
#ifdef _DEBUG
    RDebug::Printf( "CMemSpyEngineObjectContainer::LocateProcessesL() - START" );
#endif
    DestroyProcesses( iProcesses );
    //
    const TProcessId myProcessId = RProcess().Id();
	//
    TFindProcess finder( aFilter );
    TFullName nextMatch;
    //
    while( finder.Next( nextMatch ) == KErrNone )
        {
        if  ( nextMatch.FindF(KEKernProcessName) == KErrNotFound )
            {
#ifdef _DEBUG
            RDebug::Print(_L( "CMemSpyEngineObjectContainer::LocateProcessesL() - found Proc: %S"), &nextMatch );
#endif

            RProcess p;
            const TInt r = p.Open( nextMatch );
            TProcessId processId( KNullProcessId );
            if ( r == KErrNone )
                {
                processId = p.Id();
                }
            p.Close();

#ifdef _DEBUG
            RDebug::Printf( "CMemSpyEngineObjectContainer::LocateProcessesL() - open err: %d", r );
#endif

            if  ( r == KErrNone )
                {
                if  ( processId != myProcessId )
                    {
                    TRAPD( err, CreateProcessL( processId ) );
#ifdef _DEBUG
                    RDebug::Printf( "CMemSpyEngineObjectContainer::LocateProcessesL() - CreateProcessL err: %d", err );
#endif
                    err = err;
                    }
                }
            }
        }
    //
    SortByName();

#ifdef _DEBUG
    RDebug::Printf( "CMemSpyEngineObjectContainer::LocateProcessesL() - END" );
#endif
    }


CMemSpyProcess* CMemSpyEngineObjectContainer::CreateProcessL( const TProcessId& aId )
    {
#ifdef _DEBUG
	RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::CreateProcessL() - START - pid: 0x%04x", (TUint) aId );
#endif

    CMemSpyProcess* processObj = CMemSpyProcess::NewLC( aId, iEngine );
    const TPtrC pName( processObj->Name() );

    if  ( pName.FindF( KEKernProcessName ) == KErrNotFound )
        {
        if  ( processObj->Count() == 0 )
            {
            // Calls delete
#ifdef _DEBUG
	        RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::CreateProcessL() - process has no threads... deleting it!" );
#endif
            processObj->Close();
            processObj = NULL;
            }
        else
            {
            // Make sure we don't add duplicate processes...
            AppendL( processObj );
            }
        }
    else
        {
        // We don't support kernel-side threads
#ifdef _DEBUG
        RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::CreateProcessL() - kernel process... deleting it!" );
#endif
        processObj->Close();
        processObj = NULL;
        }

    CleanupStack::Pop();

#ifdef _DEBUG
    RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::CreateProcessL() - END - pid: 0x%04x, processObj: 0x%08x", (TUint) aId, processObj );
#endif
    return processObj;
    }


void CMemSpyEngineObjectContainer::DestroyProcesses( RArray< CMemSpyProcess* >& aList )
    {
    while( aList.Count() ) 
        {
        CMemSpyProcess* process = aList[ 0 ];
        aList.Remove( 0 );
        process->Close();
        }

    aList.Close();
    }


void CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL( const TThreadId& aTid, CMemSpyProcess*& aProcess, CMemSpyThread*& aThread )
    {
#ifdef _DEBUG
    RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL(1) - START - aTid: 0x%04x", (TUint) aTid );
#endif

    // Must open thread and process in order to get process id.
    RThread thread;
    User::LeaveIfError( iEngine.Driver().OpenThread( aTid, thread ) );
    CleanupClosePushL( thread );
    RProcess process;
    User::LeaveIfError( thread.Process( process ) );
    const TProcessId processId = process.Id();
    process.Close();

    // Call overload
    TryToCreateProcessAndThreadL( thread, aProcess, aThread );

    CleanupStack::PopAndDestroy( &thread );

#ifdef _DEBUG
    RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL(1) - END - aTid: 0x%04x", (TUint) aTid );
#endif
    }


void CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL( const RThread& aRThread, CMemSpyProcess*& aProcess, CMemSpyThread*& aThread )
    {
#ifdef _DEBUG
    RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL(2) - START - aTid: 0x%04x", (TUint) aRThread.Id() );
#endif

    // Full name of process
    TFullName* name = new(ELeave) TFullName();
    CleanupStack::PushL( name );

    // Must open thread and process in order to get process id.
    RProcess process;
    User::LeaveIfError( aRThread.Process( process ) );
    process.FullName( *name );
    const TProcessId processId = process.Id();
    process.Close();

    const TBool isKernel = ( name->FindF( KEKernProcessName ) >= KErrNone );

#ifdef _DEBUG
    RDebug::Print( _L("[MemSpy] CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL(2) - pid: 0x%04x, name: %S"), (TUint) processId, name );
#endif

    CleanupStack::PopAndDestroy( name );

    // See if we have already created a process for this process id...
    CMemSpyProcess* processObj = NULL;
    const TInt procIndex = ProcessIndexById( processId );

#ifdef _DEBUG
    RDebug::Print( _L("[MemSpy] CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL(2) - pid: 0x%04x, index: %d"), (TUint) processId, procIndex );
#endif

    if  ( isKernel )
        {
#ifdef _DEBUG
        RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL(2) - caller requested a kernel thread/process..." );
#endif
        __ASSERT_ALWAYS( procIndex == KErrNotFound, MemSpyEngineUtils::Panic( EMemSpyEnginePanicEncounteredKernelUnexpectedly ) );

#ifdef _DEBUG
        RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL(2) - leaving with KErrNotSupported" );
#endif
        User::Leave( KErrNotSupported );
        }
    else if ( aRThread.Id() == RThread().Id() )
        {
        TRACE( RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL(2) - trying to create MemSpy -> leaving with KErrNotSupported" ) );
        User::Leave( KErrNotSupported );
        }
    else
        {
        if  ( procIndex >= 0 )
            {
            // Existing process, but new thread?
            processObj = iProcesses[ procIndex ];

            // Make new thread
            processObj->HandleThreadIsBornL( aRThread.Id() );
            }
        else
            {
            // Must also create a new process. This should, in theory, ensure
            // the thread is also created.
            processObj = CMemSpyProcess::NewLC( processId, iEngine );
            if  ( processObj->Count() == 0 )
                {
                // No threads, discard process and leave
                User::Leave( KErrNotFound );
                }
            else
                {
                // Save process
                AppendL( processObj );
                }

            // Tidy up
            CleanupStack::Pop();
            }

        // Check to see if the process contains the specified thread (it should do, since we just found
        // the process using it).
        CMemSpyThread& threadObj = processObj->ThreadByIdL( aRThread.Id() );

        // We're done now.
        aProcess = processObj;
        aThread = &threadObj;

        // Update sort order
        Resort();

        // Signal UI        
        AsyncNotifyUiOfContainerChanges();
        }

#ifdef _DEBUG
    RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::TryToCreateProcessAndThreadL(2) - END - aTid: 0x%04x, pid: 0x%04x", (TUint) aRThread.Id(), (TUint) processId );
#endif
    }


void CMemSpyEngineObjectContainer::Resort()
    {
    switch( iSortType )
        {
    case ESortById:
        {
        TLinearOrder< CMemSpyProcess* > comparer( CompareById );
        iProcesses.Sort( comparer );
        }
        break;
    default:
    case ESortByName:
        {
        TLinearOrder< CMemSpyProcess* > comparer( CompareByName );
        iProcesses.Sort( comparer );
        }
        break;
    case ESortByThreadCount:
        {
        TLinearOrder< CMemSpyProcess* > comparer( CompareByThreadCount );
        iProcesses.Sort( comparer );
        }
        break;
    case ESortByCodeSegs:
        {
        TLinearOrder< CMemSpyProcess* > comparer( CompareByCodeSegs );
        iProcesses.Sort( comparer );
        }
        break;
    case ESortByHeapUsage:
        {
        TLinearOrder< CMemSpyProcess* > comparer( CompareByHeapUsage );
        iProcesses.Sort( comparer );
        }
        break;
    case ESortByStackUsage:
        {
        TLinearOrder< CMemSpyProcess* > comparer( CompareByStackUsage );
        iProcesses.Sort( comparer );
        }
        break;
        }
    }


EXPORT_C void CMemSpyEngineObjectContainer::SortById()
    {
    iSortType = ESortById;
    Resort();
    }


EXPORT_C void CMemSpyEngineObjectContainer::SortByName()
    {
    iSortType = ESortByName;
    Resort();
    }


EXPORT_C void CMemSpyEngineObjectContainer::SortByThreadCount()
    {
    iSortType = ESortByThreadCount;
    Resort();
    }


EXPORT_C void CMemSpyEngineObjectContainer::SortByCodeSegs()
    {
    iSortType = ESortByCodeSegs;
    Resort();
    }


EXPORT_C void CMemSpyEngineObjectContainer::SortByHeapUsage()
    {
    iSortType = ESortByHeapUsage;
    Resort();
    }


EXPORT_C void CMemSpyEngineObjectContainer::SortByStackUsage()
    {
    iSortType = ESortByStackUsage;
    Resort();
    }


void CMemSpyEngineObjectContainer::Remove( CMemSpyProcess& aProcess )
    {
    const TInt index = ProcessIndexById( aProcess.Id() );
    if ( index >= 0 )
        {
        iProcesses.Remove( index );
        }
    }
    
    
void CMemSpyEngineObjectContainer::AppendL( CMemSpyProcess* aProcess )
    {
    const TInt error = iProcesses.Append( aProcess );
    User::LeaveIfError( error );    
    }


TInt CMemSpyEngineObjectContainer::CompareById( CMemSpyProcess* const & aLeft, CMemSpyProcess* const & aRight )
    {
    TInt ret = 1;
    //
    if  ( aLeft->Id() < aRight->Id() )
        {
        ret = -1;
        }
    else if ( aLeft->Id() == aRight->Id() )
        {
        ret = 0;
        }
    //
    return ret;
    }


TInt CMemSpyEngineObjectContainer::CompareByThreadCount( CMemSpyProcess* const & aLeft, CMemSpyProcess* const & aRight )
    {
    TInt ret = -1;
    //
    if  ( aLeft->Count() < aRight->Count() )
        {
        ret = 1;
        }
    else if ( aLeft->Count() == aRight->Count() )
        {
        // Sort by name when thread counts are the same
        ret = aLeft->Name().CompareF( aRight->Name() );
        }
    //
    return ret;
    }


TInt CMemSpyEngineObjectContainer::CompareByName( CMemSpyProcess* const & aLeft, CMemSpyProcess* const & aRight )
    {
    return aLeft->Name().CompareF( aRight->Name() );
    }


TInt CMemSpyEngineObjectContainer::CompareByCodeSegs( CMemSpyProcess* const & aLeft, CMemSpyProcess* const & aRight )
    {
    CMemSpyEngine& engine = aLeft->Engine();
    CMemSpyEngineHelperCodeSegment& helper = engine.HelperCodeSegment();
    //
    TInt leftCount = 0;
    TInt rightCount = 0;
    //
    TRAP_IGNORE( 
        CMemSpyEngineCodeSegList* leftList = helper.CodeSegmentListL( aLeft->Id() );
        leftCount = leftList->Count();
        delete leftList;
        //
        CMemSpyEngineCodeSegList* rightList = helper.CodeSegmentListL( aRight->Id() );
        rightCount = rightList->Count();
        delete rightList;
        );
    //
    TInt ret = -1;
    //
    if  ( leftCount < rightCount )
        {
        ret = 1;
        }
    else if ( leftCount == rightCount )
        {
        ret = 0;
        }
    //
    return ret;
    }


TInt CMemSpyEngineObjectContainer::CompareByHeapUsage( CMemSpyProcess* const & aLeft, CMemSpyProcess* const & aRight )
    {
    CMemSpyEngine& engine = aLeft->Engine();
    CMemSpyEngineHelperHeap& helper = engine.HelperHeap();
    //
    TInt leftSize = 0;
    TInt rightSize = 0;
    //
    TRAP_IGNORE( 
        RArray< TMemSpyHeapInfo > leftInfos;
        CleanupClosePushL( leftInfos );
        helper.GetHeapInfoUserL( *aLeft, leftInfos );
        const TInt leftCount = leftInfos.Count();
        for( TInt i=0; i<leftCount; i++ )
            {
            const TMemSpyHeapInfo& info = leftInfos[ i ];
            if ( info.Type() == TMemSpyHeapInfo::ETypeRHeap )
                {
                leftSize += (TInt) info.AsRHeap().ObjectData().Size();
                }
            }
        CleanupStack::PopAndDestroy( &leftInfos );
        );
    //
    TRAP_IGNORE( 
        RArray< TMemSpyHeapInfo > rightInfos;
        CleanupClosePushL( rightInfos );
        helper.GetHeapInfoUserL( *aRight, rightInfos );
        const TInt rightCount = rightInfos.Count();
        for( TInt i=0; i<rightCount; i++ )
            {
            const TMemSpyHeapInfo& info = rightInfos[ i ];
            if ( info.Type() == TMemSpyHeapInfo::ETypeRHeap )
                {
                rightSize += (TInt) info.AsRHeap().ObjectData().Size();
                }
            }
        CleanupStack::PopAndDestroy( &rightInfos );
        );
    //
    TInt ret = -1;
    //
    if  ( leftSize < rightSize )
        {
        ret = 1;
        }
    else if ( leftSize == rightSize )
        {
        ret = 0;
        }
    //
    return ret;
    }


TInt CMemSpyEngineObjectContainer::CompareByStackUsage( CMemSpyProcess* const & aLeft, CMemSpyProcess* const & aRight )
    {
    CMemSpyEngine& engine = aLeft->Engine();
    CMemSpyEngineHelperStack& helper = engine.HelperStack();
    //
    const TInt leftCount = helper.CalculateStackSizes( *aLeft );
    const TInt rightCount = helper.CalculateStackSizes( *aRight );
    //
    TInt ret = -1;
    //
    if  ( leftCount < rightCount )
        {
        ret = 1;
        }
    else if ( leftCount == rightCount )
        {
        ret = 0;
        }
    //
    return ret;
    }


TBool CMemSpyEngineObjectContainer::MoveToGarbageL( const TProcessId& aId )
    {
    const TInt pos = ProcessIndexById( aId );

    if  ( pos >= 0 )
        {
        CMemSpyProcess* proc = iProcesses[ pos ];

#ifdef _DEBUG
        const TPtrC pName( proc->Name() );
        RDebug::Print( _L("[MemSpy] CMemSpyEngineObjectContainer::MoveToGarbageL() - pid: 0x%04x, proc: 0x%08x, pos: %d, id: 0x%04x, name: %S"), (TUint) aId, proc, pos, (TUint) proc->Id(), &pName );
#endif

        User::LeaveIfError( iGarbage.Append( proc ) );
        iProcesses.Remove( pos );
        }
	else
        {
#ifdef _DEBUG
        RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::MoveToGarbageL() - pid: 0x%04x, pos: KErrNotFound", (TUint) aId );
#endif
        }
        
    return ( pos >= 0 );
    }


















void CMemSpyEngineObjectContainer::ThreadIsDeadL( const TThreadId& aId, const RThread& aThread )
    {
    // Try to find the thread in question...
    CMemSpyProcess* process = NULL;
    CMemSpyThread* thread = NULL;
    //
    const TInt err = ProcessAndThreadByThreadId( aId, process, thread );
    if  ( err == KErrNone && thread )
        {
        // This will force the thread to detect if it is dead or not...
        thread->SetDeadL( aThread );

        // Signal UI        
        AsyncNotifyUiOfContainerChanges();
        }
    }
    

void CMemSpyEngineObjectContainer::ProcessIsDeadL( const TProcessId& aId, const RProcess& aProcess )
    {
    const TInt index = ProcessIndexById( aId );
    if  ( index >= 0 )
        {
        CMemSpyProcess* process = iProcesses[ index ];
        process->SetDeadL( aProcess );

        // Signal UI        
        AsyncNotifyUiOfContainerChanges();
        }
    }


void CMemSpyEngineObjectContainer::ThreadIsBornL( const TThreadId& /*aId*/, const RThread& aThread )
    {
    if  ( aThread.Handle() != KNullHandle )
        {
        // The thread and process objects that will be created (or found, if they already exist).
        CMemSpyProcess* process = NULL;
        CMemSpyThread* thread = NULL;

        // Create the objects if needed
        TryToCreateProcessAndThreadL( aThread, process, thread );
        }
    }


void CMemSpyEngineObjectContainer::ProcessIsBornL( const TProcessId& aId, const RProcess& /*aProcess*/ )
    {
#ifdef _DEBUG
    RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::ProcessIsBornL() - START - pid: 0x%04x", (TUint) aId );
#endif
    (void) aId;

    // This gets handled by the new thread creation. When the first thread in the process
    // is created, we'll also prepare a new process object.

#ifdef _DEBUG
	RDebug::Printf( "[MemSpy] CMemSpyEngineObjectContainer::ProcessIsBornL() - END - pid: 0x%04x", (TUint) aId );
#endif
    }
    

void CMemSpyEngineObjectContainer::AsyncNotifyUiOfContainerChanges()
    {
    iIdleNotifyContainerChanged->Cancel();
    iIdleNotifyContainerChanged->Start( TCallBack( NotifyUiOfContainerChanges, this ) );
    }


TBool CMemSpyEngineObjectContainer::NotifyUiOfContainerChanges( TAny* aSelf )
    {
    CMemSpyEngineObjectContainer* self = reinterpret_cast< CMemSpyEngineObjectContainer* >( aSelf );
    TRAP_IGNORE( self->iEngine.NotifyContainerChangeL() );
    return EFalse;
    }