|         |      1 /* | 
|         |      2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).  | 
|         |      3 * All rights reserved. | 
|         |      4 * This component and the accompanying materials are made available | 
|         |      5 * under the terms of "Eclipse Public License v1.0" | 
|         |      6 * which accompanies this distribution, and is available | 
|         |      7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". | 
|         |      8 * | 
|         |      9 * Initial Contributors: | 
|         |     10 * Nokia Corporation - initial contribution. | 
|         |     11 * | 
|         |     12 * Contributors: | 
|         |     13 * | 
|         |     14 * Description: | 
|         |     15 * | 
|         |     16 */ | 
|         |     17  | 
|         |     18 #include <memspy/engine/memspyengineobjectthread.h> | 
|         |     19  | 
|         |     20 // System includes | 
|         |     21 #include <e32svr.h> | 
|         |     22  | 
|         |     23 // Driver includes | 
|         |     24 #include <memspy/driver/memspydriverclient.h> | 
|         |     25  | 
|         |     26 // User includes | 
|         |     27 #include <memspy/engine/memspyengine.h> | 
|         |     28 #include <memspy/engine/memspyengineutils.h> | 
|         |     29 #include <memspy/engine/memspyengineobjectprocess.h> | 
|         |     30 #include <memspy/engine/memspyengineobjectthreadinfocontainer.h> | 
|         |     31  | 
|         |     32 // Constants | 
|         |     33 _LIT( KMemSpyUnknownExitCategory, "Unknown ExitCat" ); | 
|         |     34 _LIT( KMemSpyUnknownThreadName, "Unknown Thread" ); | 
|         |     35 _LIT( KMemSpyThreadDoubleColon, "::" ); | 
|         |     36  | 
|         |     37  | 
|         |     38 CMemSpyThread::CMemSpyThread( TThreadId aId, CMemSpyProcess& aProcess ) | 
|         |     39 :   CMemSpyEngineObject( aProcess ), iId( aId ), iProcess( &aProcess ) | 
|         |     40     { | 
|         |     41     } | 
|         |     42  | 
|         |     43  | 
|         |     44 CMemSpyThread::~CMemSpyThread() | 
|         |     45     { | 
|         |     46     if  ( iInfoContainer ) | 
|         |     47         { | 
|         |     48         iInfoContainer->Close(); | 
|         |     49         } | 
|         |     50     delete iName; | 
|         |     51     } | 
|         |     52  | 
|         |     53  | 
|         |     54 void CMemSpyThread::ConstructL() | 
|         |     55     { | 
|         |     56     RefreshL(); | 
|         |     57     } | 
|         |     58  | 
|         |     59  | 
|         |     60 CMemSpyThread* CMemSpyThread::NewL( TThreadId aId, CMemSpyProcess& aProcess ) | 
|         |     61     { | 
|         |     62     CMemSpyThread* self = CMemSpyThread::NewLC( aId, aProcess ); | 
|         |     63     CleanupStack::Pop( self ); | 
|         |     64     return self; | 
|         |     65     } | 
|         |     66  | 
|         |     67  | 
|         |     68 CMemSpyThread* CMemSpyThread::NewLC( TThreadId aId, CMemSpyProcess& aProcess ) | 
|         |     69     { | 
|         |     70     CMemSpyThread* self = new(ELeave) CMemSpyThread( aId, aProcess ); | 
|         |     71     CleanupStack::PushL( self ); | 
|         |     72     self->ConstructL(); | 
|         |     73     return self; | 
|         |     74     } | 
|         |     75  | 
|         |     76  | 
|         |     77 EXPORT_C void CMemSpyThread::Open() | 
|         |     78     { | 
|         |     79     if  ( !OpenOrCloseInProgress() ) | 
|         |     80         { | 
|         |     81         SetOpenOrCloseInProgress( ETrue ); | 
|         |     82         CMemSpyEngineObject::Open(); | 
|         |     83         SetOpenOrCloseInProgress( EFalse ); | 
|         |     84         } | 
|         |     85     } | 
|         |     86  | 
|         |     87  | 
|         |     88 EXPORT_C void CMemSpyThread::Close() | 
|         |     89     { | 
|         |     90     if  ( !OpenOrCloseInProgress() ) | 
|         |     91         { | 
|         |     92         SetOpenOrCloseInProgress( ETrue ); | 
|         |     93         CMemSpyEngineObject::Close(); | 
|         |     94         SetOpenOrCloseInProgress( EFalse ); | 
|         |     95         } | 
|         |     96     } | 
|         |     97  | 
|         |     98  | 
|         |     99 void CMemSpyThread::AppendPriority( TDes& aDes, TThreadPriority aPriority ) | 
|         |    100     { | 
|         |    101     switch( aPriority ) | 
|         |    102         { | 
|         |    103     case EPriorityNull: | 
|         |    104         aDes += _L("[Null]"); | 
|         |    105         break; | 
|         |    106     case EPriorityMuchLess: | 
|         |    107         aDes += _L("[Much Less]"); | 
|         |    108         break; | 
|         |    109     case EPriorityLess: | 
|         |    110         aDes += _L("[Less]"); | 
|         |    111         break; | 
|         |    112     case EPriorityNormal: | 
|         |    113         aDes += _L("[Normal]"); | 
|         |    114         break; | 
|         |    115     case EPriorityMore: | 
|         |    116         aDes += _L("[More]"); | 
|         |    117         break; | 
|         |    118     case EPriorityMuchMore: | 
|         |    119         aDes += _L("[Much More]"); | 
|         |    120         break; | 
|         |    121     case EPriorityRealTime: | 
|         |    122         aDes += _L("[Real Time]"); | 
|         |    123         break; | 
|         |    124  | 
|         |    125     // Absolute values | 
|         |    126     case EPriorityAbsoluteVeryLow: | 
|         |    127         aDes += _L("[Abs Very Low]"); | 
|         |    128         break; | 
|         |    129     case EPriorityAbsoluteLowNormal: | 
|         |    130         aDes += _L("[Abs Low Norm]"); | 
|         |    131         break; | 
|         |    132     case EPriorityAbsoluteLow: | 
|         |    133         aDes += _L("[Abs Low]"); | 
|         |    134         break; | 
|         |    135     case EPriorityAbsoluteBackground: | 
|         |    136         aDes += _L("[Abs Bgnd]"); | 
|         |    137         break; | 
|         |    138     case EPriorityAbsoluteBackgroundNormal: | 
|         |    139         aDes += _L("[Abs Bgnd Norm]"); | 
|         |    140         break; | 
|         |    141     case EPriorityAbsoluteForeground: | 
|         |    142         aDes += _L("[Abs Fgnd]"); | 
|         |    143         break; | 
|         |    144     case EPriorityAbsoluteForegroundNormal: | 
|         |    145         aDes += _L("[Abs Fgnd Norm]"); | 
|         |    146         break; | 
|         |    147     case EPriorityAbsoluteHigh: | 
|         |    148         aDes += _L("[Abs High]"); | 
|         |    149         break; | 
|         |    150     case EPriorityAbsoluteHighNormal: | 
|         |    151         aDes += _L("[Abs High Norm]"); | 
|         |    152         break; | 
|         |    153     case EPriorityAbsoluteRealTime1: | 
|         |    154     case EPriorityAbsoluteRealTime2: | 
|         |    155     case EPriorityAbsoluteRealTime3: | 
|         |    156     case EPriorityAbsoluteRealTime4: | 
|         |    157     case EPriorityAbsoluteRealTime5: | 
|         |    158     case EPriorityAbsoluteRealTime6: | 
|         |    159     case EPriorityAbsoluteRealTime7: | 
|         |    160     case EPriorityAbsoluteRealTime8: | 
|         |    161         aDes.AppendFormat( _L("[Abs RT %d]"), ( aPriority - EPriorityAbsoluteRealTime1 ) + 1 ); | 
|         |    162         break; | 
|         |    163     default: | 
|         |    164         aDes += _L("[Unknown Pri.]"); | 
|         |    165         break; | 
|         |    166         } | 
|         |    167     } | 
|         |    168  | 
|         |    169  | 
|         |    170 void CMemSpyThread::AppendExitType( TDes& aDes, TExitType aType ) | 
|         |    171     { | 
|         |    172     _LIT( KExitTypeKilled, "Killed" ); | 
|         |    173     _LIT( KExitTypeTerminated, "Terminated" ); | 
|         |    174     _LIT( KExitTypePanicked, "Panicked" ); | 
|         |    175     _LIT( KExitTypePending, "Pending" ); | 
|         |    176      | 
|         |    177     // Panic and Terminate are exceptional exit conditions. | 
|         |    178     // Kill, is ironically, not an exceptional condition. | 
|         |    179     switch( aType ) | 
|         |    180         { | 
|         |    181     case EExitKill: | 
|         |    182         aDes += KExitTypeKilled; | 
|         |    183         break; | 
|         |    184     case EExitTerminate: | 
|         |    185         aDes += KExitTypeTerminated; | 
|         |    186         break; | 
|         |    187     case EExitPanic: | 
|         |    188         aDes += KExitTypePanicked; | 
|         |    189         break; | 
|         |    190     default: | 
|         |    191     case EExitPending: | 
|         |    192         aDes += KExitTypePending; | 
|         |    193         break; | 
|         |    194         } | 
|         |    195     } | 
|         |    196  | 
|         |    197  | 
|         |    198 void CMemSpyThread::AppendExitInfo( TDes& aDes, TExitType aType, TInt aExitReason, const TDesC& aExitCategory ) | 
|         |    199     { | 
|         |    200     aDes.Append( '[' ); | 
|         |    201     const TInt length = aDes.Length(); | 
|         |    202     AppendExitType( aDes, aType ); | 
|         |    203     aDes.SetLength( length + 1 ); // Remove all but the first letter | 
|         |    204     aDes.Append( ']' ); | 
|         |    205      | 
|         |    206     if  ( aType == EExitKill || aType == EExitPending ) | 
|         |    207         { | 
|         |    208         // Kill implies "clean" exit. Pending implies not yet dead. | 
|         |    209         } | 
|         |    210     else | 
|         |    211         { | 
|         |    212         TMemSpyTruncateOverflow overflow; | 
|         |    213  | 
|         |    214         // Terminate or Panic implies abnormal exit condition, so | 
|         |    215         // show full exit info. | 
|         |    216         _LIT( KAbnormalFormatSpec, " %S-%d" ); | 
|         |    217         aDes.AppendFormat( KAbnormalFormatSpec, &overflow, &aExitCategory, aExitReason ); | 
|         |    218         } | 
|         |    219     } | 
|         |    220  | 
|         |    221  | 
|         |    222 CMemSpyEngine& CMemSpyThread::Engine() const | 
|         |    223     { | 
|         |    224     return Process().Engine(); | 
|         |    225     } | 
|         |    226  | 
|         |    227  | 
|         |    228 void CMemSpyThread::OpenLC( RThread& aThread ) | 
|         |    229     { | 
|         |    230     const TInt error = Open( aThread ); | 
|         |    231     User::LeaveIfError( error ); | 
|         |    232     CleanupClosePushL( aThread ); | 
|         |    233     } | 
|         |    234  | 
|         |    235  | 
|         |    236 TInt CMemSpyThread::Open( RThread& aThread ) | 
|         |    237     { | 
|         |    238     CMemSpyEngine& engine = iProcess->Engine(); | 
|         |    239     RMemSpyDriverClient& driver = engine.Driver(); | 
|         |    240     const TInt error = driver.OpenThread( iId, aThread ); | 
|         |    241     return error; | 
|         |    242     } | 
|         |    243  | 
|         |    244  | 
|         |    245 EXPORT_C TPtrC CMemSpyThread::Name() const | 
|         |    246     { | 
|         |    247     // Just return the pure name, minus the leading tab | 
|         |    248     TPtrC pRet( iName->Mid(2) ); | 
|         |    249      | 
|         |    250     // Find the last tab position | 
|         |    251     TInt pos = pRet.Locate(TChar('\t')); | 
|         |    252     if  ( pos > 0 ) | 
|         |    253         { | 
|         |    254         pRet.Set( pRet.Left( pos ) ); | 
|         |    255         } | 
|         |    256     //     | 
|         |    257     return pRet; | 
|         |    258     } | 
|         |    259  | 
|         |    260  | 
|         |    261 EXPORT_C TFullName CMemSpyThread::FullName() const | 
|         |    262     { | 
|         |    263     TFullName name( iProcess->Name() ); | 
|         |    264     name += KMemSpyThreadDoubleColon; | 
|         |    265     name += Name(); | 
|         |    266     // | 
|         |    267     return name; | 
|         |    268     } | 
|         |    269  | 
|         |    270  | 
|         |    271 EXPORT_C TBool CMemSpyThread::IsSystemPermanent() const | 
|         |    272     { | 
|         |    273     const TBool ret = ( iFlags & KThreadFlagSystemPermanent ); | 
|         |    274     return ret; | 
|         |    275     } | 
|         |    276  | 
|         |    277  | 
|         |    278 EXPORT_C TBool CMemSpyThread::IsSystemCritical() const | 
|         |    279     { | 
|         |    280     const TBool ret = ( iFlags & KThreadFlagSystemCritical ); | 
|         |    281     return ret; | 
|         |    282     } | 
|         |    283  | 
|         |    284  | 
|         |    285 EXPORT_C CMemSpyThreadInfoContainer& CMemSpyThread::InfoContainerL() | 
|         |    286     { | 
|         |    287     if  ( iInfoContainer == NULL ) | 
|         |    288         { | 
|         |    289         const TBool KConstructAsynchronously = ETrue; | 
|         |    290         iInfoContainer = CMemSpyThreadInfoContainer::NewL( *this, KConstructAsynchronously ); | 
|         |    291         } | 
|         |    292     // | 
|         |    293     return *iInfoContainer; | 
|         |    294     } | 
|         |    295  | 
|         |    296  | 
|         |    297 EXPORT_C CMemSpyThreadInfoContainer& CMemSpyThread::InfoContainerForceSyncronousConstructionL() | 
|         |    298     { | 
|         |    299     if  ( iInfoContainer == NULL ) | 
|         |    300         { | 
|         |    301         const TBool KConstructSynchronously = EFalse; | 
|         |    302         iInfoContainer = CMemSpyThreadInfoContainer::NewL( *this, KConstructSynchronously ); | 
|         |    303         } | 
|         |    304     // | 
|         |    305     return *iInfoContainer; | 
|         |    306     } | 
|         |    307  | 
|         |    308  | 
|         |    309 EXPORT_C void CMemSpyThread::KillL() | 
|         |    310     { | 
|         |    311     CMemSpyEngine& engine = iProcess->Engine(); | 
|         |    312     RMemSpyDriverClient& driver = engine.Driver(); | 
|         |    313     // | 
|         |    314     User::LeaveIfError( driver.ThreadEnd( Id(), EExitKill ) ); | 
|         |    315     } | 
|         |    316  | 
|         |    317  | 
|         |    318 EXPORT_C void CMemSpyThread::TerminateL() | 
|         |    319     { | 
|         |    320     CMemSpyEngine& engine = iProcess->Engine(); | 
|         |    321     RMemSpyDriverClient& driver = engine.Driver(); | 
|         |    322     // | 
|         |    323     User::LeaveIfError( driver.ThreadEnd( Id(), EExitTerminate ) ); | 
|         |    324     } | 
|         |    325  | 
|         |    326  | 
|         |    327 EXPORT_C void CMemSpyThread::PanicL() | 
|         |    328     { | 
|         |    329     CMemSpyEngine& engine = iProcess->Engine(); | 
|         |    330     RMemSpyDriverClient& driver = engine.Driver(); | 
|         |    331     // | 
|         |    332     User::LeaveIfError( driver.ThreadEnd( Id(), EExitPanic ) ); | 
|         |    333     } | 
|         |    334  | 
|         |    335  | 
|         |    336 EXPORT_C void CMemSpyThread::SetPriorityL( TThreadPriority aPriority ) | 
|         |    337     { | 
|         |    338 #ifdef _DEBUG | 
|         |    339     RDebug::Printf( "CMemSpyThread::SetPriorityL() - START - aPriority: %d, orig pri: %d", aPriority, iPriority ); | 
|         |    340 #endif | 
|         |    341     CMemSpyEngine& engine = iProcess->Engine(); | 
|         |    342     RMemSpyDriverClient& driver = engine.Driver(); | 
|         |    343     // | 
|         |    344     const TInt err = driver.SetPriority( Id(), aPriority ); | 
|         |    345 #ifdef _DEBUG | 
|         |    346     TInt newPri = -1; | 
|         |    347     RThread thread; | 
|         |    348     if ( driver.OpenThread( iId, thread ) == KErrNone ) | 
|         |    349         { | 
|         |    350         newPri = thread.Priority(); | 
|         |    351         thread.Close(); | 
|         |    352         } | 
|         |    353     RDebug::Printf( "CMemSpyThread::SetPriorityL() - err: %d, newPri: %d", err, newPri ); | 
|         |    354 #endif | 
|         |    355  | 
|         |    356     User::LeaveIfError( err ); | 
|         |    357     RefreshL(); | 
|         |    358 #ifdef _DEBUG | 
|         |    359     RDebug::Printf( "CMemSpyThread::SetPriorityL() - END" ); | 
|         |    360 #endif | 
|         |    361     } | 
|         |    362  | 
|         |    363  | 
|         |    364 void CMemSpyThread::SetDeadL() | 
|         |    365     { | 
|         |    366     RefreshL(); | 
|         |    367     } | 
|         |    368  | 
|         |    369  | 
|         |    370 void CMemSpyThread::SetDeadL( const RThread& aThread ) | 
|         |    371     { | 
|         |    372     RefreshL( aThread ); | 
|         |    373 	} | 
|         |    374  | 
|         |    375  | 
|         |    376 void CMemSpyThread::FullName( TDes& aName ) const | 
|         |    377     { | 
|         |    378     iProcess->FullName( aName ); | 
|         |    379     aName.Append( KMemSpyThreadDoubleColon ); | 
|         |    380     aName.Append( Name() ); | 
|         |    381     } | 
|         |    382  | 
|         |    383  | 
|         |    384 EXPORT_C TBool CMemSpyThread::IsDead() const | 
|         |    385     { | 
|         |    386     const TBool isDead = ( iExitType != EExitPending ); | 
|         |    387     return  isDead; | 
|         |    388     } | 
|         |    389  | 
|         |    390  | 
|         |    391 void CMemSpyThread::RefreshL() | 
|         |    392     { | 
|         |    393     CMemSpyEngine& engine = iProcess->Engine(); | 
|         |    394     RMemSpyDriverClient& driver = engine.Driver(); | 
|         |    395      | 
|         |    396     // Try to open thread. We use the device driver since | 
|         |    397     // it doesn't check (i.e. it bypasses) some of the conditions which the | 
|         |    398     // default RThread::Open() implementation enforces... | 
|         |    399     // | 
|         |    400     // Deliberately ignore error. The other overload of RefreshL will | 
|         |    401     // cope with the failure. | 
|         |    402     RThread thread; | 
|         |    403     driver.OpenThread( iId, thread ); | 
|         |    404     CleanupClosePushL( thread ); | 
|         |    405  | 
|         |    406     // Call refresh with thread to perform actual heavy lifting... | 
|         |    407     RefreshL( thread ); | 
|         |    408  | 
|         |    409     // Clean up. This thread handle might actually not even be open, but that's okay... | 
|         |    410     CleanupStack::PopAndDestroy( &thread ); | 
|         |    411     } | 
|         |    412  | 
|         |    413  | 
|         |    414 void CMemSpyThread::RefreshL( const RThread& aThread ) | 
|         |    415     { | 
|         |    416     const TBool handleValid = aThread.Handle() != KNullHandle; | 
|         |    417     if  ( handleValid ) | 
|         |    418         { | 
|         |    419         // Annoyingly, we request the entire thread info structure just to get the thread flags... | 
|         |    420         iFlags = 0; | 
|         |    421         const User::TCritical critType = User::Critical( aThread ); | 
|         |    422         if  ( critType == User::ESystemPermanent ) | 
|         |    423             { | 
|         |    424             iFlags |= KThreadFlagSystemPermanent; | 
|         |    425             } | 
|         |    426         else if ( critType == User::ESystemCritical ) | 
|         |    427             { | 
|         |    428             iFlags |= KThreadFlagSystemCritical; | 
|         |    429             } | 
|         |    430  | 
|         |    431 #ifdef _DEBUG | 
|         |    432         TMemSpyDriverThreadInfo threadInfo; | 
|         |    433         User::LeaveIfError( iProcess->Engine().Driver().GetThreadInfo( iId, threadInfo ) ); | 
|         |    434         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 ); | 
|         |    435 #endif | 
|         |    436         } | 
|         |    437  | 
|         |    438     // Get exit info | 
|         |    439     iExitType = handleValid ? aThread.ExitType() : EExitKill; | 
|         |    440     iPriority = handleValid ? aThread.Priority() : EPriorityNormal; | 
|         |    441  | 
|         |    442     // If the thread is dead then we may not be able to get some attributes | 
|         |    443     // (it depends on whether the thread handle is valid anymore). | 
|         |    444     iExitReason = 0; | 
|         |    445     iExitCategory.Zero(); | 
|         |    446  | 
|         |    447     if  ( IsDead() ) | 
|         |    448         { | 
|         |    449         if  ( handleValid ) | 
|         |    450             { | 
|         |    451             iExitReason = aThread.ExitReason(); | 
|         |    452             iExitCategory = aThread.ExitCategory();  | 
|         |    453             } | 
|         |    454         else | 
|         |    455             { | 
|         |    456             iExitCategory = KMemSpyUnknownExitCategory; | 
|         |    457             } | 
|         |    458         } | 
|         |    459     else | 
|         |    460         { | 
|         |    461         } | 
|         |    462  | 
|         |    463     // Get raw thread name | 
|         |    464     HBufC* rawThreadName = GetThreadNameLC( aThread );  | 
|         |    465  | 
|         |    466     // Full name is enough for the thread name as well as the extra info | 
|         |    467     // we show | 
|         |    468     TFullName name; | 
|         |    469  | 
|         |    470     // Build S60 listbox formatted name | 
|         |    471     _LIT( KMemSpyThreadNameFormatSpecBasicName, " \t%S\t\t" ); | 
|         |    472     name.Format( KMemSpyThreadNameFormatSpecBasicName, rawThreadName ); | 
|         |    473     CleanupStack::PopAndDestroy( rawThreadName ); | 
|         |    474  | 
|         |    475     // If the thread is dead show exit info | 
|         |    476     if  ( IsDead() ) | 
|         |    477         { | 
|         |    478         AppendExitInfo( name, iExitType, iExitReason, iExitCategory ); | 
|         |    479         } | 
|         |    480     else | 
|         |    481         { | 
|         |    482         // Otherwise, show priority | 
|         |    483         AppendPriority( name, iPriority ); | 
|         |    484         } | 
|         |    485  | 
|         |    486     // Save new fully formatted name | 
|         |    487     HBufC* newName = name.AllocL(); | 
|         |    488     delete iName; | 
|         |    489     iName = newName; | 
|         |    490     } | 
|         |    491  | 
|         |    492  | 
|         |    493 HBufC* CMemSpyThread::GetThreadNameLC( const RThread& aThreadOrNull ) const | 
|         |    494     { | 
|         |    495     TName threadName; | 
|         |    496     // | 
|         |    497     const TBool handleValid = aThreadOrNull.Handle() != KNullHandle; | 
|         |    498     // | 
|         |    499     if  ( handleValid ) | 
|         |    500         { | 
|         |    501         // Easy case - we have a valid thread handle. | 
|         |    502         threadName.Append( aThreadOrNull.Name() ); | 
|         |    503         } | 
|         |    504     else | 
|         |    505         { | 
|         |    506         // Since we don't have the possibility to enquire after the thread's name | 
|         |    507         // we'll assume that it used to be alive and therefore at some point we did | 
|         |    508         // manage to grep it's name... | 
|         |    509         if  ( iName ) | 
|         |    510             { | 
|         |    511             const TPtrC pOriginalName( Name() ); | 
|         |    512             threadName.Append( pOriginalName ); | 
|         |    513             } | 
|         |    514         else | 
|         |    515             { | 
|         |    516             // Don't have a thread handle, don't have any possibility to get the | 
|         |    517             // name from a prior cached version. Must use "unknown" | 
|         |    518             threadName.Append( KMemSpyUnknownThreadName ); | 
|         |    519             } | 
|         |    520         } | 
|         |    521     // | 
|         |    522     HBufC* ret = threadName.AllocLC(); | 
|         |    523     return ret; | 
|         |    524     } | 
|         |    525  | 
|         |    526  | 
|         |    527  |