kerneltest/e32test/debug/t_codemodifier.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 07 Jan 2010 13:38:45 +0200
changeset 33 0173bcd7697c
parent 0 a41df078684a
permissions -rw-r--r--
Revision: 201001 Kit: 201001

// Copyright (c) 2005-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\debug\t_codemodifier.cpp
// Overview:
// Exercises Kernel's Code Modifier. This is common use case for run-mode debuggers.
// API Information:
// DebugSupport::InitialiseCodeModifier
// DebugSupport::CloseCodeModifier
// DebugSupport::ModifyCode
// DebugSupport::RestoreCode
// Kern::ThreadRawRead
// Kern::ThreadRawWrite
// Details:
// -Three applications are running in the test:
// - t_codemodifier.exe client
// - t_codemodifier.exe server (XIP code)
// - t_codemodifier2.exe client (non-XIP code)
// -Test1 -Checks that the TestFunc (that will be altered by breakpoints) is really deadly if un-altered.
// -Test2 -Checks Code Modifier if data segment address is passed to set breakpoint.	
// -Test3 -Checks Code Modifier if invalid address is passed to set breakpoint.Kern::ThreadRawRead\Write is also checked.	
// -Test4 -Replaces BRK instruction in TestFunc with NOP using 1.2 and 4 bytes long breakpoints. Executes the 
// function in the servers.
// -Test5 -Repeats Test4 (for XIP server only) with previously shedowed TestFunc
// -Test6 -Tests scenario when a process terminates while there are still associated breakpoints.
// -Test7 -Tests out-of-breakpoints scenario
// -Test8 -Tests breakpoint-already-exists scenario
// -Test9 -Tests CodeModifier closing when there are still active breakpoints.	Breakpoints in this test occupies more then
// one shadowed page.
// -Test10-A random stress test. Sets/Clears random brekpoints in CodeArea of the both servers. Then replaces
// all BRKs with NOPs in CodeArea and executes them.
// -Test11-Checks that overlaping breakpoints are rejected.
// Platforms/Drives/Compatibility:
// Hardware (Automatic). Not supported on emulator.
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#define __E32TEST_EXTENSION__
#include <e32test.h>
#include <e32ldr.h>
#include <e32ldr_private.h>
#include <f32dbg.h>
#include "d_codemodifier.h"
#include "../misc/prbs.h"

LOCAL_D RTest test(_L("T_CODEMODIFIER"));
_LIT(KServerXIP,"ServerXIP");
_LIT(KServerNONXIP,"ServerNONXIP");
_LIT(KAppXIP,"t_codemodifier.exe");
_LIT(KAppNONXIP,"t_codemodifier2.exe");

extern void TestFunc();
extern void CodeArea();

//------------client globals---------------------
RCodeModifierDevice Device;

/**	These addresses/names is all the client needs to test a server.
	There are XIP and non-XIP server in the test*/
struct SServerData
	{
	TInt  		 iThreadId;
	TUint 		 iVarAddr;
	TUint 		 iFuncAddr;
	TUint 		 iInvalidAddr;
	TUint 		 iCodeAreaAddr;
	const TDesC* iAppName;
	const TDesC* iServerName;
	RProcess 	 iProcess;
	} ServerData[2];

/**Will hold assembler instructions that make TestFunc.*/
struct STestFunc
	{
	TInt iBRK;
	TInt iNOP;
	TInt iRET;
	} TestFuncCode;

/**ServerData[0] is about XIP server, ServerData[1] is non-XIP server*/
enum TWhichServer
	{
	EXip = 0,
	ENonxip = 1
	};

/**Server*/
class CCodeModifierServer : public CServer2
	{
public:
	static CCodeModifierServer* New(TInt aPriority);
private:
	CCodeModifierServer(TInt aPriority) : CServer2(aPriority){}
	CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const;
public:
	TInt iTestVar;
	RChunk chunk;
	};

CCodeModifierServer* CCodeModifierServer::New(TInt aPriority)
	{
	return new CCodeModifierServer(aPriority);
	}

