memspy/Engine/Source/ThreadAndProcess/MemSpyEngineObjectThread.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/memspyengineobjectthread.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/memspyengineobjectthreadinfocontainer.h>

// Constants
_LIT( KMemSpyUnknownExitCategory, "Unknown ExitCat" );
_LIT( KMemSpyUnknownThreadName, "Unknown Thread" );
_LIT( KMemSpyThreadDoubleColon, "::" );


CMemSpyThread::CMemSpyThread( TThreadId aId, CMemSpyProcess& aProcess )
:   CMemSpyEngineObject( aProcess ), iId( aId ), iProcess( &aProcess )
    {
    }


CMemSpyThread::~CMemSpyThread()
    {
    if  ( iInfoContainer )
        {
        iInfoContainer->Close();
        }
    delete iName;
    }


void CMemSpyThread::ConstructL()
    {
    RefreshL();
    }


CMemSpyThread* CMemSpyThread::NewL( TThreadId aId, CMemSpyProcess& aProcess )
    {
    CMemSpyThread* self = CMemSpyThread::NewLC( aId, aProcess );
    CleanupStack::Pop( self );
    return self;
    }


CMemSpyThread* CMemSpyThread::NewLC( TThreadId aId, CMemSpyProcess& aProcess )
    {
    CMemSpyThread* self = new(ELeave) CMemSpyThread( aId, aProcess );
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }


EXPORT_C void CMemSpyThread::Open()
    {
    if  ( !OpenOrCloseInProgress() )
        {
        SetOpenOrCloseInProgress( ETrue );
        CMemSpyEngineObject::Open();
        SetOpenOrCloseInProgress( EFalse );
        }
    }


EXPORT_C void CMemSpyThread::Close()
    {
    if  ( !OpenOrCloseInProgress() )
        {
        SetOpenOrCloseInProgress( ETrue );
        CMemSpyEngineObject::Close();
        SetOpenOrCloseInProgress( EFalse );
        }
    }


void CMemSpyThread::AppendPriority( TDes& aDes, TThreadPriority aPriority )
    {
    switch( aPriority )
        {
    case EPriorityNull:
        aDes += _L("[Null]");
        break;
    case EPriorityMuchLess:
        aDes += _L("[Much Less]");
        break;
    case EPriorityLess:
        aDes += _L("[Less]");
        break;
    case EPriorityNormal:
        aDes += _L("[Normal]");
        break;
    case EPriorityMore:
        aDes += _L("[More]");
        break;
    case EPriorityMuchMore:
        aDes += _L("[Much More]");
        break;
    case EPriorityRealTime:
        aDes += _L("[Real Time]");
        break;

    // Absolute values
    case EPriorityAbsoluteVeryLow:
        aDes += _L("[Abs Very Low]");
        break;
    case EPriorityAbsoluteLowNormal:
        aDes += _L("[Abs Low Norm]");
        break;
    case EPriorityAbsoluteLow:
        aDes += _L("[Abs Low]");
        break;
    case EPriorityAbsoluteBackground:
        aDes += _L("[Abs Bgnd]");
        break;
    case EPriorityAbsoluteBackgroundNormal:
        aDes += _L("[Abs Bgnd Norm]");
        break;
    case EPriorityAbsoluteForeground:
        aDes += _L("[Abs Fgnd]");
        break;
    case EPriorityAbsoluteForegroundNormal:
        aDes += _L("[Abs Fgnd Norm]");
        break;
    case EPriorityAbsoluteHigh:
        aDes += _L("[Abs High]");
        break;
    case EPriorityAbsoluteHighNormal:
        aDes += _L("[Abs High Norm]");
        break;
    case EPriorityAbsoluteRealTime1:
    case EPriorityAbsoluteRealTime2:
    case EPriorityAbsoluteRealTime3:
    case EPriorityAbsoluteRealTime4:
    case EPriorityAbsoluteRealTime5:
    case EPriorityAbsoluteRealTime6:
    case EPriorityAbsoluteRealTime7:
    case EPriorityAbsoluteRealTime8:
        aDes.AppendFormat( _L("[Abs RT %d]"), ( aPriority - EPriorityAbsoluteRealTime1 ) + 1 );
        break;
    default:
        aDes += _L("[Unknown Pri.]");
        break;
        }
    }


void CMemSpyThread::AppendExitType( TDes& aDes, TExitType aType )
    {
    _LIT( KExitTypeKilled, "Killed" );
    _LIT( KExitTypeTerminated, "Terminated" );
    _LIT( KExitTypePanicked, "Panicked" );
    _LIT( KExitTypePending, "Pending" );
    
    // Panic and Terminate are exceptional exit conditions.
    // Kill, is ironically, not an exceptional condition.
    switch( aType )
        {
    case EExitKill:
        aDes += KExitTypeKilled;
        break;
    case EExitTerminate:
        aDes += KExitTypeTerminated;
        break;
    case EExitPanic:
        aDes += KExitTypePanicked;
        break;
    default:
    case EExitPending:
        aDes += KExitTypePending;
        break;
        }
    }


