kerneltest/e32test/mmu/t_alias_remove.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 8 538db54a451d
child 42 a179b74831c9
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// 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 the License "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:
// e32test\mmu\t_alias_remove.cpp
// Overview:
// Test interactions when free memory being aliases.
// Details:
// Create 3 mappings to one chunk one that owns the chunk, one to map it again another process 
// and another alias mapping.
// Then while the alias mapping is accessing the chunk close the second mapping.
// Platforms/Drives/Compatibility:
// All.
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#define __E32TEST_EXTENSION__
#include <e32test.h>
#include <e32hal.h>
#include <e32svr.h>
#include "..\defrag\d_pagemove.h"

const TPtrC KAliasProcessName = _L("T_ALIAS_REMOVE");
const TPtrC KAliasChunkName = _L("AliasChunk");
const TPtrC KAliasServerName = _L("AliasServer");
const TPtrC KMasterServerName = _L("MasterServer");

RBuf ChunkName;
RBuf MasterServerName;
RBuf AliasServerName;

RTest test(KAliasProcessName);

//#define ENABLE_PRINTFS
#ifndef __MSVC6__	// VC6 can't cope with variable arguments in macros.

#define T_PRINTF(x...) test.Printf(x)
#define D_PRINTF(x...) RDebug::Printf(x)
#ifdef ENABLE_PRINTFS
#define PRINTF(x) x
#else
#define PRINTF(x)
#endif // ENABLE_PRINTFS

#else
#define PRINTF(x)
#endif	// __MSCV6__

enum TSlaveMsgType
	{
	ESlaveConnect = -1,
	ESlaveDisconnect = -2,
	ESlaveReadChunk = 0,
	ESlaveClosedChunk = 1,
	};


struct SThreadData 
	{
	TUint8 iFillValue;
	TUint iChunkSize;
	RThread* iMasterThread;
	TUint iProcessId;
	};


class RAliasSession : public RSessionBase
	{
public:
	TInt CreateSession(const TDesC& aServerName, TInt aMsgSlots) 
		{ 
		return RSessionBase::CreateSession(aServerName,User::Version(),aMsgSlots);
		}
	TInt PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr)
		{
		return (SendReceive(aFunction, aPtr));
		}
	TInt PublicSend(TInt aFunction, const TIpcArgs &aPtr)
		{
		return (Send(aFunction, aPtr));
		}
	};


TInt SlaveProcess(TUint aProcessId, TUint aFillValue)
	{		
	// Connect to the master server to indicate that we're ready to receive ipc messages.
	RAliasSession masterSession;
	test_KErrNone(masterSession.CreateSession(MasterServerName, 1));

	PRINTF(T_PRINTF(_L("Process ID %d Slave open chunk\n"), aProcessId));
	// Open the global chunk.
	RChunk chunk;
	TInt r = chunk.OpenGlobal(ChunkName, ETrue);
	test_KErrNone(r);

	// Connect to alias server.
	PRINTF(T_PRINTF(_L("Process ID %d Slave connect to alias server\n"), aProcessId));
	RAliasSession aliasSession;
	test_KErrNone(aliasSession.CreateSession(AliasServerName, 1));

	PRINTF(T_PRINTF(_L("Process ID %d Slave send data to alias server\n"), aProcessId));
	TPtr8 arg0(chunk.Base(), chunk.Size(), chunk.Size());
	r = aliasSession.PublicSend(ESlaveReadChunk, TIpcArgs(&arg0));
	test_KErrNone(r);
	
	// Close the chunk removing its mapping before the server has read it.
	chunk.Close();
	PRINTF(T_PRINTF(_L("Process ID %d Slave closed chunk\n"), aProcessId));

	r = masterSession.PublicSendReceive(ESlaveClosedChunk, TIpcArgs());
	test_KErrNone(r);
	aliasSession.Close();
	masterSession.Close();
	return KErrNone;
	}