/**Server side session*/
class CCodeModifierSession : public CSession2
	{
public:
	enum TCommand {EStop, EGetServerInfo, ERunTestFunc, ERunCodeAreaFunc};
private:
	void ServiceL(const RMessage2& aMessage);
	};

/*Client-side session*/
class RCodeModifierSession : private RSessionBase
	{
public:
	/**Updates ServerData[aServer] with data from the server*/
	static TInt GetServerInfo(TInt aServer)
		{
		TPtr8 ptr((TUint8*)&ServerData[aServer].iThreadId, 5*sizeof(TInt),5*sizeof(TInt));
		TIpcArgs args(&ptr);
		return Control(CCodeModifierSession::EGetServerInfo,aServer,args);
		};
	/**Stops the server*/	
	static TInt Stop(TInt aServer)
		{TIpcArgs args;return Control(CCodeModifierSession::EStop,aServer, args);};
	/**Issues the command to server to run TestFunc*/
	static TInt RunTestFunc(TInt aServer)
		{TIpcArgs args;return Control(CCodeModifierSession::ERunTestFunc,aServer,args);};
	/**Issues the command to server to run CodeArea*/
	static TInt RunCodeAreaFunc(TInt aServer)
		{TIpcArgs args;return Control(CCodeModifierSession::ERunCodeAreaFunc,aServer,args);};
private:
	/*Executes a synchronius client-server request*/
	static TInt Control(CCodeModifierSession::TCommand aRequest, TInt aServer, TIpcArgs& aArgs)
		{
		RCodeModifierSession p;
		TInt r = p.CreateSession(*ServerData[aServer].iServerName, TVersion(), 0);
		if (r == KErrNone)
			p.SendReceive(aRequest, aArgs);
		p.Close();
		return r;
		};
	};

//------------server globals---------------------
CCodeModifierServer* CodeModifierServer;

/**Creates a new client for this server.*/
CSession2* CCodeModifierServer::NewSessionL(const TVersion&, const RMessage2&) const
	{
	return new(ELeave) CCodeModifierSession();
	}

/**Session entry point on the server side.*/
void CCodeModifierSession::ServiceL(const RMessage2& aMessage)
	{
	TInt r=KErrNone;
	switch (aMessage.Function())
		{
	case EGetServerInfo:		//Pass threadId and addresses to the client
		{
		struct SInfo
			{
			TInt  iThreadId;
			TUint iVarAddr;
			TUint iFuncAddr;
			TUint iInvalidAddr;
			TUint iCodeAreaAddr;
			} info;
		RThread thread;
		info.iThreadId =    (TInt) thread.Id();
		info.iVarAddr =     (TUint) &CodeModifierServer->iTestVar;
		info.iInvalidAddr = (TUint)CodeModifierServer->chunk.Base()+0x1000;
		info.iFuncAddr =    (TUint)&TestFunc;
		info.iCodeAreaAddr =(TUint)&CodeArea;
		TPtrC8 ptr((TUint8*)&info, sizeof(SInfo));
		r=aMessage.Write(0,ptr);
		}
		break;

	case ERunTestFunc:			//Execute TestFunc
		TestFunc();
		break;

	case ERunCodeAreaFunc:			//Execute CodeArea
		CodeArea();
		break;

	case EStop:					//This will stop the server thread.
		CActiveScheduler::Stop();
		break;

	default:
		r=KErrNotSupported;
		}
	aMessage.Complete(r);
	}

