sl@0: // Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32test\mmu\t_alias_remove.cpp sl@0: // Overview: sl@0: // Test interactions when free memory being aliases. sl@0: // Details: sl@0: // Create 3 mappings to one chunk one that owns the chunk, one to map it again another process sl@0: // and another alias mapping. sl@0: // Then while the alias mapping is accessing the chunk close the second mapping. sl@0: // Platforms/Drives/Compatibility: sl@0: // All. sl@0: // Assumptions/Requirement/Pre-requisites: sl@0: // Failures and causes: sl@0: // Base Port information: sl@0: // sl@0: // sl@0: sl@0: #define __E32TEST_EXTENSION__ sl@0: #include sl@0: #include sl@0: #include sl@0: #include "..\defrag\d_pagemove.h" sl@0: sl@0: const TPtrC KAliasProcessName = _L("T_ALIAS_REMOVE"); sl@0: const TPtrC KAliasChunkName = _L("AliasChunk"); sl@0: const TPtrC KAliasServerName = _L("AliasServer"); sl@0: const TPtrC KMasterServerName = _L("MasterServer"); sl@0: sl@0: RBuf ChunkName; sl@0: RBuf MasterServerName; sl@0: RBuf AliasServerName; sl@0: sl@0: RTest test(KAliasProcessName); sl@0: sl@0: //#define ENABLE_PRINTFS sl@0: #ifndef __MSVC6__ // VC6 can't cope with variable arguments in macros. sl@0: sl@0: #define T_PRINTF(x...) test.Printf(x) sl@0: #define D_PRINTF(x...) RDebug::Printf(x) sl@0: #ifdef ENABLE_PRINTFS sl@0: #define PRINTF(x) x sl@0: #else sl@0: #define PRINTF(x) sl@0: #endif // ENABLE_PRINTFS sl@0: sl@0: #else sl@0: #define PRINTF(x) sl@0: #endif // __MSCV6__ sl@0: sl@0: enum TSlaveMsgType sl@0: { sl@0: ESlaveConnect = -1, sl@0: ESlaveDisconnect = -2, sl@0: ESlaveReadChunk = 0, sl@0: ESlaveClosedChunk = 1, sl@0: }; sl@0: sl@0: sl@0: struct SThreadData sl@0: { sl@0: TUint8 iFillValue; sl@0: TUint iChunkSize; sl@0: RThread* iMasterThread; sl@0: TUint iProcessId; sl@0: }; sl@0: sl@0: sl@0: class RAliasSession : public RSessionBase sl@0: { sl@0: public: sl@0: TInt CreateSession(const TDesC& aServerName, TInt aMsgSlots) sl@0: { sl@0: return RSessionBase::CreateSession(aServerName,User::Version(),aMsgSlots); sl@0: } sl@0: TInt PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr) sl@0: { sl@0: return (SendReceive(aFunction, aPtr)); sl@0: } sl@0: TInt PublicSend(TInt aFunction, const TIpcArgs &aPtr) sl@0: { sl@0: return (Send(aFunction, aPtr)); sl@0: } sl@0: }; sl@0: sl@0: sl@0: TInt SlaveProcess(TUint aProcessId, TUint aFillValue) sl@0: { sl@0: // Connect to the master server to indicate that we're ready to receive ipc messages. sl@0: RAliasSession masterSession; sl@0: test_KErrNone(masterSession.CreateSession(MasterServerName, 1)); sl@0: sl@0: PRINTF(T_PRINTF(_L("Process ID %d Slave open chunk\n"), aProcessId)); sl@0: // Open the global chunk. sl@0: RChunk chunk; sl@0: TInt r = chunk.OpenGlobal(ChunkName, ETrue); sl@0: test_KErrNone(r); sl@0: sl@0: // Connect to alias server. sl@0: PRINTF(T_PRINTF(_L("Process ID %d Slave connect to alias server\n"), aProcessId)); sl@0: RAliasSession aliasSession; sl@0: test_KErrNone(aliasSession.CreateSession(AliasServerName, 1)); sl@0: sl@0: PRINTF(T_PRINTF(_L("Process ID %d Slave send data to alias server\n"), aProcessId)); sl@0: TPtr8 arg0(chunk.Base(), chunk.Size(), chunk.Size()); sl@0: r = aliasSession.PublicSend(ESlaveReadChunk, TIpcArgs(&arg0)); sl@0: test_KErrNone(r); sl@0: sl@0: // Close the chunk removing its mapping before the server has read it. sl@0: chunk.Close(); sl@0: PRINTF(T_PRINTF(_L("Process ID %d Slave closed chunk\n"), aProcessId)); sl@0: sl@0: r = masterSession.PublicSendReceive(ESlaveClosedChunk, TIpcArgs()); sl@0: test_KErrNone(r); sl@0: aliasSession.Close(); sl@0: masterSession.Close(); sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: TInt ChunkReadThread(TAny* aThreadData) sl@0: { sl@0: SThreadData* threadData = (SThreadData*)aThreadData; sl@0: RServer2 aliasServer; sl@0: TInt r = aliasServer.CreateGlobal(AliasServerName); sl@0: if (r != KErrNone) sl@0: { sl@0: RDebug::Printf("Process ID %d Error creating alias server r=%d", threadData->iProcessId, r); sl@0: return r; sl@0: } sl@0: // Connect to the master server to indicate that we're ready to receive ipc messages. sl@0: RAliasSession masterSession; sl@0: test_KErrNone(masterSession.CreateSession(MasterServerName, 1)); sl@0: sl@0: PRINTF(D_PRINTF("Process ID %d Alias wait for slave connection", threadData->iProcessId)); sl@0: RMessage2 aliasMessage; sl@0: // Read and complete the connect message from the slave. sl@0: aliasServer.Receive(aliasMessage); sl@0: test_Equal(ESlaveConnect, aliasMessage.Function()); sl@0: aliasMessage.Complete(KErrNone); sl@0: sl@0: // Read the data of the remote chunk. sl@0: PRINTF(D_PRINTF("Process ID %d Alias read chunk data", threadData->iProcessId)); sl@0: HBufC8* argTmp = HBufC8::New(threadData->iChunkSize); sl@0: test_NotNull(argTmp); sl@0: RBuf8 argBuf(argTmp); sl@0: aliasServer.Receive(aliasMessage); sl@0: test_Equal(ESlaveReadChunk, aliasMessage.Function()); sl@0: r = aliasMessage.Read(0, argBuf); sl@0: if (r == KErrNone) sl@0: {// Successfully read the chunk so verify it. sl@0: aliasMessage.Complete(KErrNone); sl@0: sl@0: PRINTF(D_PRINTF("Process ID %d Alias verify chunk data", threadData->iProcessId)); sl@0: const TUint8* bufPtr = argBuf.Ptr(); sl@0: const TUint8* bufEnd = bufPtr + threadData->iChunkSize; sl@0: for (; bufPtr < bufEnd; bufPtr++) sl@0: { sl@0: if (*bufPtr != threadData->iFillValue) sl@0: { sl@0: RDebug::Printf("Process ID %d Read incorrect data exp 0x%x got 0x%x", sl@0: threadData->iProcessId, threadData->iFillValue, *bufPtr); sl@0: r = *bufPtr; sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: else sl@0: { sl@0: PRINTF(D_PRINTF("Process ID %d Error reading chunk remotely %d", threadData->iProcessId, r)); sl@0: } sl@0: argBuf.Close(); sl@0: masterSession.Close(); sl@0: return r; sl@0: } sl@0: sl@0: sl@0: TInt MasterProcess(TInt aProcessId) sl@0: { sl@0: TInt pageSize; sl@0: UserHal::PageSizeInBytes(pageSize); sl@0: // Need a large chunk so that alias that reads it is held for a long sl@0: // enough period for there to be conflicts with the chunk closure in sl@0: // the slave process. sl@0: const TUint KChunkSize = pageSize * 1024; sl@0: sl@0: PRINTF(T_PRINTF(_L("Process ID %d Create chunk\n"), aProcessId)); sl@0: RChunk chunk; sl@0: TInt r = chunk.CreateGlobal(ChunkName, KChunkSize, KChunkSize); sl@0: test_KErrNone(r); sl@0: sl@0: sl@0: for (TUint8 fillValue = 1; fillValue < 255; fillValue++) sl@0: { sl@0: // Output a character every 16 iterations so test machines sl@0: // don't time out. sl@0: if ((fillValue & 0xf) == 1) sl@0: test.Printf(_L(".")); sl@0: sl@0: PRINTF(T_PRINTF(_L("Process ID %d start slave fill value %d\n"), aProcessId, fillValue)); sl@0: RServer2 masterServer; sl@0: r = masterServer.CreateGlobal(MasterServerName); sl@0: test_KErrNone(r); sl@0: RMessage2 masterMessage; sl@0: sl@0: // Update the chunk to new fill value. sl@0: memset(chunk.Base(), fillValue, KChunkSize); sl@0: sl@0: PRINTF(T_PRINTF(_L("Process ID %d Start the slave process\n"), aProcessId)); sl@0: RProcess slaveProcess; sl@0: test_KErrNone(slaveProcess.Create(KAliasProcessName, KNullDesC)); sl@0: test_KErrNone(slaveProcess.SetParameter(1, aProcessId)); sl@0: test_KErrNone(slaveProcess.SetParameter(2, fillValue)); sl@0: TRequestStatus slaveStatus; sl@0: slaveProcess.Logon(slaveStatus); sl@0: test_Equal(KRequestPending, slaveStatus.Int()); sl@0: slaveProcess.Resume(); sl@0: sl@0: // Wait for the connect message from the slave process. sl@0: masterServer.Receive(masterMessage); sl@0: test_Equal(ESlaveConnect, masterMessage.Function()); sl@0: sl@0: SThreadData threadData; sl@0: threadData.iFillValue = fillValue; sl@0: threadData.iChunkSize = KChunkSize; sl@0: threadData.iProcessId = aProcessId; sl@0: RThread readThread; sl@0: r = readThread.Create(KNullDesC, ChunkReadThread, 10 * pageSize, KChunkSize, KChunkSize * 2, &threadData); sl@0: test_KErrNone(r); sl@0: TRequestStatus threadStatus; sl@0: readThread.Logon(threadStatus); sl@0: test_Equal(KRequestPending, threadStatus.Int()); sl@0: readThread.Resume(); sl@0: sl@0: PRINTF(T_PRINTF(_L("Process ID %d Wait for alias thread to start server\n"), aProcessId)); sl@0: RMessage2 aliasMessage; sl@0: masterServer.Receive(aliasMessage); sl@0: test_Equal(ESlaveConnect, aliasMessage.Function()); sl@0: aliasMessage.Complete(KErrNone); sl@0: sl@0: // Signal to the slave process to send chunk to alias thread. sl@0: PRINTF(T_PRINTF(_L("Process ID %d Signal to slave to send chunk to alias\n"), aProcessId)); sl@0: masterMessage.Complete(KErrNone); sl@0: sl@0: // Wait for slave to close the chunk and fill it with new value. sl@0: for (;;) sl@0: { sl@0: masterServer.Receive(masterMessage); sl@0: TInt func = masterMessage.Function(); sl@0: PRINTF(T_PRINTF(_L("Process ID %d rxd %d\n"), aProcessId, func)); sl@0: if (func == ESlaveClosedChunk) sl@0: {// Slave closed the chunk. sl@0: memset(chunk.Base(), ++fillValue, KChunkSize); sl@0: break; sl@0: } sl@0: else sl@0: {// Alias has read the chunk and completed. sl@0: test_Equal(ESlaveDisconnect, func); sl@0: } sl@0: } sl@0: sl@0: PRINTF(T_PRINTF(_L("Process ID %d Wait for alias to complete\n"), aProcessId)); sl@0: masterMessage.Complete(KErrNone); sl@0: User::WaitForRequest(threadStatus); sl@0: TInt statusInt = threadStatus.Int(); sl@0: test_Value( statusInt, sl@0: statusInt == KErrNone || sl@0: statusInt == KErrBadDescriptor || sl@0: statusInt == KErrDied); sl@0: test_Equal(EExitKill, readThread.ExitType()); sl@0: readThread.Close(); sl@0: sl@0: PRINTF(T_PRINTF(_L("Process ID %d Wait for slave to complete\n"), aProcessId)); sl@0: User::WaitForRequest(slaveStatus); sl@0: test_Equal(EExitKill, slaveProcess.ExitType()); sl@0: test_Equal(KErrNone, slaveProcess.ExitReason()); sl@0: slaveProcess.Close(); sl@0: masterServer.Close(); sl@0: } sl@0: sl@0: chunk.Close(); sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: sl@0: GLDEF_C TInt E32Main() sl@0: { sl@0: TInt processId; sl@0: if(User::GetTIntParameter(1, processId)==KErrNone) sl@0: { sl@0: test_KErrNone(ChunkName.Create(KAliasChunkName.Length() + 3)); sl@0: ChunkName.Copy(KAliasChunkName); sl@0: ChunkName.AppendNum(processId); sl@0: sl@0: test_KErrNone(MasterServerName.Create(KMasterServerName.Length() + 3)); sl@0: MasterServerName.Copy(KMasterServerName); sl@0: MasterServerName.AppendNum(processId); sl@0: sl@0: test_KErrNone(AliasServerName.Create(KAliasServerName.Length() + 3)); sl@0: AliasServerName.Copy(KAliasServerName); sl@0: AliasServerName.AppendNum(processId); sl@0: sl@0: TInt fillValue; sl@0: if(User::GetTIntParameter(2, fillValue)==KErrNone) sl@0: { sl@0: return SlaveProcess(processId, fillValue); sl@0: } sl@0: return MasterProcess(processId); sl@0: } sl@0: sl@0: // Get the number of cpus and use it to determine how many processes to execute. sl@0: RPageMove pagemove; sl@0: test_KErrNone(pagemove.Open()); sl@0: TUint masterProcesses = pagemove.NumberOfCpus() + 1; sl@0: pagemove.Close(); sl@0: sl@0: TInt cmdLineLen = User::CommandLineLength(); sl@0: if(cmdLineLen) sl@0: { sl@0: RBuf cmdLine; sl@0: test_KErrNone(cmdLine.Create(cmdLineLen)); sl@0: User::CommandLine(cmdLine); sl@0: test_KErrNone(TLex(cmdLine).Val(masterProcesses)); sl@0: } sl@0: sl@0: test.Title(); sl@0: test.Start(_L("")); sl@0: test.Printf(_L("Create %d processes for accessing aliases being removed\n"), masterProcesses); sl@0: sl@0: TUint32 debugMask = UserSvr::DebugMask(); sl@0: User::SetDebugMask(0); sl@0: sl@0: // Start master processes to alias memory between each other. sl@0: RProcess* masters = new RProcess[masterProcesses]; sl@0: TRequestStatus* masterStatus = new TRequestStatus[masterProcesses]; sl@0: TUint i = 0; sl@0: for (; i < masterProcesses; i++) sl@0: { sl@0: test_KErrNone(masters[i].Create(KAliasProcessName, KNullDesC)); sl@0: test_KErrNone(masters[i].SetParameter(1, i)); sl@0: masters[i].Logon(masterStatus[i]); sl@0: test_Equal(KRequestPending, masterStatus[i].Int()); sl@0: } sl@0: test.Next(_L("Resume the processes")); sl@0: for (i = 0; i < masterProcesses; i++) sl@0: { sl@0: masters[i].Resume(); sl@0: } sl@0: sl@0: test.Next(_L("Wait for processes to exit")); sl@0: for (i = 0; i < masterProcesses; i++) sl@0: { sl@0: User::WaitForRequest(masterStatus[i]); sl@0: test_Equal(EExitKill, masters[i].ExitType()); sl@0: test_Equal(KErrNone, masters[i].ExitReason()); sl@0: } sl@0: User::SetDebugMask(debugMask); sl@0: delete masterStatus; sl@0: delete masters; sl@0: test.Printf(_L("\n")); sl@0: test.End(); sl@0: return KErrNone; sl@0: }