TInt ChunkReadThread(TAny* aThreadData)
	{
	SThreadData* threadData =  (SThreadData*)aThreadData;
	RServer2 aliasServer;
	TInt r = aliasServer.CreateGlobal(AliasServerName);
	if (r != KErrNone)
		{
		RDebug::Printf("Process ID %d Error creating alias server r=%d", threadData->iProcessId, r);
		return r;
		}
	// Connect to the master server to indicate that we're ready to receive ipc messages.
	RAliasSession masterSession;
	test_KErrNone(masterSession.CreateSession(MasterServerName, 1));

	PRINTF(D_PRINTF("Process ID %d Alias wait for slave connection", threadData->iProcessId));
	RMessage2 aliasMessage;
	// Read and complete the connect message from the slave.
	aliasServer.Receive(aliasMessage);
	test_Equal(ESlaveConnect, aliasMessage.Function());
	aliasMessage.Complete(KErrNone);

	// Read the data of the remote chunk.
	PRINTF(D_PRINTF("Process ID %d Alias read chunk data", threadData->iProcessId));
	HBufC8* argTmp = HBufC8::New(threadData->iChunkSize);
	test_NotNull(argTmp);
	RBuf8 argBuf(argTmp);
	aliasServer.Receive(aliasMessage);
	test_Equal(ESlaveReadChunk, aliasMessage.Function());
	r = aliasMessage.Read(0, argBuf);
	if (r == KErrNone)
		{// Successfully read the chunk so verify it.
		aliasMessage.Complete(KErrNone);

		PRINTF(D_PRINTF("Process ID %d Alias verify chunk data", threadData->iProcessId));
		const TUint8* bufPtr = argBuf.Ptr();
		const TUint8* bufEnd = bufPtr + threadData->iChunkSize;
		for (; bufPtr < bufEnd; bufPtr++)
			{
			if (*bufPtr != threadData->iFillValue)
				{
				RDebug::Printf("Process ID %d Read incorrect data exp 0x%x got 0x%x", 
								threadData->iProcessId, threadData->iFillValue, *bufPtr);
				r = *bufPtr;
				break;
				}
			}
		}
	else
		{
		PRINTF(D_PRINTF("Process ID %d Error reading chunk remotely %d", threadData->iProcessId, r));
		}
	argBuf.Close();
	masterSession.Close();
	return r;
	}


TInt MasterProcess(TInt aProcessId)
	{
	TInt pageSize;
	UserHal::PageSizeInBytes(pageSize);
	// Need a large chunk so that alias that reads it is held for a long
	// enough period for there to be conflicts with the chunk closure in 
	// the slave process.
	const TUint KChunkSize = pageSize * 1024;

	PRINTF(T_PRINTF(_L("Process ID %d Create chunk\n"), aProcessId));
	RChunk chunk;
	TInt r = chunk.CreateGlobal(ChunkName, KChunkSize, KChunkSize);
	test_KErrNone(r);


	for (TUint8 fillValue = 1; fillValue < 255; fillValue++)
		{
		// Output a character every 16 iterations so test machines 
		// don't time out.
		if ((fillValue & 0xf) == 1)
			test.Printf(_L("."));

		PRINTF(T_PRINTF(_L("Process ID %d start slave fill value %d\n"), aProcessId, fillValue));
		RServer2 masterServer;
		r = masterServer.CreateGlobal(MasterServerName);
		test_KErrNone(r);
		RMessage2 masterMessage;

		// Update the chunk to new fill value.
		memset(chunk.Base(), fillValue, KChunkSize);

		PRINTF(T_PRINTF(_L("Process ID %d Start the slave process\n"), aProcessId));
		RProcess slaveProcess;
		test_KErrNone(slaveProcess.Create(KAliasProcessName, KNullDesC));
		test_KErrNone(slaveProcess.SetParameter(1, aProcessId));
		test_KErrNone(slaveProcess.SetParameter(2, fillValue));
		TRequestStatus slaveStatus;
		slaveProcess.Logon(slaveStatus);
		test_Equal(KRequestPending, slaveStatus.Int());
		slaveProcess.Resume();

		// Wait for the connect message from the slave process.
		masterServer.Receive(masterMessage);
		test_Equal(ESlaveConnect, masterMessage.Function());

		SThreadData threadData;
		threadData.iFillValue = fillValue;
		threadData.iChunkSize = KChunkSize;
		threadData.iProcessId = aProcessId;
		RThread readThread;
		r = readThread.Create(KNullDesC, ChunkReadThread, 10 * pageSize, KChunkSize, KChunkSize * 2, &threadData);
		test_KErrNone(r);
		TRequestStatus threadStatus;
		readThread.Logon(threadStatus);
		test_Equal(KRequestPending, threadStatus.Int());
		readThread.Resume();

		PRINTF(T_PRINTF(_L("Process ID %d Wait for alias thread to start server\n"), aProcessId));
		RMessage2 aliasMessage;
		masterServer.Receive(aliasMessage);
		test_Equal(ESlaveConnect, aliasMessage.Function());
		aliasMessage.Complete(KErrNone);

		// Signal to the slave process to send chunk to alias thread.
		PRINTF(T_PRINTF(_L("Process ID %d Signal to slave to send chunk to alias\n"), aProcessId));
		masterMessage.Complete(KErrNone);

		// Wait for slave to close the chunk and fill it with new value.
		for (;;)
			{
			masterServer.Receive(masterMessage);
			TInt func = masterMessage.Function();
			PRINTF(T_PRINTF(_L("Process ID %d rxd %d\n"), aProcessId, func));
			if (func == ESlaveClosedChunk)
				{// Slave closed the chunk.
				memset(chunk.Base(), ++fillValue, KChunkSize);
				break;
				}
			else
				{// Alias has read the chunk and completed.
				test_Equal(ESlaveDisconnect, func);
				}
			}
		
		PRINTF(T_PRINTF(_L("Process ID %d Wait for alias to complete\n"), aProcessId));
		masterMessage.Complete(KErrNone);
		User::WaitForRequest(threadStatus);
		TInt statusInt = threadStatus.Int();
		test_Value(	statusInt, 
					statusInt == KErrNone || 
					statusInt == KErrBadDescriptor ||
					statusInt == KErrDied);
		test_Equal(EExitKill, readThread.ExitType());
		readThread.Close();

		PRINTF(T_PRINTF(_L("Process ID %d Wait for slave to complete\n"), aProcessId));
		User::WaitForRequest(slaveStatus);
		test_Equal(EExitKill, slaveProcess.ExitType());
		test_Equal(KErrNone, slaveProcess.ExitReason());
		slaveProcess.Close();
		masterServer.Close();
		}

	chunk.Close();

	return 0;
	}