void CMemSpyThread::AppendExitInfo( TDes& aDes, TExitType aType, TInt aExitReason, const TDesC& aExitCategory )
    {
    aDes.Append( '[' );
    const TInt length = aDes.Length();
    AppendExitType( aDes, aType );
    aDes.SetLength( length + 1 ); // Remove all but the first letter
    aDes.Append( ']' );
    
    if  ( aType == EExitKill || aType == EExitPending )
        {
        // Kill implies "clean" exit. Pending implies not yet dead.
        }
    else
        {
        TMemSpyTruncateOverflow overflow;

        // Terminate or Panic implies abnormal exit condition, so
        // show full exit info.
        _LIT( KAbnormalFormatSpec, " %S-%d" );
        aDes.AppendFormat( KAbnormalFormatSpec, &overflow, &aExitCategory, aExitReason );
        }
    }


CMemSpyEngine& CMemSpyThread::Engine() const
    {
    return Process().Engine();
    }


void CMemSpyThread::OpenLC( RThread& aThread )
    {
    const TInt error = Open( aThread );
    User::LeaveIfError( error );
    CleanupClosePushL( aThread );
    }


TInt CMemSpyThread::Open( RThread& aThread )
    {
    CMemSpyEngine& engine = iProcess->Engine();
    RMemSpyDriverClient& driver = engine.Driver();
    const TInt error = driver.OpenThread( iId, aThread );
    return error;
    }


EXPORT_C TPtrC CMemSpyThread::Name() const
    {
    // Just return the pure name, minus the leading tab
    TPtrC pRet( iName->Mid(2) );
    
    // Find the last tab position
    TInt pos = pRet.Locate(TChar('\t'));
    if  ( pos > 0 )
        {
        pRet.Set( pRet.Left( pos ) );
        }
    //    
    return pRet;
    }


EXPORT_C TFullName CMemSpyThread::FullName() const
    {
    TFullName name( iProcess->Name() );
    name += KMemSpyThreadDoubleColon;
    name += Name();
    //
    return name;
    }


EXPORT_C TBool CMemSpyThread::IsSystemPermanent() const
    {
    const TBool ret = ( iFlags & KThreadFlagSystemPermanent );
    return ret;
    }


EXPORT_C TBool CMemSpyThread::IsSystemCritical() const
    {
    const TBool ret = ( iFlags & KThreadFlagSystemCritical );
    return ret;
    }


EXPORT_C CMemSpyThreadInfoContainer& CMemSpyThread::InfoContainerL()
    {
    if  ( iInfoContainer == NULL )
        {
        const TBool KConstructAsynchronously = ETrue;
        iInfoContainer = CMemSpyThreadInfoContainer::NewL( *this, KConstructAsynchronously );
        }
    //
    return *iInfoContainer;
    }


EXPORT_C CMemSpyThreadInfoContainer& CMemSpyThread::InfoContainerForceSyncronousConstructionL()
    {
    if  ( iInfoContainer == NULL )
        {
        const TBool KConstructSynchronously = EFalse;
        iInfoContainer = CMemSpyThreadInfoContainer::NewL( *this, KConstructSynchronously );
        }
    //
    return *iInfoContainer;
    }


EXPORT_C void CMemSpyThread::KillL()
    {
    CMemSpyEngine& engine = iProcess->Engine();
    RMemSpyDriverClient& driver = engine.Driver();
    //
    User::LeaveIfError( driver.ThreadEnd( Id(), EExitKill ) );
    }


EXPORT_C void CMemSpyThread::TerminateL()
    {
    CMemSpyEngine& engine = iProcess->Engine();
    RMemSpyDriverClient& driver = engine.Driver();
    //
    User::LeaveIfError( driver.ThreadEnd( Id(), EExitTerminate ) );
    }


EXPORT_C void CMemSpyThread::PanicL()
    {
    CMemSpyEngine& engine = iProcess->Engine();
    RMemSpyDriverClient& driver = engine.Driver();
    //
    User::LeaveIfError( driver.ThreadEnd( Id(), EExitPanic ) );
    }


EXPORT_C void CMemSpyThread::SetPriorityL( TThreadPriority aPriority )
    {
#ifdef _DEBUG
    RDebug::Printf( "CMemSpyThread::SetPriorityL() - START - aPriority: %d, orig pri: %d", aPriority, iPriority );
#endif
    CMemSpyEngine& engine = iProcess->Engine();
    RMemSpyDriverClient& driver = engine.Driver();
    //
    const TInt err = driver.SetPriority( Id(), aPriority );
#ifdef _DEBUG
    TInt newPri = -1;
    RThread thread;
    if ( driver.OpenThread( iId, thread ) == KErrNone )
        {
        newPri = thread.Priority();
        thread.Close();
        }
    RDebug::Printf( "CMemSpyThread::SetPriorityL() - err: %d, newPri: %d", err, newPri );
#endif

    User::LeaveIfError( err );
    RefreshL();
#ifdef _DEBUG
    RDebug::Printf( "CMemSpyThread::SetPriorityL() - END" );
#endif
    }