/**Server application entry point*/
LOCAL_C TInt ServerEntryPoint(TInt aServer)
	{
	TInt r=0;
	__UHEAP_MARK;
	
	CActiveScheduler *pR=new CActiveScheduler;
	if (!pR)
		User::Panic(_L("SVR:Could't create Active Scheduler\n"), KErrNoMemory);
	CActiveScheduler::Install(pR);

	CodeModifierServer = CCodeModifierServer::New(0);
	if(!CodeModifierServer)
		{
		delete pR;
		User::Panic(_L("SVR:Create svr error\n"), KErrNoMemory);
		}

	//Create a chunk with a hole between addresses 0x1000 & 0x2000
	r=CodeModifierServer->chunk.CreateDisconnectedLocal(0,0,0x200000);
	test_KErrNone(r);
	r=CodeModifierServer->chunk.Commit(0,0x1000);
	test_KErrNone(r);
	r=CodeModifierServer->chunk.Commit(0x2000,0x1000);
	test_KErrNone(r);

	//We decide here which server to start.
	if (aServer==0)
		r=CodeModifierServer->Start(KServerXIP);
	else
		r=CodeModifierServer->Start(KServerNONXIP);
		
	if (r!=KErrNone)
		{
		delete CodeModifierServer;
		delete pR;
		User::Panic(_L("SVR:Error starting server\n"), r);
		}
	RProcess::Rendezvous(KErrNone);
	CActiveScheduler::Start();

	//We come here on CActiveScheduler::Stop()
	delete CodeModifierServer;
	delete pR;
	__UHEAP_MARKEND;
	return(KErrNone);
	}

//Initializes the globals and switch off lazy (un)loader. Called just once at the start.
void UpdateClientsGlobals()
	{
	TInt* ptr = (TInt*)TestFunc;
	TestFuncCode.iBRK = *(ptr++);
	TestFuncCode.iNOP = *(ptr++);
	TestFuncCode.iRET = *(ptr++);
	test.Printf(_L("UpdateClientsGlobals BRK:%x NOP:%x RET:%x\n"),TestFuncCode.iBRK,TestFuncCode.iNOP,TestFuncCode.iRET);

	ServerData[0].iAppName=&KAppXIP;
	ServerData[1].iAppName=&KAppNONXIP;
	ServerData[0].iServerName=&KServerXIP;
	ServerData[1].iServerName=&KServerNONXIP;

	//Turn off lazy dll (un)loader.
	//If we don't this, the second run of the test (within 2 minutes of the first one) would fail.
	RLoader l;
	test_KErrNone(l.Connect());
	test_KErrNone(l.CancelLazyDllUnload());
	l.Close();
	}
	
//Starts the server (0-XIP 1-NONXIP server)
//Obtains data from the server
//Updates the driver with the threadID of the server
void StartAndUpdate(TInt aServer)
	{
	TRequestStatus status;
	RProcess& p = ServerData[aServer].iProcess;
	test.Printf(_L("StartAndUpdate %d\n"),aServer);
	//Starts the server
	TInt r = p.Create(*ServerData[aServer].iAppName,*ServerData[aServer].iServerName);
	test_KErrNone(r);
	p.Rendezvous(status);
	p.Resume();
	User::WaitForRequest(status);
	test.Printf(_L("%d returned\n"),status.Int());
	test_KErrNone(status.Int());
	//Get threadId and addresses from the server
	test_KErrNone(RCodeModifierSession::GetServerInfo(aServer));
	SServerData& s = ServerData[aServer];
	test.Printf(_L("ServerData:TId:%x VA:%x FA:%x IA:%x CA:%x \n"),s.iThreadId,s.iVarAddr,s.iFuncAddr,s.iInvalidAddr,s.iCodeAreaAddr);
	//Update threadID of the server in device driver
	test_KErrNone(Device.ThreadId(aServer, s.iThreadId));
	}

//Kills the server(by forcing to execute TestFunc), then restarts it.
void KillAndRestart(TInt aServer)
	{
	test.Printf(_L("KillAndRestart %d\n"),aServer);
	TInt r=RCodeModifierSession::RunTestFunc(aServer);
	test.Printf(_L("%d returned\n"),r);

	test.Printf(_L("Check the server died\n"));
	r = RCodeModifierSession::GetServerInfo(aServer);
	test.Printf(_L("%d returned\n"),r);
	test(r!=KErrNone);
	
	StartAndUpdate(aServer);
	}

