|         |      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/memspyenginehelperstack.h> | 
|         |     19  | 
|         |     20 // Driver includes | 
|         |     21 #include <memspy/driver/memspydriverclient.h> | 
|         |     22  | 
|         |     23 // User includes | 
|         |     24 #include <memspy/engine/memspyengine.h> | 
|         |     25 #include <memspy/engine/memspyengineoutputsink.h> | 
|         |     26 #include <memspy/engine/memspyengineoutputlist.h> | 
|         |     27 #include <memspy/engine/memspyengineobjectthread.h> | 
|         |     28 #include <memspy/engine/memspyengineobjectprocess.h> | 
|         |     29 #include "MemSpyEngineOutputListItem.h" | 
|         |     30 #include <memspy/engine/memspyengineobjectcontainer.h> | 
|         |     31 #include <memspy/engine/memspyenginehelpercodesegment.h> | 
|         |     32  | 
|         |     33 // Literal constants | 
|         |     34 _LIT( KMemSpyPrefixStackData, "StackData - %S - " ); | 
|         |     35 _LIT( KMemSpyMarkerStackData, "<%SMEMSPY_STACK_DATA_%03d>" ); | 
|         |     36  | 
|         |     37  | 
|         |     38 CMemSpyEngineHelperStack::CMemSpyEngineHelperStack( CMemSpyEngine& aEngine ) | 
|         |     39 :   iEngine( aEngine ) | 
|         |     40     { | 
|         |     41     } | 
|         |     42  | 
|         |     43      | 
|         |     44 CMemSpyEngineHelperStack::~CMemSpyEngineHelperStack() | 
|         |     45     { | 
|         |     46     } | 
|         |     47  | 
|         |     48  | 
|         |     49 void CMemSpyEngineHelperStack::ConstructL() | 
|         |     50     { | 
|         |     51     } | 
|         |     52  | 
|         |     53  | 
|         |     54 CMemSpyEngineHelperStack* CMemSpyEngineHelperStack::NewL( CMemSpyEngine& aEngine ) | 
|         |     55     { | 
|         |     56     CMemSpyEngineHelperStack* self = new(ELeave) CMemSpyEngineHelperStack( aEngine ); | 
|         |     57     CleanupStack::PushL( self ); | 
|         |     58     self->ConstructL(); | 
|         |     59     CleanupStack::Pop( self ); | 
|         |     60     return self; | 
|         |     61     } | 
|         |     62  | 
|         |     63  | 
|         |     64 EXPORT_C void CMemSpyEngineHelperStack::OutputStackInfoL( const CMemSpyThread& aThread ) | 
|         |     65     { | 
|         |     66     const TFullName pName( aThread.FullName() ); | 
|         |     67     // | 
|         |     68     _LIT(KHeader, "STACK INFO FOR THREAD '%S'"); | 
|         |     69     TBuf<KMaxFullName + 100> printFormat; | 
|         |     70     printFormat.Format( KHeader, &pName ); | 
|         |     71     iEngine.Sink().OutputSectionHeadingL( printFormat, '=' ); | 
|         |     72  | 
|         |     73     OutputStackInfoL( aThread.Process().Id(), aThread.Id(), printFormat ); | 
|         |     74     } | 
|         |     75  | 
|         |     76  | 
|         |     77 EXPORT_C void CMemSpyEngineHelperStack::OutputStackInfoL( TProcessId aPid, TThreadId aTid, TDes& aLineBuffer ) | 
|         |     78     { | 
|         |     79     TMemSpyDriverStackInfo info; | 
|         |     80     // | 
|         |     81     iEngine.ProcessSuspendLC( aPid ); | 
|         |     82     const TInt r = iEngine.Driver().GetStackInfo( aTid, info ); | 
|         |     83     CleanupStack::PopAndDestroy(); // ProcessSuspendLC | 
|         |     84     // | 
|         |     85     if  ( r == KErrNone ) | 
|         |     86         { | 
|         |     87         CMemSpyEngineOutputList* list = CMemSpyEngineOutputList::NewLC( iEngine.Sink() ); | 
|         |     88  | 
|         |     89         { | 
|         |     90         // Header - user stack | 
|         |     91         list->AddItemL( _L("USER STACK") ); | 
|         |     92         list->AddUnderlineForPreviousItemL(); | 
|         |     93  | 
|         |     94         // Summary | 
|         |     95         list->AddItemFormatL( _L("Address range"), _L("0x%08x - 0x%08x (%8d)"), info.iUserStackBase, info.iUserStackBase + info.iUserStackSize, info.iUserStackSize ); | 
|         |     96         list->AddItemHexL( _L("Current stack pointer"), info.iUserStackPointer ); | 
|         |     97  | 
|         |     98         // Calculate usage | 
|         |     99         const TInt usedUserStack = (TInt) ( info.iUserStackBase + info.iUserStackSize ) - info.iUserStackPointer; | 
|         |    100         const TInt userStackUsagePct = (TInt) (( (TReal) usedUserStack / (TReal) info.iUserStackSize) * 100.0); | 
|         |    101         aLineBuffer.Format(_L("%d (%3d"), usedUserStack, userStackUsagePct); | 
|         |    102         aLineBuffer.Append(_L(" pct)")); | 
|         |    103         list->AddItemL( _L("Stack usage"), aLineBuffer ); | 
|         |    104  | 
|         |    105         // High watermark | 
|         |    106         list->AddItemHexL( _L("High watermark"), info.iUserStackHighWatermark ); | 
|         |    107         const TInt userHighWaterMarkUsage = (TInt) ( info.iUserStackBase + info.iUserStackSize ) - info.iUserStackHighWatermark; | 
|         |    108         const TInt userStackHighWaterMarkUsagePct = (TInt) (( (TReal) userHighWaterMarkUsage / (TReal) info.iUserStackSize) * 100.0); | 
|         |    109         aLineBuffer.Format(_L("%d (%3d"), userHighWaterMarkUsage, userStackHighWaterMarkUsagePct); | 
|         |    110         aLineBuffer.Append(_L(" pct)")); | 
|         |    111         list->AddItemL( _L("High watermark usage"), aLineBuffer ); | 
|         |    112         } | 
|         |    113  | 
|         |    114  | 
|         |    115         { | 
|         |    116         // Header - supervisor stack | 
|         |    117         list->AddItemL( _L("SUPERVISOR STACK") ); | 
|         |    118         list->AddUnderlineForPreviousItemL(); | 
|         |    119  | 
|         |    120         // Summary | 
|         |    121         list->AddItemFormatL( _L("Address range"), _L("0x%08x - 0x%08x (%8d)"), info.iSupervisorStackBase, info.iSupervisorStackBase + info.iSupervisorStackSize, info.iSupervisorStackSize ); | 
|         |    122         list->AddItemHexL( _L("Current stack pointer"), info.iSupervisorStackPointer ); | 
|         |    123  | 
|         |    124         // Calculate usage | 
|         |    125         const TInt usedSupervisorStack = (TInt) ( info.iSupervisorStackBase + info.iSupervisorStackSize ) - info.iSupervisorStackPointer; | 
|         |    126         const TInt supervisorStackUsagePct = (TInt) (( (TReal) usedSupervisorStack / (TReal) info.iSupervisorStackSize) * 100.0); | 
|         |    127         aLineBuffer.Format(_L("%d (%3d"), usedSupervisorStack, supervisorStackUsagePct ); | 
|         |    128         aLineBuffer.Append(_L(" pct)")); | 
|         |    129         list->AddItemL( _L("Stack usage"), aLineBuffer ); | 
|         |    130  | 
|         |    131         // High watermark | 
|         |    132         list->AddItemHexL( _L("High watermark"), info.iSupervisorStackHighWatermark ); | 
|         |    133         const TInt supervisorStackHighWaterMarkUsage = (TInt) ( info.iSupervisorStackBase + info.iSupervisorStackSize ) - info.iSupervisorStackHighWatermark; | 
|         |    134         const TInt supervisorStackHighWaterMarkUsagePct = (TInt) (( (TReal) supervisorStackHighWaterMarkUsage / (TReal) info.iSupervisorStackSize) * 100.0); | 
|         |    135         aLineBuffer.Format(_L("%d (%3d"), supervisorStackHighWaterMarkUsage, supervisorStackHighWaterMarkUsagePct ); | 
|         |    136         aLineBuffer.Append(_L(" pct)")); | 
|         |    137         list->AddItemL( _L("High watermark usage"), aLineBuffer ); | 
|         |    138         } | 
|         |    139  | 
|         |    140         list->PrintL(); | 
|         |    141         CleanupStack::PopAndDestroy( list ); | 
|         |    142         } | 
|         |    143  | 
|         |    144     } | 
|         |    145  | 
|         |    146  | 
|         |    147 EXPORT_C void CMemSpyEngineHelperStack::OutputStackDataL( const CMemSpyThread& aThread, TMemSpyDriverDomainType aType ) | 
|         |    148     { | 
|         |    149     OutputStackDataL( aThread, aType, ETrue ); | 
|         |    150     } | 
|         |    151  | 
|         |    152  | 
|         |    153 EXPORT_C void CMemSpyEngineHelperStack::OutputStackDataL( const CMemSpyThread& aThread, TMemSpyDriverDomainType aType, TBool aEntireStack ) | 
|         |    154     { | 
|         |    155     TBuf<KMaxFullName + 100> printFormat; | 
|         |    156     printFormat = aThread.FullName(); | 
|         |    157  | 
|         |    158     // Begin a new data stream | 
|         |    159     _LIT( KMemSpyFolder, "Stack" ); | 
|         |    160     HBufC* context = HBufC::NewLC( KMaxFileName ); | 
|         |    161     TPtr pContext( context->Des() ); | 
|         |    162     if  ( aType == EMemSpyDriverDomainUser ) | 
|         |    163         { | 
|         |    164         _LIT(KMemSpyContext, "Data (User) - %S"); | 
|         |    165         pContext.Format( KMemSpyContext, &printFormat ); | 
|         |    166         } | 
|         |    167     else if ( aType == EMemSpyDriverDomainKernel ) | 
|         |    168         { | 
|         |    169         _LIT(KMemSpyContext, "Data (Supervisor) - %S"); | 
|         |    170         pContext.Format( KMemSpyContext, &printFormat ); | 
|         |    171         } | 
|         |    172     iEngine.Sink().DataStreamBeginL( pContext, KMemSpyFolder ); | 
|         |    173     CleanupStack::PopAndDestroy( context ); | 
|         |    174  | 
|         |    175     // Suspend all threads in the process | 
|         |    176     iEngine.ProcessSuspendLC( aThread.Process().Id() ); | 
|         |    177  | 
|         |    178     // Start marker | 
|         |    179     iEngine.Sink().OutputLineFormattedL( KMemSpyMarkerStackData, &KNullDesC, (TUint) aThread.Id() ); | 
|         |    180  | 
|         |    181     // Set overall prefix | 
|         |    182     iEngine.Sink().OutputPrefixSetFormattedLC( KMemSpyPrefixStackData, &printFormat ); | 
|         |    183  | 
|         |    184     // Prepare data buffer | 
|         |    185     HBufC8* data = HBufC8::NewLC( 4096 * 4 ); | 
|         |    186     TPtr8 pData(data->Des()); | 
|         |    187     TUint remaining = 0; | 
|         |    188  | 
|         |    189     TMemSpyDriverStackInfo info; | 
|         |    190     TInt r = iEngine.Driver().GetStackInfo( aThread.Id(), info ); | 
|         |    191     if  ( r == KErrNone ) | 
|         |    192         { | 
|         |    193         TUint spAddress = 0; | 
|         |    194         if  ( aType == EMemSpyDriverDomainUser ) | 
|         |    195             { | 
|         |    196             _LIT(KHeaderUser, "USER STACK DATA"); | 
|         |    197             iEngine.Sink().OutputSectionHeadingL(KHeaderUser, '-'); | 
|         |    198             spAddress = info.iUserStackPointer; | 
|         |    199             } | 
|         |    200         else if ( aType == EMemSpyDriverDomainKernel ) | 
|         |    201             { | 
|         |    202             _LIT(KHeaderKernel, "SUPERVISOR STACK DATA"); | 
|         |    203             iEngine.Sink().OutputSectionHeadingL(KHeaderKernel, '-'); | 
|         |    204             spAddress = info.iSupervisorStackPointer; | 
|         |    205             } | 
|         |    206  | 
|         |    207         // Print header information | 
|         |    208         // ======================== | 
|         |    209         TBuf<240> buf; | 
|         |    210  | 
|         |    211         // Stack pointer | 
|         |    212         _LIT( KLine1, "Current stack pointer: 0x%08x"); | 
|         |    213         buf.Format( KLine1, spAddress ); | 
|         |    214         iEngine.Sink().OutputLineL( buf ); | 
|         |    215  | 
|         |    216         // Stack address range | 
|         |    217         _LIT( KLine2, "Stack address range:   0x%08x - 0x%08x"); | 
|         |    218         if  ( aType == EMemSpyDriverDomainUser ) | 
|         |    219             { | 
|         |    220             buf.Format( KLine2, info.iUserStackBase, info.iUserStackBase + info.iUserStackSize ); | 
|         |    221             } | 
|         |    222         else | 
|         |    223             { | 
|         |    224             buf.Format( KLine2, info.iSupervisorStackBase, info.iSupervisorStackBase + info.iSupervisorStackSize ); | 
|         |    225             } | 
|         |    226         iEngine.Sink().OutputLineL( buf ); | 
|         |    227  | 
|         |    228         // Stack size | 
|         |    229         _LIT( KLine3, "Stack size:              %d"); | 
|         |    230         buf.Format( KLine3, ( aType == EMemSpyDriverDomainUser ) ? info.iUserStackSize : info.iSupervisorStackSize ); | 
|         |    231         iEngine.Sink().OutputLineL( buf ); | 
|         |    232         iEngine.Sink().OutputBlankLineL(); | 
|         |    233  | 
|         |    234         // If we are only fetching the 'current' part of the stack, then we need to maniuplate the | 
|         |    235         // printing address used to display the stack content | 
|         |    236         if  ( !aEntireStack ) | 
|         |    237             { | 
|         |    238             // We start at the stack pointer address and work towards the end of the stack. | 
|         |    239             info.iUserStackBase = spAddress; | 
|         |    240             } | 
|         |    241  | 
|         |    242         // Code segments (needed for map file reading...) | 
|         |    243         _LIT(KCodeSegInfoPrefix, "CodeSeg - "); | 
|         |    244         iEngine.HelperCodeSegment().OutputCodeSegmentsL( aThread.Process().Id(), printFormat, KCodeSegInfoPrefix ); | 
|         |    245          | 
|         |    246         // Get the stack data | 
|         |    247         // ================== | 
|         |    248         _LIT(KStackDataPrefix, "%S"); | 
|         |    249         r = iEngine.Driver().GetStackData( aThread.Id(), pData, remaining, aType, aEntireStack ); | 
|         |    250  | 
|         |    251         if  ( r == KErrNone ) | 
|         |    252             { | 
|         |    253             while ( r == KErrNone ) | 
|         |    254                 { | 
|         |    255                 iEngine.Sink().OutputBinaryDataL( KStackDataPrefix, pData.Ptr(), (const TUint8*) info.iUserStackBase, pData.Length() ); | 
|         |    256                 // | 
|         |    257                 if  ( remaining > 0 ) | 
|         |    258                     { | 
|         |    259                     info.iUserStackBase += pData.Length(); | 
|         |    260                     r = iEngine.Driver().GetStackDataNext( aThread.Id(), pData, remaining, aType, aEntireStack ); | 
|         |    261                     } | 
|         |    262                 else | 
|         |    263                     { | 
|         |    264                     break; | 
|         |    265                     } | 
|         |    266                 } | 
|         |    267             } | 
|         |    268  | 
|         |    269         } | 
|         |    270     CleanupStack::PopAndDestroy( data ); | 
|         |    271  | 
|         |    272     CleanupStack::PopAndDestroy(); // clear prefix | 
|         |    273     CleanupStack::PopAndDestroy(); // resume process | 
|         |    274  | 
|         |    275     // End marker | 
|         |    276     iEngine.Sink().OutputLineFormattedL( KMemSpyMarkerStackData, &KMemSpySinkTagClose, (TUint) aThread.Id() ); | 
|         |    277     iEngine.Sink().DataStreamEndL(); | 
|         |    278     } | 
|         |    279  | 
|         |    280  | 
|         |    281 EXPORT_C void CMemSpyEngineHelperStack::OutputStackInfoForDeviceL() | 
|         |    282     { | 
|         |    283     const TInt count = iEngine.Container().Count(); | 
|         |    284     // | 
|         |    285     HBufC* buf = HBufC::NewLC( 1024 ); | 
|         |    286     TPtr pBuf(buf->Des()); | 
|         |    287     // | 
|         |    288     _LIT( KMemSpyContext, "Stack" ); | 
|         |    289     _LIT( KMemSpyFolder, "Device-Wide" ); | 
|         |    290     _LIT( KMemSpyExtension, ".csv" ); | 
|         |    291     iEngine.Sink().DataStreamBeginL( KMemSpyContext, KMemSpyFolder, KMemSpyExtension ); | 
|         |    292  | 
|         |    293     // Set overall prefix | 
|         |    294     _LIT(KOverallPrefix, "[Stack Summary]"); | 
|         |    295     iEngine.Sink().OutputPrefixSetLC( KOverallPrefix ); | 
|         |    296  | 
|         |    297     _LIT(KListingHeader, "Thread, US. Base Address, US. Size, US. Addr, US. Usage, US. Usage Pct, US. HWM Addr, US. HWM Usage, US. HWM Usage Pct, SS. Base Address, SS. Size, SS. Addr, SS. Usage, SS. Usage Pct., SS. HWM Addr, SS. HWM Usage, SS. HWM Usage Pct"); | 
|         |    298     iEngine.Sink().OutputBlankLineL(); | 
|         |    299     iEngine.Sink().OutputLineL(KListingHeader); | 
|         |    300      | 
|         |    301     for(TInt ii=0; ii<count; ii++) | 
|         |    302         { | 
|         |    303         const CMemSpyProcess& process = iEngine.Container().At( ii ); | 
|         |    304         const TPtrC procName( process.Name() ); | 
|         |    305         // | 
|         |    306         if  ( iEngine.ProcessSuspendAndGetErrorLC( process.Id() ) == KErrNone ) | 
|         |    307             { | 
|         |    308             TMemSpyDriverStackInfo info; | 
|         |    309             const TInt threadCount = process.Count(); | 
|         |    310             // | 
|         |    311             for(TInt j=0; j<threadCount; j++) | 
|         |    312                 { | 
|         |    313                 const CMemSpyThread& thread = process.At( j ); | 
|         |    314                 const TPtrC threadName(thread.Name()); | 
|         |    315  | 
|         |    316                 const TInt error = iEngine.Driver().GetStackInfo( thread.Id(), info ); | 
|         |    317                 if  ( error == KErrNone ) | 
|         |    318                     { | 
|         |    319                     const TInt userStackUsage = (TInt) ( info.iUserStackBase + info.iUserStackSize ) - info.iUserStackPointer; | 
|         |    320                     const TInt userStackUsagePct = (TInt) (( (TReal) userStackUsage / (TReal) info.iUserStackSize) * 100.0); | 
|         |    321                     const TInt userStackHighWaterMarkUsage = (TInt) ( info.iUserStackBase + info.iUserStackSize ) - info.iUserStackHighWatermark; | 
|         |    322                     const TInt userStackHighWaterMarkUsagePct = (TInt) (( (TReal) userStackHighWaterMarkUsage / (TReal) info.iUserStackSize) * 100.0); | 
|         |    323                     const TInt supervisorStackUsage = (TInt) ( info.iSupervisorStackBase + info.iSupervisorStackSize ) - info.iSupervisorStackPointer; | 
|         |    324                     const TInt supervisorStackUsagePct = (TInt) (( (TReal) supervisorStackUsage / (TReal) info.iSupervisorStackSize) * 100.0); | 
|         |    325                     const TInt supervisorStackHighWaterMarkUsage = (TInt) ( info.iSupervisorStackBase + info.iSupervisorStackSize ) - info.iSupervisorStackHighWatermark; | 
|         |    326                     const TInt supervisorStackHighWaterMarkUsagePct = (TInt) (( (TReal) supervisorStackHighWaterMarkUsage / (TReal) info.iSupervisorStackSize) * 100.0); | 
|         |    327  | 
|         |    328                     _LIT(KFormat, "%S::%S, 0x%08x, %8d, 0x%08x, %8d, %8d, 0x%08x, %8d, %8d, 0x%08x, %8d, 0x%08x, %8d, %8d, 0x%08x, %8d, %8d"); | 
|         |    329                     pBuf.Format(  KFormat, &procName, &threadName,  | 
|         |    330                                  info.iUserStackBase,  | 
|         |    331                                  info.iUserStackSize,  | 
|         |    332                                  info.iUserStackPointer,  | 
|         |    333                                  userStackUsage, | 
|         |    334                                  userStackUsagePct, | 
|         |    335                                  info.iUserStackHighWatermark, | 
|         |    336                                  userStackHighWaterMarkUsage, | 
|         |    337                                  userStackHighWaterMarkUsagePct, | 
|         |    338                                  info.iSupervisorStackBase, | 
|         |    339                                  info.iSupervisorStackSize, | 
|         |    340                                  info.iSupervisorStackPointer, | 
|         |    341                                  supervisorStackUsage, | 
|         |    342                                  supervisorStackUsagePct, | 
|         |    343                                  info.iSupervisorStackHighWatermark, | 
|         |    344                                  supervisorStackHighWaterMarkUsage, | 
|         |    345                                  supervisorStackHighWaterMarkUsagePct | 
|         |    346                                  ); | 
|         |    347                     iEngine.Sink().OutputLineL( pBuf ); | 
|         |    348                     } | 
|         |    349                 } | 
|         |    350             } | 
|         |    351          | 
|         |    352         CleanupStack::PopAndDestroy(); // ProcessSuspendLC | 
|         |    353         } | 
|         |    354  | 
|         |    355     CleanupStack::PopAndDestroy(); // clear prefix | 
|         |    356  | 
|         |    357     CleanupStack::PopAndDestroy( buf ); | 
|         |    358  | 
|         |    359     _LIT(KEndOfHeapListing, "<= End Stack Summary =>"); | 
|         |    360     iEngine.Sink().OutputLineL( KEndOfHeapListing ); | 
|         |    361  | 
|         |    362     iEngine.Sink().DataStreamEndL(); | 
|         |    363     } | 
|         |    364  | 
|         |    365  | 
|         |    366 EXPORT_C TInt CMemSpyEngineHelperStack::CalculateStackSizes( const CMemSpyProcess& aProcess ) | 
|         |    367     { | 
|         |    368     TInt ret = 0; | 
|         |    369     // | 
|         |    370     TRAPD( error, ret = CalculateStackSizesL( aProcess ) ); | 
|         |    371     // | 
|         |    372     if ( error != KErrNone ) | 
|         |    373         { | 
|         |    374         ret = error; | 
|         |    375         } | 
|         |    376     // | 
|         |    377     return ret; | 
|         |    378     } | 
|         |    379  | 
|         |    380  | 
|         |    381 TInt CMemSpyEngineHelperStack::CalculateStackSizesL( const CMemSpyProcess& aProcess ) | 
|         |    382     { | 
|         |    383 	TInt ret = 0; | 
|         |    384 	// | 
|         |    385     iEngine.ProcessSuspendLC( aProcess.Id() ); | 
|         |    386  | 
|         |    387     TMemSpyDriverStackInfo info; | 
|         |    388     // | 
|         |    389     const TInt threadCount = aProcess.Count(); | 
|         |    390     for( TInt i=0; i<threadCount; i++ ) | 
|         |    391         { | 
|         |    392         const CMemSpyThread& thread = aProcess.At( i ); | 
|         |    393         // | 
|         |    394         TInt r = iEngine.Driver().GetStackInfo( thread.Id(), info ); | 
|         |    395         if  ( r == KErrNone ) | 
|         |    396             { | 
|         |    397             ret += info.iUserStackSize; | 
|         |    398             ret += info.iSupervisorStackSize; | 
|         |    399             } | 
|         |    400         } | 
|         |    401     // | 
|         |    402     CleanupStack::PopAndDestroy(); // ProcessSuspendLC | 
|         |    403 	return ret; | 
|         |    404     } | 
|         |    405  | 
|         |    406  | 
|         |    407  |