void CMemSpyThread::SetDeadL()
    {
    RefreshL();
    }


void CMemSpyThread::SetDeadL( const RThread& aThread )
    {
    RefreshL( aThread );
	}


void CMemSpyThread::FullName( TDes& aName ) const
    {
    iProcess->FullName( aName );
    aName.Append( KMemSpyThreadDoubleColon );
    aName.Append( Name() );
    }


EXPORT_C TBool CMemSpyThread::IsDead() const
    {
    const TBool isDead = ( iExitType != EExitPending );
    return  isDead;
    }


void CMemSpyThread::RefreshL()
    {
    CMemSpyEngine& engine = iProcess->Engine();
    RMemSpyDriverClient& driver = engine.Driver();
    
    // Try to open thread. We use the device driver since
    // it doesn't check (i.e. it bypasses) some of the conditions which the
    // default RThread::Open() implementation enforces...
    //
    // Deliberately ignore error. The other overload of RefreshL will
    // cope with the failure.
    RThread thread;
    driver.OpenThread( iId, thread );
    CleanupClosePushL( thread );

    // Call refresh with thread to perform actual heavy lifting...
    RefreshL( thread );

    // Clean up. This thread handle might actually not even be open, but that's okay...
    CleanupStack::PopAndDestroy( &thread );
    }


void CMemSpyThread::RefreshL( const RThread& aThread )
    {
    const TBool handleValid = aThread.Handle() != KNullHandle;
    if  ( handleValid )
        {
        // Annoyingly, we request the entire thread info structure just to get the thread flags...
        iFlags = 0;
        const User::TCritical critType = User::Critical( aThread );
        if  ( critType == User::ESystemPermanent )
            {
            iFlags |= KThreadFlagSystemPermanent;
            }
        else if ( critType == User::ESystemCritical )
            {
            iFlags |= KThreadFlagSystemCritical;
            }

#ifdef _DEBUG
        TMemSpyDriverThreadInfo threadInfo;
        User::LeaveIfError( iProcess->Engine().Driver().GetThreadInfo( iId, threadInfo ) );
        RDebug::Print( _L("CMemSpyThread::RefreshL() - old user pri: %d, curr user pri: %d, curr kernel pri: %d, iFlags: %d, name: %S"), iPriority, aThread.Priority(), threadInfo.iThreadPriority, iFlags, &threadInfo.iFullName );
#endif
        }

    // Get exit info
    iExitType = handleValid ? aThread.ExitType() : EExitKill;
    iPriority = handleValid ? aThread.Priority() : EPriorityNormal;

    // If the thread is dead then we may not be able to get some attributes
    // (it depends on whether the thread handle is valid anymore).
    iExitReason = 0;
    iExitCategory.Zero();

    if  ( IsDead() )
        {
        if  ( handleValid )
            {
            iExitReason = aThread.ExitReason();
            iExitCategory = aThread.ExitCategory(); 
            }
        else
            {
            iExitCategory = KMemSpyUnknownExitCategory;
            }
        }
    else
        {
        }

    // Get raw thread name
    HBufC* rawThreadName = GetThreadNameLC( aThread ); 

    // Full name is enough for the thread name as well as the extra info
    // we show
    TFullName name;

    // Build S60 listbox formatted name
    _LIT( KMemSpyThreadNameFormatSpecBasicName, " \t%S\t\t" );
    name.Format( KMemSpyThreadNameFormatSpecBasicName, rawThreadName );
    CleanupStack::PopAndDestroy( rawThreadName );

    // If the thread is dead show exit info
    if  ( IsDead() )
        {
        AppendExitInfo( name, iExitType, iExitReason, iExitCategory );
        }
    else
        {
        // Otherwise, show priority
        AppendPriority( name, iPriority );
        }

    // Save new fully formatted name
    HBufC* newName = name.AllocL();
    delete iName;
    iName = newName;
    }


HBufC* CMemSpyThread::GetThreadNameLC( const RThread& aThreadOrNull ) const
    {
    TName threadName;
    //
    const TBool handleValid = aThreadOrNull.Handle() != KNullHandle;
    //
    if  ( handleValid )
        {
        // Easy case - we have a valid thread handle.
        threadName.Append( aThreadOrNull.Name() );
        }
    else
        {
        // Since we don't have the possibility to enquire after the thread's name
        // we'll assume that it used to be alive and therefore at some point we did
        // manage to grep it's name...
        if  ( iName )
            {
            const TPtrC pOriginalName( Name() );
            threadName.Append( pOriginalName );
            }
        else
            {
            // Don't have a thread handle, don't have any possibility to get the
            // name from a prior cached version. Must use "unknown"
            threadName.Append( KMemSpyUnknownThreadName );
            }
        }
    //
    HBufC* ret = threadName.AllocLC();
    return ret;
    }