//Terminates the server
void TerminateServer(TInt aServer)
	{
	TRequestStatus status;
	RProcess& p = ServerData[aServer].iProcess;

	test.Printf(_L("TerminateServer %d\n"),aServer);
	test_KErrNone(RCodeModifierSession::Stop(aServer));
	p.Logon(status);
	User::WaitForRequest(status);
	test_Equal(EExitKill, p.ExitType());
	CLOSE_AND_WAIT(p);
	}

//Starts LDD
void StartDriver()
	{
	test.Printf(_L("StartDriver\n"));
	TInt r = User::LoadLogicalDevice(KCodeModifierName);
	test_Value(r, r==KErrNone || r==KErrAlreadyExists);
	if((r = Device.Open())!=KErrNone)	
		{
		User::FreeLogicalDevice(KCodeModifierName);
		test.Printf(_L("Could not open LDD"));
		test(0);
		}
	}

//Unloads LDD
void StopDriver()
	{
	test.Printf(_L("StopDriver\n"));
	Device.Close();
	User::FreeLogicalDevice(KCodeModifierName);
	}

//Checks that TestFunc in servers is really deadly if we do not alter the code
void Test1()
	{
	test.Printf(_L("Test1\n"));
	KillAndRestart(EXip);
	}

//Passing data segment address to set breakpoint.
//The actual behaviour depends of the memory model
//ARMv5: KErrBadDescriptor (-38) is returned
//ARMv6: Will return 0
//We have to make sure here that nothing panics or take any nasty behaviour.
void Test2(TInt aServer)
	{
	TInt val;
	test.Printf(_L("Test2 %d\n"), aServer);
	test_KErrNone(Device.InitialiseCodeModifier(10));

	//Write/read data through ThreadRowWrite/Read
	test_KErrNone(Device.WriteWord(aServer, ServerData[aServer].iVarAddr,/*value*/1));
	test_KErrNone(Device.ReadWord(aServer, ServerData[aServer].iVarAddr,&val));
	test_Equal(1, val);
		
	//Set breakpoint
	TInt r=Device.WriteCode(aServer, ServerData[aServer].iVarAddr,/*value*/5, /*size*/4);
	test.Printf(_L("returns %d\n"),r);

	test_KErrNone(Device.CloseCodeModifier());
	}

//Passing invalid address to set breakpoint.
void Test3(TInt aServer)
	{
	TInt val;
	test.Printf(_L("Test3 %d\n"), aServer);
	test_KErrNone(Device.InitialiseCodeModifier(10));

	//Write/read by ThreadRowWrite/Read
	test_Equal(KErrBadDescriptor, Device.WriteWord(aServer, ServerData[aServer].iInvalidAddr,/*value*/1));
	test_Equal(KErrBadDescriptor, Device.ReadWord(aServer, ServerData[aServer].iInvalidAddr,&val));
		
	//Set breakpoints
	test_Equal(KErrBadDescriptor, Device.WriteCode(aServer, ServerData[aServer].iInvalidAddr,/*value*/5, /*size*/1));
	test_Equal(KErrBadDescriptor, Device.WriteCode(aServer, ServerData[aServer].iInvalidAddr,/*value*/5, /*size*/2));
	test_Equal(KErrBadDescriptor, Device.WriteCode(aServer, ServerData[aServer].iInvalidAddr,/*value*/5, /*size*/4));

	test_KErrNone(Device.CloseCodeModifier());
	}