GLDEF_C TInt E32Main()
	{
	TInt processId;
	if(User::GetTIntParameter(1, processId)==KErrNone)
		{
		test_KErrNone(ChunkName.Create(KAliasChunkName.Length() + 3));
		ChunkName.Copy(KAliasChunkName);
		ChunkName.AppendNum(processId);

		test_KErrNone(MasterServerName.Create(KMasterServerName.Length() + 3));
		MasterServerName.Copy(KMasterServerName);
		MasterServerName.AppendNum(processId);

		test_KErrNone(AliasServerName.Create(KAliasServerName.Length() + 3));
		AliasServerName.Copy(KAliasServerName);
		AliasServerName.AppendNum(processId);

		TInt fillValue;
		if(User::GetTIntParameter(2, fillValue)==KErrNone)
			{
			return SlaveProcess(processId, fillValue);
			}
		return MasterProcess(processId);
		}

	// Get the number of cpus and use it to determine how many processes to execute.
	RPageMove pagemove;
	test_KErrNone(pagemove.Open());
	TUint masterProcesses = pagemove.NumberOfCpus() + 1;
	pagemove.Close();

	TInt cmdLineLen = User::CommandLineLength();
	if(cmdLineLen)
		{
		RBuf cmdLine;
		test_KErrNone(cmdLine.Create(cmdLineLen));
		User::CommandLine(cmdLine);
		test_KErrNone(TLex(cmdLine).Val(masterProcesses));
		}

	test.Title();
	test.Start(_L(""));
	test.Printf(_L("Create %d processes for accessing aliases being removed\n"), masterProcesses); 

	TUint32 debugMask = UserSvr::DebugMask();
	User::SetDebugMask(0);

	// Start master processes to alias memory between each other.
	RProcess* masters = new RProcess[masterProcesses];
	TRequestStatus* masterStatus = new TRequestStatus[masterProcesses];
	TUint i = 0;
	for (; i < masterProcesses; i++)
		{
		test_KErrNone(masters[i].Create(KAliasProcessName, KNullDesC));
		test_KErrNone(masters[i].SetParameter(1, i));
		masters[i].Logon(masterStatus[i]);
		test_Equal(KRequestPending, masterStatus[i].Int());
		}
	test.Next(_L("Resume the processes")); 
	for (i = 0; i < masterProcesses; i++)
		{
		masters[i].Resume();
		}

	test.Next(_L("Wait for processes to exit")); 
	for (i = 0; i < masterProcesses; i++)
		{
		User::WaitForRequest(masterStatus[i]);
		test_Equal(EExitKill, masters[i].ExitType());
		test_Equal(KErrNone, masters[i].ExitReason());
		}
	User::SetDebugMask(debugMask);
	delete masterStatus;
	delete masters;
	test.Printf(_L("\n"));
	test.End();
	return KErrNone;
	}