diff -r d72fc2aace31 -r 62bb7c97884c egl/egltest/endpointtestsuite/automated/tsrc/egltest_threadedstress_remote.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/egl/egltest/endpointtestsuite/automated/tsrc/egltest_threadedstress_remote.cpp Fri Jul 30 11:41:40 2010 +0300 @@ -0,0 +1,883 @@ +// Copyright (c) 2010 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: + + +/** + @file + @test + @internalComponent - Internal Symbian test code +*/ + + +#include "egltest_threadedstress.h" +#include "eglendpointwrap.h" +#include "egltest_endpoint_images.h" +#include "egltest_threadmonitor.h" +#include +#include "egltest_endpoint_images.h" +#include + + +//Private Helper Class Declarations----------------------------------------------- + +class CTightLoopThread : public CBase, public MLog + { +public: + ~CTightLoopThread(); + + //Control the loop from the controlling thread. + //Calling Start() more than once causes panic. + void Start(); + TRemoteTestVerdict Stop(); + TThreadId ThreadId() const; + +protected: + CTightLoopThread(); + void ConstructL(TBool aSharedHeap); + MLog& Logger() const; + void Log(const TText8* aFile, TInt aLine, TInt aSeverity, TRefByValue aFmt, ...); + + //To be implemented by derived class. + virtual void SetupInThreadContextL() = 0; + virtual void TeardownInThreadContextL() = 0; + virtual TBool ExecuteInnerLoopBody() = 0; + +private: + static TInt ThreadEntryPoint(TAny* aSelf); + void EnterThreadLoopL(); + +private: + RThread iThread; + TRequestStatus iNotifyStart; + volatile TBool iNotifyStop; + TBool iHasBeenStarted; + TBool iHasBeenStopped; + }; + + +class CEndpointExercise : public CTightLoopThread + { +public: + static CEndpointExercise* NewL(TBool aSharedHeap); + ~CEndpointExercise(); + + void SetupInThreadContextL(); + void TeardownInThreadContextL(); + TBool ExecuteInnerLoopBody(); + +private: + CEndpointExercise(); + void ConstructL(TBool aSharedHeap); + void ExecuteInnerLoopBodyL(); + TInt CheckImage(EGLImageKHR aEglImage); + + //Logging helpers. + void PanicIfError(TInt aError, const TText8* aFile, TInt aLine) const; + void PanicIfFalse(TBool aBool, const TText8* aFile, TInt aLine) const; + void LogAndLeaveIfErrorL(TInt aError, const TText8* aFile, TInt aLine) const; + void LogAndLeaveIfFalseL(TBool aBool, const TText8* aFile, TInt aLine) const; + #define PANIC_IF_ERROR(ERROR) PanicIfError((ERROR), (TText8*)__FILE__, __LINE__) + #define PANIC_IF_FALSE(BOOL) PanicIfFalse((BOOL), (TText8*)__FILE__, __LINE__) + #define LOG_AND_LEAVE_IF_ERROR_L(ERROR) LogAndLeaveIfErrorL((ERROR), (TText8*)__FILE__, __LINE__) + #define LOG_AND_LEAVE_IF_FALSE_L(BOOL) LogAndLeaveIfFalseL((BOOL), (TText8*)__FILE__, __LINE__) + +private: + TInt iIteration; + TInt iCurrentColour; + RSurfaceManager iSurfaceManager; + RSurfaceUpdateSession iSurfaceUpdate; + RSurfaceManager::TSurfaceCreationAttributesBuf iSurfaceAttribs; + EGLDisplay iDisplay; + TEglEndpointWrap iEglEp; + CEglWindowSurface* iDummyWindowSurface; + }; + +//-------------------------------------------------------------------------------- + + +//Cleanup Items used through out tests-------------------------------------------- + +struct TCleanupSurface + { + RSurfaceManager* iSurfaceManager; + TSurfaceId iSurfaceId; + }; +static void CleanupSurface(TAny* aCleanupSurface) + { + TCleanupSurface* surface = static_cast(aCleanupSurface); + TInt err = surface->iSurfaceManager->CloseSurface(surface->iSurfaceId); + ASSERT(err == KErrNone); + } + + +struct TCleanupEndpoint + { + EGLDisplay iDisplay; + EGLEndpointNOK iEndpoint; + }; +static void CleanupEndpoint(TAny* aCleanupEndpoint) + { + TCleanupEndpoint* endpoint = static_cast(aCleanupEndpoint); + TEglEndpointWrap ep; + ASSERT(ep.Error() == KErrNone); + EGLBoolean err = ep.DestroyEndpoint(endpoint->iDisplay, endpoint->iEndpoint); + ASSERT(err); + } + + +struct TCleanupImage + { + EGLDisplay iDisplay; + EGLEndpointNOK iEndpoint; + EGLImageKHR iImage; + }; +static void CleanupImage(TAny* aCleanupImage) + { + TCleanupImage* image = static_cast(aCleanupImage); + TEglEndpointWrap ep; + ASSERT(ep.Error() == KErrNone); + EGLBoolean err = ep.ReleaseImage(image->iDisplay, image->iEndpoint, image->iImage, EGL_NONE); + ASSERT(err); + } + + +static void CleanupPointerArray(TAny* aPointerArray) + { + RPointerArray* array = static_cast*>(aPointerArray); + array->ResetAndDestroy(); + } + +//-------------------------------------------------------------------------------- + + +//Utility Functions--------------------------------------------------------------- + +inline TInt RandomNumberInRange(TInt aMin, TInt aMax) + { + if(aMin > aMax) + { + TInt temp = aMin; + aMin = aMax; + aMax = temp; + } + + //Scale and offset to put random into the range inclusively. + TUint range = aMax - aMin; + TUint random = Math::Random() % (range + 1); + return (TInt)random + aMin; + } + + +inline TReal Square(TReal aNumber) + { + return aNumber * aNumber; + } + + +static TBool SamplesAreIncreasing(TInt* aSampledData, TInt aNumSamples) + { + //Naive linear least squares to get gradient of fit line and correlation coefficient. + //Using TReal to avoid worrying about wrap. + + TReal n = aNumSamples; + TReal sumX = 0.0; + TReal sumXSq = 0.0; + TReal sumY = 0.0; + TReal sumYSq = 0.0; + TReal sumXTimesY = 0.0; + + for(TInt i=0; i < aNumSamples; i++) + { + TReal x = (TReal)(i + 1); + TReal y = (TReal)aSampledData[i]; + sumX += x; + sumXSq += Square(x); + sumY += y; + sumYSq += Square(y); + sumXTimesY += x * y; + } + + TReal xBar = sumX / n; + TReal yBar = sumY / n; + + TReal gradient = (sumXTimesY - (n * xBar * yBar)) / (sumXSq - (n * Square(xBar))); + TReal correlation = Square(sumXTimesY - (n * xBar * yBar)) / ((sumXSq - (n * Square(xBar))) * (sumYSq - (n * Square(yBar)))); + + //If the gradient is positive and the correlation coefficient is high, the samples are increasing. + return (correlation > 0.8) && (gradient > 0.0); + } + +//-------------------------------------------------------------------------------- + + +//CTightLoopThread---------------------------------------------------------------- + +CTightLoopThread::CTightLoopThread() : + iNotifyStop(EFalse), + iHasBeenStarted(EFalse), + iHasBeenStopped(EFalse) + { + } + + +void CTightLoopThread::ConstructL(TBool aSharedHeap) + { + //Stack and heap sizes. + static const TInt KStackSize = 0x2000; // 8KB + static const TInt KHeapMinSize = 0x1000; // 4KB + static const TInt KHeapMaxSize = 0x1000000; // 16MB + + //The new thread either has its own heap or shares ours. + if(aSharedHeap) + { + User::LeaveIfError(iThread.Create(KNullDesC, ThreadEntryPoint, KStackSize, NULL, this, EOwnerThread)); + } + else + { + User::LeaveIfError(iThread.Create(KNullDesC, ThreadEntryPoint, KStackSize, KHeapMinSize, KHeapMaxSize, this, EOwnerThread)); + } + + //Resume and rendezvous. + iThread.Resume(); + TRequestStatus rendezvous; + iThread.Rendezvous(rendezvous); + User::WaitForRequest(rendezvous); + User::LeaveIfError(rendezvous.Int()); + } + + +MLog& CTightLoopThread::Logger() const + { + return *const_cast(this); + } + + +class TOverflowTruncate : public TDesOverflow + { +public: + virtual void Overflow(TDes& /*aDes*/) + { + //Do nothing - just let it truncate. + } + }; + + +void CTightLoopThread::Log(const TText8* aFile, TInt aLine, TInt aSeverity, TRefByValue aFmt, ...) + { + TOverflowTruncate overflow; + VA_LIST list; + VA_START(list, aFmt); + TBuf<0x100> buf; + buf.AppendFormatList(aFmt, list, &overflow); + TPtrC8 file8(aFile); + TBuf<0x100> file16; + file16.Copy(file8); + //Lots of effort is required to pump this into the TEF log file, so for now we just print to debug. + RDebug::Print(_L("CTightLoopThread: %S:%d, Severity=%d, Message=\"%S\""), &file16, aLine, aSeverity, &buf); + } + + +CTightLoopThread::~CTightLoopThread() + { + //Shutdown the thread according to the state it is in. + if(!iHasBeenStarted) + { + TRequestStatus* notifyStart = &iNotifyStart; + iThread.RequestComplete(notifyStart, KErrAbort); + } + if(iHasBeenStarted && !iHasBeenStopped) + { + Stop(); + } + iThread.Close(); + } + + +void CTightLoopThread::Start() + { + ASSERT(!iHasBeenStarted); + TRequestStatus* notifyStart = &iNotifyStart; + iThread.RequestComplete(notifyStart, KErrNone); + iHasBeenStarted = ETrue; + } + + +TRemoteTestVerdict CTightLoopThread::Stop() + { + ASSERT(iHasBeenStarted); + ASSERT(!iHasBeenStopped); + + TRequestStatus logon; + iThread.Logon(logon); + __e32_atomic_store_rel32(&iNotifyStop, ETrue); + User::WaitForRequest(logon); + + TExitType exitType = iThread.ExitType(); + iThread.Close(); + iHasBeenStopped = ETrue; + + switch(exitType) + { + case EExitKill: + //Terminated normally (since we never call kill). + return ERtvPass; + + case EExitPanic: + //Thread panicked. + return ERtvPanic; + + default: + //Any other option should be impossible in our use case. + ASSERT(0); + } + return ERtvAbort; + } + + +TThreadId CTightLoopThread::ThreadId() const + { + return iThread.Id(); + } + + +TInt CTightLoopThread::ThreadEntryPoint(TAny* aSelf) + { + CTightLoopThread* self = static_cast(aSelf); + CTrapCleanup* cleanup = CTrapCleanup::New(); + + TRAPD(err, + //Create active scheduler. + CActiveScheduler* scheduler = new (ELeave) CActiveScheduler(); + CleanupStack::PushL(scheduler); + CActiveScheduler::Install(scheduler); + + //Setup the draw loop. + self->EnterThreadLoopL(); + + //Clean up. + CleanupStack::PopAndDestroy(scheduler); + ); + + __ASSERT_ALWAYS(err == KErrNone, User::PanicUnexpectedLeave()); + delete cleanup; + + return KErrNone; + } + + +void CTightLoopThread::EnterThreadLoopL() + { + //Setup the derived class in this thread context. + TRAPD(err, SetupInThreadContextL()); + + //Set the request to pending, rendezvous with parent and wait for start signal. + iNotifyStart = KRequestPending; + RThread().Rendezvous(err); + User::WaitForRequest(iNotifyStart); + + //Exit immediately if the KErrAbort signal was received. + TBool keepGoing = ETrue; + if(iNotifyStart == KErrAbort) + { + keepGoing = EFalse; + } + else + { + ASSERT(iNotifyStart == KErrNone); + } + + //Loop until we are told to stop. + while(!__e32_atomic_load_acq32(&iNotifyStop) && keepGoing) + { + keepGoing = ExecuteInnerLoopBody(); + } + + //Teardown the derived class in this thread context. + TeardownInThreadContextL(); + } + +//-------------------------------------------------------------------------------- + + +//CEndpointExercise--------------------------------------------------------------- + +CEndpointExercise* CEndpointExercise::NewL(TBool aSharedHeap) + { + CEndpointExercise* self = new (ELeave) CEndpointExercise(); + CleanupStack::PushL(self); + self->ConstructL(aSharedHeap); + CleanupStack::Pop(self); + return self; + } + + +CEndpointExercise::CEndpointExercise() + { + } + + +void CEndpointExercise::ConstructL(TBool aSharedHeap) + { + CTightLoopThread::ConstructL(aSharedHeap); + User::LeaveIfError(iEglEp.Error()); + } + + +CEndpointExercise::~CEndpointExercise() + { + } + + +void CEndpointExercise::PanicIfError(TInt aError, const TText8* aFile, TInt aLine) const + { + if(aError != KErrNone) + { + Logger().Log(aFile, aLine, ESevrErr, _L("Panicking due to error %d"), aError); + User::Panic(_L("EPTHREADEDSTRESS"), aLine); + } + } + + +void CEndpointExercise::PanicIfFalse(TBool aBool, const TText8* aFile, TInt aLine) const + { + if(!aBool) + { + Logger().Log(aFile, aLine, ESevrErr, _L("Panicking due to failing invariant test")); + User::Panic(_L("EPTHREADEDSTRESS"), aLine); + } + } + + +void CEndpointExercise::LogAndLeaveIfErrorL(TInt aError, const TText8* aFile, TInt aLine) const + { + if(aError != KErrNone) + { + Logger().Log(aFile, aLine, ESevrWarn, _L("Abandoning iteration due to error %d"), aError); + User::Leave(aError); + } + } + + +void CEndpointExercise::LogAndLeaveIfFalseL(TBool aBool, const TText8* aFile, TInt aLine) const + { + if(!aBool) + { + Logger().Log(aFile, aLine, ESevrWarn, _L("Abandoning iteration due to failing invariant test")); + User::Leave(KErrUnknown); + } + } + + +TInt CEndpointExercise::CheckImage(EGLImageKHR aEglImage) + { + TRAPD + (err, + //Convert the image to a CTestVgEglImage + CTestVgEglImage* vgEglImage = CTestVgEglImage::NewL(aEglImage); + CleanupStack::PushL(vgEglImage); + + //Check the corners and center pixel are the same colour. + //Since this test is focussed on correct OOM behaviour, + //we panic if the functionality is incorrect. + PANIC_IF_FALSE(vgEglImage->IsSolidColourL()); + + CleanupStack::PopAndDestroy(vgEglImage); + ); + return err; + } + + +void CEndpointExercise::SetupInThreadContextL() + { + //Colour to fill surface with (this is incremented every frame). + iCurrentColour = 0x88CC44; + + //Connections to SUS and surface manager. + User::LeaveIfError(iSurfaceManager.Open()); + User::LeaveIfError(iSurfaceUpdate.Connect(5)); + + //Surface attribs to create surface with. + iSurfaceAttribs().iSize = TSize(100, 100); + iSurfaceAttribs().iBuffers = 2; + iSurfaceAttribs().iPixelFormat = EUidPixelFormatARGB_8888_PRE; + iSurfaceAttribs().iStride = 100 * 4; + iSurfaceAttribs().iOffsetToFirstBuffer = 0; + iSurfaceAttribs().iAlignment = 32; + iSurfaceAttribs().iContiguous = EFalse; + iSurfaceAttribs().iCacheAttrib = RSurfaceManager::ECached; + iSurfaceAttribs().iOffsetBetweenBuffers = 0; + iSurfaceAttribs().iSurfaceHints = NULL; + iSurfaceAttribs().iHintCount = 0; + iSurfaceAttribs().iMappable = ETrue; + + iDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + //Create an EglWindowSurface so we have a current context for vg operations. + iDummyWindowSurface = CEglWindowSurface::NewL(); + iDummyWindowSurface->CreateL(EStandardSurface, TPoint(0, 0)); + iDummyWindowSurface->ActivateL(); + } + + +void CEndpointExercise::TeardownInThreadContextL() + { + delete iDummyWindowSurface; + iSurfaceUpdate.Close(); + iSurfaceManager.Close(); + } + + +TBool CEndpointExercise::ExecuteInnerLoopBody() + { + TRAPD(err, ExecuteInnerLoopBodyL()); + if(err != KErrNone) + { + Logger().Log((TText8*)__FILE__, __LINE__, ESevrWarn, _L("Iteration %d did not run to completion, due to an acceptable error in low memory conditions"), iIteration); + } + iIteration++; + return ETrue; + } + + +void CEndpointExercise::ExecuteInnerLoopBodyL() + { + //Create a surface. + TCleanupSurface surface = {&iSurfaceManager, TSurfaceId::CreateNullId()}; + LOG_AND_LEAVE_IF_ERROR_L(iSurfaceManager.CreateSurface(iSurfaceAttribs, surface.iSurfaceId)); + CleanupStack::PushL(TCleanupItem(CleanupSurface, &surface)); + + //Map surface and get pointer to buffer 0. + RChunk surfaceChunk; + TInt offset; + PANIC_IF_ERROR(iSurfaceManager.MapSurface(surface.iSurfaceId, surfaceChunk)); + CleanupClosePushL(surfaceChunk); + PANIC_IF_ERROR(iSurfaceManager.GetBufferOffset(surface.iSurfaceId, 0, offset)); + TUint32* buffer = (TUint32*)(surfaceChunk.Base() + offset); + + //Fill surface with current colour. This could + //be much faster but its good enough for testing. + TUint32 fillColour = TRgb(iCurrentColour, 255)._Color16MAP(); + for(TInt y=0; y < iSurfaceAttribs().iSize.iHeight; ++y) + { + for(TInt x=0; x < iSurfaceAttribs().iSize.iWidth; ++x) + { + buffer[x] = fillColour; + } + buffer += iSurfaceAttribs().iStride >> 2; + } + + //Create an endpoint for the surface. + TCleanupEndpoint endpoint = {iDisplay, EGL_NO_ENDPOINT_NOK}; + endpoint.iEndpoint = iEglEp.CreateEndpoint(iDisplay, EGL_ENDPOINT_TYPE_CONSUMER_NOK, EGL_TSURFACEID_NOK, &surface.iSurfaceId, NULL); + LOG_AND_LEAVE_IF_FALSE_L(endpoint.iEndpoint != EGL_NO_ENDPOINT_NOK); + CleanupStack::PushL(TCleanupItem(CleanupEndpoint, &endpoint)); + + //Submit buffer 0 to surface update server. + TRequestStatus displayed; + iSurfaceUpdate.NotifyWhenDisplayedXTimes(1, displayed); + LOG_AND_LEAVE_IF_ERROR_L(iSurfaceUpdate.SubmitUpdate(KAllScreens, surface.iSurfaceId, 0, NULL)); + User::WaitForRequest(displayed); + + //Begin streaming. Should not fail since we have submitted a buffer since creating ep. + LOG_AND_LEAVE_IF_FALSE_L(iEglEp.EndpointBeginStreaming(iDisplay, endpoint.iEndpoint)); + + //Acquire an image from the endpoint. + TCleanupImage image = {iDisplay, endpoint.iEndpoint, EGL_NO_IMAGE_KHR}; + image.iImage = iEglEp.AcquireImage(iDisplay, endpoint.iEndpoint); + LOG_AND_LEAVE_IF_FALSE_L(image.iImage != EGL_NO_IMAGE_KHR); + CleanupStack::PushL(TCleanupItem(CleanupImage, &image)); + + //Check that the image we acquired is coherrent. + LOG_AND_LEAVE_IF_ERROR_L(CheckImage(image.iImage)); + + //Release image, destroy endpoint, close chunk and close surface. + CleanupStack::PopAndDestroy(4); + + //Modify the colour that we draw. + iCurrentColour += 16; + } + +//-------------------------------------------------------------------------------- + + +//Remote test step---------------------------------------------------------------- + +CEglTest_RemoteTestStep_EndpointThreadStress::CEglTest_RemoteTestStep_EndpointThreadStress() : + CRemoteTestStepBase(ETestUidEndpointThreadStress) + { + } + + +CEglTest_RemoteTestStep_EndpointThreadStress::~CEglTest_RemoteTestStep_EndpointThreadStress() + { + } + + +TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::DoStartRemoteTestStepL(const TRemoteTestParams& /*aMessageIn*/) + { + REMOTE_INFO_PRINTF1(_L("Starting Remote Test Step.")); + EglStartL(); + return ERtvPass; + } + + +TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::DoEndRemoteTestStepL(const TRemoteTestParams& /*aMessageIn*/) + { + REMOTE_INFO_PRINTF1(_L("Ending Remote Test Step.")); + EglEndL(); + return ERtvPass; + } + + +TInt CEglTest_RemoteTestStep_EndpointThreadStress::Timeout() const + { + return 120 * 1000000; //2 min. + } + + +TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::DoRunRemoteTestCaseL(TInt aTestCase, const TRemoteTestParams& aParams) + { + switch(aTestCase) + { + case 0: return CrazyThreadingTestCaseL(aParams); + case 1: return OutOfHeapMemoryTestCaseL(aParams); + default: return ERtvAbort; + } + } + + +//For a detailed description of this test case (GRAPHICS-EGL-594), see the local side cpp file. +TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::CrazyThreadingTestCaseL(const TRemoteTestParams& /*aParams*/) + { + //Create the exercises. These run an endpoint exercise in a tight loop in a private thread. + CEndpointExercise* exercise1 = CEndpointExercise::NewL(EFalse); + CleanupStack::PushL(exercise1); + CEndpointExercise* exercise2 = CEndpointExercise::NewL(EFalse); + CleanupStack::PushL(exercise2); + + //Create a monitor to cleanup if any of the threads panic. The controller thread + //must be at index zero in the array. This will even work if a deadlock occurs + //between the exercise threads, since the call to stop the exercise will never + //return and the framework will eventually time us out. The monitor will notice + //that the controller thread has panicked and will forward the panic to the exercises. + RArray threads; + CleanupClosePushL(threads); + threads.AppendL(RThread().Id()); + threads.AppendL(exercise1->ThreadId()); + threads.AppendL(exercise2->ThreadId()); + CThreadMonitor* monitor = CThreadMonitor::NewL(threads); + CleanupStack::PushL(monitor); + + //Start the exercises. + exercise1->Start(); + exercise2->Start(); + + //Let the exercises run for 20 seconds. + User::After(20 * 1000000); + + //Stop the exercises and record the results. + TRemoteTestVerdict result1 = exercise1->Stop(); + TRemoteTestVerdict result2 = exercise2->Stop(); + + CleanupStack::PopAndDestroy(4, exercise1); + return (result1 != ERtvPass) ? result1 : result2; + } + + +class THeapGobbler + { +public: + static THeapGobbler* New(TInt aSize) + { + THeapGobbler* self = (THeapGobbler*)new TUint8[sizeof(THeapGobbler) - sizeof(TUint8) + aSize]; + if(!self) + { + return NULL; + } + self->iSize = aSize; + self->iNext = NULL; + return self; + } + +public: + THeapGobbler* iNext; + TInt iSize; + TUint8 iMemory[1]; + }; + + +//For a detailed description of this test case (GRAPHICS-EGL-601), see the local side cpp file. +TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::OutOfHeapMemoryTestCaseL(const TRemoteTestParams& aParams) + { + const TInt KHeapSizeMin = 0x100000; //1MB. + const TInt KHeapSizeMax = 0x10000000; //256MB. + + RHeap* testHeap = User::ChunkHeap(NULL, KHeapSizeMin, KHeapSizeMax, KMinHeapGrowBy, 4); + if(!testHeap) + { + REMOTE_ERR_PRINTF1(_L("Failed to create chunk heap. Aborting.")); + return ERtvAbort; + } + RHeap* oldHeap = User::SwitchHeap(testHeap); + + CTrapCleanup *cleanUpStack = CTrapCleanup::New(); + if (!cleanUpStack) + { + User::SwitchHeap(oldHeap); + testHeap->Close(); + User::Leave(KErrNoMemory); + } + + TRemoteTestVerdict verdict = ERtvPass; + TRAPD(err, verdict = DoOutOfHeapMemoryTestCaseL(aParams)); + + delete cleanUpStack; + User::SwitchHeap(oldHeap); + testHeap->Close(); + + User::LeaveIfError(err); + return verdict; + } + + +TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::DoOutOfHeapMemoryTestCaseL(const TRemoteTestParams& aParams) + { + const TInt numExercises = aParams.iEndpointThreadStress.iNumThreads; + + const TInt KMinCellSize = 500; + const TInt KMaxCellSize = 2000; + const TInt KNumIterations = 20; + TInt heapAllocSize[KNumIterations]; + TRemoteTestVerdict exerciseResult = ERtvPass; + + //One iteration of the outer loop results in one data point for deciding if the heap is leaking or not. + for(TInt x=0; x < KNumIterations; x++) + { + //Reserving space in these arrays ahead of time to + //make cleanup stack manipulation more staightforward. + RPointerArray exercises; + CleanupStack::PushL(TCleanupItem(CleanupPointerArray, &exercises)); + exercises.ReserveL(numExercises); + RArray threads; + CleanupClosePushL(threads); + threads.ReserveL(numExercises + 1); + + //Save the controller thread id for the monitor. + threads.Append(RThread().Id()); + + //Create endpoint exercise threads and save the thread Ids for the monitor. + for(TInt j=0; j < numExercises; j++) + { + //Appends can't fail since we have already reserved space. + //Note that the exercises all share the same heap as this thread. + exercises.Append(CEndpointExercise::NewL(ETrue)); + threads.Append(exercises[j]->ThreadId()); + } + + //Create a monitor to handle thread cleanup if something panics or deadlocks. + CThreadMonitor* monitor = CThreadMonitor::NewL(threads); + + //Nothing can leave after this. + CleanupStack::Pop(2); + + //Start the exercises. + for(TInt j=0; j < numExercises; j++) + { + exercises[j]->Start(); + } + + THeapGobbler* firstCell = NULL; + THeapGobbler* lastCell = NULL; + TInt numberOfCells = 0; + + for(TInt i=0; i < 2; i++) + { + //Allocate random sizes until full. + THeapGobbler* newCell = THeapGobbler::New(RandomNumberInRange(KMinCellSize, KMaxCellSize)); + while(newCell) + { + if(lastCell) + lastCell->iNext = newCell; + if(!firstCell) + firstCell = newCell; + lastCell = newCell; + numberOfCells++; + newCell = THeapGobbler::New(RandomNumberInRange(KMinCellSize, KMaxCellSize)); + } + + //Let exercise run while heap is full. + User::After(1 * 1000); + + //Deallocate n/4 cells. + for(TInt n = numberOfCells / 4; n >= 1; --n) + { + THeapGobbler* oldCell = firstCell; + firstCell = oldCell->iNext; + delete oldCell; + numberOfCells--; + if(!firstCell) + { + lastCell = NULL; + ASSERT(numberOfCells == 0); + break; + } + } + + //Let exercise run while heap is not full. + User::After(1 * 1000); + } + + //Deallocate all cells. + while(firstCell) + { + THeapGobbler* oldCell = firstCell; + firstCell = oldCell->iNext; + delete oldCell; + } + lastCell = NULL; + numberOfCells = 0; + + //Stop the exercises and save the result. + for(TInt j=0; j < numExercises; j++) + { + TRemoteTestVerdict ret = exercises[j]->Stop(); + exerciseResult = (exerciseResult == ERtvPass) ? ret : exerciseResult; + } + + delete monitor; + threads.Close(); + exercises.ResetAndDestroy(); + + if(exerciseResult != ERtvPass) + { + REMOTE_ERR_PRINTF2(_L("Aborting because the endpoint exercise failed for iteration %d"), x); + return exerciseResult; + } + + //Save the heap size. + User::Heap().AllocSize(heapAllocSize[x]); + } + + //Work out if any memory has leaked and return a verdict. + TBool memoryIsLeaking = SamplesAreIncreasing(heapAllocSize, KNumIterations); + if(memoryIsLeaking) + { + REMOTE_ERR_PRINTF1(_L("Heap memory is increasing over time with high certainty, there is probably a memory leak.")); + } + else + { + REMOTE_INFO_PRINTF1(_L("No heap memory leak detected.")); + } + return memoryIsLeaking ? ERtvFail : ERtvPass; + } + +//--------------------------------------------------------------------------------