//Replace BRK in TestFunc in server using 1,2 and 4 long breakpoints.
//Check the content of test func.
//Execute Test Func
void Test4(TInt aServer)
	{
	TInt var;
	test.Printf(_L("Test4 %d\n"), aServer);

	//Try to write code segment throught Kern::ThreadRowWrite
	test_Equal(KErrBadDescriptor, Device.WriteWord(aServer, ServerData[aServer].iFuncAddr,/*value*/1));

	test_KErrNone(Device.InitialiseCodeModifier(10));

	test.Printf(_L("Replace byte 3 of the 1st instruction\n"));
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iFuncAddr+3,TestFuncCode.iNOP>>24, /*size*/1));
	test_KErrNone(Device.ReadWord(aServer,ServerData[aServer].iFuncAddr, &var));
	test.Printf(_L("%xH returned\n"),var);
	test((TUint)var == ((TestFuncCode.iBRK & 0xffffff) | (TestFuncCode.iNOP & 0xff000000)));

	test.Printf(_L("Replace byte 2 of the 1st instruction\n"));
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iFuncAddr+2,TestFuncCode.iNOP>>16, /*size*/1));
	test_KErrNone(Device.ReadWord(aServer,ServerData[aServer].iFuncAddr, &var));
	test.Printf(_L("%xH returned\n"),var);
	test((TUint)var == ((TestFuncCode.iBRK & 0xffff) | (TestFuncCode.iNOP & 0xffff0000)));

	test.Printf(_L("Replace bytes 0 & 1 of the 1st instruction\n"));
	var = TestFuncCode.iNOP | 0xff0000; //byte 3 is messed up - but it won't be writen into code bacause iSize is 2
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iFuncAddr,var, /*size*/2));
	test_KErrNone(Device.ReadWord(aServer,ServerData[aServer].iFuncAddr, &var));
	test.Printf(_L("%xH returned\n"),var);
	test_Equal(TestFuncCode.iNOP, var);

	//We have replaced BRK with NOP. It should be safe now for the server to execute TestFunc.
	test.Printf(_L("Run TestFunc in server and check the server is still alive\n"));
	test_KErrNone(RCodeModifierSession::RunTestFunc(aServer));
	test_KErrNone(RCodeModifierSession::GetServerInfo(aServer));//Any call will work here

	test.Printf(_L("Revert bytes 0 & 1\n"));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iFuncAddr));
	test_KErrNone(Device.ReadWord(aServer, ServerData[aServer].iFuncAddr, &var));
	test.Printf(_L("%xH returned\n"),var);
	test((TUint)var == ((TestFuncCode.iBRK & 0xffff) | (TestFuncCode.iNOP & 0xffff0000)));

	test.Printf(_L("Revert byte 2\n"));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iFuncAddr+2));
	test_KErrNone(Device.ReadWord(aServer, ServerData[aServer].iFuncAddr, &var));
	test.Printf(_L("%xH returned\n"),var);
	test((TUint)var == ((TestFuncCode.iBRK & 0xffffff) | (TestFuncCode.iNOP & 0xff000000)));

	test.Printf(_L("Revert byte 3\n"));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iFuncAddr+3));
	test_KErrNone(Device.ReadWord(aServer, ServerData[aServer].iFuncAddr, &var));
	test.Printf(_L("%xH returned\n"),var);
	test(var == TestFuncCode.iBRK);

	test.Printf(_L("Replace the 1st instruction with the 2nd one in TestFunc\n"));
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iFuncAddr,TestFuncCode.iNOP, /*size*/4));
	test_KErrNone(Device.ReadWord(aServer,ServerData[aServer].iFuncAddr, &var));
	test.Printf(_L("%xH returned\n"),var);
	test(var == TestFuncCode.iNOP);

	//We have replaced BRK with NOP. It should be safe now for the server to execute TestFunc.
	test.Printf(_L("Run TestFunc in server and check the server is still alive\n"));
	test_KErrNone(RCodeModifierSession::RunTestFunc(aServer));
	test_KErrNone(RCodeModifierSession::GetServerInfo(aServer));//Any call will work here

	test_KErrNone(Device.CloseCodeModifier());
	}

//Executes Test4 but with previously shadowed page
void Test5(TInt aServer)
	{
	test.Printf(_L("Test5 %d\n"), aServer);

	test_KErrNone(Device.AllocShadowPage(ServerData[aServer].iFuncAddr));
	Test4(aServer);	
	test_KErrNone(Device.FreeShadowPage(ServerData[aServer].iFuncAddr));
	}

//Tests scenario when a process terminates while there are still associated breakpoints.
void Test6(TInt aServer)
	{
	TInt var;
	test.Printf(_L("Test6 %d\n"), aServer);

	test_KErrNone(Device.InitialiseCodeModifier(10));

	test.Printf(_L("Replace the 1st instruction (BRK) with the 2nd one (NOP) in TestFunc\n"));
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iFuncAddr,TestFuncCode.iNOP, /*size*/4));
	test_KErrNone(Device.ReadWord(aServer,ServerData[aServer].iFuncAddr, &var));
	test.Printf(_L("%xH returned\n"),var);
	test(var == TestFuncCode.iNOP);

	TerminateServer(aServer);
	//After application has stopped, Kernel should clean the breakpoint associated to the server's process....
	
	StartAndUpdate(aServer);
	KillAndRestart(aServer);//... and TestFunct must be deadly again.

	test_KErrNone(Device.CloseCodeModifier());
	}

//Tests out-of-breakpoints scenario
void Test7(TInt aServer)
	{
	test.Printf(_L("Test7 %d\n"), aServer);
	test_KErrNone(Device.InitialiseCodeModifier(1));

	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr,TestFuncCode.iNOP, /*size*/4));
	test_Equal(KErrNoMemory, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+4,TestFuncCode.iNOP, /*size*/4));

	test_KErrNone(Device.CloseCodeModifier());
	}

//Tests breakpoint-already-exists scenario
void Test8(TInt aServer)
	{
	test.Printf(_L("Test8 %d\n"), aServer);
	test_KErrNone(Device.InitialiseCodeModifier(1));

	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr,TestFuncCode.iNOP, /*size*/4));
	test_Equal(KErrAlreadyExists, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr,TestFuncCode.iNOP, /*size*/4));

	test_KErrNone(Device.CloseCodeModifier());
	}

//Tests CodeModifier closing when there are still breakpoints.
//Breakpoints in this test occupies more then one shadowed page.
void Test9()
	{
	TInt var;
	test.Printf(_L("Test9\n"));
	test_KErrNone(Device.InitialiseCodeModifier(10));

	//Put NOPs at the beginning of the code area
	test_KErrNone(Device.WriteCode(EXip, ServerData[EXip].iCodeAreaAddr,TestFuncCode.iNOP, /*size*/4));
	test_KErrNone(Device.WriteCode(ENonxip, ServerData[ENonxip].iCodeAreaAddr,TestFuncCode.iNOP, /*size*/4));

	//Put NOPs at the end of the code area (there are 1024 BRK instructions in CodeArea
	test_KErrNone(Device.WriteCode(EXip, ServerData[EXip].iCodeAreaAddr+1024*sizeof(TInt),TestFuncCode.iNOP, /*size*/4));
	test_KErrNone(Device.WriteCode(ENonxip, ServerData[ENonxip].iCodeAreaAddr+1024*sizeof(TInt),TestFuncCode.iNOP, /*size*/4));

	//Check NOPs are there
	test_KErrNone(Device.ReadWord(EXip,ServerData[EXip].iCodeAreaAddr, &var));
	test_Equal(TestFuncCode.iNOP, var);
	test_KErrNone(Device.ReadWord(ENonxip,ServerData[ENonxip].iCodeAreaAddr, &var));
	test_Equal(TestFuncCode.iNOP, var);
	test_KErrNone(Device.ReadWord(EXip,ServerData[EXip].iCodeAreaAddr+1024*sizeof(TInt), &var));
	test_Equal(TestFuncCode.iNOP, var);
	test_KErrNone(Device.ReadWord(ENonxip,ServerData[ENonxip].iCodeAreaAddr+1024*sizeof(TInt), &var));
	test_Equal(TestFuncCode.iNOP, var);

	//Close Code Modifier. It should revert the changes in the code.
	test_KErrNone(Device.CloseCodeModifier());

	//Check BRKs are back
	test_KErrNone(Device.ReadWord(EXip,ServerData[EXip].iCodeAreaAddr, &var));
	test_Equal(TestFuncCode.iBRK, var);
	test_KErrNone(Device.ReadWord(ENonxip,ServerData[ENonxip].iCodeAreaAddr, &var));
	test_Equal(TestFuncCode.iBRK, var);
	test_KErrNone(Device.ReadWord(EXip,ServerData[EXip].iCodeAreaAddr+1023*sizeof(TInt), &var));
	test_Equal(TestFuncCode.iBRK, var);
	test_KErrNone(Device.ReadWord(ENonxip,ServerData[ENonxip].iCodeAreaAddr+1023*sizeof(TInt), &var));
	test_Equal(TestFuncCode.iBRK, var);
	}



//Used in test 10 to keep the list of breakpoints
class TBrks:public CBase //derived from CBase as we need data initialized to 0
	{
public:	
	TInt iCounter;				//Counts the number of the active brakpoints
	TInt8 iBreakpoint[1025][2];		//0 - no breakpoint, 1-breakpoint set in CodeArea of XIP Server & NON-XIP server
	};

//Performs a random stress test on breakpoint pool.
//There are 1025*2 words in CodeArea in xip and non-xip server.
//A word is randomly picked up to set or clear 4 bytes long breakpoint.
void Test10()
	{
	TInt i,index,whichServer,var;
	TBrks* brks = new TBrks;
	test((TInt)brks);//fail if no memory
	TUint iSeed[2];
	iSeed[0]=User::TickCount();
	iSeed[1]=0;
	test.Printf(_L("Test10 iSeed=%x\n"), iSeed[0]);

	test_KErrNone(Device.InitialiseCodeModifier(2050));//enought to replace all BRK instructions in CodeArea with NOPs in both servers

	for (i=0; i<1000;i++)
		{
		index=Random(iSeed)%2050;
		whichServer = index>1024 ? 1 : 0;
		if (index >1024)
			index-=1025;
		
		TInt8& brk = brks->iBreakpoint[index][whichServer];
		 if (brk)
		 	{//Remove breakpoint
		 	brk = 0;
			test_KErrNone(Device.RestoreCode(whichServer, ServerData[whichServer].iCodeAreaAddr+index*sizeof(TInt)));
			brks->iCounter--;
		 	}
		 else
		 	{//Set breakpoint
		 	brk = 1;
			test_KErrNone(Device.WriteCode(whichServer, ServerData[whichServer].iCodeAreaAddr+index*sizeof(TInt),TestFuncCode.iNOP, /*size*/4));
			brks->iCounter++;	 	
		 	}
		}
	
	test.Printf(_L("Breakpoints left:%d\n"), brks->iCounter);

	//Check the content of the CodeArea in both XIP and Non-XIP Server
	for (i=0; i<2050;i++)
		{
		whichServer = i>1024 ? 1 : 0;
		if (i<=1024)index = i;
		else		index = i-1025;

		test_KErrNone(Device.ReadWord(whichServer,ServerData[whichServer].iCodeAreaAddr+index*sizeof(TInt), &var));
		if(brks->iBreakpoint[index][whichServer])
			test(var == TestFuncCode.iNOP); //Well, breakpoint is actually NOP ...
		else
			test(var == TestFuncCode.iBRK); //... while the original content is BRK instruction
		}

	//Now, apply breakpoints on all remaining addresses
	for (i=0; i<2050;i++)
		{
		whichServer = i>1024 ? 1 : 0;
		if (i<=1024)index = i;
		else		index = i-1025;

		if(!brks->iBreakpoint[index][whichServer])
			test_KErrNone(Device.WriteCode(whichServer, ServerData[whichServer].iCodeAreaAddr+index*sizeof(TInt),TestFuncCode.iNOP, /*size*/4));
		}

	//All BRKs are replaced with NOPs in CodeArea function in both Servers. It should be safe to call the function.
	test_KErrNone(RCodeModifierSession::RunCodeAreaFunc(EXip));
	test_KErrNone(RCodeModifierSession::GetServerInfo(EXip));//This will check the server is still alive
	test_KErrNone(RCodeModifierSession::RunCodeAreaFunc(ENonxip));
	test_KErrNone(RCodeModifierSession::GetServerInfo(ENonxip));//This will check the server is still alive

	test_KErrNone(Device.CloseCodeModifier()); //This will also remove all breakpoints
	delete brks;
	}

//Tests out-of-breakpoints scenario
void Test11(TInt aServer)
	{
	test.Printf(_L("Test11 %d\n"), aServer);
	test_KErrNone(Device.InitialiseCodeModifier(10));

	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+4, /*aValue*/0, /*size*/4));

	//4 bytes breakpoint
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/4));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/2));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+2, /*aValue*/0, /*size*/2));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/1));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+1, /*aValue*/0, /*size*/1));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+2, /*aValue*/0, /*size*/1));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+3, /*aValue*/0, /*size*/1));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iCodeAreaAddr));

	//2 bytes breakpoint aligned to word
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/2));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/4));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/1));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+1, /*aValue*/0, /*size*/1));
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+2, /*aValue*/0, /*size*/2));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iCodeAreaAddr));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iCodeAreaAddr+2));

	//2 bytes breakpoint aligned to word+2
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+2, /*aValue*/0, /*size*/2));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/4));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+2, /*aValue*/0, /*size*/1));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+3, /*aValue*/0, /*size*/1));
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/2));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iCodeAreaAddr));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iCodeAreaAddr+2));

	//1 byte breakpoint
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/1));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/4));
	test_Equal(KErrAccessDenied, Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr, /*aValue*/0, /*size*/2));
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+1, /*aValue*/0, /*size*/1));
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+2, /*aValue*/0, /*size*/1));
	test_KErrNone(Device.WriteCode(aServer, ServerData[aServer].iCodeAreaAddr+3, /*aValue*/0, /*size*/1));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iCodeAreaAddr));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iCodeAreaAddr+1));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iCodeAreaAddr+2));
	test_KErrNone(Device.RestoreCode(aServer, ServerData[aServer].iCodeAreaAddr+3));

	test_KErrNone(Device.CloseCodeModifier());
	}


void ClientAppL()
	{
	test.Start(_L("ClientAppL"));
	UpdateClientsGlobals();
	StartDriver();	
	StartAndUpdate(EXip);
	StartAndUpdate(ENonxip);
	
// All tests run with the following pre-conditions:
// - both XIP and nonXIP server are running.
// - device driver has updated servers' threadIDs (used for setting breakpoints and reading/writing data).
// - all global variables (ServerData, TestFuncCode)are valid.
// - CodeModifier is not installed.
	Test1();
	Test2(EXip);
	Test2(ENonxip);
	Test3(EXip);
	Test3(ENonxip);
	Test4(EXip);
	Test4(ENonxip);
	Test5(EXip);
	Test6(EXip);
	Test7(EXip);
	Test8(EXip);
	Test9();
	Test10();
	Test11(EXip);
	Test11(ENonxip);
	TerminateServer(EXip);
	TerminateServer(ENonxip);
	StopDriver();	
	test.End();
	}

/**Entry point for both client and server apps*/
TInt E32Main()
	{
	//Chech if we are client, XIP server or nonXIP server
	TBuf<64> c;
	User::CommandLine(c);
	if (c.FindF(KServerXIP) >= 0) 
		return ServerEntryPoint(EXip);
	if (c.FindF(KServerNONXIP) >= 0) 
		return ServerEntryPoint(ENonxip);
	
	// client
	CTrapCleanup* trap = CTrapCleanup::New();
	if (!trap)
		return KErrNoMemory;
	test.Title();
	__UHEAP_MARK;
	TRAPD(r,ClientAppL());
	__UHEAP_MARKEND;
	delete trap;
	return r;
	}